// 一次性脚本:把课题管理模块(yudao-module-research)的补丁并入 config_export_all_20260928.sql 的阶段一 const fs = require('node:fs'); const path = require('node:path'); const dir = __dirname; const TARGET = path.join(dir, 'config_export_all_20260928.sql'); const BPM = path.join(dir, '_research_bpm_export.sql'); const MODULE = path.join(dir, 'research_module.sql'); const EOL = '\r\n'; let doc = fs.readFileSync(TARGET, 'utf8'); const bpmLines = fs.readFileSync(BPM, 'utf8').split(/\r?\n/); const moduleLines = fs.readFileSync(MODULE, 'utf8').split(/\r?\n/); if (!doc.includes(EOL)) throw new Error('目标文件不是 CRLF'); /** 断言锚点唯一 */ function anchor(text) { const n = doc.split(text).length - 1; if (n !== 1) throw new Error(`锚点出现 ${n} 次,期望 1 次:${text.slice(0, 80)}`); } /** 在锚点前插入 */ function insertBefore(text, before) { anchor(text); doc = doc.replace(text, before + EOL + EOL + text); } /** 替换一次 */ function replaceOnce(from, to) { anchor(from); doc = doc.replace(from, to); } // ---------------------------------------------------------------- 1. 表数量 replaceOnce( '-- 范围:全库 BASE TABLE 共 423 张(0918 的 419 张 + 2 张 finance_* + 2 张 ums_*),全部输出 CREATE TABLE;', '-- 范围:全库 BASE TABLE 共 427 张(0918 的 419 张 + 2 张 finance_* + 2 张 ums_* + 4 张 research_*),全部输出 CREATE TABLE;', ); // ------------------------------------------------- 2. 前言补丁登记块 const PREAMBLE = [ '-- 补丁内容(2026-09-28,课题管理模块 yudao-module-research):', '-- ① 补 4 张 research_* 表结构(课题指南 / 课题申报书 / 专家评审明细 / 经费拨付记录,仅建空结构,无数据):', '-- research_guideline、research_application、research_topic_review、research_fund_record', '-- ② 补 5 个字典类型 + 13 条字典数据(并入阶段一的 system_dict_type / system_dict_data):', '-- research_guideline_category(重点课题/一般课题)', '-- research_guideline_status(草稿/已发布/已截止)', '-- research_application_status(草稿/审批中/已立项/已驳回)', '-- research_review_conclusion(推荐立项/不推荐立项)', '-- research_fund_status(待拨付/已拨付)', '-- ③ 补 1 条 BPM 分类(并入阶段一的 bpm_category):', '-- id=8002 \'课题立项审批\' / code=\'research_topic_approve\'', '-- ④ 补 1 条 BPM 流程定义(课题申报三级审批:秘书处形式审查→专家评审→学会立项审批),', '-- 分别落 act_re_deployment / act_re_procdef / act_ge_bytearray(3 条) / act_re_model /', '-- bpm_process_definition_info 五张表,流程 key=\'research_topic_approve\'。', '-- bpm_process_definition_info.form_type=20(自定义表单),', '-- form_custom_create_path=/research/application/index、', '-- form_custom_view_path=/research/application/detail/approve/index.vue。', '-- ⚠ 该流程的 BPMN 为手写三节点,**不是** bpm_deploy_flow_from_template 生成的单节点流程。', '-- ⑤ 补 22 条课题管理菜单:1075510「课题管理」(**顶级目录**,parent_id=0,path=/research)', '-- + 1075511「课题指南」/ 1075512「课题申报」(菜单,挂 1075510 下)', '-- + 1075520~1075524(指南查询/创建/更新/删除/发布截止)、', '-- 1075530~1075535(申报查询/创建/更新/删除/提交审批/发布立项通知)、', '-- 1075540~1075543(专家评审查询/录入/修改/删除)、', '-- 1075550~1075553(经费拨付查询/登记/修改/删除)。', '-- **菜单段本文件无需为此改动**:阶段② 的写法是 `INSERT INTO system_menu SELECT *', '-- FROM \\`mom-jssm\\`.system_menu WHERE deleted=b\'0\'`,是执行期跨库全量拷贝,', '-- 只要来源库 mom-jssm 有这几行就会被带过去 —— 与阶段一的字面量 INSERT 不同。', '-- 在此登记只为说明「这版文件确实覆盖了该变更」,避免后来者以为漏了。', '', ].join(EOL); insertBefore('-- 历史补丁内容(2026-09-18):', PREAMBLE); // --------------------------------------------- 3. 4 张 research_* 表 DDL // 从 research_module.sql 抽取 CREATE TABLE 段(第 10~30 / 34~70 / 74~96 / 100~116 行,1-based) function ddlBlock(fromLine, toLine, tableName) { const body = moduleLines.slice(fromLine - 1, toLine).join(EOL); return [ `-- ============ 表:${tableName}(仅 DDL,无数据) ============`, body, '', ].join(EOL); } const RESEARCH_DDL = [ ddlBlock(10, 30, 'research_guideline'), ddlBlock(34, 70, 'research_application'), ddlBlock(74, 96, 'research_topic_review'), ddlBlock(100, 116, 'research_fund_record'), ].join(EOL); insertBefore('-- ============ 表:yudao_demo01_contact(仅 DDL,无数据) ============', RESEARCH_DDL); // --------------------------------------------- 4. BPM 流程 INSERT(7 条语句) // _research_bpm_export.sql 的第 10~16 行(1-based) const bpmInserts = bpmLines.slice(9, 16).filter((l) => l.trim() !== ''); if (bpmInserts.length !== 7) throw new Error(`BPM INSERT 条数 ${bpmInserts.length},期望 7`); const pick = (prefix) => { const hit = bpmInserts.filter((l) => l.startsWith(prefix)); return hit; }; const insProcdef = pick('INSERT INTO `act_re_procdef`'); const insDeployment = pick('INSERT INTO `act_re_deployment`'); const insBytearray = pick('INSERT INTO `act_ge_bytearray`'); const insModel = pick('INSERT INTO `act_re_model`'); const insProcInfo = pick('INSERT INTO `bpm_process_definition_info`'); if (insProcdef.length !== 1 || insDeployment.length !== 1 || insBytearray.length !== 3 || insModel.length !== 1 || insProcInfo.length !== 1) { throw new Error('BPM INSERT 分类数量不符'); } // 4.1 act_re_procdef replaceOnce( '-- ============ 表:act_re_deployment(导出 21 行) ============', '-- ============ 表:act_re_deployment(导出 22 行) ============', ); insertBefore( '-- ============ 表:act_re_deployment(导出 22 行) ============', [...insProcdef, ''].join(EOL), ); // 4.2 act_re_deployment replaceOnce( '-- ============ 表:act_ge_bytearray(导出 22 行) ============', '-- ============ 表:act_ge_bytearray(导出 25 行) ============', ); insertBefore( '-- ============ 表:act_ge_bytearray(导出 25 行) ============', [...insDeployment, ''].join(EOL), ); // 4.3 act_ge_bytearray(bpmn / diagram / source 三条) replaceOnce( '-- ============ 表:act_re_model(导出 21 行) ============', '-- ============ 表:act_re_model(导出 22 行) ============', ); insertBefore( '-- ============ 表:act_re_model(导出 22 行) ============', [...insBytearray, ''].join(EOL), ); // 4.4 act_re_model replaceOnce( '-- ============ 表:bpm_process_definition_info(导出 21 行) ============', '-- ============ 表:bpm_process_definition_info(导出 22 行) ============', ); insertBefore( '-- ============ 表:bpm_process_definition_info(导出 22 行) ============', [...insModel, ''].join(EOL), ); // 4.5 bpm_process_definition_info(插在 ② 分组横幅之前) const BANNER2 = [ '-- ***********************************************************************', '-- ② 协同(业务附件配置 + 分类 + 表单 + 扩展配置)(DDL + 数据)', '-- ***********************************************************************', ].join(EOL); insertBefore(BANNER2, [...insProcInfo, ''].join(EOL)); // 4.6 bpm_category 补 1 行 replaceOnce( '-- ============ 表:bpm_form(导出 4 行) ============', '-- ============ 表:bpm_form(导出 4 行) ============', ); insertBefore( '-- ============ 表:bpm_form(导出 4 行) ============', [ 'INSERT INTO `bpm_category` (`id`, `name`, `code`, `description`, `status`, `sort`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)', 'VALUES (\'8002\', \'课题立项审批\', \'research_topic_approve\', \'课题申报三级审批(秘书处形式审查→专家评审→学会立项审批)\', \'0\', \'3\', \'1\', \'2026-09-28 20:00:00\', \'1\', \'2026-09-28 20:00:00\', X\'00\');', '', ].join(EOL), ); replaceOnce( '-- ============ 表:bpm_category(导出 24 行) ============', '-- ============ 表:bpm_category(导出 25 行) ============', ); // --------------------------------------- 5. 字典类型 5 条 + 字典数据 13 条 const DICT_TYPES = [ ['81000', '课题指南类别', 'research_guideline_category', '0', '重点课题/一般课题分类标注'], ['81001', '课题指南状态', 'research_guideline_status', '0', '课题指南发布状态:草稿/已发布/已截止'], ['81002', '课题申报状态', 'research_application_status', '0', '课题申报书审批状态:草稿/审批中/已立项/已驳回'], ['81003', '专家评审结论', 'research_review_conclusion', '0', '专家评审推荐立项/不推荐立项'], ['81004', '经费拨付状态', 'research_fund_status', '0', '课题经费待拨付/已拨付'], ]; const dictTypeRows = DICT_TYPES.map( ([id, name, type, status, remark]) => `('${id}', '${name}', '${type}', '${status}', '${remark}', '1', '2026-09-28 20:00:00', '1', '2026-09-28 20:00:00', X'00', NULL),`, ).join(EOL); insertBefore( '-- ============ 表:system_dict_data(导出 1192 行) ============', [ 'INSERT INTO `system_dict_type` (`id`, `name`, `type`, `status`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`, `deleted_time`)', 'VALUES', dictTypeRows.replace(/,$/, ';'), '', ].join(EOL), ); replaceOnce( '-- ============ 表:system_dict_type(导出 249 行) ============', '-- ============ 表:system_dict_type(导出 254 行) ============', ); const DICT_DATA = [ ['8100001', 1, '重点课题', '1', 'research_guideline_category', 'danger'], ['8100002', 2, '一般课题', '2', 'research_guideline_category', 'default'], ['8100101', 1, '草稿', '0', 'research_guideline_status', 'default'], ['8100102', 2, '已发布', '20', 'research_guideline_status', 'success'], ['8100103', 3, '已截止', '30', 'research_guideline_status', 'info'], ['8100201', 1, '草稿', '0', 'research_application_status', 'default'], ['8100202', 2, '审批中', '15', 'research_application_status', 'processing'], ['8100203', 3, '已立项', '20', 'research_application_status', 'success'], ['8100204', 4, '已驳回', '35', 'research_application_status', 'danger'], ['8100301', 1, '推荐立项', '1', 'research_review_conclusion', 'success'], ['8100302', 2, '不推荐立项', '2', 'research_review_conclusion', 'danger'], ['8100401', 1, '待拨付', '0', 'research_fund_status', 'warning'], ['8100402', 2, '已拨付', '20', 'research_fund_status', 'success'], ]; const dictDataRows = DICT_DATA.map( ([id, sort, label, value, type, color]) => `('${id}', '${sort}', '${label}', '${value}', '${type}', '0', '${color}', '', '', '1', '2026-09-28 20:00:00', '1', '2026-09-28 20:00:00', X'00'),`, ).join(EOL); insertBefore( '-- ============ 表:system_tenant(导出 3 行) ============', [ 'INSERT INTO `system_dict_data` (`id`, `sort`, `label`, `value`, `dict_type`, `status`, `color_type`, `css_class`, `remark`, `creator`, `create_time`, `updater`, `update_time`, `deleted`)', 'VALUES', dictDataRows.replace(/,$/, ';'), '', ].join(EOL), ); replaceOnce( '-- ============ 表:system_dict_data(导出 1192 行) ============', '-- ============ 表:system_dict_data(导出 1205 行) ============', ); // ---------------------------------------------------------------- 写出 fs.writeFileSync(TARGET, doc, 'utf8'); console.log('合并完成');