/** * 业务流程缺字段补齐(2026-08-06) * - 发放明细 1974:实发/回盘结果 * - 区划 1965:level/code/entrust_plan_code * - 流程 1967:待补发台账结构化字段 * * 用法:node migrate-missing-biz-fields.js */ const http = require("http"); const qs = require("querystring"); const fs = require("fs"); const path = require("path"); const HOST = "121.43.55.7"; const PORT = 2101; function post(urlPath, fields, token) { return new Promise((resolve, reject) => { const body = qs.stringify(fields || {}); const headers = { "Content-Type": "application/x-www-form-urlencoded", "Content-Length": Buffer.byteLength(body), }; if (token) { headers.Token = token; headers.token = token; } const req = http.request( { hostname: HOST, port: PORT, path: urlPath, method: "POST", headers }, (res) => { let data = ""; res.on("data", (c) => (data += c)); res.on("end", () => { try { resolve(JSON.parse(data)); } catch (e) { resolve({ raw: data, status: res.statusCode }); } }); } ); req.on("error", reject); req.write(body); req.end(); }); } function text(shortName, alias, must, seq) { return { alias, customType: "", describe: "格式为富文本", frontType: "content", id: 2, must: !!must, name: "c_" + shortName, searchType: "2", sequence: seq || 1, showParam: "name,alias,desc,type,front_type,must,default_value", type: "text", }; } function num(shortName, alias, must, seq) { return { alias, customType: "", describe: "number", frontType: "float_num", id: 6, must: !!must, name: "c_" + shortName, searchType: "1,3", sequence: seq || 5, showParam: "name,alias,desc,type,front_type,must,default_value", sortType: "1,2", type: "double", }; } function ts(shortName, alias, must, seq) { return { alias, customType: "", describe: "时间戳", frontType: "date_time", id: 3, must: !!must, name: "c_" + shortName, searchType: "3", sequence: seq || 2, showParam: "name,alias,describe,type,front_type,must,date_type,date_picker", sortType: "1,2", type: "timestamp", }; } function normalizeFieldList(raw) { let fl = raw; if (typeof fl === "string") fl = JSON.parse(fl); const out = {}; Object.keys(fl || {}).forEach((k) => { let v = fl[k]; if (typeof v === "string") v = JSON.parse(v); out[k] = v; }); return out; } async function getModel(token, modelId) { const res = await post("/proxy_dms/model/getModelById", { modelId: String(modelId) }, token); if (res.code != 200) throw new Error("getModel " + modelId + " fail: " + JSON.stringify(res)); return res.content; } async function updateFieldList(token, modelId, addMap) { const model = await getModel(token, modelId); const fieldList = normalizeFieldList(model.fieldList); if (Object.keys(fieldList).length < 2) { throw new Error("model " + modelId + " fieldList 异常,拒绝覆盖"); } const addfieldList = {}; Object.keys(addMap || {}).forEach((shortName) => { const full = "c_" + shortName; if (fieldList[full]) return; const p = addMap[shortName]; p.name = full; fieldList[full] = p; addfieldList[shortName] = { alias: p.alias, type: p.type, frontType: p.frontType, must: p.must, name: shortName, describe: p.describe, searchType: p.searchType, sortType: p.sortType, sequence: p.sequence, showParam: p.showParam, customType: p.customType || "", id: p.id, }; }); if (Object.keys(addfieldList).length === 0) { return { modelId, modelName: model.modelName, code: 200, message: "无需新增", added: [], fields: Object.keys(fieldList).sort(), }; } const res = await post( "/proxy_dms/model/updateFieldList", { modelId: String(modelId), fieldList: JSON.stringify(fieldList), addfieldList: JSON.stringify(addfieldList), delfieldList: JSON.stringify({}), updatefieldList: JSON.stringify({}), }, token ); const verify = await getModel(token, modelId); return { modelId, modelName: model.modelName, code: res.code, message: res.message || res.content, added: Object.keys(addfieldList), fields: Object.keys(normalizeFieldList(verify.fieldList)).sort(), }; } function remarkTag(remarks, key) { if (!remarks || !key) return null; const re = new RegExp("(?:^|[|;])" + key + "=([^|;]+)"); const m = String(remarks).match(re); return m ? m[1] : null; } async function backfillRegions(token) { const list = await post( "/proxy_dms/content/selectContentList", { columnId: "1842", modelId: "1965", page: "0", pageSize: "200", states: "0" }, token ); const rows = (list.content && list.content.data) || []; let updated = 0; for (const row of rows) { const remarks = row.c_remarks || ""; const code = row.c_code || remarkTag(remarks, "code"); const level = row.c_level || remarkTag(remarks, "level"); const plan = row.c_entrust_plan_code || remarkTag(remarks, "plan") || remarkTag(remarks, "entrust"); if (!code && !level && !plan) continue; if (row.c_code && row.c_level && (!plan || row.c_entrust_plan_code)) continue; const content = { id: row.id, c_code: code || row.c_code || "", c_level: level || row.c_level || "", }; if (plan) content.c_entrust_plan_code = plan; const res = await post( "/proxy_dms/content/updateContent", { columnId: "1842", modelId: "1965", content: JSON.stringify(content) }, token ); if (res.code == 200) updated++; } return { total: rows.length, updated }; } async function backfillPaymentReturns(token) { const list = await post( "/proxy_dms/content/selectContentList", { columnId: "1849", modelId: "1974", page: "0", pageSize: "200", states: "0" }, token ); const rows = (list.content && list.content.data) || []; let updated = 0; for (const row of rows) { if (row.c_return_success != null && row.c_return_success !== "") continue; const remarks = String(row.c_remarks || ""); const fail = /回盘失败|RETURN_FAIL/.test(remarks); const ok = /回盘成功/.test(remarks); if (!fail && !ok) continue; const total = row.c_total_amount != null ? Number(row.c_total_amount) : 0; const content = { id: row.id, c_return_success: fail ? "false" : "true", c_actual_amount: fail ? 0 : total, c_return_result: fail ? "失败" : "成功", }; const m = remarks.match(/回盘失败\|([^|]*)/); if (fail && m && m[1] && /^CODE_/.test(m[1])) { content.c_fail_reason_code = m[1]; } const res = await post( "/proxy_dms/content/updateContent", { columnId: "1849", modelId: "1974", content: JSON.stringify(content) }, token ); if (res.code == 200) updated++; } return { total: rows.length, updated }; } (async () => { const login = await post("/proxy_oauth/user/login", { userName: "user_liu", password: "WE176852439@lmx", clientId: "1", }); if (login.code != 200) throw new Error("login fail " + JSON.stringify(login)); const token = login.message; console.log("login ok"); const results = []; results.push( await updateFieldList(token, 1974, { actual_amount: num("actual_amount", "实发金额", false, 30), return_success: text("return_success", "回盘是否成功", false, 31), return_result: text("return_result", "回盘结果", false, 32), fail_reason_code: text("fail_reason_code", "回盘失败原因码", false, 33), }) ); console.log("ffmx", results[results.length - 1]); results.push( await updateFieldList(token, 1965, { level: text("level", "区划层级", false, 10), code: text("code", "区划业务码", false, 11), entrust_plan_code: text("entrust_plan_code", "委托方案代码", false, 12), }) ); console.log("xzqh", results[results.length - 1]); results.push( await updateFieldList(token, 1967, { personnel_name: text("personnel_name", "人员姓名", false, 40), id_number: text("id_number", "身份证号", false, 41), source_payment_id: text("source_payment_id", "来源发放明细ID", false, 42), source_pay_month: text("source_pay_month", "来源发放年月", false, 43), fail_reason_code: text("fail_reason_code", "失败原因码", false, 44), alert_id: text("alert_id", "关联告警ID", false, 45), reissue_batch_id: text("reissue_batch_id", "补发批次ID", false, 46), reissue_payment_id: text("reissue_payment_id", "补发明细ID", false, 47), reissue_time: ts("reissue_time", "补发时间", false, 48), }) ); console.log("lcb", results[results.length - 1]); const regionBackfill = await backfillRegions(token); console.log("region backfill", regionBackfill); const payBackfill = await backfillPaymentReturns(token); console.log("payment return backfill", payBackfill); const out = { results, regionBackfill, payBackfill }; const outPath = path.join(__dirname, "migrate-missing-biz-fields-result.json"); fs.writeFileSync(outPath, JSON.stringify(out, null, 2), "utf8"); console.log("DONE ->", outPath); const bad = results.filter((r) => r.code != 200); if (bad.length) process.exit(1); for (const r of results) { for (const a of r.added || []) { if (!r.fields.includes("c_" + a)) { console.error("missing after add", r.modelId, a); process.exit(1); } } } })().catch((e) => { console.error(e); process.exit(1); });