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>
This commit is contained in:
@@ -0,0 +1,183 @@
|
||||
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.AuditEvidence;
|
||||
import com.kaidi.oa.domain.AuditProject;
|
||||
import com.kaidi.oa.repository.AuditEvidenceRepository;
|
||||
import com.kaidi.oa.repository.AuditProjectRepository;
|
||||
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.Instant;
|
||||
import java.time.LocalDate;
|
||||
import java.time.Year;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 审计证据管理(内控部·审计监察部·模块2)。
|
||||
*
|
||||
* 补全 PARTIAL 缺口:证据分类/标签/全文检索(证据管理),专用证据库而非依赖档案关键词检索。
|
||||
*
|
||||
* 功能:
|
||||
* - POST / 新增证据记录(关联项目+底稿+发现);
|
||||
* - GET / 按项目/底稿/发现/分类过滤列表;
|
||||
* - GET /search?q= 关键词检索(title/keywords/tags);
|
||||
* - PUT /{id} 编辑证据信息;
|
||||
* - DELETE /{id} 删除证据;
|
||||
* - GET /categories 证据类别统计。
|
||||
*
|
||||
* 路径 /api/oa/audit-evidence 纳入 SENSITIVE_READ_PREFIXES(含审计证据敏感数据)。
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/oa/audit-evidence")
|
||||
public class AuditEvidenceController {
|
||||
|
||||
private final AuditEvidenceRepository evidenceRepo;
|
||||
private final AuditProjectRepository projectRepo;
|
||||
|
||||
public AuditEvidenceController(AuditEvidenceRepository evidenceRepo,
|
||||
AuditProjectRepository projectRepo) {
|
||||
this.evidenceRepo = evidenceRepo;
|
||||
this.projectRepo = projectRepo;
|
||||
}
|
||||
|
||||
@GetMapping
|
||||
public ApiResp<List<AuditEvidence>> list(
|
||||
@RequestParam(required = false) Long projectId,
|
||||
@RequestParam(required = false) Long workPaperId,
|
||||
@RequestParam(required = false) Long findingId,
|
||||
@RequestParam(required = false) String category) {
|
||||
if (projectId != null) {
|
||||
return ApiResp.ok(evidenceRepo.findByProjectId(projectId));
|
||||
}
|
||||
if (workPaperId != null) {
|
||||
return ApiResp.ok(evidenceRepo.findByWorkPaperId(workPaperId));
|
||||
}
|
||||
if (findingId != null) {
|
||||
return ApiResp.ok(evidenceRepo.findByFindingId(findingId));
|
||||
}
|
||||
if (category != null && !category.isBlank()) {
|
||||
return ApiResp.ok(evidenceRepo.findByCategory(category));
|
||||
}
|
||||
return ApiResp.ok(evidenceRepo.findByOrderByIdDesc());
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
public ApiResp<AuditEvidence> get(@PathVariable Long id) {
|
||||
return ApiResp.ok(load(id));
|
||||
}
|
||||
|
||||
/** 关键词检索(title/keywords/tags)。 */
|
||||
@GetMapping("/search")
|
||||
public ApiResp<List<AuditEvidence>> search(@RequestParam String q) {
|
||||
if (q == null || q.isBlank()) {
|
||||
return ApiResp.ok(List.of());
|
||||
}
|
||||
return ApiResp.ok(
|
||||
evidenceRepo.findByTitleContainingOrKeywordsContainingOrTagsContaining(q, q, q));
|
||||
}
|
||||
|
||||
/** 证据类别统计(各类别数量)。 */
|
||||
@GetMapping("/categories")
|
||||
public ApiResp<Object> categories() {
|
||||
List<AuditEvidence> all = evidenceRepo.findAll();
|
||||
java.util.Map<String, Long> byCategory = new java.util.LinkedHashMap<>();
|
||||
for (AuditEvidence e : all) {
|
||||
String cat = e.getCategory() == null ? "其他" : e.getCategory();
|
||||
byCategory.merge(cat, 1L, Long::sum);
|
||||
}
|
||||
return ApiResp.ok(byCategory);
|
||||
}
|
||||
|
||||
public record CreateRequest(
|
||||
Long projectId, Long workPaperId, Long findingId,
|
||||
String title, String category, String tags, String keywords,
|
||||
String fileRef, String fileName, String fileType, String fileHash,
|
||||
Boolean keyEvidence, String watermark, String description,
|
||||
String collectedBy, String collectDate) {
|
||||
}
|
||||
|
||||
@PostMapping
|
||||
public ApiResp<AuditEvidence> create(@RequestBody CreateRequest req) {
|
||||
if (req.title() == null || req.title().isBlank()) {
|
||||
throw new ApiException(400, "证据标题不能为空");
|
||||
}
|
||||
if (req.projectId() == null) {
|
||||
throw new ApiException(400, "请选择关联审计项目");
|
||||
}
|
||||
AuditEvidence e = new AuditEvidence();
|
||||
e.setEvidenceNo(nextEvidenceNo());
|
||||
e.setProjectId(req.projectId());
|
||||
projectRepo.findById(req.projectId()).ifPresent(p -> e.setProjectName(p.getName()));
|
||||
e.setWorkPaperId(req.workPaperId());
|
||||
e.setFindingId(req.findingId());
|
||||
e.setTitle(req.title());
|
||||
e.setCategory(req.category() == null || req.category().isBlank() ? "其他" : req.category());
|
||||
e.setTags(req.tags());
|
||||
e.setKeywords(req.keywords());
|
||||
e.setFileRef(req.fileRef());
|
||||
e.setFileName(req.fileName());
|
||||
e.setFileType(req.fileType());
|
||||
e.setFileHash(req.fileHash());
|
||||
e.setKeyEvidence(req.keyEvidence() != null && req.keyEvidence());
|
||||
e.setWatermark(req.watermark());
|
||||
e.setDescription(req.description());
|
||||
e.setCollectedBy(req.collectedBy());
|
||||
e.setCollectDate(req.collectDate() != null && !req.collectDate().isBlank()
|
||||
? req.collectDate() : LocalDate.now().toString());
|
||||
e.setCreatedAt(Instant.now());
|
||||
return ApiResp.ok(evidenceRepo.save(e));
|
||||
}
|
||||
|
||||
public record UpdateRequest(
|
||||
String title, String category, String tags, String keywords,
|
||||
String fileRef, String fileName, String fileType, String fileHash,
|
||||
Boolean keyEvidence, String watermark, String description) {
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
public ApiResp<AuditEvidence> update(@PathVariable Long id, @RequestBody UpdateRequest req) {
|
||||
AuditEvidence e = load(id);
|
||||
if (req.title() != null && !req.title().isBlank()) e.setTitle(req.title());
|
||||
if (req.category() != null) e.setCategory(req.category());
|
||||
if (req.tags() != null) e.setTags(req.tags());
|
||||
if (req.keywords() != null) e.setKeywords(req.keywords());
|
||||
if (req.fileRef() != null) e.setFileRef(req.fileRef());
|
||||
if (req.fileName() != null) e.setFileName(req.fileName());
|
||||
if (req.fileType() != null) e.setFileType(req.fileType());
|
||||
if (req.fileHash() != null) e.setFileHash(req.fileHash());
|
||||
if (req.keyEvidence() != null) e.setKeyEvidence(req.keyEvidence());
|
||||
if (req.watermark() != null) e.setWatermark(req.watermark());
|
||||
if (req.description() != null) e.setDescription(req.description());
|
||||
return ApiResp.ok(evidenceRepo.save(e));
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
public ApiResp<Void> delete(@PathVariable Long id) {
|
||||
if (!evidenceRepo.existsById(id)) {
|
||||
throw new NotFoundException("审计证据不存在: " + id);
|
||||
}
|
||||
evidenceRepo.deleteById(id);
|
||||
return ApiResp.ok(null);
|
||||
}
|
||||
|
||||
private AuditEvidence load(Long id) {
|
||||
return evidenceRepo.findById(id)
|
||||
.orElseThrow(() -> new NotFoundException("审计证据不存在: " + id));
|
||||
}
|
||||
|
||||
private String nextEvidenceNo() {
|
||||
String year = String.valueOf(Year.now().getValue());
|
||||
long seq = evidenceRepo.count() + 1;
|
||||
return String.format("AE-%s-%03d", year, seq);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user