|
@@ -55,6 +55,7 @@ const {
|
|
|
deleteDmsContent,
|
|
deleteDmsContent,
|
|
|
DMS_COLUMN_SESSION,
|
|
DMS_COLUMN_SESSION,
|
|
|
DMS_COLUMN_RECORD,
|
|
DMS_COLUMN_RECORD,
|
|
|
|
|
+ DMS_MODEL_RECORD,
|
|
|
enterpriseInfo,
|
|
enterpriseInfo,
|
|
|
resolveVisitorLabel,
|
|
resolveVisitorLabel,
|
|
|
} = await import('./_dms.mjs');
|
|
} = await import('./_dms.mjs');
|
|
@@ -361,5 +362,91 @@ console.log('\n【7】title 字段');
|
|
|
await deleteDmsSession(SID2);
|
|
await deleteDmsSession(SID2);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+// ── 8. 附件路径落库:c_image / c_file(用户 2026-09-23:新建了这两列,要求把路径存进去)──
|
|
|
|
|
+console.log('\n【8】附件路径 → c_image / c_file');
|
|
|
|
|
+{
|
|
|
|
|
+ const SID10 = SESSION_ID + '_att';
|
|
|
|
|
+ const REC_A = 'recA_' + Date.now().toString(36);
|
|
|
|
|
+ const REC_B = 'recB_' + Date.now().toString(36);
|
|
|
|
|
+ const IMG_1 = '/static/photo/1889-c_question-aaaa.png';
|
|
|
|
|
+ const IMG_2 = '/static/photo/1889-c_question-bbbb.jpg';
|
|
|
|
|
+ const FILE_1 = '/static/file/1889-c_question-cccc.xlsx';
|
|
|
|
|
+
|
|
|
|
|
+ const rowOf = async (columnId, field, value) =>
|
|
|
|
|
+ (
|
|
|
|
|
+ await searchDmsContents(columnId, {
|
|
|
|
|
+ search: [{ field, searchType: 1, content: { value } }],
|
|
|
|
|
+ page: 0,
|
|
|
|
|
+ pageSize: 5,
|
|
|
|
|
+ })
|
|
|
|
|
+ )[0];
|
|
|
|
|
+
|
|
|
|
|
+ // 8.1 ⚠️ 先确认这两列**确实在模型里** —— 这是这次改动的硬前提。
|
|
|
|
|
+ // `updateContent` 遇到模型里不存在的列会**静默丢弃整个 patch**(返回仍 code 200),
|
|
|
|
|
+ // 见 docs/reference/DMS_API.md 的写入陷阱:真那样,补 answer 这条主链路会被连累。
|
|
|
|
|
+ {
|
|
|
|
|
+ const res = await fetch(`${base}/model/getModelById?modelId=${DMS_MODEL_RECORD}`, { method: 'POST' });
|
|
|
|
|
+ const body = await res.json().catch(() => ({}));
|
|
|
|
|
+ let fields = {};
|
|
|
|
|
+ try {
|
|
|
|
|
+ fields = JSON.parse(body?.content?.fieldList || '{}');
|
|
|
|
|
+ } catch {
|
|
|
|
|
+ /* 取不到就当空 —— 下面的断言会报出来 */
|
|
|
|
|
+ }
|
|
|
|
|
+ check('模型 2038 里 c_image 列存在(否则前端不该发它)', !!fields.c_image, Object.keys(fields).join(','));
|
|
|
|
|
+ check('模型 2038 里 c_file 列存在', !!fields.c_file);
|
|
|
|
|
+ check('两列都是文本列(多值用逗号拼是安全的)',
|
|
|
|
|
+ fields.c_image?.frontType === 'text' && fields.c_file?.frontType === 'text',
|
|
|
|
|
+ `${fields.c_image?.frontType} / ${fields.c_file?.frontType}`);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // 8.2 带图 + 带文件:两张图 + 一个文件
|
|
|
|
|
+ enterpriseInfo.value = { credit_code: TEST_CREDIT, name: '验证企业' };
|
|
|
|
|
+ await upsertDmsRecord({
|
|
|
|
|
+ recordId: REC_A,
|
|
|
|
|
+ sessionId: SID10,
|
|
|
|
|
+ question: '这两张图里是什么?',
|
|
|
|
|
+ images: [IMG_1, IMG_2],
|
|
|
|
|
+ files: [FILE_1],
|
|
|
|
|
+ });
|
|
|
|
|
+ let row = await rowOf(DMS_COLUMN_RECORD, 'c_record_id', REC_A);
|
|
|
|
|
+ check('c_image 存的是两张图的相对路径(逗号分隔)', row?.c_image === `${IMG_1},${IMG_2}`, JSON.stringify(row?.c_image));
|
|
|
|
|
+ check('c_file 存的是文件的相对路径', row?.c_file === FILE_1, JSON.stringify(row?.c_file));
|
|
|
|
|
+ check('question 照旧', row?.c_question === '这两张图里是什么?', JSON.stringify(row?.c_question));
|
|
|
|
|
+
|
|
|
|
|
+ // 8.3 补 answer 时**不能**把附件路径冲掉、也不能把整条 patch 弄失败
|
|
|
|
|
+ await upsertDmsRecord({ recordId: REC_A, sessionId: SID10, answer: '第一轮回答。' });
|
|
|
|
|
+ row = await rowOf(DMS_COLUMN_RECORD, 'c_record_id', REC_A);
|
|
|
|
|
+ check('补 answer 后 c_image 还在(没被覆盖)', row?.c_image === `${IMG_1},${IMG_2}`, JSON.stringify(row?.c_image));
|
|
|
|
|
+ check('补 answer 后 c_file 还在', row?.c_file === FILE_1, JSON.stringify(row?.c_file));
|
|
|
|
|
+ check('**补 answer 本身写进去了**(附件列没连累主链路)', row?.c_answer === '第一轮回答。', JSON.stringify(row?.c_answer));
|
|
|
|
|
+
|
|
|
|
|
+ // 8.4 没有附件的一轮:这两列保持不写(别写空串、别写 null)
|
|
|
|
|
+ await upsertDmsRecord({ recordId: REC_B, sessionId: SID10, question: '只发文字', images: [], files: [] });
|
|
|
|
|
+ const rowB = await rowOf(DMS_COLUMN_RECORD, 'c_record_id', REC_B);
|
|
|
|
|
+ check('无附件时 c_image 不写', !rowB?.c_image, JSON.stringify(rowB?.c_image));
|
|
|
|
|
+ check('无附件时 c_file 不写', !rowB?.c_file, JSON.stringify(rowB?.c_file));
|
|
|
|
|
+
|
|
|
|
|
+ // 8.5 脏值过滤:空串 / 纯空格 / 夹带的空值都不能拼进去
|
|
|
|
|
+ const REC_C = 'recC_' + Date.now().toString(36);
|
|
|
|
|
+ await upsertDmsRecord({
|
|
|
|
|
+ recordId: REC_C,
|
|
|
|
|
+ sessionId: SID10,
|
|
|
|
|
+ question: '脏值',
|
|
|
|
|
+ images: [' ', IMG_1, ''],
|
|
|
|
|
+ files: undefined,
|
|
|
|
|
+ });
|
|
|
|
|
+ const rowC = await rowOf(DMS_COLUMN_RECORD, 'c_record_id', REC_C);
|
|
|
|
|
+ check('脏值被过滤(只剩真路径,没有多余逗号)', rowC?.c_image === IMG_1, JSON.stringify(rowC?.c_image));
|
|
|
|
|
+ check('没传 files 时不写 c_file', !rowC?.c_file, JSON.stringify(rowC?.c_file));
|
|
|
|
|
+
|
|
|
|
|
+ enterpriseInfo.value = null;
|
|
|
|
|
+ for (const rec of [REC_A, REC_B, REC_C]) {
|
|
|
|
|
+ const r = await rowOf(DMS_COLUMN_RECORD, 'c_record_id', rec);
|
|
|
|
|
+ if (r?.id) await deleteDmsContent(DMS_COLUMN_RECORD, r.id);
|
|
|
|
|
+ }
|
|
|
|
|
+ console.log(' (本节测试数据已清理)');
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
console.log(`\n===== 通过 ${pass} 项,失败 ${fail} 项 =====`);
|
|
console.log(`\n===== 通过 ${pass} 项,失败 ${fail} 项 =====`);
|
|
|
process.exit(fail ? 1 : 0);
|
|
process.exit(fail ? 1 : 0);
|