Files
ERP/oa-backend/src/main/java/com/kaidi/oa/web/SvDrawingIssueController.java
T
QiufengandClaude Opus 4.8 5e51dc3f56 SNAPSHOT W7 已部署稳定态 — 凯迪ERP+OA一体化平台 (MET 73.3%)
恢复点(restore point)。别人改崩后可 git reset --hard 回到此提交。

== 此快照内容 ==
- 后端 oa-backend: 734 控制器 / 711 实体 (Spring Boot 3.2.5 + SQLite, 端口8091)
- 前端 modern-ui/app: Vue3+Vite, 约700页 (构建产物已在 oa-backend/src/main/resources/static)
- 数据库 oa-backend/data/oa.db: 含全部演示数据 (强制入库, 6.6MB)
- 交接文档 go.md + go-code-reference/endpoints/entities/database.md
- 多代理建设脚本 .claude/wf-*.js

== 状态 ==
- 对 凯迪科技ERP_20260507.xlsx 合规 MET ~73.3% (PARTIAL 75: 34可建+6种子/bug+35外部硬天花板)
- 安全: 5轮红队+5轮复检, default-deny分级鉴权, 连续零可利用
- W3~W7 累计补完436缺口; W8末轮(40缺口)为半成品(源码树可编译但未集成)
- 运行: cd oa-backend; java -jar build/libs/oa-backend-0.1.0.jar --server.port=8091; admin/123456

== 排除(gitignore, 可再生) ==
node_modules / oa-backend/build / .jdks / *.log / Backup-ERP-* / 弃用的OFBiz核心(只保留modern-ui)
完整文件夹备份见同目录 Backup-ERP-20260615-191517/ (含上述全部, 仅缺 node_modules)

时间戳: 20260615-191517

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 19:19:15 +08:00

197 lines
9.6 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<SvDrawingIssue>> 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<SvDrawingIssue> 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<SvDrawingIssue> 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<SvDrawingIssue> update(@PathVariable Long id, @RequestBody Map<String, String> 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<SvDrawingIssue> 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<Void> 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<Map<String, Object>> 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<SvDrawingIssue> 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));
}
}