轻松聘AI开发
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

97 lines
7.3 KiB

package com.mydreamjob.onlineresume;
import com.fasterxml.jackson.databind.*;
import com.mydreamjob.common.api.ApiResponse;
import com.mydreamjob.common.exception.*;
import com.mydreamjob.common.logging.RequestIdAccessor;
import com.mydreamjob.security.CurrentUserAccessor;
import io.swagger.v3.oas.annotations.*;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import jakarta.validation.constraints.*;
import lombok.RequiredArgsConstructor;
import org.springframework.http.*;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.nio.charset.StandardCharsets;
@RestController @RequiredArgsConstructor @Validated
@RequestMapping("/profile/me/online-resume")
@Tag(name = "在线简历", description = "仅当前 STUDENT 本人;不修改账号登录信息,不读取附件正文")
public class OnlineResumeController {
private final OnlineResumeService service;
private final OnlineResumeDocxWriter docx;
private final CurrentUserAccessor currentUser;
private final RequestIdAccessor requestIds;
private final ObjectMapper mapper;
@Schema(name = "OnlineResumeEntryRequest")
@lombok.Data
public static class EntryRequest {
@NotNull @PositiveOrZero private Long revision;
@NotNull @Schema(implementation = Object.class, oneOf = {OnlineResumeContent.Education.class, OnlineResumeContent.Work.class,
OnlineResumeContent.Project.class, OnlineResumeContent.Skill.class})
private JsonNode entry;
}
@GetMapping
@Operation(operationId = "getMyOnlineResume", summary = "读取本人完整在线简历", description = "无保存记录时 revision=0,从已有资料、求职偏好和账号联系方式组装,不写库。attachment 仅返回文件元数据及鉴权下载地址。phone/email 为本人简历联系方式,不下发到企业候选人接口。")
public ApiResponse<OnlineResumeView> get(HttpServletRequest request) {
return ApiResponse.ok(service.get(currentUser.requireUserId()), requestIds.get(request));
}
@PutMapping
@Operation(operationId = "saveMyOnlineResume", summary = "保存完整在线简历", description = "携带最近 GET 的 revision;content 完整替换,空数组删除该类全部条目,null/空字符串可清空可选字段。新条目省略 id,既有条目保留 id。成功 revision+1;过期 revision 返回409/6002。同步既有个人资料和求职偏好,不改登录手机号/邮箱、附件及隐私授权。")
public ApiResponse<OnlineResumeView> save(@Valid @RequestBody OnlineResumeSaveRequest body, HttpServletRequest request) {
return ApiResponse.ok(service.save(currentUser.requireUserId(), body), requestIds.get(request));
}
@GetMapping("/{section}")
@Operation(operationId = "listMyOnlineResumeEntries", summary = "读取某类简历条目", description = "section 仅 education-experiences/work-experiences/project-experiences/skills;revision 从整份简历 GET 获取。")
public ApiResponse<java.util.List<Object>> list(@PathVariable String section, HttpServletRequest request) {
java.util.List<Object> result = new java.util.ArrayList<>();
entries(service.get(currentUser.requireUserId()), section).forEach(result::add);
return ApiResponse.ok(result, requestIds.get(request));
}
@GetMapping("/{section}/{id}")
@Operation(operationId = "getMyOnlineResumeEntry", summary = "读取本人简历条目详情", description = "只在本人指定类别内寻找条目,不存在返回404/6003。")
public ApiResponse<Object> detail(@PathVariable String section, @PathVariable String id, HttpServletRequest request) {
for (JsonNode e : entries(service.get(currentUser.requireUserId()), section))
if (id.equals(e.path("id").asText())) return ApiResponse.ok(e, requestIds.get(request));
throw new BusinessException(ErrorCode.ONLINE_RESUME_ENTRY_NOT_FOUND);
}
@PostMapping("/{section}")
@Operation(operationId = "createMyOnlineResumeEntry", summary = "新增简历条目", description = "section 仅 education-experiences/work-experiences/project-experiences/skills;entry 为相应类型,新条目不传 id;携带整份简历 revision,返回更新后的完整简历。")
public ApiResponse<OnlineResumeView> create(@PathVariable String section, @Valid @RequestBody EntryRequest body, HttpServletRequest request) {
return ApiResponse.ok(service.editEntry(currentUser.requireUserId(), section, null, body.getRevision(), body.getEntry(), false), requestIds.get(request));
}
@PutMapping("/{section}/{id}")
@Operation(operationId = "updateMyOnlineResumeEntry", summary = "替换简历条目", description = "entry 完整替换本人条目;id 可省略或须与 URL 一致;携带整份简历 revision。")
public ApiResponse<OnlineResumeView> update(@PathVariable String section, @PathVariable String id, @Valid @RequestBody EntryRequest body, HttpServletRequest request) {
return ApiResponse.ok(service.editEntry(currentUser.requireUserId(), section, id, body.getRevision(), body.getEntry(), false), requestIds.get(request));
}
@DeleteMapping("/{section}/{id}")
@Operation(operationId = "deleteMyOnlineResumeEntry", summary = "删除简历条目", description = "query revision 必填;不存在返回404/6003,过期版本返回409/6002;返回更新后的完整简历。")
public ApiResponse<OnlineResumeView> delete(@PathVariable String section, @PathVariable String id,
@RequestParam @Min(0) Long revision, HttpServletRequest request) {
return ApiResponse.ok(service.editEntry(currentUser.requireUserId(), section, id, revision, null, true), requestIds.get(request));
}
@GetMapping(value = "/download", produces = "application/vnd.openxmlformats-officedocument.wordprocessingml.document")
@Operation(operationId = "downloadMyOnlineResume", summary = "下载在线简历 DOCX", description = "从当前已保存的在线内容生成 Word 文件,不含上传附件,不写文件、不创建附件版本;浏览器应携带鉴权获取 Blob,未保存编辑需先 PUT。")
public ResponseEntity<byte[]> download() {
byte[] bytes = docx.write(service.get(currentUser.requireUserId()).content());
return ResponseEntity.ok().cacheControl(CacheControl.noStore())
.header(HttpHeaders.CONTENT_DISPOSITION, ContentDisposition.attachment().filename("个人简历.docx", StandardCharsets.UTF_8).build().toString())
.contentType(MediaType.parseMediaType("application/vnd.openxmlformats-officedocument.wordprocessingml.document"))
.contentLength(bytes.length).body(bytes);
}
private JsonNode entries(OnlineResumeView view, String section) {
return mapper.valueToTree(switch (section) {
case "education-experiences" -> view.content().getEducationExperiences();
case "work-experiences" -> view.content().getWorkExperiences();
case "project-experiences" -> view.content().getProjectExperiences();
case "skills" -> view.content().getSkills();
default -> throw new BusinessException(ErrorCode.ONLINE_RESUME_ENTRY_NOT_FOUND);
});
}
}