UNPKG

@tachybase/plugin-workflow-approval

Version:

Approval base in Workflow

358 lines (357 loc) 14 kB
var __defProp = Object.defineProperty; var __getOwnPropDesc = Object.getOwnPropertyDescriptor; var __getOwnPropNames = Object.getOwnPropertyNames; var __hasOwnProp = Object.prototype.hasOwnProperty; var __export = (target, all) => { for (var name in all) __defProp(target, name, { get: all[name], enumerable: true }); }; var __copyProps = (to, from, except, desc) => { if (from && typeof from === "object" || typeof from === "function") { for (let key of __getOwnPropNames(from)) if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable }); } return to; }; var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod); var Approval_exports = {}; __export(Approval_exports, { default: () => ApprovalTrigger }); module.exports = __toCommonJS(Approval_exports); var import_module_workflow = require("@tachybase/module-workflow"); var import_server = require("@tego/server"); var import_lodash = require("lodash"); var import_sequelize = require("sequelize"); var import_status = require("../constants/status"); var import_tools = require("../tools"); var import_tools2 = require("./tools"); const _ApprovalTrigger = class _ApprovalTrigger extends import_module_workflow.Trigger { constructor(workflow) { super(workflow); this.sync = false; this.triggerHandler = async (approval, { transaction }) => { let workflow = await approval.getWorkflow({ where: { id: approval.get("workflowId"), enabled: true, type: _ApprovalTrigger.TYPE, "config.collection": approval.collectionName }, transaction }); if (!workflow && approval.get("workflowKey")) { workflow = await this.workflow.db.getRepository("workflows").findOne({ filter: { key: approval.get("workflowKey"), enabled: true, type: _ApprovalTrigger.TYPE, "config.collection": approval.collectionName }, transaction }); if (workflow) { await approval.update({ workflowId: workflow.id }, { transaction, hooks: false }); } } const isChangedStatus = approval.changed("status"); const isAllowStatusList = [import_status.APPROVAL_STATUS.DRAFT, import_status.APPROVAL_STATUS.SUBMITTED, import_status.APPROVAL_STATUS.RESUBMIT].includes( approval.status ); const previousApprovalStatus = approval.previous("status"); const currentApprovalStatus = approval.status; const forbiddenList = [ [import_status.APPROVAL_STATUS.SUBMITTED, import_status.APPROVAL_STATUS.DRAFT] // 撤回情况 ]; const isForbiddenWhenStatusChange = forbiddenList.some( ([prev, curr]) => prev === previousApprovalStatus && curr === currentApprovalStatus ); if (!workflow || !isChangedStatus || !isAllowStatusList || isForbiddenWhenStatusChange) { return; } const [dataSourceName, collectionName] = (0, import_server.parseCollectionName)(approval.collectionName); const dataSource = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName); const collection = dataSource.collectionManager.getCollection(collectionName); const { repository } = collection; const data = await repository.findOne({ filterByTk: approval.get("dataKey"), appends: workflow.config.appends, transaction: this.workflow.useDataSourceTransaction(dataSourceName, transaction) }); this.workflow.trigger( workflow, { data: (0, import_module_workflow.toJSON)(data), approvalId: approval.id, applicantRoleName: approval.applicantRoleName, summary: (0, import_tools.getSummary)({ summaryConfig: workflow.config.summary, data, collection, // Fix: type assertion to bypass typing error app: this.workflow.app }), collectionName: approval.collectionName }, { transaction } ); }; this.onExecutionCreate = async (execution, { transaction }) => { const workflow = await execution.getWorkflow({ transaction }); if (workflow.type !== _ApprovalTrigger.TYPE) { return; } const { approvalId, data, summary, collectionName } = execution.context; const ApprovalExecutionRepo = this.workflow.db.getRepository("approvalExecutions"); const approvalExecution = await ApprovalExecutionRepo.create({ values: { approvalId, executionId: execution.id, status: execution.status, snapshot: data, summary, collectionName }, transaction }); const ApprovalsRepo = this.workflow.db.getRepository("approvals"); await ApprovalsRepo.update({ filterByTk: approvalId, values: { latestExecutionId: approvalExecution.id }, transaction }); }; this.onExecutionUpdate = async (execution, { transaction }) => { if (!execution.workflow) { execution.workflow = await execution.getWorkflow({ transaction }); } if (execution.workflow.type === _ApprovalTrigger.TYPE && execution.context && execution.context.approvalId && execution.status === import_module_workflow.EXECUTION_STATUS.STARTED) { const approval2 = await this.workflow.db.getRepository("approvals").findOne({ filterByTk: execution.context.approvalId, transaction }); if (approval2 && approval2.status === import_status.APPROVAL_STATUS.DRAFT) { await execution.update( { status: import_module_workflow.EXECUTION_STATUS.CANCELED }, { transaction } ); return; } } if (!execution.status || execution.workflow.type !== _ApprovalTrigger.TYPE) { return; } const approvalExecution = await this.workflow.db.getRepository("approvalExecutions").findOne({ filter: { executionId: execution.id, approvalId: execution.context.approvalId }, appends: ["approval"], transaction }); const { approval } = approvalExecution; if (![import_status.APPROVAL_STATUS.SUBMITTED, import_status.APPROVAL_STATUS.PROCESSING].includes(approval.status)) { return; } let status = import_status.APPROVAL_STATUS.APPROVED; if (execution.status === import_module_workflow.EXECUTION_STATUS.RESOLVED) { const [approvalNodeJob] = await execution.getJobs({ where: { status: { [import_sequelize.Op.ne]: import_module_workflow.JOB_STATUS.PENDING } }, include: [ { association: "node", where: { type: "approval" }, required: true } ], order: [["updatedAt", "DESC"]], limit: 1, transaction }); if (approvalNodeJob && import_tools2.ApprovalJobStatusMap[approvalNodeJob.result] != null) { status = import_tools2.ApprovalJobStatusMap[approvalNodeJob.result]; } } else { if (import_tools2.ExecutionStatusMap[execution.status] != null) { status = import_tools2.ExecutionStatusMap[execution.status]; } } await approval.update({ status }, { transaction }); await approvalExecution.update({ status: execution.status }, { transaction }); }; this.middleware = async (ctx, next) => { if (!ctx.action) { return; } const { resourceName, actionName, params: { triggerWorkflows, beforeWorkflows } } = ctx.action; if (beforeWorkflows) { this.collectionTriggerAction(ctx, beforeWorkflows); } if (resourceName === "workflows" && actionName === "trigger") { return this.workflowTriggerAction(ctx, next); } await next(); if (!triggerWorkflows || !["create", "update"].includes(actionName)) { return; } this.collectionTriggerAction(ctx, triggerWorkflows); }; const { db } = workflow.app; db.on("approvals.afterSave", this.triggerHandler); db.on("executions.afterCreate", this.onExecutionCreate); db.on("executions.afterUpdate", this.onExecutionUpdate); workflow.app.use(this.middleware, { tag: "workflow-trigger", after: "dataSource" }); } async workflowTriggerAction(ctx, next) { const { triggerWorkflows, values } = ctx.action.params; if (!triggerWorkflows) { return ctx.throw(400); } const triggers = triggerWorkflows.split(",").map((trigger) => trigger.split("!")); const workflowRepo = this.workflow.db.getRepository("workflows"); const workflows = await workflowRepo.find({ filter: { type: _ApprovalTrigger.TYPE, key: triggers.map((trigger) => trigger[0]), current: true, enabled: true } }); ctx.status = 202; await next(); workflows.forEach(async (workflow) => { const trigger = triggers.find((trigger2) => trigger2[0] === workflow.key); const [dataSourceName, collectionName] = (0, import_server.parseCollectionName)(workflow.config.collection); const collecton = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName).collectionManager.getCollection(collectionName); const data = await collecton.repository.create({ values: { ...trigger[1] ? (0, import_lodash.get)(values, trigger[1]) : values // createdBy: currentUser.id, // updatedById: currentUser.id, }, context: ctx }); const approvalRepo = this.workflow.db.getRepository("approvals"); await approvalRepo.create({ values: { collectionName: workflow.config.collection, data: (0, import_module_workflow.toJSON)(data), dataKey: data.get(collecton.filterTargetKey), status: import_status.APPROVAL_STATUS.SUBMITTED, // createdBy: currentUser.id, // updatedById: currentUser.id, workflowId: workflow.id, workflowKey: workflow.key, summary: (0, import_tools.getSummary)({ summaryConfig: workflow.config.summary, data, collection: collecton, // Fix: type assertion to bypass typing error app: this.workflow.app }) }, context: ctx }); }); } async collectionTriggerAction(ctx, workflowList) { const dataSourceHeader = ctx.get("x-data-source") || "main"; const approvalRepo = this.workflow.db.getRepository("approvals"); const triggers = workflowList.split(",").map((trigger) => trigger.split("!")); const triggersKeysMap = new Map(triggers); const workflows = Array.from(this.workflow.enabledCache.values()).filter( (item) => item.type === _ApprovalTrigger.TYPE && triggersKeysMap.has(item.key) ); for (const workflow of workflows) { const [dataSourceName, collectionName] = (0, import_server.parseCollectionName)(workflow.config.collection); const trigger = triggers.find((trigger2) => trigger2[0] === workflow.key); const { body: data } = ctx; if (!data) { return; } if (dataSourceName !== dataSourceHeader) { continue; } (Array.isArray(data) ? data : [data]).forEach(async (row) => { let dataCurrent = {}; if (row.id) { const { repository } = this.workflow.app.dataSourceManager.dataSources.get(dataSourceName).collectionManager.getCollection(collectionName); dataCurrent = await repository.findOne({ filterByTk: data.id, appends: [...workflow.config.appends] }); } let payload = row; if (trigger[1]) { const paths = trigger[1].split("."); for await (const field of paths) { if (payload.get(field)) { payload = payload.get(field); } else { const association = (0, import_server.modelAssociationByKey)(payload, field); payload = await payload[association.accessors.get](); } } } const collection = ctx.tego.dataSourceManager.dataSources.get(dataSourceName).collectionManager.getCollection(collectionName); if (!collection || collection.model !== payload.constructor) { return; } await approvalRepo.create({ values: { collectionName: workflow.config.collection, data: (0, import_module_workflow.toJSON)(payload), dataKey: payload.get(collection.filterTargetKey), status: import_status.APPROVAL_STATUS.SUBMITTED, // createdBy: currentUser.id, // updatedBy: currentUser.id, workflowId: workflow.id, workflowKey: workflow.key, applicantRoleName: ctx.state.currentRole, summary: (0, import_tools.getSummary)({ summaryConfig: workflow.config.summary, data: dataCurrent, collection, app: ctx.app }) }, context: ctx }); }); } } on(workflow) { } off(workflow) { } async duplicateConfig(workflow, { transaction }) { const { db } = this.workflow; const uiSchemaRepo = db.getRepository("uiSchemas"); if (!workflow.config.applyForm) { return workflow.config; } const result = await uiSchemaRepo.duplicate(workflow.config.applyForm, { transaction }); return { ...workflow.config, applyForm: result["x-uid"] }; } }; _ApprovalTrigger.TYPE = "approval"; let ApprovalTrigger = _ApprovalTrigger;