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.SvPersonalCert; import com.kaidi.oa.repository.SvPersonalCertRepository; import org.springframework.transaction.annotation.Transactional; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.PutMapping; 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.LocalDate; import java.time.format.DateTimeFormatter; import java.time.Instant; import java.util.List; import java.util.stream.Collectors; /** * 工程监理部 · 监理人员执业资格证书台账(补足 Gap 1:监理方自身执业证有效期预警)。 * * CRUD + 到期预警查询(GET /expiring?days=30): * 返回有效期在当前 + days 天内的证书列表,方便总监提前处理续期。 * 状态自动判定:GET /expiring 同时自动将已过期证书 status 更新为「已过期」。 * * 资源路径 /api/oa/sv-personal-certs。 */ @RestController @RequestMapping("/api/oa/sv-personal-certs") public class SvPersonalCertController { private final SvPersonalCertRepository certRepo; public SvPersonalCertController(SvPersonalCertRepository certRepo) { this.certRepo = certRepo; } @GetMapping public ApiResp> list( @RequestParam(required = false) Long projectId, @RequestParam(required = false) String status, @RequestParam(required = false) String personName) { if (projectId != null) { return ApiResp.ok(certRepo.findBySupervisionProjectId(projectId)); } if (status != null && !status.isBlank()) { return ApiResp.ok(certRepo.findByStatus(status)); } if (personName != null && !personName.isBlank()) { return ApiResp.ok(certRepo.findByPersonName(personName)); } return ApiResp.ok(certRepo.findAll()); } @GetMapping("/{id}") public ApiResp get(@PathVariable Long id) { return ApiResp.ok(find(id)); } /** * 到期预警查询:返回未来 days 天内到期的证书(默认 30 天), * 并同步把已过期的自动标记为「已过期」。 */ @GetMapping("/expiring") @Transactional public ApiResp> expiring(@RequestParam(defaultValue = "30") int days) { String today = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); String deadline = LocalDate.now().plusDays(days).format(DateTimeFormatter.ISO_LOCAL_DATE); // 自动更新已过期证书状态 certRepo.findByExpiryDateLessThanEqual(today).stream() .filter(c -> !"已过期".equals(c.getStatus()) && !"已注销".equals(c.getStatus())) .forEach(c -> { c.setStatus("已过期"); certRepo.save(c); }); // 返回 today <= expiryDate <= deadline 的有效证书(即将到期) List result = certRepo.findByExpiryDateLessThanEqual(deadline).stream() .filter(c -> c.getExpiryDate() != null && c.getExpiryDate().compareTo(today) >= 0) .collect(Collectors.toList()); return ApiResp.ok(result); } public record CertRequest( Long supervisionProjectId, String personName, String roleType, String certType, String certNum, String issuingAuth, String issueDate, String expiryDate, String status, String remark) { } @PostMapping public ApiResp create(@RequestBody CertRequest req) { if (req.personName() == null || req.personName().isBlank()) { throw new ApiException(400, "人员姓名(personName) 不能为空"); } if (req.expiryDate() == null || req.expiryDate().isBlank()) { throw new ApiException(400, "有效期至(expiryDate) 不能为空"); } SvPersonalCert c = new SvPersonalCert(); c.setSupervisionProjectId(req.supervisionProjectId()); c.setPersonName(req.personName()); c.setRoleType(req.roleType()); c.setCertType(req.certType() == null || req.certType().isBlank() ? "注册监理工程师" : req.certType()); c.setCertNum(req.certNum()); c.setIssuingAuth(req.issuingAuth()); c.setIssueDate(req.issueDate()); c.setExpiryDate(req.expiryDate()); // 自动判断有效/已过期 String today = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); String statusVal = req.status() != null && !req.status().isBlank() ? req.status() : (req.expiryDate().compareTo(today) < 0 ? "已过期" : "有效"); c.setStatus(statusVal); c.setRemark(req.remark()); c.setCreatedAt(Instant.now()); return ApiResp.ok(certRepo.save(c)); } @PutMapping("/{id}") public ApiResp update(@PathVariable Long id, @RequestBody CertRequest req) { SvPersonalCert c = find(id); if (req.supervisionProjectId() != null) c.setSupervisionProjectId(req.supervisionProjectId()); if (req.personName() != null && !req.personName().isBlank()) c.setPersonName(req.personName()); if (req.roleType() != null) c.setRoleType(req.roleType()); if (req.certType() != null && !req.certType().isBlank()) c.setCertType(req.certType()); if (req.certNum() != null) c.setCertNum(req.certNum()); if (req.issuingAuth() != null) c.setIssuingAuth(req.issuingAuth()); if (req.issueDate() != null) c.setIssueDate(req.issueDate()); if (req.expiryDate() != null && !req.expiryDate().isBlank()) { c.setExpiryDate(req.expiryDate()); // 重算状态 String today = LocalDate.now().format(DateTimeFormatter.ISO_LOCAL_DATE); if (!"已注销".equals(c.getStatus())) { c.setStatus(req.expiryDate().compareTo(today) < 0 ? "已过期" : "有效"); } } if (req.status() != null && !req.status().isBlank()) c.setStatus(req.status()); if (req.remark() != null) c.setRemark(req.remark()); return ApiResp.ok(certRepo.save(c)); } @DeleteMapping("/{id}") public ApiResp delete(@PathVariable Long id) { if (!certRepo.existsById(id)) { throw new NotFoundException("sv-personal-cert not found: " + id); } certRepo.deleteById(id); return ApiResp.ok(null); } private SvPersonalCert find(Long id) { return certRepo.findById(id) .orElseThrow(() -> new NotFoundException("sv-personal-cert not found: " + id)); } }