恢复点(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>
197 lines
9.6 KiB
Java
197 lines
9.6 KiB
Java
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));
|
||
}
|
||
}
|