// Copyright (c) 未来飞马 // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at https://mozilla.org/MPL/2.0/. // // Trademark Notice: // The MPL-2.0 license grants copyright permissions for source code only. // It does NOT grant any rights to use trademarks including "未来飞马", // "Harness Loop", "RSI", and associated slogan "让AI进化提前发生,让AI落地快人一步". // Any use of these trademarks requires separate written permission. /** * 测试夹具生成器:现场合成 .docx / .pptx / .png,落盘到指定目录。 * * 刻意不在仓库里存任何二进制素材(.gitignore 也排除了图片)——夹具全部即时生成, * 内容为合成的假数据,不含任何真实 PII。 */ import fs from 'node:fs'; import path from 'node:path'; import { spawnSync } from 'node:child_process'; import { ensureDir, resolvePython } from '../lib.mjs'; import { makeTinyPng } from '../case-intake.mjs'; const PY_BUILD = ` import sys, zipfile, os target = sys.argv[1] doc_xml = ''' IC 计算机科学 考前冲刺案例 客户在读帝国理工本科二年级,期末前两周找到我们,担心挂科。 跟进状态:异议处理中。客户觉得贵,家长不放心老师。 结果:期末提分 18 分,家长很满意,已续课。 查看成绩单 ''' doc_rels = ''' ''' pres_xml = ''' ''' pres_rels = ''' ''' def slide(title, body, with_pic, rid): pic = ('' % rid) if with_pic else '' return ''' %s %s %s ''' % (title, body, pic) slide_rels = ''' ''' png = bytes.fromhex( "89504e470d0a1a0a0000000d49484452000000010000000108060000001f15c489" "0000000a49444154789c6300010000050001" "0d0a2db40000000049454e44ae426082") with zipfile.ZipFile(os.path.join(target, 'case.docx'), 'w') as zf: zf.writestr('[Content_Types].xml', '') zf.writestr('word/document.xml', doc_xml) zf.writestr('word/_rels/document.xml.rels', doc_rels) zf.writestr('word/media/image1.png', png) with zipfile.ZipFile(os.path.join(target, 'deck.pptx'), 'w') as zf: zf.writestr('[Content_Types].xml', '') zf.writestr('ppt/presentation.xml', pres_xml) zf.writestr('ppt/_rels/presentation.xml.rels', pres_rels) zf.writestr('ppt/slides/slide1.xml', slide('案例合集', '英国本科 方案推荐中 导师匹配', True, 'rId2')) zf.writestr('ppt/slides/slide2.xml', slide('结果与反馈', '期末提分 出分反馈 家长认可', False, 'rId2')) zf.writestr('ppt/slides/_rels/slide1.xml.rels', slide_rels) zf.writestr('ppt/media/image1.png', png) print('ok') `; /** * 生成夹具。 * @param {string} dir 目标目录 * @returns {{docx:string, pptx:string, images:string[]}} */ export function makeFixtures(dir) { const target = ensureDir(dir); const python = resolvePython(); const run = spawnSync(python, ['-c', PY_BUILD, target], { encoding: 'utf8' }); if (run.status !== 0) { throw new Error(`夹具生成失败:${run.stderr || run.stdout}`); } const images = []; // 九宫格:9 张同尺寸、同一批连发(时间戳相邻)→ 应判为同一组 3x3 const base = 1_700_000_000_000; for (let i = 0; i < 9; i++) { const file = path.join(target, `grid-${String(i + 1).padStart(2, '0')}.png`); fs.writeFileSync(file, makeTinyPng(400, 400)); fs.utimesSync(file, new Date(base + i * 1000), new Date(base + i * 1000)); images.push(file); } // 另一批:不同尺寸 + 不同时间 → 不应并进九宫格 const other = path.join(target, 'other-01.png'); fs.writeFileSync(other, makeTinyPng(1600, 900)); fs.utimesSync(other, new Date(base - 3 * 86_400_000), new Date(base - 3 * 86_400_000)); images.push(other); return { docx: path.join(target, 'case.docx'), pptx: path.join(target, 'deck.pptx'), images }; }