_test_remarks_fields.py 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503
  1. # -*- coding: utf-8 -*-
  2. """联调:备注改字段后的业务流程回归。"""
  3. import json
  4. import sys
  5. import time
  6. import urllib.error
  7. import urllib.parse
  8. import urllib.request
  9. sys.stdout.reconfigure(encoding="utf-8")
  10. BASE = "http://127.0.0.1:8088/sjnmtybt"
  11. DMS = "http://121.43.55.7:2101"
  12. PASS = 0
  13. FAIL = 0
  14. def ok(name, cond, extra=""):
  15. global PASS, FAIL
  16. if cond:
  17. PASS += 1
  18. print(f" [OK] {name}" + (f" {extra}" if extra else ""))
  19. else:
  20. FAIL += 1
  21. print(f" [FAIL] {name}" + (f" {extra}" if extra else ""))
  22. def req(method, path, body=None, query=None, timeout=90):
  23. url = BASE + path
  24. if query:
  25. url += "?" + urllib.parse.urlencode(query)
  26. data = None
  27. headers = {}
  28. if body is not None:
  29. data = json.dumps(body, ensure_ascii=False).encode("utf-8")
  30. headers["Content-Type"] = "application/json; charset=utf-8"
  31. r = urllib.request.Request(url, data=data, method=method, headers=headers)
  32. try:
  33. with urllib.request.urlopen(r, timeout=timeout) as resp:
  34. raw = resp.read().decode("utf-8")
  35. return json.loads(raw) if raw else {}
  36. except urllib.error.HTTPError as e:
  37. raw = e.read().decode("utf-8")
  38. try:
  39. return json.loads(raw)
  40. except Exception:
  41. return {"code": e.code, "message": raw}
  42. except Exception as e:
  43. return {"code": 599, "message": str(e)}
  44. def gb18(body17):
  45. w = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
  46. m = "10X98765432"
  47. s = sum(int(body17[i]) * w[i] for i in range(17))
  48. return body17 + m[s % 11]
  49. def dms_login():
  50. body = urllib.parse.urlencode(
  51. {"userName": "user_liu", "password": "WE176852439@lmx", "clientId": "1"}
  52. ).encode("utf-8")
  53. r = urllib.request.Request(
  54. DMS + "/proxy_oauth/user/login",
  55. data=body,
  56. method="POST",
  57. headers={"Content-Type": "application/x-www-form-urlencoded"},
  58. )
  59. with urllib.request.urlopen(r, timeout=40) as resp:
  60. return json.loads(resp.read().decode("utf-8"))["message"]
  61. def dms_list(token, column_id, model_id, page_size=100):
  62. body = urllib.parse.urlencode(
  63. {
  64. "columnId": str(column_id),
  65. "modelId": str(model_id),
  66. "states": "0",
  67. "page": "0",
  68. "pageSize": str(page_size),
  69. }
  70. ).encode("utf-8")
  71. r = urllib.request.Request(
  72. DMS + "/proxy_dms/content/selectContentList",
  73. data=body,
  74. method="POST",
  75. headers={"Content-Type": "application/x-www-form-urlencoded", "Token": token},
  76. )
  77. with urllib.request.urlopen(r, timeout=40) as resp:
  78. data = json.loads(resp.read().decode("utf-8"))
  79. return (data.get("content") or {}).get("data") or []
  80. print("======== 1) 人员:查询回填 / 新增 / 改号 / 备注纯说明 ========")
  81. page = req("POST", "/api/personnel/page", {"name": "陈桂新", "pageNum": 1, "pageSize": 5})
  82. chen = ((page.get("data") or {}).get("list") or [None])[0]
  83. ok("陈桂新能查到", chen is not None)
  84. if chen:
  85. ok("业务号仍是 P197", chen.get("id") == "P197", str(chen.get("id")))
  86. ok("手机号在字段里", chen.get("phoneNumber") == "13800000001", str(chen.get("phoneNumber")))
  87. remarks = str(chen.get("remarks") or "")
  88. ok("备注不含 MOCK_SEED", "MOCK_SEED" not in remarks, remarks)
  89. ok("备注不含业务号协议", "P197" not in remarks, remarks)
  90. ok("备注是用户说明", remarks in ("村级录入", "Excel导入") or "录入" in remarks, remarks)
  91. detail = req("GET", "/api/personnel/" + chen["id"])
  92. d = detail.get("data") or {}
  93. ok("按业务号取详情", d.get("id") == "P197" and d.get("name") == "陈桂新")
  94. seq = str(int(time.time()) % 900 + 100)
  95. idno = gb18("31011719800813" + seq)
  96. name = "字段回归测试" + seq
  97. save = req(
  98. "POST",
  99. "/api/personnel",
  100. {
  101. "name": name,
  102. "idNumber": idno,
  103. "phoneNumber": "13987654321",
  104. "townId": "T_YEXIE",
  105. "villageId": "V_YX_001",
  106. "payeeName": name,
  107. "bankCardNumber": "6222021234567890123",
  108. "bankName": "农业银行",
  109. "enjoyStartMonth": "202601",
  110. "source": "联调验证",
  111. "remarks": "这是用户备注不要当协议",
  112. "heirName": "测试继承人",
  113. "heirRelation": "子女",
  114. },
  115. )
  116. ok("新增人员成功", save.get("code") == 200, str(save.get("message")))
  117. p = save.get("data") or {}
  118. pid = p.get("id")
  119. ok("新人员有业务号", bool(pid) and str(pid).startswith("P"), str(pid))
  120. ok("新人员手机号回读", p.get("phoneNumber") == "13987654321", str(p.get("phoneNumber")))
  121. ok(
  122. "新人员备注无 MOCK_SEED/业务号",
  123. "MOCK_SEED" not in str(p.get("remarks") or "") and str(pid) not in str(p.get("remarks") or ""),
  124. str(p.get("remarks")),
  125. )
  126. ok("继承人关系在字段", p.get("heirRelation") == "子女", str(p.get("heirRelation")))
  127. village_edit = req(
  128. "POST",
  129. "/api/personnel",
  130. {
  131. "id": pid,
  132. "name": name,
  133. "idNumber": idno,
  134. "phoneNumber": "13800138000",
  135. "townId": "T_YEXIE",
  136. "villageId": "V_YX_001",
  137. "remarks": "改过的用户备注",
  138. "operatorRoleKey": "village",
  139. },
  140. )
  141. vp = village_edit.get("data") or {}
  142. ok("村级改手机号/备注成功", village_edit.get("code") == 200, str(village_edit.get("message")))
  143. ok("村级编辑后手机号更新", vp.get("phoneNumber") == "13800138000", str(vp.get("phoneNumber")))
  144. ok("村级编辑后备注是用户说明", vp.get("remarks") == "改过的用户备注", str(vp.get("remarks")))
  145. ok("村级未改继承人时关系仍是子女", vp.get("heirRelation") == "子女", str(vp.get("heirRelation")))
  146. heir_deny = req(
  147. "POST",
  148. "/api/personnel",
  149. {
  150. "id": pid,
  151. "name": name,
  152. "idNumber": idno,
  153. "phoneNumber": "13800138000",
  154. "townId": "T_YEXIE",
  155. "villageId": "V_YX_001",
  156. "remarks": "改过的用户备注",
  157. "heirRelation": "配偶",
  158. "operatorRoleKey": "village",
  159. },
  160. )
  161. ok(
  162. "村级改继承人被拒",
  163. heir_deny.get("code") not in (None, 200),
  164. str(heir_deny.get("code")) + " " + str(heir_deny.get("message")),
  165. )
  166. ok("拒绝文案指向镇社区事务", "继承人" in str(heir_deny.get("message") or ""), str(heir_deny.get("message")))
  167. heir_ok = req(
  168. "POST",
  169. "/api/personnel",
  170. {
  171. "id": pid,
  172. "name": name,
  173. "idNumber": idno,
  174. "phoneNumber": "13800138000",
  175. "townId": "T_YEXIE",
  176. "villageId": "V_YX_001",
  177. "remarks": "改过的用户备注",
  178. "heirRelation": "配偶",
  179. "operatorRoleKey": "social",
  180. },
  181. )
  182. ep = heir_ok.get("data") or {}
  183. ok("镇级改继承人成功", heir_ok.get("code") == 200, str(heir_ok.get("message")))
  184. ok("编辑后手机号仍是新号", ep.get("phoneNumber") == "13800138000", str(ep.get("phoneNumber")))
  185. ok("编辑后备注是用户说明", ep.get("remarks") == "改过的用户备注", str(ep.get("remarks")))
  186. ok("编辑后继承人关系更新", ep.get("heirRelation") == "配偶", str(ep.get("heirRelation")))
  187. print("======== 2) 人员状态:无 allowDirect 拒绝,有则放行 ========")
  188. deny = req(
  189. "PUT",
  190. "/api/personnel/status",
  191. {"personnelId": pid, "bizStatus": "PAUSED", "payThisMonth": False, "remarks": "想直接暂停"},
  192. )
  193. ok(
  194. "无 allowDirect 暂停被拒",
  195. deny.get("code") not in (None, 200),
  196. str(deny.get("code")) + " " + str(deny.get("message")),
  197. )
  198. ok("拒绝文案不再提备注 ALLOW_DIRECT", "ALLOW_DIRECT" not in str(deny.get("message") or ""), str(deny.get("message")))
  199. allow = req(
  200. "PUT",
  201. "/api/personnel/status",
  202. {
  203. "personnelId": pid,
  204. "bizStatus": "PAUSED",
  205. "payThisMonth": False,
  206. "remarks": "管理员直改暂停",
  207. "allowDirect": True,
  208. "operator": "联调",
  209. },
  210. )
  211. ok("allowDirect=true 可暂停", allow.get("code") == 200, str(allow.get("message")))
  212. ok(
  213. "暂停后备注不含 ALLOW_DIRECT",
  214. "ALLOW_DIRECT" not in str((allow.get("data") or {}).get("remarks") or ""),
  215. str((allow.get("data") or {}).get("remarks")),
  216. )
  217. ok("状态已是 PAUSED", (allow.get("data") or {}).get("bizStatus") == "PAUSED", str((allow.get("data") or {}).get("bizStatus")))
  218. restore = req(
  219. "PUT",
  220. "/api/personnel/status",
  221. {"personnelId": pid, "bizStatus": "DRAFT", "payThisMonth": True, "allowDirect": True, "remarks": "测完恢复草稿"},
  222. )
  223. ok("恢复草稿", restore.get("code") == 200, str(restore.get("message")))
  224. print("======== 3) 区划:启用/银行/分担比例走字段 ========")
  225. regions = req("GET", "/api/regions", query={"level": "TOWN"})
  226. towns = regions.get("data") or []
  227. yexie = next((t for t in towns if t.get("code") == "T_YEXIE" or t.get("id") == "T_YEXIE" or "叶榭" in str(t.get("name"))), None)
  228. ok("能查到叶榭镇", yexie is not None)
  229. if yexie:
  230. ok("enabled 是布尔字段", isinstance(yexie.get("enabled"), bool), str(yexie.get("enabled")))
  231. ok("区级分担比例是数字", isinstance(yexie.get("districtSharePercent"), int), str(yexie.get("districtSharePercent")))
  232. ok("镇级分担比例是数字", isinstance(yexie.get("townSharePercent"), int), str(yexie.get("townSharePercent")))
  233. rem = str(yexie.get("remarks") or "")
  234. ok("区划备注不含 level=/code=", "level=" not in rem and "code=" not in rem, rem)
  235. old_bank = yexie.get("defaultBank")
  236. old_d = yexie.get("districtSharePercent")
  237. old_t = yexie.get("townSharePercent")
  238. save_r = req(
  239. "POST",
  240. "/api/regions",
  241. {
  242. "id": yexie.get("id"),
  243. "code": yexie.get("code") or "T_YEXIE",
  244. "name": yexie.get("name") or "叶榭镇",
  245. "level": "TOWN",
  246. "parentId": yexie.get("parentId") or "D_SJ",
  247. "entrustPlanCode": yexie.get("entrustPlanCode"),
  248. "defaultBank": "农业银行-回归测",
  249. "enabled": True,
  250. "districtSharePercent": 90,
  251. "townSharePercent": 10,
  252. "remarks": "区划用户备注",
  253. },
  254. )
  255. ok("更新街镇成功", save_r.get("code") == 200, str(save_r.get("message")))
  256. rr = save_r.get("data") or {}
  257. ok("默认银行写入字段", rr.get("defaultBank") == "农业银行-回归测", str(rr.get("defaultBank")))
  258. ok(
  259. "分担比例写入字段",
  260. rr.get("districtSharePercent") == 90 and rr.get("townSharePercent") == 10,
  261. str(rr.get("districtSharePercent")) + "/" + str(rr.get("townSharePercent")),
  262. )
  263. ok("区划备注是用户说明", rr.get("remarks") == "区划用户备注", str(rr.get("remarks")))
  264. req(
  265. "POST",
  266. "/api/regions",
  267. {
  268. "id": yexie.get("id"),
  269. "code": yexie.get("code") or "T_YEXIE",
  270. "name": yexie.get("name") or "叶榭镇",
  271. "level": "TOWN",
  272. "parentId": yexie.get("parentId") or "D_SJ",
  273. "entrustPlanCode": yexie.get("entrustPlanCode"),
  274. "defaultBank": old_bank,
  275. "enabled": True,
  276. "districtSharePercent": old_d,
  277. "townSharePercent": old_t,
  278. "remarks": "",
  279. },
  280. )
  281. print("======== 4) 批次:提交时间 / 测算锁定 / 轮次字段 ========")
  282. bp = req("POST", "/api/batches/page", {"pageNum": 1, "pageSize": 50})
  283. batches = (bp.get("data") or {}).get("list") or []
  284. ok("批次列表能查到", len(batches) > 0, "n=" + str(len(batches)))
  285. batch = next((b for b in batches if b.get("status") not in ("ARCHIVED", "EXPORTED", "RETURNED")), None)
  286. if batch is None and batches:
  287. batch = next((b for b in batches if b.get("status") not in ("ARCHIVED",)), batches[0])
  288. if batch:
  289. bid = batch.get("id")
  290. ok("批次有 submitTime 字段", batch.get("submitTime") is None or isinstance(batch.get("submitTime"), (int, float)), str(batch.get("submitTime")))
  291. ok("批次有 calcLocked 字段", isinstance(batch.get("calcLocked"), bool), str(batch.get("calcLocked")))
  292. ok("批次有 yearlyAdjust 字段", isinstance(batch.get("yearlyAdjust"), bool), str(batch.get("yearlyAdjust")))
  293. snap = req("POST", f"/api/payments/batches/{urllib.parse.quote(bid)}/calc-rounds", query={"operator": "联调"})
  294. ok("测算快照成功", snap.get("code") == 200, str(snap.get("message")))
  295. sd = snap.get("data") or {}
  296. ok("快照含 version/personCount/payableTotal", "version" in sd and "personCount" in sd, str(sd))
  297. rounds = req("GET", f"/api/payments/batches/{urllib.parse.quote(bid)}/calc-rounds")
  298. lst = rounds.get("data") or []
  299. ok("测算轮次列表可读", rounds.get("code") == 200 and len(lst) >= 1, "n=" + str(len(lst)))
  300. if lst:
  301. ok("轮次 version 来自字段", lst[0].get("version") is not None, str(lst[0]))
  302. lock = req("POST", f"/api/payments/batches/{urllib.parse.quote(bid)}/calc-lock", query={"locked": "true", "operator": "联调"})
  303. ok("锁定测算成功", lock.get("code") == 200, str(lock.get("message")))
  304. ok("锁定后 calcLocked=true", (lock.get("data") or {}).get("calcLocked") is True, str((lock.get("data") or {}).get("calcLocked")))
  305. rem = str((lock.get("data") or {}).get("remarks") or "")
  306. ok("锁定不写 CALC_LOCKED 到备注", "CALC_LOCKED" not in rem, rem)
  307. unlock = req("POST", f"/api/payments/batches/{urllib.parse.quote(bid)}/calc-lock", query={"locked": "false", "operator": "联调"})
  308. ok("解锁测算成功", unlock.get("code") == 200 and (unlock.get("data") or {}).get("calcLocked") is False, str((unlock.get("data") or {}).get("calcLocked")))
  309. detail = req("GET", "/api/batches/" + urllib.parse.quote(bid))
  310. dd = detail.get("data") or {}
  311. ok("批次详情带回 calcLocked", "calcLocked" in dd, str(dd.get("calcLocked")))
  312. print("======== 5) 发放明细:区镇金额走字段 ========")
  313. if batch:
  314. gen = req("POST", f"/api/payments/batches/{urllib.parse.quote(batch.get('id'))}/generate")
  315. ok("生成/对齐发放明细成功", gen.get("code") == 200, str(gen.get("message")))
  316. pays = (gen.get("data") or {}).get("list") or []
  317. if not pays:
  318. pay = req("POST", "/api/payments/details/page", {"batchId": batch.get("id"), "pageNum": 1, "pageSize": 10})
  319. pays = (pay.get("data") or {}).get("list") or []
  320. ok("发放明细能查到", pay.get("code") == 200, "n=" + str(len(pays)))
  321. else:
  322. ok("发放明细能查到", len(pays) > 0, "n=" + str(len(pays)))
  323. if pays:
  324. one = pays[0]
  325. ok(
  326. "区/镇承担金额有值或可空但字段存在",
  327. "districtAmount" in one and "townAmount" in one,
  328. "dist=" + str(one.get("districtAmount")) + " town=" + str(one.get("townAmount")),
  329. )
  330. prem = str(one.get("remarks") or "")
  331. ok("明细备注不含 DIST_AMT 协议", "DIST_AMT" not in prem and "TOWN_AMT" not in prem, prem)
  332. ok("明细备注不含 API生成 协议", "API生成" not in prem, prem)
  333. ok("调标字段存在", "oldMonthlyAmount" in one and "newMonthlyAmount" in one and "adjustMonths" in one)
  334. adj = next((b for b in batches if b.get("yearlyAdjust") is True), None)
  335. if adj:
  336. ap = req("POST", "/api/payments/details/page", {"batchId": adj.get("id"), "pageNum": 1, "pageSize": 5})
  337. al = (ap.get("data") or {}).get("list") or []
  338. ok("年调标批次能查明细", ap.get("code") == 200, "n=" + str(len(al)))
  339. if al:
  340. ok("年调标明细有新旧月标字段", "oldMonthlyAmount" in al[0] and "newMonthlyAmount" in al[0], str(al[0].get("oldMonthlyAmount")))
  341. else:
  342. ok("当前没有年调标批次(跳过明细断言)", True)
  343. print("======== 6) 特殊业务:金额/姓名/继承人走字段 ========")
  344. sp = req(
  345. "POST",
  346. "/api/special-biz",
  347. {
  348. "personnelId": pid,
  349. "bizType": "PAUSE",
  350. "amount": 0,
  351. "reason": "字段回归-暂停申请",
  352. "operator": "联调",
  353. "operatorRoleKey": "social",
  354. "heirName": "继承人甲",
  355. "heirIdNumber": gb18("31011719900101001"),
  356. "heirPhone": "13700001111",
  357. },
  358. )
  359. ok("创建暂停特殊业务", sp.get("code") == 200, str(sp.get("message")) + " " + str(sp.get("code")))
  360. svo = sp.get("data") or {}
  361. sid = svo.get("id")
  362. ok("特殊单姓名来自字段", svo.get("name") == name or bool(svo.get("name")), str(svo.get("name")))
  363. ok("特殊单继承人姓名", svo.get("heirName") == "继承人甲", str(svo.get("heirName")))
  364. ok("特殊单继承人电话", svo.get("heirPhone") == "13700001111", str(svo.get("heirPhone")))
  365. page_sp = req("GET", "/api/special-biz/page", query={"pageNum": "1", "pageSize": "20", "bizType": "PAUSE"})
  366. splist = (page_sp.get("data") or {}).get("list") or []
  367. hit = next((x for x in splist if x.get("id") == sid), None)
  368. ok("特殊业务分页能查到刚建的单", hit is not None)
  369. if hit:
  370. ok("列表姓名不是靠备注协议", bool(hit.get("name")), str(hit.get("name")))
  371. if sid:
  372. rej = req(
  373. "POST",
  374. "/api/special-biz/leader-approve",
  375. {"bizId": sid, "action": "REJECT", "opinion": "联调驳回,不落地"},
  376. )
  377. ok("驳回特殊业务(清理)", rej.get("code") == 200, str(rej.get("message")))
  378. rf = req(
  379. "POST",
  380. "/api/special-biz",
  381. {
  382. "personnelId": pid,
  383. "bizType": "REFUND",
  384. "amount": 100,
  385. "reason": "字段回归-退款",
  386. "refundMethod": "DEDUCT_NEXT",
  387. "operator": "联调",
  388. },
  389. )
  390. ok("创建退款特殊业务", rf.get("code") == 200, str(rf.get("message")))
  391. rvo = rf.get("data") or {}
  392. rid = rvo.get("id")
  393. ok("退款金额在字段", rvo.get("amount") == 100 or str(rvo.get("amount")) in ("100", "100.0", "100.00"), str(rvo.get("amount")))
  394. ok("退款进度字段有值", bool(rvo.get("refundProgress")), str(rvo.get("refundProgress")))
  395. ok(
  396. "退款方式字段",
  397. str(rvo.get("refundMethod")) in ("DEDUCT_NEXT", "下月扣减") or rvo.get("refundMethod") is not None,
  398. str(rvo.get("refundMethod")),
  399. )
  400. if rid:
  401. adv = req("POST", f"/api/special-biz/{rid}/refund-progress", {"stage": "VERIFYING"})
  402. ok("推进退款进度", adv.get("code") == 200, str(adv.get("message")))
  403. ok("进度更新到 VERIFYING", (adv.get("data") or {}).get("refundProgress") == "VERIFYING", str((adv.get("data") or {}).get("refundProgress")))
  404. req("POST", "/api/special-biz/leader-approve", {"bizId": rid, "action": "REJECT", "opinion": "联调驳回"})
  405. print("======== 7) 金额标准:已删除不进列表 / 无 allowDirect 走审批 ========")
  406. std = req("GET", "/api/amount-standards", query={"townId": "T_YEXIE", "pageNum": "1", "pageSize": "50"})
  407. ok("金额标准列表成功", std.get("code") == 200, str(std.get("message")))
  408. slist = (std.get("data") or {}).get("list") or []
  409. ok("当前镇有标准", len(slist) > 0, "n=" + str(len(slist)))
  410. ok("列表项无 deleted=true", all(not s.get("deleted") for s in slist))
  411. if slist:
  412. cur = next((s for s in slist if s.get("current") is True), slist[0])
  413. ok("标准金额在字段", cur.get("amount") is not None, str(cur.get("amount")))
  414. pending = req(
  415. "POST",
  416. "/api/amount-standards",
  417. {
  418. "townId": "T_YEXIE",
  419. "standardType": "YEARLY_ADJUST",
  420. "amount": 1,
  421. "funeralAmount": 1,
  422. "effectiveTime": int(time.time() * 1000) + 86400 * 1000 * 365 * 20,
  423. "adjustReason": "字段回归-不应直接生效",
  424. "remarks": "用户说明",
  425. },
  426. )
  427. ok("无 allowDirect 不直接生效", pending.get("code") == 200, str(pending.get("message")))
  428. pstd = pending.get("data") or {}
  429. ok("返回待审批说明", "审批" in str(pstd.get("remarks") or pending.get("message") or ""), str(pstd.get("remarks")))
  430. ok("待审批单 current=false", pstd.get("current") is False, str(pstd.get("current")))
  431. if pstd.get("id"):
  432. rej_std = req(
  433. "POST",
  434. "/api/special-biz/leader-approve",
  435. {"bizId": pstd.get("id"), "action": "REJECT", "opinion": "联调驳回标准变更"},
  436. )
  437. ok("驳回标准变更待审单", rej_std.get("code") == 200, str(rej_std.get("message")))
  438. print("======== 8) 待补发台账分页 ========")
  439. ar = req("POST", "/api/return-arrears/page", {"pageNum": 1, "pageSize": 10})
  440. ok("待补发台账接口通", ar.get("code") == 200, str(ar.get("message")))
  441. arlist = (ar.get("data") or {}).get("list") or []
  442. if arlist:
  443. one_ar = arlist[0]
  444. ok("待补发有人员字段", bool(one_ar.get("personnelId") or one_ar.get("name")), str(one_ar.get("personnelId")))
  445. print("======== 9) DMS 原表核对新建人员 ========")
  446. token = dms_login()
  447. rows = dms_list(token, 1847, 1970)
  448. raw = next((r for r in rows if str(r.get("c_biz_id")) == str(pid) or str(r.get("c_name")) == name), None)
  449. ok("DMS 能按 c_biz_id 找到人", raw is not None)
  450. if raw:
  451. ok("DMS c_biz_id", str(raw.get("c_biz_id")) == str(pid), str(raw.get("c_biz_id")))
  452. ok("DMS c_phone_number 是字符串 11 位", str(raw.get("c_phone_number")) == "13800138000", str(raw.get("c_phone_number")))
  453. ok("DMS c_heir_relation=配偶", str(raw.get("c_heir_relation")) == "配偶", str(raw.get("c_heir_relation")))
  454. rmk = str(raw.get("c_remarks") or "")
  455. ok("DMS 备注无 MOCK_SEED/PHONE=/P号", "MOCK_SEED" not in rmk and "PHONE=" not in rmk and str(pid) not in rmk, rmk)
  456. print("======== 10) 清理测试人员 ========")
  457. if pid:
  458. dl = req("DELETE", "/api/personnel/" + urllib.parse.quote(pid))
  459. ok("删除测试人员", dl.get("code") == 200, str(dl.get("message")))
  460. gone = req("GET", "/api/personnel/" + urllib.parse.quote(pid))
  461. st = (gone.get("data") or {}).get("bizStatus")
  462. ok(
  463. "删除后归档或不存在",
  464. gone.get("code") == 404 or st in ("ARCHIVED", "已归档") or gone.get("data") is None,
  465. str(gone.get("code")) + " " + str(st),
  466. )
  467. print()
  468. print(f"合计 PASS={PASS} FAIL={FAIL}")
  469. sys.exit(1 if FAIL else 0)