advance_audit_order.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384
  1. 'use strict';
  2. // 先手动增加 audit_order、audit_type 字段。
  3. // node db_script/bak/advance_audit_order.js uat [--tid=标段ID] [--vid=预付款ID] [--apply]
  4. // 默认预览,--apply 写入;逐期逐轮按 order 排序、按人员去重回填。
  5. const common = require('../../app/const/audit').auditType.key.common;
  6. function buildUpdates(rows) {
  7. const sorted = rows.slice().sort((a, b) => a.order - b.order || a.id - b.id);
  8. const ids = [];
  9. sorted.forEach(row => { if (!ids.includes(row.audit_id)) ids.push(row.audit_id); });
  10. if (sorted.some(row => (row.audit_order > 0 && row.audit_order !== ids.indexOf(row.audit_id) + 1) ||
  11. (row.audit_type > 0 && row.audit_type !== common))) {
  12. return { skip: '已有节点序号或审批类型冲突,请人工核对' };
  13. }
  14. return { updates: sorted.filter(row => !(row.audit_order > 0) || !(row.audit_type > 0)).map(row => ({
  15. id: row.id, audit_id: row.audit_id,
  16. before: { audit_order: row.audit_order, audit_type: row.audit_type },
  17. audit_order: ids.indexOf(row.audit_id) + 1, audit_type: common,
  18. })) };
  19. }
  20. async function main() {
  21. const env = process.argv[2];
  22. if (!['local', 'uat', 'default'].includes(env)) throw new Error('环境仅支持 local / uat / default');
  23. const filters = {};
  24. let apply = false;
  25. for (const arg of process.argv.slice(3)) {
  26. if (arg === '--apply') { apply = true; continue; }
  27. const match = /^--(vid|tid)=(\d+)$/.exec(arg);
  28. if (!match || !Number.isSafeInteger(Number(match[2])) || Number(match[2]) <= 0) throw new Error('无效参数:' + arg);
  29. filters[match[1]] = Number(match[2]);
  30. }
  31. const mysql = require('mysql');
  32. // 使用与 baseUtils 相同的配置入口,独立连接确保每轮更新处于同一事务。
  33. const path = require('path');
  34. const root = path.resolve(__dirname, '../..');
  35. const config = require(path.join(root, 'config/config.' + env))({ baseDir: root, root, name: 'calc' });
  36. const connection = mysql.createConnection(config.mysql.client);
  37. const query = (sql, params = []) => new Promise((resolve, reject) => {
  38. connection.query(sql, params, (err, rows) => err ? reject(err) : resolve(rows));
  39. });
  40. const log = data => {
  41. console.log(JSON.stringify(data));
  42. };
  43. try {
  44. const columns = await query('SHOW COLUMNS FROM zh_advance_audit');
  45. if (!['audit_order', 'audit_type'].every(name => columns.some(x => x.Field === name))) {
  46. throw new Error('请先为 zh_advance_audit 增加 audit_order 和 audit_type 字段');
  47. }
  48. log({ env, apply, filters });
  49. const where = ['1 = 1'], params = [];
  50. for (const key of ['tid', 'vid']) {
  51. if (filters[key]) { where.push(key + ' = ?'); params.push(filters[key]); }
  52. }
  53. const rounds = await query('SELECT DISTINCT tid, vid, times FROM zh_advance_audit WHERE ' + where.join(' AND ') + ' ORDER BY tid, vid, times', params);
  54. let updated = 0;
  55. for (const round of rounds) {
  56. await query('START TRANSACTION');
  57. try {
  58. const rows = await query('SELECT id, audit_id, `order`, audit_order, audit_type FROM zh_advance_audit WHERE tid = ? AND vid = ? AND times = ? ORDER BY `order`, id FOR UPDATE', [round.tid, round.vid, round.times]);
  59. const plan = buildUpdates(rows);
  60. log({ phase: 'plan', ...round, ...plan });
  61. if (!apply || plan.skip) { await query('ROLLBACK'); continue; }
  62. for (const row of plan.updates) {
  63. const result = await query('UPDATE zh_advance_audit SET audit_order = ?, audit_type = ? WHERE id = ?', [row.audit_order, row.audit_type, row.id]);
  64. if (result.affectedRows !== 1) throw new Error('回填失败:' + row.id);
  65. }
  66. await query('COMMIT');
  67. updated += plan.updates.length;
  68. log({ phase: 'committed', ...round, updated: plan.updates.length });
  69. } catch (err) {
  70. await query('ROLLBACK');
  71. throw err;
  72. }
  73. }
  74. log({ phase: 'done', rounds: rounds.length, updated });
  75. } finally {
  76. connection.end();
  77. }
  78. }
  79. module.exports = { buildUpdates };
  80. if (require.main === module) main().catch(err => { console.error(err); process.exitCode = 1; });