e2e-biz-assert.js 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. /**
  2. * 业务闭环断言:写后可读、审批改状态、区划完整、看板/回盘真数据
  3. */
  4. const http = require("http");
  5. const asserts = [];
  6. function req(method, path, body) {
  7. return new Promise((resolve) => {
  8. const data = body == null ? null : JSON.stringify(body);
  9. const headers = { Accept: "application/json" };
  10. if (data) {
  11. headers["Content-Type"] = "application/json";
  12. headers["Content-Length"] = Buffer.byteLength(data);
  13. }
  14. const r = http.request(
  15. { hostname: "localhost", port: 8088, path: "/sjnmtybt" + path, method, headers },
  16. (res) => {
  17. let raw = "";
  18. res.on("data", (c) => (raw += c));
  19. res.on("end", () => {
  20. try {
  21. resolve(JSON.parse(raw));
  22. } catch (e) {
  23. resolve({ code: res.statusCode, parseError: true, raw });
  24. }
  25. });
  26. }
  27. );
  28. r.on("error", (e) => resolve({ code: 0, error: e.message }));
  29. if (data) r.write(data);
  30. r.end();
  31. });
  32. }
  33. function check(name, cond, detail) {
  34. asserts.push({ name, pass: !!cond, detail: detail || "" });
  35. console.log((cond ? "PASS" : "FAIL") + " | " + name + (detail ? " | " + detail : ""));
  36. }
  37. (async () => {
  38. // 1 区划完整性
  39. const towns = await req("GET", "/api/regions?level=TOWN&parentId=D_SJ");
  40. check("7个镇", towns.code === 200 && towns.data.length === 7, "n=" + (towns.data || []).length);
  41. const ss = await req("GET", "/api/regions?level=VILLAGE&parentId=T_SHESHAN");
  42. check("佘山有村", ss.code === 200 && ss.data.length >= 1, "n=" + (ss.data || []).length + " " + (ss.data || []).map((x) => x.name).join(","));
  43. const cd = await req("GET", "/api/regions?level=VILLAGE&parentId=T_CHEDUN");
  44. check("车墩有村", cd.code === 200 && cd.data.length >= 1, "n=" + (cd.data || []).length);
  45. // 2 人员新增落库
  46. const idNo = "310117199908081" + String(Date.now()).slice(-4);
  47. const saved = await req("POST", "/api/personnel", {
  48. name: "闭环测试员",
  49. idNumber: idNo,
  50. townId: "T_YEXIE",
  51. villageId: "V_YX_001",
  52. bankCardNumber: "6228480030001111222",
  53. monthlyStandard: 1280,
  54. enjoyStartMonth: "202606",
  55. source: "村级录入",
  56. });
  57. check("人员保存成功", saved.code === 200 && saved.data && saved.data.id, saved.data && saved.data.id);
  58. const pid = saved.data && saved.data.id;
  59. const found = await req("POST", "/api/personnel/page", { pageNum: 1, pageSize: 50, idNumber: idNo });
  60. check("人员按身份证可查", found.code === 200 && found.data.total >= 1, "total=" + (found.data && found.data.total));
  61. // 3 创建批次并审批改状态
  62. const batch = await req("POST", "/api/batches", {
  63. batchNo: "202606",
  64. batchLevel: "VILLAGE",
  65. payMonth: "202606",
  66. townId: "T_YEXIE",
  67. villageId: "V_YX_001",
  68. payDate: "2026-06-20",
  69. personnelIds: [pid || "P001"],
  70. });
  71. check("批次创建", batch.code === 200 && batch.data && batch.data.id, batch.data && batch.data.id);
  72. const bid = batch.data && batch.data.id;
  73. const before = await req("GET", "/api/batches/" + bid);
  74. check("新建批次待初审", before.data && before.data.status === "PENDING_TOWN_APPROVE", before.data && before.data.status);
  75. const pass = await req("POST", "/api/batches/town-approve", {
  76. bizId: bid,
  77. action: "PASS",
  78. opinion: "闭环测试通过",
  79. });
  80. check("初审接口成功", pass.code === 200, pass.message);
  81. const after = await req("GET", "/api/batches/" + bid);
  82. check(
  83. "初审后阶段进入居保匹配",
  84. after.data && after.data.currentStage === "STAGE3_INSURANCE_MATCH",
  85. after.data && after.data.currentStage + "/" + after.data.status
  86. );
  87. const match = await req("POST", "/api/insurance-match/trigger", { batchId: bid });
  88. check("居保匹配返回人员项", match.code === 200 && match.data && (match.data.items || []).length >= 1, "items=" + ((match.data && match.data.items) || []).length);
  89. // 4 回盘明细来自真实发放
  90. const ret = await req("POST", "/api/return-disk/details/page", { pageNum: 1, pageSize: 20, batchId: "MB202603001" });
  91. check("回盘明细条数>=3", ret.code === 200 && ret.data.total >= 3, "total=" + (ret.data && ret.data.total));
  92. const failItem = (ret.data.list || []).find((x) => x.success === false);
  93. check("含失败回盘明细", !!failItem, failItem && failItem.name + "/" + failItem.failReasonCode);
  94. // 5 看板真实统计
  95. const dash = await req("GET", "/api/dashboard/summary?payMonth=202603&townId=T_YEXIE");
  96. check("看板非假数据100人", dash.code === 200 && dash.data.totalPeople !== 100, "people=" + (dash.data && dash.data.totalPeople));
  97. check("看板有统计明细", dash.data && (dash.data.townStats || []).length >= 1, "stats=" + ((dash.data && dash.data.townStats) || []).length);
  98. // 6 特殊业务落库可查
  99. const sp = await req("POST", "/api/special-biz", {
  100. personnelId: "P004",
  101. batchNo: "202603",
  102. bizType: "FUNERAL",
  103. amount: 6000,
  104. reason: "闭环丧葬测试",
  105. heirName: "赵小明",
  106. });
  107. check("特殊业务创建", sp.code === 200 && sp.data && sp.data.id, sp.data && sp.data.id);
  108. const spPage = await req("GET", "/api/special-biz/page?bizType=FUNERAL&pageNum=1&pageSize=50");
  109. check("特殊业务可分页查出", spPage.code === 200 && spPage.data.total >= 1, "total=" + (spPage.data && spPage.data.total));
  110. if (sp.data && sp.data.id) {
  111. const ap = await req("POST", "/api/special-biz/leader-approve", {
  112. bizId: sp.data.id,
  113. action: "PASS",
  114. opinion: "同意丧葬",
  115. });
  116. check("特殊业务审批", ap.code === 200 && ap.data.approveStatus === "已通过", ap.data && ap.data.approveStatus);
  117. }
  118. // 7 调标落库
  119. const std = await req("POST", "/api/amount-standards", {
  120. townId: "T_YEXIE",
  121. standardType: "MONTHLY",
  122. amount: 1380,
  123. effectiveTime: Date.now(),
  124. adjustReason: "闭环调标",
  125. });
  126. check("调标保存", std.code === 200, std.message);
  127. const cur = await req("GET", "/api/amount-standards/current?townId=T_YEXIE&standardType=MONTHLY");
  128. check("当前月补变为1380", cur.code === 200 && Number(cur.data.amount) === 1380, "amount=" + (cur.data && cur.data.amount));
  129. // 8 历史批次真数据(回盘+归档)
  130. const hist = await req("POST", "/api/dashboard/history-batches", {
  131. pageNum: 1,
  132. pageSize: 20,
  133. townId: "T_YEXIE",
  134. });
  135. check(
  136. "历史批次含真实回盘/归档",
  137. hist.code === 200 &&
  138. (hist.data.list || []).length >= 1 &&
  139. !(hist.data.list || []).some((x) => x.id === "VB202510001"),
  140. "ids=" + (hist.data.list || []).map((x) => x.id + ":" + x.status).join(",")
  141. );
  142. const fail = asserts.filter((a) => !a.pass);
  143. console.log("\nSUMMARY pass=" + (asserts.length - fail.length) + "/" + asserts.length);
  144. if (fail.length) {
  145. fail.forEach((f) => console.log(" - " + f.name + " :: " + f.detail));
  146. process.exit(2);
  147. }
  148. })().catch((e) => {
  149. console.error(e);
  150. process.exit(1);
  151. });