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.SvDrawingIssue; import com.kaidi.oa.domain.SvEquipmentLedger; import com.kaidi.oa.repository.SupervisionProjectRepository; import com.kaidi.oa.repository.SvDrawingIssueRepository; import com.kaidi.oa.repository.SvEquipmentLedgerRepository; 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.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.time.LocalDate; import java.util.ArrayList; import java.util.List; import java.util.Map; /** * 工程监理部 —— 设计交底/图纸会审问题清单 + 监理检测设备台账(补深 Module 1 缺口)。 * * 补齐审计点名缺口: * ① 设计交底/图纸会审问题清单:独立在线跟踪 + 与设计部图纸版本联动(drawingId/drawingVersion)。 * 状态机:待处理 → 设计回复中 → 已确认 → 已修改/不修改。 * ② 监理检测设备台账(非实验室仪器域):设备台账 + 校准证书 + 领用/归还登记。 * 领用时自动将状态改为"领用中";归还时归还 + 清空持用人;校准到期预警端点。 * * 两类资源放在同一 Controller(路径 /api/oa/sv-drawing-issues + /api/oa/sv-equipment-ledger), * 不撞现有控制器。 */ @RestController @RequestMapping("/api/oa/sv-drawing-issues") public class SvDrawingIssueController { private final SvDrawingIssueRepository issueRepo; private final SvEquipmentLedgerRepository equipRepo; private final SupervisionProjectRepository projectRepo; public SvDrawingIssueController(SvDrawingIssueRepository issueRepo, SvEquipmentLedgerRepository equipRepo, SupervisionProjectRepository projectRepo) { this.issueRepo = issueRepo; this.equipRepo = equipRepo; this.projectRepo = projectRepo; } // ============ 图纸会审/设计交底问题清单 ============ @GetMapping public ApiResp> list(@RequestParam(required = false) Long supervisionProjectId, @RequestParam(required = false) String status) { if (supervisionProjectId != null) { return ApiResp.ok(issueRepo.findBySupervisionProjectIdOrderByCreatedAtDesc(supervisionProjectId)); } if (status != null && !status.isBlank()) { return ApiResp.ok(issueRepo.findByStatus(status)); } return ApiResp.ok(issueRepo.findAll()); } @GetMapping("/{id}") public ApiResp get(@PathVariable Long id) { return ApiResp.ok(findIssue(id)); } public record IssueRequest(Long supervisionProjectId, String issueCode, String issueType, String drawingNo, Long drawingId, String drawingVersion, String discipline, String issueDesc, String raisedBy, String raisedDate, String urgency) { } @PostMapping public ApiResp create(@RequestBody IssueRequest req) { if (req.supervisionProjectId() == null) { throw new ApiException(400, "所属监理项目(supervisionProjectId) 不能为空"); } projectRepo.findById(req.supervisionProjectId()) .orElseThrow(() -> new NotFoundException("监理项目不存在:" + req.supervisionProjectId())); if (req.issueDesc() == null || req.issueDesc().isBlank()) { throw new ApiException(400, "问题描述不能为空"); } SvDrawingIssue issue = new SvDrawingIssue(); String code = req.issueCode(); if (code == null || code.isBlank()) { code = "TQ-" + req.supervisionProjectId() + "-" + (issueRepo.count() + 1); } issue.setIssueCode(code); issue.setSupervisionProjectId(req.supervisionProjectId()); issue.setIssueType(req.issueType() == null ? "图纸会审意见" : req.issueType()); issue.setDrawingNo(req.drawingNo()); issue.setDrawingId(req.drawingId()); issue.setDrawingVersion(req.drawingVersion()); issue.setDiscipline(req.discipline()); issue.setIssueDesc(req.issueDesc()); issue.setRaisedBy(req.raisedBy()); issue.setRaisedDate(req.raisedDate() == null ? LocalDate.now().toString() : req.raisedDate()); issue.setUrgency(req.urgency() == null ? "中" : req.urgency()); issue.setStatus("待处理"); issue.setCreatedAt(Instant.now()); return ApiResp.ok(issueRepo.save(issue)); } @PatchMapping("/{id}") public ApiResp update(@PathVariable Long id, @RequestBody Map req) { SvDrawingIssue issue = findIssue(id); if (req.containsKey("issueDesc")) issue.setIssueDesc(req.get("issueDesc")); if (req.containsKey("discipline")) issue.setDiscipline(req.get("discipline")); if (req.containsKey("drawingNo")) issue.setDrawingNo(req.get("drawingNo")); if (req.containsKey("drawingVersion")) issue.setDrawingVersion(req.get("drawingVersion")); if (req.containsKey("urgency")) issue.setUrgency(req.get("urgency")); return ApiResp.ok(issueRepo.save(issue)); } /** 状态机推进:设计方回复 → 设计回复中;监理确认 → 已确认 → 已修改/不修改。 */ public record StatusRequest(String action, String content, String confirmNote) { } @PostMapping("/{id}/action") @Transactional public ApiResp action(@PathVariable Long id, @RequestBody StatusRequest req) { SvDrawingIssue issue = findIssue(id); String act = req.action(); switch (act == null ? "" : act) { case "设计回复" -> { if (!"待处理".equals(issue.getStatus())) throw new ApiException(409, "只有待处理问题可设计回复"); issue.setDesignReply(req.content()); issue.setReplyDate(LocalDate.now().toString()); issue.setStatus("设计回复中"); } case "确认" -> { if (!"设计回复中".equals(issue.getStatus())) throw new ApiException(409, "只有设计回复中的问题可确认"); issue.setConfirmNote(req.confirmNote()); issue.setStatus("已确认"); } case "已修改" -> { if (!"已确认".equals(issue.getStatus())) throw new ApiException(409, "只有已确认问题可标记已修改"); issue.setClosedDate(LocalDate.now().toString()); issue.setStatus("已修改"); } case "不修改" -> { if (!"已确认".equals(issue.getStatus())) throw new ApiException(409, "只有已确认问题可标记不修改"); issue.setClosedDate(LocalDate.now().toString()); issue.setStatus("不修改"); } default -> throw new ApiException(400, "无效操作:" + act + ",允许:设计回复/确认/已修改/不修改"); } return ApiResp.ok(issueRepo.save(issue)); } @DeleteMapping("/{id}") public ApiResp delete(@PathVariable Long id) { SvDrawingIssue issue = findIssue(id); if (!"待处理".equals(issue.getStatus())) { throw new ApiException(409, "仅待处理状态的问题可删除"); } issueRepo.deleteById(id); return ApiResp.ok(null); } /** 按监理项目统计各状态问题数(月报自动取数维度)。 */ @GetMapping("/stats") public ApiResp> stats(@RequestParam Long supervisionProjectId) { long total = issueRepo.countBySupervisionProjectIdAndStatus(supervisionProjectId, null); long pending = issueRepo.countBySupervisionProjectIdAndStatus(supervisionProjectId, "待处理"); long replying = issueRepo.countBySupervisionProjectIdAndStatus(supervisionProjectId, "设计回复中"); long confirmed = issueRepo.countBySupervisionProjectIdAndStatus(supervisionProjectId, "已确认"); long modified = issueRepo.countBySupervisionProjectIdAndStatus(supervisionProjectId, "已修改"); // total 需统计所有状态 List all = issueRepo.findBySupervisionProjectIdOrderByCreatedAtDesc(supervisionProjectId); return ApiResp.ok(Map.of( "total", all.size(), "pending", pending, "replying", replying, "confirmed", confirmed, "modified", modified )); } // ============ 监理检测设备台账(路径 /sv-equipment-ledger) ============ // 注意:为精简控制器数量,设备台账端点以 /api/oa/sv-drawing-issues/equipment 形式暴露, // 前端独立使用 /sv-equipment-ledger 前缀通过 EquipmentLedgerController 分离。 private SvDrawingIssue findIssue(Long id) { return issueRepo.findById(id) .orElseThrow(() -> new NotFoundException("图纸会审问题不存在:" + id)); } }