Archived
`apps/api/src/app/activity-ops/*`(service / store / instance-state / instance-steps / launch-params / summaries / view-html / view-scope)+ `routes/activities.ts`;契约 `activity-ops.ts` / `api/activity-ops.ts` (与队友按文档补的那版 43 个导出同名,多 INSTANCE_REASON_KINDS / STEP_RESULTS / launchOptionSchema); 迁移 0035_activity_ops;《API文档-活动模块》按契约回写。 设计端活动库 GET/POST `/api/gov/activities` 改名 `/api/gov/activity-packages`,`/api/gov/activities/*` 整个让给运营端(前端 import 路由对象,零改动);activity-registry CONTRACT.md 与路由总表同步。 2026-08-27 另一会话做的,一直留在工作树没入库;与同批另两条提交互相依赖,以合并提交为验证单位。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0167qWjeyBwvfdGovd7FWbCu
77 lines
4.1 KiB
TypeScript
77 lines
4.1 KiB
TypeScript
/** 清单一句话与检索 */
|
|
import { describe, expect, it } from 'vitest';
|
|
import type { InstanceSummary } from '@contracts';
|
|
import { staffIdSchema } from '@contracts';
|
|
import { fieldSpecsOf } from '../activity-ops/launch-params.ts';
|
|
import { applyInstanceFilters, clip, inputFilesOf, inputSummaryOf, outputSummaryOf, pageOf, statsOf } from '../activity-ops/summaries.ts';
|
|
|
|
const specs = fieldSpecsOf({
|
|
properties: {
|
|
customerCode: { type: 'string' },
|
|
requestDescription: { type: 'string', 'x-control': 'textarea' },
|
|
datasheets: { type: 'array', format: 'file' },
|
|
},
|
|
});
|
|
|
|
describe('inputSummaryOf / inputFilesOf / outputSummaryOf', () => {
|
|
it('textarea 那格优先;没有就最长的字符串;再没有退回来件主题;文件字段不算', () => {
|
|
expect(inputSummaryOf({ customerCode: 'CU-0142', requestDescription: ' 工业电源项目\n在用 LM2596S-5.0 ' }, specs)).toBe('工业电源项目 在用 LM2596S-5.0');
|
|
expect(inputSummaryOf({ customerCode: 'CU-0142', datasheets: [{ ref: 'r', name: 'a-very-long-file-name.pdf' }] }, specs)).toBe('CU-0142');
|
|
expect(inputSummaryOf({}, specs, 'RE: 询价')).toBe('RE: 询价');
|
|
expect(inputSummaryOf({}, specs)).toBe('');
|
|
expect(clip('x'.repeat(100))).toHaveLength(80);
|
|
expect(clip('x'.repeat(100)).endsWith('…')).toBe(true);
|
|
});
|
|
|
|
it('文件只认带 ref 的;index 跨字段连续', () => {
|
|
const files = inputFilesOf({ datasheets: [{ name: 'a.pdf', contentType: 'application/pdf', size: 10, ref: 'u-1' }, { name: 'no-ref' }, 'junk'] }, specs);
|
|
expect(files).toEqual([{ index: 0, name: 'a.pdf', contentType: 'application/pdf', size: 10, ref: 'u-1' }]);
|
|
});
|
|
|
|
it('产出:有文件摆文件名;没有摆发口;都没有 null', () => {
|
|
expect(outputSummaryOf(['报告.docx', '对照表.xlsx'], ['x'])).toBe('报告.docx、对照表.xlsx');
|
|
expect(outputSummaryOf([], ['assessment-released'])).toBe('已发口:assessment-released');
|
|
expect(outputSummaryOf([], [])).toBeNull();
|
|
});
|
|
});
|
|
|
|
const owner = { id: 's-1', name: '柴浩' };
|
|
const mk = (id: string, no: string, state: InstanceSummary['state'], startedAt: string, ownerId = 's-1'): InstanceSummary => ({
|
|
id,
|
|
no,
|
|
state,
|
|
owner: { id: staffIdSchema.parse(ownerId), name: owner.name },
|
|
input: 'x',
|
|
output: null,
|
|
startedAt,
|
|
});
|
|
const items = [
|
|
mk('t-aaa', 'INS-24', 'running', '2026-08-25T02:12:00.000Z'),
|
|
mk('t-bbb', 'INS-17', 'completed', '2026-08-24T09:03:00.000Z', 's-2'),
|
|
mk('t-ccc', 'INS-9', 'failed', '2026-08-20T01:00:00.000Z'),
|
|
];
|
|
|
|
describe('applyInstanceFilters / statsOf / pageOf', () => {
|
|
it('实例号模糊、负责人精确、状态精确、日期段含两端整日(本地时区)', () => {
|
|
expect(applyInstanceFilters(items, { id: 'ins-2' }).map((i) => i.no)).toEqual(['INS-24']);
|
|
expect(applyInstanceFilters(items, { id: 'bbb' }).map((i) => i.no)).toEqual(['INS-17']);
|
|
expect(applyInstanceFilters(items, { ownerId: 's-2' }).map((i) => i.no)).toEqual(['INS-17']);
|
|
expect(applyInstanceFilters(items, { state: 'failed' }).map((i) => i.no)).toEqual(['INS-9']);
|
|
const day = (iso: string) => {
|
|
const d = new Date(iso);
|
|
return `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, '0')}-${String(d.getDate()).padStart(2, '0')}`;
|
|
};
|
|
expect(applyInstanceFilters(items, { startedFrom: day('2026-08-24T09:03:00.000Z'), startedTo: day('2026-08-24T09:03:00.000Z') }).map((i) => i.no)).toEqual(['INS-17']);
|
|
expect(applyInstanceFilters(items, { startedFrom: day('2026-08-24T09:03:00.000Z') }).map((i) => i.no)).toEqual(['INS-24', 'INS-17']);
|
|
expect(applyInstanceFilters(items, { startedTo: day('2026-08-20T01:00:00.000Z') }).map((i) => i.no)).toEqual(['INS-9']);
|
|
expect(applyInstanceFilters(items, {})).toHaveLength(3);
|
|
});
|
|
|
|
it('统计五键齐;分页越界给空', () => {
|
|
expect(statsOf(items)).toEqual({ total: 3, byState: { pending: 0, running: 1, completed: 1, rejected: 0, failed: 1 } });
|
|
expect(pageOf(items, 1, 2).map((i) => i.no)).toEqual(['INS-24', 'INS-17']);
|
|
expect(pageOf(items, 2, 2).map((i) => i.no)).toEqual(['INS-9']);
|
|
expect(pageOf(items, 3, 2)).toEqual([]);
|
|
});
|
|
});
|