From c453612e1102d823a5f131676d413e7f411324ff Mon Sep 17 00:00:00 2001 From: Junyi Date: Wed, 5 Mar 2025 21:55:47 +0800 Subject: [PATCH] fix(plugin-workflow-aggregate): add precision process after query (#6358) * fix(plugin-workflow-aggregate): add precision process after query * test(plugin-workflow-aggregate): add test cases * fix(evaluators): fix test cases * fix(plugin-workflow-aggregate): fix e2e test case * fix(plugin-workflow-aggregate): fix test cases --- .../core/evaluators/src/utils/formulajs.ts | 2 +- packages/core/evaluators/src/utils/mathjs.ts | 2 +- .../plugin-workflow-aggregate/package.json | 1 + .../src/client/AggregateInstruction.tsx | 2 +- .../client/__e2e__/DataOfCollection.test.ts | 3 +- .../src/server/AggregateInstruction.ts | 4 +- .../src/server/__tests__/instruction.test.ts | 84 ++++++++++++++++++- .../src/e2e/e2ePageObjectModel.ts | 2 +- .../src/server/collections/posts.ts | 5 ++ 9 files changed, 98 insertions(+), 7 deletions(-) diff --git a/packages/core/evaluators/src/utils/formulajs.ts b/packages/core/evaluators/src/utils/formulajs.ts index 21310158f3..9385f2c5eb 100644 --- a/packages/core/evaluators/src/utils/formulajs.ts +++ b/packages/core/evaluators/src/utils/formulajs.ts @@ -22,7 +22,7 @@ export default evaluate.bind(function (expression: string, scope = {}) { if (Number.isNaN(result) || !Number.isFinite(result)) { return null; } - return round(result, 9); + return round(result, 14); } return result; }, {}); diff --git a/packages/core/evaluators/src/utils/mathjs.ts b/packages/core/evaluators/src/utils/mathjs.ts index 0a1c66ae5a..0dded6a5a3 100644 --- a/packages/core/evaluators/src/utils/mathjs.ts +++ b/packages/core/evaluators/src/utils/mathjs.ts @@ -18,7 +18,7 @@ export default evaluate.bind( if (Number.isNaN(result) || !Number.isFinite(result)) { return null; } - return math.round(result, 9); + return math.round(result, 14); } if (result instanceof math.Matrix) { return result.toArray(); diff --git a/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json b/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json index 63f3c0f96d..374621d269 100644 --- a/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json +++ b/packages/plugins/@nocobase/plugin-workflow-aggregate/package.json @@ -11,6 +11,7 @@ "homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/workflow-aggregate", "devDependencies": { "antd": "5.x", + "mathjs": "^10.6.0", "react": "18.x", "react-i18next": "^11.15.1" }, diff --git a/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/AggregateInstruction.tsx b/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/AggregateInstruction.tsx index 0db525c3a5..d4dd846fea 100644 --- a/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/AggregateInstruction.tsx +++ b/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/AggregateInstruction.tsx @@ -338,9 +338,9 @@ export default class extends Instruction { properties: { distinct: { type: 'boolean', - title: `{{t("Distinct", { ns: "${NAMESPACE}" })}}`, 'x-decorator': 'FormItem', 'x-component': 'Checkbox', + 'x-content': `{{t("Distinct", { ns: "${NAMESPACE}" })}}`, 'x-reactions': [ { dependencies: ['collection', 'aggregator'], diff --git a/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/__e2e__/DataOfCollection.test.ts b/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/__e2e__/DataOfCollection.test.ts index 7762d6860b..492bfd181e 100644 --- a/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/__e2e__/DataOfCollection.test.ts +++ b/packages/plugins/@nocobase/plugin-workflow-aggregate/src/client/__e2e__/DataOfCollection.test.ts @@ -7,6 +7,7 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ +import { round } from 'mathjs'; import { faker } from '@faker-js/faker'; import { AggregateNode, @@ -350,7 +351,7 @@ test.describe('filter', () => { aggregateNodeCollectionData.reduce((total, currentValue) => { return currentValue.staffnum > 3 ? total + currentValue.staffnum : total; }, 0) / aggregateNodeCollectionDataCount; - expect(aggregateNodeJobResult).toBe(aggregateNodeCollectionDataAvg); + expect(aggregateNodeJobResult).toBe(round(aggregateNodeCollectionDataAvg, 14)); // 4、后置处理:删除工作流 await apiDeleteWorkflow(workflowId); }); diff --git a/packages/plugins/@nocobase/plugin-workflow-aggregate/src/server/AggregateInstruction.ts b/packages/plugins/@nocobase/plugin-workflow-aggregate/src/server/AggregateInstruction.ts index fb7d665c92..66423ce090 100644 --- a/packages/plugins/@nocobase/plugin-workflow-aggregate/src/server/AggregateInstruction.ts +++ b/packages/plugins/@nocobase/plugin-workflow-aggregate/src/server/AggregateInstruction.ts @@ -7,6 +7,8 @@ * For more information, please refer to: https://www.nocobase.com/agreement. */ +import { round } from 'mathjs'; + import { parseCollectionName } from '@nocobase/data-source-manager'; import { DataTypes } from '@nocobase/database'; import { Processor, Instruction, JOB_STATUS, FlowNodeModel } from '@nocobase/plugin-workflow'; @@ -47,7 +49,7 @@ export default class extends Instruction { }); return { - result: options.dataType === DataTypes.DOUBLE ? Number(result) : result, + result: round(options.dataType === DataTypes.DOUBLE ? Number(result) : result, 14), status: JOB_STATUS.RESOLVED, }; } diff --git a/packages/plugins/@nocobase/plugin-workflow-aggregate/src/server/__tests__/instruction.test.ts b/packages/plugins/@nocobase/plugin-workflow-aggregate/src/server/__tests__/instruction.test.ts index b2e76cf15d..b5cd69a8d6 100644 --- a/packages/plugins/@nocobase/plugin-workflow-aggregate/src/server/__tests__/instruction.test.ts +++ b/packages/plugins/@nocobase/plugin-workflow-aggregate/src/server/__tests__/instruction.test.ts @@ -48,7 +48,7 @@ describe('workflow > instructions > aggregate', () => { afterEach(() => app.destroy()); describe('based on collection', () => { - it('count', async () => { + it('count with data matched', async () => { const n1 = await workflow.createNode({ type: 'aggregate', config: { @@ -69,6 +69,30 @@ describe('workflow > instructions > aggregate', () => { expect(job.result).toBe(1); }); + it('count without data matched', async () => { + const n1 = await workflow.createNode({ + type: 'aggregate', + config: { + aggregator: 'count', + collection: 'posts', + params: { + field: 'id', + filter: { + id: 0, + }, + }, + }, + }); + + const post = await PostRepo.create({ values: { title: 't1' } }); + + await sleep(500); + + const [execution] = await workflow.getExecutions(); + const [job] = await execution.getJobs(); + expect(job.result).toBe(0); + }); + it('sum', async () => { const n1 = await workflow.createNode({ type: 'aggregate', @@ -98,6 +122,64 @@ describe('workflow > instructions > aggregate', () => { expect(j2.result).toBe(3); }); + it('sum double field', async () => { + const n1 = await workflow.createNode({ + type: 'aggregate', + config: { + aggregator: 'sum', + collection: 'posts', + params: { + field: 'score', + }, + }, + }); + + const p1 = await PostRepo.create({ values: { title: 't1', score: 0.1 } }); + + await sleep(500); + + const [e1] = await workflow.getExecutions(); + const [j1] = await e1.getJobs(); + expect(j1.result).toBe(0.1); + + const p2 = await PostRepo.create({ values: { title: 't2', score: 0.2 } }); + + await sleep(500); + + const [e2] = await workflow.getExecutions({ order: [['id', 'desc']] }); + const [j2] = await e2.getJobs(); + expect(j2.result).toBe(0.3); + }); + + it('sum number will be rounded', async () => { + const n1 = await workflow.createNode({ + type: 'aggregate', + config: { + aggregator: 'sum', + collection: 'posts', + params: { + field: 'score', + }, + }, + }); + + const p1 = await PostRepo.create({ values: { title: 't1', score: 0.100000000000001 } }); + + await sleep(500); + + const [e1] = await workflow.getExecutions(); + const [j1] = await e1.getJobs(); + expect(j1.result).toBe(0.1); + + const p2 = await PostRepo.create({ values: { title: 't2', score: 0.200000000000001 } }); + + await sleep(500); + + const [e2] = await workflow.getExecutions({ order: [['id', 'desc']] }); + const [j2] = await e2.getJobs(); + expect(j2.result).toBe(0.3); + }); + it('avg', async () => { const n1 = await workflow.createNode({ type: 'aggregate', diff --git a/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2ePageObjectModel.ts b/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2ePageObjectModel.ts index 1d43f4ae14..30cdac5ffa 100644 --- a/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2ePageObjectModel.ts +++ b/packages/plugins/@nocobase/plugin-workflow-test/src/e2e/e2ePageObjectModel.ts @@ -571,7 +571,7 @@ export class AggregateNode { .getByLabel('block-item-FieldsSelect-workflows-Field to aggregate') .locator('.ant-select-selection-search-input'); this.distinctCheckBox = page - .getByLabel('block-item-Checkbox-workflows-Distinct') + .getByLabel('block-item-Checkbox-workflows') .locator('input.ant-checkbox-input[type="checkbox"]'); this.submitButton = page.getByLabel('action-Action-Submit-workflows'); this.cancelButton = page.getByLabel('action-Action-Cancel-workflows'); diff --git a/packages/plugins/@nocobase/plugin-workflow-test/src/server/collections/posts.ts b/packages/plugins/@nocobase/plugin-workflow-test/src/server/collections/posts.ts index 17b6d41fed..f24ca4f809 100644 --- a/packages/plugins/@nocobase/plugin-workflow-test/src/server/collections/posts.ts +++ b/packages/plugins/@nocobase/plugin-workflow-test/src/server/collections/posts.ts @@ -45,6 +45,11 @@ export default { name: 'read', defaultValue: 0, }, + { + type: 'double', + name: 'score', + defaultValue: 0, + }, { type: 'date', name: 'createdAt',