mirror of
https://gitee.com/nocobase/nocobase.git
synced 2025-05-05 13:39:24 +08:00
Merge branch 'main' into next
This commit is contained in:
commit
ea64d975ed
@ -22,7 +22,7 @@ export default evaluate.bind(function (expression: string, scope = {}) {
|
|||||||
if (Number.isNaN(result) || !Number.isFinite(result)) {
|
if (Number.isNaN(result) || !Number.isFinite(result)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return round(result, 9);
|
return round(result, 14);
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}, {});
|
}, {});
|
||||||
|
@ -18,7 +18,7 @@ export default evaluate.bind(
|
|||||||
if (Number.isNaN(result) || !Number.isFinite(result)) {
|
if (Number.isNaN(result) || !Number.isFinite(result)) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return math.round(result, 9);
|
return math.round(result, 14);
|
||||||
}
|
}
|
||||||
if (result instanceof math.Matrix) {
|
if (result instanceof math.Matrix) {
|
||||||
return result.toArray();
|
return result.toArray();
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/workflow-aggregate",
|
"homepage.zh-CN": "https://docs-cn.nocobase.com/handbook/workflow-aggregate",
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"antd": "5.x",
|
"antd": "5.x",
|
||||||
|
"mathjs": "^10.6.0",
|
||||||
"react": "18.x",
|
"react": "18.x",
|
||||||
"react-i18next": "^11.15.1"
|
"react-i18next": "^11.15.1"
|
||||||
},
|
},
|
||||||
|
@ -338,9 +338,9 @@ export default class extends Instruction {
|
|||||||
properties: {
|
properties: {
|
||||||
distinct: {
|
distinct: {
|
||||||
type: 'boolean',
|
type: 'boolean',
|
||||||
title: `{{t("Distinct", { ns: "${NAMESPACE}" })}}`,
|
|
||||||
'x-decorator': 'FormItem',
|
'x-decorator': 'FormItem',
|
||||||
'x-component': 'Checkbox',
|
'x-component': 'Checkbox',
|
||||||
|
'x-content': `{{t("Distinct", { ns: "${NAMESPACE}" })}}`,
|
||||||
'x-reactions': [
|
'x-reactions': [
|
||||||
{
|
{
|
||||||
dependencies: ['collection', 'aggregator'],
|
dependencies: ['collection', 'aggregator'],
|
||||||
|
@ -7,6 +7,7 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { round } from 'mathjs';
|
||||||
import { faker } from '@faker-js/faker';
|
import { faker } from '@faker-js/faker';
|
||||||
import {
|
import {
|
||||||
AggregateNode,
|
AggregateNode,
|
||||||
@ -350,7 +351,7 @@ test.describe('filter', () => {
|
|||||||
aggregateNodeCollectionData.reduce((total, currentValue) => {
|
aggregateNodeCollectionData.reduce((total, currentValue) => {
|
||||||
return currentValue.staffnum > 3 ? total + currentValue.staffnum : total;
|
return currentValue.staffnum > 3 ? total + currentValue.staffnum : total;
|
||||||
}, 0) / aggregateNodeCollectionDataCount;
|
}, 0) / aggregateNodeCollectionDataCount;
|
||||||
expect(aggregateNodeJobResult).toBe(aggregateNodeCollectionDataAvg);
|
expect(aggregateNodeJobResult).toBe(round(aggregateNodeCollectionDataAvg, 14));
|
||||||
// 4、后置处理:删除工作流
|
// 4、后置处理:删除工作流
|
||||||
await apiDeleteWorkflow(workflowId);
|
await apiDeleteWorkflow(workflowId);
|
||||||
});
|
});
|
||||||
|
@ -7,6 +7,8 @@
|
|||||||
* For more information, please refer to: https://www.nocobase.com/agreement.
|
* For more information, please refer to: https://www.nocobase.com/agreement.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import { round } from 'mathjs';
|
||||||
|
|
||||||
import { parseCollectionName } from '@nocobase/data-source-manager';
|
import { parseCollectionName } from '@nocobase/data-source-manager';
|
||||||
import { DataTypes } from '@nocobase/database';
|
import { DataTypes } from '@nocobase/database';
|
||||||
import { Processor, Instruction, JOB_STATUS, FlowNodeModel } from '@nocobase/plugin-workflow';
|
import { Processor, Instruction, JOB_STATUS, FlowNodeModel } from '@nocobase/plugin-workflow';
|
||||||
@ -47,7 +49,7 @@ export default class extends Instruction {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return {
|
return {
|
||||||
result: options.dataType === DataTypes.DOUBLE ? Number(result) : result,
|
result: round(options.dataType === DataTypes.DOUBLE ? Number(result) : result, 14),
|
||||||
status: JOB_STATUS.RESOLVED,
|
status: JOB_STATUS.RESOLVED,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -48,7 +48,7 @@ describe('workflow > instructions > aggregate', () => {
|
|||||||
afterEach(() => app.destroy());
|
afterEach(() => app.destroy());
|
||||||
|
|
||||||
describe('based on collection', () => {
|
describe('based on collection', () => {
|
||||||
it('count', async () => {
|
it('count with data matched', async () => {
|
||||||
const n1 = await workflow.createNode({
|
const n1 = await workflow.createNode({
|
||||||
type: 'aggregate',
|
type: 'aggregate',
|
||||||
config: {
|
config: {
|
||||||
@ -69,6 +69,30 @@ describe('workflow > instructions > aggregate', () => {
|
|||||||
expect(job.result).toBe(1);
|
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 () => {
|
it('sum', async () => {
|
||||||
const n1 = await workflow.createNode({
|
const n1 = await workflow.createNode({
|
||||||
type: 'aggregate',
|
type: 'aggregate',
|
||||||
@ -98,6 +122,64 @@ describe('workflow > instructions > aggregate', () => {
|
|||||||
expect(j2.result).toBe(3);
|
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 () => {
|
it('avg', async () => {
|
||||||
const n1 = await workflow.createNode({
|
const n1 = await workflow.createNode({
|
||||||
type: 'aggregate',
|
type: 'aggregate',
|
||||||
|
@ -571,7 +571,7 @@ export class AggregateNode {
|
|||||||
.getByLabel('block-item-FieldsSelect-workflows-Field to aggregate')
|
.getByLabel('block-item-FieldsSelect-workflows-Field to aggregate')
|
||||||
.locator('.ant-select-selection-search-input');
|
.locator('.ant-select-selection-search-input');
|
||||||
this.distinctCheckBox = page
|
this.distinctCheckBox = page
|
||||||
.getByLabel('block-item-Checkbox-workflows-Distinct')
|
.getByLabel('block-item-Checkbox-workflows')
|
||||||
.locator('input.ant-checkbox-input[type="checkbox"]');
|
.locator('input.ant-checkbox-input[type="checkbox"]');
|
||||||
this.submitButton = page.getByLabel('action-Action-Submit-workflows');
|
this.submitButton = page.getByLabel('action-Action-Submit-workflows');
|
||||||
this.cancelButton = page.getByLabel('action-Action-Cancel-workflows');
|
this.cancelButton = page.getByLabel('action-Action-Cancel-workflows');
|
||||||
|
@ -45,6 +45,11 @@ export default {
|
|||||||
name: 'read',
|
name: 'read',
|
||||||
defaultValue: 0,
|
defaultValue: 0,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
type: 'double',
|
||||||
|
name: 'score',
|
||||||
|
defaultValue: 0,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
type: 'date',
|
type: 'date',
|
||||||
name: 'createdAt',
|
name: 'createdAt',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user