feat(workflow): support attachment field assign in workflow create/update nodes. (#1419)
Co-authored-by: sealday <sealday@gmail.com> Reviewed-on: daoyoucloud/tachybase#1419
This commit is contained in:
parent
54a7f5dc40
commit
45571c62f1
@ -83,7 +83,7 @@ export class PluginCoreClient extends Plugin {
|
||||
await this.app.pm.add(PluginFieldAppends);
|
||||
await this.app.pm.add(PluginCustomComponents);
|
||||
await this.app.pm.add(PluginSheet);
|
||||
await this.app.pm.add(PluginDemo);
|
||||
// await this.app.pm.add(PluginDemo);
|
||||
}
|
||||
|
||||
async registerSettings() {
|
||||
|
@ -20,7 +20,7 @@ export class PluginCoreServer extends Plugin {
|
||||
tstzrange: TstzrangeField,
|
||||
});
|
||||
this.addFeature(DepartmentsPlugin);
|
||||
this.addFeature(PluginDemo);
|
||||
// this.addFeature(PluginDemo);
|
||||
}
|
||||
|
||||
async load() {
|
||||
|
@ -9,7 +9,9 @@
|
||||
"license": "Apache-2.0",
|
||||
"main": "./dist/server/index.js",
|
||||
"dependencies": {
|
||||
"jsonata": "^2.0.5"
|
||||
"form-data": "^4.0.0",
|
||||
"jsonata": "^2.0.5",
|
||||
"mime-types": "^2.1.35"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@ant-design/icons": "~5.3.7",
|
||||
|
@ -23,10 +23,7 @@ function IndividualHooksRadioWithTooltip({ onChange, ...props }) {
|
||||
return;
|
||||
}
|
||||
const filteredValues = fields.reduce((result, item) => {
|
||||
if (
|
||||
item.name in valuesField.value &&
|
||||
(target.value || !['hasOne', 'hasMany', 'belongsToMany'].includes(item.type))
|
||||
) {
|
||||
if (item.name in valuesField.value && (target.value || ![].includes(item.type))) {
|
||||
result[item.name] = valuesField.value[item.name];
|
||||
}
|
||||
return result;
|
||||
@ -111,7 +108,7 @@ export default class extends Instruction {
|
||||
...values,
|
||||
'x-component-props': {
|
||||
filter(this, field) {
|
||||
return this.params?.individualHooks || !['hasOne', 'hasMany', 'belongsToMany'].includes(field.type);
|
||||
return this.params?.individualHooks || ![].includes(field.type);
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -79,7 +79,6 @@ export default class PluginWorkflowServer extends Plugin {
|
||||
this.addFeature(PluginWorkflowJSONParseServer);
|
||||
this.addFeature(PluginWorkflowJSParseServer);
|
||||
this.addFeature(PluginWorkflowDataMappingServer);
|
||||
// this.addFeature(PluginWorkflowAPIRegularServer);
|
||||
this.addFeature(PluginInterception);
|
||||
this.addFeature(PluginVariables);
|
||||
this.addFeature(PluginResponse);
|
||||
|
@ -121,9 +121,6 @@ export class OmniTrigger extends Trigger {
|
||||
for (const event of asyncGroup) {
|
||||
this.workflow.trigger(event[0], event[1]);
|
||||
}
|
||||
this.workflow.noticeManager.notify('workflow:regular', {
|
||||
msg: 'start',
|
||||
});
|
||||
await next();
|
||||
};
|
||||
constructor(workflow) {
|
||||
|
@ -1,4 +1,11 @@
|
||||
import { parseCollectionName } from '@tachybase/data-source-manager';
|
||||
import { Gateway } from '@tachybase/server';
|
||||
import { uid } from '@tachybase/utils';
|
||||
|
||||
import axios from 'axios';
|
||||
import FormData from 'form-data';
|
||||
import _ from 'lodash';
|
||||
import mime from 'mime-types';
|
||||
|
||||
import { Instruction } from '.';
|
||||
import { JOB_STATUS } from '../constants';
|
||||
@ -17,6 +24,81 @@ export class CreateInstruction extends Instruction {
|
||||
const options = processor.getParsedValue(params, node.id);
|
||||
const transaction = this.workflow.useDataSourceTransaction(dataSourceName, processor.transaction);
|
||||
|
||||
const c = this.workflow.app.dataSourceManager.dataSources
|
||||
.get(dataSourceName)
|
||||
.collectionManager.getCollection(collectionName);
|
||||
const fields = c.getFields();
|
||||
const fieldNames = Object.keys(params.values);
|
||||
const includesFields = fields.filter((field) => fieldNames.includes(field.options.name));
|
||||
|
||||
const userId = _.get(processor.getScope(node.id), '$context.user.id', '');
|
||||
const token = this.workflow.app.authManager.jwt.sign({ userId });
|
||||
|
||||
const handleUrl = async (url) => {
|
||||
const form = new FormData();
|
||||
if (url.startsWith('data:')) {
|
||||
const matches = url.match(/^data:(.+);base64,(.+)$/);
|
||||
if (matches) {
|
||||
const contentType = matches[1];
|
||||
const base64Data = matches[2];
|
||||
const buffer = Buffer.from(base64Data, 'base64');
|
||||
const ext = mime.extension(contentType);
|
||||
const filename = `${uid()}.${ext}`;
|
||||
|
||||
form.append('file', buffer, {
|
||||
filename,
|
||||
contentType,
|
||||
});
|
||||
} else {
|
||||
throw new Error('Invalid data URL format');
|
||||
}
|
||||
} else {
|
||||
// 下载指定 URL 的内容
|
||||
const response = await axios({
|
||||
method: 'get',
|
||||
url,
|
||||
responseType: 'stream',
|
||||
});
|
||||
// 获取文件的 MIME 类型
|
||||
const contentType = response.headers['content-type'];
|
||||
// 根据 MIME 类型获取文件扩展名
|
||||
const ext = mime.extension(contentType);
|
||||
const filename = `${uid()}.${ext}`;
|
||||
// 创建 FormData 实例
|
||||
form.append('file', response.data, {
|
||||
filename,
|
||||
contentType: response.headers['content-type'],
|
||||
});
|
||||
}
|
||||
// 发送 multipart 请求
|
||||
const origin = Gateway.getInstance().runAtLoop;
|
||||
const uploadResponse = await axios({
|
||||
method: 'post',
|
||||
url: origin + '/api/attachments:create',
|
||||
data: form,
|
||||
headers: {
|
||||
...form.getHeaders(),
|
||||
Authorization: 'Bearer ' + token,
|
||||
},
|
||||
});
|
||||
return uploadResponse.data.data;
|
||||
};
|
||||
|
||||
// 处理文件类型
|
||||
for (const attachmentField of includesFields) {
|
||||
if (attachmentField.options.interface === 'attachment') {
|
||||
const urls = options.values[attachmentField.options.name];
|
||||
if (Array.isArray(urls)) {
|
||||
for (const i in urls) {
|
||||
urls[i] = await handleUrl(urls[i]);
|
||||
}
|
||||
} else {
|
||||
const url = options.values[attachmentField.options.name];
|
||||
options.values[attachmentField.options.name] = await handleUrl(url);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const created = await repository.create({
|
||||
...options,
|
||||
context: {
|
||||
|
@ -1,4 +1,11 @@
|
||||
import { parseCollectionName } from '@tachybase/data-source-manager';
|
||||
import { Gateway } from '@tachybase/server';
|
||||
import { uid } from '@tachybase/utils';
|
||||
|
||||
import axios from 'axios';
|
||||
import FormData from 'form-data';
|
||||
import _ from 'lodash';
|
||||
import mime from 'mime-types';
|
||||
|
||||
import { Instruction } from '.';
|
||||
import { JOB_STATUS } from '../constants';
|
||||
@ -15,6 +22,82 @@ export class UpdateInstruction extends Instruction {
|
||||
.get(dataSourceName)
|
||||
.collectionManager.getCollection(collectionName);
|
||||
const options = processor.getParsedValue(params, node.id);
|
||||
|
||||
const c = this.workflow.app.dataSourceManager.dataSources
|
||||
.get(dataSourceName)
|
||||
.collectionManager.getCollection(collectionName);
|
||||
const fields = c.getFields();
|
||||
const fieldNames = Object.keys(params.values);
|
||||
const includesFields = fields.filter((field) => fieldNames.includes(field.options.name));
|
||||
|
||||
const userId = _.get(processor.getScope(node.id), '$context.user.id', '');
|
||||
const token = this.workflow.app.authManager.jwt.sign({ userId });
|
||||
|
||||
const handleUrl = async (url) => {
|
||||
const form = new FormData();
|
||||
if (url.startsWith('data:')) {
|
||||
const matches = url.match(/^data:(.+);base64,(.+)$/);
|
||||
if (matches) {
|
||||
const contentType = matches[1];
|
||||
const base64Data = matches[2];
|
||||
const buffer = Buffer.from(base64Data, 'base64');
|
||||
const ext = mime.extension(contentType);
|
||||
const filename = `${uid()}.${ext}`;
|
||||
|
||||
form.append('file', buffer, {
|
||||
filename,
|
||||
contentType,
|
||||
});
|
||||
} else {
|
||||
throw new Error('Invalid data URL format');
|
||||
}
|
||||
} else {
|
||||
// 下载指定 URL 的内容
|
||||
const response = await axios({
|
||||
method: 'get',
|
||||
url,
|
||||
responseType: 'stream',
|
||||
});
|
||||
// 获取文件的 MIME 类型
|
||||
const contentType = response.headers['content-type'];
|
||||
// 根据 MIME 类型获取文件扩展名
|
||||
const ext = mime.extension(contentType);
|
||||
const filename = `${uid()}.${ext}`;
|
||||
// 创建 FormData 实例
|
||||
form.append('file', response.data, {
|
||||
filename,
|
||||
contentType: response.headers['content-type'],
|
||||
});
|
||||
}
|
||||
// 发送 multipart 请求
|
||||
const origin = Gateway.getInstance().runAtLoop;
|
||||
const uploadResponse = await axios({
|
||||
method: 'post',
|
||||
url: origin + '/api/attachments:create',
|
||||
data: form,
|
||||
headers: {
|
||||
...form.getHeaders(),
|
||||
Authorization: 'Bearer ' + token,
|
||||
},
|
||||
});
|
||||
return uploadResponse.data.data;
|
||||
};
|
||||
|
||||
// 处理文件类型
|
||||
for (const attachmentField of includesFields) {
|
||||
if (attachmentField.options.interface === 'attachment') {
|
||||
const urls = options.values[attachmentField.options.name];
|
||||
if (Array.isArray(urls)) {
|
||||
for (const i in urls) {
|
||||
urls[i] = await handleUrl(urls[i]);
|
||||
}
|
||||
} else {
|
||||
const url = options.values[attachmentField.options.name];
|
||||
options.values[attachmentField.options.name] = [await handleUrl(url)];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const result = await repository.update({
|
||||
...options,
|
||||
context: {
|
||||
|
14013
pnpm-lock.yaml
14013
pnpm-lock.yaml
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user