package com.kaidi.oa.web; import com.kaidi.oa.common.ApiException; import com.kaidi.oa.common.ApiResp; import com.kaidi.oa.common.NotFoundException; import com.kaidi.oa.domain.Handover; import com.kaidi.oa.repository.HandoverRepository; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.time.Instant; import java.util.List; /** * Work handover center (工作交接). handoverType is one of 离职交接 / 调动交接 / * 轮岗交接; status flows 进行中 -> 已完成 -> 已确认. */ @RestController @RequestMapping("/api/oa/handovers") public class HandoverController { public static final String STATUS_IN_PROGRESS = "进行中"; public static final String STATUS_DONE = "已完成"; public static final String STATUS_CONFIRMED = "已确认"; /** Accepted handover statuses (交接全过程)。 */ public static final List HANDOVER_STATUSES = List.of(STATUS_IN_PROGRESS, STATUS_DONE, STATUS_CONFIRMED); private final HandoverRepository handoverRepo; public HandoverController(HandoverRepository handoverRepo) { this.handoverRepo = handoverRepo; } @GetMapping public ApiResp> list(@RequestParam(required = false) String status) { List list; if (status != null && !status.isBlank()) { list = handoverRepo.findByStatus(status); } else { list = handoverRepo.findAll(); } return ApiResp.ok(list); } @GetMapping("/{id}") public ApiResp get(@PathVariable Long id) { return ApiResp.ok(handoverRepo.findById(id) .orElseThrow(() -> new NotFoundException("handover not found: " + id))); } /** 新建/更新共用请求体——含结构化字段(需求功能14)。 */ public record HandoverRequest( String handoverPerson, String receiver, String dept, String handoverType, String items, String status, String handoverDate, // 结构化字段(可空) String jobDuties, String pendingTaskList, String systemAccounts, String keyContacts, String filePaths) { } /** @deprecated 旧签名保留别名,前端升级后可删 */ public record CreateHandoverRequest( String handoverPerson, String receiver, String dept, String handoverType, String items, String status, String handoverDate) { } @PostMapping public ApiResp create(@RequestBody HandoverRequest req) { if (req.handoverPerson() == null || req.handoverPerson().isBlank()) { throw new ApiException(400, "handoverPerson is required"); } Handover h = new Handover(); applyFields(h, req); h.setStatus(req.status() == null || req.status().isBlank() ? STATUS_IN_PROGRESS : req.status()); h.setCreatedAt(Instant.now()); return ApiResp.ok(handoverRepo.save(h)); } /** * PATCH /{id} -> partial update incl. status progression * (提交 进行中->已完成 / 确认 已完成->已确认). Only non-null fields are applied; * a supplied status must belong to {@link #HANDOVER_STATUSES}. */ @PatchMapping("/{id}") public ApiResp update(@PathVariable Long id, @RequestBody HandoverRequest req) { Handover h = handoverRepo.findById(id) .orElseThrow(() -> new NotFoundException("handover not found: " + id)); if (req.handoverPerson() != null) { if (req.handoverPerson().isBlank()) { throw new ApiException(400, "handoverPerson is required"); } } applyFields(h, req); if (req.status() != null && !req.status().isBlank()) { if (!HANDOVER_STATUSES.contains(req.status())) { throw new ApiException(400, "invalid status: " + req.status() + "; expected one of " + HANDOVER_STATUSES); } h.setStatus(req.status()); } return ApiResp.ok(handoverRepo.save(h)); } /** DELETE /{id} -> remove a handover record. */ @DeleteMapping("/{id}") public ApiResp delete(@PathVariable Long id) { if (!handoverRepo.existsById(id)) { throw new NotFoundException("handover not found: " + id); } handoverRepo.deleteById(id); return ApiResp.ok(null); } // ====== 一键生成交接清单(需求功能14)====== public record ChecklistSection(String section, java.util.List items) {} public record ChecklistResult(Long handoverId, String handoverPerson, String receiver, String handoverDate, java.util.List sections) {} /** * POST /{id}/generate-checklist * 根据结构化字段(岗位职责/在办事项/系统账号/关键联系人/文件路径/遗留 items) * 一键组装"工作交接清单",返回分节结构,前端可直接渲染或打印。 */ @PostMapping("/{id}/generate-checklist") public ApiResp generateChecklist(@PathVariable Long id) { Handover h = handoverRepo.findById(id) .orElseThrow(() -> new NotFoundException("handover not found: " + id)); java.util.List sections = new java.util.ArrayList<>(); // 1. 岗位职责 if (h.getJobDuties() != null && !h.getJobDuties().isBlank()) { sections.add(new ChecklistSection("岗位职责", splitLines(h.getJobDuties()))); } // 2. 在办事项清单 if (h.getPendingTaskList() != null && !h.getPendingTaskList().isBlank()) { sections.add(new ChecklistSection("在办事项清单", splitLines(h.getPendingTaskList()))); } // 3. 常用系统账号 if (h.getSystemAccounts() != null && !h.getSystemAccounts().isBlank()) { sections.add(new ChecklistSection("常用系统账号", splitLines(h.getSystemAccounts()))); } // 4. 关键联系人 if (h.getKeyContacts() != null && !h.getKeyContacts().isBlank()) { sections.add(new ChecklistSection("关键联系人", splitLines(h.getKeyContacts()))); } // 5. 重要文件路径 if (h.getFilePaths() != null && !h.getFilePaths().isBlank()) { sections.add(new ChecklistSection("重要文件路径", splitLines(h.getFilePaths()))); } // 6. 其他事项(来自原 items 自由文本) if (h.getItems() != null && !h.getItems().isBlank()) { sections.add(new ChecklistSection("其他交接事项", splitLines(h.getItems()))); } if (sections.isEmpty()) { throw new ApiException(409, "交接单尚无任何结构化内容,请先填写岗位职责/在办事项/系统账号等字段"); } return ApiResp.ok(new ChecklistResult(h.getId(), h.getHandoverPerson(), h.getReceiver(), h.getHandoverDate(), sections)); } // ====== 私有方法 ====== private void applyFields(Handover h, HandoverRequest req) { if (req.handoverPerson() != null && !req.handoverPerson().isBlank()) h.setHandoverPerson(req.handoverPerson()); if (req.receiver() != null) h.setReceiver(req.receiver()); if (req.dept() != null) h.setDept(req.dept()); if (req.handoverType() != null) h.setHandoverType(req.handoverType()); if (req.items() != null) h.setItems(req.items()); if (req.handoverDate() != null) h.setHandoverDate(req.handoverDate()); if (req.jobDuties() != null) h.setJobDuties(req.jobDuties()); if (req.pendingTaskList() != null) h.setPendingTaskList(req.pendingTaskList()); if (req.systemAccounts() != null) h.setSystemAccounts(req.systemAccounts()); if (req.keyContacts() != null) h.setKeyContacts(req.keyContacts()); if (req.filePaths() != null) h.setFilePaths(req.filePaths()); } /** 按换行或分号拆分为非空行列表。 */ private java.util.List splitLines(String raw) { java.util.List out = new java.util.ArrayList<>(); for (String part : raw.split("[\\r\\n;;]+")) { String line = part.trim(); if (!line.isEmpty()) out.add(line); } return out.isEmpty() ? java.util.List.of(raw.trim()) : out; } }