From 271d43a5a5edaac01797f477d9f825dcfd093a77 Mon Sep 17 00:00:00 2001 From: zk Date: Thu, 26 Mar 2026 16:46:13 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A4=A7=E7=89=88=E6=9C=AC=E4=BF=AE=E6=94=B9?= =?UTF-8?q?=E5=89=8D=E7=9A=84=E6=96=B9=E6=A1=88=E6=94=B9=E9=80=A0=20?= =?UTF-8?q?=E5=AE=9E=E4=BD=93=E7=B1=BB=E4=BF=AE=E6=94=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../specs/scoring-system-refactor/改造方案.md | 163 ++++++++++++++++++ .../jiayunet/mapper/MajorCategoryMapper.java | 13 ++ .../main/java/org/jiayunet/pojo/po/Job.java | 7 + .../org/jiayunet/pojo/po/MajorCategory.java | 32 ++++ .../java/org/jiayunet/pojo/po/SkillTag.java | 5 +- .../org/jiayunet/pojo/po/UserProfile.java | 28 ++- .../org/jiayunet/pojo/vo/UserHonorsVo.java | 27 +++ 7 files changed, 266 insertions(+), 9 deletions(-) create mode 100644 .kiro/specs/scoring-system-refactor/改造方案.md create mode 100644 manager/src/main/java/org/jiayunet/mapper/MajorCategoryMapper.java create mode 100644 manager/src/main/java/org/jiayunet/pojo/po/MajorCategory.java create mode 100644 manager/src/main/java/org/jiayunet/pojo/vo/UserHonorsVo.java diff --git a/.kiro/specs/scoring-system-refactor/改造方案.md b/.kiro/specs/scoring-system-refactor/改造方案.md new file mode 100644 index 0000000..c8fd6b8 --- /dev/null +++ b/.kiro/specs/scoring-system-refactor/改造方案.md @@ -0,0 +1,163 @@ +# 评分体系改造方案 + +## 核心公式 + +`MatchScore = Sedu × 30% + Sexp × 40% + Sskill × 30%` + +--- + +## 一、表结构改动 + +### 1. 新建表:bg_major_category(专业分类,三级树形) + +```sql +create table bg_major_category ( + id bigint not null comment 'ID' primary key, + name varchar(50) not null comment '类型名称', + root_id bigint not null comment '根节点ID,顶级=自身ID', + parent_id bigint not null comment '父级ID,0=顶级', + level int not null comment '层级 1=一级 2=二级 3=三级' +) comment '专业分类' row_format = DYNAMIC; +``` + +基于《教育部普通高等学校本科专业目录》建立层级索引,AI 将简历/岗位专业归一化到标准 ID。 + +### 2. bg_skill_tag — 改造 + +| 操作 | 字段 | 说明 | +|------|------|------| +| 删除 | `category_id` | 不再挂岗位分类 | +| 新增 | `name` 唯一索引 | 去重用 | + +定位从"人工预定义"变为"AI 提取自动入库"。提取技能时先查表去重,不存在则 insert,拿 ID 写关联表。 + +### 3. bg_job — 新增字段 + +| 字段 | 类型 | 备注 | +|------|------|------| +| `required_major_ids` | json | 要求专业ID数组,关联 bg_major_category | +| `major_sensitivity` | tinyint | 专业敏感度 0=不限 1=优先 2=强制 | + +### 4. bg_user_profile — 新增字段 + +| 字段 | 类型 | 备注 | +|------|------|------| +| `major_ids` | json | 用户专业ID数组,关联 bg_major_category | +| `school_rank` | tinyint | 学校等级 1=C9/985/QS前50 2=211/双一流/QS前200 3=普通一本/QS前500 4=其他 | +| `company_prestige` | tinyint | 公司背书 1=名企 2=普通实习 3=校内活动 4=无 | +| `experience_duration` | tinyint | 经历时长 1=≥3月 2=1-3月 3=≤1月 | +| `role_depth` | tinyint | 职责深度 1=主导/创新 2=执行/应用 3=辅助/学习 | +| `output_quality` | tinyint | 量化产出 1=有量化结果 2=有具体产出 3=纯描述 | +| `honors` | json | 荣誉/竞赛/论文,格式:{"national":[],"provincial":[],"school":[],"paper":[]} | + +以上字段全部不在前端展示,纯用于评分计算,由 AI 分析简历后写入。 + +### 5. bg_user_profile — 废弃字段 + +| 字段 | 说明 | +|------|------| +| `work_years` | 被经历质量维度替代 | +| `experience_industry_ids` | 行业前置过滤废除 | + +### 6. 不变的表 + +- `bg_job_skill_tag_relation` — 结构不变,数据来源从预定义标签变为 AI 自动入库标签 +- `bg_user_profile_skill_tag_relation` — 同上 + +--- + +## 二、评分算法 + +### 维度1:教育背景(Sedu)— 权重 30% + +学校等级分(Srank)与专业相关度(Smajor)的权重由专业敏感度决定: + +| 专业敏感度 | 公式 | 特殊规则 | +|-----------|------|---------| +| 强制(2) | Sedu = Srank × 0.4 + Smajor × 0.6 | 若 Smajor < 40,Sedu = Sedu × 0.5 | +| 优先(1) | Sedu = Srank × 0.6 + Smajor × 0.4 | 无 | +| 不限(0) | Sedu = Srank × 1.0 | Smajor 默认100,不减分 | + +学校等级映射:1→100, 2→80, 3→60, 4→40 + +专业相关度(基于 bg_major_category 树形匹配): +- 完全匹配(同三级专业)→ 100 +- 相关对口(同二级专业类)→ 70 +- 跨专业相关(同一级学科门类)→ 30 +- 不相关 → 0 + +### 维度2:经历质量(Sexp)— 权重 40% + +`Sexp = 公司背书 × 30% + 时长 × 10% + 经历深度 × 60%` + +公司背书映射:1→100, 2→60, 3→30, 4→0 +经历时长映射:1→100, 2→60, 3→30 + +经历深度(60%)内部拆解: + +| 子项 | 占比 | 说明 | +|------|------|------| +| 语义相关度 | 40% | AI 判断经历与岗位方向的相关性 | +| 职责深度 | 25% | 映射:1→100, 2→80, 3→40 | +| 量化产出 | 20% | 映射:1→100, 2→70, 3→40 | +| 荣誉加分 | 15% | 封顶累加制,总分不超过100 | + +荣誉分值规则: +- 国奖级(national):每项 +15-20 +- 省奖/大厂赛(provincial):每项 +8-10 +- 院校级/专业证(school):每项 +3-5 +- 顶刊顶会论文:每项 +15-20,普通论文:每项 +8-10 + +### 维度3:技能匹配(Sskill)— 权重 30% + +废除产品方案中的向量距离、技能近亲表、分层技能池。 + +采用简化方案: +- AI 自由提取技能 + prompt demo 约束颗粒度 +- 提取结果入 bg_skill_tag(去重),写关联表 +- 匹配时通过 skill_tag_id 做集合碰撞 +- 公式:`Sskill = (匹配数量 / 岗位要求数量) × 100` + +--- + +## 三、数据清洗改造 + +### 岗位清洗(JobCleanService) + +改动: +- 第二次 AI 调用从"预定义标签匹配"改为"自由提取 + prompt demo 约束" +- 新增:提取要求专业 → 归一化到 bg_major_category ID +- 新增:提取专业敏感度 +- 技能入库逻辑:查 bg_skill_tag 去重 → insert → 写关联表 + +### 用户简历分析(UserSkillTagMatchService) + +改动: +- 可能简化为一次 AI 调用,直接从简历全文提取 +- 新增:识别学校等级、公司背书、经历时长、职责深度、量化产出、荣誉 +- 新增:提取用户专业 → 归一化到 bg_major_category ID +- 技能入库逻辑同上 + +### DictCacheService + +- 删掉 skillTagMap、getSkillTagText()、getSkillTagIds() +- 新增:加载 bg_major_category 缓存(供专业归一化使用) + +--- + +## 四、影响文件汇总 + +| 文件 | 改动 | +|------|------| +| SkillTag.java | 删 categoryId | +| Job.java | 加 requiredMajorIds、majorSensitivity | +| UserProfile.java | 加 7 个新字段,删 workYears、experienceIndustryIds | +| MajorCategory.java | 新建 PO | +| MajorCategoryMapper.java | 新建 Mapper | +| DictCacheService | 删技能缓存,加专业缓存 | +| JobCleanService | 改技能提取 + 新增专业/敏感度提取 | +| JobCleanTransactionService | 改技能入库逻辑 | +| UserSkillTagMatchService | 大改,新增多维度识别 | +| JobMatchService | 重写,三维度算法全换 | +| JobMatchScoreDto | 改字段(教育/经历/技能) | +| JobDto / JobDetailDto | matchScore 范围可能变化 | diff --git a/manager/src/main/java/org/jiayunet/mapper/MajorCategoryMapper.java b/manager/src/main/java/org/jiayunet/mapper/MajorCategoryMapper.java new file mode 100644 index 0000000..abbc8d0 --- /dev/null +++ b/manager/src/main/java/org/jiayunet/mapper/MajorCategoryMapper.java @@ -0,0 +1,13 @@ +package org.jiayunet.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.jiayunet.pojo.po.MajorCategory; + +/** + * 专业分类Mapper + * + * @author zk + */ +@Mapper +public interface MajorCategoryMapper extends CommonMapper { +} diff --git a/manager/src/main/java/org/jiayunet/pojo/po/Job.java b/manager/src/main/java/org/jiayunet/pojo/po/Job.java index 9f36b64..d8537d4 100644 --- a/manager/src/main/java/org/jiayunet/pojo/po/Job.java +++ b/manager/src/main/java/org/jiayunet/pojo/po/Job.java @@ -63,6 +63,13 @@ public class Job { /** 要求的行业经验ID,关联bg_industry */ private Long requiredIndustryId; + /** 要求专业ID数组,关联bg_major_category */ + @TableField(typeHandler = JacksonTypeHandler.class) + private List requiredMajorIds; + + /** 专业敏感度 0=不限 1=优先 2=强制 */ + private Integer majorSensitivity; + /** 来源链接 */ private String sourceUrl; diff --git a/manager/src/main/java/org/jiayunet/pojo/po/MajorCategory.java b/manager/src/main/java/org/jiayunet/pojo/po/MajorCategory.java new file mode 100644 index 0000000..98eceb1 --- /dev/null +++ b/manager/src/main/java/org/jiayunet/pojo/po/MajorCategory.java @@ -0,0 +1,32 @@ +package org.jiayunet.pojo.po; + +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; +import lombok.Data; + +/** + * 专业分类表(bg_major_category) + *

