336 lines
17 KiB
Java
336 lines
17 KiB
Java
package com.kaidi.oa.seed;
|
||
|
||
import com.kaidi.oa.domain.HrAiDocDraft;
|
||
import com.kaidi.oa.domain.HrInterviewQuestion;
|
||
import com.kaidi.oa.domain.HrRecruitCandidate;
|
||
import com.kaidi.oa.domain.HrRecruitJob;
|
||
import com.kaidi.oa.repository.HrAiDocDraftRepository;
|
||
import com.kaidi.oa.repository.HrInterviewQuestionRepository;
|
||
import com.kaidi.oa.repository.HrRecruitCandidateRepository;
|
||
import com.kaidi.oa.repository.HrRecruitJobRepository;
|
||
import org.springframework.boot.CommandLineRunner;
|
||
import org.springframework.core.annotation.Order;
|
||
|
||
import java.time.Instant;
|
||
import java.time.LocalDate;
|
||
|
||
/**
|
||
* 行政·综合部——招聘与人才管理 + AI文档初稿 演示种子。
|
||
*
|
||
* 幂等:各表已有数据则跳过。
|
||
* 覆盖:
|
||
* - HrRecruitJob 3 条岗位(招聘中/面试中/已录用)
|
||
* - HrRecruitCandidate 6 条候选人(多状态,含 AI 评分)
|
||
* - HrInterviewQuestion 5 条面试题(AI 生成标记)
|
||
* - HrAiDocDraft 4 条 AI 生成文档(入职资料/岗位说明/员工手册/转正考核,多状态)
|
||
*/
|
||
@DemoSeed
|
||
@Order(203)
|
||
public class HrRecruitSeeder implements CommandLineRunner {
|
||
|
||
private final HrRecruitJobRepository jobRepo;
|
||
private final HrRecruitCandidateRepository candidateRepo;
|
||
private final HrInterviewQuestionRepository questionRepo;
|
||
private final HrAiDocDraftRepository aiDocRepo;
|
||
|
||
public HrRecruitSeeder(HrRecruitJobRepository jobRepo,
|
||
HrRecruitCandidateRepository candidateRepo,
|
||
HrInterviewQuestionRepository questionRepo,
|
||
HrAiDocDraftRepository aiDocRepo) {
|
||
this.jobRepo = jobRepo;
|
||
this.candidateRepo = candidateRepo;
|
||
this.questionRepo = questionRepo;
|
||
this.aiDocRepo = aiDocRepo;
|
||
}
|
||
|
||
@Override
|
||
public void run(String... args) {
|
||
seedJobs();
|
||
seedAiDocs();
|
||
}
|
||
|
||
// ============ 岗位 + 候选人 ============
|
||
|
||
private void seedJobs() {
|
||
if (jobRepo.count() > 0) return;
|
||
|
||
// Job 1:高级工程师(招聘中)
|
||
HrRecruitJob j1 = new HrRecruitJob();
|
||
j1.setPosition("高级结构工程师");
|
||
j1.setDept("工程技术部");
|
||
j1.setHeadcount(2);
|
||
j1.setChannel("猎聘");
|
||
j1.setRequirement("注册结构工程师,BIM,超高层,地下工程,CAD,10年以上");
|
||
j1.setSalaryRange("25k-40k");
|
||
j1.setTargetDate(LocalDate.now().plusMonths(1).toString());
|
||
j1.setStatus("招聘中");
|
||
j1.setHrOwner("王芳");
|
||
j1.setRemark("紧急招募,优先有超高层经验");
|
||
j1.setCreatedAt(Instant.now().minusSeconds(86400 * 20));
|
||
j1.setUpdatedAt(Instant.now().minusSeconds(86400 * 5));
|
||
j1 = jobRepo.save(j1);
|
||
|
||
// Job 2:项目经理(面试中)
|
||
HrRecruitJob j2 = new HrRecruitJob();
|
||
j2.setPosition("项目经理(EPC)");
|
||
j2.setDept("项目管理部");
|
||
j2.setHeadcount(1);
|
||
j2.setChannel("BOSS直聘");
|
||
j2.setRequirement("一级建造师,EPC总承包,项目全周期管理,甲方协调,8年以上");
|
||
j2.setSalaryRange("30k-45k");
|
||
j2.setTargetDate(LocalDate.now().plusWeeks(3).toString());
|
||
j2.setStatus("面试中");
|
||
j2.setHrOwner("李娜");
|
||
j2.setRemark("要求有大型市政工程经验");
|
||
j2.setCreatedAt(Instant.now().minusSeconds(86400 * 30));
|
||
j2.setUpdatedAt(Instant.now().minusSeconds(86400 * 3));
|
||
j2 = jobRepo.save(j2);
|
||
|
||
// Job 3:财务主管(已录用)
|
||
HrRecruitJob j3 = new HrRecruitJob();
|
||
j3.setPosition("财务主管");
|
||
j3.setDept("财务中心");
|
||
j3.setHeadcount(1);
|
||
j3.setChannel("内部推荐");
|
||
j3.setRequirement("注册会计师,ERP系统,财务分析,税务筹划,5年以上");
|
||
j3.setSalaryRange("18k-25k");
|
||
j3.setTargetDate(LocalDate.now().minusWeeks(1).toString());
|
||
j3.setStatus("已录用");
|
||
j3.setHrOwner("王芳");
|
||
j3.setRemark("已完成录用,等待入职");
|
||
j3.setCreatedAt(Instant.now().minusSeconds(86400 * 45));
|
||
j3.setUpdatedAt(Instant.now().minusSeconds(86400 * 7));
|
||
j3 = jobRepo.save(j3);
|
||
|
||
// 候选人 for Job 1
|
||
addCandidate(j1.getId(), "张明远", "138****5201", "硕士", 12,
|
||
"注册结构工程师,超高层钢结构,BIM建模,CAD,地下工程,设计院15年",
|
||
75, "推荐(评分:75)。学历:硕士;工作年限:12年。", "初筛通过", "35k", "2026-06-25 10:00", "技术背景强,建议安排技术面试");
|
||
addCandidate(j1.getId(), "刘雪梅", "139****6302", "本科", 8,
|
||
"结构工程,CAD,AutoCAD,工程设计,住宅项目",
|
||
62, "推荐(评分:62)。学历:本科;工作年限:8年。", "面试安排", "28k", "2026-06-28 14:00", "");
|
||
addCandidate(j1.getId(), "赵国强", "136****4403", "大专", 5,
|
||
"辅助设计,AutoCAD,钢筋翻样",
|
||
51, "不推荐(评分:51)。学历:大专;工作年限:5年。", "已淘汰", "20k", null, "学历和经验不满足要求");
|
||
|
||
// 候选人 for Job 2
|
||
addCandidate(j2.getId(), "陈志远", "137****7504", "本科", 10,
|
||
"一级建造师,EPC总承包,项目全周期,市政工程,甲方协调,合同管理",
|
||
88, "强烈推荐(评分:88)。学历:本科;工作年限:10年。", "面试通过", "40k", "2026-06-20 09:00", "各项能力突出,建议发Offer");
|
||
addCandidate(j2.getId(), "孙丽华", "135****8605", "硕士", 7,
|
||
"项目管理,EPC,一建,工程合同,进度管理",
|
||
80, "强烈推荐(评分:80)。学历:硕士;工作年限:7年。", "offer发出", "38k", "2026-06-18 14:00", "综合素质优秀");
|
||
|
||
// 候选人 for Job 3(已入职)
|
||
addCandidate(j3.getId(), "吴美玲", "132****9706", "本科", 6,
|
||
"注册会计师,ERP财务模块,财务分析,税务筹划,预算管理",
|
||
90, "强烈推荐(评分:90)。学历:本科;工作年限:6年。", "已入职", "22k", "2026-06-10 10:00", "已正式入职,表现优异");
|
||
|
||
// 面试题(for Job 1,AI生成)
|
||
if (questionRepo.count() == 0) {
|
||
addQuestion(j1.getId(), "高级结构工程师", "技能考察",
|
||
"请描述您在超高层建筑钢结构设计中遇到的最大技术挑战,以及您的解决方案。",
|
||
"考察实际技术经验;期望看到:抗风抗震计算、复杂节点处理、BIM协同经验。",
|
||
"高级");
|
||
addQuestion(j1.getId(), "高级结构工程师", "面试话术",
|
||
"请介绍您主导过的最大单体项目,包括规模、难点和您的贡献。",
|
||
"关注项目规模、复杂度与个人贡献比重;是否有超过5万平方米单体。",
|
||
"中级");
|
||
addQuestion(j2.getId(), "项目经理(EPC)", "情景题",
|
||
"假设EPC项目在施工过程中甲方提出重大变更,可能导致工期延误3个月,您如何应对?",
|
||
"考察变更管理和谈判能力;期望看到:书面确认→工期/费用影响评估→变更谈判→合同签署。",
|
||
"高级");
|
||
addQuestion(j2.getId(), "项目经理(EPC)", "综合素质",
|
||
"您如何处理团队内部成员工作风格冲突的问题?请举例说明。",
|
||
"考察领导力与团队管理;期望看到积极沟通、明确责任、结果导向。",
|
||
"中级");
|
||
addQuestion(j3.getId(), "财务主管", "笔试题",
|
||
"请说明税务筹划与逃税的本质区别,并举例说明合法的增值税筹划方法。",
|
||
"考察税务专业知识合规意识;期望看到清晰区分合规边界,举例一般纳税人优惠政策。",
|
||
"高级");
|
||
}
|
||
}
|
||
|
||
private void addCandidate(Long jobId, String name, String phone, String edu, int years,
|
||
String summary, int score, String aiReport,
|
||
String status, String salary, String interviewTime, String note) {
|
||
HrRecruitCandidate c = new HrRecruitCandidate();
|
||
c.setJobId(jobId);
|
||
c.setName(name);
|
||
c.setPhone(phone);
|
||
c.setEducation(edu);
|
||
c.setWorkYears(years);
|
||
c.setResumeSummary(summary);
|
||
c.setAiScore(score);
|
||
c.setAiReport(aiReport);
|
||
c.setStatus(status);
|
||
c.setExpectedSalary(salary);
|
||
c.setInterviewTime(interviewTime);
|
||
c.setInterviewNote(note);
|
||
c.setCreatedAt(Instant.now().minusSeconds(86400 * (long)(Math.random() * 15 + 1)));
|
||
c.setUpdatedAt(Instant.now().minusSeconds(3600));
|
||
candidateRepo.save(c);
|
||
}
|
||
|
||
private void addQuestion(Long jobId, String position, String qType,
|
||
String content, String refAnswer, String difficulty) {
|
||
HrInterviewQuestion q = new HrInterviewQuestion();
|
||
q.setJobId(jobId);
|
||
q.setPosition(position);
|
||
q.setQuestionType(qType);
|
||
q.setContent(content);
|
||
q.setReferenceAnswer(refAnswer);
|
||
q.setAiGenerated(true);
|
||
q.setDifficulty(difficulty);
|
||
q.setStatus("启用");
|
||
q.setCreator("AI系统");
|
||
q.setCreatedAt(Instant.now().minusSeconds(86400 * 10));
|
||
questionRepo.save(q);
|
||
}
|
||
|
||
// ============ AI 文档初稿 ============
|
||
|
||
private void seedAiDocs() {
|
||
if (aiDocRepo.count() > 0) return;
|
||
|
||
// 1. 入职资料(已发布)
|
||
HrAiDocDraft d1 = new HrAiDocDraft();
|
||
d1.setDocType("入职资料");
|
||
d1.setRelatedPosition(null);
|
||
d1.setRelatedDept("工程技术部");
|
||
d1.setTitle("工程技术部 新员工入职资料包(AI初稿)");
|
||
d1.setContent("# 工程技术部 新员工入职资料包\n\n"
|
||
+ "> AI生成初稿,已经HR审核。\n\n"
|
||
+ "## 一、欢迎信\n\n"
|
||
+ "亲爱的新同事,\n\n热烈欢迎您加入凯迪科技工程技术部大家庭!\n\n"
|
||
+ "## 二、报到须知\n\n"
|
||
+ "**报到时间**:请于入职通知书上注明的日期 09:00 前到达人力资源部。\n\n"
|
||
+ "**携带材料**:身份证、学历证书、学位证书、离职证明、近期照片 2 张、银行卡。\n\n"
|
||
+ "## 三、专属材料\n\n"
|
||
+ "技术部员工还需提供:职业资格证书(建造师/结构工程师等)原件复印件。\n\n"
|
||
+ "## 四、入职第一周\n\n"
|
||
+ "- Day1:行政手续 + 系统开通\n- Day2:公司文化宣贯\n"
|
||
+ "- Day3-4:技术规范学习 + 导师结对\n- Day5:与主管沟通试用期目标\n\n"
|
||
+ "---\n*由 AI 辅助生成,HR 已审核。*");
|
||
d1.setGenContext("类型=入职资料 岗位= 部门=工程技术部 背景=职业资格证书");
|
||
d1.setAiGenerated(true);
|
||
d1.setStatus("已发布");
|
||
d1.setReviewNote("内容准确,补充了专业证书要求,审核通过");
|
||
d1.setOperator("HR-王芳");
|
||
d1.setCreatedAt(Instant.now().minusSeconds(86400 * 30));
|
||
d1.setUpdatedAt(Instant.now().minusSeconds(86400 * 25));
|
||
aiDocRepo.save(d1);
|
||
|
||
// 2. 岗位说明(已审核)
|
||
HrAiDocDraft d2 = new HrAiDocDraft();
|
||
d2.setDocType("岗位说明");
|
||
d2.setRelatedPosition("高级结构工程师");
|
||
d2.setRelatedDept("工程技术部");
|
||
d2.setTitle("高级结构工程师 岗位说明书(AI初稿)");
|
||
d2.setContent("# 高级结构工程师 岗位说明书\n\n"
|
||
+ "> AI生成初稿,已审核待发布。\n\n"
|
||
+ "## 一、基本信息\n\n"
|
||
+ "| 项目 | 内容 |\n| ---- | ---- |\n"
|
||
+ "| 岗位名称 | 高级结构工程师 |\n"
|
||
+ "| 所属部门 | 工程技术部 |\n"
|
||
+ "| 直接上级 | 技术总工 |\n"
|
||
+ "| 岗位类别 | 专业技术类 |\n"
|
||
+ "| 编制人数 | 2 |\n\n"
|
||
+ "## 二、岗位职责\n\n"
|
||
+ "1. 负责大型建筑结构设计、审核与优化\n"
|
||
+ "2. 参与超高层/复杂结构的技术攻关\n"
|
||
+ "3. 指导初中级工程师完成设计任务\n"
|
||
+ "4. 负责结构设计图纸的质量把控\n"
|
||
+ "5. 参与投标技术方案编写\n\n"
|
||
+ "## 三、任职资格\n\n"
|
||
+ "**学历**:本科及以上,土木/结构工程专业\n\n"
|
||
+ "**证书**:注册结构工程师(一级)优先\n\n"
|
||
+ "**经验**:10年以上相关经验,有超高层项目经验\n\n"
|
||
+ "**技能**:BIM、CAD、SAP2000/PKPM、Revit\n\n"
|
||
+ "---\n*由 AI 辅助生成,已审核待签发。*");
|
||
d2.setGenContext("类型=岗位说明 岗位=高级结构工程师 部门=工程技术部 背景=注册结构工程师,BIM,超高层");
|
||
d2.setAiGenerated(true);
|
||
d2.setStatus("已审核");
|
||
d2.setReviewNote("内容基本准确,已补充证书要求,待部门负责人签发");
|
||
d2.setOperator("HR-李娜");
|
||
d2.setCreatedAt(Instant.now().minusSeconds(86400 * 15));
|
||
d2.setUpdatedAt(Instant.now().minusSeconds(86400 * 10));
|
||
aiDocRepo.save(d2);
|
||
|
||
// 3. 员工手册(草稿)
|
||
HrAiDocDraft d3 = new HrAiDocDraft();
|
||
d3.setDocType("员工手册");
|
||
d3.setRelatedPosition(null);
|
||
d3.setRelatedDept("凯迪科技有限公司");
|
||
d3.setTitle("凯迪科技有限公司 员工手册(AI初稿·2026版)");
|
||
d3.setContent("# 凯迪科技有限公司 员工手册\n\n"
|
||
+ "> AI生成草稿,待法务及HR审核。\n\n"
|
||
+ "## 第一章 公司简介\n\n"
|
||
+ "凯迪科技有限公司是一家专业从事工程技术、建设管理及 ERP 系统集成的企业,"
|
||
+ "秉承「诚信、专业、创新」的核心价值观。\n\n"
|
||
+ "## 第二章 行为规范\n\n"
|
||
+ "- 诚实守信,不弄虚作假\n"
|
||
+ "- 尊重同事,维护良好工作氛围\n"
|
||
+ "- 保守公司机密,不泄露商业信息\n\n"
|
||
+ "## 第三章 考勤制度\n\n"
|
||
+ "- 工作时间:周一至周五 09:00-18:00\n"
|
||
+ "- 打卡方式:APP/指纹识别\n"
|
||
+ "- 迟到:15分钟以上计考勤异常\n\n"
|
||
+ "## 第四章 假期\n\n"
|
||
+ "| 假期 | 时长 |\n| ---- | ---- |\n"
|
||
+ "| 年假 | 5-15天 |\n| 病假 | 按医院证明 |\n| 婚假 | 3天 |\n"
|
||
+ "| 产假 | 按国家规定 |\n\n"
|
||
+ "## 第五章 薪酬福利\n\n"
|
||
+ "每月15日发薪;五险一金按规定缴纳;节日发放礼品/礼金。\n\n"
|
||
+ "---\n*由 AI 辅助生成,待法务审核。*");
|
||
d3.setGenContext("类型=员工手册 部门=凯迪科技有限公司");
|
||
d3.setAiGenerated(true);
|
||
d3.setStatus("草稿");
|
||
d3.setReviewNote(null);
|
||
d3.setOperator("HR-王芳");
|
||
d3.setCreatedAt(Instant.now().minusSeconds(86400 * 5));
|
||
d3.setUpdatedAt(Instant.now().minusSeconds(86400 * 5));
|
||
aiDocRepo.save(d3);
|
||
|
||
// 4. 转正考核(草稿)
|
||
HrAiDocDraft d4 = new HrAiDocDraft();
|
||
d4.setDocType("转正考核");
|
||
d4.setRelatedPosition("财务主管");
|
||
d4.setRelatedDept("财务中心");
|
||
d4.setTitle("财务主管 转正考核表(AI初稿)");
|
||
d4.setContent("# 财务主管 转正考核表\n\n"
|
||
+ "> AI生成草稿,请直属主管填写评分。\n\n"
|
||
+ "## 基本信息\n\n"
|
||
+ "| 项目 | 内容 |\n| ---- | ---- |\n"
|
||
+ "| 员工姓名 | 吴美玲 |\n"
|
||
+ "| 岗位 | 财务主管 |\n"
|
||
+ "| 入职日期 | 2026-06-10 |\n"
|
||
+ "| 试用期截止 | 2026-09-10 |\n"
|
||
+ "| 直属主管 | 财务总监 |\n\n"
|
||
+ "## 试用期目标\n\n"
|
||
+ "| 目标 | 计划完成 | 实际完成 | 完成率 |\n"
|
||
+ "| ---- | ---- | ---- | ---- |\n"
|
||
+ "| 完成岗位培训 | 入职第1周 | | |\n"
|
||
+ "| 接管月度账务 | 入职第1月 | | |\n"
|
||
+ "| 独立完成税务申报 | 入职第2月 | | |\n"
|
||
+ "| 完成半年财务分析 | 试用期末 | | |\n\n"
|
||
+ "## 综合评分\n\n"
|
||
+ "| 维度 | 权重 | 得分 |\n"
|
||
+ "| ---- | ---- | ---- |\n"
|
||
+ "| 专业能力 | 40% | |\n"
|
||
+ "| 工作态度 | 20% | |\n"
|
||
+ "| 团队协作 | 20% | |\n"
|
||
+ "| 执行力 | 20% | |\n\n"
|
||
+ "**转正建议**:[ ] 正式转正 [ ] 延长 [ ] 不予转正\n\n"
|
||
+ "---\n*由 AI 辅助生成,待主管填写。*");
|
||
d4.setGenContext("类型=转正考核 岗位=财务主管 部门=财务中心");
|
||
d4.setAiGenerated(true);
|
||
d4.setStatus("草稿");
|
||
d4.setReviewNote(null);
|
||
d4.setOperator("HR-李娜");
|
||
d4.setCreatedAt(Instant.now().minusSeconds(86400 * 3));
|
||
d4.setUpdatedAt(Instant.now().minusSeconds(86400 * 3));
|
||
aiDocRepo.save(d4);
|
||
}
|
||
}
|