Files
ERP/oa-backend/src/main/java/com/kaidi/oa/web/ArchiveVaultController.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

134 lines
5.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.ArchivePhysicalItem;
import com.kaidi.oa.domain.ArchiveVault;
import com.kaidi.oa.repository.ArchivePhysicalItemRepository;
import com.kaidi.oa.repository.ArchiveVaultRepository;
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.util.List;
/**
* 资料室·虚拟档案库管理(Module 1「档案库管理」缺口补深)。维护对应实体库房 / 密集架 /
* 电子存储区 / 云存储的虚拟档案库,支持按年度、部门、保密等级、保存期限维度建库。
* 实体盒上架时落库到此处档案库,电子档记云存储 ID。写口默认受 default-deny(ADMIN/APPROVER) 保护。
*/
@RestController
@RequestMapping("/api/oa/archive-vaults")
public class ArchiveVaultController {
private final ArchiveVaultRepository vaultRepo;
private final ArchivePhysicalItemRepository itemRepo;
public ArchiveVaultController(ArchiveVaultRepository vaultRepo,
ArchivePhysicalItemRepository itemRepo) {
this.vaultRepo = vaultRepo;
this.itemRepo = itemRepo;
}
@GetMapping
public ApiResp<List<ArchiveVault>> list(@RequestParam(required = false) String storeType,
@RequestParam(required = false) Boolean onlyEnabled) {
List<ArchiveVault> list;
if (storeType != null && !storeType.isBlank()) {
list = vaultRepo.findByStoreType(storeType);
} else if (Boolean.TRUE.equals(onlyEnabled)) {
list = vaultRepo.findByEnabledTrue();
} else {
list = vaultRepo.findAll();
}
return ApiResp.ok(list);
}
@GetMapping("/{id}")
public ApiResp<ArchiveVault> get(@PathVariable Long id) {
return ApiResp.ok(require(id));
}
/** 该档案库已上架(非销毁)实体盒数,用于容量占用展示。 */
@GetMapping("/{id}/usage")
public ApiResp<VaultUsage> usage(@PathVariable Long id) {
ArchiveVault v = require(id);
long used = itemRepo.findByVaultId(id).stream()
.filter(i -> !"已销毁".equals(i.getStatus()))
.count();
int cap = v.getCapacity() == null ? 0 : v.getCapacity();
return ApiResp.ok(new VaultUsage(id, v.getName(), cap, used,
cap <= 0 ? null : Math.max(0, cap - used)));
}
public record VaultUsage(Long vaultId, String name, int capacity, long used, Long remaining) {
}
public record VaultRequest(
String name, String storeType, String roomNo, String cloudId,
String dimYear, String dimDept, String dimSecLevel, String dimRetention,
Integer capacity, Boolean enabled, String remark) {
}
@PostMapping
public ApiResp<ArchiveVault> create(@RequestBody VaultRequest req) {
if (req.name() == null || req.name().isBlank()) {
throw new ApiException(400, "档案库名称(name) 不能为空");
}
ArchiveVault v = new ArchiveVault();
v.setCode("VL-" + (vaultRepo.count() + 1));
apply(v, req, true);
v.setCreatedAt(Instant.now());
return ApiResp.ok(vaultRepo.save(v));
}
@PatchMapping("/{id}")
public ApiResp<ArchiveVault> update(@PathVariable Long id, @RequestBody VaultRequest req) {
ArchiveVault v = require(id);
apply(v, req, false);
return ApiResp.ok(vaultRepo.save(v));
}
@DeleteMapping("/{id}")
public ApiResp<Void> delete(@PathVariable Long id) {
ArchiveVault v = require(id);
long inUse = itemRepo.findByVaultId(id).stream()
.filter(i -> !"已销毁".equals(i.getStatus()))
.count();
if (inUse > 0) {
throw new ApiException(409, "该档案库内仍有 " + inUse + " 个在库盒/卷,不能删除");
}
vaultRepo.delete(v);
return ApiResp.ok();
}
private void apply(ArchiveVault v, VaultRequest req, boolean create) {
if (req.name() != null && !req.name().isBlank()) v.setName(req.name().trim());
if (req.storeType() != null) v.setStoreType(req.storeType());
else if (create) v.setStoreType("实体库房");
if (req.roomNo() != null) v.setRoomNo(req.roomNo());
if (req.cloudId() != null) v.setCloudId(req.cloudId());
if (req.dimYear() != null) v.setDimYear(req.dimYear());
if (req.dimDept() != null) v.setDimDept(req.dimDept());
if (req.dimSecLevel() != null) v.setDimSecLevel(req.dimSecLevel());
if (req.dimRetention() != null) v.setDimRetention(req.dimRetention());
if (req.capacity() != null) v.setCapacity(req.capacity());
if (req.enabled() != null) v.setEnabled(req.enabled());
else if (create) v.setEnabled(Boolean.TRUE);
if (req.remark() != null) v.setRemark(req.remark());
}
private ArchiveVault require(Long id) {
return vaultRepo.findById(id)
.orElseThrow(() -> new NotFoundException("archive vault not found: " + id));
}
}