diff --git a/.kiro/steering/项目结构说明.md b/.kiro/steering/项目结构说明.md index 4810d83..4d87f46 100644 --- a/.kiro/steering/项目结构说明.md +++ b/.kiro/steering/项目结构说明.md @@ -39,10 +39,12 @@ offerpie/back-end │ └─ pojo/ │ ├─ param/ │ │ ├─ userProfile/ # 个人资料入参(UserProfileParam、各子表Param) +│ │ ├─ resume/ # 简历入参(ResumeParam、各子表Param、ResumeSubTableParam) │ │ └─ job/ # 岗位相关入参(JobIntentionParam、JobQueryParam) │ ├─ dto/ │ │ ├─ SmsLoginDto.java # 短信登录入参(mobileNumber + code + inviteCode) │ │ ├─ userProfile/ # 个人资料出参(UserProfileDto、各子表Dto) +│ │ ├─ resume/ # 简历出参(ResumeDto、ResumeListItemDto、各子表Dto) │ │ └─ job/ # 岗位相关出参(JobIntentionDto、JobDto、JobMatchScoreDto) │ └─ vo/ │ ├─ LoginVo.java # 登录返回(userId + nick) diff --git a/client-api/src/main/java/org/jiayunet/controller/UserResumeController.java b/client-api/src/main/java/org/jiayunet/controller/UserResumeController.java index a83770b..c0d4606 100644 --- a/client-api/src/main/java/org/jiayunet/controller/UserResumeController.java +++ b/client-api/src/main/java/org/jiayunet/controller/UserResumeController.java @@ -1,18 +1,22 @@ package org.jiayunet.controller; import lombok.AllArgsConstructor; -import org.jiayunet.pojo.dto.resume.ResumeListItemDto; +import org.jiayunet.pojo.dto.resume.*; +import org.jiayunet.pojo.param.resume.*; +import org.jiayunet.pojo.po.*; import org.jiayunet.service.UserResumeService; import org.springframework.beans.BeanUtils; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; +import javax.validation.Valid; import java.util.List; import java.util.stream.Collectors; /** * 用户简历接口 - * 提供简历列表查询等功能 + *

提供简历列表、主表及5张子表(教育/工作/实习/项目/竞赛)的查询与保存、简历删除功能

+ *

所有保存接口支持自动创建:前端不传resumeId时自动创建新简历并返回resumeId

