'use strict'; // 独立回归测试:node --test test/app/service/advance_audit_order.test.js(无需连接数据库) const { test } = require('node:test'); const assert = require('node:assert/strict'); const _ = require('lodash'); const status = require('../../../app/const/audit').advance.status; const Service = require('../../../app/service/advance_audit')({ BaseService: class {} }); const { buildUpdates } = require('../../../db_script/bak/advance_audit_order'); function fixture(rows) { const service = new Service(); service._ = _; service.initSqlBuilder = () => { service.sqlBuilder = new (require('../../../app/lib/sql_builder'))(); }; const advance = { id: 1, tid: 2, times: 1, type: 1, uid: 99, order: 1, cur_amount: 0 }; let records = rows.map(x => ({ vid: 1, tid: 2, times: 1, type: 1, audit_type: 1, ...x })); const tx = { select: async (table, options) => _.orderBy(records.filter(x => _.isMatch(x, options.where)), options.orders.map(x => x[0]), options.orders.map(x => x[1])).map(x => ({ ...x })), insert: async (table, data) => { if (table === 'advance_audit') for (const row of [].concat(data)) records.push({ id: Math.max(0, ...records.map(x => x.id)) + 1, ...row }); return { affectedRows: 1 }; }, update: async (table, row) => { if (table === 'advance_audit') Object.assign(records.find(x => x.id === row.id), row); }, delete: async (table, where) => { records = records.filter(x => !_.isMatch(x, where)); }, query: async (sql, params) => { assert(params.includes('times'), '退回顺移必须限定本轮'); }, commit: async () => {}, rollback: async () => {}, }; service.db = { beginTransaction: async () => tx, query: async sql => { assert(sql.includes('`audit_order`') && sql.includes('`audit_type`')); return _.uniqBy(_.sortBy(records, 'audit_order'), 'audit_order').map(x => _.pick(x, ['tid', 'vid', 'type', 'audit_id', 'audit_order', 'audit_type'])); } }; service.getAllDataByCondition = async options => records.filter(x => _.isMatch(x, options.where)); service.getDataByCondition = async where => records.find(x => _.isMatch(x, where)); service.getAuditors = async () => _.sortBy(records, 'order').map(x => ({ ...x })); service._syncOrderByDelete = async (transaction, vid, order, times, operation, auditOrder) => { for (const x of records.filter(x => x.vid === vid && x.times === times)) { if (x.order >= order) x.order += operation === '+' ? 1 : -1; if (x.audit_order >= auditOrder) x.audit_order += operation === '+' ? 1 : -1; } }; service.getNoticeContent = async () => ''; service.ctx = { advance, tender: { id: 2, info: { shenpi: { advance: 1 } } }, session: { sessionProject: { id: 1 }, sessionUser: { accountId: 30 } }, helper: { urlToShort: async () => '', sendWechat: async () => {} }, service: { projectAccount: { getDataById: async () => ({ enable: 1, project_id: 1 }) }, advance: { tableName: 'advance', getDataById: async () => advance }, noticePush: { tableName: 'notice' }, noticeAgain: { stopNoticeAgain: async () => {}, addNoticeAgain: async () => {} }, specMsg: { addAdvanceMsg: async () => {} } } }; return { service, advance, records: () => records }; } test('中途插人后退回原报,保留插入位置和原终审', async () => { const f = fixture([{ id: 1, audit_id: 10, order: 1, audit_order: 1, status: status.checking }, { id: 2, audit_id: 30, order: 2, audit_order: 2, status: status.uncheck }]); await f.service.saveAudit(f.advance, { operate: 'add', old_aid: 10, new_aid: 20 }); assert.deepEqual(_.sortBy(f.records(), 'order').map(x => [x.audit_id, x.audit_order]), [[10, 1], [20, 2], [30, 3]]); await f.service._checkNo(1, 1, { checkType: status.checkNo, opinion: '' }, 1); assert.deepEqual(f.records().filter(x => x.times === 2).map(x => [x.audit_id, x.order, x.audit_order, x.audit_type]), [[10, 1, 1, 1], [20, 2, 2, 1], [30, 3, 3, 1]]); }); test('过程顺序与节点不同,插人分别移动两种序号,管理员取最新节点', async () => { const f = fixture([{ id: 1, audit_id: 10, order: 1, audit_order: 1, status: status.checked }, { id: 2, audit_id: 10, order: 4, audit_order: 1, status: status.checking }, { id: 3, audit_id: 30, order: 5, audit_order: 2, status: status.uncheck }]); await f.service.saveAudit(f.advance, { operate: 'add', old_aid: 10, new_aid: 20 }); assert.deepEqual(_.sortBy(f.records(), 'order').map(x => [x.order, x.audit_order]), [[1, 1], [4, 1], [5, 2], [6, 3]]); assert.deepEqual((await f.service.getAuditGroupByList(1, 1)).map(x => x.audit_id), [10, 20, 30]); }); test('顺移SQL使用独立边界并限定轮次', async () => { const s = new Service(), calls = []; s.db = { escape: x => x }; s.initSqlBuilder = () => { s.sqlBuilder = new (require('../../../app/lib/sql_builder'))(); }; await s._syncOrderByDelete({ query: async (...args) => calls.push(args) }, 1, 7, 2, '+', 3); assert(calls[0][0].includes('>= 7') && calls[0][0].includes('= 2')); assert.deepEqual(calls[1][1], ['advance_audit', 1, 2, 3]); }); test('退回上一审只追加过程记录,节点序号保持不变', async () => { const f = fixture([{ id: 1, audit_id: 10, order: 1, audit_order: 1, status: status.checked }, { id: 2, audit_id: 30, order: 2, audit_order: 2, status: status.checking }]); await f.service._checkNoPre(1, 1, { checkType: status.checkNoPre, opinion: '' }, 1, 1); assert.deepEqual(f.records().slice(2).map(x => [x.audit_id, x.order, x.audit_order, x.audit_type]), [[10, 3, 1, 1], [30, 4, 2, 1]]); }); test('终审重新审批,新增两条过程记录沿用终审节点', async () => { const f = fixture([{ id: 1, audit_id: 10, order: 1, audit_order: 1, status: status.checked }, { id: 2, audit_id: 30, order: 6, audit_order: 2, status: status.checked }]); f.advance.auditors = f.records(); assert.equal(await f.service.checkAgain(f.advance), true); assert.deepEqual(f.records().slice(2).map(x => [x.order, x.audit_order, x.audit_type]), [[7, 2, 1], [8, 2, 1]]); }); test('历史回填去重且可重复运行,冲突轮次跳过', () => { const rows = [{ id: 1, audit_id: 10, order: 1 }, { id: 2, audit_id: 30, order: 3 }, { id: 3, audit_id: 20, order: 2 }, { id: 4, audit_id: 10, order: 4 }]; const updates = buildUpdates(rows).updates; assert.deepEqual(updates.map(x => x.audit_order), [1, 2, 3, 1]); assert.equal(buildUpdates(rows.map(x => ({ ...x, ...updates.find(y => y.id === x.id) }))).updates.length, 0); assert(buildUpdates([{ ...rows[0], audit_order: 5 }]).skip); }); test('普通增加固定终审前人员、删除及替换同步节点', async () => { const f = fixture([{ id: 1, audit_id: 10, order: 1, audit_order: 1, status: status.uncheck }, { id: 2, audit_id: 30, order: 2, audit_order: 2, status: status.uncheck }]); await f.service.addAuditor(2, 1, 20, 1, 1, 1); assert.deepEqual(_.sortBy(f.records(), 'audit_order').map(x => x.audit_id), [10, 20, 30]); await f.service.saveAudit(f.advance, { operate: 'change', old_aid: 20, new_aid: 40 }); assert.equal(f.records().find(x => x.audit_id === 40).audit_order, 2); await f.service.deleteAuditor(1, 40, 1); assert.deepEqual(_.sortBy(f.records(), 'audit_order').map(x => [x.audit_id, x.order, x.audit_order]), [[10, 1, 1], [30, 2, 2]]); });