L2考核落地:人才测评整合为L2考核track,新增查重初筛与e2e验证

- 新增 L2考核 track(7维标准/100分):选题难度赋分cap、功能完整性地板线、合格判定
- 人才测评整合进 L2考核:移除 L2/L3 两级认定与 question_id 机制
- 新增 l2-topics 选题元数据服务与 config/l2-topics.json(11命题题+自选题)
- 新增查重初筛 plagiarism-detect(MD5精确比对+归一化相似度+提交行为,仅告警)
- 修复 L2 维度 DIM_FILE_FILTERS 缺失导致 AI 协作记录证据漏喂
- e2e:修复 Windows spawn、DB 隔离,L2 用例 27 项全绿
This commit is contained in:
hangshuo652
2026-08-23 20:58:56 +08:00
parent 4da7044c4b
commit 6a2419d4a4
26 changed files with 1306 additions and 662 deletions
+29 -22
View File
@@ -175,55 +175,62 @@ test.describe('PDF 下载', () => {
});
// ══════════════════════════════════════
// 4. 人才测评 L2/L3 分表展示
// 4. L2考核:条目列表合格/不合格徽章展示
// ══════════════════════════════════════
test.describe('人才测评 L2/L3 分表', () => {
test.describe('L2考核 合格/不合格徽章', () => {
let pid = ''; let eid = ''; let token = '';
test.beforeAll(async ({ request }) => {
token = await apiToken(request);
const proj = await request.post('http://localhost:3002/api/projects', {
headers: { Authorization: `Bearer ${token}` }, data: { name: `l2l3-${UNIQUE}`, track: '人才测评' },
headers: { Authorization: `Bearer ${token}` }, data: { name: `l2badge-${UNIQUE}`, track: 'L2考核' },
});
pid = (await proj.json()).id;
// 删除自动标准,替换为自定义 Q2 标准
const list = await request.get(`http://localhost:3002/api/projects/${pid}/standards`, {
headers: { Authorization: `Bearer ${token}` },
});
for (const s of await list.json()) {
await request.delete(`http://localhost:3002/api/projects/${pid}/standards/${s.id}`, { headers: { Authorization: `Bearer ${token}` } });
}
await request.post(`http://localhost:3002/api/projects/${pid}/standards`, {
headers: { Authorization: `Bearer ${token}` },
data: { name: 'l2-std', category_tag: '人才测评', content: '## 功能完整性(40分)\n## 设计文档(10分)\n## [Q2] LLM生成问卷(15分)' },
});
const entry = await request.post(`http://localhost:3002/api/projects/${pid}/entries`, {
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
data: { title: 'l2l3-entry', repo_url: `file://C:\\l2-${UNIQUE}`, question_id: 'Q2' },
data: { title: 'l2-entry', repo_url: `file://C:\\l2-${UNIQUE}`, selected_topic: '11' },
});
eid = (await entry.json()).id;
// 播种评审结果:总分 70 ≥60 且 功能完整性 25 ≥15 → 合格
await request.put(`http://localhost:3002/api/projects/${pid}/entries/${eid}/force-review`, {
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
data: {
dimensions: [
{ name: '功能完整性', score: 40, maxScore: 40, group: 'common' },
{ name: '设计文档', score: 10, maxScore: 10, group: 'common' },
{ name: 'LLM生成问卷', score: 15, maxScore: 15, group: 'Q2' },
{ name: '功能完整性', score: 25, maxScore: 30 },
{ name: '设计文档', score: 8, maxScore: 10 },
{ name: '测试用例与测试结果', score: 7, maxScore: 10 },
{ name: 'AI协作过程记录', score: 10, maxScore: 15 },
{ name: '技术选型与范式运用', score: 10, maxScore: 15 },
{ name: '代码质量+README', score: 6, maxScore: 10 },
{ name: '业务场景理解与需求分析', score: 4, maxScore: 10 },
],
},
});
// 触发合格判定(PUT /report 重算 final_level
await request.put(`http://localhost:3002/api/projects/${pid}/entries/${eid}/report`, {
headers: { Authorization: `Bearer ${token}`, 'Content-Type': 'application/json' },
data: { dimensions: [
{ name: '功能完整性', score: 25, maxScore: 30 },
{ name: '设计文档', score: 8, maxScore: 10 },
{ name: '测试用例与测试结果', score: 7, maxScore: 10 },
{ name: 'AI协作过程记录', score: 10, maxScore: 15 },
{ name: '技术选型与范式运用', score: 10, maxScore: 15 },
{ name: '代码质量+README', score: 6, maxScore: 10 },
{ name: '业务场景理解与需求分析', score: 4, maxScore: 10 },
] },
});
});
test.afterAll(async ({ request }) => {
await request.delete(`http://localhost:3002/api/projects/${pid}?force=true`, { headers: { Authorization: `Bearer ${token}` } });
});
test('详情面板分别展示 L2共通 与 L3追加 分表', async ({ page }) => {
test('列表展示「合格」徽章(L2考核 判定)', async ({ page }) => {
await login(page);
await page.goto(`/project/${pid}`);
await page.click('.title-cell');
await expect(page.getByText('L2共通评分')).toBeVisible();
await expect(page.getByText('L3追加评分')).toBeVisible();
await expect(page.getByText('合格', { exact: true })).toBeVisible();
// 条目还展示选题徽章
await expect(page.getByText('命题 11')).toBeVisible();
});
});