* * @author zk */ @@ -24,9 +28,9 @@ public class UserResumeController { private UserResumeService userResumeService; - /** - * 查询当前用户的简历列表 - */ + // ==================== 简历列表 ==================== + + /** 查询当前用户的简历列表 */ @GetMapping("/list") public List listResume() { return userResumeService.listResume().stream().map(po -> { @@ -35,4 +39,144 @@ public class UserResumeController { return dto; }).collect(Collectors.toList()); } + + // ==================== 主表 ==================== + + /** 查询简历主表 */ + @GetMapping + public ResumeDto getResume(@RequestParam Long resumeId) { + UserResume po = userResumeService.getResume(resumeId); + ResumeDto dto = new ResumeDto(); + BeanUtils.copyProperties(po, dto); + return dto; + } + + /** 保存简历主表,返回resumeId(resumeId为空则自动创建新简历) */ + @PostMapping + public Long saveResume(@Validated @RequestBody ResumeParam param) { + UserResume po = new UserResume(); + BeanUtils.copyProperties(param, po); + return userResumeService.saveResume(po, param.getResumeId()); + } + + /** 删除简历(主表 + 全部子表) */ + @PostMapping("/delete") + public void deleteResume(@RequestParam Long resumeId) { + userResumeService.deleteResume(resumeId); + } + + // ==================== 教育经历 ==================== + + /** 查询简历的教育经历列表 */ + @GetMapping("/education") + public List listEducation(@RequestParam Long resumeId) { + return userResumeService.listEducation(resumeId).stream().map(po -> { + ResumeEducationDto dto = new ResumeEducationDto(); + BeanUtils.copyProperties(po, dto); + return dto; + }).collect(Collectors.toList()); + } + + /** 保存简历的教育经历列表(先删后插),返回resumeId */ + @PostMapping("/education") + public Long saveEducation(@Validated @RequestBody ResumeSubTableParam<@Valid ResumeEducationParam> param) { + List list = param.getItems().stream().map(p -> { + UserResumeEducation po = new UserResumeEducation(); + BeanUtils.copyProperties(p, po); + return po; + }).collect(Collectors.toList()); + return userResumeService.saveEducationList(list, param.getResumeId()); + } + + // ==================== 工作经历 ==================== + + /** 查询简历的工作经历列表 */ + @GetMapping("/work") + public List listWork(@RequestParam Long resumeId) { + return userResumeService.listWork(resumeId).stream().map(po -> { + ResumeWorkDto dto = new ResumeWorkDto(); + BeanUtils.copyProperties(po, dto); + return dto; + }).collect(Collectors.toList()); + } + + /** 保存简历的工作经历列表(先删后插),返回resumeId */ + @PostMapping("/work") + public Long saveWork(@Validated @RequestBody ResumeSubTableParam<@Valid ResumeWorkParam> param) { + List list = param.getItems().stream().map(p -> { + UserResumeWork po = new UserResumeWork(); + BeanUtils.copyProperties(p, po); + return po; + }).collect(Collectors.toList()); + return userResumeService.saveWorkList(list, param.getResumeId()); + } + + // ==================== 实习经历 ==================== + + /** 查询简历的实习经历列表 */ + @GetMapping("/internship") + public List listInternship(@RequestParam Long resumeId) { + return userResumeService.listInternship(resumeId).stream().map(po -> { + ResumeInternshipDto dto = new ResumeInternshipDto(); + BeanUtils.copyProperties(po, dto); + return dto; + }).collect(Collectors.toList()); + } + + /** 保存简历的实习经历列表(先删后插),返回resumeId */ + @PostMapping("/internship") + public Long saveInternship(@Validated @RequestBody ResumeSubTableParam<@Valid ResumeInternshipParam> param) { + List list = param.getItems().stream().map(p -> { + UserResumeInternship po = new UserResumeInternship(); + BeanUtils.copyProperties(p, po); + return po; + }).collect(Collectors.toList()); + return userResumeService.saveInternshipList(list, param.getResumeId()); + } + + // ==================== 项目经历 ==================== + + /** 查询简历的项目经历列表 */ + @GetMapping("/project") + public List listProject(@RequestParam Long resumeId) { + return userResumeService.listProject(resumeId).stream().map(po -> { + ResumeProjectDto dto = new ResumeProjectDto(); + BeanUtils.copyProperties(po, dto); + return dto; + }).collect(Collectors.toList()); + } + + /** 保存简历的项目经历列表(先删后插),返回resumeId */ + @PostMapping("/project") + public Long saveProject(@Validated @RequestBody ResumeSubTableParam<@Valid ResumeProjectParam> param) { + List list = param.getItems().stream().map(p -> { + UserResumeProject po = new UserResumeProject(); + BeanUtils.copyProperties(p, po); + return po; + }).collect(Collectors.toList()); + return userResumeService.saveProjectList(list, param.getResumeId()); + } + + // ==================== 竞赛经历 ==================== + + /** 查询简历的竞赛经历列表 */ + @GetMapping("/competition") + public List listCompetition(@RequestParam Long resumeId) { + return userResumeService.listCompetition(resumeId).stream().map(po -> { + ResumeCompetitionDto dto = new ResumeCompetitionDto(); + BeanUtils.copyProperties(po, dto); + return dto; + }).collect(Collectors.toList()); + } + + /** 保存简历的竞赛经历列表(先删后插),返回resumeId */ + @PostMapping("/competition") + public Long saveCompetition(@Validated @RequestBody ResumeSubTableParam<@Valid ResumeCompetitionParam> param) { + List list = param.getItems().stream().map(p -> { + UserResumeCompetition po = new UserResumeCompetition(); + BeanUtils.copyProperties(p, po); + return po; + }).collect(Collectors.toList()); + return userResumeService.saveCompetitionList(list, param.getResumeId()); + } } diff --git a/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeCompetitionDto.java b/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeCompetitionDto.java new file mode 100644 index 0000000..f4b2605 --- /dev/null +++ b/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeCompetitionDto.java @@ -0,0 +1,29 @@ +package org.jiayunet.pojo.dto.resume; + +import lombok.Data; +import org.jiayunet.pojo.vo.DescriptionParagraph; + +import java.util.List; + +/** + * 简历-竞赛经历返回 + * + * @author zk + */ +@Data +public class ResumeCompetitionDto { + + private Long id; + + /** 竞赛名称 */ + private String competitionName; + + /** 获奖情况 */ + private String award; + + /** 获奖时间,格式:2023.07 */ + private String awardDate; + + /** 描述段落 */ + private List description; +} diff --git a/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeDto.java b/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeDto.java new file mode 100644 index 0000000..24a6eb6 --- /dev/null +++ b/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeDto.java @@ -0,0 +1,55 @@ +package org.jiayunet.pojo.dto.resume; + +import lombok.Data; + +import java.util.List; + +/** + * 简历主表返回 + * + * @author zk + */ +@Data +public class ResumeDto { + + private Long id; + + /** 简历名称 */ + private String resumeName; + + /** 目标岗位 */ + private String targetPosition; + + /** 是否默认简历 0=否 1=是 */ + private Integer isDefault; + + /** 头像URL */ + private String avatarUrl; + + /** 真实姓名 */ + private String name; + + /** 邮箱 */ + private String email; + + /** 手机号码 */ + private String mobileNumber; + + /** 所在城市 */ + private String city; + + /** 微信号 */ + private String wechatNumber; + + /** 作品集链接 */ + private String portfolioUrl; + + /** 技能标签列表 */ + private List skills; + + /** 证书标签列表 */ + private List certificates; + + /** 个人概述 */ + private String summary; +} diff --git a/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeEducationDto.java b/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeEducationDto.java new file mode 100644 index 0000000..0f2e1e9 --- /dev/null +++ b/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeEducationDto.java @@ -0,0 +1,38 @@ +package org.jiayunet.pojo.dto.resume; + +import lombok.Data; +import org.jiayunet.pojo.vo.DescriptionParagraph; + +import java.util.List; + +/** + * 简历-教育经历返回 + * + * @author zk + */ +@Data +public class ResumeEducationDto { + + private Long id; + + /** 学校名称 */ + private String school; + + /** 专业 */ + private String major; + + /** 学历:大专/本科/硕士/博士 */ + private String degree; + + /** 学习形式:全日制/非全日制 */ + private String studyType; + + /** 开始时间,格式:2023.09 */ + private String startDate; + + /** 结束时间,格式:2024.06 */ + private String endDate; + + /** 描述段落 */ + private List description; +} diff --git a/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeInternshipDto.java b/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeInternshipDto.java new file mode 100644 index 0000000..ec044e3 --- /dev/null +++ b/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeInternshipDto.java @@ -0,0 +1,32 @@ +package org.jiayunet.pojo.dto.resume; + +import lombok.Data; +import org.jiayunet.pojo.vo.DescriptionParagraph; + +import java.util.List; + +/** + * 简历-实习经历返回 + * + * @author zk + */ +@Data +public class ResumeInternshipDto { + + private Long id; + + /** 公司名称 */ + private String companyName; + + /** 职位 */ + private String position; + + /** 开始时间 */ + private String startDate; + + /** 结束时间 */ + private String endDate; + + /** 描述段落 */ + private List description; +} diff --git a/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeProjectDto.java b/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeProjectDto.java new file mode 100644 index 0000000..faece75 --- /dev/null +++ b/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeProjectDto.java @@ -0,0 +1,35 @@ +package org.jiayunet.pojo.dto.resume; + +import lombok.Data; +import org.jiayunet.pojo.vo.DescriptionParagraph; + +import java.util.List; + +/** + * 简历-项目经历返回 + * + * @author zk + */ +@Data +public class ResumeProjectDto { + + private Long id; + + /** 所属公司 */ + private String companyName; + + /** 项目名称 */ + private String projectName; + + /** 担任角色 */ + private String role; + + /** 开始时间 */ + private String startDate; + + /** 结束时间 */ + private String endDate; + + /** 描述段落 */ + private List description; +} diff --git a/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeWorkDto.java b/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeWorkDto.java new file mode 100644 index 0000000..2c7c9b0 --- /dev/null +++ b/client-api/src/main/java/org/jiayunet/pojo/dto/resume/ResumeWorkDto.java @@ -0,0 +1,32 @@ +package org.jiayunet.pojo.dto.resume; + +import lombok.Data; +import org.jiayunet.pojo.vo.DescriptionParagraph; + +import java.util.List; + +/** + * 简历-工作经历返回 + * + * @author zk + */ +@Data +public class ResumeWorkDto { + + private Long id; + + /** 公司名称 */ + private String companyName; + + /** 职位 */ + private String position; + + /** 开始时间 */ + private String startDate; + + /** 结束时间 */ + private String endDate; + + /** 描述段落 */ + private List description; +} diff --git a/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeCompetitionParam.java b/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeCompetitionParam.java new file mode 100644 index 0000000..e4ccfc2 --- /dev/null +++ b/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeCompetitionParam.java @@ -0,0 +1,29 @@ +package org.jiayunet.pojo.param.resume; + +import lombok.Data; +import org.jiayunet.pojo.vo.DescriptionParagraph; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * 简历-竞赛经历保存入参 + * + * @author zk + */ +@Data +public class ResumeCompetitionParam { + + /** 竞赛名称 */ + @NotBlank(message = "竞赛名称不能为空") + private String competitionName; + + /** 获奖情况,如全国二等奖 */ + private String award; + + /** 获奖时间,格式:2023.07 */ + private String awardDate; + + /** 描述段落 */ + private List description; +} diff --git a/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeEducationParam.java b/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeEducationParam.java new file mode 100644 index 0000000..5bdbb0a --- /dev/null +++ b/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeEducationParam.java @@ -0,0 +1,38 @@ +package org.jiayunet.pojo.param.resume; + +import lombok.Data; +import org.jiayunet.pojo.vo.DescriptionParagraph; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * 简历-教育经历保存入参 + * + * @author zk + */ +@Data +public class ResumeEducationParam { + + /** 学校名称 */ + @NotBlank(message = "学校名称不能为空") + private String school; + + /** 专业 */ + private String major; + + /** 学历:大专/本科/硕士/博士 */ + private String degree; + + /** 学习形式:全日制/非全日制 */ + private String studyType; + + /** 开始时间,格式:2023.09 */ + private String startDate; + + /** 结束时间,格式:2024.06 */ + private String endDate; + + /** 描述段落 */ + private List description; +} diff --git a/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeInternshipParam.java b/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeInternshipParam.java new file mode 100644 index 0000000..4ff4332 --- /dev/null +++ b/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeInternshipParam.java @@ -0,0 +1,34 @@ +package org.jiayunet.pojo.param.resume; + +import lombok.Data; +import org.jiayunet.pojo.vo.DescriptionParagraph; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * 简历-实习经历保存入参 + * + * @author zk + */ +@Data +public class ResumeInternshipParam { + + /** 公司名称 */ + @NotBlank(message = "公司名称不能为空") + private String companyName; + + /** 职位 */ + @NotBlank(message = "职位不能为空") + private String position; + + /** 开始时间,格式:2023.06 */ + @NotBlank(message = "开始时间不能为空") + private String startDate; + + /** 结束时间,格式:2023.09,至今则为空 */ + private String endDate; + + /** 描述段落 */ + private List description; +} diff --git a/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeParam.java b/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeParam.java new file mode 100644 index 0000000..485d3a8 --- /dev/null +++ b/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeParam.java @@ -0,0 +1,53 @@ +package org.jiayunet.pojo.param.resume; + +import lombok.Data; + +import java.util.List; + +/** + * 简历主表保存入参 + * + * @author zk + */ +@Data +public class ResumeParam { + + /** 简历ID,为空则自动创建新简历 */ + private Long resumeId; + + /** 简历名称 */ + private String resumeName; + + /** 目标岗位 */ + private String targetPosition; + + /** 头像URL */ + private String avatarUrl; + + /** 真实姓名 */ + private String name; + + /** 邮箱 */ + private String email; + + /** 手机号码 */ + private String mobileNumber; + + /** 所在城市 */ + private String city; + + /** 微信号 */ + private String wechatNumber; + + /** 作品集链接 */ + private String portfolioUrl; + + /** 技能标签列表 */ + private List skills; + + /** 证书标签列表 */ + private List certificates; + + /** 个人概述 */ + private String summary; +} diff --git a/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeProjectParam.java b/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeProjectParam.java new file mode 100644 index 0000000..9c61807 --- /dev/null +++ b/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeProjectParam.java @@ -0,0 +1,36 @@ +package org.jiayunet.pojo.param.resume; + +import lombok.Data; +import org.jiayunet.pojo.vo.DescriptionParagraph; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * 简历-项目经历保存入参 + * + * @author zk + */ +@Data +public class ResumeProjectParam { + + /** 所属公司 */ + private String companyName; + + /** 项目名称 */ + @NotBlank(message = "项目名称不能为空") + private String projectName; + + /** 担任角色 */ + private String role; + + /** 开始时间,格式:2023.06 */ + @NotBlank(message = "开始时间不能为空") + private String startDate; + + /** 结束时间,格式:2023.09,至今则为空 */ + private String endDate; + + /** 描述段落 */ + private List description; +} diff --git a/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeSubTableParam.java b/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeSubTableParam.java new file mode 100644 index 0000000..2fe83b0 --- /dev/null +++ b/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeSubTableParam.java @@ -0,0 +1,23 @@ +package org.jiayunet.pojo.param.resume; + +import lombok.Data; + +import javax.validation.Valid; +import java.util.List; + +/** + * 简历子表保存通用包装入参 + *

携带可选的resumeId,为空则自动创建新简历

+ * + * @author zk + */ +@Data +public class ResumeSubTableParam { + + /** 简历ID,为空则自动创建新简历 */ + private Long resumeId; + + /** 子表数据列表 */ + @Valid + private List items; +} diff --git a/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeWorkParam.java b/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeWorkParam.java new file mode 100644 index 0000000..cf6d9b1 --- /dev/null +++ b/client-api/src/main/java/org/jiayunet/pojo/param/resume/ResumeWorkParam.java @@ -0,0 +1,34 @@ +package org.jiayunet.pojo.param.resume; + +import lombok.Data; +import org.jiayunet.pojo.vo.DescriptionParagraph; + +import javax.validation.constraints.NotBlank; +import java.util.List; + +/** + * 简历-工作经历保存入参 + * + * @author zk + */ +@Data +public class ResumeWorkParam { + + /** 公司名称 */ + @NotBlank(message = "公司名称不能为空") + private String companyName; + + /** 职位 */ + @NotBlank(message = "职位不能为空") + private String position; + + /** 开始时间,格式:2023.06 */ + @NotBlank(message = "开始时间不能为空") + private String startDate; + + /** 结束时间,格式:2023.09,至今则为空 */ + private String endDate; + + /** 描述段落 */ + private List description; +} diff --git a/client-api/src/main/java/org/jiayunet/service/UserResumeService.java b/client-api/src/main/java/org/jiayunet/service/UserResumeService.java index ee1b845..4eb08e2 100644 --- a/client-api/src/main/java/org/jiayunet/service/UserResumeService.java +++ b/client-api/src/main/java/org/jiayunet/service/UserResumeService.java @@ -1,19 +1,27 @@ package org.jiayunet.service; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper; import lombok.extern.slf4j.Slf4j; +import org.jiayunet.exception.BusinessException; +import org.jiayunet.exception.BusinessExpCodeEnum; import org.jiayunet.mapper.*; import org.jiayunet.pojo.po.*; import org.jiayunet.tool.UserSecurityTool; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import java.time.Instant; import java.util.List; +import java.util.stream.IntStream; /** * 用户简历服务 *

依赖:无

- *

使用表:bg_user_resume(简历主表查询)

+ *

使用表:bg_user_resume(简历主表CRUD)、bg_user_resume_education(教育经历)、 + * bg_user_resume_work(工作经历)、bg_user_resume_internship(实习经历)、 + * bg_user_resume_project(项目经历)、bg_user_resume_competition(竞赛经历)

* * @author zk */ @@ -24,6 +32,23 @@ public class UserResumeService { @Autowired private UserResumeMapper userResumeMapper; + @Autowired + private UserResumeEducationMapper educationMapper; + + @Autowired + private UserResumeWorkMapper workMapper; + + @Autowired + private UserResumeInternshipMapper internshipMapper; + + @Autowired + private UserResumeProjectMapper projectMapper; + + @Autowired + private UserResumeCompetitionMapper competitionMapper; + + // ==================== 简历列表 ==================== + /** * 查询当前用户的简历列表 *

按sort_order升序、create_time降序排列

@@ -35,4 +60,299 @@ public class UserResumeService { .orderByAsc(UserResume::getSortOrder) .orderByDesc(UserResume::getCreateTime)); } + + // ==================== 主表 ==================== + + /** + * 查询简历主表 + *

校验简历归属当前用户

+ */ + public UserResume getResume(Long resumeId) { + UserResume resume = userResumeMapper.selectById(resumeId); + checkOwnership(resume); + return resume; + } + + /** + * 保存简历主表 + *

1. resumeId为空则创建新简历(默认名"我的简历") 2. 不为空则更新 3. 返回resumeId

+ */ + @Transactional(rollbackFor = Exception.class) + public Long saveResume(UserResume resume, Long resumeId) { + Long userId = UserSecurityTool.getUserId(); + Instant now = Instant.now(); + if (resumeId == null) { + // 创建新简历 + resume.setUserId(userId); + if (resume.getResumeName() == null) { + resume.setResumeName("我的简历"); + } + resume.setIsDefault(0); + resume.setSortOrder(0); + resume.setCreateTime(now); + resume.setUpdateTime(now); + userResumeMapper.insert(resume); + return resume.getId(); + } + // 更新已有简历 + UserResume existing = userResumeMapper.selectById(resumeId); + checkOwnership(existing); + resume.setId(resumeId); + resume.setUserId(userId); + resume.setCreateTime(existing.getCreateTime()); + resume.setUpdateTime(now); + resume.setIsDefault(existing.getIsDefault()); + resume.setSortOrder(existing.getSortOrder()); + userResumeMapper.updateById(resume); + return resumeId; + } + + /** + * 删除简历(物理删除主表 + 全部子表) + *

1. 校验归属 2. 删除5张子表 3. 删除主表

+ */ + @Transactional(rollbackFor = Exception.class) + public void deleteResume(Long resumeId) { + UserResume resume = userResumeMapper.selectById(resumeId); + checkOwnership(resume); + // 删除全部子表 + educationMapper.delete(new LambdaQueryWrapper().eq(UserResumeEducation::getResumeId, resumeId)); + workMapper.delete(new LambdaQueryWrapper().eq(UserResumeWork::getResumeId, resumeId)); + internshipMapper.delete(new LambdaQueryWrapper().eq(UserResumeInternship::getResumeId, resumeId)); + projectMapper.delete(new LambdaQueryWrapper().eq(UserResumeProject::getResumeId, resumeId)); + competitionMapper.delete(new LambdaQueryWrapper().eq(UserResumeCompetition::getResumeId, resumeId)); + // 删除主表 + userResumeMapper.deleteById(resumeId); + } + + // ==================== 教育经历 ==================== + + /** 查询简历的教育经历列表 */ + public List listEducation(Long resumeId) { + checkResumeOwnership(resumeId); + return educationMapper.selectList(new LambdaQueryWrapper() + .eq(UserResumeEducation::getResumeId, resumeId) + .orderByAsc(UserResumeEducation::getSortOrder)); + } + + /** + * 保存简历的教育经历列表(先删后插) + *

1. resumeId为空则自动创建新简历 2. 按resumeId删除旧数据 3. 批量插入 4. 更新主表修改时间

+ */ + @Transactional(rollbackFor = Exception.class) + public Long saveEducationList(List list, Long resumeId) { + Long userId = UserSecurityTool.getUserId(); + resumeId = getOrCreateResumeId(resumeId, userId); + educationMapper.delete(new LambdaQueryWrapper().eq(UserResumeEducation::getResumeId, resumeId)); + if (!list.isEmpty()) { + Instant now = Instant.now(); + Long finalResumeId = resumeId; + IntStream.range(0, list.size()).forEach(i -> { + UserResumeEducation item = list.get(i); + item.setUserId(userId); + item.setResumeId(finalResumeId); + item.setSortOrder(i); + item.setCreateTime(now); + item.setUpdateTime(now); + }); + educationMapper.batchInsert(list); + } + touchResumeUpdateTime(resumeId); + return resumeId; + } + + // ==================== 工作经历 ==================== + + /** 查询简历的工作经历列表 */ + public List listWork(Long resumeId) { + checkResumeOwnership(resumeId); + return workMapper.selectList(new LambdaQueryWrapper() + .eq(UserResumeWork::getResumeId, resumeId) + .orderByAsc(UserResumeWork::getSortOrder)); + } + + /** + * 保存简历的工作经历列表(先删后插) + *

1. resumeId为空则自动创建新简历 2. 按resumeId删除旧数据 3. 批量插入 4. 更新主表修改时间

+ */ + @Transactional(rollbackFor = Exception.class) + public Long saveWorkList(List list, Long resumeId) { + Long userId = UserSecurityTool.getUserId(); + resumeId = getOrCreateResumeId(resumeId, userId); + workMapper.delete(new LambdaQueryWrapper().eq(UserResumeWork::getResumeId, resumeId)); + if (!list.isEmpty()) { + Instant now = Instant.now(); + Long finalResumeId = resumeId; + IntStream.range(0, list.size()).forEach(i -> { + UserResumeWork item = list.get(i); + item.setUserId(userId); + item.setResumeId(finalResumeId); + item.setSortOrder(i); + item.setCreateTime(now); + item.setUpdateTime(now); + }); + workMapper.batchInsert(list); + } + touchResumeUpdateTime(resumeId); + return resumeId; + } + + // ==================== 实习经历 ==================== + + /** 查询简历的实习经历列表 */ + public List listInternship(Long resumeId) { + checkResumeOwnership(resumeId); + return internshipMapper.selectList(new LambdaQueryWrapper() + .eq(UserResumeInternship::getResumeId, resumeId) + .orderByAsc(UserResumeInternship::getSortOrder)); + } + + /** + * 保存简历的实习经历列表(先删后插) + *

1. resumeId为空则自动创建新简历 2. 按resumeId删除旧数据 3. 批量插入 4. 更新主表修改时间

+ */ + @Transactional(rollbackFor = Exception.class) + public Long saveInternshipList(List list, Long resumeId) { + Long userId = UserSecurityTool.getUserId(); + resumeId = getOrCreateResumeId(resumeId, userId); + internshipMapper.delete(new LambdaQueryWrapper().eq(UserResumeInternship::getResumeId, resumeId)); + if (!list.isEmpty()) { + Instant now = Instant.now(); + Long finalResumeId = resumeId; + IntStream.range(0, list.size()).forEach(i -> { + UserResumeInternship item = list.get(i); + item.setUserId(userId); + item.setResumeId(finalResumeId); + item.setSortOrder(i); + item.setCreateTime(now); + item.setUpdateTime(now); + }); + internshipMapper.batchInsert(list); + } + touchResumeUpdateTime(resumeId); + return resumeId; + } + + // ==================== 项目经历 ==================== + + /** 查询简历的项目经历列表 */ + public List listProject(Long resumeId) { + checkResumeOwnership(resumeId); + return projectMapper.selectList(new LambdaQueryWrapper() + .eq(UserResumeProject::getResumeId, resumeId) + .orderByAsc(UserResumeProject::getSortOrder)); + } + + /** + * 保存简历的项目经历列表(先删后插) + *

1. resumeId为空则自动创建新简历 2. 按resumeId删除旧数据 3. 批量插入 4. 更新主表修改时间

+ */ + @Transactional(rollbackFor = Exception.class) + public Long saveProjectList(List list, Long resumeId) { + Long userId = UserSecurityTool.getUserId(); + resumeId = getOrCreateResumeId(resumeId, userId); + projectMapper.delete(new LambdaQueryWrapper().eq(UserResumeProject::getResumeId, resumeId)); + if (!list.isEmpty()) { + Instant now = Instant.now(); + Long finalResumeId = resumeId; + IntStream.range(0, list.size()).forEach(i -> { + UserResumeProject item = list.get(i); + item.setUserId(userId); + item.setResumeId(finalResumeId); + item.setSortOrder(i); + item.setCreateTime(now); + item.setUpdateTime(now); + }); + projectMapper.batchInsert(list); + } + touchResumeUpdateTime(resumeId); + return resumeId; + } + + // ==================== 竞赛经历 ==================== + + /** 查询简历的竞赛经历列表 */ + public List listCompetition(Long resumeId) { + checkResumeOwnership(resumeId); + return competitionMapper.selectList(new LambdaQueryWrapper() + .eq(UserResumeCompetition::getResumeId, resumeId) + .orderByAsc(UserResumeCompetition::getSortOrder)); + } + + /** + * 保存简历的竞赛经历列表(先删后插) + *

1. resumeId为空则自动创建新简历 2. 按resumeId删除旧数据 3. 批量插入 4. 更新主表修改时间

+ */ + @Transactional(rollbackFor = Exception.class) + public Long saveCompetitionList(List list, Long resumeId) { + Long userId = UserSecurityTool.getUserId(); + resumeId = getOrCreateResumeId(resumeId, userId); + competitionMapper.delete(new LambdaQueryWrapper().eq(UserResumeCompetition::getResumeId, resumeId)); + if (!list.isEmpty()) { + Instant now = Instant.now(); + Long finalResumeId = resumeId; + IntStream.range(0, list.size()).forEach(i -> { + UserResumeCompetition item = list.get(i); + item.setUserId(userId); + item.setResumeId(finalResumeId); + item.setSortOrder(i); + item.setCreateTime(now); + item.setUpdateTime(now); + }); + competitionMapper.batchInsert(list); + } + touchResumeUpdateTime(resumeId); + return resumeId; + } + + // ==================== 内部方法 ==================== + + /** + * 获取或创建简历,返回resumeId + *

resumeId为空则创建新简历(默认名"我的简历"),不为空则校验归属后返回

+ */ + private Long getOrCreateResumeId(Long resumeId, Long userId) { + if (resumeId != null) { + checkResumeOwnership(resumeId); + return resumeId; + } + UserResume resume = new UserResume(); + resume.setUserId(userId); + resume.setResumeName("我的简历"); + resume.setIsDefault(0); + resume.setSortOrder(0); + Instant now = Instant.now(); + resume.setCreateTime(now); + resume.setUpdateTime(now); + userResumeMapper.insert(resume); + return resume.getId(); + } + + /** + * 更新简历主表的修改时间 + *

子表保存后调用,确保主表update_time反映简历整体最后修改时间

+ */ + private void touchResumeUpdateTime(Long resumeId) { + userResumeMapper.update(null, new LambdaUpdateWrapper() + .eq(UserResume::getId, resumeId) + .set(UserResume::getUpdateTime, Instant.now())); + } + + /** + * 校验简历归属当前用户 + */ + private void checkOwnership(UserResume resume) { + Long userId = UserSecurityTool.getUserId(); + if (resume == null || !resume.getUserId().equals(userId)) { + throw new BusinessException(BusinessExpCodeEnum.PERMISSION_DENIED, "简历不存在或无权操作"); + } + } + + /** + * 根据resumeId校验简历归属当前用户 + */ + private void checkResumeOwnership(Long resumeId) { + UserResume resume = userResumeMapper.selectById(resumeId); + checkOwnership(resume); + } } diff --git a/manager/src/main/java/org/jiayunet/mapper/UserResumeCompetitionMapper.java b/manager/src/main/java/org/jiayunet/mapper/UserResumeCompetitionMapper.java index 003c96a..29630f9 100644 --- a/manager/src/main/java/org/jiayunet/mapper/UserResumeCompetitionMapper.java +++ b/manager/src/main/java/org/jiayunet/mapper/UserResumeCompetitionMapper.java @@ -1,6 +1,6 @@ package org.jiayunet.mapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; import org.jiayunet.pojo.po.UserResumeCompetition; /** @@ -8,5 +8,6 @@ import org.jiayunet.pojo.po.UserResumeCompetition; * * @author zk */ -public interface UserResumeCompetitionMapper extends BaseMapper { +@Mapper +public interface UserResumeCompetitionMapper extends CommonMapper { } diff --git a/manager/src/main/java/org/jiayunet/mapper/UserResumeEducationMapper.java b/manager/src/main/java/org/jiayunet/mapper/UserResumeEducationMapper.java index b347d0a..09b81b5 100644 --- a/manager/src/main/java/org/jiayunet/mapper/UserResumeEducationMapper.java +++ b/manager/src/main/java/org/jiayunet/mapper/UserResumeEducationMapper.java @@ -1,6 +1,6 @@ package org.jiayunet.mapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; import org.jiayunet.pojo.po.UserResumeEducation; /** @@ -8,5 +8,6 @@ import org.jiayunet.pojo.po.UserResumeEducation; * * @author zk */ -public interface UserResumeEducationMapper extends BaseMapper { +@Mapper +public interface UserResumeEducationMapper extends CommonMapper { } diff --git a/manager/src/main/java/org/jiayunet/mapper/UserResumeInternshipMapper.java b/manager/src/main/java/org/jiayunet/mapper/UserResumeInternshipMapper.java index 30f2389..3dfb9f0 100644 --- a/manager/src/main/java/org/jiayunet/mapper/UserResumeInternshipMapper.java +++ b/manager/src/main/java/org/jiayunet/mapper/UserResumeInternshipMapper.java @@ -1,6 +1,6 @@ package org.jiayunet.mapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; import org.jiayunet.pojo.po.UserResumeInternship; /** @@ -8,5 +8,6 @@ import org.jiayunet.pojo.po.UserResumeInternship; * * @author zk */ -public interface UserResumeInternshipMapper extends BaseMapper { +@Mapper +public interface UserResumeInternshipMapper extends CommonMapper { } diff --git a/manager/src/main/java/org/jiayunet/mapper/UserResumeMapper.java b/manager/src/main/java/org/jiayunet/mapper/UserResumeMapper.java index a31554b..8764483 100644 --- a/manager/src/main/java/org/jiayunet/mapper/UserResumeMapper.java +++ b/manager/src/main/java/org/jiayunet/mapper/UserResumeMapper.java @@ -1,6 +1,6 @@ package org.jiayunet.mapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; import org.jiayunet.pojo.po.UserResume; /** @@ -8,5 +8,6 @@ import org.jiayunet.pojo.po.UserResume; * * @author zk */ -public interface UserResumeMapper extends BaseMapper { +@Mapper +public interface UserResumeMapper extends CommonMapper { } diff --git a/manager/src/main/java/org/jiayunet/mapper/UserResumeProjectMapper.java b/manager/src/main/java/org/jiayunet/mapper/UserResumeProjectMapper.java index b1b8a32..9abdde3 100644 --- a/manager/src/main/java/org/jiayunet/mapper/UserResumeProjectMapper.java +++ b/manager/src/main/java/org/jiayunet/mapper/UserResumeProjectMapper.java @@ -1,6 +1,6 @@ package org.jiayunet.mapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; import org.jiayunet.pojo.po.UserResumeProject; /** @@ -8,5 +8,6 @@ import org.jiayunet.pojo.po.UserResumeProject; * * @author zk */ -public interface UserResumeProjectMapper extends BaseMapper { +@Mapper +public interface UserResumeProjectMapper extends CommonMapper { } diff --git a/manager/src/main/java/org/jiayunet/mapper/UserResumeWorkMapper.java b/manager/src/main/java/org/jiayunet/mapper/UserResumeWorkMapper.java index 16e1477..309fac2 100644 --- a/manager/src/main/java/org/jiayunet/mapper/UserResumeWorkMapper.java +++ b/manager/src/main/java/org/jiayunet/mapper/UserResumeWorkMapper.java @@ -1,6 +1,6 @@ package org.jiayunet.mapper; -import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; import org.jiayunet.pojo.po.UserResumeWork; /** @@ -8,5 +8,6 @@ import org.jiayunet.pojo.po.UserResumeWork; * * @author zk */ -public interface UserResumeWorkMapper extends BaseMapper { +@Mapper +public interface UserResumeWorkMapper extends CommonMapper { }