| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225 |
- /**
- * Step-0:切换前端到 DMS 之前的**小样本实测**。
- *
- * 为什么需要它:DMS 的 content 读写接口在文档里全是「📄 未实测」
- * (参数来自 SKILL.md/Apifox,OpenAPI 里是 @RequestBody 抓不到),
- * 而前端的 upsert 依赖三件没被证实的事:
- * ① addContent 是否要求 c_id(模型里 must=true)→ 若强校验,整个方案要改
- * ② addContent 返回体里有没有记录 uuid(有则省掉一次反查)
- * ③ timestamp 写入格式、states 默认值、长文本往返
- *
- * 怎么跑(在项目根目录;token 不落盘,从参数读):
- * node harness/tools/dms-step0-verify.mjs <DMS_TOKEN>
- *
- * 脚本会自行清理它写入的测试数据(session_id/record_id 以 step0_ 开头)。
- */
- const HOST = process.env.DMS_HOST || '121.43.55.7:10081';
- const BASE = `http://${HOST}/dms`;
- const TOKEN = process.argv[2] || process.env.DMS_TOKEN || '';
- const COL_SESSION = 1887;
- const MODEL_SESSION = 2034;
- const COL_RECORD = 1889;
- const MODEL_RECORD = 2038;
- const SUFFIX = Date.now().toString(36);
- const SESSION_ID = `step0_sess_${SUFFIX}`;
- const RECORD_ID = `step0_rec_${SUFFIX}`;
- const CREDIT = 'step0_credit_code';
- if (!TOKEN) {
- console.error('缺 token。用法:node harness/tools/dms-step0-verify.mjs <DMS_TOKEN>');
- process.exit(2);
- }
- const results = [];
- const note = (name, ok, detail) => {
- results.push({ name, ok, detail });
- console.log(` ${ok === null ? '? ' : ok ? 'ok ' : 'FAIL'} ${name}${detail ? ' → ' + detail : ''}`);
- };
- async function call(method, path, form) {
- const init = { method, headers: { token: TOKEN } };
- if (form) {
- init.headers['Content-Type'] = 'application/x-www-form-urlencoded';
- init.body = new URLSearchParams(form).toString();
- }
- 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 };
- }
- const search = (columnId, field, value, extra = {}) =>
- call('POST', '/content/selectContentList', {
- columnId: String(columnId),
- page: '0',
- pageSize: '10',
- search: JSON.stringify([{ field, searchType: 1, content: { value } }]),
- ...extra,
- });
- const cleanup = async (columnId, field, value) => {
- const r = await search(columnId, field, value);
- const rows = r.json?.content?.data || [];
- for (const row of rows) {
- if (row?.id) await call('DELETE', `/content/delContentById?id=${encodeURIComponent(row.id)}`);
- }
- return rows.length;
- };
- const run = async () => {
- console.log(`\nDMS Step-0 实测 ${BASE} 标记=${SUFFIX}\n`);
- // ── 1. addContent:不传 c_id 会不会被拒(最高优先级)────────────────────
- console.log('【1】addContent(1887 会话)不传 c_id');
- const addSession = await call('POST', '/content/addContent', {
- columnId: String(COL_SESSION),
- modelId: String(MODEL_SESSION),
- content: JSON.stringify({
- c_credit_code: CREDIT,
- c_session_id: SESSION_ID,
- c_title: 'step0 会话标题',
- c_source: 'zhaoshang',
- }),
- });
- console.log(' 原始响应:', addSession.text.slice(0, 400));
- const addOk = addSession.json?.code === 200;
- note('addContent 不传 c_id 被接受', addOk, addOk ? '' : `code=${addSession.json?.code}`);
- const returnedUuid = addSession.json?.content?.id || addSession.json?.content?.uuid || null;
- note('addContent 响应回传记录 uuid', !!returnedUuid, returnedUuid ? String(returnedUuid) : '(未回传,需反查)');
- // ── 2. 反查:states 默认值 / c_id 是否自动生成 ──────────────────────────
- console.log('\n【2】反查(不带 states)');
- const found = await search(COL_SESSION, 'c_session_id', SESSION_ID);
- const rows = found.json?.content?.data || [];
- note('写后能查回(不传 states)', rows.length > 0, `命中 ${rows.length} 行, code=${found.json?.code}`);
- const row = rows[0] || {};
- console.log(' 行内容:', JSON.stringify(row).slice(0, 500));
- note('c_id 由 DMS 自动生成', row.c_id !== undefined && row.c_id !== null && row.c_id !== '', `c_id=${row.c_id}`);
- note('记录 uuid 存在行里(反查可取)', !!row.id, String(row.id || ''));
- const uuid = row.id || returnedUuid;
- // ── 3. timestamp 形态 ──────────────────────────────────────────────────
- console.log('\n【3】timestamp');
- note('c_created_at 未传时是否自动填充', row.c_created_at !== undefined && row.c_created_at !== null, `读到 ${JSON.stringify(row.c_created_at)}`);
- const tsFormats = ['2026-09-17 21:30:00', '2026-09-17T21:30:00+08:00'];
- for (const ts of tsFormats) {
- const r = await call('POST', '/content/updateContent', {
- columnId: String(COL_SESSION),
- modelId: String(MODEL_SESSION),
- content: JSON.stringify({ id: uuid, c_updated_at: ts }),
- });
- const back = await search(COL_SESSION, 'c_session_id', SESSION_ID);
- const got = back.json?.content?.data?.[0]?.c_updated_at;
- note(`写入 "${ts}"`, r.json?.code === 200 && got != null, `code=${r.json?.code}, 读回=${JSON.stringify(got)}`);
- }
- // ── 4. updateContent 改标题 ───────────────────────────────────────────
- console.log('\n【4】updateContent(带 id=uuid)');
- const upd = await call('POST', '/content/updateContent', {
- columnId: String(COL_SESSION),
- modelId: String(MODEL_SESSION),
- content: JSON.stringify({ id: uuid, c_title: 'step0 改后的标题' }),
- });
- const afterUpd = await search(COL_SESSION, 'c_session_id', SESSION_ID);
- const newTitle = afterUpd.json?.content?.data?.[0]?.c_title;
- note('改标题生效', upd.json?.code === 200 && newTitle === 'step0 改后的标题', `code=${upd.json?.code}, c_title=${JSON.stringify(newTitle)}`);
- // ── 5. 长文本 + 中文 + 协议标记往返 ────────────────────────────────────
- console.log('\n【5】1889 长文本往返(含协议标记)');
- const longAnswer = [
- '<!-- POLICY_TABLE {"data":[{"title":"测试政策"}]} -->',
- '<scope title="正在思考中...">\n</scope>',
- '第一段中文回答。'.repeat(120),
- '<question-cards>{"kind":"company_need"}</question-cards>',
- '结尾行',
- ].join('\n');
- const addRecord = await call('POST', '/content/addContent', {
- columnId: String(COL_RECORD),
- modelId: String(MODEL_RECORD),
- content: JSON.stringify({
- c_credit_code: CREDIT,
- c_session_id: SESSION_ID,
- c_record_id: RECORD_ID,
- c_question: 'step0 问题:高新技术企业奖励是多少?',
- c_answer: longAnswer,
- c_source: 'zhaoshang',
- }),
- });
- note('1889 addContent 成功', addRecord.json?.code === 200, `code=${addRecord.json?.code}`);
- const recRows = (await search(COL_RECORD, 'c_record_id', RECORD_ID)).json?.content?.data || [];
- const rec = recRows[0] || {};
- note('长文本逐字节一致', rec.c_answer === longAnswer, `原长 ${longAnswer.length} / 读回 ${String(rec.c_answer || '').length}`);
- note('中文问题往返一致', rec.c_question === 'step0 问题:高新技术企业奖励是多少?', JSON.stringify(rec.c_question));
- // ── 6. 反馈字段 update ────────────────────────────────────────────────
- console.log('\n【6】1889 反馈字段');
- const fb = await call('POST', '/content/updateContent', {
- columnId: String(COL_RECORD),
- modelId: String(MODEL_RECORD),
- content: JSON.stringify({
- id: rec.id,
- c_feedback_status: 2,
- c_feedback_option: '没有回答我的问题',
- c_feedback_remark: 'step0 备注',
- c_feedback_at: '2026-09-17 21:31:00',
- }),
- });
- const recAfter = (await search(COL_RECORD, 'c_record_id', RECORD_ID)).json?.content?.data?.[0] || {};
- note(
- '反馈字段写入生效',
- fb.json?.code === 200 && Number(recAfter.c_feedback_status) === 2 && recAfter.c_feedback_option === '没有回答我的问题',
- `code=${fb.json?.code}, status=${recAfter.c_feedback_status}, option=${JSON.stringify(recAfter.c_feedback_option)}, at=${JSON.stringify(recAfter.c_feedback_at)}`
- );
- // ── 7. 中文 search 精确匹配(访客 访客_xxx 依赖)──────────────────────
- console.log('\n【7】中文精确 search');
- const cn = await search(COL_SESSION, 'c_credit_code', CREDIT);
- note('中文/下划线 credit_code 精确命中', (cn.json?.content?.data || []).length > 0, `命中 ${(cn.json?.content?.data || []).length} 行`);
- // ── 8. pageSize 上限 ─────────────────────────────────────────────────
- console.log('\n【8】pageSize');
- const big = await call('POST', '/content/selectContentList', {
- columnId: String(COL_RECORD),
- page: '0',
- pageSize: '200',
- search: JSON.stringify([{ field: 'c_session_id', searchType: 1, content: { value: SESSION_ID } }]),
- });
- note('pageSize=200 被接受', big.json?.code === 200 || big.json?.code === 202, `code=${big.json?.code}, 返回 ${(big.json?.content?.data || []).length} 行`);
- // ── 9. delContentById 参数形态 ───────────────────────────────────────
- console.log('\n【9】delContentById 参数形态');
- const probe = await call('DELETE', `/content/delContentById?id=${encodeURIComponent(rec.id)}`);
- const stillThere = ((await search(COL_RECORD, 'c_record_id', RECORD_ID)).json?.content?.data || []).length;
- note('DELETE ?id=<uuid> 生效', stillThere === 0, `code=${probe.json?.code}, 剩余 ${stillThere} 行`);
- // ── 10. 清理 1887 测试行 ─────────────────────────────────────────────
- console.log('\n【10】清理');
- const removed = await cleanup(COL_SESSION, 'c_session_id', SESSION_ID);
- const left = await cleanup(COL_RECORD, 'c_record_id', RECORD_ID);
- note('测试数据已清理', true, `1887 删 ${removed} 行 / 1889 删 ${left} 行`);
- // ── 汇总 ─────────────────────────────────────────────────────────────
- const failed = results.filter((r) => r.ok === false);
- console.log(`\n===== 汇总:${results.length - failed.length}/${results.length} 通过 =====`);
- if (failed.length) {
- console.log('未通过项:');
- failed.forEach((f) => console.log(' - ' + f.name + (f.detail ? ' ' + f.detail : '')));
- }
- console.log('\n关键结论(回填到计划/记录里):');
- console.log(' addContent 是否需 c_id :', addOk ? '不需要(自动生成)' : '被拒 → 需 DMS 侧调整模型');
- console.log(' addContent 是否回 uuid :', returnedUuid ? '回传,可省一次反查' : '不回传,需 search 反查');
- console.log(' 时间戳 : 见【3】读回值');
- };
- run().catch((err) => {
- console.error('\n脚本异常:', err);
- process.exit(1);
- });
|