日期: 2026-06-04
简历信息模块(resume)是招聘管理系统中个人基本信息模块的子模块,负责管理求职者的简历信息及其关联的子表数据。该模块前端基于 Vue3 + Ant Design Vue,采用 JeecgBoot 的前后端分离架构。
| 文件 | 用途 |
|---|---|
components/ResumeInfoForm.vue |
简历信息表单组件,包含主表字段和 8 个子表 JVxeTable |
components/ResumeInfoModal.vue |
简历信息弹窗组件,包裹 ResumeInfoForm |
components/ResumeListModal.vue |
简历列表弹窗组件,查看个人信息关联的简历列表 |
| 层次 | 文件 |
|---|---|
| Service Impl | service/impl/ResumeInfoServiceImpl.java |
| 日期 | 内容 | 涉及文件 | 说明 |
|---|---|---|---|
| 2026-06-04 | 默认简历互斥逻辑 | ResumeInfoServiceImpl.java |
新增/编辑简历时,若设置当前简历为默认,则同人员其他简历自动置为非默认 |
需求描述: 一个求职者只能有一份默认简历。在新增或编辑简历时,如果用户将当前简历的"是否默认简历"设置为"是"(
isDefault = "1"),保存成功后系统应自动将该人员的其他所有简历的默认状态清除(置为 "0"),确保同一人员下最多只有一份默认简历。
实现方案: 在 ResumeInfoServiceImpl 中新增私有方法 clearOtherDefaultResumes(),使用 MyBatis-Plus 的
lambdaUpdate() 按人员ID匹配、排除当前操作的简历ID,将匹配到的其他简历的 isDefault 字段批量更新为 "0"。在
saveMainAndSub() 和 updateMainAndSub() 方法中,保存或更新主表成功后,判断 dto.getIsDefault() == "1" 时调用该方法。
涉及文件:
jeecg-boot-module/jeecg-module-zjrs/.../personal/service/impl/ResumeInfoServiceImpl.java主要逻辑代码:
// saveMainAndSub() 中:保存主表后调用
if ("1".equals(dto.getIsDefault())) {
clearOtherDefaultResumes(dto.getPersonalId(), main.getId());
}
// updateMainAndSub() 中:更新主表后调用
if ("1".equals(dto.getIsDefault())) {
clearOtherDefaultResumes(dto.getPersonalId(), main.getId());
}
/**
* 清除指定人员下除当前简历外的其他简历的默认状态
*/
private void clearOtherDefaultResumes(String personalId, String excludeResumeId) {
this.lambdaUpdate()
.eq(ResumeInfo::getPersonalId, personalId)
.ne(ResumeInfo::getId, excludeResumeId)
.set(ResumeInfo::getIsDefault, "0")
.update();
}
问题描述: 在简历列表弹窗(ResumeListModal)中,先点击"修改"后关闭弹窗,再点击"查看"时,表单字段和子表编辑功能未处于禁用/只读状态。
复现条件: 页面刷新后先点修改再点查看才会出现;页面刷新后直接点查看则正常禁用。
原因分析: Ant Design Vue 的 <a-modal> 默认 destroyOnClose=false,弹窗关闭时内部 ResumeInfoForm 组件仍然挂载在 DOM
中。
formDisabled=false 的状态挂载disableSubmit 被设为 true,但由于组件实例未重新创建,props 的响应式更新未能正确触发
JFormContainer 的 <fieldset disabled> 状态变更修复方案: 使用 :key 方式强制组件重建(与 PersonalInfoModal 相同方案)。
formKey ref 和 nextFormKey() 方法<ResumeInfoForm> 上绑定 :key="formKey"nextFormKey() 递增 key,触发 Vue 重建组件实例v-if 控制),ref 在重建完成后自然可用涉及文件:
jeecgboot-vue3/src/views/recruitment/personal/components/ResumeInfoModal.vue
formKey ref(初始值 0)nextFormKey() 方法,递增 formKey<ResumeInfoForm> 上添加 :key="formKey" 绑定add() 和 edit() 方法中在设置 visible=true 之前调用 nextFormKey()问题描述: 检查 ResumeInfoForm 中是否存在与 PersonalInfoForm 相同的日期字符串未转为 dayjs 对象的问题。
检查结果: ResumeInfoForm.vue 中日期字段已正确处理,无需修复。
validDate 字段在 edit() 方法中已将字符串转为 dayjs() 对象startDate、endDate、issueDate)已通过 convertDateFields() 工具方法统一转换需求描述: 求职意向(expectedIndustry、expectedPosition、positionName、expectedSalary、workLocation、workNature)之前被错误地设计为子表(RESUME_INTENTION),实际需求中这些字段应属于简历主表(RESUME_INFO)的一对一字段,不存在一对多关系。
修复方案:
resumeIntentionList;ResumeInfoPage 同步调整resumeIntentionMapper 的注入及 save/update/delete 中的子表 CRUD 代码resumeIntentionMapper 注入、queryIntentionListByMainId 接口、queryById 中的意向列表查询涉及文件:
.docs/sql/个人信息与简历信息.sql — 求职意向字段合并入主表,删除子表 DDLjeecg-boot-module/jeecg-module-zjrs/.../personal/entity/ResumeInfo.java — 新增6个求职意向字段jeecg-boot-module/jeecg-module-zjrs/.../personal/vo/ResumeInfoDTO.java — 新增求职意向字段,移除 resumeIntentionListjeecg-boot-module/jeecg-module-zjrs/.../personal/vo/ResumeInfoPage.java — 新增求职意向字段,移除
resumeIntentionListjeecg-boot-module/jeecg-module-zjrs/.../personal/service/impl/ResumeInfoServiceImpl.java — 移除意向子表注入及 CRUDjeecg-boot-module/jeecg-module-zjrs/.../personal/controller/ResumeInfoController.java — 移除意向子表注入及接口jeecgboot-vue3/src/views/recruitment/personal/components/ResumeInfoForm.vue — 移除意向子表 tab,新增6个主表表单字段jeecgboot-vue3/src/views/recruitment/personal/ResumeInfo.data.ts — 移除 intentionColumns 导出
-
删除文件:ResumeIntention.java、ResumeIntentionMapper.java、ResumeIntentionMapper.xml、IResumeIntentionService.java、ResumeIntentionServiceImpl.java需求描述: 按照框架开发规范(后端规范 第七节 + 前端规范第四节),对 RESUME_INFO 表中所有 NOT NULL 字段,前后端均应有对应的数据校验。
检查结果: 以下6个 NOT NULL 字段(排除自增主键 ID 和自动填充的 CREATE_TIME/UPDATE_TIME)均缺少校验:
| 字段 | SQL | 后端JSR303 | 前端required |
|---|---|---|---|
| personalId | NOT NULL | 缺失 | 缺失(系统内部填充,非用户输入) |
| expectedPosition | NOT NULL | 缺失 | 缺失 |
| positionName | NOT NULL | 缺失 | 缺失 |
| expectedSalary | NOT NULL | 缺失 | 缺失 |
| workLocation | NOT NULL | 缺失 | 缺失 |
| workNature | NOT NULL | 缺失 | 缺失 |
修复方案:
import jakarta.validation.constraints.NotBlank/NotNull,对 NOT NULL 字段添加注解:
@NotBlank(message = "...")@NotNull(message = "...")add() 和 edit() 方法的 DTO 参数添加 @Validated 注解,触发 JSR303 校验validatorRules 新增 expectedPosition、positionName、expectedSalary、workLocation、workNature 的
required: true 规则涉及文件:
jeecg-boot-module/jeecg-module-zjrs/.../personal/vo/ResumeInfoDTO.java — 新增 JSR303 注解及 importjeecg-boot-module/jeecg-module-zjrs/.../personal/controller/ResumeInfoController.java — add/edit 加 @Validatedjeecgboot-vue3/src/views/recruitment/personal/components/ResumeInfoForm.vue — validatorRules 新增5个必填规则需求描述: 简历表单中子表区域原来使用 <a-tabs> 组件以标签页方式展示7个子表(工作经历、教育经历等),新需求改为不使用
tabs,所有子表纵向依次排列,各子表之间用 <a-divider> 分隔。
修复方案: 在 ResumeInfoForm.vue 中:
<a-tabs> 及 <a-tab-pane> 包裹层,每个子表的标题改用 <a-divider orientation="left"> 显示,JVxeTable
直接放在下方activeKey ref 和 handleChangeTabs() 方法(该方法用于 tab 切换时重置滚动位置,改为纵向排列后不再需要)涉及文件:
jeecgboot-vue3/src/views/recruitment/personal/components/ResumeInfoForm.vue — 移除 tabs,子表纵向排布,移除
activeKey/handleChangeTabs需求描述: 将简历信息表单中部分字段从普通输入框(input/radio)改为下拉选择框,选项数据来源于数据字典。涉及以下字段:
主表字段:
| 字段 | 字典编码 | 变更前 | 变更后 |
|---|---|---|---|
| workNature(工作性质) | WorkNature | 硬编码 radio 组(全职/实习) | <a-select> 下拉选择 |
子表 JVxeTable 列:
| 子表 | 列 | 字典编码 | 变更前 | 变更后 |
|---|---|---|---|---|
| 工作经历 | companyType(单位类型) | CompanyType | <a-input> |
JVxeTypes.slot 下拉选择 |
| 工作经历 | companySize(人员规模) | CompanySize | <a-input> |
JVxeTypes.slot 下拉选择 |
| 教育经历 | education(学历) | Education | <a-input> |
JVxeTypes.slot 下拉选择 |
| 教育经历 | degree(学位) | Degree | <a-input> |
JVxeTypes.slot 下拉选择 |
| 教育经历 | eduSystem(学制) | EduSystem | <a-input> |
JVxeTypes.slot 下拉选择 |
| 职业技能 | skillLevel(技能等级) | SkillLevel | <a-input> |
JVxeTypes.slot 下拉选择 |
| 职称情况 | level(级别) | TitleLevel | <a-input> |
JVxeTypes.slot 下拉选择 |
实现方案:
type 从 JVxeTypes.input 改为 JVxeTypes.slot
,并指定对应的 slotName(如 companyTypeSlot、educationSlot 等),placeholder 同步调整为"请选择"。useDict composable,预加载 WorkNature、CompanyType、CompanySize、Education、Degree、EduSystem、
SkillLevel、TitleLevel 共8个数据字典computed 为每个字典创建对应的选项列表(如 workNatureOptions、companyTypeOptions 等)workNature 字段由硬编码的 <a-radio-group> 改为 <a-select :options="workNatureOptions" /><a-select> 绑定对应的字典选项,通过 triggerChange 将选中值同步回表格数据源涉及文件:
jeecgboot-vue3/src/views/recruitment/personal/ResumeInfo.data.ts — 7个字典列由 JVxeTypes.input 改为
JVxeTypes.slot,添加 slotName 配置jeecgboot-vue3/src/views/recruitment/personal/components/ResumeInfoForm.vue — 引入 useDict 加载8个字典,主表
workNature 改为 <a-select>,为5个 JVxeTable 添加字典下拉插槽模板