dms-delete-probe.mjs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. /**
  2. * 探测 DMS 删除内容的**正确姿势**(OpenAPI 与 Apifox 对 delContentById 的参数都未文档化)。
  3. *
  4. * 背景:Step-0 用 `DELETE /content/delContentById?id=<uuid>` 得 code=-1,行未删。
  5. * 线索:OpenAPI 里另有 `POST /content/updateAudit`(修改内容状态),
  6. * 而 DMS 的 states 语义是 0草稿 1待审 2审完 3发布 4销毁 5退回
  7. * —— “删除”很可能是把 state 改成 4,而不是物理删除。
  8. *
  9. * 本脚本会:造测试行 → 逐一试候选形态 → 校验行是否真的消失 → 清理残留。
  10. * 用法:node harness/tools/dms-delete-probe.mjs <DMS_TOKEN>
  11. */
  12. const HOST = process.env.DMS_HOST || '121.43.55.7:10081';
  13. const BASE = `http://${HOST}/dms`;
  14. const TOKEN = process.argv[2] || '';
  15. const COL = '1887';
  16. const MODEL = '2034';
  17. const TAG = `deltest_${Date.now().toString(36)}`;
  18. if (!TOKEN) {
  19. console.error('用法:node harness/tools/dms-delete-probe.mjs <DMS_TOKEN>');
  20. process.exit(2);
  21. }
  22. const call = async (method, path, form, jsonBody) => {
  23. const init = { method, headers: { token: TOKEN } };
  24. if (jsonBody !== undefined) {
  25. init.headers['Content-Type'] = 'application/json';
  26. init.body = JSON.stringify(jsonBody);
  27. } else if (form) {
  28. init.headers['Content-Type'] = 'application/x-www-form-urlencoded';
  29. init.body = new URLSearchParams(form).toString();
  30. }
  31. try {
  32. const res = await fetch(`${BASE}${path}`, init);
  33. const text = await res.text();
  34. let json = null;
  35. try {
  36. json = JSON.parse(text);
  37. } catch {
  38. /* 非 JSON */
  39. }
  40. return { status: res.status, json, text };
  41. } catch (err) {
  42. return { status: 0, json: null, text: String(err) };
  43. }
  44. };
  45. /** 该 column 下用 c_credit_code 标记的所有行 */
  46. const rowsWithTag = async (columnId, tag) => {
  47. const r = await call('POST', '/content/selectContentList', {
  48. columnId: String(columnId),
  49. page: '0',
  50. pageSize: '100',
  51. search: JSON.stringify([{ field: 'c_credit_code', searchType: 1, content: { value: tag } }]),
  52. });
  53. return r.json?.content?.data || [];
  54. };
  55. const mkRow = async () => {
  56. const sid = `${TAG}_${Math.random().toString(36).slice(2, 8)}`;
  57. const r = await call('POST', '/content/addContent', {
  58. columnId: COL,
  59. modelId: MODEL,
  60. content: JSON.stringify({ c_credit_code: TAG, c_session_id: sid, c_title: 'deltest' }),
  61. });
  62. return typeof r.json?.content === 'string' ? { uuid: r.json.content, sid } : null;
  63. };
  64. const run = async () => {
  65. console.log(`\nDMS 删除姿势探测 ${BASE}\n`);
  66. const candidates = [
  67. ['POST /content/delContentById form{columnId,contentId}', (u) => call('POST', '/content/delContentById', { columnId: COL, contentId: u })],
  68. ['DELETE /content/delContentById json{columnId,contentId}', (u) => call('DELETE', '/content/delContentById', null, { columnId: Number(COL), contentId: u })],
  69. ['DELETE /content/delContentById json{id}', (u) => call('DELETE', '/content/delContentById', null, { id: u })],
  70. ['DELETE form body{columnId,contentId}', (u) => call('DELETE', '/content/delContentById', { columnId: COL, contentId: u })],
  71. ['POST /content/updateAudit form{columnId,contentId,state=4}', (u) => call('POST', '/content/updateAudit', { columnId: COL, contentId: u, state: '4' })],
  72. ['POST /content/updateAudit form{columnId,ids,state=4}', (u) => call('POST', '/content/updateAudit', { columnId: COL, ids: u, state: '4' })],
  73. ['POST /content/updateAudit form{columnId,contentIds,state=4}', (u) => call('POST', '/content/updateAudit', { columnId: COL, contentIds: u, state: '4' })],
  74. ['POST /content/updateAudit form{columnId,id,state=4}', (u) => call('POST', '/content/updateAudit', { columnId: COL, id: u, state: '4' })],
  75. ['POST /content/updateAudit json{columnId,contentIds:[uuid],state:4}', (u) => call('POST', '/content/updateAudit', null, { columnId: Number(COL), contentIds: [u], state: 4 })],
  76. ];
  77. const winners = [];
  78. for (const [label, fn] of candidates) {
  79. const row = await mkRow();
  80. if (!row) {
  81. console.log(` ?? ${label} —— 造行失败`);
  82. continue;
  83. }
  84. let resp;
  85. try {
  86. resp = await fn(row.uuid);
  87. } catch (err) {
  88. console.log(` xx ${label} —— 异常 ${err.message}`);
  89. continue;
  90. }
  91. // 判定:默认查询里还能不能看到这一行
  92. const still = (await rowsWithTag(COL, TAG)).some((r) => r.id === row.uuid);
  93. const code = resp.json?.code ?? resp.status;
  94. const ok = !still && code === 200;
  95. console.log(` ${ok ? 'ok ' : 'xx '} ${label}\n → code=${code}${still ? '(行仍在)' : '(行已消失)'} ${String(resp.text).slice(0, 120)}`);
  96. if (ok) winners.push({ label, fn });
  97. }
  98. console.log(`\n可用姿势:${winners.length ? winners.map((w) => w.label).join(' / ') : '(都不行)'}`);
  99. // ── 清理:用可用的姿势删掉所有 deltest_* 与 step0_* 残留 ──────────────
  100. console.log('\n【清理残留】');
  101. const targets = [
  102. ...(await rowsWithTag(COL, TAG)),
  103. ...(await rowsWithTag('1889', TAG)),
  104. ...(await rowsWithTag(COL, 'step0_credit_code')),
  105. ...(await rowsWithTag('1889', 'step0_credit_code')),
  106. ...(await rowsWithTag(COL, 'probe_cc')),
  107. ...(await rowsWithTag('1889', 'probe_cc')),
  108. ];
  109. console.log(` 待清理 ${targets.length} 行`);
  110. if (!winners.length) {
  111. console.log(' ⚠️ 没有可用姿势,残留无法自动清理(需人工在 DMS 后台处理)');
  112. console.log(' 残留 id:', targets.map((t) => `${t.id}(col ${t.column_id})`).join(', '));
  113. return;
  114. }
  115. for (const row of targets) {
  116. for (const w of winners) {
  117. try {
  118. await w.fn(row.id);
  119. if (!(await rowsWithTag(String(row.column_id), row.c_credit_code)).some((r) => r.id === row.id)) break;
  120. } catch {
  121. /* 换下一种 */
  122. }
  123. }
  124. }
  125. const left = [
  126. ...(await rowsWithTag(COL, TAG)),
  127. ...(await rowsWithTag('1889', TAG)),
  128. ...(await rowsWithTag(COL, 'step0_credit_code')),
  129. ...(await rowsWithTag('1889', 'step0_credit_code')),
  130. ...(await rowsWithTag(COL, 'probe_cc')),
  131. ...(await rowsWithTag('1889', 'probe_cc')),
  132. ];
  133. console.log(` 清理后剩余 ${left.length} 行${left.length ? '(' + left.map((r) => r.id).join(', ') + ')' : ' ✅'}`);
  134. };
  135. run().catch((e) => {
  136. console.error('脚本异常:', e);
  137. process.exit(1);
  138. });