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,124 @@
|
||||
package com.kaidi.oa.web;
|
||||
|
||||
import com.kaidi.oa.common.ApiResp;
|
||||
import com.kaidi.oa.domain.FormInstance;
|
||||
import com.kaidi.oa.domain.SysUser;
|
||||
import com.kaidi.oa.repository.FormInstanceRepository;
|
||||
import com.kaidi.oa.repository.ProjectRepository;
|
||||
import com.kaidi.oa.service.AuthorizationService;
|
||||
import com.kaidi.oa.service.WorkflowService;
|
||||
import com.kaidi.oa.web.AlertController.Alert;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Mobile work-bench aggregation (Request.MD 3.10 移动工作台后端聚合). Folds the
|
||||
* counts and short feeds the mobile home screen needs into a single response:
|
||||
* pending tasks, platform alerts, danger-level alerts and project total, plus
|
||||
* the first five alerts and first five todos. Alert aggregation is reused from
|
||||
* AlertController to avoid re-implementing the warning logic.
|
||||
*/
|
||||
@RestController
|
||||
@RequestMapping("/api/oa/mobile")
|
||||
public class MobileController {
|
||||
|
||||
/** Maximum number of items in each short preview list. */
|
||||
private static final int PREVIEW_LIMIT = 5;
|
||||
|
||||
private final FormInstanceRepository instanceRepo;
|
||||
private final ProjectRepository projectRepo;
|
||||
private final AlertController alertController;
|
||||
private final CurrentUserResolver currentUser;
|
||||
private final AuthorizationService authz;
|
||||
private final WorkflowService workflowService;
|
||||
|
||||
public MobileController(FormInstanceRepository instanceRepo,
|
||||
ProjectRepository projectRepo,
|
||||
AlertController alertController,
|
||||
CurrentUserResolver currentUser,
|
||||
AuthorizationService authz,
|
||||
WorkflowService workflowService) {
|
||||
this.instanceRepo = instanceRepo;
|
||||
this.projectRepo = projectRepo;
|
||||
this.alertController = alertController;
|
||||
this.currentUser = currentUser;
|
||||
this.authz = authz;
|
||||
this.workflowService = workflowService;
|
||||
}
|
||||
|
||||
@GetMapping("/summary")
|
||||
public ApiResp<Map<String, Object>> summary(HttpServletRequest http) {
|
||||
// 平台预警里含证件PII/预算/履约等敏感聚合,与 /alerts 同源——移动端汇总对非 ADMIN/APPROVER
|
||||
// 一律剥除预警内容与计数,只保留本人待办/项目数,避免移动端成为敏感读门槛的旁路。
|
||||
SysUser su = currentUser.resolve(http);
|
||||
Set<String> roles = su == null ? Collections.emptySet() : authz.roleCodesOf(su.getId());
|
||||
boolean canSeeAlerts = roles.contains("ADMIN") || roles.contains("APPROVER");
|
||||
// Pending tasks: same todo definition as TaskController (待办 / 办理中 /
|
||||
// 已退回 form instances)。**按查看者过滤为"本人发起或本人当前办理"的单据**——否则该聚合端点
|
||||
// 会把全库待办的标题/类别/审批节点端给任意 USER,旁路 form-instance 详情的对象级读门槛(读 IDOR)。
|
||||
List<FormInstance> todoInstances = new ArrayList<>();
|
||||
for (FormInstance it : instanceRepo.findAll()) {
|
||||
String st = it.getStatus();
|
||||
boolean pending = "待办".equals(st) || "办理中".equals(st) || "已退回".equals(st);
|
||||
if (!pending) {
|
||||
continue;
|
||||
}
|
||||
boolean mine = authz.isOwner(su, it.getOriginUserId(), it.getOriginUser())
|
||||
|| workflowService.isCurrentHandler(it.getId(), su);
|
||||
if (mine) {
|
||||
todoInstances.add(it);
|
||||
}
|
||||
}
|
||||
|
||||
// Platform alerts reused from AlertController.aggregate() —— 仅对有权看敏感聚合的角色计算/返回。
|
||||
List<Alert> alerts = canSeeAlerts ? alertController.aggregate() : Collections.emptyList();
|
||||
int dangerCount = 0;
|
||||
for (Alert a : alerts) {
|
||||
if ("danger".equals(a.level())) {
|
||||
dangerCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Map<String, Object> data = new LinkedHashMap<>();
|
||||
data.put("todoCount", todoInstances.size());
|
||||
data.put("alertCount", alerts.size());
|
||||
data.put("dangerCount", dangerCount);
|
||||
data.put("projectCount", (int) projectRepo.count());
|
||||
data.put("alerts", preview(alerts));
|
||||
data.put("todos", todoPreview(todoInstances));
|
||||
return ApiResp.ok(data);
|
||||
}
|
||||
|
||||
/** First five alerts, untouched. */
|
||||
private List<Alert> preview(List<Alert> alerts) {
|
||||
return alerts.size() > PREVIEW_LIMIT
|
||||
? new ArrayList<>(alerts.subList(0, PREVIEW_LIMIT))
|
||||
: alerts;
|
||||
}
|
||||
|
||||
/** First five todos projected to a compact map for the mobile list. */
|
||||
private List<Map<String, Object>> todoPreview(List<FormInstance> todos) {
|
||||
List<Map<String, Object>> out = new ArrayList<>();
|
||||
int limit = Math.min(PREVIEW_LIMIT, todos.size());
|
||||
for (int i = 0; i < limit; i++) {
|
||||
FormInstance it = todos.get(i);
|
||||
Map<String, Object> m = new LinkedHashMap<>();
|
||||
m.put("id", it.getId());
|
||||
m.put("title", it.getTitle());
|
||||
m.put("category", it.getCategory());
|
||||
m.put("status", it.getStatus());
|
||||
m.put("currentNode", it.getCurrentNode());
|
||||
out.add(m);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user