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(PluginFieldAppends);
|
||||||
await this.app.pm.add(PluginCustomComponents);
|
await this.app.pm.add(PluginCustomComponents);
|
||||||
await this.app.pm.add(PluginSheet);
|
await this.app.pm.add(PluginSheet);
|
||||||
await this.app.pm.add(PluginDemo);
|
// await this.app.pm.add(PluginDemo);
|
||||||
}
|
}
|
||||||
|
|
||||||
async registerSettings() {
|
async registerSettings() {
|
||||||
|
@ -20,7 +20,7 @@ export class PluginCoreServer extends Plugin {
|
|||||||
tstzrange: TstzrangeField,
|
tstzrange: TstzrangeField,
|
||||||
});
|
});
|
||||||
this.addFeature(DepartmentsPlugin);
|
this.addFeature(DepartmentsPlugin);
|
||||||
this.addFeature(PluginDemo);
|
// this.addFeature(PluginDemo);
|
||||||
}
|
}
|
||||||
|
|
||||||
async load() {
|
async load() {
|
||||||
|
@ -9,7 +9,9 @@
|
|||||||
"license": "Apache-2.0",
|
"license": "Apache-2.0",
|
||||||
"main": "./dist/server/index.js",
|
"main": "./dist/server/index.js",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"jsonata": "^2.0.5"
|
"form-data": "^4.0.0",
|
||||||
|
"jsonata": "^2.0.5",
|
||||||
|
"mime-types": "^2.1.35"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@ant-design/icons": "~5.3.7",
|
"@ant-design/icons": "~5.3.7",
|
||||||
|
@ -23,10 +23,7 @@ function IndividualHooksRadioWithTooltip({ onChange, ...props }) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const filteredValues = fields.reduce((result, item) => {
|
const filteredValues = fields.reduce((result, item) => {
|
||||||
if (
|
if (item.name in valuesField.value && (target.value || ![].includes(item.type))) {
|
||||||
item.name in valuesField.value &&
|
|
||||||
(target.value || !['hasOne', 'hasMany', 'belongsToMany'].includes(item.type))
|
|
||||||
) {
|
|
||||||
result[item.name] = valuesField.value[item.name];
|
result[item.name] = valuesField.value[item.name];
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
@ -111,7 +108,7 @@ export default class extends Instruction {
|
|||||||
...values,
|
...values,
|
||||||
'x-component-props': {
|
'x-component-props': {
|
||||||
filter(this, field) {
|
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(PluginWorkflowJSONParseServer);
|
||||||
this.addFeature(PluginWorkflowJSParseServer);
|
this.addFeature(PluginWorkflowJSParseServer);
|
||||||
this.addFeature(PluginWorkflowDataMappingServer);
|
this.addFeature(PluginWorkflowDataMappingServer);
|
||||||
// this.addFeature(PluginWorkflowAPIRegularServer);
|
|
||||||
this.addFeature(PluginInterception);
|
this.addFeature(PluginInterception);
|
||||||
this.addFeature(PluginVariables);
|
this.addFeature(PluginVariables);
|
||||||
this.addFeature(PluginResponse);
|
this.addFeature(PluginResponse);
|
||||||
|
@ -121,9 +121,6 @@ export class OmniTrigger extends Trigger {
|
|||||||
for (const event of asyncGroup) {
|
for (const event of asyncGroup) {
|
||||||
this.workflow.trigger(event[0], event[1]);
|
this.workflow.trigger(event[0], event[1]);
|
||||||
}
|
}
|
||||||
this.workflow.noticeManager.notify('workflow:regular', {
|
|
||||||
msg: 'start',
|
|
||||||
});
|
|
||||||
await next();
|
await next();
|
||||||
};
|
};
|
||||||
constructor(workflow) {
|
constructor(workflow) {
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
import { parseCollectionName } from '@tachybase/data-source-manager';
|
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 { Instruction } from '.';
|
||||||
import { JOB_STATUS } from '../constants';
|
import { JOB_STATUS } from '../constants';
|
||||||
@ -17,6 +24,81 @@ export class CreateInstruction extends Instruction {
|
|||||||
const options = processor.getParsedValue(params, node.id);
|
const options = processor.getParsedValue(params, node.id);
|
||||||
const transaction = this.workflow.useDataSourceTransaction(dataSourceName, processor.transaction);
|
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({
|
const created = await repository.create({
|
||||||
...options,
|
...options,
|
||||||
context: {
|
context: {
|
||||||
|
@ -1,4 +1,11 @@
|
|||||||
import { parseCollectionName } from '@tachybase/data-source-manager';
|
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 { Instruction } from '.';
|
||||||
import { JOB_STATUS } from '../constants';
|
import { JOB_STATUS } from '../constants';
|
||||||
@ -15,6 +22,82 @@ export class UpdateInstruction extends Instruction {
|
|||||||
.get(dataSourceName)
|
.get(dataSourceName)
|
||||||
.collectionManager.getCollection(collectionName);
|
.collectionManager.getCollection(collectionName);
|
||||||
const options = processor.getParsedValue(params, node.id);
|
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({
|
const result = await repository.update({
|
||||||
...options,
|
...options,
|
||||||
context: {
|
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