基于教育部专业目录,三级树形结构

+ * + * @author zk + */ +@Data +@TableName(value = "bg_major_category") +public class MajorCategory { + + @TableId(type = IdType.ASSIGN_ID) + private Long id; + + /** 类型名称 */ + private String name; + + /** 根节点ID,顶级=自身ID */ + private Long rootId; + + /** 父级ID,0=顶级 */ + private Long parentId; + + /** 层级 1=一级 2=二级 3=三级 */ + private Integer level; +} diff --git a/manager/src/main/java/org/jiayunet/pojo/po/SkillTag.java b/manager/src/main/java/org/jiayunet/pojo/po/SkillTag.java index 9266764..39c99e5 100644 --- a/manager/src/main/java/org/jiayunet/pojo/po/SkillTag.java +++ b/manager/src/main/java/org/jiayunet/pojo/po/SkillTag.java @@ -18,9 +18,6 @@ public class SkillTag { @TableId(type = IdType.ASSIGN_ID) private Long id; - /** 标签名称 */ + /** 标签名称(唯一索引) */ private String name; - - /** 所属岗位类型ID */ - private Long categoryId; } diff --git a/manager/src/main/java/org/jiayunet/pojo/po/UserProfile.java b/manager/src/main/java/org/jiayunet/pojo/po/UserProfile.java index f658e43..271049a 100644 --- a/manager/src/main/java/org/jiayunet/pojo/po/UserProfile.java +++ b/manager/src/main/java/org/jiayunet/pojo/po/UserProfile.java @@ -7,6 +7,8 @@ import com.baomidou.mybatisplus.annotation.TableName; import com.baomidou.mybatisplus.extension.handlers.JacksonTypeHandler; import lombok.Data; +import org.jiayunet.pojo.vo.UserHonorsVo; + import java.time.Instant; import java.util.List; @@ -46,12 +48,28 @@ public class UserProfile { /** 作品集链接 */ private String portfolioUrl; - /** 工作年限(不展示,用于适配度计算) */ - private Integer workYears; - - /** 拥有经验的行业ID列表(不展示,用于适配度计算) */ + /** 用户专业ID数组,关联bg_major_category(不展示,用于适配度计算) */ @TableField(typeHandler = JacksonTypeHandler.class) - private List experienceIndustryIds; + private List majorIds; + + /** 学校等级 1=C9/985/QS前50 2=211/双一流/QS前200 3=普通一本/QS前500 4=其他(不展示,用于适配度计算) */ + private Integer schoolRank; + + /** 公司背书 1=名企 2=普通实习 3=校内活动 4=无(不展示,用于适配度计算) */ + private Integer companyPrestige; + + /** 经历时长 1=≥3月 2=1-3月 3=≤1月(不展示,用于适配度计算) */ + private Integer experienceDuration; + + /** 职责深度 1=主导/创新 2=执行/应用 3=辅助/学习(不展示,用于适配度计算) */ + private Integer roleDepth; + + /** 量化产出 1=有量化结果 2=有具体产出 3=纯描述(不展示,用于适配度计算) */ + private Integer outputQuality; + + /** 荣誉/竞赛/论文(不展示,用于适配度计算) */ + @TableField(typeHandler = JacksonTypeHandler.class) + private UserHonorsVo honors; /** 技能标签列表 */ @TableField(typeHandler = JacksonTypeHandler.class) diff --git a/manager/src/main/java/org/jiayunet/pojo/vo/UserHonorsVo.java b/manager/src/main/java/org/jiayunet/pojo/vo/UserHonorsVo.java new file mode 100644 index 0000000..eea6a36 --- /dev/null +++ b/manager/src/main/java/org/jiayunet/pojo/vo/UserHonorsVo.java @@ -0,0 +1,27 @@ +package org.jiayunet.pojo.vo; + +import lombok.Data; + +import java.util.List; + +/** + * 用户荣誉/竞赛/论文VO + *

存储在 bg_user_profile.honors JSON 字段中

+ * + * @author zk + */ +@Data +public class UserHonorsVo { + + /** 国奖级:ACM-ICPC、数学建模国赛、互联网+金奖、挑战杯国赛、国家奖学金等 */ + private List national; + + /** 省奖/大厂赛:省赛一/二等奖、蓝桥杯、阿里/腾讯专项赛等 */ + private List provincial; + + /** 院校级/专业证:优秀毕业生、院系奖学金、CPA/CFA、软考高级等 */ + private List school; + + /** 论文:顶刊顶会/普通论文 */ + private List paper; +}