答辩级 UI 改造:指挥台驾驶舱/证据地图/演化生命线/成长路线图/候选审核台
- 驾驶舱改'人才能力变化指挥台':变化事件流主图+可信度卡+行动卡+播报卡 - 图谱子图同心分层布局、技能变化角标、系统发现折叠入右栏、主操作收敛 - 演化页双 Tab(岗位报告/全岗位扫描)+ 能力生命线 Swimlane + 更替流 - 学习路径成长路线图:概要条/示范路线/预计投入/标记完成联动 - 新增候选审核台抽屉 + GET /api/stats/credibility - 简历裸姓名/专硕提取修复(桌面真实简历验收通过)+ BGE-M3 加载提示
This commit is contained in:
@@ -38,6 +38,20 @@ def test_graph_endpoint(client):
|
||||
assert any(e.get("rel") == "BELONGS_TO" for e in data["edges"])
|
||||
|
||||
|
||||
def test_credibility_stats(client):
|
||||
"""数据可信度卡:有效 JD / 独立企业 / 来源类型 / 人工复核率 / 异常剔除率。"""
|
||||
r = client.get("/api/stats/credibility")
|
||||
assert r.status_code == 200
|
||||
d = r.json()["data"]
|
||||
assert d["jd_total"] >= d["jd_valid"] > 0
|
||||
assert d["companies"] >= 5
|
||||
assert d["sources"] >= 1 and len(d["source_names"]) == d["sources"]
|
||||
assert 0.0 <= d["excluded_rate"] <= 1.0
|
||||
assert d["reviewed"] >= 0 and d["pending"] >= 0
|
||||
if d["review_rate"] is not None:
|
||||
assert 0.0 <= d["review_rate"] <= 1.0
|
||||
|
||||
|
||||
def test_jobs_list_and_filter(client):
|
||||
r = client.get("/api/jobs")
|
||||
assert r.status_code == 200
|
||||
|
||||
@@ -70,5 +70,28 @@ const heat = Array.from({ length: 30 }, (_, i) => [`20${10 + i}Q1`, i]);
|
||||
eq(heat.slice(-8).length, 8, "近8季切片");
|
||||
eq(heat.slice(-Infinity).length, 30, "全部范围");
|
||||
|
||||
/* candidateTrust:候选可信等级四指标(JD≥15 / 企业≥5 / 增长趋势 / 差异度≥0.40) */
|
||||
const candidateTrust = loadFn("candidateTrust");
|
||||
const stageEffortText = loadFn("stageEffortText");
|
||||
const existJobs = [{ title: "Java开发工程师", required_skills: ["Java", "MySQL"], preferred_skills: ["Redis"] }];
|
||||
const strong = candidateTrust({
|
||||
source_count: 31, company_count: 12,
|
||||
support_windows: ["2025Q1", "2025Q2", "2025Q3", "2025Q4", "2026Q1", "2026Q2"],
|
||||
required_skills: ["RAG", "向量检索"], preferred_skills: [],
|
||||
}, existJobs);
|
||||
eq(strong.dots.length, 4, "四指标点");
|
||||
eq(strong.level, "高", "四指标全达标为高");
|
||||
const weak = candidateTrust({
|
||||
source_count: 3, company_count: 1, support_windows: ["2026Q2"],
|
||||
required_skills: ["Java", "MySQL"], preferred_skills: ["Redis"],
|
||||
}, existJobs);
|
||||
eq(weak.level, "低", "弱证据且与既有岗位重合为低");
|
||||
eq(weak.nearest.title, "Java开发工程师", "识别出最相近既有岗位");
|
||||
eq(weak.distinctness, 0, "技能完全重合时差异度为 0");
|
||||
|
||||
/* stageEffortText:阶段预计投入(每周约 8 小时) */
|
||||
eq(stageEffortText(2), "2 周 / 约 16 小时", "阶段投入文案");
|
||||
eq(stageEffortText(1), "1 周 / 约 8 小时", "单周投入文案");
|
||||
|
||||
console.log(`\n${pass} passed, ${fail} failed`);
|
||||
process.exit(fail ? 1 : 0);
|
||||
|
||||
@@ -91,3 +91,17 @@ def test_resume_parse_skills_and_years():
|
||||
assert profile.years == 5.0
|
||||
assert set(profile.skills) == {"Java", "MySQL", "Docker"}
|
||||
assert "138" not in profile.raw_masked_text
|
||||
|
||||
|
||||
def test_resume_bare_name_and_edu_alias():
|
||||
"""真实简历排版:首行裸姓名(无"姓名:"前缀)可识别并脱敏;专硕归一为硕士。"""
|
||||
from app.match.resume import parse_resume
|
||||
text = (" 王伟航 \n出生年月:2003年10月 现居地:辽宁大连\n"
|
||||
"联系电话:19293819303 电子邮件:wwh@example.com\n"
|
||||
"东北财经大学 金融科技(专硕) 2025.09-至今\n"
|
||||
"职业技能:熟练掌握Python、Hadoop等数据分析技能。")
|
||||
p = parse_resume(text)
|
||||
assert p.name == "王**" and "王伟航" not in p.raw_masked_text # 裸姓名识别+脱敏
|
||||
assert p.education == "硕士" # 专硕→硕士
|
||||
assert p.address.startswith("辽宁大连")
|
||||
assert "Python" in p.skills
|
||||
|
||||
Reference in New Issue
Block a user