tachybase_todo/packages/plugins/math-formula-field/src/server/math-formula-field.ts

108 lines
2.8 KiB
TypeScript

import { BaseFieldOptions, Field } from '@nocobase/database';
import * as math from 'mathjs';
import { DataTypes } from 'sequelize';
import { evaluate } from '../utils/evaluate';
export class MathFormulaField extends Field {
get dataType() {
const { dataType } = this.options;
return dataType === 'string' ? DataTypes.STRING : DataTypes.DOUBLE;
}
calculate(expression, scope) {
let result: any = null;
try {
result = evaluate(expression, scope);
result = math.round(result, 9);
} catch {}
return result;
}
initFieldData = async ({ transaction }) => {
const { expression, name } = this.options;
const records = await this.collection.repository.find({
order: [this.collection.model.primaryKeyAttribute],
transaction,
});
for (const record of records) {
const scope = record.toJSON();
const result = this.calculate(expression, scope);
if (result) {
await record.update(
{
[name]: result,
},
{
transaction,
silent: true,
hooks: false,
},
);
}
}
};
calculateField = async (instance) => {
const { expression, name } = this.options;
const scope = instance.toJSON();
let result;
try {
result = evaluate(expression, scope);
result = math.round(result, 9);
} catch {}
if (result === 0 || result) {
instance.set(name, result);
}
};
updateFieldData = async (instance, { transaction }) => {
if (this.collection.name === instance.collectionName && instance.name === this.options.name) {
this.options = Object.assign(this.options, instance.options);
const { name, expression } = this.options;
const records = await this.collection.repository.find({
order: [this.collection.model.primaryKeyAttribute],
transaction,
});
for (const record of records) {
const scope = record.toJSON();
const result = this.calculate(expression, scope);
await record.update(
{
[name]: result,
},
{
transaction,
silent: true,
hooks: false,
},
);
}
}
};
bind() {
super.bind();
this.on('afterSync', this.initFieldData);
// TODO: should not depends on fields table (which is defined by other plugin)
this.database.on('fields.afterUpdate', this.updateFieldData);
this.on('beforeSave', this.calculateField);
}
unbind() {
super.unbind();
this.off('beforeSave', this.calculateField);
// TODO: should not depends on fields table
this.database.off('fields.afterUpdate', this.updateFieldData);
this.off('afterSync', this.initFieldData);
}
}
export interface MathFormulaFieldOptions extends BaseFieldOptions {
type: 'mathFormula';
expression: string;
}