/** * 探测 DMS 删除内容的**正确姿势**(OpenAPI 与 Apifox 对 delContentById 的参数都未文档化)。 * * 背景:Step-0 用 `DELETE /content/delContentById?id=` 得 code=-1,行未删。 * 线索:OpenAPI 里另有 `POST /content/updateAudit`(修改内容状态), * 而 DMS 的 states 语义是 0草稿 1待审 2审完 3发布 4销毁 5退回 * —— “删除”很可能是把 state 改成 4,而不是物理删除。 * * 本脚本会:造测试行 → 逐一试候选形态 → 校验行是否真的消失 → 清理残留。 * 用法:node harness/tools/dms-delete-probe.mjs */ const HOST = process.env.DMS_HOST || '121.43.55.7:10081'; const BASE = `http://${HOST}/dms`; const TOKEN = process.argv[2] || ''; const COL = '1887'; const MODEL = '2034'; const TAG = `deltest_${Date.now().toString(36)}`; if (!TOKEN) { console.error('用法:node harness/tools/dms-delete-probe.mjs '); process.exit(2); } const call = async (method, path, form, jsonBody) => { const init = { method, headers: { token: TOKEN } }; if (jsonBody !== undefined) { init.headers['Content-Type'] = 'application/json'; init.body = JSON.stringify(jsonBody); } else if (form) { init.headers['Content-Type'] = 'application/x-www-form-urlencoded'; init.body = new URLSearchParams(form).toString(); } try { const res = await fetch(`${BASE}${path}`, init); const text = await res.text(); let json = null; try { json = JSON.parse(text); } catch { /* 非 JSON */ } return { status: res.status, json, text }; } catch (err) { return { status: 0, json: null, text: String(err) }; } }; /** 该 column 下用 c_credit_code 标记的所有行 */ const rowsWithTag = async (columnId, tag) => { const r = await call('POST', '/content/selectContentList', { columnId: String(columnId), page: '0', pageSize: '100', search: JSON.stringify([{ field: 'c_credit_code', searchType: 1, content: { value: tag } }]), }); return r.json?.content?.data || []; }; const mkRow = async () => { const sid = `${TAG}_${Math.random().toString(36).slice(2, 8)}`; const r = await call('POST', '/content/addContent', { columnId: COL, modelId: MODEL, content: JSON.stringify({ c_credit_code: TAG, c_session_id: sid, c_title: 'deltest' }), }); return typeof r.json?.content === 'string' ? { uuid: r.json.content, sid } : null; }; const run = async () => { console.log(`\nDMS 删除姿势探测 ${BASE}\n`); const candidates = [ ['POST /content/delContentById form{columnId,contentId}', (u) => call('POST', '/content/delContentById', { columnId: COL, contentId: u })], ['DELETE /content/delContentById json{columnId,contentId}', (u) => call('DELETE', '/content/delContentById', null, { columnId: Number(COL), contentId: u })], ['DELETE /content/delContentById json{id}', (u) => call('DELETE', '/content/delContentById', null, { id: u })], ['DELETE form body{columnId,contentId}', (u) => call('DELETE', '/content/delContentById', { columnId: COL, contentId: u })], ['POST /content/updateAudit form{columnId,contentId,state=4}', (u) => call('POST', '/content/updateAudit', { columnId: COL, contentId: u, state: '4' })], ['POST /content/updateAudit form{columnId,ids,state=4}', (u) => call('POST', '/content/updateAudit', { columnId: COL, ids: u, state: '4' })], ['POST /content/updateAudit form{columnId,contentIds,state=4}', (u) => call('POST', '/content/updateAudit', { columnId: COL, contentIds: u, state: '4' })], ['POST /content/updateAudit form{columnId,id,state=4}', (u) => call('POST', '/content/updateAudit', { columnId: COL, id: u, state: '4' })], ['POST /content/updateAudit json{columnId,contentIds:[uuid],state:4}', (u) => call('POST', '/content/updateAudit', null, { columnId: Number(COL), contentIds: [u], state: 4 })], ]; const winners = []; for (const [label, fn] of candidates) { const row = await mkRow(); if (!row) { console.log(` ?? ${label} —— 造行失败`); continue; } let resp; try { resp = await fn(row.uuid); } catch (err) { console.log(` xx ${label} —— 异常 ${err.message}`); continue; } // 判定:默认查询里还能不能看到这一行 const still = (await rowsWithTag(COL, TAG)).some((r) => r.id === row.uuid); const code = resp.json?.code ?? resp.status; const ok = !still && code === 200; console.log(` ${ok ? 'ok ' : 'xx '} ${label}\n → code=${code}${still ? '(行仍在)' : '(行已消失)'} ${String(resp.text).slice(0, 120)}`); if (ok) winners.push({ label, fn }); } console.log(`\n可用姿势:${winners.length ? winners.map((w) => w.label).join(' / ') : '(都不行)'}`); // ── 清理:用可用的姿势删掉所有 deltest_* 与 step0_* 残留 ────────────── console.log('\n【清理残留】'); const targets = [ ...(await rowsWithTag(COL, TAG)), ...(await rowsWithTag('1889', TAG)), ...(await rowsWithTag(COL, 'step0_credit_code')), ...(await rowsWithTag('1889', 'step0_credit_code')), ...(await rowsWithTag(COL, 'probe_cc')), ...(await rowsWithTag('1889', 'probe_cc')), ]; console.log(` 待清理 ${targets.length} 行`); if (!winners.length) { console.log(' ⚠️ 没有可用姿势,残留无法自动清理(需人工在 DMS 后台处理)'); console.log(' 残留 id:', targets.map((t) => `${t.id}(col ${t.column_id})`).join(', ')); return; } for (const row of targets) { for (const w of winners) { try { await w.fn(row.id); if (!(await rowsWithTag(String(row.column_id), row.c_credit_code)).some((r) => r.id === row.id)) break; } catch { /* 换下一种 */ } } } const left = [ ...(await rowsWithTag(COL, TAG)), ...(await rowsWithTag('1889', TAG)), ...(await rowsWithTag(COL, 'step0_credit_code')), ...(await rowsWithTag('1889', 'step0_credit_code')), ...(await rowsWithTag(COL, 'probe_cc')), ...(await rowsWithTag('1889', 'probe_cc')), ]; console.log(` 清理后剩余 ${left.length} 行${left.length ? '(' + left.map((r) => r.id).join(', ') + ')' : ' ✅'}`); }; run().catch((e) => { console.error('脚本异常:', e); process.exit(1); });