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,30 @@
|
||||
package com.kaidi.oa;
|
||||
|
||||
import org.springframework.boot.SpringApplication;
|
||||
import org.springframework.boot.autoconfigure.SpringBootApplication;
|
||||
import org.springframework.scheduling.annotation.EnableScheduling;
|
||||
|
||||
import java.io.File;
|
||||
|
||||
/**
|
||||
* Entry point for the OA (collaborative office) backend.
|
||||
*
|
||||
* Standalone Spring Boot application that replaces the legacy OFBiz engine.
|
||||
* It mirrors the Vue3 + Element Plus OA frontend engine model: form schemas,
|
||||
* flow schemas, templates and submitted form instances driven by a simple
|
||||
* approval state machine.
|
||||
*/
|
||||
@SpringBootApplication
|
||||
@EnableScheduling
|
||||
public class OaBackendApplication {
|
||||
|
||||
public static void main(String[] args) {
|
||||
// Ensure the SQLite data directory exists before the datasource opens it.
|
||||
File dataDir = new File("./data");
|
||||
if (!dataDir.exists()) {
|
||||
//noinspection ResultOfMethodCallIgnored
|
||||
dataDir.mkdirs();
|
||||
}
|
||||
SpringApplication.run(OaBackendApplication.class, args);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
package com.kaidi.oa.common;
|
||||
|
||||
/**
|
||||
* Application-level exception carrying a non-zero response code.
|
||||
* Thrown by services/controllers and translated to an ApiResp by the
|
||||
* global exception handler.
|
||||
*/
|
||||
public class ApiException extends RuntimeException {
|
||||
|
||||
private final int code;
|
||||
|
||||
public ApiException(int code, String message) {
|
||||
super(message);
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public ApiException(String message) {
|
||||
this(1, message);
|
||||
}
|
||||
|
||||
public int getCode() {
|
||||
return code;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package com.kaidi.oa.common;
|
||||
|
||||
/**
|
||||
* Uniform API response envelope.
|
||||
*
|
||||
* code == 0 means success; any non-zero code indicates an error and message
|
||||
* carries the human-readable reason. data holds the payload (null on error).
|
||||
*/
|
||||
public record ApiResp<T>(int code, String message, T data) {
|
||||
|
||||
public static <T> ApiResp<T> ok(T data) {
|
||||
return new ApiResp<>(0, "ok", data);
|
||||
}
|
||||
|
||||
public static <T> ApiResp<T> ok() {
|
||||
return new ApiResp<>(0, "ok", null);
|
||||
}
|
||||
|
||||
public static <T> ApiResp<T> error(int code, String message) {
|
||||
return new ApiResp<>(code, message, null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,87 @@
|
||||
package com.kaidi.oa.common;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.SecureRandom;
|
||||
import java.util.Base64;
|
||||
|
||||
/**
|
||||
* 申报域外部政务平台凭据的「可逆加密」工具(创新研发中心·申报服务部,需求 §2 账号密码加密存储)。
|
||||
*
|
||||
* 政务平台登录口令必须能取回供真实登录代填,故不能用单向哈希(与 {@link PasswordUtil} 区别);
|
||||
* 这里用「每条随机盐 + 与盐派生的密钥流做 XOR + Base64 编码」做可逆加密落库,避免明文入库与撞库。
|
||||
* 落库串格式:{@code v1:<base64(salt)>:<base64(xor密文)>},前缀 {@code v1:} 用于识别已加密串避免二次加密。
|
||||
*
|
||||
* 说明:演示环境用对称流加密满足「加密存储/不回明文」的合规口径;生产可替换为 KMS/AES-GCM,
|
||||
* 解密入口受角色门禁(控制器层),列表/详情默认只回掩码而非明文。
|
||||
*/
|
||||
public final class DeclCrypto {
|
||||
|
||||
private static final String PREFIX = "v1:";
|
||||
private static final SecureRandom RANDOM = new SecureRandom();
|
||||
/** 与盐混合的固定基密钥(演示口径;生产应来自外部密钥管理)。 */
|
||||
private static final byte[] BASE_KEY = "kaidi-decl-portal-key-2026".getBytes(StandardCharsets.UTF_8);
|
||||
|
||||
private DeclCrypto() {
|
||||
}
|
||||
|
||||
/** 是否已是本工具加密后的密文串(避免对已加密值二次加密)。 */
|
||||
public static boolean isEncrypted(String s) {
|
||||
return s != null && s.startsWith(PREFIX);
|
||||
}
|
||||
|
||||
/** 加密:明文 → {@code v1:<saltB64>:<cipherB64>}。null/空 → 原样返回(无口令可存)。 */
|
||||
public static String encrypt(String plain) {
|
||||
if (plain == null || plain.isEmpty()) {
|
||||
return plain;
|
||||
}
|
||||
if (isEncrypted(plain)) {
|
||||
return plain; // 已加密,幂等
|
||||
}
|
||||
byte[] salt = new byte[12];
|
||||
RANDOM.nextBytes(salt);
|
||||
byte[] data = plain.getBytes(StandardCharsets.UTF_8);
|
||||
byte[] cipher = xor(data, salt);
|
||||
return PREFIX + b64(salt) + ":" + b64(cipher);
|
||||
}
|
||||
|
||||
/** 解密:{@code v1:<saltB64>:<cipherB64>} → 明文。非本格式串原样返回(兼容历史明文)。 */
|
||||
public static String decrypt(String stored) {
|
||||
if (stored == null || !isEncrypted(stored)) {
|
||||
return stored;
|
||||
}
|
||||
try {
|
||||
String[] parts = stored.substring(PREFIX.length()).split(":", 2);
|
||||
if (parts.length != 2) {
|
||||
return "";
|
||||
}
|
||||
byte[] salt = Base64.getDecoder().decode(parts[0]);
|
||||
byte[] cipher = Base64.getDecoder().decode(parts[1]);
|
||||
byte[] data = xor(cipher, salt);
|
||||
return new String(data, StandardCharsets.UTF_8);
|
||||
} catch (RuntimeException e) {
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/** 掩码:用于列表/详情展示,不回明文(如 已设置 ●●●●●●)。 */
|
||||
public static String mask(String stored) {
|
||||
if (stored == null || stored.isEmpty()) {
|
||||
return "";
|
||||
}
|
||||
return "已设置 ●●●●●●";
|
||||
}
|
||||
|
||||
/** 用盐与基密钥派生密钥流做按位异或(流加密)。 */
|
||||
private static byte[] xor(byte[] data, byte[] salt) {
|
||||
byte[] out = new byte[data.length];
|
||||
for (int i = 0; i < data.length; i++) {
|
||||
byte k = (byte) (BASE_KEY[i % BASE_KEY.length] ^ salt[i % salt.length] ^ (i * 31));
|
||||
out[i] = (byte) (data[i] ^ k);
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
private static String b64(byte[] b) {
|
||||
return Base64.getEncoder().encodeToString(b);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,201 @@
|
||||
package com.kaidi.oa.common;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.dao.ConcurrencyFailureException;
|
||||
import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.orm.jpa.JpaSystemException;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.http.converter.HttpMessageNotReadableException;
|
||||
import org.springframework.transaction.UnexpectedRollbackException;
|
||||
import org.springframework.validation.FieldError;
|
||||
import org.springframework.web.HttpRequestMethodNotSupportedException;
|
||||
import org.springframework.web.HttpMediaTypeNotSupportedException;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.MissingServletRequestParameterException;
|
||||
import org.springframework.web.multipart.MultipartException;
|
||||
import org.springframework.web.multipart.support.MissingServletRequestPartException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
import org.springframework.web.method.annotation.MethodArgumentTypeMismatchException;
|
||||
import org.springframework.web.servlet.NoHandlerFoundException;
|
||||
import org.springframework.web.servlet.resource.NoResourceFoundException;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Translates exceptions into the uniform {@link ApiResp} envelope so that every
|
||||
* error response also carries {code != 0, message}. HTTP status stays 200 for
|
||||
* application errors (envelope code conveys the failure) except where a more
|
||||
* specific status is meaningful.
|
||||
*
|
||||
* 横切健壮:框架层异常(请求方法不支持 / 路径不存在 / 路径参数类型不符 / 请求体坏 JSON /
|
||||
* 缺必填参数)一律映射成对应的 4xx + 友好 message,**绝不把原始 Java/SQL 异常文案回吐前端**
|
||||
* (避免信息泄露 + 误导性的 500)。原始异常仅写服务端日志,便于排障。
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
@ExceptionHandler(NotFoundException.class)
|
||||
public ResponseEntity<ApiResp<Void>> handleNotFound(NotFoundException ex) {
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND)
|
||||
.body(ApiResp.error(ex.getCode(), ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(ApiException.class)
|
||||
public ResponseEntity<ApiResp<Void>> handleApi(ApiException ex) {
|
||||
return ResponseEntity.ok(ApiResp.error(ex.getCode(), ex.getMessage()));
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
public ResponseEntity<ApiResp<Void>> handleValidation(MethodArgumentNotValidException ex) {
|
||||
String msg = ex.getBindingResult().getFieldErrors().stream()
|
||||
.map(GlobalExceptionHandler::formatFieldError)
|
||||
.collect(Collectors.joining("; "));
|
||||
return ResponseEntity.ok(ApiResp.error(400, msg.isEmpty() ? "validation failed" : msg));
|
||||
}
|
||||
|
||||
@ExceptionHandler(IllegalArgumentException.class)
|
||||
public ResponseEntity<ApiResp<Void>> handleIllegalArgument(IllegalArgumentException ex) {
|
||||
return ResponseEntity.ok(ApiResp.error(400, ex.getMessage()));
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求方法不支持(如对集合端点发 DELETE)。原来落到通用 500 并回吐
|
||||
* "Request method 'DELETE' is not supported"。现统一映射成 405 + 友好提示。
|
||||
*/
|
||||
@ExceptionHandler(HttpRequestMethodNotSupportedException.class)
|
||||
public ResponseEntity<ApiResp<Void>> handleMethodNotSupported(HttpRequestMethodNotSupportedException ex) {
|
||||
log.warn("method not supported: {} {}", ex.getMethod(), ex.getSupportedHttpMethods());
|
||||
return ResponseEntity.status(HttpStatus.METHOD_NOT_ALLOWED)
|
||||
.body(ApiResp.error(405, "请求方法不被支持"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径不存在。Spring Boot 3.2 / Spring 6.1 对未匹配路径抛
|
||||
* {@link NoResourceFoundException}("No static resource ..."),旧版本抛
|
||||
* {@link NoHandlerFoundException}。两者都收口成 404 + 友好提示,不回吐路径细节文案。
|
||||
*/
|
||||
@ExceptionHandler({NoResourceFoundException.class, NoHandlerFoundException.class})
|
||||
public ResponseEntity<ApiResp<Void>> handleNoHandler(Exception ex) {
|
||||
log.warn("no handler / resource: {}", ex.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.NOT_FOUND)
|
||||
.body(ApiResp.error(404, "请求的资源不存在"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 路径/查询参数类型不符(如 /cost-centers/abc 把 abc 喂给 Long id)。原来落到 500 并
|
||||
* 回吐 "Failed to convert value of type ... For input string: \"abc\""。现映射成 400。
|
||||
*/
|
||||
@ExceptionHandler(MethodArgumentTypeMismatchException.class)
|
||||
public ResponseEntity<ApiResp<Void>> handleTypeMismatch(MethodArgumentTypeMismatchException ex) {
|
||||
log.warn("argument type mismatch on '{}': {}", ex.getName(), ex.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
.body(ApiResp.error(400, "参数「" + ex.getName() + "」格式不正确"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 请求体不可读 / 坏 JSON。原来落到 500 并回吐 Jackson 内部解析细节
|
||||
* ("JSON parse error: Unexpected character ...")。现映射成 400 + 通用提示。
|
||||
*/
|
||||
@ExceptionHandler(HttpMessageNotReadableException.class)
|
||||
public ResponseEntity<ApiResp<Void>> handleNotReadable(HttpMessageNotReadableException ex) {
|
||||
log.warn("unreadable request body: {}", ex.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
.body(ApiResp.error(400, "请求体格式不正确(无法解析的 JSON)"));
|
||||
}
|
||||
|
||||
/** 缺少必填查询参数。统一 400 + 指明参数名,不回吐底层文案。 */
|
||||
@ExceptionHandler(MissingServletRequestParameterException.class)
|
||||
public ResponseEntity<ApiResp<Void>> handleMissingParam(MissingServletRequestParameterException ex) {
|
||||
log.warn("missing request parameter: {}", ex.getParameterName());
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
.body(ApiResp.error(400, "缺少必填参数「" + ex.getParameterName() + "」"));
|
||||
}
|
||||
|
||||
/** 缺少必填的 multipart 分片(如上传缺 file 字段)。属非法入参,统一 400 而非 500。 */
|
||||
@ExceptionHandler(MissingServletRequestPartException.class)
|
||||
public ResponseEntity<ApiResp<Void>> handleMissingPart(MissingServletRequestPartException ex) {
|
||||
log.warn("missing request part: {}", ex.getRequestPartName());
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
.body(ApiResp.error(400, "缺少必填上传内容「" + ex.getRequestPartName() + "」"));
|
||||
}
|
||||
|
||||
/** 非法/非 multipart 的上传请求(如对上传口发了非 multipart 请求)。同属非法入参,统一 400 而非 500。 */
|
||||
@ExceptionHandler(MultipartException.class)
|
||||
public ResponseEntity<ApiResp<Void>> handleMultipart(MultipartException ex) {
|
||||
log.warn("invalid multipart request: {}", ex.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.BAD_REQUEST)
|
||||
.body(ApiResp.error(400, "上传请求格式不正确:需以 multipart/form-data 提交文件"));
|
||||
}
|
||||
|
||||
/** 请求体 Content-Type 不被支持。统一 415 + 友好提示。 */
|
||||
@ExceptionHandler(HttpMediaTypeNotSupportedException.class)
|
||||
public ResponseEntity<ApiResp<Void>> handleMediaType(HttpMediaTypeNotSupportedException ex) {
|
||||
log.warn("unsupported media type: {}", ex.getContentType());
|
||||
return ResponseEntity.status(HttpStatus.UNSUPPORTED_MEDIA_TYPE)
|
||||
.body(ApiResp.error(415, "不支持的请求内容类型"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 数据库约束冲突(唯一键 / NOT NULL / 外键)。原来落到通用 500 并可能回吐 SQL/表名。
|
||||
* 现统一映射成 409 + 通用提示,且原始 SQL 异常仅写日志——绝不外泄表结构。
|
||||
* 典型来源:并发写撞唯一约束、必填字段缺失。
|
||||
*/
|
||||
@ExceptionHandler(DataIntegrityViolationException.class)
|
||||
public ResponseEntity<ApiResp<Void>> handleDataIntegrity(DataIntegrityViolationException ex) {
|
||||
log.warn("data integrity violation: {}",
|
||||
ex.getMostSpecificCause() != null ? ex.getMostSpecificCause().getMessage() : ex.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT)
|
||||
.body(ApiResp.error(409, "操作与现有数据冲突或违反完整性约束,请核对后重试"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 并发/回滚冲突。典型:同一事项被并发办结,落后的事务被标记 rollback-only 或撞唯一约束。
|
||||
* 此前会透成不明 500;现收口成 409 + 可理解提示(数据一致性已由约束/守卫保证,无脏写)。
|
||||
*/
|
||||
@ExceptionHandler({UnexpectedRollbackException.class, ConcurrencyFailureException.class})
|
||||
public ResponseEntity<ApiResp<Void>> handleConcurrency(Exception ex) {
|
||||
log.warn("concurrent / rollback conflict: {}", ex.getMessage());
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT)
|
||||
.body(ApiResp.error(409, "操作冲突:该事项可能正在被处理或已处理,请刷新后重试"));
|
||||
}
|
||||
|
||||
/**
|
||||
* Hibernate/SQLite 下,唯一键 / NOT NULL / 外键等约束违反常被包成 {@link JpaSystemException}
|
||||
* 而非 Spring 的 DataIntegrityViolationException(后者对 SQLite 几乎命中不到)。这里按根因
|
||||
* 文案识别 SQLITE_CONSTRAINT,将约束类失败收口成 409,其余 JPA 系统异常仍回 500——
|
||||
* 两种情况都只给通用提示,绝不外泄 SQL / 表名 / 栈。
|
||||
*/
|
||||
@ExceptionHandler(JpaSystemException.class)
|
||||
public ResponseEntity<ApiResp<Void>> handleJpaSystem(JpaSystemException ex) {
|
||||
String root = ex.getMostSpecificCause() != null ? ex.getMostSpecificCause().getMessage() : ex.getMessage();
|
||||
String r = root == null ? "" : root.toUpperCase();
|
||||
boolean constraint = r.contains("CONSTRAINT") || r.contains("UNIQUE") || r.contains("NOT NULL");
|
||||
log.warn("jpa system exception (constraint={}): {}", constraint, root);
|
||||
if (constraint) {
|
||||
return ResponseEntity.status(HttpStatus.CONFLICT)
|
||||
.body(ApiResp.error(409, "操作与现有数据冲突或违反完整性约束,请核对后重试"));
|
||||
}
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResp.error(500, "服务器内部错误,请稍后重试"));
|
||||
}
|
||||
|
||||
/**
|
||||
* 兜底:任何未被上面专门处理的异常都回 500,但**只给通用友好提示**,绝不回吐
|
||||
* 原始异常 getMessage()(可能含 Java 栈细节 / SQL / 表名)。原始异常写日志便于排障。
|
||||
*/
|
||||
@ExceptionHandler(Exception.class)
|
||||
public ResponseEntity<ApiResp<Void>> handleGeneric(Exception ex) {
|
||||
log.error("unhandled exception", ex);
|
||||
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
|
||||
.body(ApiResp.error(500, "服务器内部错误,请稍后重试"));
|
||||
}
|
||||
|
||||
private static String formatFieldError(FieldError fe) {
|
||||
return fe.getField() + ": " + fe.getDefaultMessage();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.kaidi.oa.common;
|
||||
|
||||
import org.jsoup.Jsoup;
|
||||
import org.jsoup.nodes.Document;
|
||||
import org.jsoup.safety.Safelist;
|
||||
|
||||
/**
|
||||
* 服务端 HTML 白名单净化:用于用户可控的富文本正文(讨论/协作文档/博客/动态/公告/会议纪要/评论/回帖),
|
||||
* 在写入时清除存储型 XSS 向量,使其即便被前端以 v-html 渲染也不会执行脚本。
|
||||
*
|
||||
* <p>实现采用 jsoup 的 {@link Safelist}(白名单)而非正则黑名单:jsoup 会像浏览器一样把输入解析成
|
||||
* DOM(这一步会解码 HTML 实体,如 {@code javascript:} → {@code javascript:}),再仅保留白名单内的
|
||||
* 标签/属性,并对 a[href]、img[src] 等按白名单协议(http/https/mailto/ftp)校验——非白名单协议
|
||||
* ({@code javascript:}/{@code vbscript:}/{@code data:})、所有 {@code on*} 事件处理器、
|
||||
* {@code <script>/<svg>/<iframe>/<object>} 等危险元素一律被剥离。
|
||||
*
|
||||
* <p>这从根上消除了"正则只匹配字面 javascript: 而被 :/: 等实体编码绕过"的整类问题
|
||||
* (正则黑名单是军备竞赛,jsoup 的解析-再-白名单序列化才是稳健解)。前端仍建议对 v-html 再加一层
|
||||
* DOMPurify 作为纵深防御。
|
||||
*/
|
||||
public final class HtmlSanitizer {
|
||||
|
||||
private HtmlSanitizer() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 富文本白名单:jsoup {@code relaxed}(允许 p/div/span/a/img/ul/table 等常见排版标签,
|
||||
* a[href] 仅 http/https/mailto/ftp、img[src] 仅 http/https;自动剥离所有事件处理器与危险元素)。
|
||||
* 额外允许 a[target] 以兼容"新窗口打开"的链接。
|
||||
*/
|
||||
private static final Safelist SAFELIST = Safelist.relaxed().addAttributes("a", "target");
|
||||
|
||||
/** 紧凑输出:不美化(prettyPrint=false),尽量保留原正文结构。 */
|
||||
private static final Document.OutputSettings OUTPUT =
|
||||
new Document.OutputSettings().prettyPrint(false);
|
||||
|
||||
/**
|
||||
* 净化一段可能含 HTML 的用户正文;null/空 原样返回。
|
||||
*/
|
||||
public static String sanitize(String html) {
|
||||
if (html == null || html.isEmpty()) {
|
||||
return html;
|
||||
}
|
||||
// baseUri 传空串即可;协议白名单仍生效。jsoup.clean 解析(解码实体)→按白名单过滤→再序列化。
|
||||
return Jsoup.clean(html, "", SAFELIST, OUTPUT);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
package com.kaidi.oa.common;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* 金额工具(统一标度与空值处理)。
|
||||
*
|
||||
* 所有"钱"字段一律用 {@link BigDecimal} 而非 double,避免二进制浮点累加误差导致
|
||||
* 对账(已付=合同额、分包累计、发票回写)判等不可靠。本工具把请求层传来的装箱
|
||||
* {@code Double}(前端 JSON number 反序列化结果)安全收口成标度 2、HALF_UP 的
|
||||
* BigDecimal,并提供 null 安全的加减比较,供各控制器/服务复用。
|
||||
*/
|
||||
public final class Money {
|
||||
|
||||
/** 金额标准标度:分(2 位小数)。 */
|
||||
public static final int SCALE = 2;
|
||||
|
||||
public static final BigDecimal ZERO = scaled(BigDecimal.ZERO);
|
||||
|
||||
private Money() {
|
||||
}
|
||||
|
||||
/** 装箱 Double(可空)→ 标度 2 的 BigDecimal;null 视为 0。非有限数(NaN/Infinity)→干净 400。 */
|
||||
public static BigDecimal of(Double v) {
|
||||
if (v == null) {
|
||||
return ZERO;
|
||||
}
|
||||
requireFinite(v);
|
||||
return scaled(BigDecimal.valueOf(v));
|
||||
}
|
||||
|
||||
/** double → 标度 2 的 BigDecimal。非有限数(NaN/Infinity)→干净 400。 */
|
||||
public static BigDecimal of(double v) {
|
||||
requireFinite(v);
|
||||
return scaled(BigDecimal.valueOf(v));
|
||||
}
|
||||
|
||||
/**
|
||||
* 金额必须是有限数值:拒绝 NaN/Infinity/-Infinity(JSON 里的 "NaN"/"Infinity"/1e400 等)。
|
||||
* 统一在金额收口处给出干净的 400 中文提示,杜绝把 {@code BigDecimal.valueOf(非有限)} 的底层
|
||||
* NumberFormatException 原文("Character I is neither a decimal digit...")外泄给前端。
|
||||
*/
|
||||
private static void requireFinite(double v) {
|
||||
if (!Double.isFinite(v)) {
|
||||
throw new ApiException(400, "金额必须是有限数值");
|
||||
}
|
||||
}
|
||||
|
||||
/** BigDecimal(可空)→ 规整标度;null 视为 0。供已是 BigDecimal 的表达式统一收口。 */
|
||||
public static BigDecimal of(BigDecimal v) {
|
||||
return nz(v);
|
||||
}
|
||||
|
||||
/** null 安全:null → 0,否则规整标度。 */
|
||||
public static BigDecimal nz(BigDecimal v) {
|
||||
return v == null ? ZERO : scaled(v);
|
||||
}
|
||||
|
||||
public static BigDecimal add(BigDecimal a, BigDecimal b) {
|
||||
return scaled(nz(a).add(nz(b)));
|
||||
}
|
||||
|
||||
public static BigDecimal sub(BigDecimal a, BigDecimal b) {
|
||||
return scaled(nz(a).subtract(nz(b)));
|
||||
}
|
||||
|
||||
/** a 是否 > b(null 当 0)。 */
|
||||
public static boolean gt(BigDecimal a, BigDecimal b) {
|
||||
return nz(a).compareTo(nz(b)) > 0;
|
||||
}
|
||||
|
||||
/** a 是否 <= 0(null 当 0)。 */
|
||||
public static boolean lte0(BigDecimal a) {
|
||||
return nz(a).compareTo(ZERO) <= 0;
|
||||
}
|
||||
|
||||
private static BigDecimal scaled(BigDecimal v) {
|
||||
return v.setScale(SCALE, RoundingMode.HALF_UP);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
package com.kaidi.oa.common;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
|
||||
/**
|
||||
* 金额文本解析收口(BigDecimal-native)。
|
||||
*
|
||||
* <p>历史上 {@code TriggerRuleEngine.parseNumber} / {@code WorkflowService.parseNumber} /
|
||||
* {@code MobileApprovalController.parseNumber} 三处各自手写了一份"剥噪声→{@code Double.parseDouble}"
|
||||
* 的金额解析,返回裸 {@code double}:自动联动据此生成付款单/发票,金额下游与台账留痕里就会冒出
|
||||
* {@code 5.07}、{@code 9.9999999997} 这类二进制浮点伪值(如 5000万经 JSON 序列化为 "5.0E7",或
|
||||
* 表单 "10.00" 累加后抖动)。本工具把"解析"这一步直接落到 {@link BigDecimal},杜绝中途经过 double:
|
||||
* 用 {@link BigDecimal#BigDecimal(String)}(字符串构造,精确)而非 {@code Double.parseDouble},
|
||||
* 再统一 {@code setScale(2, HALF_UP)} 规整成"分"标度,使解析结果与 {@link Money} 完全同口径。</p>
|
||||
*
|
||||
* <p>容噪规则与原三处保持一致:剥掉 ¥/¥/元/逗号/空白等装饰字符,<b>保留</b> 数字、小数点、正负号
|
||||
* 与科学计数标记 {@code e/E}(大额金额经 JSON 常序列化为 "5.0E7",剥掉 E 会把它错解析成 5.07,
|
||||
* 量级灾难);末尾带「万」整体 ×10000。无法解析时返回 {@code null}(业务侧据此走"未解析到金额→跳过")。</p>
|
||||
*/
|
||||
public final class MoneyParser {
|
||||
|
||||
private static final BigDecimal TEN_THOUSAND = new BigDecimal("10000");
|
||||
|
||||
private MoneyParser() {
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析金额文本 → 标度 2、HALF_UP 的 {@link BigDecimal};解析失败返回 {@code null}。
|
||||
*
|
||||
* <p>关键:用 {@code new BigDecimal(cleaned)}(字符串精确构造,支持科学计数法),<b>绝不</b>
|
||||
* 走 {@code Double.parseDouble},故 "5.0E7"→50000000.00、"10.00"→10.00 全程无浮点抖动。</p>
|
||||
*/
|
||||
public static BigDecimal parse(String raw) {
|
||||
if (raw == null) {
|
||||
return null;
|
||||
}
|
||||
String s = raw.trim();
|
||||
if (s.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
boolean wan = s.contains("万");
|
||||
// 保留 0-9 . e E + - ,剥掉其余(¥ 元 万 千分位逗号 空白…)。务必保留 e/E:科学计数法大额。
|
||||
String cleaned = s.replaceAll("[^0-9.eE+-]", "");
|
||||
if (cleaned.isEmpty() || "+".equals(cleaned) || "-".equals(cleaned) || ".".equals(cleaned)) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
BigDecimal v = new BigDecimal(cleaned);
|
||||
if (wan) {
|
||||
v = v.multiply(TEN_THOUSAND);
|
||||
}
|
||||
return v.setScale(Money.SCALE, RoundingMode.HALF_UP);
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 同 {@link #parse(String)},但解析失败回退为 {@link Money#ZERO}(标度 2 的 0.00)。
|
||||
* 适合"展示/排序用金额,缺失即按 0 处理且不抛错"的场景(如移动待办列表金额列)。
|
||||
*/
|
||||
public static BigDecimal parseOrZero(String raw) {
|
||||
BigDecimal v = parse(raw);
|
||||
return v == null ? Money.ZERO : v;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.kaidi.oa.common;
|
||||
|
||||
/**
|
||||
* Thrown when a requested resource does not exist. Maps to response code 404.
|
||||
*/
|
||||
public class NotFoundException extends ApiException {
|
||||
|
||||
public NotFoundException(String message) {
|
||||
super(404, message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.kaidi.oa.common;
|
||||
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.security.NoSuchAlgorithmException;
|
||||
import java.security.SecureRandom;
|
||||
import java.security.spec.InvalidKeySpecException;
|
||||
import java.util.Base64;
|
||||
import javax.crypto.SecretKeyFactory;
|
||||
import javax.crypto.spec.PBEKeySpec;
|
||||
|
||||
/**
|
||||
* Password hashing helper — salted PBKDF2-HMAC-SHA256 (JDK built-in, no extra
|
||||
* dependency). Each hash embeds a random per-user salt and an iteration count,
|
||||
* stored as {@code pbkdf2$<iterations>$<saltB64>$<hashB64>}. Verification is
|
||||
* constant-time. This defeats rainbow-table / precomputation attacks that plain
|
||||
* unsalted SHA-256 is vulnerable to.
|
||||
*
|
||||
* Legacy unsalted SHA-256 hex hashes are still accepted by {@link #matches} for
|
||||
* backward compatibility, so old records keep working until re-hashed.
|
||||
*/
|
||||
public final class PasswordUtil {
|
||||
|
||||
private static final int ITERATIONS = 120_000;
|
||||
private static final int KEY_LENGTH = 256; // bits
|
||||
private static final int SALT_BYTES = 16;
|
||||
/** 口令长度上限:防超长口令触发 PBKDF2 CPU 放大(DoS)。正常口令远短于此。 */
|
||||
private static final int MAX_PASSWORD_LEN = 200;
|
||||
private static final SecureRandom RANDOM = new SecureRandom();
|
||||
|
||||
private PasswordUtil() {
|
||||
}
|
||||
|
||||
/** Produce a salted PBKDF2 hash string: {@code pbkdf2$<iter>$<saltB64>$<hashB64>}. */
|
||||
public static String hash(String raw) {
|
||||
if (raw != null && raw.length() > MAX_PASSWORD_LEN) {
|
||||
throw new IllegalArgumentException("password too long");
|
||||
}
|
||||
byte[] salt = new byte[SALT_BYTES];
|
||||
RANDOM.nextBytes(salt);
|
||||
byte[] dk = pbkdf2(raw, salt, ITERATIONS);
|
||||
return "pbkdf2$" + ITERATIONS + "$"
|
||||
+ Base64.getEncoder().encodeToString(salt) + "$"
|
||||
+ Base64.getEncoder().encodeToString(dk);
|
||||
}
|
||||
|
||||
/** Verify a raw password against a stored hash (PBKDF2 or legacy SHA-256). */
|
||||
public static boolean matches(String raw, String stored) {
|
||||
if (raw == null || stored == null || stored.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
// 超长口令直接判否:既挡 PBKDF2 DoS,又不影响任何正常口令。
|
||||
if (raw.length() > MAX_PASSWORD_LEN) {
|
||||
return false;
|
||||
}
|
||||
if (stored.startsWith("pbkdf2$")) {
|
||||
String[] parts = stored.split("\\$");
|
||||
if (parts.length != 4) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
int iterations = Integer.parseInt(parts[1]);
|
||||
byte[] salt = Base64.getDecoder().decode(parts[2]);
|
||||
byte[] expected = Base64.getDecoder().decode(parts[3]);
|
||||
byte[] actual = pbkdf2(raw, salt, iterations);
|
||||
return MessageDigest.isEqual(expected, actual);
|
||||
} catch (RuntimeException e) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// legacy unsalted SHA-256 hex fallback (constant-time compare)
|
||||
return MessageDigest.isEqual(
|
||||
stored.getBytes(StandardCharsets.UTF_8),
|
||||
legacySha256Hex(raw).getBytes(StandardCharsets.UTF_8));
|
||||
}
|
||||
|
||||
private static byte[] pbkdf2(String raw, byte[] salt, int iterations) {
|
||||
try {
|
||||
PBEKeySpec spec = new PBEKeySpec(raw.toCharArray(), salt, iterations, KEY_LENGTH);
|
||||
SecretKeyFactory skf = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA256");
|
||||
return skf.generateSecret(spec).getEncoded();
|
||||
} catch (NoSuchAlgorithmException | InvalidKeySpecException e) {
|
||||
throw new IllegalStateException("PBKDF2 not available", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static String legacySha256Hex(String raw) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
byte[] digest = md.digest(raw.getBytes(StandardCharsets.UTF_8));
|
||||
StringBuilder sb = new StringBuilder(digest.length * 2);
|
||||
for (byte b : digest) {
|
||||
sb.append(Character.forDigit((b >> 4) & 0xF, 16));
|
||||
sb.append(Character.forDigit(b & 0xF, 16));
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (NoSuchAlgorithmException e) {
|
||||
throw new IllegalStateException("SHA-256 not available", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.kaidi.oa.config;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* 统一 AI 服务层配置(oa.ai.*)。绑定 application.yml 的 oa.ai 段。
|
||||
*
|
||||
* apiKey 解析优先级:application.yml 的 oa.ai.api-key 为空时回退到环境变量
|
||||
* ANTHROPIC_API_KEY(见 {@link #resolvedApiKey()})。未配置 Key 时
|
||||
* {@link AiService} 走优雅降级(不报错、不崩库),返回 configured=false 的提示。
|
||||
*
|
||||
* model 默认 claude-haiku-4-5-20251001(高性价比);baseUrl 默认 Anthropic
|
||||
* Messages API;maxTokens 默认 1024;anthropicVersion 默认 2023-06-01。
|
||||
*/
|
||||
@Component
|
||||
@ConfigurationProperties(prefix = "oa.ai")
|
||||
public class AiProperties {
|
||||
|
||||
/** Anthropic API Key;为空则回退环境变量 ANTHROPIC_API_KEY,仍为空则降级。 */
|
||||
private String apiKey = "";
|
||||
|
||||
/** 调用的模型,默认高性价比的 Haiku。 */
|
||||
private String model = "claude-haiku-4-5-20251001";
|
||||
|
||||
/** Anthropic Messages API 端点。 */
|
||||
private String baseUrl = "https://api.anthropic.com/v1/messages";
|
||||
|
||||
/** anthropic-version 请求头。 */
|
||||
private String anthropicVersion = "2023-06-01";
|
||||
|
||||
/** 单次最大输出 token。 */
|
||||
private int maxTokens = 1024;
|
||||
|
||||
/** 请求超时(毫秒)。 */
|
||||
private int timeoutMs = 30000;
|
||||
|
||||
/**
|
||||
* 实际生效的 Key:oa.ai.api-key 优先,空白则回退环境变量 ANTHROPIC_API_KEY。
|
||||
* 两者都空返回空串(调用方据此走降级,不抛异常)。
|
||||
*/
|
||||
public String resolvedApiKey() {
|
||||
if (apiKey != null && !apiKey.isBlank()) {
|
||||
return apiKey.trim();
|
||||
}
|
||||
String env = System.getenv("ANTHROPIC_API_KEY");
|
||||
return env == null ? "" : env.trim();
|
||||
}
|
||||
|
||||
/** 是否已配置可用的 API Key(决定真调 LLM 还是降级)。 */
|
||||
public boolean isConfigured() {
|
||||
return !resolvedApiKey().isBlank();
|
||||
}
|
||||
|
||||
public String getApiKey() {
|
||||
return apiKey;
|
||||
}
|
||||
|
||||
public void setApiKey(String apiKey) {
|
||||
this.apiKey = apiKey;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public String getBaseUrl() {
|
||||
return baseUrl;
|
||||
}
|
||||
|
||||
public void setBaseUrl(String baseUrl) {
|
||||
this.baseUrl = baseUrl;
|
||||
}
|
||||
|
||||
public String getAnthropicVersion() {
|
||||
return anthropicVersion;
|
||||
}
|
||||
|
||||
public void setAnthropicVersion(String anthropicVersion) {
|
||||
this.anthropicVersion = anthropicVersion;
|
||||
}
|
||||
|
||||
public int getMaxTokens() {
|
||||
return maxTokens;
|
||||
}
|
||||
|
||||
public void setMaxTokens(int maxTokens) {
|
||||
this.maxTokens = maxTokens;
|
||||
}
|
||||
|
||||
public int getTimeoutMs() {
|
||||
return timeoutMs;
|
||||
}
|
||||
|
||||
public void setTimeoutMs(int timeoutMs) {
|
||||
this.timeoutMs = timeoutMs;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,255 @@
|
||||
package com.kaidi.oa.config;
|
||||
|
||||
import com.kaidi.oa.domain.OperationAuditLog;
|
||||
import com.kaidi.oa.domain.SysUser;
|
||||
import com.kaidi.oa.repository.OperationAuditLogRepository;
|
||||
import com.kaidi.oa.service.AuthorizationService;
|
||||
import com.kaidi.oa.web.CurrentUserResolver;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.security.MessageDigest;
|
||||
import java.time.Instant;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
import java.util.TreeSet;
|
||||
|
||||
/**
|
||||
* 统一操作审计拦截器(P3 贯穿性合规基础设施)。
|
||||
*
|
||||
* 在 {@code afterCompletion}(请求处理彻底结束、响应码已定)时,对一切对 {@code /api/oa/**}
|
||||
* 的「写请求」(POST/PUT/PATCH/DELETE) 落一条 {@link OperationAuditLog}:
|
||||
* 谁(operator/operatorId/operatorRole)、何时(occurredAt)、用什么方法(method)打了哪个路径
|
||||
* (path/resourceType/resourceId)、结果如何(statusCode)、从哪个 IP(clientIp)发起。
|
||||
*
|
||||
* <h3>不可篡改哈希链</h3>
|
||||
* 落库前计算 {@code hash = sha256(prevHash + 本条规范化字段)},{@code prevHash} 取链上「上一条」
|
||||
* 记录的 hash。续链是 check-then-act(读上一条 → 算 hash → 落库),必须串行化,否则并发写会让
|
||||
* 两条记录读到同一个 prevHash 而分叉。这里用单一锁对「读上一条 + 落库」整段加锁保证链的全序。
|
||||
*
|
||||
* <h3>健壮性铁律</h3>
|
||||
* 审计是旁路:本拦截器任何异常都被吞并仅记日志,<b>绝不</b>影响用户本次业务请求的结果
|
||||
* (写已在 afterCompletion 之前完成提交,落审计失败不应回滚业务,也不应改变已返回的响应)。
|
||||
*/
|
||||
@Component
|
||||
public class AuditLogInterceptor implements HandlerInterceptor {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(AuditLogInterceptor.class);
|
||||
|
||||
/** 仅这些方法触发审计落库(读请求 GET/HEAD/OPTIONS 不留痕,避免日志爆量)。 */
|
||||
private static final Set<String> WRITE_METHODS = Set.of("POST", "PUT", "PATCH", "DELETE");
|
||||
|
||||
/** 哈希链创世值(首条记录的 prevHash),固定常量便于离线复算从头校验。 */
|
||||
public static final String GENESIS_HASH = "0".repeat(64);
|
||||
|
||||
private static final String ANON = "匿名/未登录";
|
||||
|
||||
private final OperationAuditLogRepository auditRepo;
|
||||
private final CurrentUserResolver currentUser;
|
||||
private final AuthorizationService authz;
|
||||
|
||||
/** 续链锁:序列化「取上一条 hash + 落新记录」,保证哈希链单一全序、不分叉。 */
|
||||
private final Object chainLock = new Object();
|
||||
|
||||
public AuditLogInterceptor(OperationAuditLogRepository auditRepo,
|
||||
CurrentUserResolver currentUser,
|
||||
AuthorizationService authz) {
|
||||
this.auditRepo = auditRepo;
|
||||
this.currentUser = currentUser;
|
||||
this.authz = authz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
|
||||
Object handler, Exception ex) {
|
||||
try {
|
||||
String method = request.getMethod() == null ? "" : request.getMethod().toUpperCase(Locale.ROOT);
|
||||
if (!WRITE_METHODS.contains(method)) {
|
||||
return;
|
||||
}
|
||||
String path = normalizePath(request.getRequestURI());
|
||||
if (!path.startsWith("/api/oa/")) {
|
||||
return;
|
||||
}
|
||||
recordWrite(method, path, response.getStatus(), resolveOperator(request), clientIp(request));
|
||||
} catch (RuntimeException e) {
|
||||
// 审计旁路:绝不让落审计的失败影响用户本次业务请求(业务写已提交、响应已返回)。
|
||||
log.warn("操作审计落库失败(已忽略,不影响业务): {} {}", request.getMethod(), request.getRequestURI(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/** 解析操作人(显示名 / 不可变 id / 角色快照);未登录回退匿名占位。 */
|
||||
private OperatorInfo resolveOperator(HttpServletRequest request) {
|
||||
try {
|
||||
SysUser u = currentUser.resolve(request);
|
||||
if (u == null) {
|
||||
return new OperatorInfo(ANON, null, "");
|
||||
}
|
||||
String name = u.getDisplayName() != null && !u.getDisplayName().isBlank()
|
||||
? u.getDisplayName() : u.getLoginName();
|
||||
// 角色集合排序后逗号拼接,得到稳定快照(参与哈希,需可复现)。
|
||||
Set<String> roles = new TreeSet<>(authz.roleCodesOf(u.getId()));
|
||||
return new OperatorInfo(name, u.getId(), String.join(",", roles));
|
||||
} catch (RuntimeException e) {
|
||||
return new OperatorInfo(ANON, null, "");
|
||||
}
|
||||
}
|
||||
|
||||
private record OperatorInfo(String name, Long id, String roles) {
|
||||
}
|
||||
|
||||
/**
|
||||
* 落一条审计并续哈希链。整段在 {@link #chainLock} 下串行:先取链上最后一条的 hash 作 prevHash,
|
||||
* 再算本条 hash,最后落库——保证并发写不会读到同一 prevHash 而分叉。
|
||||
*/
|
||||
private void recordWrite(String method, String path, int statusCode,
|
||||
OperatorInfo op, String clientIp) {
|
||||
String[] res = parseResource(path);
|
||||
Instant now = Instant.now();
|
||||
|
||||
synchronized (chainLock) {
|
||||
OperationAuditLog last = auditRepo.findTopByOrderByIdDesc();
|
||||
String prevHash = last == null ? GENESIS_HASH : last.getHash();
|
||||
|
||||
OperationAuditLog rec = new OperationAuditLog();
|
||||
rec.setOperator(op.name());
|
||||
rec.setOperatorId(op.id());
|
||||
rec.setOperatorRole(op.roles());
|
||||
rec.setMethod(method);
|
||||
rec.setPath(path);
|
||||
rec.setResourceType(res[0]);
|
||||
rec.setResourceId(res[1]);
|
||||
rec.setStatusCode(statusCode);
|
||||
rec.setClientIp(clientIp);
|
||||
rec.setOccurredAt(now);
|
||||
rec.setPrevHash(prevHash);
|
||||
|
||||
String canonical = canonicalOf(prevHash, rec);
|
||||
rec.setCanonical(canonical);
|
||||
rec.setHash(sha256Hex(canonical));
|
||||
|
||||
auditRepo.save(rec);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 规范化参与哈希的字段串:{@code prevHash | operatorId | operator | role | method | path |
|
||||
* resourceType | resourceId | statusCode | clientIp | occurredAtEpochMilli}。
|
||||
* 字段顺序与分隔符固定,便于离线逐条复算校验链完整性。
|
||||
*/
|
||||
public static String canonicalOf(String prevHash, OperationAuditLog r) {
|
||||
return String.join("|",
|
||||
nz(prevHash),
|
||||
r.getOperatorId() == null ? "" : String.valueOf(r.getOperatorId()),
|
||||
nz(r.getOperator()),
|
||||
nz(r.getOperatorRole()),
|
||||
nz(r.getMethod()),
|
||||
nz(r.getPath()),
|
||||
nz(r.getResourceType()),
|
||||
nz(r.getResourceId()),
|
||||
r.getStatusCode() == null ? "" : String.valueOf(r.getStatusCode()),
|
||||
nz(r.getClientIp()),
|
||||
r.getOccurredAt() == null ? "" : String.valueOf(r.getOccurredAt().toEpochMilli()));
|
||||
}
|
||||
|
||||
/** sha256 → 64 位小写十六进制。 */
|
||||
public static String sha256Hex(String s) {
|
||||
try {
|
||||
MessageDigest md = MessageDigest.getInstance("SHA-256");
|
||||
byte[] digest = md.digest(s.getBytes(StandardCharsets.UTF_8));
|
||||
StringBuilder sb = new StringBuilder(64);
|
||||
for (byte b : digest) {
|
||||
sb.append(Character.forDigit((b >> 4) & 0xF, 16));
|
||||
sb.append(Character.forDigit(b & 0xF, 16));
|
||||
}
|
||||
return sb.toString();
|
||||
} catch (Exception e) {
|
||||
// SHA-256 是 JDK 必备算法,理论不会缺;缺失时退化为不可比对的空哈希但仍记录。
|
||||
log.error("SHA-256 不可用,审计哈希降级为空", e);
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从规范化路径解析「资源类型 + 资源 id」。
|
||||
* {@code /api/oa/financings} -> [financings, null];
|
||||
* {@code /api/oa/financings/12} -> [financings, 12];
|
||||
* {@code /api/oa/financings/repayments/5/pay} -> [financings, null](子动作,资源段后非纯数字)。
|
||||
*/
|
||||
static String[] parseResource(String path) {
|
||||
// 去掉前缀 /api/oa/
|
||||
String rest = path.startsWith("/api/oa/") ? path.substring("/api/oa/".length()) : path;
|
||||
if (rest.isEmpty()) {
|
||||
return new String[]{"", null};
|
||||
}
|
||||
String[] seg = rest.split("/");
|
||||
String type = seg.length > 0 ? seg[0] : "";
|
||||
// 第二段若是纯数字/uuid 形态的标识,记为 resourceId;否则视为子路径,不记 id。
|
||||
String id = null;
|
||||
if (seg.length > 1 && isIdLike(seg[1])) {
|
||||
id = seg[1];
|
||||
}
|
||||
return new String[]{type, id};
|
||||
}
|
||||
|
||||
private static boolean isIdLike(String s) {
|
||||
if (s == null || s.isEmpty() || s.length() > 64) {
|
||||
return false;
|
||||
}
|
||||
boolean hasDigit = false;
|
||||
for (int i = 0; i < s.length(); i++) {
|
||||
char c = s.charAt(i);
|
||||
boolean ok = (c >= '0' && c <= '9') || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F') || c == '-';
|
||||
if (!ok) {
|
||||
return false;
|
||||
}
|
||||
if (c >= '0' && c <= '9') {
|
||||
hasDigit = true;
|
||||
}
|
||||
}
|
||||
return hasDigit; // 至少含一位数字,避免把 "alerts"/"cost" 等纯单词误判成 id
|
||||
}
|
||||
|
||||
/** 取发起方 IP:优先 X-Forwarded-For 首段(隧道/代理后),否则 remoteAddr。 */
|
||||
private static String clientIp(HttpServletRequest request) {
|
||||
String xff = request.getHeader("X-Forwarded-For");
|
||||
if (xff != null && !xff.isBlank()) {
|
||||
int comma = xff.indexOf(',');
|
||||
return (comma > 0 ? xff.substring(0, comma) : xff).trim();
|
||||
}
|
||||
String ip = request.getRemoteAddr();
|
||||
return ip == null ? "" : ip;
|
||||
}
|
||||
|
||||
/**
|
||||
* 与 AuthInterceptor 同口径的路径规范化:URL 解码、剥矩阵参数、折叠重复斜杠、去尾斜杠、小写。
|
||||
* 保证审计记录的 path 与授权判定看到的 path 一致,便于对账「越权尝试」。
|
||||
*/
|
||||
static String normalizePath(String uri) {
|
||||
if (uri == null) {
|
||||
return "";
|
||||
}
|
||||
String p = uri;
|
||||
try {
|
||||
p = URLDecoder.decode(p, StandardCharsets.UTF_8);
|
||||
} catch (RuntimeException ignore) {
|
||||
// 非法转义序列:保留原串继续规范化。
|
||||
}
|
||||
p = p.replaceAll(";[^/]*", "");
|
||||
p = p.replaceAll("/{2,}", "/");
|
||||
if (p.length() > 1 && p.endsWith("/")) {
|
||||
p = p.substring(0, p.length() - 1);
|
||||
}
|
||||
return p.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private static String nz(String s) {
|
||||
return s == null ? "" : s;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,535 @@
|
||||
package com.kaidi.oa.config;
|
||||
|
||||
import com.kaidi.oa.domain.SysUser;
|
||||
import com.kaidi.oa.service.AuthorizationService;
|
||||
import com.kaidi.oa.web.CurrentUserResolver;
|
||||
import jakarta.servlet.http.HttpServletRequest;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.MediaType;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import java.net.URLDecoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Requires a valid auth token for every /api/oa/** endpoint except the public
|
||||
* allowlist (login / session-check / logout). Unauthenticated calls receive a
|
||||
* 401 in the standard ApiResp shape. This closes the "all endpoints open" hole
|
||||
* (H10) and is the prerequisite for per-user behaviour (待办 by assignee, etc.).
|
||||
*
|
||||
* 在认证之上加一层最小授权:对少数敏感写接口(用户管理 / 资金支付 / 用印)按角色门槛
|
||||
* 放行,堵住"任意登录用户都能删合同、改任意单据、增删员工"的水平越权面(仅认证不授权)。
|
||||
* 读接口(GET)一律不限制;门槛只针对增删改(POST/PUT/PATCH/DELETE)。
|
||||
*/
|
||||
@Component
|
||||
public class AuthInterceptor implements HandlerInterceptor {
|
||||
|
||||
private final CurrentUserResolver currentUser;
|
||||
private final AuthorizationService authz;
|
||||
|
||||
/** Endpoints that must work without a token. */
|
||||
private static final Set<String> PUBLIC = Set.of(
|
||||
"/api/oa/auth/login",
|
||||
"/api/oa/auth/session",
|
||||
"/api/oa/auth/logout"
|
||||
);
|
||||
|
||||
/** 写操作方法(只有这些方法才触发授权门槛;GET/HEAD 等读操作不限制)。 */
|
||||
private static final Set<String> WRITE_METHODS = Set.of("POST", "PUT", "PATCH", "DELETE");
|
||||
|
||||
private static final Set<String> ADMIN = Set.of("ADMIN");
|
||||
private static final Set<String> APPROVER_OR_ADMIN = Set.of("ADMIN", "APPROVER");
|
||||
|
||||
/**
|
||||
* 系统 / 组织 / 权限 / 配置 类写接口 → 仅 ADMIN。
|
||||
* 改这些会动用户、组织树、角色、全局配置、审批委托、各类模板——管理员职责。
|
||||
*/
|
||||
private static final List<String> ADMIN_PREFIXES = List.of(
|
||||
"/api/oa/users", "/api/oa/depts", "/api/oa/roles", "/api/oa/settings",
|
||||
"/api/oa/delegations", "/api/oa/form-templates", "/api/oa/declaration-templates",
|
||||
"/api/oa/contract-templates", "/api/oa/report-definitions", "/api/oa/crawl-sources",
|
||||
// 全文索引重建(POST /search/reindex)是全库重活,仅 ADMIN 可触发(杜绝任意角色发起整库扫描)。
|
||||
"/api/oa/search",
|
||||
// 内部审计项目(问题数/结论)属高完整性记录,写口仅 ADMIN,杜绝被改成"无问题"掩盖违规。
|
||||
"/api/oa/audit-projects"
|
||||
);
|
||||
|
||||
/**
|
||||
* 财务 / 合同 / 资金 / 主数据 类写接口 → ADMIN 或 APPROVER。
|
||||
* 凡能改钱、改合同(含 contract-changes / -milestones 这些会回写合同的旁路)、改账户、
|
||||
* 改预算/成本/科目、改客商主档、改补贴申报的写口,统一收口到审批人/管理员,杜绝 BFLA 旁路。
|
||||
*/
|
||||
private static final List<String> FINANCE_PREFIXES = List.of(
|
||||
"/api/oa/contracts", "/api/oa/contract-changes", "/api/oa/contract-milestones",
|
||||
"/api/oa/payments", "/api/oa/invoices", "/api/oa/vouchers", "/api/oa/accounts",
|
||||
"/api/oa/bank-accounts", "/api/oa/fund-pool", "/api/oa/seals", "/api/oa/seal-uses",
|
||||
"/api/oa/bids", "/api/oa/budgets", "/api/oa/cost-centers", "/api/oa/standard-costs",
|
||||
"/api/oa/rd-expenses", "/api/oa/company-subjects", "/api/oa/price-items",
|
||||
"/api/oa/bom-items", "/api/oa/customers", "/api/oa/suppliers", "/api/oa/policies",
|
||||
"/api/oa/policy-applications", "/api/oa/declarations", "/api/oa/declaration-steps",
|
||||
// HR / 个人隐私写口(职业健康、人员证书)也收口到 ADMIN/APPROVER,杜绝任意 USER 伪造/篡改。
|
||||
"/api/oa/health-records", "/api/oa/personnel-certs",
|
||||
// 主数据/资产写口补全(穷尽审查发现这些写口此前漏收,任意有角色 USER 可增删改):
|
||||
// 库存物料、排污客户、专利(知识产权)登记,均收口到 ADMIN/APPROVER,与 customers/suppliers/bom-items 一致。
|
||||
"/api/oa/inventory", "/api/oa/wastewater-clients", "/api/oa/patents",
|
||||
// 融资域(金融办):融资台账/还本付息/还款生成付款单,写口含金额,收口 ADMIN/APPROVER。
|
||||
"/api/oa/financings",
|
||||
// P0 整域补建(监理计量/结算复核/银行对账/资金计划,写口含金额或资金口径)。
|
||||
"/api/oa/supervision-projects", "/api/oa/measurement-payments",
|
||||
"/api/oa/settlements", "/api/oa/reconciliations", "/api/oa/fund-plans",
|
||||
// P1 财务内核(税务/固定资产/应收应付/费用报销/领料含金额)+ 机构目录(主数据写)。
|
||||
"/api/oa/tax-filings", "/api/oa/fixed-assets", "/api/oa/ar-ap-items",
|
||||
"/api/oa/expense-claims", "/api/oa/material-issues", "/api/oa/org-branches",
|
||||
// P3 外部对接框架:对接系统配置(endpoint/凭证)与同步执行触发,写口收口 ADMIN/APPROVER。
|
||||
"/api/oa/external-systems", "/api/oa/integrations", "/api/oa/rule-configs",
|
||||
// 长尾全建W1:23机构新增含金额/资金写口,统一收口 ADMIN/APPROVER。
|
||||
"/api/oa/admin-dorms", "/api/oa/admin-requisitions", "/api/oa/admin-supplies", "/api/oa/admin-vehicles",
|
||||
"/api/oa/archive-borrows", "/api/oa/archive-categories", "/api/oa/archive-submissions", "/api/oa/audit-findings",
|
||||
"/api/oa/bid-bonds", "/api/oa/bid-deposits", "/api/oa/biz-budgets", "/api/oa/biz-plans",
|
||||
"/api/oa/branch-assets", "/api/oa/branch-budget-lines", "/api/oa/brand-events", "/api/oa/brand-plans",
|
||||
"/api/oa/brand-projects", "/api/oa/contract-budget-reviews", "/api/oa/csl-projects", "/api/oa/csl-reports",
|
||||
"/api/oa/csl-timesheets", "/api/oa/culture-awards", "/api/oa/culture-schemes", "/api/oa/culture-stories",
|
||||
"/api/oa/customer-complaints", "/api/oa/customer-credits", "/api/oa/customer-visits", "/api/oa/design-change-orders",
|
||||
"/api/oa/design-projects", "/api/oa/design-tasks", "/api/oa/dev-dashboard", "/api/oa/dev-project-budgets",
|
||||
"/api/oa/dev-projects", "/api/oa/discharge-bills", "/api/oa/discharge-contracts", "/api/oa/ehs-incidents",
|
||||
"/api/oa/feedstock-batches", "/api/oa/fert-recipes", "/api/oa/food-batches", "/api/oa/fraud-reports",
|
||||
"/api/oa/hazwaste-manifests", "/api/oa/ip-assets", "/api/oa/it-budgets", "/api/oa/local-resources",
|
||||
"/api/oa/logistics-budgets", "/api/oa/logistics-dashboard", "/api/oa/maint-orders", "/api/oa/market-budgets",
|
||||
"/api/oa/market-campaigns", "/api/oa/market-expenses", "/api/oa/market-intels", "/api/oa/mfg-project-costs",
|
||||
"/api/oa/monitor-exceptions", "/api/oa/monitor-rules", "/api/oa/ops-maint", "/api/oa/production-reports",
|
||||
"/api/oa/project-budget-lines", "/api/oa/prototype-orders", "/api/oa/qual-certs", "/api/oa/qual-declarations",
|
||||
"/api/oa/qualifications", "/api/oa/rd-grant-disbursements", "/api/oa/rd-service-agencies", "/api/oa/rectifications",
|
||||
"/api/oa/service-tickets", "/api/oa/sewage-energy-logs", "/api/oa/sewage-equipments", "/api/oa/sewage-process-runs",
|
||||
"/api/oa/sewage-sludge-records", "/api/oa/software-licenses", "/api/oa/staff-credentials", "/api/oa/staff-dossiers",
|
||||
"/api/oa/tech-achievements", "/api/oa/water-exceed-events", "/api/oa/work-method-rewards", "/api/oa/work-methods",
|
||||
// 市场部/经营部补完缺口新控制器写口:投标报价明细(含金额)/客户联系人PII子表/商机阶段/经营绩效考核,收口 ADMIN/APPROVER。
|
||||
"/api/oa/crm-bid-quote-details", "/api/oa/crm-customer-contacts",
|
||||
"/api/oa/crm-opp-stages", "/api/oa/crm-performance",
|
||||
// 申报服务部补完缺口新控制器写口:审计报告(含财务数据)/申报项目参与台账,收口 ADMIN/APPROVER。
|
||||
"/api/oa/decl-audit-reports", "/api/oa/decl-project-assignments",
|
||||
// 办事处请示报告(含涉及金额/多节点上报审批链),写口收口 ADMIN/APPROVER。
|
||||
"/api/oa/crm-branch-petitions",
|
||||
// 审计监察部新缺口补完写口:审计报告/证据/风险有效性评估/舞弊预警规则/自动归档,收口 ADMIN/APPROVER。
|
||||
"/api/oa/audit-reports", "/api/oa/audit-evidence",
|
||||
"/api/oa/audit-risk-assessments", "/api/oa/fraud-alert-rules", "/api/oa/audit-auto-archive",
|
||||
// 支付中心/财务部缺口补完写口:科目辅助核算/收入确认/BOM-成本集成/采购预算检查/财务岗位权限矩阵,收口 ADMIN/APPROVER。
|
||||
"/api/oa/fin-account-aux", "/api/oa/fin-rev-recognitions",
|
||||
"/api/oa/fin-bom-cost", "/api/oa/fin-procure-budget-check", "/api/oa/fin-account-perms"
|
||||
);
|
||||
|
||||
/**
|
||||
* 机密财务 / 个人隐私 类**读**接口 → 需 ADMIN/APPROVER。读侧此前对任意登录用户全开,
|
||||
* 导致银行账户/付款/凭证/资金池/职业健康等机密数据被任意账号横向读取——这里对最敏感的
|
||||
* 读口补一层角色门槛(其余一般业务读维持登录可读,避免误伤前端大量列表)。
|
||||
*/
|
||||
private static final List<String> SENSITIVE_READ_PREFIXES = List.of(
|
||||
"/api/oa/accounts", "/api/oa/bank-accounts", "/api/oa/payments", "/api/oa/vouchers",
|
||||
"/api/oa/fund-pool", "/api/oa/health-records", "/api/oa/personnel-certs",
|
||||
// BI / 数据中心 / 自定义报表 / 预警 聚合读会把付款/资金/合同/证件PII 等汇总端出来,
|
||||
// 是敏感读门槛的聚合旁路(基路径非敏感却读敏感数据),一并收口到 ADMIN/APPROVER。
|
||||
"/api/oa/reports", "/api/oa/datacenter", "/api/oa/report-definitions", "/api/oa/alerts",
|
||||
// 预算 / 成本中心 / 标准成本 / 研发费 含公司财务明细,读侧也收口(写侧已在财务前缀)。
|
||||
"/api/oa/budgets", "/api/oa/cost-centers", "/api/oa/standard-costs", "/api/oa/rd-expenses",
|
||||
// 穷尽审查补收的聚合/机密读旁路:
|
||||
// - search:全文检索把单据正文/合同甲乙方/供应商开户行/统一社会信用代码等明文灌入索引并返回高亮片段,
|
||||
// 整体旁路了对象级读与敏感读门槛,全局检索收口到 ADMIN/APPROVER;
|
||||
// - automation-logs:审批办结后的财务联动审计流水(含金额与付款/合同指向);
|
||||
// - suppliers:列表/详情直接吐供应商开户行+银行账号。
|
||||
"/api/oa/search", "/api/oa/automation-logs", "/api/oa/suppliers",
|
||||
// 财务读端点族(含金额/对账/资金口径)系统性收口:与已门禁的 payments/vouchers/budgets 同源,
|
||||
// 此前从这些未收口的读口整体漏出(红队实证:合同实付/已开票、补贴申请/拨付额、研发预算等)。
|
||||
// 一次性把含金额的业务读全部纳入 ADMIN/APPROVER,封死"敏感财务从旁路读口外泄"的整类。
|
||||
"/api/oa/contracts", "/api/oa/contract-changes", "/api/oa/contract-milestones",
|
||||
"/api/oa/invoices", "/api/oa/bids", "/api/oa/opportunities", "/api/oa/opportunity-followups",
|
||||
"/api/oa/policy-applications", "/api/oa/rd-projects", "/api/oa/price-items",
|
||||
"/api/oa/bom-items", "/api/oa/company-subjects",
|
||||
// 评审专家库含姓名+明文联系电话(PII),收口到 ADMIN/APPROVER,杜绝任意员工导出专家名单定向接触/围标。
|
||||
"/api/oa/experts",
|
||||
// 通用键值配置(settings)写口已收口 ADMIN,读口本对任意 USER 全开(读写不对称)。settings 是 ADMIN 专管
|
||||
// 的配置/台账持久化(将来可能存敏感配置),读侧也收口到 ADMIN/APPROVER,消除不对称。
|
||||
"/api/oa/settings",
|
||||
// 融资域读侧(融资台账/还款计划/到期预警/成本分析含授信额度与利率,属机密财务)。
|
||||
"/api/oa/financings",
|
||||
// P0 整域补建读侧(监理项目/计量支付含金额;结算/对账/资金计划含资金口径;
|
||||
// 法务合规义务/内控矩阵/诉讼仲裁/法律咨询/风险评估含敏感法律信息;实验室检测任务/报告含检测结论)。
|
||||
"/api/oa/supervision-projects", "/api/oa/measurement-payments",
|
||||
"/api/oa/settlements", "/api/oa/reconciliations", "/api/oa/fund-plans",
|
||||
"/api/oa/compliance-obligations", "/api/oa/control-matrices", "/api/oa/litigation-cases",
|
||||
"/api/oa/legal-consults", "/api/oa/risk-assessments",
|
||||
"/api/oa/test-reports", "/api/oa/test-tasks",
|
||||
// P1 财务内核读侧(税额/资产原值净值/往来金额/报销金额/领料成本,属机密财务)+ MRP/条款比对(读库存/合同条款)。
|
||||
"/api/oa/tax-filings", "/api/oa/fixed-assets", "/api/oa/ar-ap-items",
|
||||
"/api/oa/expense-claims", "/api/oa/material-issues", "/api/oa/mrp", "/api/oa/clause-compare",
|
||||
// P2 跨模块自动取数/看板读侧:申报条件自查(研发费用/科技人员PII/知识产权聚合)、研发费用证据链、
|
||||
// 财务多维看板(合同额/付款/应收应付/凭证聚合)——属机密财务+PII 聚合读,收口 ADMIN/APPROVER。
|
||||
"/api/oa/eligibility-checks", "/api/oa/rd-expense-traces", "/api/oa/finance-dashboard",
|
||||
// P3 贯穿性读侧:操作审计台账(操作人/资源指向/IP)、外部对接配置(endpoint/凭证)、可配置联动规则——属机密配置/审计读。
|
||||
"/api/oa/operation-audit-logs", "/api/oa/external-systems", "/api/oa/rule-configs",
|
||||
// 长尾全建W1:23机构新增含金额/机密/PII 聚合读口,统一收口 ADMIN/APPROVER。
|
||||
"/api/oa/admin-dorms", "/api/oa/admin-requisitions", "/api/oa/admin-supplies", "/api/oa/admin-vehicles",
|
||||
"/api/oa/admin-visitors", "/api/oa/archive-board", "/api/oa/archive-borrows", "/api/oa/archive-categories",
|
||||
"/api/oa/archive-submissions", "/api/oa/audit-dashboard", "/api/oa/audit-findings", "/api/oa/bid-bonds",
|
||||
"/api/oa/bid-decisions", "/api/oa/bid-deposits", "/api/oa/biz-budgets", "/api/oa/biz-plans",
|
||||
"/api/oa/branch-budget-lines", "/api/oa/branch-pnl", "/api/oa/brand-dashboard", "/api/oa/brand-events",
|
||||
"/api/oa/brand-plans", "/api/oa/brand-projects", "/api/oa/contract-budget-reviews", "/api/oa/cost-audit",
|
||||
"/api/oa/csl-dashboard", "/api/oa/csl-projects", "/api/oa/csl-reports", "/api/oa/csl-timesheets",
|
||||
"/api/oa/culture-assessment", "/api/oa/culture-awards", "/api/oa/culture-schemes", "/api/oa/culture-stories",
|
||||
"/api/oa/customer-credits", "/api/oa/declaration-dashboard", "/api/oa/design-change-orders", "/api/oa/design-projects",
|
||||
"/api/oa/design-reviews", "/api/oa/design-tasks", "/api/oa/dev-dashboard", "/api/oa/dev-project-budgets",
|
||||
"/api/oa/dev-projects", "/api/oa/discharge-bills", "/api/oa/discharge-contracts", "/api/oa/ehs-board",
|
||||
"/api/oa/ehs-hazards", "/api/oa/ehs-incidents", "/api/oa/engineering-changes", "/api/oa/feedstock-batches",
|
||||
"/api/oa/ferment-batches", "/api/oa/fert-recipes", "/api/oa/food-batches", "/api/oa/fraud-reports",
|
||||
"/api/oa/hazwaste-manifests", "/api/oa/ip-assets", "/api/oa/ip-insights", "/api/oa/it-budgets",
|
||||
"/api/oa/it-tickets", "/api/oa/logistics-budgets", "/api/oa/logistics-dashboard", "/api/oa/maint-orders",
|
||||
"/api/oa/market-budgets", "/api/oa/market-expenses", "/api/oa/market-intels", "/api/oa/mfg-project-costs",
|
||||
"/api/oa/monitor-exceptions", "/api/oa/monitor-rules", "/api/oa/ops-cost-allocation", "/api/oa/ops-maint",
|
||||
"/api/oa/product-requirements", "/api/oa/production-reports", "/api/oa/project-budget-lines", "/api/oa/prototype-orders",
|
||||
"/api/oa/qc-inspections", "/api/oa/qual-certs", "/api/oa/qual-declarations", "/api/oa/qualifications",
|
||||
"/api/oa/rd-grant-disbursements", "/api/oa/rd-service-agencies", "/api/oa/rectifications", "/api/oa/service-tickets",
|
||||
"/api/oa/sewage-energy-logs", "/api/oa/sewage-equipments", "/api/oa/sewage-process-runs", "/api/oa/sewage-sludge-records",
|
||||
"/api/oa/software-licenses", "/api/oa/staff-credentials", "/api/oa/staff-dossiers", "/api/oa/tech-achievements",
|
||||
"/api/oa/water-exceed-events", "/api/oa/work-method-dashboard", "/api/oa/work-method-rewards", "/api/oa/work-methods",
|
||||
// W2 长尾新控制器读口统一收口 ADMIN/APPROVER(演示下admin可读全, 写口已default-deny)。
|
||||
"/api/oa/announcements", "/api/oa/appdev", "/api/oa/archive-comments", "/api/oa/archive-physical-items",
|
||||
"/api/oa/archive-vaults", "/api/oa/archives", "/api/oa/auth", "/api/oa/biz",
|
||||
"/api/oa/canteen-menus", "/api/oa/certifications", "/api/oa/chronicle-events", "/api/oa/competitor-ips",
|
||||
"/api/oa/completion-acceptances", "/api/oa/content-pieces", "/api/oa/crawl-jobs", "/api/oa/csl-archives",
|
||||
"/api/oa/csl-experts", "/api/oa/csl-finance", "/api/oa/csl-risks", "/api/oa/csl-tasks",
|
||||
"/api/oa/culture-cases", "/api/oa/culture-surveys", "/api/oa/data-sync-logs", "/api/oa/decl-acceptances",
|
||||
"/api/oa/decl-achievements", "/api/oa/decl-evidence-items", "/api/oa/decl-failure-cases", "/api/oa/decl-grant-ledger",
|
||||
"/api/oa/decl-mobile-queue", "/api/oa/decl-policy-subscriptions", "/api/oa/decl-push-devices", "/api/oa/decl-submissions",
|
||||
"/api/oa/design-audit", "/api/oa/design-changes", "/api/oa/design-clashes", "/api/oa/design-docs",
|
||||
"/api/oa/design-drawings", "/api/oa/design-handoffs", "/api/oa/design-interfaces", "/api/oa/design-knowledge",
|
||||
"/api/oa/designer-capabilities", "/api/oa/documents", "/api/oa/ehs-capas", "/api/oa/ehs-objectives",
|
||||
"/api/oa/ehs-work-permits", "/api/oa/emergency-drills", "/api/oa/fert-energy-logs", "/api/oa/fert-mps",
|
||||
"/api/oa/fert-procure-plans", "/api/oa/fert-qc-standards", "/api/oa/fert-sales-orders", "/api/oa/fert-stock-lots",
|
||||
"/api/oa/folders", "/api/oa/handovers", "/api/oa/hazard-sources", "/api/oa/intel",
|
||||
"/api/oa/ip-budgets", "/api/oa/ip-knowledge", "/api/oa/ip-regulations", "/api/oa/it-assets",
|
||||
"/api/oa/it-knowledge", "/api/oa/it-monitors", "/api/oa/lab-costs", "/api/oa/lab-deviations",
|
||||
"/api/oa/lab-ehs", "/api/oa/lab-elns", "/api/oa/lab-haz-requests", "/api/oa/lab-instrument-maints",
|
||||
"/api/oa/lab-instruments", "/api/oa/lab-internal-audits", "/api/oa/lab-report-archives", "/api/oa/lab-reservations",
|
||||
"/api/oa/lab-sample-flow", "/api/oa/lab-samples", "/api/oa/lab-stocktakes", "/api/oa/leader-trips",
|
||||
"/api/oa/logistics-tickets", "/api/oa/meal-orders", "/api/oa/meeting-action-items", "/api/oa/meeting-rooms",
|
||||
"/api/oa/meetings", "/api/oa/mfg-deliveries", "/api/oa/mfg-drawings", "/api/oa/mfg-eng-boms",
|
||||
"/api/oa/mfg-quotes", "/api/oa/mfg-sales-orders", "/api/oa/minutes", "/api/oa/mobile",
|
||||
"/api/oa/mobile-approvals", "/api/oa/net-scan-tasks", "/api/oa/ops-kpi", "/api/oa/product-certs",
|
||||
"/api/oa/product-roadmaps", "/api/oa/project-tasks", "/api/oa/projects", "/api/oa/prototype-stocks",
|
||||
"/api/oa/qhse-dashboard", "/api/oa/qms-audits", "/api/oa/qms-documents", "/api/oa/qms-records",
|
||||
"/api/oa/rd-eboms", "/api/oa/rd-issues", "/api/oa/rd-maintenance-tasks", "/api/oa/rd-materials",
|
||||
"/api/oa/rd-purchase-requests", "/api/oa/rd-timesheets", "/api/oa/reagents", "/api/oa/safety-checks",
|
||||
"/api/oa/safety-facilities", "/api/oa/safety-investments", "/api/oa/safety-responsibilities", "/api/oa/safety-trainings",
|
||||
"/api/oa/secret-access-logs", "/api/oa/sentiment-records", "/api/oa/std-applications", "/api/oa/supervision-inspections",
|
||||
"/api/oa/supervision-logs", "/api/oa/supplier-quality-lots", "/api/oa/supplier-samples", "/api/oa/surveys",
|
||||
"/api/oa/sv-change-claims", "/api/oa/sv-material-inspects", "/api/oa/sv-monthly-reports", "/api/oa/sv-notices",
|
||||
"/api/oa/sv-progress-plans", "/api/oa/sv-schemes", "/api/oa/tech-contracts", "/api/oa/test-methods",
|
||||
// 工程监理部深化补建:监理规划/细则(版本受控)含监理机密、资质审查含PII/证书编号、开工令/隐蔽工程含工程敏感数据,统一收口。
|
||||
"/api/oa/sv-plans", "/api/oa/sv-qualifications", "/api/oa/sv-open-work-orders", "/api/oa/sv-hidden-works",
|
||||
"/api/oa/water-alert-rules", "/api/oa/water-quality", "/api/oa/work-orders",
|
||||
// W2-sonnet 新控制器读口统一收口。
|
||||
"/api/oa/admin-cleaning-schedules", "/api/oa/admin-dorm-repairs", "/api/oa/admin-express", "/api/oa/admin-greenery",
|
||||
"/api/oa/admin-patrols", "/api/oa/admin-procurement-requests", "/api/oa/admin-receptions", "/api/oa/admin-security-events",
|
||||
"/api/oa/admin-shuttle-buses", "/api/oa/admin-shuttles", "/api/oa/admin-vehicle-violations", "/api/oa/annual-reports",
|
||||
"/api/oa/archive-access-logs", "/api/oa/archive-acls", "/api/oa/archive-destructions", "/api/oa/archive-repair-records",
|
||||
"/api/oa/archive-reservations", "/api/oa/archive-versions", "/api/oa/audit-caats", "/api/oa/audit-notices",
|
||||
"/api/oa/audit-procedures", "/api/oa/audit-work-papers", "/api/oa/bid-loss-cases", "/api/oa/bom-variance-details",
|
||||
"/api/oa/brand-assets", "/api/oa/caats", "/api/oa/canteen-sanitations", "/api/oa/compliance-checklists",
|
||||
"/api/oa/contract-cost-ext", "/api/oa/cost-purchase-orders", "/api/oa/cost-reductions", "/api/oa/crisis-plans",
|
||||
"/api/oa/crm-analysis", "/api/oa/crm-bid-analysis", "/api/oa/crm-bid-analytics", "/api/oa/crm-biz-analytics",
|
||||
"/api/oa/crm-competitors", "/api/oa/crm-contract-approvals", "/api/oa/crm-customer-alerts", "/api/oa/crm-doc-receipts",
|
||||
"/api/oa/crm-petty-cash", "/api/oa/crm-satisfaction", "/api/oa/csl-cross-dept", "/api/oa/csl-report-chapters",
|
||||
"/api/oa/culture-archives", "/api/oa/culture-materials", "/api/oa/doc-receipts", "/api/oa/economic-audits",
|
||||
"/api/oa/env-discharge-permits", "/api/oa/event-checkins", "/api/oa/event-guests", "/api/oa/fert-batch-costs",
|
||||
"/api/oa/fert-gas-emissions", "/api/oa/fert-pack-orders", "/api/oa/fin-bad-debts", "/api/oa/fin-budget-executions",
|
||||
"/api/oa/fin-cash-journals", "/api/oa/fin-statements", "/api/oa/fin-voucher-templates", "/api/oa/hr-dossier-search",
|
||||
"/api/oa/hr-event-archive", "/api/oa/hr-labor-contracts", "/api/oa/imprest-funds", "/api/oa/ip-license-transfers",
|
||||
"/api/oa/it-asset-events", "/api/oa/it-backups", "/api/oa/it-network-devices", "/api/oa/it-sec-events",
|
||||
"/api/oa/it-vuln-patches", "/api/oa/legal-auth-limits", "/api/oa/legal-opinions", "/api/oa/legal-regulations",
|
||||
"/api/oa/litigation-firms", "/api/oa/media-contacts", "/api/oa/mfg-production-anomalies", "/api/oa/mfg-purchase-orders",
|
||||
"/api/oa/mfg-stocktakes", "/api/oa/mfg-warranty-contracts", "/api/oa/occ-hazard-factors", "/api/oa/pmt-bank-relations",
|
||||
"/api/oa/pmt-financing-contracts", "/api/oa/pmt-inbound-receipts", "/api/oa/pmt-internal-trans", "/api/oa/pmt-loan-schemes",
|
||||
"/api/oa/ppe-items", "/api/oa/publicity-tasks", "/api/oa/qual-achievements", "/api/oa/qual-exports",
|
||||
"/api/oa/qual-risk-alerts", "/api/oa/rd-reuse-modules", "/api/oa/rd-weekly-reports", "/api/oa/regulatory-submissions",
|
||||
"/api/oa/sewage-emergency-plans", "/api/oa/sewage-kpi", "/api/oa/sewage-process-schemes", "/api/oa/sewage-sampling-plans",
|
||||
"/api/oa/sewage-shift-handovers", "/api/oa/sewage-spare-parts", "/api/oa/wm-decl-plans", "/api/oa/wm-fees",
|
||||
"/api/oa/wm-gov-subsidies", "/api/oa/wm-promotions", "/api/oa/ww-process-units", "/api/oa/ww-sampling-tasks",
|
||||
"/api/oa/ww-service-reports",
|
||||
// 质安部补完缺口新控制器:管理评审/作业场所监测含职业健康敏感数据,统一收口 ADMIN/APPROVER。
|
||||
"/api/oa/qms-management-reviews", "/api/oa/occ-workplace-monitors",
|
||||
// 成本控制部补完缺口新控制器:大宗采购比价(含报价/定标金额)、一对多进度款批次(含应付未付/合同金额)。
|
||||
"/api/oa/cost-procurement-bids", "/api/oa/multi-payment-batches",
|
||||
// W3 新控制器读口收口。
|
||||
"/api/oa/archive-acl-aware", "/api/oa/archive-acl-enforce", "/api/oa/archive-compliance", "/api/oa/archive-cross",
|
||||
"/api/oa/compliance-alert-rules", "/api/oa/cost-adjust-logs", "/api/oa/csl-analytics", "/api/oa/csl-audit-log",
|
||||
"/api/oa/csl-chapter-comments", "/api/oa/csl-entity-history", "/api/oa/csl-expert-scores", "/api/oa/csl-integral",
|
||||
"/api/oa/csl-market-analytics", "/api/oa/decl-calendar", "/api/oa/fert-finance-integration", "/api/oa/fert-occ-health",
|
||||
"/api/oa/fert-process-qc", "/api/oa/fert-raw-material-qc", "/api/oa/fert-solid-waste", "/api/oa/fert-supplier-profiles",
|
||||
"/api/oa/fert-wastewater", "/api/oa/fert-work-sub-orders", "/api/oa/fin-advances", "/api/oa/fin-budget-adjs",
|
||||
"/api/oa/fin-cost-calcs", "/api/oa/fin-ledger", "/api/oa/fin-post-loan-checks", "/api/oa/fin-rd-expenses",
|
||||
"/api/oa/hr-non-compete", "/api/oa/inventory-valuation-configs", "/api/oa/legal-entities", "/api/oa/legal-knowledge",
|
||||
"/api/oa/legal-risk-assess-tasks", "/api/oa/legal-risk-events", "/api/oa/legal-supplier-blacklist", "/api/oa/legal-trainings",
|
||||
"/api/oa/mfg-env-contracts", "/api/oa/mfg-inventory-lots", "/api/oa/mfg-mps", "/api/oa/mfg-self-service",
|
||||
"/api/oa/mfg-wip-costs", "/api/oa/mrp-from-engbom", "/api/oa/rd-decl-plans", "/api/oa/rd-doc-versions", "/api/oa/rd-knowledge",
|
||||
"/api/oa/rd-mobile", "/api/oa/rd-sci-tech-personnel", "/api/oa/sewage-cost-months", "/api/oa/sewage-env-records",
|
||||
"/api/oa/sewage-mobile-queues", "/api/oa/sewage-patrol", "/api/oa/sewage-power-sections", "/api/oa/sewage-reagent-stocks",
|
||||
"/api/oa/sv-archive-transfers", "/api/oa/sv-drawing-issues", "/api/oa/sv-equipment-ledger", "/api/oa/sv-safety-agg",
|
||||
"/api/oa/wm-cross-link", "/api/oa/wm-decl-packs", "/api/oa/wm-decl-templates", "/api/oa/wm-doc-versions",
|
||||
// 成本控制部补完缺口新控制器:跨部门联动(含HR费率/审计包/差异分摊/付款闸门,均含金额),收口 ADMIN/APPROVER。
|
||||
"/api/oa/cost-cross-dept",
|
||||
// 市场部/经营部补完缺口新控制器:投标报价明细(含金额)/客户联系人PII/商机阶段配置/经营业绩考核(含绩效得分),收口 ADMIN/APPROVER。
|
||||
"/api/oa/crm-bid-quote-details", "/api/oa/crm-customer-contacts",
|
||||
"/api/oa/crm-opp-stages", "/api/oa/crm-performance",
|
||||
// 工业废水运营中心·药剂费用按企业分摊(含成本金额,财务敏感读),收口 ADMIN/APPROVER。
|
||||
"/api/oa/ops-reagent-cost-alloc",
|
||||
// 结算中心/支付中心缺口补完新控制器(含金额/名单/预测,属机密财务与风控读),收口 ADMIN/APPROVER。
|
||||
"/api/oa/pmt-collection", "/api/oa/pmt-cost-alloc",
|
||||
"/api/oa/pmt-payee-blacklist", "/api/oa/stl-rolling-forecast", "/api/oa/stl-fund-report",
|
||||
// 内控部/审计监察部缺口补完:年度审计计划/资料请求/档案库/质量复核/跨模块聚合 含审计机密,收口 ADMIN/APPROVER。
|
||||
"/api/oa/audit-annual-plans", "/api/oa/audit-material-requests",
|
||||
"/api/oa/audit-archives", "/api/oa/audit-quality-reviews", "/api/oa/audit-data-aggregate",
|
||||
// W3 resume 全量补完新控制器读口收口 ADMIN/APPROVER。
|
||||
"/api/oa/admin-vehicle-reports", "/api/oa/branch-fin-statements", "/api/oa/branch-licenses",
|
||||
"/api/oa/brand-chronicle", "/api/oa/canteen-procurement", "/api/oa/crm-branch-leases",
|
||||
"/api/oa/crm-daily-reports", "/api/oa/crm-gps-tracks", "/api/oa/culture-material-downloads",
|
||||
"/api/oa/fin-ar-ap-auto", "/api/oa/fin-asset-inventories", "/api/oa/fin-period-close",
|
||||
"/api/oa/fin-rd-closeout", "/api/oa/hr-recruit", "/api/oa/internal-newsletters",
|
||||
"/api/oa/it-account-audit", "/api/oa/it-data-exports", "/api/oa/it-disaster-drills",
|
||||
"/api/oa/it-procurement", "/api/oa/it-sec-training", "/api/oa/it-sys-logs",
|
||||
"/api/oa/legal-compliance-certs", "/api/oa/legal-risk-measures", "/api/oa/logistics-suppliers",
|
||||
"/api/oa/logistics-surveys", "/api/oa/qual-analytics", "/api/oa/qual-year-checks",
|
||||
"/api/oa/sewage-detection-reports", "/api/oa/sewage-occ-health", "/api/oa/sewage-process-flow",
|
||||
"/api/oa/staff-self",
|
||||
// 申报服务部补完缺口新控制器读口:近三年审计报告(含财务数据)收口 ADMIN/APPROVER。
|
||||
"/api/oa/decl-audit-reports",
|
||||
// 申报材料协同编制(申报文档绑定+参与人列表,属过程文件/PII 聚合读),收口 ADMIN/APPROVER。
|
||||
"/api/oa/decl-material-collab",
|
||||
// W3 成本控制部补完缺口新控制器(含金额/主体/项目层级穿透,财务敏感读),收口 ADMIN/APPROVER。
|
||||
"/api/oa/cost-ctrl-ext", "/api/oa/supplier-contract-prereq",
|
||||
// 办事处请示报告(含涉及金额与审批链记录,属敏感读),收口 ADMIN/APPROVER。
|
||||
"/api/oa/crm-branch-petitions",
|
||||
// 审计监察部新缺口补完:审计证据/跨模块取数(HR+生产+申报敏感数据),收口 ADMIN/APPROVER。
|
||||
"/api/oa/audit-evidence", "/api/oa/audit-cross-module",
|
||||
// 审计监察部 W4 缺口补完:审计师档案(含利益冲突/资质证书)/结算法务聚合(含拆借金额/诉讼记录)。
|
||||
"/api/oa/auditor-profiles", "/api/oa/audit-settlement-agg",
|
||||
// 审计报告含审计机密/结论,收口 ADMIN/APPROVER。
|
||||
"/api/oa/audit-reports",
|
||||
// 风险管理有效性审计(含风险缺陷评估结论),收口 ADMIN/APPROVER。
|
||||
"/api/oa/audit-risk-assessments",
|
||||
// 支付中心/财务部缺口补完:科目辅助核算余额(含科目余额)/收入确认(含合同金额)/
|
||||
// BOM-成本集成(含成本金额)/采购预算检查(含预算/合同金额)/财务岗位权限矩阵(含权限配置),
|
||||
// 均含机密财务数据或权限主数据,统一收口 ADMIN/APPROVER。
|
||||
"/api/oa/fin-account-aux", "/api/oa/fin-rev-recognitions",
|
||||
"/api/oa/fin-bom-cost", "/api/oa/fin-procure-budget-check", "/api/oa/fin-account-perms",
|
||||
// W4 可建缺口补完新控制器读口收口 ADMIN/APPROVER。
|
||||
"/api/oa/archive-hash-records", "/api/oa/bid-knowledge", "/api/oa/compliance-risk-reports",
|
||||
"/api/oa/crm-follow-scripts", "/api/oa/crm-marketing-dashboard", "/api/oa/crm-mobile-biz",
|
||||
"/api/oa/design-gantt", "/api/oa/dev-launch-stats", "/api/oa/env-waste-records",
|
||||
"/api/oa/env-waste-records/exceed", "/api/oa/env-waste-records/stats", "/api/oa/hr-cross-dept",
|
||||
"/api/oa/hr-interview-questions", "/api/oa/lab-cross-dept", "/api/oa/patent-disclosures",
|
||||
"/api/oa/patent-kpi", "/api/oa/pmt-internal-trans (already in SENSITIVE_READ_PREFIXES)", "/api/oa/qhse-interface/cross-dept-summary",
|
||||
"/api/oa/qhse-interface/health-alert", "/api/oa/qhse-reports/env-discharge-report", "/api/oa/qual-elec-licenses",
|
||||
"/api/oa/rd-ebom-cost-calc", "/api/oa/rd-innovation-points", "/api/oa/rd-test-plans",
|
||||
"/api/oa/settlements (already in FINANCE_PREFIXES + SENSITIVE_READ_PREFIXES)", "/api/oa/stl-fund-report (already in SENSITIVE_READ_PREFIXES)", "/api/oa/sv-accidents",
|
||||
"/api/oa/sv-cross-link", "/api/oa/sv-personal-certs", "/api/oa/sv-sequence-inspects",
|
||||
// 行政/综合部补完缺口新控制器:安全事故台账(含死亡/受伤/直接损失金额,属法规敏感/事故机密);
|
||||
// 资质延续换证变更(含证书编号/受理回执/历史材料清单,属资质主数据敏感读),统一收口 ADMIN/APPROVER。
|
||||
"/api/oa/qual-safety-accidents", "/api/oa/qual-cert-changes",
|
||||
// W5 可建缺口补完新控制器读口收口。
|
||||
"/api/oa/branch-cost-entries", "/api/oa/branch-monthly-pnl", "/api/oa/compliance-violations",
|
||||
"/api/oa/cost-ctrl-deep", "/api/oa/cost-estimates", "/api/oa/crm-biz-achiev-link",
|
||||
"/api/oa/crm-pnl-monthly", "/api/oa/csl-customers", "/api/oa/decl-cross-dept",
|
||||
"/api/oa/decl-finance-data", "/api/oa/ehs-special-equip", "/api/oa/external-regulations",
|
||||
"/api/oa/fert-customer-profiles", "/api/oa/fert-raw-material-catalog", "/api/oa/fin-audit-export",
|
||||
"/api/oa/fin-budget-plans", "/api/oa/fin-period-ext", "/api/oa/financial-guarantees",
|
||||
"/api/oa/food-batches/waste-rate — 已在 AuthInterceptor SENSITIVE_READ_PREFIXES 中以 /api/oa/food-batches 前缀覆盖,无需额外配置", "/api/oa/ip-infringement-cases", "/api/oa/it-cross-dept",
|
||||
"/api/oa/it-inspection-reports", "/api/oa/legal-credit-invest", "/api/oa/litigation-documents",
|
||||
"/api/oa/publish-channels", "/api/oa/qhse-cross-dept-sync", "/api/oa/rd-after-sales-feedback",
|
||||
"/api/oa/rd-dev-cross-dept", "/api/oa/stl-forex-risks", "/api/oa/stl-fund-plan-submissions",
|
||||
"/api/oa/stl-internal-settle-rules", "/api/oa/stl-payment-refunds", "GET /api/oa/financings/{id}/legal-reviews — 落入 FINANCE_PREFIXES + SENSITIVE_READ_PREFIXES,需 ADMIN/APPROVER",
|
||||
"GET /api/oa/legal-risk-events/by-financing/{id} — 落入 SENSITIVE_READ_PREFIXES,需 ADMIN/APPROVER", "POST /api/oa/financings/{id}/legal-reviews/link — FINANCE_PREFIXES 写口,需 ADMIN/APPROVER", "POST /api/oa/financings/{id}/push-cost-alloc — FINANCE_PREFIXES 写口,需 ADMIN/APPROVER",
|
||||
"admin-ai-actions(AI 行动日志,需加入 AuthInterceptor SELF_SERVICE_WRITE_PREFIXES,见 sharedFileSnippets)", "admin-cleaning-supplies(保洁物资台账,默认 APPROVER_OR_ADMIN)", "admin-conference-materials(会务物料清单,含物料规划数据,默认 APPROVER_OR_ADMIN)",
|
||||
"admin-conference-staff(现场人员安排含联系电话 PII,默认 APPROVER_OR_ADMIN)", "admin-equip-setups(设备调试记录,默认 APPROVER_OR_ADMIN)", "admin-recurring-bookings(周期预订规则,默认 APPROVER_OR_ADMIN)",
|
||||
// W6 可建缺口补完新控制器读口收口。
|
||||
"/api/oa/cost-hr-labor-rates", "/api/oa/cost-integration", "/api/oa/csl-contracts",
|
||||
"/api/oa/design-external-portal", "/api/oa/expense-claims — 已在 AuthInterceptor FINANCE_PREFIXES + SENSITIVE_READ_PREFIXES 中,新 PATCH /{id}/invoice 自动继承保护,无需额外配置", "/api/oa/fert-wip-stocks",
|
||||
"/api/oa/ip-archive-search", "/api/oa/ip-free-tags", "/api/oa/it-dlp-policies",
|
||||
"/api/oa/it-dlp-policies — 含字段级脱敏规则/拦截策略明细(affectedFields/ruleDetail),属安全机密读,需加入 AuthInterceptor.SENSITIVE_READ_PREFIXES", "/api/oa/it-remote-assist", "/api/oa/it-remote-assist — 含授权码(authCode)和申请人联系方式(contact),属安全敏感读,需加入 AuthInterceptor.SENSITIVE_READ_PREFIXES",
|
||||
"/api/oa/patent-fees", "/api/oa/pmt-contract-fee-items 建议补入 AuthInterceptor FINANCE_PREFIXES(与 pmt-financing-contracts 同级)", "/api/oa/rd-annual-budgets",
|
||||
"/api/oa/stl-debt-ledger", "/api/oa/wm-subsidy-finance",
|
||||
// W7 可建/种子缺口补完新控制器读口收口。
|
||||
"/api/oa/audit-reg-lib", "/api/oa/branch-fin-statements (already in AuthInterceptor SENSITIVE_READ_PREFIXES line 295, covers all sub-paths including /ar-ap-detail)", "/api/oa/cost-hr-labor-rates (already in SENSITIVE_READ_PREFIXES)",
|
||||
"/api/oa/cost-purchase-orders (already in SENSITIVE_READ_PREFIXES)", "/api/oa/cost-reductions (already in SENSITIVE_READ_PREFIXES)", "/api/oa/csl-timesheets (already in FINANCE_PREFIXES + SENSITIVE_READ_PREFIXES in AuthInterceptor)",
|
||||
"/api/oa/csl-timesheets — already in FINANCE_PREFIXES + SENSITIVE_READ_PREFIXES (AuthInterceptor lines 96, 180); no change needed", "/api/oa/fert-accounting", "/api/oa/fin-emp-loans",
|
||||
"/api/oa/financing-policies", "/api/oa/fraud-investigation-notes", "/api/oa/hr-emp-satisfaction",
|
||||
"/api/oa/it-sec-policy", "/api/oa/legal-cross-dept", "/api/oa/mfg-cost-entries",
|
||||
"/api/oa/mfg-fqc-reports", "/api/oa/pmt-bank-evals", "/api/oa/sewage-cross-dept-linkages",
|
||||
"/api/oa/sewage-ext-lab-commissions", "/api/oa/standard-costs (already in SENSITIVE_READ_PREFIXES)", "/api/oa/ww-msds-files(MSDS安全说明书文件库,含危险类别/应急处置/PPE要求等安全敏感信息,建议集成时加入 AuthInterceptor.SENSITIVE_READ_PREFIXES)",
|
||||
"/api/oa/ww-xdept-bridge(跨部门联动桥接,GET /status 为汇总读,POST 写口已由 default-deny 保护,无需额外读收口)"
|
||||
);
|
||||
|
||||
/**
|
||||
* 自助写白名单(写操作 default-deny 翻转后的"放行档"):仅这些个人/社交/协作/工作流端点,
|
||||
* 任意已授角色(含普通 USER)可写——它们的"只动自己的"由各控制器内对象级属主校验保证
|
||||
* (favorites/schedule-events/work-plans/blogs/feeds/discussions/collab-docs/files/messages/
|
||||
* form-instances/tasks 均已加 isOwner/当前办理人/收件人校验;surveys 为投票/公共协作)。
|
||||
* **白名单之外的一切写口默认需 ADMIN/APPROVER**(见 requiredRolesFor 默认分支),从而系统性封死
|
||||
* "任意 USER 改/删他人业务记录(商机/会议/审计/监理/资产/项目/主数据…)"的 BFLA 整类,而非逐个打补丁。
|
||||
*/
|
||||
private static final List<String> SELF_SERVICE_WRITE_PREFIXES = List.of(
|
||||
"/api/oa/favorites", "/api/oa/schedule-events", "/api/oa/work-plans",
|
||||
"/api/oa/blogs", "/api/oa/feeds", "/api/oa/discussions", "/api/oa/collab-docs",
|
||||
"/api/oa/messages", "/api/oa/files", "/api/oa/form-instances",
|
||||
"/api/oa/tasks",
|
||||
// P2:AI 助手(/ai/* 个人辅助工具,不读写敏感业务库,未配 Key 时降级无副作用)、
|
||||
// 移动外勤打卡(mobile-checkins 个人行踪,对象级属主校验在控制器内)——放给任意已授角色。
|
||||
"/api/oa/ai", "/api/oa/mobile-checkins"
|
||||
// 注意:surveys 不在此白名单——其 create/update/delete 缺对象级校验,整体收口到 ADMIN/APPROVER;
|
||||
// 仅"投票"动作(POST /surveys/{id}/vote)由 requiredRolesFor 的 /vote 特判放行给任意角色。
|
||||
);
|
||||
|
||||
public AuthInterceptor(CurrentUserResolver currentUser, AuthorizationService authz) {
|
||||
this.currentUser = currentUser;
|
||||
this.authz = authz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
|
||||
// CORS preflight carries no auth header by design -> let it through.
|
||||
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
|
||||
return true;
|
||||
}
|
||||
// 关键:用与控制器同口径的"规范化路径"(已解码、剥矩阵参数、折叠斜杠、小写)做匹配。
|
||||
// 直接拿 getRequestURI() 是原始未解码串,会被 /api/oa/%75sers、/api/oa/users;x=1 这类
|
||||
// 绕过门槛——而 Spring 控制器仍按解码后路径命中,等于授权形同虚设(已实测可越权)。
|
||||
String path = normalizePath(request.getRequestURI());
|
||||
if (PUBLIC.contains(path)) {
|
||||
return true;
|
||||
}
|
||||
SysUser user = currentUser.resolve(request);
|
||||
if (user == null) {
|
||||
writeError(response, HttpStatus.UNAUTHORIZED, 401, "未登录或会话已过期");
|
||||
return false;
|
||||
}
|
||||
Set<String> have = authz.roleCodesOf(user.getId());
|
||||
// 默认拒绝(读写皆然):无任何角色的账号一律零权限,封死"建了用户没授权即可随意读写"
|
||||
// 的无角色越权面(读侧此前对任意登录用户全开是系统性 IDOR 根因之一)。
|
||||
if (have.isEmpty()) {
|
||||
writeError(response, HttpStatus.FORBIDDEN, 403, "无权限:账号未分配任何角色");
|
||||
return false;
|
||||
}
|
||||
boolean isWrite = WRITE_METHODS.contains(request.getMethod().toUpperCase(Locale.ROOT));
|
||||
if (isWrite) {
|
||||
// 写:ADMIN 前缀→需 ADMIN;自助白名单→任意已授角色即可(对象级属主校验在控制器内);
|
||||
// **其余一切业务/主数据写默认需 ADMIN/APPROVER**(default-deny 翻转,封死任意 USER 越权写他人业务记录)。
|
||||
Set<String> required = requiredRolesFor(path);
|
||||
if (required != null && required.stream().noneMatch(have::contains)) {
|
||||
writeError(response, HttpStatus.FORBIDDEN, 403,
|
||||
"无权限:该操作需要 " + String.join("/", required));
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
// 读:机密财务/PII 读口需 ADMIN/APPROVER,其余业务读维持"已登录且有角色"可读。
|
||||
if (isSensitiveRead(path) && APPROVER_OR_ADMIN.stream().noneMatch(have::contains)) {
|
||||
writeError(response, HttpStatus.FORBIDDEN, 403, "无权限:该数据需要 ADMIN/APPROVER 角色");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private static boolean isSensitiveRead(String path) {
|
||||
// 研发证据链聚合端点 /api/oa/rd-projects/{id}/evidence-chain 会端出研发费用金额/凭证号/申报/专利
|
||||
// 等本应被 rd-expenses 读门槛拦下的明细(基路径 rd-projects 非敏感,故按后缀精确收口,
|
||||
// 不误伤 RdProjectController 的普通项目登记读)。
|
||||
if (path.endsWith("/evidence-chain")) {
|
||||
return true;
|
||||
}
|
||||
// 水质超标溯源 /api/oa/water-quality/trace 连表把受保护的排污客户主数据(企业名/行业/日排放量/追偿建议)
|
||||
// 端出来——基路径 water-quality 非敏感,按后缀精确收口到 ADMIN/APPROVER(同 evidence-chain 旁路)。
|
||||
if (path.endsWith("/water-quality/trace")) {
|
||||
return true;
|
||||
}
|
||||
for (String p : SENSITIVE_READ_PREFIXES) {
|
||||
if (path.equals(p) || path.startsWith(p + "/")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// ★系统性收口:凡写侧受 ADMIN/APPROVER 门禁的财务/主数据/PII 端点(FINANCE_PREFIXES),读侧一律同门禁。
|
||||
// 杜绝"写口收了、读口漏收"的反复缺口(红队实证:合同/客户/供应商/申报/专利/补贴等读口逐个漏出敏感数据)。
|
||||
for (String p : FINANCE_PREFIXES) {
|
||||
if (path.equals(p) || path.startsWith(p + "/")) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** 规范化路径命中哪一档授权要求;都不命中返回 null(默认档:需任一已授角色)。 */
|
||||
private static Set<String> requiredRolesFor(String path) {
|
||||
// 侧写财务副作用端点:基路径非财务,但会写投标/合同/记账等财务实体(如商机转投标、
|
||||
// 项目阶段推进记账)。按路径特征单独收口到 ADMIN/APPROVER,杜绝绕开财务前缀门槛的旁路写。
|
||||
if (path.endsWith("/convert-bid")
|
||||
|| (path.startsWith("/api/oa/projects/") && path.endsWith("/advance"))) {
|
||||
return APPROVER_OR_ADMIN;
|
||||
}
|
||||
// 问卷投票是公共动作:任意已授角色可对任意问卷投票(仅 POST /surveys/{id}/vote,
|
||||
// 不含问卷的创建/修改/删除——那些经默认档收口到 ADMIN/APPROVER)。
|
||||
if (path.endsWith("/vote")) {
|
||||
return null;
|
||||
}
|
||||
for (String p : ADMIN_PREFIXES) {
|
||||
if (path.equals(p) || path.startsWith(p + "/")) {
|
||||
return ADMIN;
|
||||
}
|
||||
}
|
||||
// 自助写白名单:个人/社交/协作/工作流端点放给任意已授角色(对象级属主校验在控制器内)。
|
||||
for (String p : SELF_SERVICE_WRITE_PREFIXES) {
|
||||
if (path.equals(p) || path.startsWith(p + "/")) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
// FINANCE_PREFIXES 显式归在 ADMIN/APPROVER(与下方默认同档,保留以自文档化财务/主数据写口)。
|
||||
for (String p : FINANCE_PREFIXES) {
|
||||
if (path.equals(p) || path.startsWith(p + "/")) {
|
||||
return APPROVER_OR_ADMIN;
|
||||
}
|
||||
}
|
||||
// ★default-deny 翻转:其余一切业务/主数据写口(商机/会议/审计/监理/IT资产/项目/实验室/各台账…)
|
||||
// 默认需 ADMIN/APPROVER,普通 USER 一律挡。系统性封死"未显式登记的写口=任意角色可写"的 BFLA 整类。
|
||||
return APPROVER_OR_ADMIN;
|
||||
}
|
||||
|
||||
/**
|
||||
* 把请求 URI 规范化到与 Spring 控制器映射一致的口径,杜绝授权前缀匹配被绕过:
|
||||
* URL 解码(%75→u)、剥除每段矩阵参数(;k=v)、折叠重复斜杠、去尾斜杠、统一小写。
|
||||
* 解码异常时回退原串(宁可多拦不可漏拦)。
|
||||
*/
|
||||
static String normalizePath(String uri) {
|
||||
if (uri == null) {
|
||||
return "";
|
||||
}
|
||||
String p = uri;
|
||||
try {
|
||||
p = URLDecoder.decode(p, StandardCharsets.UTF_8);
|
||||
} catch (RuntimeException ignore) {
|
||||
// 非法转义序列:保留原串继续规范化(授权偏保守,不放行可疑请求)。
|
||||
}
|
||||
p = p.replaceAll(";[^/]*", ""); // 剥矩阵参数 /users;x=1 -> /users
|
||||
p = p.replaceAll("/{2,}", "/"); // 折叠重复斜杠 //api -> /api
|
||||
if (p.length() > 1 && p.endsWith("/")) {
|
||||
p = p.substring(0, p.length() - 1);
|
||||
}
|
||||
return p.toLowerCase(Locale.ROOT);
|
||||
}
|
||||
|
||||
private void writeError(HttpServletResponse response, HttpStatus status, int code, String message) throws Exception {
|
||||
response.setStatus(status.value());
|
||||
response.setContentType(MediaType.APPLICATION_JSON_VALUE);
|
||||
response.setCharacterEncoding("UTF-8");
|
||||
response.getWriter().write("{\"code\":" + code + ",\"message\":\"" + message + "\",\"data\":null}");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.kaidi.oa.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.servlet.config.annotation.CorsRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
|
||||
/**
|
||||
* CORS + auth-interceptor registration for the OA frontend.
|
||||
*
|
||||
* Allows the Vue dev servers and local hosts to call /api/** during development.
|
||||
* allowedOriginPatterns is used (instead of allowedOrigins) so the 127.0.0.1
|
||||
* wildcard port pattern is accepted alongside credentials.
|
||||
*/
|
||||
@Configuration
|
||||
public class CorsConfig implements WebMvcConfigurer {
|
||||
|
||||
private final AuthInterceptor authInterceptor;
|
||||
private final AuditLogInterceptor auditLogInterceptor;
|
||||
|
||||
public CorsConfig(AuthInterceptor authInterceptor, AuditLogInterceptor auditLogInterceptor) {
|
||||
this.authInterceptor = authInterceptor;
|
||||
this.auditLogInterceptor = auditLogInterceptor;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addInterceptors(@NonNull InterceptorRegistry registry) {
|
||||
// 审计拦截器先注册:afterCompletion 按注册逆序执行,且 Spring 仅对 preHandle 返回 true 的拦截器
|
||||
// 回调 afterCompletion;审计 preHandle 恒 true 且排在 Auth 之前,故连「被授权拒绝(401/403)的越权写尝试」
|
||||
// 也能落操作审计留痕(完整留痕)。
|
||||
registry.addInterceptor(auditLogInterceptor).addPathPatterns("/api/oa/**");
|
||||
registry.addInterceptor(authInterceptor).addPathPatterns("/api/oa/**");
|
||||
}
|
||||
|
||||
@Override
|
||||
public void addCorsMappings(@NonNull CorsRegistry registry) {
|
||||
registry.addMapping("/api/**")
|
||||
.allowedOriginPatterns(
|
||||
// any localhost / 127.0.0.1 port (Vite dev defaults to 5175,
|
||||
// built app is served same-origin); covers dev + preview.
|
||||
"http://localhost:*",
|
||||
"http://127.0.0.1:*",
|
||||
// 公网隧道演示(ngrok / cloudflare)。整站同源经隧道访问本不触发 CORS,
|
||||
// 这几条只覆盖个别 scheme/host 口径差异。**不再用 "*" 反射任意源**——
|
||||
// 通配反射 + allowCredentials 是公认反模式(将来改用 cookie 即成 CSRF 通道),
|
||||
// 收敛到隧道域名精确模式:既能跑演示,又关掉任意源反射面。
|
||||
"https://*.ngrok-free.dev",
|
||||
"https://*.ngrok-free.app",
|
||||
"https://*.ngrok.io",
|
||||
"https://*.trycloudflare.com"
|
||||
)
|
||||
.allowedMethods("GET", "POST", "PUT", "DELETE", "PATCH", "OPTIONS")
|
||||
.allowedHeaders("*")
|
||||
.allowCredentials(true)
|
||||
.maxAge(3600);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.kaidi.oa.config;
|
||||
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.SerializationFeature;
|
||||
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.context.annotation.Primary;
|
||||
|
||||
/**
|
||||
* Shared ObjectMapper bean used by services that read/write the JSON TEXT
|
||||
* columns (form schema / flow schema / instance data) and by the web layer.
|
||||
*
|
||||
* Registers the JavaTime module so java.time.Instant fields serialize as ISO-8601
|
||||
* strings (not numeric timestamps), and marks the bean @Primary so it is the one
|
||||
* Spring MVC uses for HTTP message conversion as well.
|
||||
*/
|
||||
@Configuration
|
||||
public class JacksonConfig {
|
||||
|
||||
@Bean
|
||||
@Primary
|
||||
public ObjectMapper objectMapper() {
|
||||
return new ObjectMapper()
|
||||
.registerModule(new JavaTimeModule())
|
||||
.disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
package com.kaidi.oa.config;
|
||||
|
||||
import jakarta.servlet.Filter;
|
||||
import jakarta.servlet.FilterChain;
|
||||
import jakarta.servlet.ServletException;
|
||||
import jakarta.servlet.ServletRequest;
|
||||
import jakarta.servlet.ServletResponse;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import org.springframework.core.Ordered;
|
||||
import org.springframework.core.annotation.Order;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/**
|
||||
* 全站安全响应头。
|
||||
*
|
||||
* 给所有响应统一加:
|
||||
* - X-Content-Type-Options: nosniff —— 关掉 MIME 嗅探,配合文件下载的类型降级防存储型 XSS;
|
||||
* - X-Frame-Options: SAMEORIGIN —— 防点击劫持(页面被第三方 iframe 套壳);
|
||||
* - Referrer-Policy: same-origin —— 跳转外站不泄露内网 URL/带参;
|
||||
* - X-XSS-Protection: 0 —— 显式关闭旧版浏览器有缺陷的 XSS 审计器(现代做法)。
|
||||
*
|
||||
* 不在此处强加严格 CSP:现有 Element Plus SPA 大量内联样式,盲加 CSP 会白屏;CSP 需与前端
|
||||
* 配合(nonce / hash)单独推进,列为已知后续项。
|
||||
*/
|
||||
@Component
|
||||
@Order(Ordered.HIGHEST_PRECEDENCE)
|
||||
public class SecurityHeadersFilter implements Filter {
|
||||
|
||||
@Override
|
||||
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
|
||||
throws IOException, ServletException {
|
||||
if (response instanceof HttpServletResponse resp) {
|
||||
resp.setHeader("X-Content-Type-Options", "nosniff");
|
||||
resp.setHeader("X-Frame-Options", "SAMEORIGIN");
|
||||
resp.setHeader("Referrer-Policy", "same-origin");
|
||||
resp.setHeader("X-XSS-Protection", "0");
|
||||
}
|
||||
chain.doFilter(request, response);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.kaidi.oa.config;
|
||||
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.core.io.ClassPathResource;
|
||||
import org.springframework.core.io.Resource;
|
||||
import org.springframework.http.CacheControl;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
|
||||
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
|
||||
import org.springframework.web.servlet.resource.PathResourceResolver;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
/**
|
||||
* Serves the built Vue SPA from classpath:/static so the whole platform ships as a
|
||||
* SINGLE deployable jar — running the backend brings the frontend up on the same
|
||||
* port (no separate vite/static server, no分离部署).
|
||||
*
|
||||
* Caching strategy (so frontend updates always reach users):
|
||||
* - /assets/** are content-hashed (filename changes every build) -> cache hard.
|
||||
* - index.html + SPA routes -> no-cache, so a rebuilt UI is never served stale.
|
||||
*
|
||||
* Any path that is not an existing asset and not an /api/** call falls back to
|
||||
* index.html so vue-router history mode (deep links / refresh) resolves to the SPA.
|
||||
* @RestController mappings for /api/** take precedence, so API calls are unaffected.
|
||||
*/
|
||||
@Configuration
|
||||
public class SpaWebConfig implements WebMvcConfigurer {
|
||||
|
||||
@Override
|
||||
public void addResourceHandlers(@NonNull ResourceHandlerRegistry registry) {
|
||||
// Content-hashed build assets -> safe to cache for a long time.
|
||||
registry.addResourceHandler("/assets/**")
|
||||
.addResourceLocations("classpath:/static/assets/")
|
||||
.setCacheControl(CacheControl.maxAge(365, TimeUnit.DAYS).cachePublic().immutable());
|
||||
|
||||
// index.html, SPA routes, and other root files -> never cache, so a rebuilt
|
||||
// frontend is picked up immediately; non-asset routes fall back to index.html.
|
||||
registry.addResourceHandler("/**")
|
||||
.addResourceLocations("classpath:/static/")
|
||||
.setCacheControl(CacheControl.noCache())
|
||||
.resourceChain(true)
|
||||
.addResolver(new PathResourceResolver() {
|
||||
@Override
|
||||
protected Resource getResource(@NonNull String resourcePath, @NonNull Resource location)
|
||||
throws IOException {
|
||||
Resource requested = location.createRelative(resourcePath);
|
||||
if (requested.exists() && requested.isReadable()) {
|
||||
return requested;
|
||||
}
|
||||
// Never hand API calls an HTML page; let them resolve elsewhere / 404.
|
||||
if (resourcePath.startsWith("api/")) {
|
||||
return null;
|
||||
}
|
||||
// A missing asset-looking path (has an extension) is a real 404.
|
||||
if (resourcePath.contains(".")) {
|
||||
return null;
|
||||
}
|
||||
// SPA route (no extension) -> index.html (history-mode fallback).
|
||||
Resource index = new ClassPathResource("/static/index.html");
|
||||
return index.exists() ? index : null;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
package com.kaidi.oa.config;
|
||||
|
||||
import com.kaidi.oa.domain.CollabDoc;
|
||||
import com.kaidi.oa.domain.SysUser;
|
||||
import com.kaidi.oa.repository.CollabDocRepository;
|
||||
import com.kaidi.oa.service.AuthService;
|
||||
import com.kaidi.oa.service.AuthorizationService;
|
||||
import com.kaidi.oa.web.ws.CollabSocketHandler;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.http.server.ServerHttpRequest;
|
||||
import org.springframework.http.server.ServerHttpResponse;
|
||||
import org.springframework.lang.NonNull;
|
||||
import org.springframework.web.socket.WebSocketHandler;
|
||||
import org.springframework.web.socket.config.annotation.EnableWebSocket;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketConfigurer;
|
||||
import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry;
|
||||
import org.springframework.web.socket.server.HandshakeInterceptor;
|
||||
|
||||
import java.net.URI;
|
||||
import java.util.Arrays;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Registers the real-time collaboration WebSocket endpoint.
|
||||
*
|
||||
* /ws/collab/{docId} relays yjs CRDT updates between clients editing the same
|
||||
* document. 它在 /api/oa/** 之外,AuthInterceptor 不覆盖它——因此**在握手阶段**自行鉴权:
|
||||
* 浏览器 WS 无法带自定义头,token 通过查询串 ?token=xxx 传入,经 AuthService.resolve 校验,
|
||||
* 未认证一律拒绝握手(401)。**并做对象级鉴权**:解析路径里的 docId,仅文档属主(按稳定 ownerId)、
|
||||
* 参与者(participants 含本人 displayName)或 ADMIN 可握手,杜绝任意登录用户按 docId 枚举读/写他人
|
||||
* 文档(与 CollabDocController 的对象级口径一致,堵 WS 旁路 BOLA)。同时把来源从
|
||||
* setAllowedOrigins("*") 收敛到与 HTTP CORS 一致的域名模式,杜绝跨站 WebSocket 劫持(CSWSH)。
|
||||
*/
|
||||
@Configuration
|
||||
@EnableWebSocket
|
||||
public class WebSocketConfig implements WebSocketConfigurer {
|
||||
|
||||
private final CollabSocketHandler collabSocketHandler;
|
||||
private final AuthService authService;
|
||||
private final CollabDocRepository collabDocRepo;
|
||||
private final AuthorizationService authz;
|
||||
|
||||
public WebSocketConfig(CollabSocketHandler collabSocketHandler, AuthService authService,
|
||||
CollabDocRepository collabDocRepo, AuthorizationService authz) {
|
||||
this.collabSocketHandler = collabSocketHandler;
|
||||
this.authService = authService;
|
||||
this.collabDocRepo = collabDocRepo;
|
||||
this.authz = authz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void registerWebSocketHandlers(@NonNull WebSocketHandlerRegistry registry) {
|
||||
registry.addHandler(collabSocketHandler, "/ws/collab/**")
|
||||
.addInterceptors(new TokenHandshakeInterceptor(authService, collabDocRepo, authz))
|
||||
.setAllowedOriginPatterns(
|
||||
"http://localhost:*", "http://127.0.0.1:*",
|
||||
"https://*.ngrok-free.dev", "https://*.ngrok-free.app",
|
||||
"https://*.ngrok.io", "https://*.trycloudflare.com");
|
||||
}
|
||||
|
||||
/** 握手鉴权:先验 token,再按 docId 做对象级访问控制(属主/参与者/ADMIN)。 */
|
||||
static final class TokenHandshakeInterceptor implements HandshakeInterceptor {
|
||||
|
||||
private final AuthService authService;
|
||||
private final CollabDocRepository collabDocRepo;
|
||||
private final AuthorizationService authz;
|
||||
|
||||
TokenHandshakeInterceptor(AuthService authService, CollabDocRepository collabDocRepo,
|
||||
AuthorizationService authz) {
|
||||
this.authService = authService;
|
||||
this.collabDocRepo = collabDocRepo;
|
||||
this.authz = authz;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean beforeHandshake(@NonNull ServerHttpRequest request, @NonNull ServerHttpResponse response,
|
||||
@NonNull WebSocketHandler wsHandler, @NonNull Map<String, Object> attributes) {
|
||||
// 1) 认证:?token= 经 AuthService 校验,未认证拒绝握手。
|
||||
String token = tokenFrom(request.getURI());
|
||||
SysUser su = token == null ? null : authService.resolve(token);
|
||||
if (su == null) {
|
||||
response.setStatusCode(HttpStatus.UNAUTHORIZED);
|
||||
return false;
|
||||
}
|
||||
// 2) 对象级鉴权:解析 docId,仅属主/参与者/ADMIN 可接入该文档。
|
||||
Long docId = docIdFrom(request.getURI());
|
||||
if (docId == null) {
|
||||
// 路径无有效数字 docId:无法定位文档,拒绝(杜绝绕过对象级校验的非法房间名)。
|
||||
response.setStatusCode(HttpStatus.FORBIDDEN);
|
||||
return false;
|
||||
}
|
||||
CollabDoc doc = collabDocRepo.findById(docId).orElse(null);
|
||||
if (doc == null) {
|
||||
response.setStatusCode(HttpStatus.NOT_FOUND);
|
||||
return false;
|
||||
}
|
||||
if (!canAccess(su, doc)) {
|
||||
response.setStatusCode(HttpStatus.FORBIDDEN);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/** ADMIN、文档属主(稳定 ownerId 优先、空回退 owner 名),或参与者(participants 含本人 displayName)。 */
|
||||
private boolean canAccess(SysUser su, CollabDoc doc) {
|
||||
if (authz.isAdmin(su) || authz.isOwner(su, doc.getOwnerId(), doc.getOwner())) {
|
||||
return true;
|
||||
}
|
||||
String me = su.getDisplayName();
|
||||
String participants = doc.getParticipants();
|
||||
if (me == null || me.isBlank() || participants == null || participants.isBlank()) {
|
||||
return false;
|
||||
}
|
||||
// participants 为逗号分隔的 displayName 列表(协作成员)。
|
||||
return Arrays.stream(participants.split(","))
|
||||
.map(String::trim)
|
||||
.anyMatch(me::equals);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterHandshake(@NonNull ServerHttpRequest request, @NonNull ServerHttpResponse response,
|
||||
@NonNull WebSocketHandler wsHandler, Exception exception) {
|
||||
// no-op
|
||||
}
|
||||
|
||||
/** 从 /ws/collab/{docId} 路径解析末段为数字 docId;非数字/缺失返回 null。 */
|
||||
private static Long docIdFrom(URI uri) {
|
||||
String path = uri.getPath();
|
||||
if (path == null) {
|
||||
return null;
|
||||
}
|
||||
int slash = path.lastIndexOf('/');
|
||||
String last = slash >= 0 ? path.substring(slash + 1) : path;
|
||||
try {
|
||||
return Long.parseLong(last.trim());
|
||||
} catch (NumberFormatException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static String tokenFrom(URI uri) {
|
||||
String q = uri.getQuery();
|
||||
if (q == null) {
|
||||
return null;
|
||||
}
|
||||
for (String pair : q.split("&")) {
|
||||
int eq = pair.indexOf('=');
|
||||
if (eq > 0 && "token".equals(pair.substring(0, eq))) {
|
||||
String v = pair.substring(eq + 1);
|
||||
return v.isBlank() ? null : v;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* An accounting subject (会计科目) for the finance general-ledger center
|
||||
* (财务总账). code is the subject code (such as 1001 / 1122 / 6001); category is
|
||||
* one of 资产 / 负债 / 权益 / 成本 / 损益; direction (余额方向) is one of 借 / 贷;
|
||||
* parentCode is the optional parent code and level is the subject level (级次);
|
||||
* balance is the closing balance (期末余额).
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "account")
|
||||
public class Account {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String code;
|
||||
|
||||
private String name;
|
||||
|
||||
private String category;
|
||||
|
||||
private String direction;
|
||||
|
||||
private String parentCode;
|
||||
|
||||
private int level;
|
||||
|
||||
private BigDecimal balance = BigDecimal.ZERO;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public void setDirection(String direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public String getParentCode() {
|
||||
return parentCode;
|
||||
}
|
||||
|
||||
public void setParentCode(String parentCode) {
|
||||
this.parentCode = parentCode;
|
||||
}
|
||||
|
||||
public int getLevel() {
|
||||
return level;
|
||||
}
|
||||
|
||||
public void setLevel(int level) {
|
||||
this.level = level;
|
||||
}
|
||||
|
||||
public BigDecimal getBalance() {
|
||||
return balance;
|
||||
}
|
||||
|
||||
public void setBalance(BigDecimal balance) {
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·门禁出入记录(安保与门禁 PARTIAL 缺口补全)。
|
||||
* 记录员工工卡/人脸识别门禁进出记录、外来车辆登记。
|
||||
* recordType: 员工刷卡 / 人脸识别 / 外来车辆 / 大件物品
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_access_record")
|
||||
public class AdminAccessRecord {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 记录类型:员工刷卡 / 人脸识别 / 外来车辆 / 大件物品带出。 */
|
||||
private String recordType;
|
||||
|
||||
/** 员工姓名或来访人员/车辆信息。 */
|
||||
private String personInfo;
|
||||
|
||||
/** 员工工号/访客证号/车牌号。 */
|
||||
private String identifier;
|
||||
|
||||
/** 门禁位置/门区(主门/东门/车库/货运入口等)。 */
|
||||
private String gateLocation;
|
||||
|
||||
/** 进/出方向。 */
|
||||
private String direction;
|
||||
|
||||
/** 出入时间。 */
|
||||
private Instant accessTime;
|
||||
|
||||
/** 大件物品描述(recordType=大件物品带出时填写)。 */
|
||||
private String itemDesc;
|
||||
|
||||
/** 关联审批单号(大件物品带出审批)。 */
|
||||
private String approvalCode;
|
||||
|
||||
/** 操作员/门卫姓名。 */
|
||||
private String guardName;
|
||||
|
||||
/** 备注。 */
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getRecordType() { return recordType; }
|
||||
public void setRecordType(String recordType) { this.recordType = recordType; }
|
||||
public String getPersonInfo() { return personInfo; }
|
||||
public void setPersonInfo(String personInfo) { this.personInfo = personInfo; }
|
||||
public String getIdentifier() { return identifier; }
|
||||
public void setIdentifier(String identifier) { this.identifier = identifier; }
|
||||
public String getGateLocation() { return gateLocation; }
|
||||
public void setGateLocation(String gateLocation) { this.gateLocation = gateLocation; }
|
||||
public String getDirection() { return direction; }
|
||||
public void setDirection(String direction) { this.direction = direction; }
|
||||
public Instant getAccessTime() { return accessTime; }
|
||||
public void setAccessTime(Instant accessTime) { this.accessTime = accessTime; }
|
||||
public String getItemDesc() { return itemDesc; }
|
||||
public void setItemDesc(String itemDesc) { this.itemDesc = itemDesc; }
|
||||
public String getApprovalCode() { return approvalCode; }
|
||||
public void setApprovalCode(String approvalCode) { this.approvalCode = approvalCode; }
|
||||
public String getGuardName() { return guardName; }
|
||||
public void setGuardName(String guardName) { this.guardName = guardName; }
|
||||
public String getRemark() { return remark; }
|
||||
public void setRemark(String remark) { this.remark = remark; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·AI行政动作落库日志。
|
||||
* 记录"会议纪要AI整理→推送到指定位置"、"多部门周报/月报AI汇总→生成文档"等
|
||||
* AI工作流动作结果,实现业务闭环(push target 落库)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_ai_action_log")
|
||||
public class AdminAiActionLog {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 动作类型:MINUTES_ORGANIZE(纪要整理)/ REPORT_SUMMARY(周月报汇总)/ DOC_GENERATE(公文生成)。 */
|
||||
@Column(nullable = false)
|
||||
private String actionType;
|
||||
|
||||
/** 来源对象类型:meeting_minute / dept_report / custom。 */
|
||||
private String sourceType;
|
||||
|
||||
/** 来源对象 ID(如 meetingMinute.id)。 */
|
||||
private Long sourceId;
|
||||
|
||||
/** 来源对象标题/描述。 */
|
||||
private String sourceTitle;
|
||||
|
||||
/** 推送目标位置描述(如"行政档案库-2026年Q2纪要"、"各部门周报汇总钉钉群")。 */
|
||||
@Column(length = 500)
|
||||
private String pushTarget;
|
||||
|
||||
/** 涉及部门(逗号分隔,周报汇总时使用)。 */
|
||||
@Column(length = 1000)
|
||||
private String involvedDepts;
|
||||
|
||||
/** 报告周期(如 2026-W24、2026-06)。 */
|
||||
private String reportPeriod;
|
||||
|
||||
/** AI生成结果摘要(存储前256字符)。 */
|
||||
@Column(length = 2000)
|
||||
private String resultSummary;
|
||||
|
||||
/** AI模型。 */
|
||||
private String aiModel;
|
||||
|
||||
/** 执行人(谁触发了此动作)。 */
|
||||
private String operator;
|
||||
|
||||
/** 状态:成功 / 失败 / AI未配置。 */
|
||||
@Column(nullable = false)
|
||||
private String actionStatus;
|
||||
|
||||
/** 失败原因。 */
|
||||
@Column(length = 500)
|
||||
private String errorMsg;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getActionType() { return actionType; }
|
||||
public void setActionType(String actionType) { this.actionType = actionType; }
|
||||
public String getSourceType() { return sourceType; }
|
||||
public void setSourceType(String sourceType) { this.sourceType = sourceType; }
|
||||
public Long getSourceId() { return sourceId; }
|
||||
public void setSourceId(Long sourceId) { this.sourceId = sourceId; }
|
||||
public String getSourceTitle() { return sourceTitle; }
|
||||
public void setSourceTitle(String sourceTitle) { this.sourceTitle = sourceTitle; }
|
||||
public String getPushTarget() { return pushTarget; }
|
||||
public void setPushTarget(String pushTarget) { this.pushTarget = pushTarget; }
|
||||
public String getInvolvedDepts() { return involvedDepts; }
|
||||
public void setInvolvedDepts(String involvedDepts) { this.involvedDepts = involvedDepts; }
|
||||
public String getReportPeriod() { return reportPeriod; }
|
||||
public void setReportPeriod(String reportPeriod) { this.reportPeriod = reportPeriod; }
|
||||
public String getResultSummary() { return resultSummary; }
|
||||
public void setResultSummary(String resultSummary) { this.resultSummary = resultSummary; }
|
||||
public String getAiModel() { return aiModel; }
|
||||
public void setAiModel(String aiModel) { this.aiModel = aiModel; }
|
||||
public String getOperator() { return operator; }
|
||||
public void setOperator(String operator) { this.operator = operator; }
|
||||
public String getActionStatus() { return actionStatus; }
|
||||
public void setActionStatus(String actionStatus) { this.actionStatus = actionStatus; }
|
||||
public String getErrorMsg() { return errorMsg; }
|
||||
public void setErrorMsg(String errorMsg) { this.errorMsg = errorMsg; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·监控设备台账(安保与门禁 PARTIAL 缺口补全)。
|
||||
* 记录监控摄像头/NVR等设备的安装位置、运行状态、巡检记录。
|
||||
* status: 运行正常 / 故障 / 维修中 / 已停用
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_cctv_device")
|
||||
public class AdminCctvDevice {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 设备编号(自动生成)。 */
|
||||
private String deviceCode;
|
||||
|
||||
/** 设备名称。 */
|
||||
private String deviceName;
|
||||
|
||||
/** 设备类型(球机/枪机/半球/NVR/其他)。 */
|
||||
private String deviceType;
|
||||
|
||||
/** 安装位置。 */
|
||||
private String location;
|
||||
|
||||
/** 安装日期(YYYY-MM-DD)。 */
|
||||
private String installDate;
|
||||
|
||||
/** 品牌/型号。 */
|
||||
private String brandModel;
|
||||
|
||||
/** IP 地址。 */
|
||||
private String ipAddress;
|
||||
|
||||
/** 存储时长(天)。 */
|
||||
private Integer storeDays;
|
||||
|
||||
/** 运行状态(运行正常/故障/维修中/已停用)。 */
|
||||
private String status;
|
||||
|
||||
/** 最近巡检日期。 */
|
||||
private String lastInspectDate;
|
||||
|
||||
/** 备注。 */
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getDeviceCode() { return deviceCode; }
|
||||
public void setDeviceCode(String deviceCode) { this.deviceCode = deviceCode; }
|
||||
public String getDeviceName() { return deviceName; }
|
||||
public void setDeviceName(String deviceName) { this.deviceName = deviceName; }
|
||||
public String getDeviceType() { return deviceType; }
|
||||
public void setDeviceType(String deviceType) { this.deviceType = deviceType; }
|
||||
public String getLocation() { return location; }
|
||||
public void setLocation(String location) { this.location = location; }
|
||||
public String getInstallDate() { return installDate; }
|
||||
public void setInstallDate(String installDate) { this.installDate = installDate; }
|
||||
public String getBrandModel() { return brandModel; }
|
||||
public void setBrandModel(String brandModel) { this.brandModel = brandModel; }
|
||||
public String getIpAddress() { return ipAddress; }
|
||||
public void setIpAddress(String ipAddress) { this.ipAddress = ipAddress; }
|
||||
public Integer getStoreDays() { return storeDays; }
|
||||
public void setStoreDays(Integer storeDays) { this.storeDays = storeDays; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public String getLastInspectDate() { return lastInspectDate; }
|
||||
public void setLastInspectDate(String lastInspectDate) { this.lastInspectDate = lastInspectDate; }
|
||||
public String getRemark() { return remark; }
|
||||
public void setRemark(String remark) { this.remark = remark; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·保洁排班。制定保洁人员每日/每周工作区域与频次,签到打卡,记录完成情况。
|
||||
* status: 待执行 / 执行中 / 已完成 / 已缺勤
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_cleaning_schedule")
|
||||
public class AdminCleaningSchedule {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 保洁人员姓名。 */
|
||||
private String cleaner;
|
||||
|
||||
/** 工作区域(办公区/公共卫生间/大厅/停车场等)。 */
|
||||
private String area;
|
||||
|
||||
/** 排班日期(YYYY-MM-DD)。 */
|
||||
private String scheduleDate;
|
||||
|
||||
/** 时间段(上午/下午/全天/其他)。 */
|
||||
private String shift;
|
||||
|
||||
/** 清洁频次(每日/每周/按需)。 */
|
||||
private String frequency;
|
||||
|
||||
/** 签到时间(ISO 时间戳)。 */
|
||||
private Instant checkinAt;
|
||||
|
||||
/** 完成时间(ISO 时间戳)。 */
|
||||
private Instant completedAt;
|
||||
|
||||
/** 完成情况说明。 */
|
||||
private String completionNote;
|
||||
|
||||
/** 状态:待执行 / 执行中 / 已完成 / 已缺勤。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getCleaner() { return cleaner; }
|
||||
public void setCleaner(String cleaner) { this.cleaner = cleaner; }
|
||||
public String getArea() { return area; }
|
||||
public void setArea(String area) { this.area = area; }
|
||||
public String getScheduleDate() { return scheduleDate; }
|
||||
public void setScheduleDate(String scheduleDate) { this.scheduleDate = scheduleDate; }
|
||||
public String getShift() { return shift; }
|
||||
public void setShift(String shift) { this.shift = shift; }
|
||||
public String getFrequency() { return frequency; }
|
||||
public void setFrequency(String frequency) { this.frequency = frequency; }
|
||||
public Instant getCheckinAt() { return checkinAt; }
|
||||
public void setCheckinAt(Instant checkinAt) { this.checkinAt = checkinAt; }
|
||||
public Instant getCompletedAt() { return completedAt; }
|
||||
public void setCompletedAt(Instant completedAt) { this.completedAt = completedAt; }
|
||||
public String getCompletionNote() { return completionNote; }
|
||||
public void setCompletionNote(String completionNote) { this.completionNote = completionNote; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·保洁专属清洁物资台账(清洁剂/工具/垃圾桶等)。
|
||||
* 独立于 AdminSupply 通用物资,聚焦保洁域库存与领用预警,可由保洁排班联动触发领用记录。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_cleaning_supply")
|
||||
public class AdminCleaningSupply {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 物料编号(自动生成,格式 CS-XXXX)。 */
|
||||
@Column(nullable = false, unique = true)
|
||||
private String supplyCode;
|
||||
|
||||
/** 物料名称(如 84消毒液、拖把头、垃圾袋50L)。 */
|
||||
@Column(nullable = false)
|
||||
private String supplyName;
|
||||
|
||||
/** 分类:清洁剂 / 清洁工具 / 垃圾桶/袋 / 消耗品 / 防护用品 / 其他。 */
|
||||
@Column(nullable = false)
|
||||
private String supplyCategory;
|
||||
|
||||
/** 规格(如 5L/桶、10只/包)。 */
|
||||
private String spec;
|
||||
|
||||
/** 单位。 */
|
||||
private String unitName;
|
||||
|
||||
/** 参考单价(BigDecimal)。 */
|
||||
@Column(precision = 12, scale = 2)
|
||||
private BigDecimal refPrice;
|
||||
|
||||
/** 当前库存数量。 */
|
||||
private Double onHand;
|
||||
|
||||
/** 安全库存(低于此值触发预警)。 */
|
||||
private Double safetyStock;
|
||||
|
||||
/** 存放位置(如 保洁间A、地下室储物间)。 */
|
||||
private String location;
|
||||
|
||||
/** 状态:启用 / 停用。 */
|
||||
@Column(nullable = false)
|
||||
private String supplyStatus;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getSupplyCode() { return supplyCode; }
|
||||
public void setSupplyCode(String supplyCode) { this.supplyCode = supplyCode; }
|
||||
public String getSupplyName() { return supplyName; }
|
||||
public void setSupplyName(String supplyName) { this.supplyName = supplyName; }
|
||||
public String getSupplyCategory() { return supplyCategory; }
|
||||
public void setSupplyCategory(String supplyCategory) { this.supplyCategory = supplyCategory; }
|
||||
public String getSpec() { return spec; }
|
||||
public void setSpec(String spec) { this.spec = spec; }
|
||||
public String getUnitName() { return unitName; }
|
||||
public void setUnitName(String unitName) { this.unitName = unitName; }
|
||||
public BigDecimal getRefPrice() { return refPrice; }
|
||||
public void setRefPrice(BigDecimal refPrice) { this.refPrice = refPrice; }
|
||||
public Double getOnHand() { return onHand; }
|
||||
public void setOnHand(Double onHand) { this.onHand = onHand; }
|
||||
public Double getSafetyStock() { return safetyStock; }
|
||||
public void setSafetyStock(Double safetyStock) { this.safetyStock = safetyStock; }
|
||||
public String getLocation() { return location; }
|
||||
public void setLocation(String location) { this.location = location; }
|
||||
public String getSupplyStatus() { return supplyStatus; }
|
||||
public void setSupplyStatus(String supplyStatus) { this.supplyStatus = supplyStatus; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·保洁清洁物资出入库流水(领用登记,可由保洁排班触发)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_cleaning_supply_move")
|
||||
public class AdminCleaningSupplyMove {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 关联清洁物资 ID。 */
|
||||
@Column(nullable = false)
|
||||
private Long cleaningSupplyId;
|
||||
|
||||
/** 物料名称(冗余快照)。 */
|
||||
private String supplyName;
|
||||
|
||||
/** 方向:入库 / 领用。 */
|
||||
@Column(nullable = false)
|
||||
private String moveDirection;
|
||||
|
||||
/** 数量。 */
|
||||
@Column(nullable = false)
|
||||
private Double moveQty;
|
||||
|
||||
/** 领用人/入库经手人。 */
|
||||
private String handler;
|
||||
|
||||
/** 关联保洁排班 ID(保洁日程联动领用时填写)。 */
|
||||
private Long scheduleId;
|
||||
|
||||
/** 发生日期(yyyy-MM-dd)。 */
|
||||
private String moveDate;
|
||||
|
||||
/** 入库:供应商;领用:领用区域。 */
|
||||
private String source;
|
||||
|
||||
/** 备注。 */
|
||||
@Column(length = 500)
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public Long getCleaningSupplyId() { return cleaningSupplyId; }
|
||||
public void setCleaningSupplyId(Long cleaningSupplyId) { this.cleaningSupplyId = cleaningSupplyId; }
|
||||
public String getSupplyName() { return supplyName; }
|
||||
public void setSupplyName(String supplyName) { this.supplyName = supplyName; }
|
||||
public String getMoveDirection() { return moveDirection; }
|
||||
public void setMoveDirection(String moveDirection) { this.moveDirection = moveDirection; }
|
||||
public Double getMoveQty() { return moveQty; }
|
||||
public void setMoveQty(Double moveQty) { this.moveQty = moveQty; }
|
||||
public String getHandler() { return handler; }
|
||||
public void setHandler(String handler) { this.handler = handler; }
|
||||
public Long getScheduleId() { return scheduleId; }
|
||||
public void setScheduleId(Long scheduleId) { this.scheduleId = scheduleId; }
|
||||
public String getMoveDate() { return moveDate; }
|
||||
public void setMoveDate(String moveDate) { this.moveDate = moveDate; }
|
||||
public String getSource() { return source; }
|
||||
public void setSource(String source) { this.source = source; }
|
||||
public String getRemark() { return remark; }
|
||||
public void setRemark(String remark) { this.remark = remark; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·大型会议物料准备清单(席卡/茶歇/签到表/水/笔记本/宣传品等)。
|
||||
* 每条记录对应某次会议的某一物料项,可独立登记数量、准备状态与负责人。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_conference_material")
|
||||
public class AdminConferenceMaterial {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 关联会议 ID(对应 meeting.id,可选,也可为大型活动标题)。 */
|
||||
private Long meetingId;
|
||||
|
||||
/** 活动/会议名称(meetingId 为空时直接填写)。 */
|
||||
@Column(nullable = false)
|
||||
private String meetingTitle;
|
||||
|
||||
/** 物料类型:席卡 / 茶歇 / 签到表 / 纸笔 / 水 / 宣传品 / 礼品 / 其他。 */
|
||||
@Column(nullable = false)
|
||||
private String materialType;
|
||||
|
||||
/** 物料名称/规格描述。 */
|
||||
private String materialName;
|
||||
|
||||
/** 计划数量。 */
|
||||
private Integer planQty;
|
||||
|
||||
/** 实际准备数量。 */
|
||||
private Integer actualQty;
|
||||
|
||||
/** 单位(套/份/瓶/本/张)。 */
|
||||
private String unitName;
|
||||
|
||||
/** 负责人。 */
|
||||
private String handler;
|
||||
|
||||
/** 准备状态:待准备 / 准备中 / 已就绪 / 缺货。 */
|
||||
@Column(nullable = false)
|
||||
private String prepStatus;
|
||||
|
||||
/** 备注(采购来源/注意事项)。 */
|
||||
@Column(length = 1000)
|
||||
private String remark;
|
||||
|
||||
/** 计划就绪日期(yyyy-MM-dd)。 */
|
||||
private String readyDate;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public Long getMeetingId() { return meetingId; }
|
||||
public void setMeetingId(Long meetingId) { this.meetingId = meetingId; }
|
||||
public String getMeetingTitle() { return meetingTitle; }
|
||||
public void setMeetingTitle(String meetingTitle) { this.meetingTitle = meetingTitle; }
|
||||
public String getMaterialType() { return materialType; }
|
||||
public void setMaterialType(String materialType) { this.materialType = materialType; }
|
||||
public String getMaterialName() { return materialName; }
|
||||
public void setMaterialName(String materialName) { this.materialName = materialName; }
|
||||
public Integer getPlanQty() { return planQty; }
|
||||
public void setPlanQty(Integer planQty) { this.planQty = planQty; }
|
||||
public Integer getActualQty() { return actualQty; }
|
||||
public void setActualQty(Integer actualQty) { this.actualQty = actualQty; }
|
||||
public String getUnitName() { return unitName; }
|
||||
public void setUnitName(String unitName) { this.unitName = unitName; }
|
||||
public String getHandler() { return handler; }
|
||||
public void setHandler(String handler) { this.handler = handler; }
|
||||
public String getPrepStatus() { return prepStatus; }
|
||||
public void setPrepStatus(String prepStatus) { this.prepStatus = prepStatus; }
|
||||
public String getRemark() { return remark; }
|
||||
public void setRemark(String remark) { this.remark = remark; }
|
||||
public String getReadyDate() { return readyDate; }
|
||||
public void setReadyDate(String readyDate) { this.readyDate = readyDate; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·大型会议现场服务人员安排。
|
||||
* 记录每次大型会议的服务岗位分工(迎宾/签到/引导/摄影/速记/茶水/IT支持等)与人员对应关系。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_conference_staff")
|
||||
public class AdminConferenceStaff {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 关联会议 ID(可选)。 */
|
||||
private Long meetingId;
|
||||
|
||||
/** 会议/活动名称。 */
|
||||
@Column(nullable = false)
|
||||
private String meetingTitle;
|
||||
|
||||
/** 服务岗位:迎宾 / 签到 / 引导 / 摄影 / 速记 / 茶水 / IT支持 / 主持协助 / 其他。 */
|
||||
@Column(nullable = false)
|
||||
private String roleType;
|
||||
|
||||
/** 负责人姓名。 */
|
||||
@Column(nullable = false)
|
||||
private String staffName;
|
||||
|
||||
/** 所属部门。 */
|
||||
private String dept;
|
||||
|
||||
/** 联系电话。 */
|
||||
private String phone;
|
||||
|
||||
/** 服务时间段(如 09:00-12:00)。 */
|
||||
private String serviceTime;
|
||||
|
||||
/** 到岗状态:待到岗 / 已到岗 / 缺勤。 */
|
||||
@Column(nullable = false)
|
||||
private String arrivalStatus;
|
||||
|
||||
/** 备注。 */
|
||||
@Column(length = 500)
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public Long getMeetingId() { return meetingId; }
|
||||
public void setMeetingId(Long meetingId) { this.meetingId = meetingId; }
|
||||
public String getMeetingTitle() { return meetingTitle; }
|
||||
public void setMeetingTitle(String meetingTitle) { this.meetingTitle = meetingTitle; }
|
||||
public String getRoleType() { return roleType; }
|
||||
public void setRoleType(String roleType) { this.roleType = roleType; }
|
||||
public String getStaffName() { return staffName; }
|
||||
public void setStaffName(String staffName) { this.staffName = staffName; }
|
||||
public String getDept() { return dept; }
|
||||
public void setDept(String dept) { this.dept = dept; }
|
||||
public String getPhone() { return phone; }
|
||||
public void setPhone(String phone) { this.phone = phone; }
|
||||
public String getServiceTime() { return serviceTime; }
|
||||
public void setServiceTime(String serviceTime) { this.serviceTime = serviceTime; }
|
||||
public String getArrivalStatus() { return arrivalStatus; }
|
||||
public void setArrivalStatus(String arrivalStatus) { this.arrivalStatus = arrivalStatus; }
|
||||
public String getRemark() { return remark; }
|
||||
public void setRemark(String remark) { this.remark = remark; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·公文模板库。
|
||||
*
|
||||
* 状态流转:DRAFT(草稿)→ REVIEW(审核中)→ APPROVED(已发布)→ ARCHIVED(已归档)。
|
||||
* 支持按公文类型(通知/请示/报告/函等)、适用场景、责任部门分类管理。
|
||||
* 版本号字段支持模板修订记录。
|
||||
*
|
||||
* 解决 GAP 2 low:「无专属公文模板管理(正式模板库/审批流+公文归档)」。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_doc_template")
|
||||
public class AdminDocTemplate {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 模板名称,如「关于XXX的通知」。 */
|
||||
@Column(nullable = false, length = 200)
|
||||
private String templateName;
|
||||
|
||||
/** 公文类型:通知/公告/请示/报告/函/会议纪要/批复/决定/规定/办法/合同/协议/其他。 */
|
||||
@Column(nullable = false, length = 50)
|
||||
private String docCategory;
|
||||
|
||||
/** 适用场景描述,如「跨部门协调事项通知」。 */
|
||||
@Column(length = 500)
|
||||
private String scenario;
|
||||
|
||||
/** 责任部门,如「行政办公室」。 */
|
||||
@Column(length = 100)
|
||||
private String ownerDept;
|
||||
|
||||
/** 模板正文内容(含占位符,如【标题】【日期】【责任人】)。 */
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String templateContent;
|
||||
|
||||
/** 格式说明(纸张/字体/页边距/格式要求)。 */
|
||||
@Column(length = 1000)
|
||||
private String formatSpec;
|
||||
|
||||
/** 使用说明。 */
|
||||
@Column(length = 1000)
|
||||
private String usage;
|
||||
|
||||
/** 版本号,如 v1.0/v2.1。 */
|
||||
@Column(nullable = false, length = 20)
|
||||
private String version;
|
||||
|
||||
/**
|
||||
* 状态:DRAFT(草稿)/ REVIEW(审核中)/ APPROVED(已发布)/ ARCHIVED(已归档)。
|
||||
* 注意:status 是 SQLite 保留字,映射到物理列 doc_status。
|
||||
*/
|
||||
@Column(name = "doc_status", nullable = false, length = 20)
|
||||
private String status;
|
||||
|
||||
/** 创建人(displayName)。 */
|
||||
@Column(length = 100)
|
||||
private String createdBy;
|
||||
|
||||
/** 审核人(displayName)。 */
|
||||
@Column(length = 100)
|
||||
private String reviewedBy;
|
||||
|
||||
/** 审核意见。 */
|
||||
@Column(length = 500)
|
||||
private String reviewComment;
|
||||
|
||||
/** 发布人(displayName)。 */
|
||||
@Column(length = 100)
|
||||
private String publishedBy;
|
||||
|
||||
/** 发布时间。 */
|
||||
private Instant publishedAt;
|
||||
|
||||
/** 归档原因。 */
|
||||
@Column(length = 500)
|
||||
private String archiveReason;
|
||||
|
||||
/** 归档时间。 */
|
||||
private Instant archivedAt;
|
||||
|
||||
/** 使用次数(每次基于本模板起草时+1)。 */
|
||||
private int usageCount;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
// ----------------------------------------------------------------
|
||||
// Getters & Setters
|
||||
// ----------------------------------------------------------------
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public String getTemplateName() { return templateName; }
|
||||
public void setTemplateName(String templateName) { this.templateName = templateName; }
|
||||
|
||||
public String getDocCategory() { return docCategory; }
|
||||
public void setDocCategory(String docCategory) { this.docCategory = docCategory; }
|
||||
|
||||
public String getScenario() { return scenario; }
|
||||
public void setScenario(String scenario) { this.scenario = scenario; }
|
||||
|
||||
public String getOwnerDept() { return ownerDept; }
|
||||
public void setOwnerDept(String ownerDept) { this.ownerDept = ownerDept; }
|
||||
|
||||
public String getTemplateContent() { return templateContent; }
|
||||
public void setTemplateContent(String templateContent) { this.templateContent = templateContent; }
|
||||
|
||||
public String getFormatSpec() { return formatSpec; }
|
||||
public void setFormatSpec(String formatSpec) { this.formatSpec = formatSpec; }
|
||||
|
||||
public String getUsage() { return usage; }
|
||||
public void setUsage(String usage) { this.usage = usage; }
|
||||
|
||||
public String getVersion() { return version; }
|
||||
public void setVersion(String version) { this.version = version; }
|
||||
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
|
||||
public String getCreatedBy() { return createdBy; }
|
||||
public void setCreatedBy(String createdBy) { this.createdBy = createdBy; }
|
||||
|
||||
public String getReviewedBy() { return reviewedBy; }
|
||||
public void setReviewedBy(String reviewedBy) { this.reviewedBy = reviewedBy; }
|
||||
|
||||
public String getReviewComment() { return reviewComment; }
|
||||
public void setReviewComment(String reviewComment) { this.reviewComment = reviewComment; }
|
||||
|
||||
public String getPublishedBy() { return publishedBy; }
|
||||
public void setPublishedBy(String publishedBy) { this.publishedBy = publishedBy; }
|
||||
|
||||
public Instant getPublishedAt() { return publishedAt; }
|
||||
public void setPublishedAt(Instant publishedAt) { this.publishedAt = publishedAt; }
|
||||
|
||||
public String getArchiveReason() { return archiveReason; }
|
||||
public void setArchiveReason(String archiveReason) { this.archiveReason = archiveReason; }
|
||||
|
||||
public Instant getArchivedAt() { return archivedAt; }
|
||||
public void setArchivedAt(Instant archivedAt) { this.archivedAt = archivedAt; }
|
||||
|
||||
public int getUsageCount() { return usageCount; }
|
||||
public void setUsageCount(int usageCount) { this.usageCount = usageCount; }
|
||||
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,179 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 宿舍入住/退宿申请(行政/办公室·宿舍管理)。员工在线申请入住(入住时间、预计退宿),
|
||||
* 审批通过后分配房间/床位(占用 +1);退宿时释放床位(占用 -1)并提示检查设施、结清水电费。
|
||||
*
|
||||
* 状态机:待审批 → 已入住 → 已退宿 / 已驳回。assign 分配房间占床,checkout 退宿释床。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_dorm_checkin")
|
||||
public class AdminDormCheckin {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String code;
|
||||
|
||||
/** 入住员工。 */
|
||||
private String employee;
|
||||
|
||||
private String dept;
|
||||
|
||||
/** 联系电话。 */
|
||||
private String phone;
|
||||
|
||||
private String checkinDate;
|
||||
|
||||
/** 预计退宿日期。 */
|
||||
private String expectCheckout;
|
||||
|
||||
/** 实际退宿日期(退宿时回填)。 */
|
||||
private String actualCheckout;
|
||||
|
||||
/** 分配的房间 id(审批分配后回填)。 */
|
||||
private Long roomId;
|
||||
|
||||
private String building;
|
||||
|
||||
private String roomNo;
|
||||
|
||||
/** 待审批 / 已入住 / 已退宿 / 已驳回。 */
|
||||
private String status;
|
||||
|
||||
private String approver;
|
||||
|
||||
/** 退宿设施检查结论。 */
|
||||
private String checkoutNote;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getEmployee() {
|
||||
return employee;
|
||||
}
|
||||
|
||||
public void setEmployee(String employee) {
|
||||
this.employee = employee;
|
||||
}
|
||||
|
||||
public String getDept() {
|
||||
return dept;
|
||||
}
|
||||
|
||||
public void setDept(String dept) {
|
||||
this.dept = dept;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getCheckinDate() {
|
||||
return checkinDate;
|
||||
}
|
||||
|
||||
public void setCheckinDate(String checkinDate) {
|
||||
this.checkinDate = checkinDate;
|
||||
}
|
||||
|
||||
public String getExpectCheckout() {
|
||||
return expectCheckout;
|
||||
}
|
||||
|
||||
public void setExpectCheckout(String expectCheckout) {
|
||||
this.expectCheckout = expectCheckout;
|
||||
}
|
||||
|
||||
public String getActualCheckout() {
|
||||
return actualCheckout;
|
||||
}
|
||||
|
||||
public void setActualCheckout(String actualCheckout) {
|
||||
this.actualCheckout = actualCheckout;
|
||||
}
|
||||
|
||||
public Long getRoomId() {
|
||||
return roomId;
|
||||
}
|
||||
|
||||
public void setRoomId(Long roomId) {
|
||||
this.roomId = roomId;
|
||||
}
|
||||
|
||||
public String getBuilding() {
|
||||
return building;
|
||||
}
|
||||
|
||||
public void setBuilding(String building) {
|
||||
this.building = building;
|
||||
}
|
||||
|
||||
public String getRoomNo() {
|
||||
return roomNo;
|
||||
}
|
||||
|
||||
public void setRoomNo(String roomNo) {
|
||||
this.roomNo = roomNo;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getApprover() {
|
||||
return approver;
|
||||
}
|
||||
|
||||
public void setApprover(String approver) {
|
||||
this.approver = approver;
|
||||
}
|
||||
|
||||
public String getCheckoutNote() {
|
||||
return checkoutNote;
|
||||
}
|
||||
|
||||
public void setCheckoutNote(String checkoutNote) {
|
||||
this.checkoutNote = checkoutNote;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·宿舍定期巡检。记录卫生/安全巡检评分、隐患、整改跟踪。
|
||||
* status: 待巡检 / 巡检中 / 待整改 / 整改完成 / 已关闭
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_dorm_inspection")
|
||||
public class AdminDormInspection {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 巡检单号,自动生成。 */
|
||||
private String code;
|
||||
|
||||
/** 巡检类型(卫生巡检/安全巡检)。 */
|
||||
private String inspectionType;
|
||||
|
||||
/** 巡检楼栋(null 表示全楼)。 */
|
||||
private String building;
|
||||
|
||||
/** 巡检人。 */
|
||||
private String inspector;
|
||||
|
||||
/** 计划巡检日期(YYYY-MM-DD)。 */
|
||||
private String planDate;
|
||||
|
||||
/** 实际巡检日期(YYYY-MM-DD)。 */
|
||||
private String actualDate;
|
||||
|
||||
/** 卫生评分(0-100)。 */
|
||||
private Integer score;
|
||||
|
||||
/** 发现隐患描述(多条逗号分隔或 JSON)。 */
|
||||
private String hazards;
|
||||
|
||||
/** 照片(逗号分隔 URL)。 */
|
||||
private String photos;
|
||||
private String grade;
|
||||
|
||||
/** 整改要求。 */
|
||||
private String rectifyRequirement;
|
||||
|
||||
/** 整改截止日期(YYYY-MM-DD)。 */
|
||||
private String rectifyDeadline;
|
||||
|
||||
/** 整改人。 */
|
||||
private String rectifyBy;
|
||||
|
||||
/** 整改说明。 */
|
||||
private String rectifyNote;
|
||||
|
||||
/** 整改完成日期(YYYY-MM-DD)。 */
|
||||
private String rectifiedDate;
|
||||
|
||||
/** 状态:待巡检 / 巡检中 / 待整改 / 整改完成 / 已关闭。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getCode() { return code; }
|
||||
public void setCode(String code) { this.code = code; }
|
||||
public String getInspectionType() { return inspectionType; }
|
||||
public void setInspectionType(String inspectionType) { this.inspectionType = inspectionType; }
|
||||
public String getBuilding() { return building; }
|
||||
public void setBuilding(String building) { this.building = building; }
|
||||
public String getInspector() { return inspector; }
|
||||
public void setInspector(String inspector) { this.inspector = inspector; }
|
||||
public String getPlanDate() { return planDate; }
|
||||
public void setPlanDate(String planDate) { this.planDate = planDate; }
|
||||
public String getActualDate() { return actualDate; }
|
||||
public void setActualDate(String actualDate) { this.actualDate = actualDate; }
|
||||
public Integer getScore() { return score; }
|
||||
public void setScore(Integer score) { this.score = score; }
|
||||
public String getHazards() { return hazards; }
|
||||
public void setHazards(String hazards) { this.hazards = hazards; }
|
||||
public String getGrade() { return grade; }
|
||||
public void setGrade(String grade) { this.grade = grade; }
|
||||
public String getPhotos() { return photos; }
|
||||
public void setPhotos(String photos) { this.photos = photos; }
|
||||
public String getRectifyRequirement() { return rectifyRequirement; }
|
||||
public void setRectifyRequirement(String rectifyRequirement) { this.rectifyRequirement = rectifyRequirement; }
|
||||
public String getRectifyDeadline() { return rectifyDeadline; }
|
||||
public void setRectifyDeadline(String rectifyDeadline) { this.rectifyDeadline = rectifyDeadline; }
|
||||
public String getRectifyBy() { return rectifyBy; }
|
||||
public void setRectifyBy(String rectifyBy) { this.rectifyBy = rectifyBy; }
|
||||
public String getRectifyNote() { return rectifyNote; }
|
||||
public void setRectifyNote(String rectifyNote) { this.rectifyNote = rectifyNote; }
|
||||
public String getRectifiedDate() { return rectifiedDate; }
|
||||
public void setRectifiedDate(String rectifiedDate) { this.rectifiedDate = rectifiedDate; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·宿舍维修报修。住宿员工在线报修故障 → 派工至维修人员 → 维修确认闭环。
|
||||
* status: 待派工 / 维修中 / 已完成 / 已取消
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_dorm_repair")
|
||||
public class AdminDormRepair {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 报修单号,自动生成。 */
|
||||
private String code;
|
||||
|
||||
/** 关联宿舍房间 ID。 */
|
||||
private Long roomId;
|
||||
|
||||
/** 宿舍楼(冗余,便于展示)。 */
|
||||
private String building;
|
||||
|
||||
/** 房间号(冗余)。 */
|
||||
private String roomNo;
|
||||
|
||||
/** 报修人姓名。 */
|
||||
private String reporter;
|
||||
|
||||
/** 故障类型(水电/门窗/空调/热水器/其他)。 */
|
||||
private String faultType;
|
||||
|
||||
/** 故障描述。 */
|
||||
private String description;
|
||||
|
||||
/** 照片附件(逗号分隔 URL)。 */
|
||||
private String photos;
|
||||
|
||||
/** 紧急程度(紧急/一般/低)。 */
|
||||
private String urgency;
|
||||
|
||||
/** 派工维修人员。 */
|
||||
private String assignedTo;
|
||||
|
||||
/** 派工时间。 */
|
||||
private Instant assignedAt;
|
||||
|
||||
/** 维修说明。 */
|
||||
private String repairNote;
|
||||
|
||||
/** 完成时间。 */
|
||||
private Instant completedAt;
|
||||
|
||||
/** 报修人满意度评价(5星制,0=未评)。 */
|
||||
private Integer satisfaction;
|
||||
|
||||
/** 状态:待派工 / 维修中 / 已完成 / 已取消。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getCode() { return code; }
|
||||
public void setCode(String code) { this.code = code; }
|
||||
public Long getRoomId() { return roomId; }
|
||||
public void setRoomId(Long roomId) { this.roomId = roomId; }
|
||||
public String getBuilding() { return building; }
|
||||
public void setBuilding(String building) { this.building = building; }
|
||||
public String getRoomNo() { return roomNo; }
|
||||
public void setRoomNo(String roomNo) { this.roomNo = roomNo; }
|
||||
public String getReporter() { return reporter; }
|
||||
public void setReporter(String reporter) { this.reporter = reporter; }
|
||||
public String getFaultType() { return faultType; }
|
||||
public void setFaultType(String faultType) { this.faultType = faultType; }
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
public String getPhotos() { return photos; }
|
||||
public void setPhotos(String photos) { this.photos = photos; }
|
||||
public String getUrgency() { return urgency; }
|
||||
public void setUrgency(String urgency) { this.urgency = urgency; }
|
||||
public String getAssignedTo() { return assignedTo; }
|
||||
public void setAssignedTo(String assignedTo) { this.assignedTo = assignedTo; }
|
||||
public Instant getAssignedAt() { return assignedAt; }
|
||||
public void setAssignedAt(Instant assignedAt) { this.assignedAt = assignedAt; }
|
||||
public String getRepairNote() { return repairNote; }
|
||||
public void setRepairNote(String repairNote) { this.repairNote = repairNote; }
|
||||
public Instant getCompletedAt() { return completedAt; }
|
||||
public void setCompletedAt(Instant completedAt) { this.completedAt = completedAt; }
|
||||
public Integer getSatisfaction() { return satisfaction; }
|
||||
public void setSatisfaction(Integer satisfaction) { this.satisfaction = satisfaction; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,116 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 宿舍资源台账(行政/办公室·宿舍管理)。记录宿舍楼、房间号、床位总数、设施、当前入住数 occupied。
|
||||
* occupied 由入住申请审批分配 / 退宿({@link AdminDormCheckin})在控制器内级联自动增减。
|
||||
* status 据 occupied 与 beds 自动派生展示:空置(0)/ 部分入住 / 已满。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_dorm_room")
|
||||
public class AdminDormRoom {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String building;
|
||||
|
||||
private String roomNo;
|
||||
|
||||
/** 床位总数。 */
|
||||
private Integer beds = 0;
|
||||
|
||||
/** 当前已入住人数(级联维护)。 */
|
||||
private Integer occupied = 0;
|
||||
|
||||
/** 设施(空调、热水器等)。 */
|
||||
private String facilities;
|
||||
|
||||
/** 房间用途/性别(男/女/混住)。 */
|
||||
private String roomType;
|
||||
|
||||
/** 启用 / 停用(停用不再分配)。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getBuilding() {
|
||||
return building;
|
||||
}
|
||||
|
||||
public void setBuilding(String building) {
|
||||
this.building = building;
|
||||
}
|
||||
|
||||
public String getRoomNo() {
|
||||
return roomNo;
|
||||
}
|
||||
|
||||
public void setRoomNo(String roomNo) {
|
||||
this.roomNo = roomNo;
|
||||
}
|
||||
|
||||
public Integer getBeds() {
|
||||
return beds;
|
||||
}
|
||||
|
||||
public void setBeds(Integer beds) {
|
||||
this.beds = beds;
|
||||
}
|
||||
|
||||
public Integer getOccupied() {
|
||||
return occupied;
|
||||
}
|
||||
|
||||
public void setOccupied(Integer occupied) {
|
||||
this.occupied = occupied;
|
||||
}
|
||||
|
||||
public String getFacilities() {
|
||||
return facilities;
|
||||
}
|
||||
|
||||
public void setFacilities(String facilities) {
|
||||
this.facilities = facilities;
|
||||
}
|
||||
|
||||
public String getRoomType() {
|
||||
return roomType;
|
||||
}
|
||||
|
||||
public void setRoomType(String roomType) {
|
||||
this.roomType = roomType;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,223 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 宿舍水电费账单(行政/办公室·宿舍管理)。每月录入水/电表起止读数与单价,
|
||||
* 系统自动算用量与费用:电费 =(elecEnd-elecStart)×elecPrice,水费 同理,合计 totalAmount。
|
||||
* settled 标记是否已结清(从工资扣除或在线缴费后置已结清)。
|
||||
* 读数为 double(用量口径),单价/费用为金额(BigDecimal)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_dorm_utility")
|
||||
public class AdminDormUtility {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private Long roomId;
|
||||
|
||||
private String building;
|
||||
|
||||
private String roomNo;
|
||||
|
||||
/** 账期 YYYY-MM。 */
|
||||
private String period;
|
||||
|
||||
private Double elecStart = 0d;
|
||||
|
||||
private Double elecEnd = 0d;
|
||||
|
||||
/** 电单价(元/度)。 */
|
||||
private BigDecimal elecPrice = BigDecimal.ZERO;
|
||||
|
||||
private Double waterStart = 0d;
|
||||
|
||||
private Double waterEnd = 0d;
|
||||
|
||||
/** 水单价(元/吨)。 */
|
||||
private BigDecimal waterPrice = BigDecimal.ZERO;
|
||||
|
||||
/** 用电量(度,自动算)。 */
|
||||
private Double elecUsage = 0d;
|
||||
|
||||
/** 用水量(吨,自动算)。 */
|
||||
private Double waterUsage = 0d;
|
||||
|
||||
/** 电费(自动算)。 */
|
||||
private BigDecimal elecAmount = BigDecimal.ZERO;
|
||||
|
||||
/** 水费(自动算)。 */
|
||||
private BigDecimal waterAmount = BigDecimal.ZERO;
|
||||
|
||||
/** 合计费用(自动算)。 */
|
||||
private BigDecimal totalAmount = BigDecimal.ZERO;
|
||||
|
||||
/** 结算方式:工资扣除 / 在线缴费。 */
|
||||
private String settleMethod;
|
||||
|
||||
/** 是否已结清。 */
|
||||
private Boolean settled = Boolean.FALSE;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getRoomId() {
|
||||
return roomId;
|
||||
}
|
||||
|
||||
public void setRoomId(Long roomId) {
|
||||
this.roomId = roomId;
|
||||
}
|
||||
|
||||
public String getBuilding() {
|
||||
return building;
|
||||
}
|
||||
|
||||
public void setBuilding(String building) {
|
||||
this.building = building;
|
||||
}
|
||||
|
||||
public String getRoomNo() {
|
||||
return roomNo;
|
||||
}
|
||||
|
||||
public void setRoomNo(String roomNo) {
|
||||
this.roomNo = roomNo;
|
||||
}
|
||||
|
||||
public String getPeriod() {
|
||||
return period;
|
||||
}
|
||||
|
||||
public void setPeriod(String period) {
|
||||
this.period = period;
|
||||
}
|
||||
|
||||
public Double getElecStart() {
|
||||
return elecStart;
|
||||
}
|
||||
|
||||
public void setElecStart(Double elecStart) {
|
||||
this.elecStart = elecStart;
|
||||
}
|
||||
|
||||
public Double getElecEnd() {
|
||||
return elecEnd;
|
||||
}
|
||||
|
||||
public void setElecEnd(Double elecEnd) {
|
||||
this.elecEnd = elecEnd;
|
||||
}
|
||||
|
||||
public BigDecimal getElecPrice() {
|
||||
return elecPrice;
|
||||
}
|
||||
|
||||
public void setElecPrice(BigDecimal elecPrice) {
|
||||
this.elecPrice = elecPrice;
|
||||
}
|
||||
|
||||
public Double getWaterStart() {
|
||||
return waterStart;
|
||||
}
|
||||
|
||||
public void setWaterStart(Double waterStart) {
|
||||
this.waterStart = waterStart;
|
||||
}
|
||||
|
||||
public Double getWaterEnd() {
|
||||
return waterEnd;
|
||||
}
|
||||
|
||||
public void setWaterEnd(Double waterEnd) {
|
||||
this.waterEnd = waterEnd;
|
||||
}
|
||||
|
||||
public BigDecimal getWaterPrice() {
|
||||
return waterPrice;
|
||||
}
|
||||
|
||||
public void setWaterPrice(BigDecimal waterPrice) {
|
||||
this.waterPrice = waterPrice;
|
||||
}
|
||||
|
||||
public Double getElecUsage() {
|
||||
return elecUsage;
|
||||
}
|
||||
|
||||
public void setElecUsage(Double elecUsage) {
|
||||
this.elecUsage = elecUsage;
|
||||
}
|
||||
|
||||
public Double getWaterUsage() {
|
||||
return waterUsage;
|
||||
}
|
||||
|
||||
public void setWaterUsage(Double waterUsage) {
|
||||
this.waterUsage = waterUsage;
|
||||
}
|
||||
|
||||
public BigDecimal getElecAmount() {
|
||||
return elecAmount;
|
||||
}
|
||||
|
||||
public void setElecAmount(BigDecimal elecAmount) {
|
||||
this.elecAmount = elecAmount;
|
||||
}
|
||||
|
||||
public BigDecimal getWaterAmount() {
|
||||
return waterAmount;
|
||||
}
|
||||
|
||||
public void setWaterAmount(BigDecimal waterAmount) {
|
||||
this.waterAmount = waterAmount;
|
||||
}
|
||||
|
||||
public BigDecimal getTotalAmount() {
|
||||
return totalAmount;
|
||||
}
|
||||
|
||||
public void setTotalAmount(BigDecimal totalAmount) {
|
||||
this.totalAmount = totalAmount;
|
||||
}
|
||||
|
||||
public String getSettleMethod() {
|
||||
return settleMethod;
|
||||
}
|
||||
|
||||
public void setSettleMethod(String settleMethod) {
|
||||
this.settleMethod = settleMethod;
|
||||
}
|
||||
|
||||
public Boolean getSettled() {
|
||||
return settled;
|
||||
}
|
||||
|
||||
public void setSettled(Boolean settled) {
|
||||
this.settled = settled;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·大型会议设备调试记录(投影仪/音响/麦克风/视频会议终端等)。
|
||||
* 记录每次会议前设备调试结果,异常时留存处理过程。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_equip_setup")
|
||||
public class AdminEquipSetup {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 关联会议 ID(可选)。 */
|
||||
private Long meetingId;
|
||||
|
||||
/** 会议/活动名称。 */
|
||||
@Column(nullable = false)
|
||||
private String meetingTitle;
|
||||
|
||||
/** 设备类型:投影仪 / 音响 / 麦克风 / 视频终端 / 屏幕 / 网络 / 灯光 / 其他。 */
|
||||
@Column(nullable = false)
|
||||
private String equipType;
|
||||
|
||||
/** 设备型号/编号。 */
|
||||
private String equipModel;
|
||||
|
||||
/** 调试人员。 */
|
||||
private String technician;
|
||||
|
||||
/** 调试日期(yyyy-MM-dd)。 */
|
||||
private String setupDate;
|
||||
|
||||
/** 调试结果:正常 / 异常 / 待处理。 */
|
||||
@Column(nullable = false)
|
||||
private String setupResult;
|
||||
|
||||
/** 异常描述(调试结果=异常时必填)。 */
|
||||
@Column(length = 2000)
|
||||
private String issueDesc;
|
||||
|
||||
/** 处理措施。 */
|
||||
@Column(length = 2000)
|
||||
private String resolution;
|
||||
|
||||
/** 最终状态:正常 / 故障未修 / 已更换备机。 */
|
||||
private String finalStatus;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public Long getMeetingId() { return meetingId; }
|
||||
public void setMeetingId(Long meetingId) { this.meetingId = meetingId; }
|
||||
public String getMeetingTitle() { return meetingTitle; }
|
||||
public void setMeetingTitle(String meetingTitle) { this.meetingTitle = meetingTitle; }
|
||||
public String getEquipType() { return equipType; }
|
||||
public void setEquipType(String equipType) { this.equipType = equipType; }
|
||||
public String getEquipModel() { return equipModel; }
|
||||
public void setEquipModel(String equipModel) { this.equipModel = equipModel; }
|
||||
public String getTechnician() { return technician; }
|
||||
public void setTechnician(String technician) { this.technician = technician; }
|
||||
public String getSetupDate() { return setupDate; }
|
||||
public void setSetupDate(String setupDate) { this.setupDate = setupDate; }
|
||||
public String getSetupResult() { return setupResult; }
|
||||
public void setSetupResult(String setupResult) { this.setupResult = setupResult; }
|
||||
public String getIssueDesc() { return issueDesc; }
|
||||
public void setIssueDesc(String issueDesc) { this.issueDesc = issueDesc; }
|
||||
public String getResolution() { return resolution; }
|
||||
public void setResolution(String resolution) { this.resolution = resolution; }
|
||||
public String getFinalStatus() { return finalStatus; }
|
||||
public void setFinalStatus(String finalStatus) { this.finalStatus = finalStatus; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·快递收发台账(收件 + 寄件二合一)。
|
||||
* 收件:门卫登记收件人/快递公司/单号 → 自动通知收件人取件。
|
||||
* 寄件:员工在线申请 → 审批(选填)→ 打印面单 → 费用归集部门。
|
||||
* direction: 收件 / 寄件
|
||||
* status(收件): 待取件 / 已取件 / 已退回
|
||||
* status(寄件): 草稿 / 待审批 / 已批准 / 已寄出 / 已驳回
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_express")
|
||||
public class AdminExpress {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 快递单号(登记单号)。 */
|
||||
private String code;
|
||||
|
||||
/** 方向:收件 / 寄件。 */
|
||||
private String direction;
|
||||
|
||||
/** 快递公司(顺丰/EMS/圆通/中通/京东/其他)。 */
|
||||
private String carrier;
|
||||
|
||||
/** 运单号(快递公司单号)。 */
|
||||
private String trackingNo;
|
||||
|
||||
/** 收件人(收件场景:目标员工;寄件场景:对方收件人)。 */
|
||||
private String recipient;
|
||||
|
||||
/** 收件人部门。 */
|
||||
private String recipientDept;
|
||||
|
||||
/** 收件人联系电话。 */
|
||||
private String recipientPhone;
|
||||
|
||||
/** 收件地址(寄件用)。 */
|
||||
private String recipientAddress;
|
||||
|
||||
/** 寄件人(寄件场景:申请员工)。 */
|
||||
private String sender;
|
||||
|
||||
/** 寄件人部门。 */
|
||||
private String senderDept;
|
||||
|
||||
/** 物品描述。 */
|
||||
private String itemDesc;
|
||||
|
||||
/** 重量(kg,寄件用)。 */
|
||||
private Double weight;
|
||||
|
||||
/** 快递费用(元,寄件用)。 */
|
||||
private BigDecimal freightCost;
|
||||
|
||||
/** 费用归集部门(寄件)。 */
|
||||
private String costDept;
|
||||
|
||||
/** 登记人/门卫(收件场景)。 */
|
||||
private String registrar;
|
||||
|
||||
/** 登记/签收时间。 */
|
||||
private Instant registeredAt;
|
||||
|
||||
/** 取件/寄出时间。 */
|
||||
private Instant pickedAt;
|
||||
|
||||
/** 通知发送时间(收件到件通知)。 */
|
||||
private Instant notifiedAt;
|
||||
|
||||
/** 备注。 */
|
||||
private String remark;
|
||||
|
||||
/** 状态(见类注释)。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getCode() { return code; }
|
||||
public void setCode(String code) { this.code = code; }
|
||||
public String getDirection() { return direction; }
|
||||
public void setDirection(String direction) { this.direction = direction; }
|
||||
public String getCarrier() { return carrier; }
|
||||
public void setCarrier(String carrier) { this.carrier = carrier; }
|
||||
public String getTrackingNo() { return trackingNo; }
|
||||
public void setTrackingNo(String trackingNo) { this.trackingNo = trackingNo; }
|
||||
public String getRecipient() { return recipient; }
|
||||
public void setRecipient(String recipient) { this.recipient = recipient; }
|
||||
public String getRecipientDept() { return recipientDept; }
|
||||
public void setRecipientDept(String recipientDept) { this.recipientDept = recipientDept; }
|
||||
public String getRecipientPhone() { return recipientPhone; }
|
||||
public void setRecipientPhone(String recipientPhone) { this.recipientPhone = recipientPhone; }
|
||||
public String getRecipientAddress() { return recipientAddress; }
|
||||
public void setRecipientAddress(String recipientAddress) { this.recipientAddress = recipientAddress; }
|
||||
public String getSender() { return sender; }
|
||||
public void setSender(String sender) { this.sender = sender; }
|
||||
public String getSenderDept() { return senderDept; }
|
||||
public void setSenderDept(String senderDept) { this.senderDept = senderDept; }
|
||||
public String getItemDesc() { return itemDesc; }
|
||||
public void setItemDesc(String itemDesc) { this.itemDesc = itemDesc; }
|
||||
public Double getWeight() { return weight; }
|
||||
public void setWeight(Double weight) { this.weight = weight; }
|
||||
public BigDecimal getFreightCost() { return freightCost; }
|
||||
public void setFreightCost(BigDecimal freightCost) { this.freightCost = freightCost; }
|
||||
public String getCostDept() { return costDept; }
|
||||
public void setCostDept(String costDept) { this.costDept = costDept; }
|
||||
public String getRegistrar() { return registrar; }
|
||||
public void setRegistrar(String registrar) { this.registrar = registrar; }
|
||||
public Instant getRegisteredAt() { return registeredAt; }
|
||||
public void setRegisteredAt(Instant registeredAt) { this.registeredAt = registeredAt; }
|
||||
public Instant getPickedAt() { return pickedAt; }
|
||||
public void setPickedAt(Instant pickedAt) { this.pickedAt = pickedAt; }
|
||||
public Instant getNotifiedAt() { return notifiedAt; }
|
||||
public void setNotifiedAt(Instant notifiedAt) { this.notifiedAt = notifiedAt; }
|
||||
public String getRemark() { return remark; }
|
||||
public void setRemark(String remark) { this.remark = remark; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,100 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·录像调阅申请(安保与门禁 PARTIAL 缺口补全)。
|
||||
* 调阅申请 → 审批 → 已调阅 三步审批流闭环。
|
||||
* status: 待审批 / 已批准 / 已拒绝 / 已调阅
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_footage_request")
|
||||
public class AdminFootageRequest {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 申请编号(自动生成)。 */
|
||||
private String code;
|
||||
|
||||
/** 申请人。 */
|
||||
private String applicant;
|
||||
|
||||
/** 申请人部门。 */
|
||||
private String dept;
|
||||
|
||||
/** 申请用途(事故调查/纠纷取证/安全稽查/其他)。 */
|
||||
private String purpose;
|
||||
|
||||
/** 涉及设备(关联 AdminCctvDevice.deviceCode 或自由填写)。 */
|
||||
private String deviceDesc;
|
||||
|
||||
/** 调阅时间段 - 开始(YYYY-MM-DD HH:mm)。 */
|
||||
private String footageStart;
|
||||
|
||||
/** 调阅时间段 - 结束(YYYY-MM-DD HH:mm)。 */
|
||||
private String footageEnd;
|
||||
|
||||
/** 申请日期(YYYY-MM-DD)。 */
|
||||
private String applyDate;
|
||||
|
||||
/** 审批人。 */
|
||||
private String approver;
|
||||
|
||||
/** 审批意见。 */
|
||||
private String approvalComment;
|
||||
|
||||
/** 审批时间。 */
|
||||
private Instant approvalTime;
|
||||
|
||||
/** 实际调阅时间。 */
|
||||
private Instant viewTime;
|
||||
|
||||
/** 调阅操作员(负责提供录像的人员)。 */
|
||||
private String operator;
|
||||
|
||||
/** 状态:待审批 / 已批准 / 已拒绝 / 已调阅。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getCode() { return code; }
|
||||
public void setCode(String code) { this.code = code; }
|
||||
public String getApplicant() { return applicant; }
|
||||
public void setApplicant(String applicant) { this.applicant = applicant; }
|
||||
public String getDept() { return dept; }
|
||||
public void setDept(String dept) { this.dept = dept; }
|
||||
public String getPurpose() { return purpose; }
|
||||
public void setPurpose(String purpose) { this.purpose = purpose; }
|
||||
public String getDeviceDesc() { return deviceDesc; }
|
||||
public void setDeviceDesc(String deviceDesc) { this.deviceDesc = deviceDesc; }
|
||||
public String getFootageStart() { return footageStart; }
|
||||
public void setFootageStart(String footageStart) { this.footageStart = footageStart; }
|
||||
public String getFootageEnd() { return footageEnd; }
|
||||
public void setFootageEnd(String footageEnd) { this.footageEnd = footageEnd; }
|
||||
public String getApplyDate() { return applyDate; }
|
||||
public void setApplyDate(String applyDate) { this.applyDate = applyDate; }
|
||||
public String getApprover() { return approver; }
|
||||
public void setApprover(String approver) { this.approver = approver; }
|
||||
public String getApprovalComment() { return approvalComment; }
|
||||
public void setApprovalComment(String approvalComment) { this.approvalComment = approvalComment; }
|
||||
public Instant getApprovalTime() { return approvalTime; }
|
||||
public void setApprovalTime(Instant approvalTime) { this.approvalTime = approvalTime; }
|
||||
public Instant getViewTime() { return viewTime; }
|
||||
public void setViewTime(Instant viewTime) { this.viewTime = viewTime; }
|
||||
public String getOperator() { return operator; }
|
||||
public void setOperator(String operator) { this.operator = operator; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·绿化养护台账。记录绿植租摆、浇水/施肥/修剪计划、病虫害处理。
|
||||
* status: 正常 / 待养护 / 病虫害处理中 / 已报废
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_greenery")
|
||||
public class AdminGreenery {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 植物名称。 */
|
||||
private String plantName;
|
||||
|
||||
/** 摆放位置(区域/楼层/具体位置)。 */
|
||||
private String location;
|
||||
|
||||
/** 来源(自购/租摆)。 */
|
||||
private String source;
|
||||
|
||||
/** 租摆供应商。 */
|
||||
private String supplier;
|
||||
|
||||
/** 租摆开始日期(YYYY-MM-DD)。 */
|
||||
private String rentalStart;
|
||||
|
||||
/** 租摆到期日期(YYYY-MM-DD)。 */
|
||||
private String rentalEnd;
|
||||
|
||||
/** 浇水频率(如:每天/隔天/每周)。 */
|
||||
private String waterFreq;
|
||||
|
||||
/** 最近浇水日期(YYYY-MM-DD)。 */
|
||||
private String lastWatered;
|
||||
|
||||
/** 施肥频率(如:每月/每季度)。 */
|
||||
private String fertFreq;
|
||||
|
||||
/** 最近施肥日期(YYYY-MM-DD)。 */
|
||||
private String lastFertilized;
|
||||
|
||||
/** 最近修剪日期(YYYY-MM-DD)。 */
|
||||
private String lastPruned;
|
||||
|
||||
/** 病虫害描述。 */
|
||||
private String pestNote;
|
||||
|
||||
/** 病虫害处理记录。 */
|
||||
private String pestHandleNote;
|
||||
|
||||
/** 养护负责人。 */
|
||||
private String maintainedBy;
|
||||
|
||||
/** 状态:正常 / 待养护 / 病虫害处理中 / 已报废。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getPlantName() { return plantName; }
|
||||
public void setPlantName(String plantName) { this.plantName = plantName; }
|
||||
public String getLocation() { return location; }
|
||||
public void setLocation(String location) { this.location = location; }
|
||||
public String getSource() { return source; }
|
||||
public void setSource(String source) { this.source = source; }
|
||||
public String getSupplier() { return supplier; }
|
||||
public void setSupplier(String supplier) { this.supplier = supplier; }
|
||||
public String getRentalStart() { return rentalStart; }
|
||||
public void setRentalStart(String rentalStart) { this.rentalStart = rentalStart; }
|
||||
public String getRentalEnd() { return rentalEnd; }
|
||||
public void setRentalEnd(String rentalEnd) { this.rentalEnd = rentalEnd; }
|
||||
public String getWaterFreq() { return waterFreq; }
|
||||
public void setWaterFreq(String waterFreq) { this.waterFreq = waterFreq; }
|
||||
public String getLastWatered() { return lastWatered; }
|
||||
public void setLastWatered(String lastWatered) { this.lastWatered = lastWatered; }
|
||||
public String getFertFreq() { return fertFreq; }
|
||||
public void setFertFreq(String fertFreq) { this.fertFreq = fertFreq; }
|
||||
public String getLastFertilized() { return lastFertilized; }
|
||||
public void setLastFertilized(String lastFertilized) { this.lastFertilized = lastFertilized; }
|
||||
public String getLastPruned() { return lastPruned; }
|
||||
public void setLastPruned(String lastPruned) { this.lastPruned = lastPruned; }
|
||||
public String getPestNote() { return pestNote; }
|
||||
public void setPestNote(String pestNote) { this.pestNote = pestNote; }
|
||||
public String getPestHandleNote() { return pestHandleNote; }
|
||||
public void setPestHandleNote(String pestHandleNote) { this.pestHandleNote = pestHandleNote; }
|
||||
public String getMaintainedBy() { return maintainedBy; }
|
||||
public void setMaintainedBy(String maintainedBy) { this.maintainedBy = maintainedBy; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·卫生巡检。管理人员巡检办公区/公共区域卫生,打分、拍照上传,不合格项立即整改跟踪。
|
||||
* status: 待整改 / 整改中 / 已关闭
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_hygiene_inspection")
|
||||
public class AdminHygieneInspection {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 巡检人。 */
|
||||
private String inspector;
|
||||
|
||||
/** 巡检区域。 */
|
||||
private String area;
|
||||
|
||||
/** 巡检日期(YYYY-MM-DD)。 */
|
||||
private String inspectDate;
|
||||
|
||||
/** 卫生评分(0-100)。 */
|
||||
private Integer score;
|
||||
|
||||
/** 评级(优秀/良好/一般/不合格)。 */
|
||||
private String grade;
|
||||
|
||||
/** 不合格项描述(可多项,JSON 字符串或换行分隔)。 */
|
||||
private String issues;
|
||||
|
||||
/** 照片(逗号分隔 URL)。 */
|
||||
private String photos;
|
||||
|
||||
/** 整改负责人。 */
|
||||
private String rectifyBy;
|
||||
|
||||
/** 整改截止日期(YYYY-MM-DD)。 */
|
||||
private String rectifyDeadline;
|
||||
|
||||
/** 整改说明。 */
|
||||
private String rectifyNote;
|
||||
|
||||
/** 整改完成日期。 */
|
||||
private String rectifiedDate;
|
||||
|
||||
/** 状态:待整改 / 整改中 / 已关闭。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getInspector() { return inspector; }
|
||||
public void setInspector(String inspector) { this.inspector = inspector; }
|
||||
public String getArea() { return area; }
|
||||
public void setArea(String area) { this.area = area; }
|
||||
public String getInspectDate() { return inspectDate; }
|
||||
public void setInspectDate(String inspectDate) { this.inspectDate = inspectDate; }
|
||||
public Integer getScore() { return score; }
|
||||
public void setScore(Integer score) { this.score = score; }
|
||||
public String getGrade() { return grade; }
|
||||
public void setGrade(String grade) { this.grade = grade; }
|
||||
public String getIssues() { return issues; }
|
||||
public void setIssues(String issues) { this.issues = issues; }
|
||||
public String getPhotos() { return photos; }
|
||||
public void setPhotos(String photos) { this.photos = photos; }
|
||||
public String getRectifyBy() { return rectifyBy; }
|
||||
public void setRectifyBy(String rectifyBy) { this.rectifyBy = rectifyBy; }
|
||||
public String getRectifyDeadline() { return rectifyDeadline; }
|
||||
public void setRectifyDeadline(String rectifyDeadline) { this.rectifyDeadline = rectifyDeadline; }
|
||||
public String getRectifyNote() { return rectifyNote; }
|
||||
public void setRectifyNote(String rectifyNote) { this.rectifyNote = rectifyNote; }
|
||||
public String getRectifiedDate() { return rectifiedDate; }
|
||||
public void setRectifiedDate(String rectifiedDate) { this.rectifiedDate = rectifiedDate; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,153 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·公文模板台账(需求 §2 日常行政工作优化·专属公文模板管理)。
|
||||
*
|
||||
* 支持正式模板库管理:通知/请示/报告/决定/纪要/批复/函/规定等公文类型;
|
||||
* 模板版本控制(versionNo)、审批流标识(approvalFlowId 关联 WorkflowDef);
|
||||
* 公文归档路径记录(archivePath)。
|
||||
*
|
||||
* status:草稿 / 已发布 / 已归档 / 已废止
|
||||
*
|
||||
* 表名 admin_official_doc_tpl,零撞名、无保留字列。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_official_doc_tpl")
|
||||
public class AdminOfficialDocTemplate {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 模板编号(自动生成,如 TPL-20260615-001) */
|
||||
@Column(name = "tpl_no")
|
||||
private String tplNo;
|
||||
|
||||
/** 模板名称(如:公司通知模板、向上级请示模板) */
|
||||
private String tplName;
|
||||
|
||||
/** 公文类型:通知 / 请示 / 报告 / 决定 / 纪要 / 批复 / 函 / 规定 / 其他 */
|
||||
@Column(name = "doc_type")
|
||||
private String docType;
|
||||
|
||||
/** 适用场景描述 */
|
||||
@Column(name = "use_case")
|
||||
private String useCase;
|
||||
|
||||
/** 模板正文内容(含占位符,如 {{受文单位}} {{正文}} {{落款}} ) */
|
||||
@Column(name = "body_content", length = 8000)
|
||||
private String bodyContent;
|
||||
|
||||
/** 版本号(如 v1.0 / v2.0) */
|
||||
@Column(name = "version_no")
|
||||
private String versionNo;
|
||||
|
||||
/** 关联审批流 ID(引用 WorkflowDef 或字符串标识) */
|
||||
@Column(name = "approval_flow_id")
|
||||
private String approvalFlowId;
|
||||
|
||||
/** 归档路径/编号(公文发布后的归档位置标识) */
|
||||
@Column(name = "archive_path")
|
||||
private String archivePath;
|
||||
|
||||
/** 制定人 */
|
||||
@Column(name = "created_by")
|
||||
private String createdBy;
|
||||
|
||||
/** 审核人 */
|
||||
@Column(name = "reviewed_by")
|
||||
private String reviewedBy;
|
||||
|
||||
/** 发布人 */
|
||||
@Column(name = "published_by")
|
||||
private String publishedBy;
|
||||
|
||||
/** 发布日期 */
|
||||
@Column(name = "published_date")
|
||||
private String publishedDate;
|
||||
|
||||
/** 废止日期 */
|
||||
@Column(name = "obsolete_date")
|
||||
private String obsoleteDate;
|
||||
|
||||
/** 是否需要 AI 辅助起草(0=否,1=是) */
|
||||
@Column(name = "ai_draft_enabled")
|
||||
private Integer aiDraftEnabled;
|
||||
|
||||
/** 状态:草稿 / 已发布 / 已归档 / 已废止 */
|
||||
private String status;
|
||||
|
||||
/** 备注 */
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
// --- getters & setters ---
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public String getTplNo() { return tplNo; }
|
||||
public void setTplNo(String tplNo) { this.tplNo = tplNo; }
|
||||
|
||||
public String getTplName() { return tplName; }
|
||||
public void setTplName(String tplName) { this.tplName = tplName; }
|
||||
|
||||
public String getDocType() { return docType; }
|
||||
public void setDocType(String docType) { this.docType = docType; }
|
||||
|
||||
public String getUseCase() { return useCase; }
|
||||
public void setUseCase(String useCase) { this.useCase = useCase; }
|
||||
|
||||
public String getBodyContent() { return bodyContent; }
|
||||
public void setBodyContent(String bodyContent) { this.bodyContent = bodyContent; }
|
||||
|
||||
public String getVersionNo() { return versionNo; }
|
||||
public void setVersionNo(String versionNo) { this.versionNo = versionNo; }
|
||||
|
||||
public String getApprovalFlowId() { return approvalFlowId; }
|
||||
public void setApprovalFlowId(String approvalFlowId) { this.approvalFlowId = approvalFlowId; }
|
||||
|
||||
public String getArchivePath() { return archivePath; }
|
||||
public void setArchivePath(String archivePath) { this.archivePath = archivePath; }
|
||||
|
||||
public String getCreatedBy() { return createdBy; }
|
||||
public void setCreatedBy(String createdBy) { this.createdBy = createdBy; }
|
||||
|
||||
public String getReviewedBy() { return reviewedBy; }
|
||||
public void setReviewedBy(String reviewedBy) { this.reviewedBy = reviewedBy; }
|
||||
|
||||
public String getPublishedBy() { return publishedBy; }
|
||||
public void setPublishedBy(String publishedBy) { this.publishedBy = publishedBy; }
|
||||
|
||||
public String getPublishedDate() { return publishedDate; }
|
||||
public void setPublishedDate(String publishedDate) { this.publishedDate = publishedDate; }
|
||||
|
||||
public String getObsoleteDate() { return obsoleteDate; }
|
||||
public void setObsoleteDate(String obsoleteDate) { this.obsoleteDate = obsoleteDate; }
|
||||
|
||||
public Integer getAiDraftEnabled() { return aiDraftEnabled; }
|
||||
public void setAiDraftEnabled(Integer aiDraftEnabled) { this.aiDraftEnabled = aiDraftEnabled; }
|
||||
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
|
||||
public String getRemark() { return remark; }
|
||||
public void setRemark(String remark) { this.remark = remark; }
|
||||
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·安保巡逻记录。巡逻人员扫码签到,记录巡逻时间、路线,异常情况拍照上报。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_patrol_log")
|
||||
public class AdminPatrolLog {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 巡逻人员。 */
|
||||
private String guard;
|
||||
|
||||
/** 巡逻日期(YYYY-MM-DD)。 */
|
||||
private String patrolDate;
|
||||
|
||||
/** 班次(白班/夜班/全天)。 */
|
||||
private String shift;
|
||||
|
||||
/** 巡更点 ID(关联 AdminPatrolPoint)。 */
|
||||
private Long pointId;
|
||||
|
||||
/** 巡更点名称(冗余)。 */
|
||||
private String pointName;
|
||||
|
||||
/** 签到时间(ISO 时间戳)。 */
|
||||
private Instant checkinAt;
|
||||
|
||||
/** 是否有异常(true=有异常)。 */
|
||||
private Boolean hasAnomaly;
|
||||
|
||||
/** 异常描述。 */
|
||||
private String anomalyDesc;
|
||||
|
||||
/** 异常照片(逗号分隔 URL)。 */
|
||||
private String anomalyPhotos;
|
||||
|
||||
/** 处理结果。 */
|
||||
private String handleResult;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getGuard() { return guard; }
|
||||
public void setGuard(String guard) { this.guard = guard; }
|
||||
public String getPatrolDate() { return patrolDate; }
|
||||
public void setPatrolDate(String patrolDate) { this.patrolDate = patrolDate; }
|
||||
public String getShift() { return shift; }
|
||||
public void setShift(String shift) { this.shift = shift; }
|
||||
public Long getPointId() { return pointId; }
|
||||
public void setPointId(Long pointId) { this.pointId = pointId; }
|
||||
public String getPointName() { return pointName; }
|
||||
public void setPointName(String pointName) { this.pointName = pointName; }
|
||||
public Instant getCheckinAt() { return checkinAt; }
|
||||
public void setCheckinAt(Instant checkinAt) { this.checkinAt = checkinAt; }
|
||||
public Boolean getHasAnomaly() { return hasAnomaly; }
|
||||
public void setHasAnomaly(Boolean hasAnomaly) { this.hasAnomaly = hasAnomaly; }
|
||||
public String getAnomalyDesc() { return anomalyDesc; }
|
||||
public void setAnomalyDesc(String anomalyDesc) { this.anomalyDesc = anomalyDesc; }
|
||||
public String getAnomalyPhotos() { return anomalyPhotos; }
|
||||
public void setAnomalyPhotos(String anomalyPhotos) { this.anomalyPhotos = anomalyPhotos; }
|
||||
public String getHandleResult() { return handleResult; }
|
||||
public void setHandleResult(String handleResult) { this.handleResult = handleResult; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·安保巡更点台账。电子巡更点二维码/NFC 设置,供巡逻签到使用。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_patrol_point")
|
||||
public class AdminPatrolPoint {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 巡更点名称。 */
|
||||
private String name;
|
||||
|
||||
/** 位置描述(楼层/区域)。 */
|
||||
private String location;
|
||||
|
||||
/** 标识码(二维码/NFC 内容,通常是 UUID 或编码)。 */
|
||||
private String code;
|
||||
|
||||
/** 标识类型(QR/NFC)。 */
|
||||
private String codeType;
|
||||
|
||||
/** 状态(启用/停用)。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
public String getLocation() { return location; }
|
||||
public void setLocation(String location) { this.location = location; }
|
||||
public String getCode() { return code; }
|
||||
public void setCode(String code) { this.code = code; }
|
||||
public String getCodeType() { return codeType; }
|
||||
public void setCodeType(String codeType) { this.codeType = codeType; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·非生产性物资采购申请(物资采购链)。
|
||||
* 各部门在线提交采购申请 → 系统汇总生成采购计划 → 审批 → 审批通过后入库。
|
||||
* status: 草稿 → 待审批 → 已批准 → 采购中 → 已入库 / 已驳回
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_procurement_request")
|
||||
public class AdminProcurementRequest {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 申请单号,自动生成。 */
|
||||
private String code;
|
||||
|
||||
/** 申请部门。 */
|
||||
private String dept;
|
||||
|
||||
/** 申请人。 */
|
||||
private String applicant;
|
||||
|
||||
/** 物资名称(可关联 AdminSupply.name)。 */
|
||||
private String supplyName;
|
||||
|
||||
/** 物资品类。 */
|
||||
private String category;
|
||||
|
||||
/** 规格/型号。 */
|
||||
private String spec;
|
||||
|
||||
/** 单位。 */
|
||||
private String unit;
|
||||
|
||||
/** 申请数量。 */
|
||||
private Double qty;
|
||||
|
||||
/** 预算金额(元)。 */
|
||||
private java.math.BigDecimal budgetAmount;
|
||||
|
||||
/** 用途说明。 */
|
||||
private String purpose;
|
||||
|
||||
/** 期望到货日期(YYYY-MM-DD)。 */
|
||||
private String expectDate;
|
||||
|
||||
/** 状态:草稿 / 待审批 / 已批准 / 采购中 / 已入库 / 已驳回。 */
|
||||
private String status;
|
||||
|
||||
/** 审批意见。 */
|
||||
private String approveOpinion;
|
||||
|
||||
/** 审批人。 */
|
||||
private String approver;
|
||||
|
||||
/** 审批时间。 */
|
||||
private Instant approvedAt;
|
||||
|
||||
/** 关联的入库批次号(审批通过入库后回填)。 */
|
||||
private String batchNo;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getCode() { return code; }
|
||||
public void setCode(String code) { this.code = code; }
|
||||
public String getDept() { return dept; }
|
||||
public void setDept(String dept) { this.dept = dept; }
|
||||
public String getApplicant() { return applicant; }
|
||||
public void setApplicant(String applicant) { this.applicant = applicant; }
|
||||
public String getSupplyName() { return supplyName; }
|
||||
public void setSupplyName(String supplyName) { this.supplyName = supplyName; }
|
||||
public String getCategory() { return category; }
|
||||
public void setCategory(String category) { this.category = category; }
|
||||
public String getSpec() { return spec; }
|
||||
public void setSpec(String spec) { this.spec = spec; }
|
||||
public String getUnit() { return unit; }
|
||||
public void setUnit(String unit) { this.unit = unit; }
|
||||
public Double getQty() { return qty; }
|
||||
public void setQty(Double qty) { this.qty = qty; }
|
||||
public java.math.BigDecimal getBudgetAmount() { return budgetAmount; }
|
||||
public void setBudgetAmount(java.math.BigDecimal budgetAmount) { this.budgetAmount = budgetAmount; }
|
||||
public String getPurpose() { return purpose; }
|
||||
public void setPurpose(String purpose) { this.purpose = purpose; }
|
||||
public String getExpectDate() { return expectDate; }
|
||||
public void setExpectDate(String expectDate) { this.expectDate = expectDate; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public String getApproveOpinion() { return approveOpinion; }
|
||||
public void setApproveOpinion(String approveOpinion) { this.approveOpinion = approveOpinion; }
|
||||
public String getApprover() { return approver; }
|
||||
public void setApprover(String approver) { this.approver = approver; }
|
||||
public Instant getApprovedAt() { return approvedAt; }
|
||||
public void setApprovedAt(Instant approvedAt) { this.approvedAt = approvedAt; }
|
||||
public String getBatchNo() { return batchNo; }
|
||||
public void setBatchNo(String batchNo) { this.batchNo = batchNo; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,114 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·培训题库题目(培训学习AI题库补全缺口)。
|
||||
* 支持按岗位/课程管理题目;可由AI生成后人工复核入库,
|
||||
* 也可手动录入。类型:单选/多选/判断/简答。
|
||||
* status: 待审 / 已启用 / 已停用
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_quiz_question")
|
||||
public class AdminQuizQuestion {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 关联培训课程ID(可选,为null表示通用题库)。 */
|
||||
private Long courseId;
|
||||
|
||||
/** 适用岗位(逗号分隔;"全员"表示所有岗位)。 */
|
||||
private String targetPosition;
|
||||
|
||||
/** 题目类型:单选/多选/判断/简答。 */
|
||||
@Column(name = "question_type")
|
||||
private String questionType;
|
||||
|
||||
/** 题目内容。 */
|
||||
@Column(name = "question_text", length = 2000)
|
||||
private String questionText;
|
||||
|
||||
/** 选项A。 */
|
||||
@Column(name = "option_a")
|
||||
private String optionA;
|
||||
|
||||
/** 选项B。 */
|
||||
@Column(name = "option_b")
|
||||
private String optionB;
|
||||
|
||||
/** 选项C。 */
|
||||
@Column(name = "option_c")
|
||||
private String optionC;
|
||||
|
||||
/** 选项D。 */
|
||||
@Column(name = "option_d")
|
||||
private String optionD;
|
||||
|
||||
/** 正确答案(单选:A/B/C/D,多选:AB等,判断:true/false,简答:参考答案文本)。 */
|
||||
@Column(name = "correct_answer", length = 2000)
|
||||
private String correctAnswer;
|
||||
|
||||
/** 题目解析/解释。 */
|
||||
@Column(name = "explanation", length = 2000)
|
||||
private String explanation;
|
||||
|
||||
/** 难度:简单/中等/困难。 */
|
||||
private String difficulty;
|
||||
|
||||
/** 分值。 */
|
||||
private Integer score;
|
||||
|
||||
/** 来源:手动录入 / AI生成。 */
|
||||
@Column(name = "question_source")
|
||||
private String questionSource;
|
||||
|
||||
/** 状态:待审 / 已启用 / 已停用。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public Long getCourseId() { return courseId; }
|
||||
public void setCourseId(Long courseId) { this.courseId = courseId; }
|
||||
public String getTargetPosition() { return targetPosition; }
|
||||
public void setTargetPosition(String targetPosition) { this.targetPosition = targetPosition; }
|
||||
public String getQuestionType() { return questionType; }
|
||||
public void setQuestionType(String questionType) { this.questionType = questionType; }
|
||||
public String getQuestionText() { return questionText; }
|
||||
public void setQuestionText(String questionText) { this.questionText = questionText; }
|
||||
public String getOptionA() { return optionA; }
|
||||
public void setOptionA(String optionA) { this.optionA = optionA; }
|
||||
public String getOptionB() { return optionB; }
|
||||
public void setOptionB(String optionB) { this.optionB = optionB; }
|
||||
public String getOptionC() { return optionC; }
|
||||
public void setOptionC(String optionC) { this.optionC = optionC; }
|
||||
public String getOptionD() { return optionD; }
|
||||
public void setOptionD(String optionD) { this.optionD = optionD; }
|
||||
public String getCorrectAnswer() { return correctAnswer; }
|
||||
public void setCorrectAnswer(String correctAnswer) { this.correctAnswer = correctAnswer; }
|
||||
public String getExplanation() { return explanation; }
|
||||
public void setExplanation(String explanation) { this.explanation = explanation; }
|
||||
public String getDifficulty() { return difficulty; }
|
||||
public void setDifficulty(String difficulty) { this.difficulty = difficulty; }
|
||||
public Integer getScore() { return score; }
|
||||
public void setScore(Integer score) { this.score = score; }
|
||||
public String getQuestionSource() { return questionSource; }
|
||||
public void setQuestionSource(String questionSource) { this.questionSource = questionSource; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 物资领用定额(行政/办公室·领用定额控制)。按岗位/部门 × 物资设置每月领用上限
|
||||
* (如每人每月 2 支笔)。领用申请时按 (post|dept, supplyId) 匹配定额,结合本月累计已领量
|
||||
* 判断是否超定额。monthlyLimit 为月度上限(数量口径,double)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_quota")
|
||||
public class AdminQuota {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private Long supplyId;
|
||||
|
||||
private String supplyName;
|
||||
|
||||
/** 适用岗位(与部门二选一或叠加;空表示不限岗位)。 */
|
||||
private String post;
|
||||
|
||||
/** 适用部门(空表示不限部门)。 */
|
||||
private String dept;
|
||||
|
||||
/** 月度领用上限(数量口径)。 */
|
||||
private Double monthlyLimit = 0d;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getSupplyId() {
|
||||
return supplyId;
|
||||
}
|
||||
|
||||
public void setSupplyId(Long supplyId) {
|
||||
this.supplyId = supplyId;
|
||||
}
|
||||
|
||||
public String getSupplyName() {
|
||||
return supplyName;
|
||||
}
|
||||
|
||||
public void setSupplyName(String supplyName) {
|
||||
this.supplyName = supplyName;
|
||||
}
|
||||
|
||||
public String getPost() {
|
||||
return post;
|
||||
}
|
||||
|
||||
public void setPost(String post) {
|
||||
this.post = post;
|
||||
}
|
||||
|
||||
public String getDept() {
|
||||
return dept;
|
||||
}
|
||||
|
||||
public void setDept(String dept) {
|
||||
this.dept = dept;
|
||||
}
|
||||
|
||||
public Double getMonthlyLimit() {
|
||||
return monthlyLimit;
|
||||
}
|
||||
|
||||
public void setMonthlyLimit(Double monthlyLimit) {
|
||||
this.monthlyLimit = monthlyLimit;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·接待申请。部门申请接待客户/访客(级别/人数/行程/餐饮住宿),
|
||||
* 后勤部安排车辆/餐饮/会议室/礼品,记录接待费用,分摊至对应部门/项目。
|
||||
* status: 草稿 / 待审批 / 已批准 / 接待中 / 已完成 / 已驳回
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_reception")
|
||||
public class AdminReception {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 接待单号,自动生成。 */
|
||||
private String code;
|
||||
|
||||
/** 申请部门。 */
|
||||
private String dept;
|
||||
|
||||
/** 申请人。 */
|
||||
private String applicant;
|
||||
|
||||
/** 接待对象/客户名称。 */
|
||||
private String guestName;
|
||||
|
||||
/** 接待级别(VIP/一般/内部)。 */
|
||||
private String level;
|
||||
|
||||
/** 接待人数。 */
|
||||
private Integer guestCount;
|
||||
|
||||
/** 接待目的/行程说明。 */
|
||||
private String purpose;
|
||||
|
||||
/** 接待开始日期(YYYY-MM-DD)。 */
|
||||
private String startDate;
|
||||
|
||||
/** 接待结束日期(YYYY-MM-DD)。 */
|
||||
private String endDate;
|
||||
|
||||
/** 是否需要餐饮。 */
|
||||
private Boolean needDining;
|
||||
|
||||
/** 是否需要住宿。 */
|
||||
private Boolean needAccommodation;
|
||||
|
||||
/** 是否需要用车。 */
|
||||
private Boolean needVehicle;
|
||||
|
||||
/** 是否需要礼品。 */
|
||||
private Boolean needGift;
|
||||
|
||||
/** 是否需要会议室。 */
|
||||
private Boolean needMeetingRoom;
|
||||
|
||||
/** 关联会议室预订 ID。 */
|
||||
private Long meetingId;
|
||||
|
||||
/** 备注/特殊要求。 */
|
||||
private String remark;
|
||||
|
||||
/** 安排说明(后勤填写)。 */
|
||||
private String arrangement;
|
||||
|
||||
/** 审批意见。 */
|
||||
private String approveOpinion;
|
||||
|
||||
/** 餐饮费用。 */
|
||||
private BigDecimal diningCost;
|
||||
|
||||
/** 住宿费用。 */
|
||||
private BigDecimal accommodationCost;
|
||||
|
||||
/** 礼品费用。 */
|
||||
private BigDecimal giftCost;
|
||||
|
||||
/** 其他费用。 */
|
||||
private BigDecimal otherCost;
|
||||
|
||||
/** 总费用(自动累计)。 */
|
||||
private BigDecimal totalCost;
|
||||
|
||||
/** 费用归集项目/部门。 */
|
||||
private String costCenter;
|
||||
|
||||
/** 状态:草稿 / 待审批 / 已批准 / 接待中 / 已完成 / 已驳回。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getCode() { return code; }
|
||||
public void setCode(String code) { this.code = code; }
|
||||
public String getDept() { return dept; }
|
||||
public void setDept(String dept) { this.dept = dept; }
|
||||
public String getApplicant() { return applicant; }
|
||||
public void setApplicant(String applicant) { this.applicant = applicant; }
|
||||
public String getGuestName() { return guestName; }
|
||||
public void setGuestName(String guestName) { this.guestName = guestName; }
|
||||
public String getLevel() { return level; }
|
||||
public void setLevel(String level) { this.level = level; }
|
||||
public Integer getGuestCount() { return guestCount; }
|
||||
public void setGuestCount(Integer guestCount) { this.guestCount = guestCount; }
|
||||
public String getPurpose() { return purpose; }
|
||||
public void setPurpose(String purpose) { this.purpose = purpose; }
|
||||
public String getStartDate() { return startDate; }
|
||||
public void setStartDate(String startDate) { this.startDate = startDate; }
|
||||
public String getEndDate() { return endDate; }
|
||||
public void setEndDate(String endDate) { this.endDate = endDate; }
|
||||
public Boolean getNeedDining() { return needDining; }
|
||||
public void setNeedDining(Boolean needDining) { this.needDining = needDining; }
|
||||
public Boolean getNeedAccommodation() { return needAccommodation; }
|
||||
public void setNeedAccommodation(Boolean needAccommodation) { this.needAccommodation = needAccommodation; }
|
||||
public Boolean getNeedVehicle() { return needVehicle; }
|
||||
public void setNeedVehicle(Boolean needVehicle) { this.needVehicle = needVehicle; }
|
||||
public Boolean getNeedGift() { return needGift; }
|
||||
public void setNeedGift(Boolean needGift) { this.needGift = needGift; }
|
||||
public Boolean getNeedMeetingRoom() { return needMeetingRoom; }
|
||||
public void setNeedMeetingRoom(Boolean needMeetingRoom) { this.needMeetingRoom = needMeetingRoom; }
|
||||
public Long getMeetingId() { return meetingId; }
|
||||
public void setMeetingId(Long meetingId) { this.meetingId = meetingId; }
|
||||
public String getRemark() { return remark; }
|
||||
public void setRemark(String remark) { this.remark = remark; }
|
||||
public String getArrangement() { return arrangement; }
|
||||
public void setArrangement(String arrangement) { this.arrangement = arrangement; }
|
||||
public String getApproveOpinion() { return approveOpinion; }
|
||||
public void setApproveOpinion(String approveOpinion) { this.approveOpinion = approveOpinion; }
|
||||
public BigDecimal getDiningCost() { return diningCost; }
|
||||
public void setDiningCost(BigDecimal diningCost) { this.diningCost = diningCost; }
|
||||
public BigDecimal getAccommodationCost() { return accommodationCost; }
|
||||
public void setAccommodationCost(BigDecimal accommodationCost) { this.accommodationCost = accommodationCost; }
|
||||
public BigDecimal getGiftCost() { return giftCost; }
|
||||
public void setGiftCost(BigDecimal giftCost) { this.giftCost = giftCost; }
|
||||
public BigDecimal getOtherCost() { return otherCost; }
|
||||
public void setOtherCost(BigDecimal otherCost) { this.otherCost = otherCost; }
|
||||
public BigDecimal getTotalCost() { return totalCost; }
|
||||
public void setTotalCost(BigDecimal totalCost) { this.totalCost = totalCost; }
|
||||
public String getCostCenter() { return costCenter; }
|
||||
public void setCostCenter(String costCenter) { this.costCenter = costCenter; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·周期会议预订规则。
|
||||
* 存储周期规则(每天/每周N/每月第N天),由调度 Job 自动展开为具体的 Meeting 记录。
|
||||
* status: 生效中 / 已暂停 / 已结束
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_recurring_booking")
|
||||
public class AdminRecurringBooking {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 周期规则名称(如"周一例会")。 */
|
||||
@Column(nullable = false)
|
||||
private String ruleName;
|
||||
|
||||
/** 会议主题模板(实际生成时追加日期)。 */
|
||||
@Column(nullable = false)
|
||||
private String subject;
|
||||
|
||||
/** 会议室 ID。 */
|
||||
private Long roomId;
|
||||
|
||||
/** 组织者。 */
|
||||
private String organizer;
|
||||
|
||||
/** 参会人(逗号分隔)。 */
|
||||
@Column(length = 2000)
|
||||
private String attendees;
|
||||
|
||||
/**
|
||||
* 周期类型:DAILY(每天)/ WEEKLY(每周,需指定 weekDays)/ MONTHLY(每月,需指定 monthDay)。
|
||||
*/
|
||||
@Column(nullable = false)
|
||||
private String recurType;
|
||||
|
||||
/**
|
||||
* WEEKLY 时:1-7 逗号分隔(1=周一,7=周日),如 "1,3,5" 表示周一三五。
|
||||
* MONTHLY 时:月中的第几天(1-28),如 "15" 表示每月15日。
|
||||
*/
|
||||
private String recurParam;
|
||||
|
||||
/** 每次会议开始时间(HH:mm),如 "09:00"。 */
|
||||
@Column(nullable = false)
|
||||
private String startTime;
|
||||
|
||||
/** 每次会议结束时间(HH:mm),如 "10:00"。 */
|
||||
@Column(nullable = false)
|
||||
private String endTime;
|
||||
|
||||
/** 周期生效开始日期(yyyy-MM-dd)。 */
|
||||
@Column(nullable = false)
|
||||
private String effectFrom;
|
||||
|
||||
/** 周期结束日期(yyyy-MM-dd,null=长期有效)。 */
|
||||
private String effectTo;
|
||||
|
||||
/** 最后一次自动展开到的日期(yyyy-MM-dd)。 */
|
||||
private String lastExpandedDate;
|
||||
|
||||
/** 状态:生效中 / 已暂停 / 已结束。 */
|
||||
@Column(nullable = false)
|
||||
private String bookingStatus;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getRuleName() { return ruleName; }
|
||||
public void setRuleName(String ruleName) { this.ruleName = ruleName; }
|
||||
public String getSubject() { return subject; }
|
||||
public void setSubject(String subject) { this.subject = subject; }
|
||||
public Long getRoomId() { return roomId; }
|
||||
public void setRoomId(Long roomId) { this.roomId = roomId; }
|
||||
public String getOrganizer() { return organizer; }
|
||||
public void setOrganizer(String organizer) { this.organizer = organizer; }
|
||||
public String getAttendees() { return attendees; }
|
||||
public void setAttendees(String attendees) { this.attendees = attendees; }
|
||||
public String getRecurType() { return recurType; }
|
||||
public void setRecurType(String recurType) { this.recurType = recurType; }
|
||||
public String getRecurParam() { return recurParam; }
|
||||
public void setRecurParam(String recurParam) { this.recurParam = recurParam; }
|
||||
public String getStartTime() { return startTime; }
|
||||
public void setStartTime(String startTime) { this.startTime = startTime; }
|
||||
public String getEndTime() { return endTime; }
|
||||
public void setEndTime(String endTime) { this.endTime = endTime; }
|
||||
public String getEffectFrom() { return effectFrom; }
|
||||
public void setEffectFrom(String effectFrom) { this.effectFrom = effectFrom; }
|
||||
public String getEffectTo() { return effectTo; }
|
||||
public void setEffectTo(String effectTo) { this.effectTo = effectTo; }
|
||||
public String getLastExpandedDate() { return lastExpandedDate; }
|
||||
public void setLastExpandedDate(String lastExpandedDate) { this.lastExpandedDate = lastExpandedDate; }
|
||||
public String getBookingStatus() { return bookingStatus; }
|
||||
public void setBookingStatus(String bookingStatus) { this.bookingStatus = bookingStatus; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 物资领用申请单(行政/办公室·领用定额控制)。员工按岗位/部门提交领用申请,
|
||||
* 系统按 {@link AdminQuota} 自动判断是否超定额:未超 → 直接「已通过」;
|
||||
* 超定额 → 进入「待审批」需特别审批,审批通过后才可出库。
|
||||
* status 流转:待审批 → 已通过 / 已驳回 → 已出库。overQuota 标记本单是否超定额。
|
||||
* qty 为申请数量(数量口径,double)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_requisition")
|
||||
public class AdminRequisition {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String code;
|
||||
|
||||
private Long supplyId;
|
||||
|
||||
private String supplyName;
|
||||
|
||||
private String unit;
|
||||
|
||||
/** 申请数量。 */
|
||||
private Double qty = 0d;
|
||||
|
||||
/** 申请人。 */
|
||||
private String applicant;
|
||||
|
||||
/** 领用部门(费用分摊归集维度)。 */
|
||||
private String dept;
|
||||
|
||||
/** 岗位(定额按岗位/部门设置)。 */
|
||||
private String post;
|
||||
|
||||
/** 用途。 */
|
||||
private String purpose;
|
||||
|
||||
/** 本单是否超定额。 */
|
||||
private Boolean overQuota = Boolean.FALSE;
|
||||
|
||||
/** 本月该领用人对该物资的累计已领量(提交时快照,便于审批参考)。 */
|
||||
private Double monthUsed = 0d;
|
||||
|
||||
/** 适用定额(提交时快照)。 */
|
||||
private Double quotaLimit = 0d;
|
||||
|
||||
/** 待审批 / 已通过 / 已驳回 / 已出库。 */
|
||||
private String status;
|
||||
|
||||
/** 审批人。 */
|
||||
private String approver;
|
||||
|
||||
/** 审批意见。 */
|
||||
private String approveComment;
|
||||
|
||||
/** 出库流水 id(出库后回填)。 */
|
||||
private Long stockMoveId;
|
||||
|
||||
private String applyDate;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Long getSupplyId() {
|
||||
return supplyId;
|
||||
}
|
||||
|
||||
public void setSupplyId(Long supplyId) {
|
||||
this.supplyId = supplyId;
|
||||
}
|
||||
|
||||
public String getSupplyName() {
|
||||
return supplyName;
|
||||
}
|
||||
|
||||
public void setSupplyName(String supplyName) {
|
||||
this.supplyName = supplyName;
|
||||
}
|
||||
|
||||
public String getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public void setUnit(String unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
public Double getQty() {
|
||||
return qty;
|
||||
}
|
||||
|
||||
public void setQty(Double qty) {
|
||||
this.qty = qty;
|
||||
}
|
||||
|
||||
public String getApplicant() {
|
||||
return applicant;
|
||||
}
|
||||
|
||||
public void setApplicant(String applicant) {
|
||||
this.applicant = applicant;
|
||||
}
|
||||
|
||||
public String getDept() {
|
||||
return dept;
|
||||
}
|
||||
|
||||
public void setDept(String dept) {
|
||||
this.dept = dept;
|
||||
}
|
||||
|
||||
public String getPost() {
|
||||
return post;
|
||||
}
|
||||
|
||||
public void setPost(String post) {
|
||||
this.post = post;
|
||||
}
|
||||
|
||||
public String getPurpose() {
|
||||
return purpose;
|
||||
}
|
||||
|
||||
public void setPurpose(String purpose) {
|
||||
this.purpose = purpose;
|
||||
}
|
||||
|
||||
public Boolean getOverQuota() {
|
||||
return overQuota;
|
||||
}
|
||||
|
||||
public void setOverQuota(Boolean overQuota) {
|
||||
this.overQuota = overQuota;
|
||||
}
|
||||
|
||||
public Double getMonthUsed() {
|
||||
return monthUsed;
|
||||
}
|
||||
|
||||
public void setMonthUsed(Double monthUsed) {
|
||||
this.monthUsed = monthUsed;
|
||||
}
|
||||
|
||||
public Double getQuotaLimit() {
|
||||
return quotaLimit;
|
||||
}
|
||||
|
||||
public void setQuotaLimit(Double quotaLimit) {
|
||||
this.quotaLimit = quotaLimit;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getApprover() {
|
||||
return approver;
|
||||
}
|
||||
|
||||
public void setApprover(String approver) {
|
||||
this.approver = approver;
|
||||
}
|
||||
|
||||
public String getApproveComment() {
|
||||
return approveComment;
|
||||
}
|
||||
|
||||
public void setApproveComment(String approveComment) {
|
||||
this.approveComment = approveComment;
|
||||
}
|
||||
|
||||
public Long getStockMoveId() {
|
||||
return stockMoveId;
|
||||
}
|
||||
|
||||
public void setStockMoveId(Long stockMoveId) {
|
||||
this.stockMoveId = stockMoveId;
|
||||
}
|
||||
|
||||
public String getApplyDate() {
|
||||
return applyDate;
|
||||
}
|
||||
|
||||
public void setApplyDate(String applyDate) {
|
||||
this.applyDate = applyDate;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·安保事件(安保事件台账)。记录偷盗、打架、消防等安全事件的处理过程闭环。
|
||||
* status: 已上报 / 处理中 / 已结案
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_security_event")
|
||||
public class AdminSecurityEvent {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 事件编号,自动生成。 */
|
||||
private String code;
|
||||
|
||||
/** 事件类型(盗窃/斗殴/消防/违规/其他)。 */
|
||||
private String eventType;
|
||||
|
||||
/** 发生时间(YYYY-MM-DD HH:mm)。 */
|
||||
private String eventTime;
|
||||
|
||||
/** 发生地点。 */
|
||||
private String location;
|
||||
|
||||
/** 事件描述。 */
|
||||
private String description;
|
||||
|
||||
/** 当事人(逗号分隔多人)。 */
|
||||
private String parties;
|
||||
|
||||
/** 照片/证据(逗号分隔 URL)。 */
|
||||
private String evidencePhotos;
|
||||
|
||||
/** 上报人。 */
|
||||
private String reporter;
|
||||
|
||||
/** 处理人。 */
|
||||
private String handler;
|
||||
|
||||
/** 处理措施。 */
|
||||
private String handleMeasures;
|
||||
|
||||
/** 是否上报公安(true=已报警)。 */
|
||||
private Boolean policeReported;
|
||||
|
||||
/** 结案说明。 */
|
||||
private String closureNote;
|
||||
|
||||
/** 结案日期(YYYY-MM-DD)。 */
|
||||
private String closureDate;
|
||||
|
||||
/** 状态:已上报 / 处理中 / 已结案。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getCode() { return code; }
|
||||
public void setCode(String code) { this.code = code; }
|
||||
public String getEventType() { return eventType; }
|
||||
public void setEventType(String eventType) { this.eventType = eventType; }
|
||||
public String getEventTime() { return eventTime; }
|
||||
public void setEventTime(String eventTime) { this.eventTime = eventTime; }
|
||||
public String getLocation() { return location; }
|
||||
public void setLocation(String location) { this.location = location; }
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
public String getParties() { return parties; }
|
||||
public void setParties(String parties) { this.parties = parties; }
|
||||
public String getEvidencePhotos() { return evidencePhotos; }
|
||||
public void setEvidencePhotos(String evidencePhotos) { this.evidencePhotos = evidencePhotos; }
|
||||
public String getReporter() { return reporter; }
|
||||
public void setReporter(String reporter) { this.reporter = reporter; }
|
||||
public String getHandler() { return handler; }
|
||||
public void setHandler(String handler) { this.handler = handler; }
|
||||
public String getHandleMeasures() { return handleMeasures; }
|
||||
public void setHandleMeasures(String handleMeasures) { this.handleMeasures = handleMeasures; }
|
||||
public Boolean getPoliceReported() { return policeReported; }
|
||||
public void setPoliceReported(Boolean policeReported) { this.policeReported = policeReported; }
|
||||
public String getClosureNote() { return closureNote; }
|
||||
public void setClosureNote(String closureNote) { this.closureNote = closureNote; }
|
||||
public String getClosureDate() { return closureDate; }
|
||||
public void setClosureDate(String closureDate) { this.closureDate = closureDate; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·通勤班车台账。记录班车路线、司机、车辆、排班计划。
|
||||
* status: 运营中 / 暂停 / 停运
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_shuttle_bus")
|
||||
public class AdminShuttleBus {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 路线名称。 */
|
||||
private String routeName;
|
||||
|
||||
/** 起点站。 */
|
||||
private String startStop;
|
||||
|
||||
/** 终点站。 */
|
||||
private String endStop;
|
||||
|
||||
/** 途经站点(JSON 数组字符串)。 */
|
||||
private String stops;
|
||||
|
||||
/** 发车时间(如 07:30)。 */
|
||||
private String departureTime;
|
||||
|
||||
/** 返程时间(如 18:00)。 */
|
||||
private String returnTime;
|
||||
|
||||
/** 关联车牌(来自 AdminVehicle)。 */
|
||||
private String vehiclePlate;
|
||||
|
||||
/** 司机姓名。 */
|
||||
private String driver;
|
||||
|
||||
/** 座位数。 */
|
||||
private Integer seats;
|
||||
|
||||
/** 当前预约人数。 */
|
||||
private Integer bookedCount;
|
||||
|
||||
/** 运行日(工作日/周一至六/每天/自定义)。 */
|
||||
private String runDays;
|
||||
|
||||
/** 状态:运营中 / 暂停 / 停运。 */
|
||||
private String status;
|
||||
|
||||
/** 备注。 */
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getRouteName() { return routeName; }
|
||||
public void setRouteName(String routeName) { this.routeName = routeName; }
|
||||
public String getStartStop() { return startStop; }
|
||||
public void setStartStop(String startStop) { this.startStop = startStop; }
|
||||
public String getEndStop() { return endStop; }
|
||||
public void setEndStop(String endStop) { this.endStop = endStop; }
|
||||
public String getStops() { return stops; }
|
||||
public void setStops(String stops) { this.stops = stops; }
|
||||
public String getDepartureTime() { return departureTime; }
|
||||
public void setDepartureTime(String departureTime) { this.departureTime = departureTime; }
|
||||
public String getReturnTime() { return returnTime; }
|
||||
public void setReturnTime(String returnTime) { this.returnTime = returnTime; }
|
||||
public String getVehiclePlate() { return vehiclePlate; }
|
||||
public void setVehiclePlate(String vehiclePlate) { this.vehiclePlate = vehiclePlate; }
|
||||
public String getDriver() { return driver; }
|
||||
public void setDriver(String driver) { this.driver = driver; }
|
||||
public Integer getSeats() { return seats; }
|
||||
public void setSeats(Integer seats) { this.seats = seats; }
|
||||
public Integer getBookedCount() { return bookedCount; }
|
||||
public void setBookedCount(Integer bookedCount) { this.bookedCount = bookedCount; }
|
||||
public String getRunDays() { return runDays; }
|
||||
public void setRunDays(String runDays) { this.runDays = runDays; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public String getRemark() { return remark; }
|
||||
public void setRemark(String remark) { this.remark = remark; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·通勤班车乘客预约(乘客统计)。
|
||||
* status: 已预约 / 已乘坐 / 已取消
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_shuttle_passenger")
|
||||
public class AdminShuttlePassenger {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 关联班车 ID。 */
|
||||
private Long shuttleId;
|
||||
|
||||
/** 乘客姓名。 */
|
||||
private String passenger;
|
||||
|
||||
/** 乘客部门。 */
|
||||
private String dept;
|
||||
|
||||
/** 乘坐日期(YYYY-MM-DD)。 */
|
||||
private String rideDate;
|
||||
|
||||
/** 上下行(去/回)。 */
|
||||
private String direction;
|
||||
|
||||
/** 上车站点。 */
|
||||
private String boardStop;
|
||||
|
||||
/** 状态:已预约 / 已乘坐 / 已取消。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public Long getShuttleId() { return shuttleId; }
|
||||
public void setShuttleId(Long shuttleId) { this.shuttleId = shuttleId; }
|
||||
public String getPassenger() { return passenger; }
|
||||
public void setPassenger(String passenger) { this.passenger = passenger; }
|
||||
public String getDept() { return dept; }
|
||||
public void setDept(String dept) { this.dept = dept; }
|
||||
public String getRideDate() { return rideDate; }
|
||||
public void setRideDate(String rideDate) { this.rideDate = rideDate; }
|
||||
public String getDirection() { return direction; }
|
||||
public void setDirection(String direction) { this.direction = direction; }
|
||||
public String getBoardStop() { return boardStop; }
|
||||
public void setBoardStop(String boardStop) { this.boardStop = boardStop; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 物资库存流水(一次入库或一次出库领用)。direction 为 入库 / 出库。
|
||||
* 入库登记数量、金额、供应商;出库领用登记领用部门 / 项目 / 领用人,用于费用分摊归集。
|
||||
* 每条流水在控制器内级联调整 {@link AdminSupply#getOnHand()}(入库 +qty,出库 -qty)。
|
||||
* qty 为数量口径(double),amount 为金额口径(BigDecimal,入库总金额)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_stock_move")
|
||||
public class AdminStockMove {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private Long supplyId;
|
||||
|
||||
/** 冗余物资编号/名称,便于流水列表展示。 */
|
||||
private String supplyCode;
|
||||
|
||||
private String supplyName;
|
||||
|
||||
/** 入库 / 出库。 */
|
||||
private String direction;
|
||||
|
||||
/** 数量(数量口径)。 */
|
||||
private Double qty = 0d;
|
||||
|
||||
/** 入库总金额(出库时一般为 0)。 */
|
||||
private BigDecimal amount = BigDecimal.ZERO;
|
||||
|
||||
/** 入库供应商。 */
|
||||
private String supplier;
|
||||
|
||||
/** 出库领用部门(费用分摊归集维度)。 */
|
||||
private String dept;
|
||||
|
||||
/** 出库归集项目(可空)。 */
|
||||
private String project;
|
||||
|
||||
/** 领用人 / 经办人。 */
|
||||
private String handler;
|
||||
|
||||
/** 批次/有效期(批次管理物资)。 */
|
||||
private String batchNo;
|
||||
|
||||
private String expireDate;
|
||||
|
||||
/** 发生日期(YYYY-MM-DD),用于月度分摊归集。 */
|
||||
private String moveDate;
|
||||
|
||||
/** 关联领用申请单 id(由超定额审批通过的领用单出库时回填)。 */
|
||||
private Long requisitionId;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getSupplyId() {
|
||||
return supplyId;
|
||||
}
|
||||
|
||||
public void setSupplyId(Long supplyId) {
|
||||
this.supplyId = supplyId;
|
||||
}
|
||||
|
||||
public String getSupplyCode() {
|
||||
return supplyCode;
|
||||
}
|
||||
|
||||
public void setSupplyCode(String supplyCode) {
|
||||
this.supplyCode = supplyCode;
|
||||
}
|
||||
|
||||
public String getSupplyName() {
|
||||
return supplyName;
|
||||
}
|
||||
|
||||
public void setSupplyName(String supplyName) {
|
||||
this.supplyName = supplyName;
|
||||
}
|
||||
|
||||
public String getDirection() {
|
||||
return direction;
|
||||
}
|
||||
|
||||
public void setDirection(String direction) {
|
||||
this.direction = direction;
|
||||
}
|
||||
|
||||
public Double getQty() {
|
||||
return qty;
|
||||
}
|
||||
|
||||
public void setQty(Double qty) {
|
||||
this.qty = qty;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public String getSupplier() {
|
||||
return supplier;
|
||||
}
|
||||
|
||||
public void setSupplier(String supplier) {
|
||||
this.supplier = supplier;
|
||||
}
|
||||
|
||||
public String getDept() {
|
||||
return dept;
|
||||
}
|
||||
|
||||
public void setDept(String dept) {
|
||||
this.dept = dept;
|
||||
}
|
||||
|
||||
public String getProject() {
|
||||
return project;
|
||||
}
|
||||
|
||||
public void setProject(String project) {
|
||||
this.project = project;
|
||||
}
|
||||
|
||||
public String getHandler() {
|
||||
return handler;
|
||||
}
|
||||
|
||||
public void setHandler(String handler) {
|
||||
this.handler = handler;
|
||||
}
|
||||
|
||||
public String getBatchNo() {
|
||||
return batchNo;
|
||||
}
|
||||
|
||||
public void setBatchNo(String batchNo) {
|
||||
this.batchNo = batchNo;
|
||||
}
|
||||
|
||||
public String getExpireDate() {
|
||||
return expireDate;
|
||||
}
|
||||
|
||||
public void setExpireDate(String expireDate) {
|
||||
this.expireDate = expireDate;
|
||||
}
|
||||
|
||||
public String getMoveDate() {
|
||||
return moveDate;
|
||||
}
|
||||
|
||||
public void setMoveDate(String moveDate) {
|
||||
this.moveDate = moveDate;
|
||||
}
|
||||
|
||||
public Long getRequisitionId() {
|
||||
return requisitionId;
|
||||
}
|
||||
|
||||
public void setRequisitionId(Long requisitionId) {
|
||||
this.requisitionId = requisitionId;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 非生产性物资档案(行政/办公室)。一条 AdminSupply 代表一种可领用的物资:
|
||||
* category 为 办公用品 / 劳保用品 / 清洁用品 / 低值易耗品 之一;记录规格、单位、参考单价、
|
||||
* 安全库存与当前结存量 onHand。onHand 由入库 / 出库领用({@link AdminStockMove})在
|
||||
* 控制器内级联自动累减,低于 safetyStock 触发低库存预警。物资数量口径为 double(数量非金额),
|
||||
* 参考单价 refPrice 为金额(BigDecimal)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_supply")
|
||||
public class AdminSupply {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String code;
|
||||
|
||||
/** 物资名称。 */
|
||||
private String name;
|
||||
|
||||
/** 分类:办公用品 / 劳保用品 / 清洁用品 / 低值易耗品。 */
|
||||
private String category;
|
||||
|
||||
/** 规格(如 A4 70g、9 号牛皮纸袋)。 */
|
||||
private String spec;
|
||||
|
||||
/** 计量单位(支 / 包 / 个 / 瓶)。 */
|
||||
private String unit;
|
||||
|
||||
/** 参考单价(金额)。 */
|
||||
private BigDecimal refPrice = BigDecimal.ZERO;
|
||||
|
||||
/** 安全库存(数量口径)。低于此值触发低库存预警。 */
|
||||
private Double safetyStock = 0d;
|
||||
|
||||
/** 当前结存量(数量口径),由入库/出库级联维护。 */
|
||||
private Double onHand = 0d;
|
||||
|
||||
/** 是否批次管理(如清洁剂有效期)。 */
|
||||
private Boolean batchManaged = Boolean.FALSE;
|
||||
|
||||
/** 默认归集部门(领用未指定时回退)。 */
|
||||
private String defaultDept;
|
||||
|
||||
/** 启用 / 停用。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getSpec() {
|
||||
return spec;
|
||||
}
|
||||
|
||||
public void setSpec(String spec) {
|
||||
this.spec = spec;
|
||||
}
|
||||
|
||||
public String getUnit() {
|
||||
return unit;
|
||||
}
|
||||
|
||||
public void setUnit(String unit) {
|
||||
this.unit = unit;
|
||||
}
|
||||
|
||||
public BigDecimal getRefPrice() {
|
||||
return refPrice;
|
||||
}
|
||||
|
||||
public void setRefPrice(BigDecimal refPrice) {
|
||||
this.refPrice = refPrice;
|
||||
}
|
||||
|
||||
public Double getSafetyStock() {
|
||||
return safetyStock;
|
||||
}
|
||||
|
||||
public void setSafetyStock(Double safetyStock) {
|
||||
this.safetyStock = safetyStock;
|
||||
}
|
||||
|
||||
public Double getOnHand() {
|
||||
return onHand;
|
||||
}
|
||||
|
||||
public void setOnHand(Double onHand) {
|
||||
this.onHand = onHand;
|
||||
}
|
||||
|
||||
public Boolean getBatchManaged() {
|
||||
return batchManaged;
|
||||
}
|
||||
|
||||
public void setBatchManaged(Boolean batchManaged) {
|
||||
this.batchManaged = batchManaged;
|
||||
}
|
||||
|
||||
public String getDefaultDept() {
|
||||
return defaultDept;
|
||||
}
|
||||
|
||||
public void setDefaultDept(String defaultDept) {
|
||||
this.defaultDept = defaultDept;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·培训课程(培训学习 PARTIAL 缺口补全)。
|
||||
* 支持岗位定制化学习路径:按目标岗位设置课程,学员按岗位自动匹配。
|
||||
* status: 计划中 / 报名中 / 进行中 / 已完成 / 已取消
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_training_course")
|
||||
public class AdminTrainingCourse {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 课程编号(自动生成)。 */
|
||||
private String courseCode;
|
||||
|
||||
/** 课程名称。 */
|
||||
private String title;
|
||||
|
||||
/** 培训类型(新员工入职/岗位技能/安全规范/管理提升/法律合规/其他)。 */
|
||||
private String trainingType;
|
||||
|
||||
/** 目标岗位(适用岗位,逗号分隔;"全员"表示所有人)。 */
|
||||
private String targetPosition;
|
||||
|
||||
/** 目标部门(逗号分隔;"全部"表示所有部门)。 */
|
||||
private String targetDept;
|
||||
|
||||
/** 讲师/培训师。 */
|
||||
private String lecturer;
|
||||
|
||||
/** 培训场地/方式(线下+地点 或 线上平台名)。 */
|
||||
private String venue;
|
||||
|
||||
/** 培训日期(YYYY-MM-DD)。 */
|
||||
private String trainDate;
|
||||
|
||||
/** 培训时长(小时)。 */
|
||||
private Double durationHours;
|
||||
|
||||
/** 报名截止日期(YYYY-MM-DD)。 */
|
||||
private String enrollDeadline;
|
||||
|
||||
/** 学习材料描述/链接(逗号分隔多份)。 */
|
||||
private String materials;
|
||||
|
||||
/** 考核方式(笔试/线上答题/问卷/无)。 */
|
||||
private String assessMethod;
|
||||
|
||||
/** 合格分数线(0-100,0表示无需考核)。 */
|
||||
private Integer passScore;
|
||||
|
||||
/** 参训人数上限(0=不限)。 */
|
||||
private Integer capacityLimit;
|
||||
|
||||
/** 已报名人数(统计字段,随报名更新)。 */
|
||||
private Integer enrolledCount;
|
||||
|
||||
/** 培训小结(完成后填写)。 */
|
||||
private String summary;
|
||||
|
||||
/** 状态:计划中 / 报名中 / 进行中 / 已完成 / 已取消。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getCourseCode() { return courseCode; }
|
||||
public void setCourseCode(String courseCode) { this.courseCode = courseCode; }
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
public String getTrainingType() { return trainingType; }
|
||||
public void setTrainingType(String trainingType) { this.trainingType = trainingType; }
|
||||
public String getTargetPosition() { return targetPosition; }
|
||||
public void setTargetPosition(String targetPosition) { this.targetPosition = targetPosition; }
|
||||
public String getTargetDept() { return targetDept; }
|
||||
public void setTargetDept(String targetDept) { this.targetDept = targetDept; }
|
||||
public String getLecturer() { return lecturer; }
|
||||
public void setLecturer(String lecturer) { this.lecturer = lecturer; }
|
||||
public String getVenue() { return venue; }
|
||||
public void setVenue(String venue) { this.venue = venue; }
|
||||
public String getTrainDate() { return trainDate; }
|
||||
public void setTrainDate(String trainDate) { this.trainDate = trainDate; }
|
||||
public Double getDurationHours() { return durationHours; }
|
||||
public void setDurationHours(Double durationHours) { this.durationHours = durationHours; }
|
||||
public String getEnrollDeadline() { return enrollDeadline; }
|
||||
public void setEnrollDeadline(String enrollDeadline) { this.enrollDeadline = enrollDeadline; }
|
||||
public String getMaterials() { return materials; }
|
||||
public void setMaterials(String materials) { this.materials = materials; }
|
||||
public String getAssessMethod() { return assessMethod; }
|
||||
public void setAssessMethod(String assessMethod) { this.assessMethod = assessMethod; }
|
||||
public Integer getPassScore() { return passScore; }
|
||||
public void setPassScore(Integer passScore) { this.passScore = passScore; }
|
||||
public Integer getCapacityLimit() { return capacityLimit; }
|
||||
public void setCapacityLimit(Integer capacityLimit) { this.capacityLimit = capacityLimit; }
|
||||
public Integer getEnrolledCount() { return enrolledCount; }
|
||||
public void setEnrolledCount(Integer enrolledCount) { this.enrolledCount = enrolledCount; }
|
||||
public String getSummary() { return summary; }
|
||||
public void setSummary(String summary) { this.summary = summary; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,95 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·培训报名与考核明细(培训学习 PARTIAL 缺口补全)。
|
||||
* 一条记录 = 一名学员对一次培训课程的报名+签到+考核状态。
|
||||
* status: 已报名 / 已签到 / 已考核 / 未通过 / 已通过
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_training_enrollment")
|
||||
public class AdminTrainingEnrollment {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 关联培训课程 ID。 */
|
||||
private Long courseId;
|
||||
|
||||
/** 学员姓名。 */
|
||||
private String traineeName;
|
||||
|
||||
/** 学员工号。 */
|
||||
private String traineeNo;
|
||||
|
||||
/** 学员部门。 */
|
||||
private String dept;
|
||||
|
||||
/** 学员岗位。 */
|
||||
private String position;
|
||||
|
||||
/** 报名日期(YYYY-MM-DD)。 */
|
||||
private String enrollDate;
|
||||
|
||||
/** 是否签到出席。 */
|
||||
private boolean signedIn;
|
||||
|
||||
/** 签到时间。 */
|
||||
private Instant signInTime;
|
||||
|
||||
/** 学习进度(0-100,线上课程用;线下课统一0或100)。 */
|
||||
private Integer progressPct;
|
||||
|
||||
/** 考核得分。 */
|
||||
private Integer score;
|
||||
|
||||
/** 是否通过(按课程及格分自动判定)。 */
|
||||
private boolean passed;
|
||||
|
||||
/** 状态:已报名 / 已签到 / 已考核 / 未通过 / 已通过。 */
|
||||
private String status;
|
||||
|
||||
/** 备注。 */
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public Long getCourseId() { return courseId; }
|
||||
public void setCourseId(Long courseId) { this.courseId = courseId; }
|
||||
public String getTraineeName() { return traineeName; }
|
||||
public void setTraineeName(String traineeName) { this.traineeName = traineeName; }
|
||||
public String getTraineeNo() { return traineeNo; }
|
||||
public void setTraineeNo(String traineeNo) { this.traineeNo = traineeNo; }
|
||||
public String getDept() { return dept; }
|
||||
public void setDept(String dept) { this.dept = dept; }
|
||||
public String getPosition() { return position; }
|
||||
public void setPosition(String position) { this.position = position; }
|
||||
public String getEnrollDate() { return enrollDate; }
|
||||
public void setEnrollDate(String enrollDate) { this.enrollDate = enrollDate; }
|
||||
public boolean isSignedIn() { return signedIn; }
|
||||
public void setSignedIn(boolean signedIn) { this.signedIn = signedIn; }
|
||||
public Instant getSignInTime() { return signInTime; }
|
||||
public void setSignInTime(Instant signInTime) { this.signInTime = signInTime; }
|
||||
public Integer getProgressPct() { return progressPct; }
|
||||
public void setProgressPct(Integer progressPct) { this.progressPct = progressPct; }
|
||||
public Integer getScore() { return score; }
|
||||
public void setScore(Integer score) { this.score = score; }
|
||||
public boolean isPassed() { return passed; }
|
||||
public void setPassed(boolean passed) { this.passed = passed; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public String getRemark() { return remark; }
|
||||
public void setRemark(String remark) { this.remark = remark; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 车辆档案(行政/办公室·车辆管理)。记录车牌、品牌车型、座位数、购置日期、
|
||||
* 保险到期 / 年检到期 / 下次保养日期(用于到期分级预警),以及当前里程表读数。
|
||||
* usage 为 公务 / 通勤 之一;status 为 可用 / 出车中 / 维保 / 停用。
|
||||
* 用车申请({@link AdminVehicleDispatch})派车时把车置「出车中」,出车记录归还后置「可用」。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_vehicle")
|
||||
public class AdminVehicle {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String plate;
|
||||
|
||||
private String brand;
|
||||
|
||||
private String model;
|
||||
|
||||
/** 用途:公务 / 通勤。 */
|
||||
private String usage;
|
||||
|
||||
private Integer seats;
|
||||
|
||||
private String purchaseDate;
|
||||
|
||||
/** 保险到期日(YYYY-MM-DD)。 */
|
||||
private String insuranceDue;
|
||||
|
||||
/** 年检到期日(YYYY-MM-DD)。 */
|
||||
private String inspectionDue;
|
||||
|
||||
/** 下次保养日期(YYYY-MM-DD)。 */
|
||||
private String maintenanceDue;
|
||||
|
||||
/** 当前里程表读数(公里)。 */
|
||||
private Double odometer = 0d;
|
||||
|
||||
/** 可用 / 出车中 / 维保 / 停用。 */
|
||||
private String status;
|
||||
|
||||
/** 默认驾驶员 / 责任人。 */
|
||||
private String driver;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getPlate() {
|
||||
return plate;
|
||||
}
|
||||
|
||||
public void setPlate(String plate) {
|
||||
this.plate = plate;
|
||||
}
|
||||
|
||||
public String getBrand() {
|
||||
return brand;
|
||||
}
|
||||
|
||||
public void setBrand(String brand) {
|
||||
this.brand = brand;
|
||||
}
|
||||
|
||||
public String getModel() {
|
||||
return model;
|
||||
}
|
||||
|
||||
public void setModel(String model) {
|
||||
this.model = model;
|
||||
}
|
||||
|
||||
public String getUsage() {
|
||||
return usage;
|
||||
}
|
||||
|
||||
public void setUsage(String usage) {
|
||||
this.usage = usage;
|
||||
}
|
||||
|
||||
public Integer getSeats() {
|
||||
return seats;
|
||||
}
|
||||
|
||||
public void setSeats(Integer seats) {
|
||||
this.seats = seats;
|
||||
}
|
||||
|
||||
public String getPurchaseDate() {
|
||||
return purchaseDate;
|
||||
}
|
||||
|
||||
public void setPurchaseDate(String purchaseDate) {
|
||||
this.purchaseDate = purchaseDate;
|
||||
}
|
||||
|
||||
public String getInsuranceDue() {
|
||||
return insuranceDue;
|
||||
}
|
||||
|
||||
public void setInsuranceDue(String insuranceDue) {
|
||||
this.insuranceDue = insuranceDue;
|
||||
}
|
||||
|
||||
public String getInspectionDue() {
|
||||
return inspectionDue;
|
||||
}
|
||||
|
||||
public void setInspectionDue(String inspectionDue) {
|
||||
this.inspectionDue = inspectionDue;
|
||||
}
|
||||
|
||||
public String getMaintenanceDue() {
|
||||
return maintenanceDue;
|
||||
}
|
||||
|
||||
public void setMaintenanceDue(String maintenanceDue) {
|
||||
this.maintenanceDue = maintenanceDue;
|
||||
}
|
||||
|
||||
public Double getOdometer() {
|
||||
return odometer;
|
||||
}
|
||||
|
||||
public void setOdometer(Double odometer) {
|
||||
this.odometer = odometer;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getDriver() {
|
||||
return driver;
|
||||
}
|
||||
|
||||
public void setDriver(String driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,280 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 用车申请与派车单(行政/办公室·车辆管理)。员工提交用车申请(时间区间、起止点、事由、乘客数),
|
||||
* 调度员派车——派车时系统按时间区间做冲突检测(同一车辆同时段不可重复派车),紧急用车走绿色通道。
|
||||
*
|
||||
* 状态机:待派车 → 已派车 → 已出车 → 已归还 / 已取消。派车占用车辆(车置「出车中」),
|
||||
* 归还时回填实际里程/油耗/费用并自动算百公里油耗、车辆复位「可用」。
|
||||
* 费用 tollFee/parkingFee/fuelCost 为金额(BigDecimal)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_vehicle_dispatch")
|
||||
public class AdminVehicleDispatch {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String code;
|
||||
|
||||
/** 申请人。 */
|
||||
private String applicant;
|
||||
|
||||
private String dept;
|
||||
|
||||
/** 用车事由。 */
|
||||
private String reason;
|
||||
|
||||
private String fromPlace;
|
||||
|
||||
private String toPlace;
|
||||
|
||||
/** 用车开始时间(YYYY-MM-DD HH:mm 或 YYYY-MM-DD)。 */
|
||||
private String startTime;
|
||||
|
||||
/** 用车结束时间。 */
|
||||
private String endTime;
|
||||
|
||||
private Integer passengers;
|
||||
|
||||
/** 是否紧急用车(绿色通道)。 */
|
||||
private Boolean urgent = Boolean.FALSE;
|
||||
|
||||
/** 派定车辆 id(派车后回填)。 */
|
||||
private Long vehicleId;
|
||||
|
||||
private String vehiclePlate;
|
||||
|
||||
/** 指派驾驶员。 */
|
||||
private String driver;
|
||||
|
||||
/** 待派车 / 已派车 / 已出车 / 已归还 / 已取消。 */
|
||||
private String status;
|
||||
|
||||
// ---- 出车记录(归还时回填)----
|
||||
|
||||
/** 出车起始里程。 */
|
||||
private Double startOdometer;
|
||||
|
||||
/** 归还里程。 */
|
||||
private Double endOdometer;
|
||||
|
||||
/** 行驶里程(归还时自动算 endOdometer-startOdometer)。 */
|
||||
private Double mileage = 0d;
|
||||
|
||||
/** 油耗(升)。 */
|
||||
private Double fuelLiters = 0d;
|
||||
|
||||
/** 百公里油耗(自动算 fuelLiters/mileage*100)。 */
|
||||
private Double fuelPer100km = 0d;
|
||||
|
||||
private BigDecimal fuelCost = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal tollFee = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal parkingFee = BigDecimal.ZERO;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getApplicant() {
|
||||
return applicant;
|
||||
}
|
||||
|
||||
public void setApplicant(String applicant) {
|
||||
this.applicant = applicant;
|
||||
}
|
||||
|
||||
public String getDept() {
|
||||
return dept;
|
||||
}
|
||||
|
||||
public void setDept(String dept) {
|
||||
this.dept = dept;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public String getFromPlace() {
|
||||
return fromPlace;
|
||||
}
|
||||
|
||||
public void setFromPlace(String fromPlace) {
|
||||
this.fromPlace = fromPlace;
|
||||
}
|
||||
|
||||
public String getToPlace() {
|
||||
return toPlace;
|
||||
}
|
||||
|
||||
public void setToPlace(String toPlace) {
|
||||
this.toPlace = toPlace;
|
||||
}
|
||||
|
||||
public String getStartTime() {
|
||||
return startTime;
|
||||
}
|
||||
|
||||
public void setStartTime(String startTime) {
|
||||
this.startTime = startTime;
|
||||
}
|
||||
|
||||
public String getEndTime() {
|
||||
return endTime;
|
||||
}
|
||||
|
||||
public void setEndTime(String endTime) {
|
||||
this.endTime = endTime;
|
||||
}
|
||||
|
||||
public Integer getPassengers() {
|
||||
return passengers;
|
||||
}
|
||||
|
||||
public void setPassengers(Integer passengers) {
|
||||
this.passengers = passengers;
|
||||
}
|
||||
|
||||
public Boolean getUrgent() {
|
||||
return urgent;
|
||||
}
|
||||
|
||||
public void setUrgent(Boolean urgent) {
|
||||
this.urgent = urgent;
|
||||
}
|
||||
|
||||
public Long getVehicleId() {
|
||||
return vehicleId;
|
||||
}
|
||||
|
||||
public void setVehicleId(Long vehicleId) {
|
||||
this.vehicleId = vehicleId;
|
||||
}
|
||||
|
||||
public String getVehiclePlate() {
|
||||
return vehiclePlate;
|
||||
}
|
||||
|
||||
public void setVehiclePlate(String vehiclePlate) {
|
||||
this.vehiclePlate = vehiclePlate;
|
||||
}
|
||||
|
||||
public String getDriver() {
|
||||
return driver;
|
||||
}
|
||||
|
||||
public void setDriver(String driver) {
|
||||
this.driver = driver;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Double getStartOdometer() {
|
||||
return startOdometer;
|
||||
}
|
||||
|
||||
public void setStartOdometer(Double startOdometer) {
|
||||
this.startOdometer = startOdometer;
|
||||
}
|
||||
|
||||
public Double getEndOdometer() {
|
||||
return endOdometer;
|
||||
}
|
||||
|
||||
public void setEndOdometer(Double endOdometer) {
|
||||
this.endOdometer = endOdometer;
|
||||
}
|
||||
|
||||
public Double getMileage() {
|
||||
return mileage;
|
||||
}
|
||||
|
||||
public void setMileage(Double mileage) {
|
||||
this.mileage = mileage;
|
||||
}
|
||||
|
||||
public Double getFuelLiters() {
|
||||
return fuelLiters;
|
||||
}
|
||||
|
||||
public void setFuelLiters(Double fuelLiters) {
|
||||
this.fuelLiters = fuelLiters;
|
||||
}
|
||||
|
||||
public Double getFuelPer100km() {
|
||||
return fuelPer100km;
|
||||
}
|
||||
|
||||
public void setFuelPer100km(Double fuelPer100km) {
|
||||
this.fuelPer100km = fuelPer100km;
|
||||
}
|
||||
|
||||
public BigDecimal getFuelCost() {
|
||||
return fuelCost;
|
||||
}
|
||||
|
||||
public void setFuelCost(BigDecimal fuelCost) {
|
||||
this.fuelCost = fuelCost;
|
||||
}
|
||||
|
||||
public BigDecimal getTollFee() {
|
||||
return tollFee;
|
||||
}
|
||||
|
||||
public void setTollFee(BigDecimal tollFee) {
|
||||
this.tollFee = tollFee;
|
||||
}
|
||||
|
||||
public BigDecimal getParkingFee() {
|
||||
return parkingFee;
|
||||
}
|
||||
|
||||
public void setParkingFee(BigDecimal parkingFee) {
|
||||
this.parkingFee = parkingFee;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 行政/办公室·车辆违章记录。记录违章时间/地点/类型/罚款,通知责任人(驾驶员)并跟踪处理。
|
||||
* status: 未处理 / 处理中 / 已缴罚款 / 已销记
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_vehicle_violation")
|
||||
public class AdminVehicleViolation {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 关联车辆 ID。 */
|
||||
private Long vehicleId;
|
||||
|
||||
/** 车牌号(冗余,便于展示)。 */
|
||||
private String plate;
|
||||
|
||||
/** 驾驶员(责任人)。 */
|
||||
private String driver;
|
||||
|
||||
/** 违章日期(YYYY-MM-DD)。 */
|
||||
private String violationDate;
|
||||
|
||||
/** 违章地点。 */
|
||||
private String location;
|
||||
|
||||
/** 违章类型(超速/闯红灯/违停/不系安全带/其他)。 */
|
||||
private String violationType;
|
||||
|
||||
/** 违章描述。 */
|
||||
private String description;
|
||||
|
||||
/** 罚款金额(元)。 */
|
||||
private BigDecimal fine;
|
||||
|
||||
/** 扣分数量。 */
|
||||
private Integer deductPoints;
|
||||
|
||||
/** 通知责任人时间。 */
|
||||
private Instant notifiedAt;
|
||||
|
||||
/** 处理说明。 */
|
||||
private String handleNote;
|
||||
|
||||
/** 状态:未处理 / 处理中 / 已缴罚款 / 已销记。 */
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public Long getVehicleId() { return vehicleId; }
|
||||
public void setVehicleId(Long vehicleId) { this.vehicleId = vehicleId; }
|
||||
public String getPlate() { return plate; }
|
||||
public void setPlate(String plate) { this.plate = plate; }
|
||||
public String getDriver() { return driver; }
|
||||
public void setDriver(String driver) { this.driver = driver; }
|
||||
public String getViolationDate() { return violationDate; }
|
||||
public void setViolationDate(String violationDate) { this.violationDate = violationDate; }
|
||||
public String getLocation() { return location; }
|
||||
public void setLocation(String location) { this.location = location; }
|
||||
public String getViolationType() { return violationType; }
|
||||
public void setViolationType(String violationType) { this.violationType = violationType; }
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
public BigDecimal getFine() { return fine; }
|
||||
public void setFine(BigDecimal fine) { this.fine = fine; }
|
||||
public Integer getDeductPoints() { return deductPoints; }
|
||||
public void setDeductPoints(Integer deductPoints) { this.deductPoints = deductPoints; }
|
||||
public Instant getNotifiedAt() { return notifiedAt; }
|
||||
public void setNotifiedAt(Instant notifiedAt) { this.notifiedAt = notifiedAt; }
|
||||
public String getHandleNote() { return handleNote; }
|
||||
public void setHandleNote(String handleNote) { this.handleNote = handleNote; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 访客预约与门禁记录(行政/办公室·安保与门禁)。访客在线预约(姓名、单位、被访人、来访时间)
|
||||
* → 被访人确认(生成访客通行码 passCode)→ 门卫扫码放行(签到)→ 离开签退。
|
||||
*
|
||||
* 状态机:待确认 → 已确认(生成通行码)→ 已入场(签到)→ 已离场 / 已拒绝 / 已过期。
|
||||
* passCode 仅在确认后生成,用于门卫核验。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "admin_visitor")
|
||||
public class AdminVisitor {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String code;
|
||||
|
||||
private String visitorName;
|
||||
|
||||
/** 访客单位。 */
|
||||
private String company;
|
||||
|
||||
/** 身份证号(登记用)。 */
|
||||
private String idCard;
|
||||
|
||||
private String phone;
|
||||
|
||||
/** 被访人。 */
|
||||
private String host;
|
||||
|
||||
/** 被访部门。 */
|
||||
private String hostDept;
|
||||
|
||||
/** 来访事由。 */
|
||||
private String reason;
|
||||
|
||||
/** 预约来访时间(YYYY-MM-DD HH:mm 或 YYYY-MM-DD)。 */
|
||||
private String visitTime;
|
||||
|
||||
private Integer companions;
|
||||
|
||||
/** 待确认 / 已确认 / 已入场 / 已离场 / 已拒绝 / 已过期。 */
|
||||
private String status;
|
||||
|
||||
/** 通行码(确认后生成)。 */
|
||||
private String passCode;
|
||||
|
||||
/** 实际签到时间(门卫扫码)。 */
|
||||
private String checkinTime;
|
||||
|
||||
/** 实际签退时间。 */
|
||||
private String checkoutTime;
|
||||
|
||||
/** 确认人/拒绝人备注。 */
|
||||
private String hostNote;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getVisitorName() {
|
||||
return visitorName;
|
||||
}
|
||||
|
||||
public void setVisitorName(String visitorName) {
|
||||
this.visitorName = visitorName;
|
||||
}
|
||||
|
||||
public String getCompany() {
|
||||
return company;
|
||||
}
|
||||
|
||||
public void setCompany(String company) {
|
||||
this.company = company;
|
||||
}
|
||||
|
||||
public String getIdCard() {
|
||||
return idCard;
|
||||
}
|
||||
|
||||
public void setIdCard(String idCard) {
|
||||
this.idCard = idCard;
|
||||
}
|
||||
|
||||
public String getPhone() {
|
||||
return phone;
|
||||
}
|
||||
|
||||
public void setPhone(String phone) {
|
||||
this.phone = phone;
|
||||
}
|
||||
|
||||
public String getHost() {
|
||||
return host;
|
||||
}
|
||||
|
||||
public void setHost(String host) {
|
||||
this.host = host;
|
||||
}
|
||||
|
||||
public String getHostDept() {
|
||||
return hostDept;
|
||||
}
|
||||
|
||||
public void setHostDept(String hostDept) {
|
||||
this.hostDept = hostDept;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public String getVisitTime() {
|
||||
return visitTime;
|
||||
}
|
||||
|
||||
public void setVisitTime(String visitTime) {
|
||||
this.visitTime = visitTime;
|
||||
}
|
||||
|
||||
public Integer getCompanions() {
|
||||
return companions;
|
||||
}
|
||||
|
||||
public void setCompanions(Integer companions) {
|
||||
this.companions = companions;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getPassCode() {
|
||||
return passCode;
|
||||
}
|
||||
|
||||
public void setPassCode(String passCode) {
|
||||
this.passCode = passCode;
|
||||
}
|
||||
|
||||
public String getCheckinTime() {
|
||||
return checkinTime;
|
||||
}
|
||||
|
||||
public void setCheckinTime(String checkinTime) {
|
||||
this.checkinTime = checkinTime;
|
||||
}
|
||||
|
||||
public String getCheckoutTime() {
|
||||
return checkoutTime;
|
||||
}
|
||||
|
||||
public void setCheckoutTime(String checkoutTime) {
|
||||
this.checkoutTime = checkoutTime;
|
||||
}
|
||||
|
||||
public String getHostNote() {
|
||||
return hostNote;
|
||||
}
|
||||
|
||||
public void setHostNote(String hostNote) {
|
||||
this.hostNote = hostNote;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/** News / announcement post. category is one of: 新闻 / 公告. */
|
||||
@Entity
|
||||
@Table(name = "announcement")
|
||||
public class Announcement {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 新闻 / 公告 */
|
||||
private String category;
|
||||
|
||||
private String title;
|
||||
|
||||
@Lob
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String content;
|
||||
|
||||
private String author;
|
||||
|
||||
@Column(name = "is_top")
|
||||
private boolean top;
|
||||
|
||||
private Instant publishedAt;
|
||||
|
||||
/** 发布范围(逗号分隔:全体员工 / 各部门)。 */
|
||||
@Column(length = 1000)
|
||||
private String publishRange;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public boolean isTop() {
|
||||
return top;
|
||||
}
|
||||
|
||||
public void setTop(boolean top) {
|
||||
this.top = top;
|
||||
}
|
||||
|
||||
public Instant getPublishedAt() {
|
||||
return publishedAt;
|
||||
}
|
||||
|
||||
public void setPublishedAt(Instant publishedAt) {
|
||||
this.publishedAt = publishedAt;
|
||||
}
|
||||
|
||||
public String getPublishRange() {
|
||||
return publishRange;
|
||||
}
|
||||
|
||||
public void setPublishRange(String publishRange) {
|
||||
this.publishRange = publishRange;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,199 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Lob;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
/**
|
||||
* 科技年报填报(知识产权部,需求功能12「年报与统计填报辅助」— 补深 LOGIC_GAP)。
|
||||
*
|
||||
* 支持多类型年报模板(工程勘察设计年报/经开区统计年报/火炬报表/高企/专精特新等),
|
||||
* 系统自动从 RdExpense/Patent/RdProject/PersonnelCert 四仓抽取基础数据预填关键指标;
|
||||
* 填报数据校验:研发费用占比一致性(与项目数匹配),人员工时总量范围校验;
|
||||
* 支持历年对比:同一 reportType 按 fiscalYear 查近几年对比;
|
||||
* 状态机:草稿 → 自动预填 → 填报中 → 内部审核 → 已提交 → 已反馈。
|
||||
*
|
||||
* 关键指标:rdExpenseAmount(研发费用)、staffRdCount(科技人员数)、patentCount(有效专利数)、
|
||||
* revenueAmount(营收,手填)、rdExpenseRate(研发投入占比,自动算)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "annual_report")
|
||||
public class AnnualReport {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 报表编号,系统自动生成(AR-2026-001)。 */
|
||||
private String code;
|
||||
|
||||
/** 报表名称(如"2025年度火炬统计年报")。 */
|
||||
private String reportName;
|
||||
|
||||
/**
|
||||
* 报表类型:高企认定 / 专精特新 / 火炬报表 / 经开区统计 / 工程勘察设计年报 / 科技计划 / 其他。
|
||||
*/
|
||||
private String reportType;
|
||||
|
||||
/** 填报对象(科技局 / 统计局 / 税务局 / 高企认定 / 工程学会 等)。 */
|
||||
private String reportTarget;
|
||||
|
||||
/** 填报年度(四位整数)。 */
|
||||
private Integer fiscalYear;
|
||||
|
||||
/** 截止日期(YYYY-MM-DD)。 */
|
||||
private String deadline;
|
||||
|
||||
/** 状态:草稿 / 填报中 / 内部审核 / 已提交 / 已反馈。 */
|
||||
private String status;
|
||||
|
||||
// ---------- 自动抽取并预填的基础指标 ----------
|
||||
|
||||
/** 营收金额(手填,单位元)。 */
|
||||
private BigDecimal revenueAmount = BigDecimal.ZERO;
|
||||
|
||||
/** 研发费用(从 RdExpense 自动归集,单位元)。 */
|
||||
private BigDecimal rdExpenseAmount = BigDecimal.ZERO;
|
||||
|
||||
/** 研发投入占比(rdExpenseAmount/revenueAmount,自动算)。 */
|
||||
private BigDecimal rdExpenseRate = BigDecimal.ZERO;
|
||||
|
||||
/** 科技人员数(从 PersonnelCert 自动统计)。 */
|
||||
private Integer staffRdCount;
|
||||
|
||||
/** 企业总人员数(手填或 HR 同步)。 */
|
||||
private Integer staffTotalCount;
|
||||
|
||||
/** 科技人员占比(自动算)。 */
|
||||
private BigDecimal staffRdRate = BigDecimal.ZERO;
|
||||
|
||||
/** 有效专利数(从 Patent status=授权维持 自动统计)。 */
|
||||
private Integer patentCount;
|
||||
|
||||
/** 发明专利数(专利类型=发明,授权维持)。 */
|
||||
private Integer inventionPatentCount;
|
||||
|
||||
/** 研发项目数(从 RdProject 自动统计,按年度)。 */
|
||||
private Integer rdProjectCount;
|
||||
|
||||
/** 科技成果数(从 TechAchievement 自动统计)。 */
|
||||
private Integer techAchievementCount;
|
||||
|
||||
/** 技术合同金额(从 TechContract 自动统计,单位元)。 */
|
||||
private BigDecimal techContractAmount = BigDecimal.ZERO;
|
||||
|
||||
// ---------- 扩展填报字段(模板化,各类年报共享) ----------
|
||||
|
||||
/** 关键指标描述(校验摘要/主要指标文字汇总)。 */
|
||||
@Lob
|
||||
private String keyIndicatorSummary;
|
||||
|
||||
/** 数据一致性校验结果(自动校验后填入,如"研发费用占比符合要求")。 */
|
||||
@Lob
|
||||
private String validationResult;
|
||||
|
||||
/** 校验是否通过:true 全部通过,false 有警告或错误。 */
|
||||
private Boolean validationPassed;
|
||||
|
||||
/** 备注/填报说明。 */
|
||||
@Lob
|
||||
private String remark;
|
||||
|
||||
/** 填报负责人。 */
|
||||
private String owner;
|
||||
|
||||
/** 审核意见。 */
|
||||
private String reviewNote;
|
||||
|
||||
private Instant createdAt;
|
||||
private Instant updatedAt;
|
||||
|
||||
// ---------- getters/setters ----------
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public String getCode() { return code; }
|
||||
public void setCode(String code) { this.code = code; }
|
||||
|
||||
public String getReportName() { return reportName; }
|
||||
public void setReportName(String reportName) { this.reportName = reportName; }
|
||||
|
||||
public String getReportType() { return reportType; }
|
||||
public void setReportType(String reportType) { this.reportType = reportType; }
|
||||
|
||||
public String getReportTarget() { return reportTarget; }
|
||||
public void setReportTarget(String reportTarget) { this.reportTarget = reportTarget; }
|
||||
|
||||
public Integer getFiscalYear() { return fiscalYear; }
|
||||
public void setFiscalYear(Integer fiscalYear) { this.fiscalYear = fiscalYear; }
|
||||
|
||||
public String getDeadline() { return deadline; }
|
||||
public void setDeadline(String deadline) { this.deadline = deadline; }
|
||||
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
|
||||
public BigDecimal getRevenueAmount() { return revenueAmount; }
|
||||
public void setRevenueAmount(BigDecimal revenueAmount) { this.revenueAmount = revenueAmount; }
|
||||
|
||||
public BigDecimal getRdExpenseAmount() { return rdExpenseAmount; }
|
||||
public void setRdExpenseAmount(BigDecimal rdExpenseAmount) { this.rdExpenseAmount = rdExpenseAmount; }
|
||||
|
||||
public BigDecimal getRdExpenseRate() { return rdExpenseRate; }
|
||||
public void setRdExpenseRate(BigDecimal rdExpenseRate) { this.rdExpenseRate = rdExpenseRate; }
|
||||
|
||||
public Integer getStaffRdCount() { return staffRdCount; }
|
||||
public void setStaffRdCount(Integer staffRdCount) { this.staffRdCount = staffRdCount; }
|
||||
|
||||
public Integer getStaffTotalCount() { return staffTotalCount; }
|
||||
public void setStaffTotalCount(Integer staffTotalCount) { this.staffTotalCount = staffTotalCount; }
|
||||
|
||||
public BigDecimal getStaffRdRate() { return staffRdRate; }
|
||||
public void setStaffRdRate(BigDecimal staffRdRate) { this.staffRdRate = staffRdRate; }
|
||||
|
||||
public Integer getPatentCount() { return patentCount; }
|
||||
public void setPatentCount(Integer patentCount) { this.patentCount = patentCount; }
|
||||
|
||||
public Integer getInventionPatentCount() { return inventionPatentCount; }
|
||||
public void setInventionPatentCount(Integer inventionPatentCount) { this.inventionPatentCount = inventionPatentCount; }
|
||||
|
||||
public Integer getRdProjectCount() { return rdProjectCount; }
|
||||
public void setRdProjectCount(Integer rdProjectCount) { this.rdProjectCount = rdProjectCount; }
|
||||
|
||||
public Integer getTechAchievementCount() { return techAchievementCount; }
|
||||
public void setTechAchievementCount(Integer techAchievementCount) { this.techAchievementCount = techAchievementCount; }
|
||||
|
||||
public BigDecimal getTechContractAmount() { return techContractAmount; }
|
||||
public void setTechContractAmount(BigDecimal techContractAmount) { this.techContractAmount = techContractAmount; }
|
||||
|
||||
public String getKeyIndicatorSummary() { return keyIndicatorSummary; }
|
||||
public void setKeyIndicatorSummary(String keyIndicatorSummary) { this.keyIndicatorSummary = keyIndicatorSummary; }
|
||||
|
||||
public String getValidationResult() { return validationResult; }
|
||||
public void setValidationResult(String validationResult) { this.validationResult = validationResult; }
|
||||
|
||||
public Boolean getValidationPassed() { return validationPassed; }
|
||||
public void setValidationPassed(Boolean validationPassed) { this.validationPassed = validationPassed; }
|
||||
|
||||
public String getRemark() { return remark; }
|
||||
public void setRemark(String remark) { this.remark = remark; }
|
||||
|
||||
public String getOwner() { return owner; }
|
||||
public void setOwner(String owner) { this.owner = owner; }
|
||||
|
||||
public String getReviewNote() { return reviewNote; }
|
||||
public void setReviewNote(String reviewNote) { this.reviewNote = reviewNote; }
|
||||
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
@@ -0,0 +1,200 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 应收/应付款项(财务管理·往来核算)。一条 ArApItem 代表一笔往来账:
|
||||
* arApType 类型(应收 / 应付);partyName 往来单位(客户/供应商);
|
||||
* relatedRef 关联合同号或发票号;amount 款项金额;writtenOff 已核销;
|
||||
* unwrittenOff 未核销(= 金额 - 已核销,服务端维护);status 流转
|
||||
* 未核销 → 部分核销 → 已结清(全核销即「已结清」);dueDate 账期;overdue 是否逾期(按账期)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "ar_ap_item")
|
||||
public class ArApItem {
|
||||
|
||||
public static final String T_AR = "应收";
|
||||
public static final String T_AP = "应付";
|
||||
|
||||
public static final String S_OPEN = "未核销";
|
||||
public static final String S_PARTIAL = "部分核销";
|
||||
public static final String S_SETTLED = "已结清";
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String code;
|
||||
|
||||
/** 类型:应收 / 应付。 */
|
||||
private String arApType;
|
||||
|
||||
/** 往来单位(客户 / 供应商)。 */
|
||||
private String partyName;
|
||||
|
||||
/** 关联合同号或发票号。 */
|
||||
private String relatedRef;
|
||||
|
||||
/** 关联合同 id(可空)。 */
|
||||
private Long contractId;
|
||||
|
||||
/** 关联发票 id(可空)。 */
|
||||
private Long invoiceId;
|
||||
|
||||
/** 款项金额。 */
|
||||
private BigDecimal amount = BigDecimal.ZERO;
|
||||
|
||||
/** 已核销金额。 */
|
||||
private BigDecimal writtenOff = BigDecimal.ZERO;
|
||||
|
||||
/** 未核销金额(= 金额 - 已核销)。 */
|
||||
private BigDecimal unwrittenOff = BigDecimal.ZERO;
|
||||
|
||||
/** 未核销 / 部分核销 / 已结清。 */
|
||||
private String status;
|
||||
|
||||
private String dueDate;
|
||||
|
||||
private String companySubject;
|
||||
|
||||
private String owner;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getArApType() {
|
||||
return arApType;
|
||||
}
|
||||
|
||||
public void setArApType(String arApType) {
|
||||
this.arApType = arApType;
|
||||
}
|
||||
|
||||
public String getPartyName() {
|
||||
return partyName;
|
||||
}
|
||||
|
||||
public void setPartyName(String partyName) {
|
||||
this.partyName = partyName;
|
||||
}
|
||||
|
||||
public String getRelatedRef() {
|
||||
return relatedRef;
|
||||
}
|
||||
|
||||
public void setRelatedRef(String relatedRef) {
|
||||
this.relatedRef = relatedRef;
|
||||
}
|
||||
|
||||
public Long getContractId() {
|
||||
return contractId;
|
||||
}
|
||||
|
||||
public void setContractId(Long contractId) {
|
||||
this.contractId = contractId;
|
||||
}
|
||||
|
||||
public Long getInvoiceId() {
|
||||
return invoiceId;
|
||||
}
|
||||
|
||||
public void setInvoiceId(Long invoiceId) {
|
||||
this.invoiceId = invoiceId;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public BigDecimal getWrittenOff() {
|
||||
return writtenOff;
|
||||
}
|
||||
|
||||
public void setWrittenOff(BigDecimal writtenOff) {
|
||||
this.writtenOff = writtenOff;
|
||||
}
|
||||
|
||||
public BigDecimal getUnwrittenOff() {
|
||||
return unwrittenOff;
|
||||
}
|
||||
|
||||
public void setUnwrittenOff(BigDecimal unwrittenOff) {
|
||||
this.unwrittenOff = unwrittenOff;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getDueDate() {
|
||||
return dueDate;
|
||||
}
|
||||
|
||||
public void setDueDate(String dueDate) {
|
||||
this.dueDate = dueDate;
|
||||
}
|
||||
|
||||
public String getCompanySubject() {
|
||||
return companySubject;
|
||||
}
|
||||
|
||||
public void setCompanySubject(String companySubject) {
|
||||
this.companySubject = companySubject;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,186 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* A unified archive / knowledge-base entry. category is one of 项目档案 /
|
||||
* 合同档案 / 申报成果 / 制度文件 / 会议纪要 / 技术资料; sourceType is one of
|
||||
* 合同 / 项目 / 申报 / 会议 / 手动. sourceId optionally links the entry back to
|
||||
* the originating business record. fileSize is in bytes; tags is a
|
||||
* comma-separated keyword list used for keyword search. accessLevel is one
|
||||
* of 公开 / 内部 / 机密 and controls visibility; fileUrl is a preview link to
|
||||
* the underlying file.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive")
|
||||
public class Archive {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String category;
|
||||
|
||||
private String title;
|
||||
|
||||
private String sourceType;
|
||||
|
||||
private Long sourceId;
|
||||
|
||||
private String fileName;
|
||||
|
||||
private String fileType;
|
||||
|
||||
private long fileSize;
|
||||
|
||||
private String uploader;
|
||||
|
||||
private String archiveDate;
|
||||
|
||||
private String tags;
|
||||
|
||||
private String summary;
|
||||
|
||||
private String accessLevel;
|
||||
|
||||
private String fileUrl;
|
||||
|
||||
/** Optional link to the real stored binary (StoredFile.id) for online preview/download. */
|
||||
private Long storedFileId;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getSourceType() {
|
||||
return sourceType;
|
||||
}
|
||||
|
||||
public void setSourceType(String sourceType) {
|
||||
this.sourceType = sourceType;
|
||||
}
|
||||
|
||||
public Long getSourceId() {
|
||||
return sourceId;
|
||||
}
|
||||
|
||||
public void setSourceId(Long sourceId) {
|
||||
this.sourceId = sourceId;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public String getFileType() {
|
||||
return fileType;
|
||||
}
|
||||
|
||||
public void setFileType(String fileType) {
|
||||
this.fileType = fileType;
|
||||
}
|
||||
|
||||
public long getFileSize() {
|
||||
return fileSize;
|
||||
}
|
||||
|
||||
public void setFileSize(long fileSize) {
|
||||
this.fileSize = fileSize;
|
||||
}
|
||||
|
||||
public String getUploader() {
|
||||
return uploader;
|
||||
}
|
||||
|
||||
public void setUploader(String uploader) {
|
||||
this.uploader = uploader;
|
||||
}
|
||||
|
||||
public String getArchiveDate() {
|
||||
return archiveDate;
|
||||
}
|
||||
|
||||
public void setArchiveDate(String archiveDate) {
|
||||
this.archiveDate = archiveDate;
|
||||
}
|
||||
|
||||
public String getTags() {
|
||||
return tags;
|
||||
}
|
||||
|
||||
public void setTags(String tags) {
|
||||
this.tags = tags;
|
||||
}
|
||||
|
||||
public String getSummary() {
|
||||
return summary;
|
||||
}
|
||||
|
||||
public void setSummary(String summary) {
|
||||
this.summary = summary;
|
||||
}
|
||||
|
||||
public String getAccessLevel() {
|
||||
return accessLevel;
|
||||
}
|
||||
|
||||
public void setAccessLevel(String accessLevel) {
|
||||
this.accessLevel = accessLevel;
|
||||
}
|
||||
|
||||
public String getFileUrl() {
|
||||
return fileUrl;
|
||||
}
|
||||
|
||||
public void setFileUrl(String fileUrl) {
|
||||
this.fileUrl = fileUrl;
|
||||
}
|
||||
|
||||
public Long getStoredFileId() {
|
||||
return storedFileId;
|
||||
}
|
||||
|
||||
public void setStoredFileId(Long storedFileId) {
|
||||
this.storedFileId = storedFileId;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,132 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 档案访问日志(资料室·Module 6「访问日志」+ Module 7「合规审计支持」)。所有对档案的访问
|
||||
* (查询/预览/下载/打印/修改/删除/授权变更)均落一条日志,记录操作人/时间/IP/操作类型。
|
||||
* 补审计「访问日志、权限变更记录、销毁记录维度审计」缺口(满足审计追溯要求)。
|
||||
*
|
||||
* <p>action 操作类型:查询 / 预览 / 下载 / 打印 / 修改 / 删除 / 授权变更 / 销毁。
|
||||
* archiveId 关联档案(可空,分类级操作时为空)。日志只追加不修改,供 Module 7 四维审计报告取数。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_access_log")
|
||||
public class ArchiveAccessLog {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 关联档案 id(可空)。 */
|
||||
private Long archiveId;
|
||||
|
||||
/** 档案标题(冗余)。 */
|
||||
private String archiveTitle;
|
||||
|
||||
/** 操作类型:查询 / 预览 / 下载 / 打印 / 修改 / 删除 / 授权变更 / 销毁。 */
|
||||
private String action;
|
||||
|
||||
/** 操作人。 */
|
||||
private String operator;
|
||||
|
||||
/** 操作人部门。 */
|
||||
private String operatorDept;
|
||||
|
||||
/** 来源 IP。 */
|
||||
private String ip;
|
||||
|
||||
/** 操作详情/备注。 */
|
||||
private String detail;
|
||||
|
||||
/** 操作时间(YYYY-MM-DD HH:mm:ss)。 */
|
||||
private String occurredAt;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getArchiveId() {
|
||||
return archiveId;
|
||||
}
|
||||
|
||||
public void setArchiveId(Long archiveId) {
|
||||
this.archiveId = archiveId;
|
||||
}
|
||||
|
||||
public String getArchiveTitle() {
|
||||
return archiveTitle;
|
||||
}
|
||||
|
||||
public void setArchiveTitle(String archiveTitle) {
|
||||
this.archiveTitle = archiveTitle;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public String getOperator() {
|
||||
return operator;
|
||||
}
|
||||
|
||||
public void setOperator(String operator) {
|
||||
this.operator = operator;
|
||||
}
|
||||
|
||||
public String getOperatorDept() {
|
||||
return operatorDept;
|
||||
}
|
||||
|
||||
public void setOperatorDept(String operatorDept) {
|
||||
this.operatorDept = operatorDept;
|
||||
}
|
||||
|
||||
public String getIp() {
|
||||
return ip;
|
||||
}
|
||||
|
||||
public void setIp(String ip) {
|
||||
this.ip = ip;
|
||||
}
|
||||
|
||||
public String getDetail() {
|
||||
return detail;
|
||||
}
|
||||
|
||||
public void setDetail(String detail) {
|
||||
this.detail = detail;
|
||||
}
|
||||
|
||||
public String getOccurredAt() {
|
||||
return occurredAt;
|
||||
}
|
||||
|
||||
public void setOccurredAt(String occurredAt) {
|
||||
this.occurredAt = occurredAt;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 档案细粒度授权(资料室·Module 6「细粒度权限」)。一条 ArchiveAcl 为「某授权主体」对
|
||||
* 「某档案 / 某分类(文件夹)」的一组操作位授权(可见/预览/下载/打印/改/删/授权),支持按
|
||||
* 部门 / 岗位 / 项目组 / 个人动态授权。补审计「细粒度权限无、ArchiveController 仅密级粗粒度」缺口。
|
||||
*
|
||||
* <p>授权主体 granteeType:部门 / 岗位 / 项目组 / 个人;granteeKey 为对应的部门名/岗位名/项目组名/人名。
|
||||
* 授权目标 targetType:档案 / 分类;targetId 为 Archive.id 或 ArchiveCategory.id(分类即文件夹粒度)。
|
||||
* 七个操作位为独立布尔:canView/canPreview/canDownload/canPrint/canEdit/canDelete/canGrant。
|
||||
* expireDate 为授权到期日(可空表示长期),过期由查询/看板判定失效。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_acl")
|
||||
public class ArchiveAcl {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 授权主体类型:部门 / 岗位 / 项目组 / 个人。 */
|
||||
private String granteeType;
|
||||
|
||||
/** 授权主体标识(部门名/岗位名/项目组名/人名)。 */
|
||||
private String granteeKey;
|
||||
|
||||
/** 授权目标类型:档案 / 分类。 */
|
||||
private String targetType;
|
||||
|
||||
/** 授权目标 id(Archive.id 或 ArchiveCategory.id)。 */
|
||||
private Long targetId;
|
||||
|
||||
/** 冗余目标名(便于列表展示)。 */
|
||||
private String targetName;
|
||||
|
||||
private Boolean canView = Boolean.TRUE;
|
||||
|
||||
private Boolean canPreview = Boolean.TRUE;
|
||||
|
||||
private Boolean canDownload = Boolean.FALSE;
|
||||
|
||||
private Boolean canPrint = Boolean.FALSE;
|
||||
|
||||
private Boolean canEdit = Boolean.FALSE;
|
||||
|
||||
private Boolean canDelete = Boolean.FALSE;
|
||||
|
||||
private Boolean canGrant = Boolean.FALSE;
|
||||
|
||||
/** 授权到期日(YYYY-MM-DD,可空=长期)。 */
|
||||
private String expireDate;
|
||||
|
||||
/** 授权人。 */
|
||||
private String grantedBy;
|
||||
|
||||
/** 授权日期。 */
|
||||
private String grantDate;
|
||||
|
||||
/** 是否启用(撤销授权时置 false 而非删除,留痕)。 */
|
||||
private Boolean enabled = Boolean.TRUE;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getGranteeType() {
|
||||
return granteeType;
|
||||
}
|
||||
|
||||
public void setGranteeType(String granteeType) {
|
||||
this.granteeType = granteeType;
|
||||
}
|
||||
|
||||
public String getGranteeKey() {
|
||||
return granteeKey;
|
||||
}
|
||||
|
||||
public void setGranteeKey(String granteeKey) {
|
||||
this.granteeKey = granteeKey;
|
||||
}
|
||||
|
||||
public String getTargetType() {
|
||||
return targetType;
|
||||
}
|
||||
|
||||
public void setTargetType(String targetType) {
|
||||
this.targetType = targetType;
|
||||
}
|
||||
|
||||
public Long getTargetId() {
|
||||
return targetId;
|
||||
}
|
||||
|
||||
public void setTargetId(Long targetId) {
|
||||
this.targetId = targetId;
|
||||
}
|
||||
|
||||
public String getTargetName() {
|
||||
return targetName;
|
||||
}
|
||||
|
||||
public void setTargetName(String targetName) {
|
||||
this.targetName = targetName;
|
||||
}
|
||||
|
||||
public Boolean getCanView() {
|
||||
return canView;
|
||||
}
|
||||
|
||||
public void setCanView(Boolean canView) {
|
||||
this.canView = canView;
|
||||
}
|
||||
|
||||
public Boolean getCanPreview() {
|
||||
return canPreview;
|
||||
}
|
||||
|
||||
public void setCanPreview(Boolean canPreview) {
|
||||
this.canPreview = canPreview;
|
||||
}
|
||||
|
||||
public Boolean getCanDownload() {
|
||||
return canDownload;
|
||||
}
|
||||
|
||||
public void setCanDownload(Boolean canDownload) {
|
||||
this.canDownload = canDownload;
|
||||
}
|
||||
|
||||
public Boolean getCanPrint() {
|
||||
return canPrint;
|
||||
}
|
||||
|
||||
public void setCanPrint(Boolean canPrint) {
|
||||
this.canPrint = canPrint;
|
||||
}
|
||||
|
||||
public Boolean getCanEdit() {
|
||||
return canEdit;
|
||||
}
|
||||
|
||||
public void setCanEdit(Boolean canEdit) {
|
||||
this.canEdit = canEdit;
|
||||
}
|
||||
|
||||
public Boolean getCanDelete() {
|
||||
return canDelete;
|
||||
}
|
||||
|
||||
public void setCanDelete(Boolean canDelete) {
|
||||
this.canDelete = canDelete;
|
||||
}
|
||||
|
||||
public Boolean getCanGrant() {
|
||||
return canGrant;
|
||||
}
|
||||
|
||||
public void setCanGrant(Boolean canGrant) {
|
||||
this.canGrant = canGrant;
|
||||
}
|
||||
|
||||
public String getExpireDate() {
|
||||
return expireDate;
|
||||
}
|
||||
|
||||
public void setExpireDate(String expireDate) {
|
||||
this.expireDate = expireDate;
|
||||
}
|
||||
|
||||
public String getGrantedBy() {
|
||||
return grantedBy;
|
||||
}
|
||||
|
||||
public void setGrantedBy(String grantedBy) {
|
||||
this.grantedBy = grantedBy;
|
||||
}
|
||||
|
||||
public String getGrantDate() {
|
||||
return grantDate;
|
||||
}
|
||||
|
||||
public void setGrantDate(String grantDate) {
|
||||
this.grantDate = grantDate;
|
||||
}
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,167 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 资料室电子档案自动备份执行日志。
|
||||
*
|
||||
* 每次 @Scheduled 备份调度运行后追加一条记录(成功/失败/跳过),
|
||||
* 作为「自动备份无真实调度执行」缺口的真实落地证明,
|
||||
* 替代 ArchiveComplianceController.backup-config 仅返回静态配置的现状。
|
||||
*
|
||||
* 状态:运行中 / 成功 / 失败 / 跳过。
|
||||
* backupType:增量 / 全量。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_backup_log")
|
||||
public class ArchiveBackupLog {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 备份类型:增量 / 全量。 */
|
||||
private String backupType;
|
||||
|
||||
/** 备份状态:运行中 / 成功 / 失败 / 跳过。 */
|
||||
private String backupStatus;
|
||||
|
||||
/** 触发方式:定时 / 手动。 */
|
||||
private String triggerMode;
|
||||
|
||||
/** 备份开始时间(ISO 字符串,便于 SQLite 存储与前端展示)。 */
|
||||
private String startedAt;
|
||||
|
||||
/** 备份完成时间。 */
|
||||
private String finishedAt;
|
||||
|
||||
/** 耗时秒数(完成后填写)。 */
|
||||
private Long durationSeconds;
|
||||
|
||||
/** 备份文件路径(本地快照路径或 S3 前缀)。 */
|
||||
private String backupPath;
|
||||
|
||||
/** 备份文件大小(字节,-1 表示未知)。 */
|
||||
private Long fileSizeBytes;
|
||||
|
||||
/** 备份档案条数(实际扫描档案总数)。 */
|
||||
private Long archiveCount;
|
||||
|
||||
/** 操作结果详细说明(成功摘要 / 失败原因)。 */
|
||||
private String resultMsg;
|
||||
|
||||
/** 执行节点(本机 hostname,多活场景区分节点)。 */
|
||||
private String executorNode;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getBackupType() {
|
||||
return backupType;
|
||||
}
|
||||
|
||||
public void setBackupType(String backupType) {
|
||||
this.backupType = backupType;
|
||||
}
|
||||
|
||||
public String getBackupStatus() {
|
||||
return backupStatus;
|
||||
}
|
||||
|
||||
public void setBackupStatus(String backupStatus) {
|
||||
this.backupStatus = backupStatus;
|
||||
}
|
||||
|
||||
public String getTriggerMode() {
|
||||
return triggerMode;
|
||||
}
|
||||
|
||||
public void setTriggerMode(String triggerMode) {
|
||||
this.triggerMode = triggerMode;
|
||||
}
|
||||
|
||||
public String getStartedAt() {
|
||||
return startedAt;
|
||||
}
|
||||
|
||||
public void setStartedAt(String startedAt) {
|
||||
this.startedAt = startedAt;
|
||||
}
|
||||
|
||||
public String getFinishedAt() {
|
||||
return finishedAt;
|
||||
}
|
||||
|
||||
public void setFinishedAt(String finishedAt) {
|
||||
this.finishedAt = finishedAt;
|
||||
}
|
||||
|
||||
public Long getDurationSeconds() {
|
||||
return durationSeconds;
|
||||
}
|
||||
|
||||
public void setDurationSeconds(Long durationSeconds) {
|
||||
this.durationSeconds = durationSeconds;
|
||||
}
|
||||
|
||||
public String getBackupPath() {
|
||||
return backupPath;
|
||||
}
|
||||
|
||||
public void setBackupPath(String backupPath) {
|
||||
this.backupPath = backupPath;
|
||||
}
|
||||
|
||||
public Long getFileSizeBytes() {
|
||||
return fileSizeBytes;
|
||||
}
|
||||
|
||||
public void setFileSizeBytes(Long fileSizeBytes) {
|
||||
this.fileSizeBytes = fileSizeBytes;
|
||||
}
|
||||
|
||||
public Long getArchiveCount() {
|
||||
return archiveCount;
|
||||
}
|
||||
|
||||
public void setArchiveCount(Long archiveCount) {
|
||||
this.archiveCount = archiveCount;
|
||||
}
|
||||
|
||||
public String getResultMsg() {
|
||||
return resultMsg;
|
||||
}
|
||||
|
||||
public void setResultMsg(String resultMsg) {
|
||||
this.resultMsg = resultMsg;
|
||||
}
|
||||
|
||||
public String getExecutorNode() {
|
||||
return executorNode;
|
||||
}
|
||||
|
||||
public void setExecutorNode(String executorNode) {
|
||||
this.executorNode = executorNode;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,249 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 档案借阅(资料室·借阅与利用 + 保密分级审批)。员工在线申请借阅某份正式档案
|
||||
* ({@link Archive}),按档案密级走分级审批,获批后借出,按期归还,逾期催还。状态机:
|
||||
*
|
||||
* <pre>
|
||||
* 待审批 ──批准──▶ 已批准 ──借出──▶ 借阅中 ──归还──▶ 已归还
|
||||
* │ │
|
||||
* └──驳回──▶ 已驳回 └─(超期未还)─▶ 借阅中(overdue 标记)
|
||||
* </pre>
|
||||
*
|
||||
* borrowType 为 在线预览 / 电子下载 / 实体借出;电子类(预览/下载)在「借出」时不占实体,
|
||||
* 实体借出需登记预计归还日 dueDate,逾期由看板/预警按 dueDate 与今天比对标红催还。
|
||||
* approvalLevel 按档案密级派生(公开/内部→部门级,秘密/机密→资料室主任级,绝密→高管级),
|
||||
* 仅作展示与审批留痕分级提示。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_borrow")
|
||||
public class ArchiveBorrow {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 借阅单号(BY-流水)。 */
|
||||
private String code;
|
||||
|
||||
/** 被借阅的正式档案 id({@link Archive})。 */
|
||||
private Long archiveId;
|
||||
|
||||
/** 冗余档案标题。 */
|
||||
private String archiveTitle;
|
||||
|
||||
/** 冗余档案密级(决定审批分级)。 */
|
||||
private String secLevel;
|
||||
|
||||
/** 借阅人。 */
|
||||
private String borrower;
|
||||
|
||||
/** 借阅人部门。 */
|
||||
private String borrowerDept;
|
||||
|
||||
/** 借阅方式:在线预览 / 电子下载 / 实体借出。 */
|
||||
private String borrowType;
|
||||
|
||||
/** 借阅目的。 */
|
||||
private String purpose;
|
||||
|
||||
/** 申请日期。 */
|
||||
private String applyDate;
|
||||
|
||||
/** 预计归还日(实体借出/下载有效期,YYYY-MM-DD)。 */
|
||||
private String dueDate;
|
||||
|
||||
/** 实际归还日。 */
|
||||
private String returnDate;
|
||||
|
||||
/** 审批分级(部门级 / 资料室主任级 / 高管级),按密级派生。 */
|
||||
private String approvalLevel;
|
||||
|
||||
/** 状态:待审批 / 已批准 / 借阅中 / 已归还 / 已驳回。 */
|
||||
private String status;
|
||||
|
||||
/** 审批人。 */
|
||||
private String approver;
|
||||
|
||||
/** 审批意见。 */
|
||||
private String approveOpinion;
|
||||
|
||||
/** 归还检查结果(完好 / 缺页 / 污损…)。 */
|
||||
private String returnCheck;
|
||||
|
||||
/** 续借次数(每次续借 +1,由 renew 端点维护)。 */
|
||||
private int renewCount;
|
||||
|
||||
/** 最近一次续借后的新到期日(YYYY-MM-DD);同步写入 dueDate。 */
|
||||
private String renewedDueDate;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Long getArchiveId() {
|
||||
return archiveId;
|
||||
}
|
||||
|
||||
public void setArchiveId(Long archiveId) {
|
||||
this.archiveId = archiveId;
|
||||
}
|
||||
|
||||
public String getArchiveTitle() {
|
||||
return archiveTitle;
|
||||
}
|
||||
|
||||
public void setArchiveTitle(String archiveTitle) {
|
||||
this.archiveTitle = archiveTitle;
|
||||
}
|
||||
|
||||
public String getSecLevel() {
|
||||
return secLevel;
|
||||
}
|
||||
|
||||
public void setSecLevel(String secLevel) {
|
||||
this.secLevel = secLevel;
|
||||
}
|
||||
|
||||
public String getBorrower() {
|
||||
return borrower;
|
||||
}
|
||||
|
||||
public void setBorrower(String borrower) {
|
||||
this.borrower = borrower;
|
||||
}
|
||||
|
||||
public String getBorrowerDept() {
|
||||
return borrowerDept;
|
||||
}
|
||||
|
||||
public void setBorrowerDept(String borrowerDept) {
|
||||
this.borrowerDept = borrowerDept;
|
||||
}
|
||||
|
||||
public String getBorrowType() {
|
||||
return borrowType;
|
||||
}
|
||||
|
||||
public void setBorrowType(String borrowType) {
|
||||
this.borrowType = borrowType;
|
||||
}
|
||||
|
||||
public String getPurpose() {
|
||||
return purpose;
|
||||
}
|
||||
|
||||
public void setPurpose(String purpose) {
|
||||
this.purpose = purpose;
|
||||
}
|
||||
|
||||
public String getApplyDate() {
|
||||
return applyDate;
|
||||
}
|
||||
|
||||
public void setApplyDate(String applyDate) {
|
||||
this.applyDate = applyDate;
|
||||
}
|
||||
|
||||
public String getDueDate() {
|
||||
return dueDate;
|
||||
}
|
||||
|
||||
public void setDueDate(String dueDate) {
|
||||
this.dueDate = dueDate;
|
||||
}
|
||||
|
||||
public String getReturnDate() {
|
||||
return returnDate;
|
||||
}
|
||||
|
||||
public void setReturnDate(String returnDate) {
|
||||
this.returnDate = returnDate;
|
||||
}
|
||||
|
||||
public String getApprovalLevel() {
|
||||
return approvalLevel;
|
||||
}
|
||||
|
||||
public void setApprovalLevel(String approvalLevel) {
|
||||
this.approvalLevel = approvalLevel;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getApprover() {
|
||||
return approver;
|
||||
}
|
||||
|
||||
public void setApprover(String approver) {
|
||||
this.approver = approver;
|
||||
}
|
||||
|
||||
public String getApproveOpinion() {
|
||||
return approveOpinion;
|
||||
}
|
||||
|
||||
public void setApproveOpinion(String approveOpinion) {
|
||||
this.approveOpinion = approveOpinion;
|
||||
}
|
||||
|
||||
public String getReturnCheck() {
|
||||
return returnCheck;
|
||||
}
|
||||
|
||||
public void setReturnCheck(String returnCheck) {
|
||||
this.returnCheck = returnCheck;
|
||||
}
|
||||
|
||||
public int getRenewCount() {
|
||||
return renewCount;
|
||||
}
|
||||
|
||||
public void setRenewCount(int renewCount) {
|
||||
this.renewCount = renewCount;
|
||||
}
|
||||
|
||||
public String getRenewedDueDate() {
|
||||
return renewedDueDate;
|
||||
}
|
||||
|
||||
public void setRenewedDueDate(String renewedDueDate) {
|
||||
this.renewedDueDate = renewedDueDate;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 档案分类(资料室·分类体系 + 保存期限规则)。一条 ArchiveCategory 代表一个可自定义的
|
||||
* 档案类别(行政档案 / 人事档案 / 财务档案 / 项目档案 / 合同档案 / 图纸档案 /
|
||||
* 知识产权档案 / 质量档案 / 设备档案…),支持多级(parentId 指向上级类别,顶级为空)。
|
||||
*
|
||||
* <p>每个类别可挂保存期限规则:retentionType 为 永久 / 定期;retentionYears 为定期年限
|
||||
* (10 / 30 / 5 等,永久时忽略)。归档时系统按「形成日期 + retentionYears」自动算到期日,
|
||||
* 到期前进入鉴定销毁预警。codePrefix 为该类别归档编号前缀(如 XM 表示项目档案),
|
||||
* 归档审核通过时按「codePrefix-年度-流水号」生成唯一档案编号。
|
||||
*
|
||||
* <p>metaFields 为该类别要求的自定义元数据字段(逗号分隔,如「项目编号,起止日期,投资额」),
|
||||
* 仅作归档申请表单的提示用途,不强约束。defaultSecLevel 为该类别建议密级
|
||||
* (公开 / 内部 / 秘密 / 机密 / 绝密)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_category")
|
||||
public class ArchiveCategory {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 类别名称(如 项目档案 / 合同档案)。 */
|
||||
private String name;
|
||||
|
||||
/** 上级类别 id(多级分类,顶级为空)。 */
|
||||
private Long parentId;
|
||||
|
||||
/** 归档编号前缀(如 XM / HT / TZ)。 */
|
||||
private String codePrefix;
|
||||
|
||||
/** 保存期限类型:永久 / 定期。 */
|
||||
private String retentionType;
|
||||
|
||||
/** 定期年限(年;永久时忽略)。 */
|
||||
private Integer retentionYears;
|
||||
|
||||
/** 该类别要求的自定义元数据字段(逗号分隔,仅提示)。 */
|
||||
private String metaFields;
|
||||
|
||||
/** 建议密级:公开 / 内部 / 秘密 / 机密 / 绝密。 */
|
||||
private String defaultSecLevel;
|
||||
|
||||
/** 是否启用(停用后不在归档申请下拉中出现)。 */
|
||||
private Boolean enabled = Boolean.TRUE;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public Long getParentId() {
|
||||
return parentId;
|
||||
}
|
||||
|
||||
public void setParentId(Long parentId) {
|
||||
this.parentId = parentId;
|
||||
}
|
||||
|
||||
public String getCodePrefix() {
|
||||
return codePrefix;
|
||||
}
|
||||
|
||||
public void setCodePrefix(String codePrefix) {
|
||||
this.codePrefix = codePrefix;
|
||||
}
|
||||
|
||||
public String getRetentionType() {
|
||||
return retentionType;
|
||||
}
|
||||
|
||||
public void setRetentionType(String retentionType) {
|
||||
this.retentionType = retentionType;
|
||||
}
|
||||
|
||||
public Integer getRetentionYears() {
|
||||
return retentionYears;
|
||||
}
|
||||
|
||||
public void setRetentionYears(Integer retentionYears) {
|
||||
this.retentionYears = retentionYears;
|
||||
}
|
||||
|
||||
public String getMetaFields() {
|
||||
return metaFields;
|
||||
}
|
||||
|
||||
public void setMetaFields(String metaFields) {
|
||||
this.metaFields = metaFields;
|
||||
}
|
||||
|
||||
public String getDefaultSecLevel() {
|
||||
return defaultSecLevel;
|
||||
}
|
||||
|
||||
public void setDefaultSecLevel(String defaultSecLevel) {
|
||||
this.defaultSecLevel = defaultSecLevel;
|
||||
}
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* A comment attached to an archive entry. archiveId links the comment to the
|
||||
* archive record it annotates; author is the commenter name and content the
|
||||
* comment body. createdAt is set on creation.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_comment")
|
||||
public class ArchiveComment {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private Long archiveId;
|
||||
|
||||
private String author;
|
||||
|
||||
private String content;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getArchiveId() {
|
||||
return archiveId;
|
||||
}
|
||||
|
||||
public void setArchiveId(Long archiveId) {
|
||||
this.archiveId = archiveId;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public String getContent() {
|
||||
return content;
|
||||
}
|
||||
|
||||
public void setContent(String content) {
|
||||
this.content = content;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,283 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 档案销毁鉴定单(资料室·Module 4「销毁管理」)。保存期满的档案进入销毁鉴定流程,
|
||||
* 经鉴定 + 审批后执行销毁(实体拍照/视频留证,电子彻底/逻辑删除),系统留存销毁证明。
|
||||
* 补审计「销毁鉴定审批流程 + 实体销毁拍照视频留证 + 电子彻底/逻辑删除 + 销毁证明」全缺缺口。
|
||||
*
|
||||
* <p>状态机(双重把关:先鉴定再审批,审批后才可执行销毁,杜绝误销):
|
||||
* <pre>
|
||||
* 待鉴定 ──鉴定(appraise: 留存/销毁)──┬─[留存]─▶ 已鉴定·留存(回写延长保存期, 终态)
|
||||
* └─[销毁]─▶ 待审批
|
||||
* 待审批 ──审批通过(approve)──▶ 已批准 ──执行(execute: 录留证)──▶ 已销毁(终态)
|
||||
* 待审批 ──审批驳回(reject)──▶ 已驳回(终态)
|
||||
* </pre>
|
||||
*
|
||||
* <p>disposalMode 销毁方式:实体粉碎 / 实体焚毁 / 电子彻底删除 / 电子逻辑删除。
|
||||
* proofUrl 为拍照/视频留证链接,certNo 为系统生成的销毁证明编号。targetItemId 关联被销毁实体盒。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_destruction")
|
||||
public class ArchiveDestruction {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 销毁单号(XH-流水)。 */
|
||||
private String code;
|
||||
|
||||
/** 被销毁档案标题(冗余)。 */
|
||||
private String archiveTitle;
|
||||
|
||||
/** 关联正式档案 id({@link Archive},可空)。 */
|
||||
private Long archiveId;
|
||||
|
||||
/** 关联实体盒 id({@link ArchivePhysicalItem},可空,实体销毁时回写盒状态)。 */
|
||||
private Long targetItemId;
|
||||
|
||||
/** 密级。 */
|
||||
private String secLevel;
|
||||
|
||||
/** 保存到期日(YYYY-MM-DD,纳入销毁理由)。 */
|
||||
private String retentionDueDate;
|
||||
|
||||
/** 销毁理由(保存期满 / 重复件 / 失去保存价值…)。 */
|
||||
private String reason;
|
||||
|
||||
/** 销毁方式:实体粉碎 / 实体焚毁 / 电子彻底删除 / 电子逻辑删除。 */
|
||||
private String disposalMode;
|
||||
|
||||
/** 状态:待鉴定 / 已鉴定·留存 / 待审批 / 已批准 / 已销毁 / 已驳回。 */
|
||||
private String status;
|
||||
|
||||
/** 申请人。 */
|
||||
private String applicant;
|
||||
|
||||
/** 申请日期。 */
|
||||
private String applyDate;
|
||||
|
||||
/** 鉴定人。 */
|
||||
private String appraiser;
|
||||
|
||||
/** 鉴定结论:留存 / 销毁。 */
|
||||
private String appraisalResult;
|
||||
|
||||
/** 鉴定意见。 */
|
||||
private String appraisalOpinion;
|
||||
|
||||
/** 鉴定为留存时延长的保存到期日(回写)。 */
|
||||
private String extendedDueDate;
|
||||
|
||||
/** 审批人。 */
|
||||
private String approver;
|
||||
|
||||
/** 审批意见。 */
|
||||
private String approveOpinion;
|
||||
|
||||
/** 执行人(实际销毁操作人)。 */
|
||||
private String executor;
|
||||
|
||||
/** 执行日期。 */
|
||||
private String executeDate;
|
||||
|
||||
/** 拍照/视频留证链接。 */
|
||||
private String proofUrl;
|
||||
|
||||
/** 系统生成的销毁证明编号。 */
|
||||
private String certNo;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getArchiveTitle() {
|
||||
return archiveTitle;
|
||||
}
|
||||
|
||||
public void setArchiveTitle(String archiveTitle) {
|
||||
this.archiveTitle = archiveTitle;
|
||||
}
|
||||
|
||||
public Long getArchiveId() {
|
||||
return archiveId;
|
||||
}
|
||||
|
||||
public void setArchiveId(Long archiveId) {
|
||||
this.archiveId = archiveId;
|
||||
}
|
||||
|
||||
public Long getTargetItemId() {
|
||||
return targetItemId;
|
||||
}
|
||||
|
||||
public void setTargetItemId(Long targetItemId) {
|
||||
this.targetItemId = targetItemId;
|
||||
}
|
||||
|
||||
public String getSecLevel() {
|
||||
return secLevel;
|
||||
}
|
||||
|
||||
public void setSecLevel(String secLevel) {
|
||||
this.secLevel = secLevel;
|
||||
}
|
||||
|
||||
public String getRetentionDueDate() {
|
||||
return retentionDueDate;
|
||||
}
|
||||
|
||||
public void setRetentionDueDate(String retentionDueDate) {
|
||||
this.retentionDueDate = retentionDueDate;
|
||||
}
|
||||
|
||||
public String getReason() {
|
||||
return reason;
|
||||
}
|
||||
|
||||
public void setReason(String reason) {
|
||||
this.reason = reason;
|
||||
}
|
||||
|
||||
public String getDisposalMode() {
|
||||
return disposalMode;
|
||||
}
|
||||
|
||||
public void setDisposalMode(String disposalMode) {
|
||||
this.disposalMode = disposalMode;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getApplicant() {
|
||||
return applicant;
|
||||
}
|
||||
|
||||
public void setApplicant(String applicant) {
|
||||
this.applicant = applicant;
|
||||
}
|
||||
|
||||
public String getApplyDate() {
|
||||
return applyDate;
|
||||
}
|
||||
|
||||
public void setApplyDate(String applyDate) {
|
||||
this.applyDate = applyDate;
|
||||
}
|
||||
|
||||
public String getAppraiser() {
|
||||
return appraiser;
|
||||
}
|
||||
|
||||
public void setAppraiser(String appraiser) {
|
||||
this.appraiser = appraiser;
|
||||
}
|
||||
|
||||
public String getAppraisalResult() {
|
||||
return appraisalResult;
|
||||
}
|
||||
|
||||
public void setAppraisalResult(String appraisalResult) {
|
||||
this.appraisalResult = appraisalResult;
|
||||
}
|
||||
|
||||
public String getAppraisalOpinion() {
|
||||
return appraisalOpinion;
|
||||
}
|
||||
|
||||
public void setAppraisalOpinion(String appraisalOpinion) {
|
||||
this.appraisalOpinion = appraisalOpinion;
|
||||
}
|
||||
|
||||
public String getExtendedDueDate() {
|
||||
return extendedDueDate;
|
||||
}
|
||||
|
||||
public void setExtendedDueDate(String extendedDueDate) {
|
||||
this.extendedDueDate = extendedDueDate;
|
||||
}
|
||||
|
||||
public String getApprover() {
|
||||
return approver;
|
||||
}
|
||||
|
||||
public void setApprover(String approver) {
|
||||
this.approver = approver;
|
||||
}
|
||||
|
||||
public String getApproveOpinion() {
|
||||
return approveOpinion;
|
||||
}
|
||||
|
||||
public void setApproveOpinion(String approveOpinion) {
|
||||
this.approveOpinion = approveOpinion;
|
||||
}
|
||||
|
||||
public String getExecutor() {
|
||||
return executor;
|
||||
}
|
||||
|
||||
public void setExecutor(String executor) {
|
||||
this.executor = executor;
|
||||
}
|
||||
|
||||
public String getExecuteDate() {
|
||||
return executeDate;
|
||||
}
|
||||
|
||||
public void setExecuteDate(String executeDate) {
|
||||
this.executeDate = executeDate;
|
||||
}
|
||||
|
||||
public String getProofUrl() {
|
||||
return proofUrl;
|
||||
}
|
||||
|
||||
public void setProofUrl(String proofUrl) {
|
||||
this.proofUrl = proofUrl;
|
||||
}
|
||||
|
||||
public String getCertNo() {
|
||||
return certNo;
|
||||
}
|
||||
|
||||
public void setCertNo(String certNo) {
|
||||
this.certNo = certNo;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,256 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 资料室灾难恢复演练记录(档案系统 Module 7「合规与审计支持」)。
|
||||
*
|
||||
* 需求:「灾难恢复演练记录表/端点缺失」——本实体补全该缺口,
|
||||
* 支持定期演练记录的创建、状态跟踪(计划中/演练中/已完成/演练失败)
|
||||
* 及恢复时间/恢复点目标(RTO/RPO)实测记录,满足合规审计要求。
|
||||
*
|
||||
* 状态机:
|
||||
* 计划中 ──开始演练──▶ 演练中 ──完成──▶ 已完成
|
||||
* └────失败──▶ 演练失败
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_drill_record")
|
||||
public class ArchiveDrillRecord {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 演练编号(DR-年度-流水)。 */
|
||||
private String drillCode;
|
||||
|
||||
/** 演练名称(如「年度档案系统全量恢复演练」)。 */
|
||||
private String drillName;
|
||||
|
||||
/** 演练类型:全量恢复 / 增量恢复 / 单档案恢复 / 异地切换。 */
|
||||
private String drillType;
|
||||
|
||||
/** 演练状态:计划中 / 演练中 / 已完成 / 演练失败。 */
|
||||
private String drillStatus;
|
||||
|
||||
/** 计划演练日期(YYYY-MM-DD)。 */
|
||||
private String plannedDate;
|
||||
|
||||
/** 实际开始时间。 */
|
||||
private String actualStartTime;
|
||||
|
||||
/** 实际结束时间。 */
|
||||
private String actualEndTime;
|
||||
|
||||
/** 演练负责人。 */
|
||||
private String leader;
|
||||
|
||||
/** 参与人员(逗号分隔)。 */
|
||||
private String participants;
|
||||
|
||||
/** 依赖备份批次/路径(演练所用备份的标识)。 */
|
||||
private String backupRef;
|
||||
|
||||
/** 实测 RTO(恢复时间目标,分钟)。 */
|
||||
private Integer actualRtoMinutes;
|
||||
|
||||
/** 实测 RPO(恢复点目标,数据丢失分钟数)。 */
|
||||
private Integer actualRpoMinutes;
|
||||
|
||||
/** 目标 RTO(分钟,合规要求)。 */
|
||||
private Integer targetRtoMinutes;
|
||||
|
||||
/** 目标 RPO(分钟,合规要求)。 */
|
||||
private Integer targetRpoMinutes;
|
||||
|
||||
/** 是否达标(实测 RTO/RPO 均满足目标)。 */
|
||||
private Boolean passFlag;
|
||||
|
||||
/** 演练结果摘要(成功/失败原因、数据完整性、问题描述)。 */
|
||||
private String resultSummary;
|
||||
|
||||
/** 改进措施(演练中发现问题的整改建议)。 */
|
||||
private String improvements;
|
||||
|
||||
/** 演练记录附件 URL(如录屏/截图/报告 PDF)。 */
|
||||
private String evidenceUrl;
|
||||
|
||||
/** 下次演练计划日期。 */
|
||||
private String nextDrillDate;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getDrillCode() {
|
||||
return drillCode;
|
||||
}
|
||||
|
||||
public void setDrillCode(String drillCode) {
|
||||
this.drillCode = drillCode;
|
||||
}
|
||||
|
||||
public String getDrillName() {
|
||||
return drillName;
|
||||
}
|
||||
|
||||
public void setDrillName(String drillName) {
|
||||
this.drillName = drillName;
|
||||
}
|
||||
|
||||
public String getDrillType() {
|
||||
return drillType;
|
||||
}
|
||||
|
||||
public void setDrillType(String drillType) {
|
||||
this.drillType = drillType;
|
||||
}
|
||||
|
||||
public String getDrillStatus() {
|
||||
return drillStatus;
|
||||
}
|
||||
|
||||
public void setDrillStatus(String drillStatus) {
|
||||
this.drillStatus = drillStatus;
|
||||
}
|
||||
|
||||
public String getPlannedDate() {
|
||||
return plannedDate;
|
||||
}
|
||||
|
||||
public void setPlannedDate(String plannedDate) {
|
||||
this.plannedDate = plannedDate;
|
||||
}
|
||||
|
||||
public String getActualStartTime() {
|
||||
return actualStartTime;
|
||||
}
|
||||
|
||||
public void setActualStartTime(String actualStartTime) {
|
||||
this.actualStartTime = actualStartTime;
|
||||
}
|
||||
|
||||
public String getActualEndTime() {
|
||||
return actualEndTime;
|
||||
}
|
||||
|
||||
public void setActualEndTime(String actualEndTime) {
|
||||
this.actualEndTime = actualEndTime;
|
||||
}
|
||||
|
||||
public String getLeader() {
|
||||
return leader;
|
||||
}
|
||||
|
||||
public void setLeader(String leader) {
|
||||
this.leader = leader;
|
||||
}
|
||||
|
||||
public String getParticipants() {
|
||||
return participants;
|
||||
}
|
||||
|
||||
public void setParticipants(String participants) {
|
||||
this.participants = participants;
|
||||
}
|
||||
|
||||
public String getBackupRef() {
|
||||
return backupRef;
|
||||
}
|
||||
|
||||
public void setBackupRef(String backupRef) {
|
||||
this.backupRef = backupRef;
|
||||
}
|
||||
|
||||
public Integer getActualRtoMinutes() {
|
||||
return actualRtoMinutes;
|
||||
}
|
||||
|
||||
public void setActualRtoMinutes(Integer actualRtoMinutes) {
|
||||
this.actualRtoMinutes = actualRtoMinutes;
|
||||
}
|
||||
|
||||
public Integer getActualRpoMinutes() {
|
||||
return actualRpoMinutes;
|
||||
}
|
||||
|
||||
public void setActualRpoMinutes(Integer actualRpoMinutes) {
|
||||
this.actualRpoMinutes = actualRpoMinutes;
|
||||
}
|
||||
|
||||
public Integer getTargetRtoMinutes() {
|
||||
return targetRtoMinutes;
|
||||
}
|
||||
|
||||
public void setTargetRtoMinutes(Integer targetRtoMinutes) {
|
||||
this.targetRtoMinutes = targetRtoMinutes;
|
||||
}
|
||||
|
||||
public Integer getTargetRpoMinutes() {
|
||||
return targetRpoMinutes;
|
||||
}
|
||||
|
||||
public void setTargetRpoMinutes(Integer targetRpoMinutes) {
|
||||
this.targetRpoMinutes = targetRpoMinutes;
|
||||
}
|
||||
|
||||
public Boolean getPassFlag() {
|
||||
return passFlag;
|
||||
}
|
||||
|
||||
public void setPassFlag(Boolean passFlag) {
|
||||
this.passFlag = passFlag;
|
||||
}
|
||||
|
||||
public String getResultSummary() {
|
||||
return resultSummary;
|
||||
}
|
||||
|
||||
public void setResultSummary(String resultSummary) {
|
||||
this.resultSummary = resultSummary;
|
||||
}
|
||||
|
||||
public String getImprovements() {
|
||||
return improvements;
|
||||
}
|
||||
|
||||
public void setImprovements(String improvements) {
|
||||
this.improvements = improvements;
|
||||
}
|
||||
|
||||
public String getEvidenceUrl() {
|
||||
return evidenceUrl;
|
||||
}
|
||||
|
||||
public void setEvidenceUrl(String evidenceUrl) {
|
||||
this.evidenceUrl = evidenceUrl;
|
||||
}
|
||||
|
||||
public String getNextDrillDate() {
|
||||
return nextDrillDate;
|
||||
}
|
||||
|
||||
public void setNextDrillDate(String nextDrillDate) {
|
||||
this.nextDrillDate = nextDrillDate;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,110 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 资料室·电子档案哈希存证(防篡改)。
|
||||
*
|
||||
* 满足需求 16:「重要电子档案计算哈希值存证,防止篡改」。
|
||||
* 每次对档案计算 SHA-256 并记录在此表;后续可随时对比重算值与存档值是否一致。
|
||||
*
|
||||
* algorithm:哈希算法,默认 SHA-256。
|
||||
* hashValue:十六进制摘要字符串(64位)。
|
||||
* source:MANUAL(人工触发)/ SCHEDULED(定时自动)。
|
||||
* verifyStatus:UNVERIFIED / OK / TAMPERED(篡改告警)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_hash_record")
|
||||
public class ArchiveHashRecord {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 关联档案 id(Archive.id)。 */
|
||||
private Long archiveId;
|
||||
|
||||
/** 档案标题(冗余,便于展示)。 */
|
||||
private String archiveTitle;
|
||||
|
||||
/** 哈希算法,默认 SHA-256。 */
|
||||
private String algorithm;
|
||||
|
||||
/** 十六进制摘要。 */
|
||||
@Column(name = "hash_val", length = 256)
|
||||
private String hashValue;
|
||||
|
||||
/** 档案文件 URL(计算时取自 Archive.fileUrl,用于再次拉取校验)。 */
|
||||
@Column(name = "src_url")
|
||||
private String srcUrl;
|
||||
|
||||
/** 文件大小(bytes),与 hashValue 一起作为存证基础。 */
|
||||
private Long fileSize;
|
||||
|
||||
/** 触发来源:MANUAL / SCHEDULED。 */
|
||||
private String source;
|
||||
|
||||
/** 操作人(触发者 displayName)。 */
|
||||
private String operator;
|
||||
|
||||
/**
|
||||
* 验证状态:UNVERIFIED(未校验)/ OK(与上次一致)/ TAMPERED(检测到篡改)。
|
||||
* 通过 /archive-hash-records/{id}/verify 端点更新。
|
||||
*/
|
||||
private String verifyStatus;
|
||||
|
||||
/** 最近一次核验时间。 */
|
||||
private Instant verifiedAt;
|
||||
|
||||
/** 核验时的重算 hash(与 hashValue 比对)。 */
|
||||
@Column(name = "verify_hash", length = 256)
|
||||
private String verifyHash;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public Long getArchiveId() { return archiveId; }
|
||||
public void setArchiveId(Long archiveId) { this.archiveId = archiveId; }
|
||||
|
||||
public String getArchiveTitle() { return archiveTitle; }
|
||||
public void setArchiveTitle(String archiveTitle) { this.archiveTitle = archiveTitle; }
|
||||
|
||||
public String getAlgorithm() { return algorithm; }
|
||||
public void setAlgorithm(String algorithm) { this.algorithm = algorithm; }
|
||||
|
||||
public String getHashValue() { return hashValue; }
|
||||
public void setHashValue(String hashValue) { this.hashValue = hashValue; }
|
||||
|
||||
public String getSrcUrl() { return srcUrl; }
|
||||
public void setSrcUrl(String srcUrl) { this.srcUrl = srcUrl; }
|
||||
|
||||
public Long getFileSize() { return fileSize; }
|
||||
public void setFileSize(Long fileSize) { this.fileSize = fileSize; }
|
||||
|
||||
public String getSource() { return source; }
|
||||
public void setSource(String source) { this.source = source; }
|
||||
|
||||
public String getOperator() { return operator; }
|
||||
public void setOperator(String operator) { this.operator = operator; }
|
||||
|
||||
public String getVerifyStatus() { return verifyStatus; }
|
||||
public void setVerifyStatus(String verifyStatus) { this.verifyStatus = verifyStatus; }
|
||||
|
||||
public Instant getVerifiedAt() { return verifiedAt; }
|
||||
public void setVerifiedAt(Instant verifiedAt) { this.verifiedAt = verifiedAt; }
|
||||
|
||||
public String getVerifyHash() { return verifyHash; }
|
||||
public void setVerifyHash(String verifyHash) { this.verifyHash = verifyHash; }
|
||||
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,260 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 实体档案盒/卷(资料室·实体档案管理)。一条 ArchivePhysicalItem 代表一个实体档案盒(卷),
|
||||
* 是实体档案上架定位与借阅出入库的最小载体。补审计 Module 4「入库上架(扫码录位/移动端上架)」
|
||||
* 与「档案盒/卷管理(盒内文件清单 + 盒号位置绑定)」缺口。
|
||||
*
|
||||
* <p>状态机:
|
||||
* <pre>
|
||||
* 待上架 ──上架(scanShelve, 录入库房/排/列/层 + 绑定条码)──▶ 在库
|
||||
* 在库 ──出库(checkOut, 借阅出库)──▶ 出库中 ──入库(checkIn, 归还入库)──▶ 在库
|
||||
* 在库 ──销毁(linkDestruction)──▶ 已销毁
|
||||
* </pre>
|
||||
*
|
||||
* <p>位置六段定位:vaultId(虚拟档案库) + roomNo(库房号) + rackNo(密集架排号) + colNo(列号)
|
||||
* + rowNo(层号) + boxNo(盒号)。barcode 为条形码/二维码/RFID 标签内容,扫码可秒级定位。
|
||||
* itemsJson 为盒内文件清单(JSON 数组,每项含 archiveId/title/pages),实现「同一项目多份文件合并入盒」。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_physical_item")
|
||||
public class ArchivePhysicalItem {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 盒/卷编号(PB-流水)。 */
|
||||
private String code;
|
||||
|
||||
/** 盒/卷题名(如「2026 凯迪大厦项目竣工验收卷」)。 */
|
||||
private String title;
|
||||
|
||||
/** 所属虚拟档案库 id({@link ArchiveVault},可空)。 */
|
||||
private Long vaultId;
|
||||
|
||||
/** 冗余档案库名(便于列表展示)。 */
|
||||
private String vaultName;
|
||||
|
||||
/** 关联档案类别名(如 项目档案 / 合同档案)。 */
|
||||
private String categoryName;
|
||||
|
||||
/** 密级(公开/内部/秘密/机密/绝密)。 */
|
||||
private String secLevel;
|
||||
|
||||
/** 条码/二维码/RFID 标签内容(上架时绑定,扫码定位用)。 */
|
||||
private String barcode;
|
||||
|
||||
/** 库房号。 */
|
||||
private String roomNo;
|
||||
|
||||
/** 密集架排号。 */
|
||||
private String rackNo;
|
||||
|
||||
/** 列号。 */
|
||||
private String colNo;
|
||||
|
||||
/** 层号。 */
|
||||
private String rowNo;
|
||||
|
||||
/** 盒号。 */
|
||||
private String boxNo;
|
||||
|
||||
/** 盒内文件清单 JSON(数组,每项 {archiveId,title,pages})。 */
|
||||
private String itemsJson;
|
||||
|
||||
/** 盒内文件份数(itemsJson 长度冗余,便于统计)。 */
|
||||
private Integer itemCount;
|
||||
|
||||
/** 状态:待上架 / 在库 / 出库中 / 已销毁。 */
|
||||
private String status;
|
||||
|
||||
/** 上架人。 */
|
||||
private String shelvedBy;
|
||||
|
||||
/** 上架日期(YYYY-MM-DD)。 */
|
||||
private String shelvedDate;
|
||||
|
||||
/** 关联销毁单 id(销毁后回填,{@link ArchiveDestruction})。 */
|
||||
private Long destructionId;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public Long getVaultId() {
|
||||
return vaultId;
|
||||
}
|
||||
|
||||
public void setVaultId(Long vaultId) {
|
||||
this.vaultId = vaultId;
|
||||
}
|
||||
|
||||
public String getVaultName() {
|
||||
return vaultName;
|
||||
}
|
||||
|
||||
public void setVaultName(String vaultName) {
|
||||
this.vaultName = vaultName;
|
||||
}
|
||||
|
||||
public String getCategoryName() {
|
||||
return categoryName;
|
||||
}
|
||||
|
||||
public void setCategoryName(String categoryName) {
|
||||
this.categoryName = categoryName;
|
||||
}
|
||||
|
||||
public String getSecLevel() {
|
||||
return secLevel;
|
||||
}
|
||||
|
||||
public void setSecLevel(String secLevel) {
|
||||
this.secLevel = secLevel;
|
||||
}
|
||||
|
||||
public String getBarcode() {
|
||||
return barcode;
|
||||
}
|
||||
|
||||
public void setBarcode(String barcode) {
|
||||
this.barcode = barcode;
|
||||
}
|
||||
|
||||
public String getRoomNo() {
|
||||
return roomNo;
|
||||
}
|
||||
|
||||
public void setRoomNo(String roomNo) {
|
||||
this.roomNo = roomNo;
|
||||
}
|
||||
|
||||
public String getRackNo() {
|
||||
return rackNo;
|
||||
}
|
||||
|
||||
public void setRackNo(String rackNo) {
|
||||
this.rackNo = rackNo;
|
||||
}
|
||||
|
||||
public String getColNo() {
|
||||
return colNo;
|
||||
}
|
||||
|
||||
public void setColNo(String colNo) {
|
||||
this.colNo = colNo;
|
||||
}
|
||||
|
||||
public String getRowNo() {
|
||||
return rowNo;
|
||||
}
|
||||
|
||||
public void setRowNo(String rowNo) {
|
||||
this.rowNo = rowNo;
|
||||
}
|
||||
|
||||
public String getBoxNo() {
|
||||
return boxNo;
|
||||
}
|
||||
|
||||
public void setBoxNo(String boxNo) {
|
||||
this.boxNo = boxNo;
|
||||
}
|
||||
|
||||
public String getItemsJson() {
|
||||
return itemsJson;
|
||||
}
|
||||
|
||||
public void setItemsJson(String itemsJson) {
|
||||
this.itemsJson = itemsJson;
|
||||
}
|
||||
|
||||
public Integer getItemCount() {
|
||||
return itemCount;
|
||||
}
|
||||
|
||||
public void setItemCount(Integer itemCount) {
|
||||
this.itemCount = itemCount;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getShelvedBy() {
|
||||
return shelvedBy;
|
||||
}
|
||||
|
||||
public void setShelvedBy(String shelvedBy) {
|
||||
this.shelvedBy = shelvedBy;
|
||||
}
|
||||
|
||||
public String getShelvedDate() {
|
||||
return shelvedDate;
|
||||
}
|
||||
|
||||
public void setShelvedDate(String shelvedDate) {
|
||||
this.shelvedDate = shelvedDate;
|
||||
}
|
||||
|
||||
public Long getDestructionId() {
|
||||
return destructionId;
|
||||
}
|
||||
|
||||
public void setDestructionId(Long destructionId) {
|
||||
this.destructionId = destructionId;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 档案修复与保护记录(资料室·Module 4「档案修复与保护」)。记录实体档案破损、褪色、虫蛀等
|
||||
* 损坏情况与修复过程;未修复且达预警条件的进入褪色/虫蛀预警。补审计「档案破损修复记录与
|
||||
* 褪色虫蛀预警」全缺缺口。
|
||||
*
|
||||
* <p>状态机:
|
||||
* <pre>
|
||||
* 待处理(登记损坏) ──开始修复(startRepair)──▶ 修复中 ──完成(finishRepair, 录修复人/方法/日期)──▶ 已修复
|
||||
* 待处理 / 修复中 ──标记报废(scrap)──▶ 已报废
|
||||
* </pre>
|
||||
*
|
||||
* <p>damageType 损坏类型:破损 / 褪色 / 虫蛀 / 霉变 / 水渍 / 缺页。severity 严重度:轻微/中度/严重。
|
||||
* 「严重」且「待处理」的记录由看板纳入预警(褪色/虫蛀严重需尽快修复)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_repair_record")
|
||||
public class ArchiveRepairRecord {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 记录单号(XF-流水)。 */
|
||||
private String code;
|
||||
|
||||
/** 关联实体盒 id({@link ArchivePhysicalItem},可空)。 */
|
||||
private Long itemId;
|
||||
|
||||
/** 档案/盒标题(冗余)。 */
|
||||
private String archiveTitle;
|
||||
|
||||
/** 损坏类型:破损 / 褪色 / 虫蛀 / 霉变 / 水渍 / 缺页。 */
|
||||
private String damageType;
|
||||
|
||||
/** 严重度:轻微 / 中度 / 严重。 */
|
||||
private String severity;
|
||||
|
||||
/** 损坏描述。 */
|
||||
private String damageDesc;
|
||||
|
||||
/** 状态:待处理 / 修复中 / 已修复 / 已报废。 */
|
||||
private String status;
|
||||
|
||||
/** 发现人。 */
|
||||
private String reporter;
|
||||
|
||||
/** 登记日期。 */
|
||||
private String reportDate;
|
||||
|
||||
/** 修复人。 */
|
||||
private String repairer;
|
||||
|
||||
/** 修复方法(如 托裱 / 去酸 / 杀虫熏蒸 / 数字化抢救)。 */
|
||||
private String repairMethod;
|
||||
|
||||
/** 修复完成日期。 */
|
||||
private String repairDate;
|
||||
|
||||
/** 修复结果说明。 */
|
||||
private String repairResult;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Long getItemId() {
|
||||
return itemId;
|
||||
}
|
||||
|
||||
public void setItemId(Long itemId) {
|
||||
this.itemId = itemId;
|
||||
}
|
||||
|
||||
public String getArchiveTitle() {
|
||||
return archiveTitle;
|
||||
}
|
||||
|
||||
public void setArchiveTitle(String archiveTitle) {
|
||||
this.archiveTitle = archiveTitle;
|
||||
}
|
||||
|
||||
public String getDamageType() {
|
||||
return damageType;
|
||||
}
|
||||
|
||||
public void setDamageType(String damageType) {
|
||||
this.damageType = damageType;
|
||||
}
|
||||
|
||||
public String getSeverity() {
|
||||
return severity;
|
||||
}
|
||||
|
||||
public void setSeverity(String severity) {
|
||||
this.severity = severity;
|
||||
}
|
||||
|
||||
public String getDamageDesc() {
|
||||
return damageDesc;
|
||||
}
|
||||
|
||||
public void setDamageDesc(String damageDesc) {
|
||||
this.damageDesc = damageDesc;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getReporter() {
|
||||
return reporter;
|
||||
}
|
||||
|
||||
public void setReporter(String reporter) {
|
||||
this.reporter = reporter;
|
||||
}
|
||||
|
||||
public String getReportDate() {
|
||||
return reportDate;
|
||||
}
|
||||
|
||||
public void setReportDate(String reportDate) {
|
||||
this.reportDate = reportDate;
|
||||
}
|
||||
|
||||
public String getRepairer() {
|
||||
return repairer;
|
||||
}
|
||||
|
||||
public void setRepairer(String repairer) {
|
||||
this.repairer = repairer;
|
||||
}
|
||||
|
||||
public String getRepairMethod() {
|
||||
return repairMethod;
|
||||
}
|
||||
|
||||
public void setRepairMethod(String repairMethod) {
|
||||
this.repairMethod = repairMethod;
|
||||
}
|
||||
|
||||
public String getRepairDate() {
|
||||
return repairDate;
|
||||
}
|
||||
|
||||
public void setRepairDate(String repairDate) {
|
||||
this.repairDate = repairDate;
|
||||
}
|
||||
|
||||
public String getRepairResult() {
|
||||
return repairResult;
|
||||
}
|
||||
|
||||
public void setRepairResult(String repairResult) {
|
||||
this.repairResult = repairResult;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,159 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 档案预约(资料室·Module 5「预约与续借」)。档案被借出时,其他用户可预约;档案归还后
|
||||
* 按预约队列先后通知可借。补审计「预约与续借完全缺、ArchiveBorrow 无 reserve/renew 字段」缺口
|
||||
* 的预约侧(续借在 ArchiveBorrow 上以 renewCount/续借 动作补深)。
|
||||
*
|
||||
* <p>状态机:
|
||||
* <pre>
|
||||
* 排队中 ──(档案归还后通知)──▶ 可借 ──(预约人发起借阅)──▶ 已转借阅(终态)
|
||||
* 排队中 / 可借 ──取消(cancel)──▶ 已取消(终态)
|
||||
* </pre>
|
||||
*
|
||||
* queueNo 为同一档案下的预约排队序号(先到先得)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_reservation")
|
||||
public class ArchiveReservation {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 预约单号(YY-流水)。 */
|
||||
private String code;
|
||||
|
||||
/** 预约的档案 id({@link Archive})。 */
|
||||
private Long archiveId;
|
||||
|
||||
/** 档案标题(冗余)。 */
|
||||
private String archiveTitle;
|
||||
|
||||
/** 预约人。 */
|
||||
private String requester;
|
||||
|
||||
/** 预约人部门。 */
|
||||
private String requesterDept;
|
||||
|
||||
/** 预约目的。 */
|
||||
private String purpose;
|
||||
|
||||
/** 排队序号(同档案先到先得)。 */
|
||||
private Integer queueNo;
|
||||
|
||||
/** 状态:排队中 / 可借 / 已转借阅 / 已取消。 */
|
||||
private String status;
|
||||
|
||||
/** 预约日期。 */
|
||||
private String reserveDate;
|
||||
|
||||
/** 转为可借的通知日期。 */
|
||||
private String notifyDate;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Long getArchiveId() {
|
||||
return archiveId;
|
||||
}
|
||||
|
||||
public void setArchiveId(Long archiveId) {
|
||||
this.archiveId = archiveId;
|
||||
}
|
||||
|
||||
public String getArchiveTitle() {
|
||||
return archiveTitle;
|
||||
}
|
||||
|
||||
public void setArchiveTitle(String archiveTitle) {
|
||||
this.archiveTitle = archiveTitle;
|
||||
}
|
||||
|
||||
public String getRequester() {
|
||||
return requester;
|
||||
}
|
||||
|
||||
public void setRequester(String requester) {
|
||||
this.requester = requester;
|
||||
}
|
||||
|
||||
public String getRequesterDept() {
|
||||
return requesterDept;
|
||||
}
|
||||
|
||||
public void setRequesterDept(String requesterDept) {
|
||||
this.requesterDept = requesterDept;
|
||||
}
|
||||
|
||||
public String getPurpose() {
|
||||
return purpose;
|
||||
}
|
||||
|
||||
public void setPurpose(String purpose) {
|
||||
this.purpose = purpose;
|
||||
}
|
||||
|
||||
public Integer getQueueNo() {
|
||||
return queueNo;
|
||||
}
|
||||
|
||||
public void setQueueNo(Integer queueNo) {
|
||||
this.queueNo = queueNo;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getReserveDate() {
|
||||
return reserveDate;
|
||||
}
|
||||
|
||||
public void setReserveDate(String reserveDate) {
|
||||
this.reserveDate = reserveDate;
|
||||
}
|
||||
|
||||
public String getNotifyDate() {
|
||||
return notifyDate;
|
||||
}
|
||||
|
||||
public void setNotifyDate(String notifyDate) {
|
||||
this.notifyDate = notifyDate;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,248 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 归档申请(资料室·在线归档 + 归档审核 + 归档编号生成)。业务部门在线提交归档申请,
|
||||
* 资料室管理员审核格式 / 元数据 / 密级。状态机:
|
||||
*
|
||||
* <pre>
|
||||
* 待审核 ──通过──▶ 已归档(自动生成档案编号 + 落一条正式 Archive + 算保存到期日)
|
||||
* │
|
||||
* └──退回──▶ 已退回(填退回意见,业务部门改后可重新提交→回到 待审核)
|
||||
* </pre>
|
||||
*
|
||||
* archiveCode 在审核通过时由「类别前缀-年度-流水号」自动生成(与 {@link Archive} 打通:
|
||||
* 通过后自动落一条正式档案记录,archiveId 回填)。retentionDueDate 按类别保存期限规则
|
||||
* (形成日期 formedDate + 保留年限)自动算出,永久类别为空。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_submission")
|
||||
public class ArchiveSubmission {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 资料名称(必填)。 */
|
||||
private String title;
|
||||
|
||||
/** 档案类别 id(关联 {@link ArchiveCategory})。 */
|
||||
private Long categoryId;
|
||||
|
||||
/** 冗余类别名(便于列表/审计展示)。 */
|
||||
private String categoryName;
|
||||
|
||||
/** 来源部门。 */
|
||||
private String sourceDept;
|
||||
|
||||
/** 形成日期(YYYY-MM-DD,用于算保存到期日)。 */
|
||||
private String formedDate;
|
||||
|
||||
/** 归档范围(如 全宗 / 案卷级 / 文件级)。 */
|
||||
private String archiveScope;
|
||||
|
||||
/** 保密等级:公开 / 内部 / 秘密 / 机密 / 绝密。 */
|
||||
private String secLevel;
|
||||
|
||||
/** 文件类型(PDF / CAD / DOC…)。 */
|
||||
private String fileType;
|
||||
|
||||
/** 自定义元数据 JSON 文本(前端按类别 metaFields 拼装,原样存储)。 */
|
||||
private String metaJson;
|
||||
|
||||
/** 申请人。 */
|
||||
private String applicant;
|
||||
|
||||
/** 申请日期。 */
|
||||
private String applyDate;
|
||||
|
||||
/** 状态:待审核 / 已归档 / 已退回。 */
|
||||
private String status;
|
||||
|
||||
/** 审核人。 */
|
||||
private String reviewer;
|
||||
|
||||
/** 审核 / 退回意见。 */
|
||||
private String reviewOpinion;
|
||||
|
||||
/** 审核日期。 */
|
||||
private String reviewDate;
|
||||
|
||||
/** 审核通过自动生成的档案编号(类别前缀-年度-流水号)。 */
|
||||
private String archiveCode;
|
||||
|
||||
/** 审核通过后落地的正式档案 id({@link Archive})。 */
|
||||
private Long archiveId;
|
||||
|
||||
/** 保存到期日(YYYY-MM-DD;永久档案为空)。审核通过时按类别保留年限算出。 */
|
||||
private String retentionDueDate;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public Long getCategoryId() {
|
||||
return categoryId;
|
||||
}
|
||||
|
||||
public void setCategoryId(Long categoryId) {
|
||||
this.categoryId = categoryId;
|
||||
}
|
||||
|
||||
public String getCategoryName() {
|
||||
return categoryName;
|
||||
}
|
||||
|
||||
public void setCategoryName(String categoryName) {
|
||||
this.categoryName = categoryName;
|
||||
}
|
||||
|
||||
public String getSourceDept() {
|
||||
return sourceDept;
|
||||
}
|
||||
|
||||
public void setSourceDept(String sourceDept) {
|
||||
this.sourceDept = sourceDept;
|
||||
}
|
||||
|
||||
public String getFormedDate() {
|
||||
return formedDate;
|
||||
}
|
||||
|
||||
public void setFormedDate(String formedDate) {
|
||||
this.formedDate = formedDate;
|
||||
}
|
||||
|
||||
public String getArchiveScope() {
|
||||
return archiveScope;
|
||||
}
|
||||
|
||||
public void setArchiveScope(String archiveScope) {
|
||||
this.archiveScope = archiveScope;
|
||||
}
|
||||
|
||||
public String getSecLevel() {
|
||||
return secLevel;
|
||||
}
|
||||
|
||||
public void setSecLevel(String secLevel) {
|
||||
this.secLevel = secLevel;
|
||||
}
|
||||
|
||||
public String getFileType() {
|
||||
return fileType;
|
||||
}
|
||||
|
||||
public void setFileType(String fileType) {
|
||||
this.fileType = fileType;
|
||||
}
|
||||
|
||||
public String getMetaJson() {
|
||||
return metaJson;
|
||||
}
|
||||
|
||||
public void setMetaJson(String metaJson) {
|
||||
this.metaJson = metaJson;
|
||||
}
|
||||
|
||||
public String getApplicant() {
|
||||
return applicant;
|
||||
}
|
||||
|
||||
public void setApplicant(String applicant) {
|
||||
this.applicant = applicant;
|
||||
}
|
||||
|
||||
public String getApplyDate() {
|
||||
return applyDate;
|
||||
}
|
||||
|
||||
public void setApplyDate(String applyDate) {
|
||||
this.applyDate = applyDate;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getReviewer() {
|
||||
return reviewer;
|
||||
}
|
||||
|
||||
public void setReviewer(String reviewer) {
|
||||
this.reviewer = reviewer;
|
||||
}
|
||||
|
||||
public String getReviewOpinion() {
|
||||
return reviewOpinion;
|
||||
}
|
||||
|
||||
public void setReviewOpinion(String reviewOpinion) {
|
||||
this.reviewOpinion = reviewOpinion;
|
||||
}
|
||||
|
||||
public String getReviewDate() {
|
||||
return reviewDate;
|
||||
}
|
||||
|
||||
public void setReviewDate(String reviewDate) {
|
||||
this.reviewDate = reviewDate;
|
||||
}
|
||||
|
||||
public String getArchiveCode() {
|
||||
return archiveCode;
|
||||
}
|
||||
|
||||
public void setArchiveCode(String archiveCode) {
|
||||
this.archiveCode = archiveCode;
|
||||
}
|
||||
|
||||
public Long getArchiveId() {
|
||||
return archiveId;
|
||||
}
|
||||
|
||||
public void setArchiveId(Long archiveId) {
|
||||
this.archiveId = archiveId;
|
||||
}
|
||||
|
||||
public String getRetentionDueDate() {
|
||||
return retentionDueDate;
|
||||
}
|
||||
|
||||
public void setRetentionDueDate(String retentionDueDate) {
|
||||
this.retentionDueDate = retentionDueDate;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,175 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 虚拟档案库(资料室·Module 1「档案库管理」)。一条 ArchiveVault 对应一个实体库房 / 密集架 /
|
||||
* 电子存储区,支持按年度、部门、保密等级、保存期限等维度建库。补审计「无虚拟档案库实体/接口」缺口。
|
||||
*
|
||||
* <p>storeType 区分载体:实体库房 / 密集架 / 电子存储区 / 云存储。实体类记 roomNo(库房号),
|
||||
* 电子/云类记 cloudId(云存储 ID / 存储路径),打通需求「电子档案记录存储路径或云存储ID」。
|
||||
* 维度字段(year/dept/secLevel/retentionScope)用于按维度建库与归档时落库匹配。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_vault")
|
||||
public class ArchiveVault {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 档案库编号(VL-流水)。 */
|
||||
private String code;
|
||||
|
||||
/** 档案库名称(如「2026 项目档案密集架 A 区」)。 */
|
||||
private String name;
|
||||
|
||||
/** 载体类型:实体库房 / 密集架 / 电子存储区 / 云存储。 */
|
||||
private String storeType;
|
||||
|
||||
/** 实体库房号(实体/密集架类填)。 */
|
||||
private String roomNo;
|
||||
|
||||
/** 云存储 ID / 存储路径(电子/云类填)。 */
|
||||
private String cloudId;
|
||||
|
||||
/** 建库维度·年度(如 2026,可空表示不限)。 */
|
||||
private String dimYear;
|
||||
|
||||
/** 建库维度·部门。 */
|
||||
private String dimDept;
|
||||
|
||||
/** 建库维度·保密等级上限(该库可容纳的最高密级)。 */
|
||||
private String dimSecLevel;
|
||||
|
||||
/** 建库维度·保存期限范围(永久 / 30年 / 10年 / 5年)。 */
|
||||
private String dimRetention;
|
||||
|
||||
/** 容量(盒/卷上限,0 表示不限)。 */
|
||||
private Integer capacity;
|
||||
|
||||
/** 是否启用。 */
|
||||
private Boolean enabled = Boolean.TRUE;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getStoreType() {
|
||||
return storeType;
|
||||
}
|
||||
|
||||
public void setStoreType(String storeType) {
|
||||
this.storeType = storeType;
|
||||
}
|
||||
|
||||
public String getRoomNo() {
|
||||
return roomNo;
|
||||
}
|
||||
|
||||
public void setRoomNo(String roomNo) {
|
||||
this.roomNo = roomNo;
|
||||
}
|
||||
|
||||
public String getCloudId() {
|
||||
return cloudId;
|
||||
}
|
||||
|
||||
public void setCloudId(String cloudId) {
|
||||
this.cloudId = cloudId;
|
||||
}
|
||||
|
||||
public String getDimYear() {
|
||||
return dimYear;
|
||||
}
|
||||
|
||||
public void setDimYear(String dimYear) {
|
||||
this.dimYear = dimYear;
|
||||
}
|
||||
|
||||
public String getDimDept() {
|
||||
return dimDept;
|
||||
}
|
||||
|
||||
public void setDimDept(String dimDept) {
|
||||
this.dimDept = dimDept;
|
||||
}
|
||||
|
||||
public String getDimSecLevel() {
|
||||
return dimSecLevel;
|
||||
}
|
||||
|
||||
public void setDimSecLevel(String dimSecLevel) {
|
||||
this.dimSecLevel = dimSecLevel;
|
||||
}
|
||||
|
||||
public String getDimRetention() {
|
||||
return dimRetention;
|
||||
}
|
||||
|
||||
public void setDimRetention(String dimRetention) {
|
||||
this.dimRetention = dimRetention;
|
||||
}
|
||||
|
||||
public Integer getCapacity() {
|
||||
return capacity;
|
||||
}
|
||||
|
||||
public void setCapacity(Integer capacity) {
|
||||
this.capacity = capacity;
|
||||
}
|
||||
|
||||
public Boolean getEnabled() {
|
||||
return enabled;
|
||||
}
|
||||
|
||||
public void setEnabled(Boolean enabled) {
|
||||
this.enabled = enabled;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,155 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 档案版本(资料室·Module 3「版本控制」)。同一档案可存在多个版本(如图纸 V1.0、V2.0),
|
||||
* 系统记录版本历史与变更说明;当前有效版本标记为「现行」,旧版本标记为「作废」但保留。
|
||||
* 补审计「Archive 无 version/现行/作废字段、DocumentController 恒置 setVersion(1) 无版本历史」缺口。
|
||||
*
|
||||
* <p>每条 ArchiveVersion 隶属一个 Archive(archiveId)。versionNo 形如 V1.0 / V2.0;
|
||||
* isCurrent 标记现行版本(同一 archiveId 下仅一条为 true,升级新版时自动把旧现行版置作废)。
|
||||
* status:现行 / 作废。changeNote 为本次版本的变更说明。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "archive_version")
|
||||
public class ArchiveVersion {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 所属档案 id({@link Archive})。 */
|
||||
private Long archiveId;
|
||||
|
||||
/** 档案标题(冗余)。 */
|
||||
private String archiveTitle;
|
||||
|
||||
/** 版本号(V1.0 / V2.0…)。 */
|
||||
private String versionNo;
|
||||
|
||||
/** 是否现行版本(同一档案下唯一)。 */
|
||||
private Boolean isCurrent = Boolean.FALSE;
|
||||
|
||||
/** 状态:现行 / 作废。 */
|
||||
private String status;
|
||||
|
||||
/** 变更说明。 */
|
||||
private String changeNote;
|
||||
|
||||
/** 关联存储文件 id(StoredFile.id,可空)。 */
|
||||
private Long storedFileId;
|
||||
|
||||
/** 文件名。 */
|
||||
private String fileName;
|
||||
|
||||
/** 上传/制版人。 */
|
||||
private String author;
|
||||
|
||||
/** 版本日期(YYYY-MM-DD)。 */
|
||||
private String versionDate;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getArchiveId() {
|
||||
return archiveId;
|
||||
}
|
||||
|
||||
public void setArchiveId(Long archiveId) {
|
||||
this.archiveId = archiveId;
|
||||
}
|
||||
|
||||
public String getArchiveTitle() {
|
||||
return archiveTitle;
|
||||
}
|
||||
|
||||
public void setArchiveTitle(String archiveTitle) {
|
||||
this.archiveTitle = archiveTitle;
|
||||
}
|
||||
|
||||
public String getVersionNo() {
|
||||
return versionNo;
|
||||
}
|
||||
|
||||
public void setVersionNo(String versionNo) {
|
||||
this.versionNo = versionNo;
|
||||
}
|
||||
|
||||
public Boolean getIsCurrent() {
|
||||
return isCurrent;
|
||||
}
|
||||
|
||||
public void setIsCurrent(Boolean isCurrent) {
|
||||
this.isCurrent = isCurrent;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getChangeNote() {
|
||||
return changeNote;
|
||||
}
|
||||
|
||||
public void setChangeNote(String changeNote) {
|
||||
this.changeNote = changeNote;
|
||||
}
|
||||
|
||||
public Long getStoredFileId() {
|
||||
return storedFileId;
|
||||
}
|
||||
|
||||
public void setStoredFileId(Long storedFileId) {
|
||||
this.storedFileId = storedFileId;
|
||||
}
|
||||
|
||||
public String getFileName() {
|
||||
return fileName;
|
||||
}
|
||||
|
||||
public void setFileName(String fileName) {
|
||||
this.fileName = fileName;
|
||||
}
|
||||
|
||||
public String getAuthor() {
|
||||
return author;
|
||||
}
|
||||
|
||||
public void setAuthor(String author) {
|
||||
this.author = author;
|
||||
}
|
||||
|
||||
public String getVersionDate() {
|
||||
return versionDate;
|
||||
}
|
||||
|
||||
public void setVersionDate(String versionDate) {
|
||||
this.versionDate = versionDate;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 年度审计计划(内控部·审计监察部·第1模块)。
|
||||
*
|
||||
* 每年由审计监察部编制,经审批后作为全年审计工作指引。
|
||||
* 计划项涵盖财务审计/内控审计/经济责任审计/专项审计/IT审计等。
|
||||
*
|
||||
* - planNo 计划编号,自动生成 AP-yyyy-序号;
|
||||
* - year 计划年度;
|
||||
* - auditType 审计类型:财务审计 / 内控审计 / 经济责任审计 / 专项审计 / IT审计;
|
||||
* - auditee 被审计单位/部门;
|
||||
* - riskLevel 风险等级:高 / 中 / 低;
|
||||
* - importance 重要性:重要 / 一般;
|
||||
* - basis 列入依据(风险评估结果/管理层要求/法规要求);
|
||||
* - plannedStartDate 计划开始日期 YYYY-MM-DD;
|
||||
* - plannedEndDate 计划结束日期;
|
||||
* - leadAuditor 项目负责人;
|
||||
* - teamMembers 审计组成员(逗号分隔);
|
||||
* - budget 审计项目预算(差旅费+外聘专家费等),BigDecimal;
|
||||
* - status 状态:草稿 → 待审批 → 已批准 → 实施中 → 已完成;
|
||||
* - approver 审批人;
|
||||
* - approvedAt 审批时间;
|
||||
* - linkedProjectId 关联的 audit_project 记录(立项后回写);
|
||||
* - notes 备注。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_annual_plan")
|
||||
public class AuditAnnualPlan {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String planNo;
|
||||
private String year;
|
||||
private String auditType;
|
||||
private String auditee;
|
||||
private String riskLevel;
|
||||
private String importance;
|
||||
private String basis;
|
||||
private String plannedStartDate;
|
||||
private String plannedEndDate;
|
||||
private String leadAuditor;
|
||||
private String teamMembers;
|
||||
private BigDecimal budget = BigDecimal.ZERO;
|
||||
private String status;
|
||||
private String approver;
|
||||
private Instant approvedAt;
|
||||
private Long linkedProjectId;
|
||||
private String notes;
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getPlanNo() { return planNo; }
|
||||
public void setPlanNo(String planNo) { this.planNo = planNo; }
|
||||
public String getYear() { return year; }
|
||||
public void setYear(String year) { this.year = year; }
|
||||
public String getAuditType() { return auditType; }
|
||||
public void setAuditType(String auditType) { this.auditType = auditType; }
|
||||
public String getAuditee() { return auditee; }
|
||||
public void setAuditee(String auditee) { this.auditee = auditee; }
|
||||
public String getRiskLevel() { return riskLevel; }
|
||||
public void setRiskLevel(String riskLevel) { this.riskLevel = riskLevel; }
|
||||
public String getImportance() { return importance; }
|
||||
public void setImportance(String importance) { this.importance = importance; }
|
||||
public String getBasis() { return basis; }
|
||||
public void setBasis(String basis) { this.basis = basis; }
|
||||
public String getPlannedStartDate() { return plannedStartDate; }
|
||||
public void setPlannedStartDate(String plannedStartDate) { this.plannedStartDate = plannedStartDate; }
|
||||
public String getPlannedEndDate() { return plannedEndDate; }
|
||||
public void setPlannedEndDate(String plannedEndDate) { this.plannedEndDate = plannedEndDate; }
|
||||
public String getLeadAuditor() { return leadAuditor; }
|
||||
public void setLeadAuditor(String leadAuditor) { this.leadAuditor = leadAuditor; }
|
||||
public String getTeamMembers() { return teamMembers; }
|
||||
public void setTeamMembers(String teamMembers) { this.teamMembers = teamMembers; }
|
||||
public BigDecimal getBudget() { return budget; }
|
||||
public void setBudget(BigDecimal budget) { this.budget = budget; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public String getApprover() { return approver; }
|
||||
public void setApprover(String approver) { this.approver = approver; }
|
||||
public Instant getApprovedAt() { return approvedAt; }
|
||||
public void setApprovedAt(Instant approvedAt) { this.approvedAt = approvedAt; }
|
||||
public Long getLinkedProjectId() { return linkedProjectId; }
|
||||
public void setLinkedProjectId(Long linkedProjectId) { this.linkedProjectId = linkedProjectId; }
|
||||
public String getNotes() { return notes; }
|
||||
public void setNotes(String notes) { this.notes = notes; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 审计档案库(内控部·审计监察部·第9模块)。
|
||||
*
|
||||
* 统一审计档案归档实体:底稿/报告/证据/整改记录按年度/项目类型分类归档。
|
||||
* 每条 AuditArchive 代表一个归档记录(可归档多类文件)。
|
||||
*
|
||||
* - archiveNo 档案编号,自动生成 AC-yyyy-序号;
|
||||
* - projectId 关联审计项目(可选,专项归档可无项目);
|
||||
* - projectName 冗余项目名;
|
||||
* - year 档案归属年度;
|
||||
* - archiveType 档案类型:底稿 / 报告 / 证据 / 整改记录 / 法规制度 / 知识总结;
|
||||
* - auditType 审计类型(来自关联项目);
|
||||
* - auditee 被审计单位;
|
||||
* - title 档案标题;
|
||||
* - description 档案描述/摘要;
|
||||
* - fileRef 文件路径/ID;
|
||||
* - fileHash 防篡改哈希值;
|
||||
* - tags 标签(逗号分隔,便于检索);
|
||||
* - keywords 关键词(供简易全文检索);
|
||||
* - status 状态:归档中 / 已归档 / 已销毁;
|
||||
* - archiver 归档人;
|
||||
* - archivedAt 归档时间;
|
||||
* - retentionYear 保存年限(年,-1=永久);
|
||||
* - createdAt 创建时间。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_archive")
|
||||
public class AuditArchive {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String archiveNo;
|
||||
private Long projectId;
|
||||
private String projectName;
|
||||
private String year;
|
||||
private String archiveType;
|
||||
private String auditType;
|
||||
private String auditee;
|
||||
private String title;
|
||||
private String description;
|
||||
private String fileRef;
|
||||
private String fileHash;
|
||||
private String tags;
|
||||
private String keywords;
|
||||
private String status;
|
||||
private String archiver;
|
||||
private Instant archivedAt;
|
||||
private int retentionYear;
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getArchiveNo() { return archiveNo; }
|
||||
public void setArchiveNo(String archiveNo) { this.archiveNo = archiveNo; }
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
public String getProjectName() { return projectName; }
|
||||
public void setProjectName(String projectName) { this.projectName = projectName; }
|
||||
public String getYear() { return year; }
|
||||
public void setYear(String year) { this.year = year; }
|
||||
public String getArchiveType() { return archiveType; }
|
||||
public void setArchiveType(String archiveType) { this.archiveType = archiveType; }
|
||||
public String getAuditType() { return auditType; }
|
||||
public void setAuditType(String auditType) { this.auditType = auditType; }
|
||||
public String getAuditee() { return auditee; }
|
||||
public void setAuditee(String auditee) { this.auditee = auditee; }
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
public String getFileRef() { return fileRef; }
|
||||
public void setFileRef(String fileRef) { this.fileRef = fileRef; }
|
||||
public String getFileHash() { return fileHash; }
|
||||
public void setFileHash(String fileHash) { this.fileHash = fileHash; }
|
||||
public String getTags() { return tags; }
|
||||
public void setTags(String tags) { this.tags = tags; }
|
||||
public String getKeywords() { return keywords; }
|
||||
public void setKeywords(String keywords) { this.keywords = keywords; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public String getArchiver() { return archiver; }
|
||||
public void setArchiver(String archiver) { this.archiver = archiver; }
|
||||
public Instant getArchivedAt() { return archivedAt; }
|
||||
public void setArchivedAt(Instant archivedAt) { this.archivedAt = archivedAt; }
|
||||
public int getRetentionYear() { return retentionYear; }
|
||||
public void setRetentionYear(int retentionYear) { this.retentionYear = retentionYear; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,84 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 连续审计日志(内控部·审计监察部·CAATs·连续审计 @Scheduled 结果存储)。
|
||||
*
|
||||
* 每次调度执行后,将异常分析摘要写入本表留存:
|
||||
* - runDate 运行日期(YYYY-MM-DD);
|
||||
* - analysisPeriod 分析周期描述(如 "近1日");
|
||||
* - module 分析模块(付款/费用/合同/库存 等);
|
||||
* - anomalyCount 本次发现异常条目数;
|
||||
* - totalAmount 涉及金额合计(BigDecimal);
|
||||
* - detail 摘要详情(JSON 字符串或多行文本,前端展开显示);
|
||||
* - riskLevel 风险等级:低/中/高;
|
||||
* - handled 是否已处置(false=待审查)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_continuous_log")
|
||||
public class AuditContinuousLog {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 调度运行日期(YYYY-MM-DD)。 */
|
||||
private String runDate;
|
||||
|
||||
/** 分析周期描述(近1日 / 近7日)。 */
|
||||
private String analysisPeriod;
|
||||
|
||||
/** 分析模块:付款 / 费用报销 / 合同 / 综合。 */
|
||||
@Column(name = "analysis_module")
|
||||
private String module;
|
||||
|
||||
/** 本次发现异常条目数(0 = 无异常)。 */
|
||||
private int anomalyCount;
|
||||
|
||||
/** 涉及金额合计(BigDecimal)。 */
|
||||
@Column(name = "total_amount")
|
||||
private BigDecimal totalAmount = BigDecimal.ZERO;
|
||||
|
||||
/** 异常摘要详情(可多行或 JSON)。 */
|
||||
@Column(columnDefinition = "TEXT")
|
||||
private String detail;
|
||||
|
||||
/** 风险等级:低 / 中 / 高。 */
|
||||
private String riskLevel;
|
||||
|
||||
/** 是否已处置(false=待审查,true=已处置)。 */
|
||||
@Column(name = "audit_handled")
|
||||
private boolean handled = false;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getRunDate() { return runDate; }
|
||||
public void setRunDate(String runDate) { this.runDate = runDate; }
|
||||
public String getAnalysisPeriod() { return analysisPeriod; }
|
||||
public void setAnalysisPeriod(String analysisPeriod) { this.analysisPeriod = analysisPeriod; }
|
||||
public String getModule() { return module; }
|
||||
public void setModule(String module) { this.module = module; }
|
||||
public int getAnomalyCount() { return anomalyCount; }
|
||||
public void setAnomalyCount(int anomalyCount) { this.anomalyCount = anomalyCount; }
|
||||
public BigDecimal getTotalAmount() { return totalAmount; }
|
||||
public void setTotalAmount(BigDecimal totalAmount) { this.totalAmount = totalAmount; }
|
||||
public String getDetail() { return detail; }
|
||||
public void setDetail(String detail) { this.detail = detail; }
|
||||
public String getRiskLevel() { return riskLevel; }
|
||||
public void setRiskLevel(String riskLevel) { this.riskLevel = riskLevel; }
|
||||
public boolean isHandled() { return handled; }
|
||||
public void setHandled(boolean handled) { this.handled = handled; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 审计证据库(内控部·审计监察部·模块2)。
|
||||
*
|
||||
* 上传并管理证据文件(扫描件/截图/数据导出文件),关联底稿。
|
||||
* 支持证据分类、标签、关键词检索(补全 PARTIAL 缺口:证据分类/标签/全文检索)。
|
||||
* 关键证据哈希防篡改。
|
||||
*
|
||||
* category: 财务凭证 / 合同文件 / 实物照片 / 系统截图 / 数据导出 / 会议记录 / 访谈记录 / 其他
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_evidence")
|
||||
public class AuditEvidence {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 关联审计项目 id。 */
|
||||
private Long projectId;
|
||||
|
||||
/** 冗余项目名称。 */
|
||||
private String projectName;
|
||||
|
||||
/** 关联底稿 id(可空)。 */
|
||||
private Long workPaperId;
|
||||
|
||||
/** 关联审计发现 id(可空)。 */
|
||||
private Long findingId;
|
||||
|
||||
/** 证据编号 AE-yyyy-序号。 */
|
||||
private String evidenceNo;
|
||||
|
||||
/** 证据标题/描述。 */
|
||||
private String title;
|
||||
|
||||
/** 证据类别:财务凭证 / 合同文件 / 实物照片 / 系统截图 / 数据导出 / 会议记录 / 访谈记录 / 其他。 */
|
||||
private String category;
|
||||
|
||||
/** 标签(逗号分隔,供检索)。 */
|
||||
private String tags;
|
||||
|
||||
/** 关键词(供全文检索匹配)。 */
|
||||
@Column(length = 2048)
|
||||
private String keywords;
|
||||
|
||||
/** 文件路径/引用(通过 FileController 上传后记录路径)。 */
|
||||
private String fileRef;
|
||||
|
||||
/** 文件名。 */
|
||||
private String fileName;
|
||||
|
||||
/** 文件类型(pdf/jpg/png/xlsx/docx等)。 */
|
||||
private String fileType;
|
||||
|
||||
/** 防篡改哈希值(SHA-256,关键证据必填)。 */
|
||||
private String fileHash;
|
||||
|
||||
/** 是否关键证据(关键证据加水印/哈希校验)。 */
|
||||
private boolean keyEvidence;
|
||||
|
||||
/** 水印文字(关键证据)。 */
|
||||
private String watermark;
|
||||
|
||||
/** 证据说明(解释证据内容与审计结论的关联)。 */
|
||||
@Column(length = 2048)
|
||||
private String description;
|
||||
|
||||
/** 收集人。 */
|
||||
private String collectedBy;
|
||||
|
||||
/** 收集日期。 */
|
||||
private String collectDate;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
|
||||
public String getProjectName() { return projectName; }
|
||||
public void setProjectName(String projectName) { this.projectName = projectName; }
|
||||
|
||||
public Long getWorkPaperId() { return workPaperId; }
|
||||
public void setWorkPaperId(Long workPaperId) { this.workPaperId = workPaperId; }
|
||||
|
||||
public Long getFindingId() { return findingId; }
|
||||
public void setFindingId(Long findingId) { this.findingId = findingId; }
|
||||
|
||||
public String getEvidenceNo() { return evidenceNo; }
|
||||
public void setEvidenceNo(String evidenceNo) { this.evidenceNo = evidenceNo; }
|
||||
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
|
||||
public String getCategory() { return category; }
|
||||
public void setCategory(String category) { this.category = category; }
|
||||
|
||||
public String getTags() { return tags; }
|
||||
public void setTags(String tags) { this.tags = tags; }
|
||||
|
||||
public String getKeywords() { return keywords; }
|
||||
public void setKeywords(String keywords) { this.keywords = keywords; }
|
||||
|
||||
public String getFileRef() { return fileRef; }
|
||||
public void setFileRef(String fileRef) { this.fileRef = fileRef; }
|
||||
|
||||
public String getFileName() { return fileName; }
|
||||
public void setFileName(String fileName) { this.fileName = fileName; }
|
||||
|
||||
public String getFileType() { return fileType; }
|
||||
public void setFileType(String fileType) { this.fileType = fileType; }
|
||||
|
||||
public String getFileHash() { return fileHash; }
|
||||
public void setFileHash(String fileHash) { this.fileHash = fileHash; }
|
||||
|
||||
public boolean isKeyEvidence() { return keyEvidence; }
|
||||
public void setKeyEvidence(boolean keyEvidence) { this.keyEvidence = keyEvidence; }
|
||||
|
||||
public String getWatermark() { return watermark; }
|
||||
public void setWatermark(String watermark) { this.watermark = watermark; }
|
||||
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
|
||||
public String getCollectedBy() { return collectedBy; }
|
||||
public void setCollectedBy(String collectedBy) { this.collectedBy = collectedBy; }
|
||||
|
||||
public String getCollectDate() { return collectDate; }
|
||||
public void setCollectDate(String collectDate) { this.collectDate = collectDate; }
|
||||
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,214 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 审计发现 / 问题登记(内控部·审计监察部)。一条 AuditFinding 代表一次审计项目中登记的
|
||||
* 一个问题:findingNo 为系统自动生成的问题编号(AF-yyyy-序号),关联 {@link AuditProject}。
|
||||
*
|
||||
* severity 风险等级为 高 / 中 / 低 之一;amountInvolved 为涉及金额(BigDecimal)。
|
||||
* status 流转 待沟通 → 已确认 → 整改中 → 已整改 / 不采纳:
|
||||
* - 待沟通:刚登记,待与被审计单位沟通确认;
|
||||
* - 已确认:被审计单位确认后进入整改流程,此时自动派生一条 {@link Rectification} 整改任务;
|
||||
* - 整改中:整改任务进行中(由整改任务状态回写);
|
||||
* - 已整改:整改验证通过(由整改任务回写);
|
||||
* - 不采纳:被审计单位不认可且复核维持,问题闭环不整改。
|
||||
*
|
||||
* 涉及金额、违反制度、事实依据为审计底稿核心要素;rootCause 供问题根源分析归并同类问题。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_finding")
|
||||
public class AuditFinding {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 自动生成的问题编号 AF-yyyy-序号。 */
|
||||
private String findingNo;
|
||||
|
||||
/** 关联审计项目 id。 */
|
||||
private Long projectId;
|
||||
|
||||
/** 冗余项目名/被审计单位,便于台账与整改页直接展示,免每行回查。 */
|
||||
private String projectName;
|
||||
|
||||
private String auditee;
|
||||
|
||||
/** 问题描述。 */
|
||||
private String title;
|
||||
|
||||
/** 事实依据(审计底稿要素)。 */
|
||||
private String factBasis;
|
||||
|
||||
/** 违反的制度 / 法规。 */
|
||||
private String violatedRule;
|
||||
|
||||
/** 涉及金额。 */
|
||||
private BigDecimal amountInvolved = BigDecimal.ZERO;
|
||||
|
||||
/** 风险等级:高 / 中 / 低。 */
|
||||
private String severity;
|
||||
|
||||
/** 问题类型(用于根源分析归并:采购循环 / 资金循环 / 费用报销 / 内控缺陷 / 合规 等)。 */
|
||||
private String category;
|
||||
|
||||
/** 根源(问题根源分析填写,识别系统性缺陷)。 */
|
||||
private String rootCause;
|
||||
|
||||
/** 待沟通 / 已确认 / 整改中 / 已整改 / 不采纳。 */
|
||||
private String status;
|
||||
|
||||
/** 被审计单位反馈意见(沟通确认环节)。 */
|
||||
private String auditeeFeedback;
|
||||
|
||||
/** 派生的整改任务 id(已确认后生成,便于双向跳转)。 */
|
||||
private Long rectificationId;
|
||||
|
||||
private String auditor;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getFindingNo() {
|
||||
return findingNo;
|
||||
}
|
||||
|
||||
public void setFindingNo(String findingNo) {
|
||||
this.findingNo = findingNo;
|
||||
}
|
||||
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public void setProjectId(Long projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public String getProjectName() {
|
||||
return projectName;
|
||||
}
|
||||
|
||||
public void setProjectName(String projectName) {
|
||||
this.projectName = projectName;
|
||||
}
|
||||
|
||||
public String getAuditee() {
|
||||
return auditee;
|
||||
}
|
||||
|
||||
public void setAuditee(String auditee) {
|
||||
this.auditee = auditee;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public void setTitle(String title) {
|
||||
this.title = title;
|
||||
}
|
||||
|
||||
public String getFactBasis() {
|
||||
return factBasis;
|
||||
}
|
||||
|
||||
public void setFactBasis(String factBasis) {
|
||||
this.factBasis = factBasis;
|
||||
}
|
||||
|
||||
public String getViolatedRule() {
|
||||
return violatedRule;
|
||||
}
|
||||
|
||||
public void setViolatedRule(String violatedRule) {
|
||||
this.violatedRule = violatedRule;
|
||||
}
|
||||
|
||||
public BigDecimal getAmountInvolved() {
|
||||
return amountInvolved;
|
||||
}
|
||||
|
||||
public void setAmountInvolved(BigDecimal amountInvolved) {
|
||||
this.amountInvolved = amountInvolved;
|
||||
}
|
||||
|
||||
public String getSeverity() {
|
||||
return severity;
|
||||
}
|
||||
|
||||
public void setSeverity(String severity) {
|
||||
this.severity = severity;
|
||||
}
|
||||
|
||||
public String getCategory() {
|
||||
return category;
|
||||
}
|
||||
|
||||
public void setCategory(String category) {
|
||||
this.category = category;
|
||||
}
|
||||
|
||||
public String getRootCause() {
|
||||
return rootCause;
|
||||
}
|
||||
|
||||
public void setRootCause(String rootCause) {
|
||||
this.rootCause = rootCause;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getAuditeeFeedback() {
|
||||
return auditeeFeedback;
|
||||
}
|
||||
|
||||
public void setAuditeeFeedback(String auditeeFeedback) {
|
||||
this.auditeeFeedback = auditeeFeedback;
|
||||
}
|
||||
|
||||
public Long getRectificationId() {
|
||||
return rectificationId;
|
||||
}
|
||||
|
||||
public void setRectificationId(Long rectificationId) {
|
||||
this.rectificationId = rectificationId;
|
||||
}
|
||||
|
||||
public String getAuditor() {
|
||||
return auditor;
|
||||
}
|
||||
|
||||
public void setAuditor(String auditor) {
|
||||
this.auditor = auditor;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 审计资料清单请求(内控部·审计监察部·第2模块)。
|
||||
*
|
||||
* 审计人员向被审计单位发出资料收集请求,系统跟踪每项资料的提供状态。
|
||||
* 一个审计项目可发出多条资料清单请求(按资料类别分批发出)。
|
||||
*
|
||||
* - requestNo 请求编号,自动生成 MR-yyyy-序号;
|
||||
* - projectId 关联审计项目;
|
||||
* - projectName 冗余项目名;
|
||||
* - auditee 被审计单位;
|
||||
* - itemName 资料名称(如:2024年度财务报表/采购合同台账/...);
|
||||
* - itemType 资料类型:报表 / 凭证 / 合同 / 制度 / 其他;
|
||||
* - description 详细描述(格式要求/期间范围等);
|
||||
* - dueDate 要求提供日期 YYYY-MM-DD;
|
||||
* - status 状态:待提供 → 已提供 → 验收中 → 已验收 / 已退回;
|
||||
* - fileRef 上传文件引用(被审计单位上传后记录路径/ID);
|
||||
* - fileHash 防篡改哈希值(SHA-256,文件上传时系统计算);
|
||||
* - submittedAt 被审计单位提交时间;
|
||||
* - verifiedAt 审计人员验收时间;
|
||||
* - verifier 验收人;
|
||||
* - rejectReason 退回原因;
|
||||
* - requester 请求发出人(审计人员);
|
||||
* - createdAt 创建时间。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_material_request")
|
||||
public class AuditMaterialRequest {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String requestNo;
|
||||
private Long projectId;
|
||||
private String projectName;
|
||||
private String auditee;
|
||||
private String itemName;
|
||||
private String itemType;
|
||||
private String description;
|
||||
private String dueDate;
|
||||
private String status;
|
||||
private String fileRef;
|
||||
private String fileHash;
|
||||
private Instant submittedAt;
|
||||
private Instant verifiedAt;
|
||||
private String verifier;
|
||||
private String rejectReason;
|
||||
private String requester;
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getRequestNo() { return requestNo; }
|
||||
public void setRequestNo(String requestNo) { this.requestNo = requestNo; }
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
public String getProjectName() { return projectName; }
|
||||
public void setProjectName(String projectName) { this.projectName = projectName; }
|
||||
public String getAuditee() { return auditee; }
|
||||
public void setAuditee(String auditee) { this.auditee = auditee; }
|
||||
public String getItemName() { return itemName; }
|
||||
public void setItemName(String itemName) { this.itemName = itemName; }
|
||||
public String getItemType() { return itemType; }
|
||||
public void setItemType(String itemType) { this.itemType = itemType; }
|
||||
public String getDescription() { return description; }
|
||||
public void setDescription(String description) { this.description = description; }
|
||||
public String getDueDate() { return dueDate; }
|
||||
public void setDueDate(String dueDate) { this.dueDate = dueDate; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public String getFileRef() { return fileRef; }
|
||||
public void setFileRef(String fileRef) { this.fileRef = fileRef; }
|
||||
public String getFileHash() { return fileHash; }
|
||||
public void setFileHash(String fileHash) { this.fileHash = fileHash; }
|
||||
public Instant getSubmittedAt() { return submittedAt; }
|
||||
public void setSubmittedAt(Instant submittedAt) { this.submittedAt = submittedAt; }
|
||||
public Instant getVerifiedAt() { return verifiedAt; }
|
||||
public void setVerifiedAt(Instant verifiedAt) { this.verifiedAt = verifiedAt; }
|
||||
public String getVerifier() { return verifier; }
|
||||
public void setVerifier(String verifier) { this.verifier = verifier; }
|
||||
public String getRejectReason() { return rejectReason; }
|
||||
public void setRejectReason(String rejectReason) { this.rejectReason = rejectReason; }
|
||||
public String getRequester() { return requester; }
|
||||
public void setRequester(String requester) { this.requester = requester; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 审计通知书(内控部·审计监察部·审计项目实施管理)。
|
||||
* 关联 {@link AuditProject},记录向被审计单位发出审计通知的全过程:
|
||||
* - 生成通知书(noticeNo 自动编号 AN-yyyy-序号);
|
||||
* - 被审计单位在线签收确认(signedAt 不为空且 signedBy 有值);
|
||||
* - status: 待签收 → 已签收 / 已撤回。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_notice")
|
||||
public class AuditNotice {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 自动生成的通知书编号 AN-yyyy-序号。 */
|
||||
private String noticeNo;
|
||||
|
||||
/** 关联审计项目 id。 */
|
||||
private Long projectId;
|
||||
|
||||
/** 冗余项目名称,便于列表展示。 */
|
||||
private String projectName;
|
||||
|
||||
/** 被审计单位名称。 */
|
||||
private String auditee;
|
||||
|
||||
/** 审计期间。 */
|
||||
private String auditPeriod;
|
||||
|
||||
/** 审计目的与范围(通知书正文摘要)。 */
|
||||
private String purpose;
|
||||
|
||||
/** 拟开始日期。 */
|
||||
private String plannedStartDate;
|
||||
|
||||
/** 主审 / 签发人。 */
|
||||
private String issuer;
|
||||
|
||||
/** 签发日期。 */
|
||||
private String issueDate;
|
||||
|
||||
/** 待签收 / 已签收 / 已撤回。 */
|
||||
private String status;
|
||||
|
||||
/** 被审计单位签收人。 */
|
||||
private String signedBy;
|
||||
|
||||
/** 签收时间。 */
|
||||
private Instant signedAt;
|
||||
|
||||
/** 签收备注(如有异议)。 */
|
||||
private String signNote;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public String getNoticeNo() { return noticeNo; }
|
||||
public void setNoticeNo(String noticeNo) { this.noticeNo = noticeNo; }
|
||||
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
|
||||
public String getProjectName() { return projectName; }
|
||||
public void setProjectName(String projectName) { this.projectName = projectName; }
|
||||
|
||||
public String getAuditee() { return auditee; }
|
||||
public void setAuditee(String auditee) { this.auditee = auditee; }
|
||||
|
||||
public String getAuditPeriod() { return auditPeriod; }
|
||||
public void setAuditPeriod(String auditPeriod) { this.auditPeriod = auditPeriod; }
|
||||
|
||||
public String getPurpose() { return purpose; }
|
||||
public void setPurpose(String purpose) { this.purpose = purpose; }
|
||||
|
||||
public String getPlannedStartDate() { return plannedStartDate; }
|
||||
public void setPlannedStartDate(String plannedStartDate) { this.plannedStartDate = plannedStartDate; }
|
||||
|
||||
public String getIssuer() { return issuer; }
|
||||
public void setIssuer(String issuer) { this.issuer = issuer; }
|
||||
|
||||
public String getIssueDate() { return issueDate; }
|
||||
public void setIssueDate(String issueDate) { this.issueDate = issueDate; }
|
||||
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
|
||||
public String getSignedBy() { return signedBy; }
|
||||
public void setSignedBy(String signedBy) { this.signedBy = signedBy; }
|
||||
|
||||
public Instant getSignedAt() { return signedAt; }
|
||||
public void setSignedAt(Instant signedAt) { this.signedAt = signedAt; }
|
||||
|
||||
public String getSignNote() { return signNote; }
|
||||
public void setSignNote(String signNote) { this.signNote = signNote; }
|
||||
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 审计程序库(内控部·审计监察部·审计档案与知识库 · 审计程序库)。
|
||||
* 维护各业务循环的标准审计程序,新审计项目可引用复用,提高效率。
|
||||
*
|
||||
* businessCycle 业务循环:采购循环 / 销售循环 / 资金循环 / 存货循环 / 固定资产循环 / 薪酬循环 / 合规循环 / IT审计 / 其他;
|
||||
* auditObjective 审计目标(完整性/准确性/合规性/存在性等);
|
||||
* procedures 标准审计步骤(可为多行文本,详细描述测试程序与抽样方法);
|
||||
* samplingMethod 抽样方法(随机/判断/属性/变量/其他);
|
||||
* isTemplate 是否为模板程序(模板不可修改,仅可复制使用)。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_procedure")
|
||||
public class AuditProcedure {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 程序编号 AP-yyyy-序号。 */
|
||||
private String procedureNo;
|
||||
|
||||
/** 程序名称。 */
|
||||
private String name;
|
||||
|
||||
/** 业务循环:采购/销售/资金/存货/固定资产/薪酬/合规/IT审计/其他。 */
|
||||
private String businessCycle;
|
||||
|
||||
/** 审计目标(完整性/准确性/合规性/存在性/切割/计价/呈报)。 */
|
||||
private String auditObjective;
|
||||
|
||||
/** 标准审计程序步骤(多行文本)。 */
|
||||
@Column(length = 8192)
|
||||
private String procedures;
|
||||
|
||||
/** 抽样方法:随机 / 判断 / 属性 / 变量 / 系统 / 不抽样。 */
|
||||
private String samplingMethod;
|
||||
|
||||
/** 参考法规/准则(如《内部审计准则》第X号、《企业内部控制基本规范》等)。 */
|
||||
@Column(name = "reference_links", length = 1024)
|
||||
private String references;
|
||||
|
||||
/** 是否为内置模板(true=仅可引用/复制,不可直接修改)。 */
|
||||
private boolean isTemplate;
|
||||
|
||||
/** 维护人。 */
|
||||
private String maintainedBy;
|
||||
|
||||
/** 最后更新日期。 */
|
||||
private String lastUpdatedDate;
|
||||
|
||||
/** 使用次数(被新项目引用的次数,便于排序/推荐)。 */
|
||||
private int usageCount;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public String getProcedureNo() { return procedureNo; }
|
||||
public void setProcedureNo(String procedureNo) { this.procedureNo = procedureNo; }
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
public String getBusinessCycle() { return businessCycle; }
|
||||
public void setBusinessCycle(String businessCycle) { this.businessCycle = businessCycle; }
|
||||
|
||||
public String getAuditObjective() { return auditObjective; }
|
||||
public void setAuditObjective(String auditObjective) { this.auditObjective = auditObjective; }
|
||||
|
||||
public String getProcedures() { return procedures; }
|
||||
public void setProcedures(String procedures) { this.procedures = procedures; }
|
||||
|
||||
public String getSamplingMethod() { return samplingMethod; }
|
||||
public void setSamplingMethod(String samplingMethod) { this.samplingMethod = samplingMethod; }
|
||||
|
||||
public String getReferences() { return references; }
|
||||
public void setReferences(String references) { this.references = references; }
|
||||
|
||||
public boolean isTemplate() { return isTemplate; }
|
||||
public void setTemplate(boolean template) { isTemplate = template; }
|
||||
|
||||
public String getMaintainedBy() { return maintainedBy; }
|
||||
public void setMaintainedBy(String maintainedBy) { this.maintainedBy = maintainedBy; }
|
||||
|
||||
public String getLastUpdatedDate() { return lastUpdatedDate; }
|
||||
public void setLastUpdatedDate(String lastUpdatedDate) { this.lastUpdatedDate = lastUpdatedDate; }
|
||||
|
||||
public int getUsageCount() { return usageCount; }
|
||||
public void setUsageCount(int usageCount) { this.usageCount = usageCount; }
|
||||
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,111 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* An audit project (审计项目) run by the audit and supervision center. auditType
|
||||
* is one of 财务审计 / 工程审计 / 合规审计 / 内控评价 / 经济责任审计; status is one
|
||||
* of 计划 / 实施中 / 报告 / 整改 / 已结. findings holds the count of issues found.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_project")
|
||||
public class AuditProject {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String name;
|
||||
|
||||
private String auditType;
|
||||
|
||||
private String auditee;
|
||||
|
||||
private String leadAuditor;
|
||||
|
||||
private String period;
|
||||
|
||||
private int findings;
|
||||
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public void setName(String name) {
|
||||
this.name = name;
|
||||
}
|
||||
|
||||
public String getAuditType() {
|
||||
return auditType;
|
||||
}
|
||||
|
||||
public void setAuditType(String auditType) {
|
||||
this.auditType = auditType;
|
||||
}
|
||||
|
||||
public String getAuditee() {
|
||||
return auditee;
|
||||
}
|
||||
|
||||
public void setAuditee(String auditee) {
|
||||
this.auditee = auditee;
|
||||
}
|
||||
|
||||
public String getLeadAuditor() {
|
||||
return leadAuditor;
|
||||
}
|
||||
|
||||
public void setLeadAuditor(String leadAuditor) {
|
||||
this.leadAuditor = leadAuditor;
|
||||
}
|
||||
|
||||
public String getPeriod() {
|
||||
return period;
|
||||
}
|
||||
|
||||
public void setPeriod(String period) {
|
||||
this.period = period;
|
||||
}
|
||||
|
||||
public int getFindings() {
|
||||
return findings;
|
||||
}
|
||||
|
||||
public void setFindings(int findings) {
|
||||
this.findings = findings;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 审计质量复核(内控部·审计监察部·第10模块)。
|
||||
*
|
||||
* 审计项目完成后,指定独立复核人进行质量检查,包括:
|
||||
* - 审计程序执行情况;
|
||||
* - 证据充分性;
|
||||
* - 底稿规范性。
|
||||
* 复核结论记录在此实体。
|
||||
*
|
||||
* - reviewNo 复核编号,自动生成 QR-yyyy-序号;
|
||||
* - projectId 关联审计项目;
|
||||
* - projectName 冗余项目名;
|
||||
* - reviewer 独立复核人(须非项目组成员);
|
||||
* - reviewDate 复核日期 YYYY-MM-DD;
|
||||
* - procedureScore 程序执行评分(0-100);
|
||||
* - evidenceScore 证据充分性评分(0-100);
|
||||
* - workpaperScore 底稿规范性评分(0-100);
|
||||
* - overallScore 综合评分(自动加权平均);
|
||||
* - overallResult 复核结论:优良 / 合格 / 需改进 / 不合格;
|
||||
* - procedureComment 程序执行意见;
|
||||
* - evidenceComment 证据充分性意见;
|
||||
* - workpaperComment 底稿规范性意见;
|
||||
* - overallComment 综合复核意见;
|
||||
* - improvementRequired 是否需要整改(true=需);
|
||||
* - improvementItems 需改进事项(换行分隔);
|
||||
* - status 状态:草稿 → 复核中 → 已完成;
|
||||
* - createdAt 创建时间。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_quality_review")
|
||||
public class AuditQualityReview {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String reviewNo;
|
||||
private Long projectId;
|
||||
private String projectName;
|
||||
private String reviewer;
|
||||
private String reviewDate;
|
||||
private int procedureScore;
|
||||
private int evidenceScore;
|
||||
private int workpaperScore;
|
||||
private int overallScore;
|
||||
private String overallResult;
|
||||
private String procedureComment;
|
||||
private String evidenceComment;
|
||||
private String workpaperComment;
|
||||
private String overallComment;
|
||||
private boolean improvementRequired;
|
||||
private String improvementItems;
|
||||
private String status;
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getReviewNo() { return reviewNo; }
|
||||
public void setReviewNo(String reviewNo) { this.reviewNo = reviewNo; }
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
public String getProjectName() { return projectName; }
|
||||
public void setProjectName(String projectName) { this.projectName = projectName; }
|
||||
public String getReviewer() { return reviewer; }
|
||||
public void setReviewer(String reviewer) { this.reviewer = reviewer; }
|
||||
public String getReviewDate() { return reviewDate; }
|
||||
public void setReviewDate(String reviewDate) { this.reviewDate = reviewDate; }
|
||||
public int getProcedureScore() { return procedureScore; }
|
||||
public void setProcedureScore(int procedureScore) { this.procedureScore = procedureScore; }
|
||||
public int getEvidenceScore() { return evidenceScore; }
|
||||
public void setEvidenceScore(int evidenceScore) { this.evidenceScore = evidenceScore; }
|
||||
public int getWorkpaperScore() { return workpaperScore; }
|
||||
public void setWorkpaperScore(int workpaperScore) { this.workpaperScore = workpaperScore; }
|
||||
public int getOverallScore() { return overallScore; }
|
||||
public void setOverallScore(int overallScore) { this.overallScore = overallScore; }
|
||||
public String getOverallResult() { return overallResult; }
|
||||
public void setOverallResult(String overallResult) { this.overallResult = overallResult; }
|
||||
public String getProcedureComment() { return procedureComment; }
|
||||
public void setProcedureComment(String procedureComment) { this.procedureComment = procedureComment; }
|
||||
public String getEvidenceComment() { return evidenceComment; }
|
||||
public void setEvidenceComment(String evidenceComment) { this.evidenceComment = evidenceComment; }
|
||||
public String getWorkpaperComment() { return workpaperComment; }
|
||||
public void setWorkpaperComment(String workpaperComment) { this.workpaperComment = workpaperComment; }
|
||||
public String getOverallComment() { return overallComment; }
|
||||
public void setOverallComment(String overallComment) { this.overallComment = overallComment; }
|
||||
public boolean isImprovementRequired() { return improvementRequired; }
|
||||
public void setImprovementRequired(boolean improvementRequired) { this.improvementRequired = improvementRequired; }
|
||||
public String getImprovementItems() { return improvementItems; }
|
||||
public void setImprovementItems(String improvementItems) { this.improvementItems = improvementItems; }
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,164 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 审计报告(内控部·审计监察部·模块2)。
|
||||
*
|
||||
* 基于审计底稿和问题清单自动生成审计报告,支持自定义报告模板,
|
||||
* 报告经审批后发送至管理层和被审计单位。
|
||||
*
|
||||
* 状态机:草稿 → 待审批 → 已审批 → 已发送
|
||||
* reportNo 格式:AR-yyyy-序号
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_report")
|
||||
public class AuditReport {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 报告编号 AR-yyyy-序号。 */
|
||||
private String reportNo;
|
||||
|
||||
/** 关联审计项目 id。 */
|
||||
private Long projectId;
|
||||
|
||||
/** 冗余项目名称。 */
|
||||
private String projectName;
|
||||
|
||||
/** 被审计单位。 */
|
||||
private String auditee;
|
||||
|
||||
/** 审计类型。 */
|
||||
private String auditType;
|
||||
|
||||
/** 审计期间。 */
|
||||
private String auditPeriod;
|
||||
|
||||
/** 报告标题。 */
|
||||
private String title;
|
||||
|
||||
/** 报告模板名称。 */
|
||||
private String templateName;
|
||||
|
||||
/** 审计结论(简要摘要)。 */
|
||||
@Column(length = 4096)
|
||||
private String conclusion;
|
||||
|
||||
/** 主要审计发现(自动从 AuditFinding 聚合或手动填写)。 */
|
||||
@Column(length = 8192)
|
||||
private String majorFindings;
|
||||
|
||||
/** 整改建议。 */
|
||||
@Column(length = 4096)
|
||||
private String recommendations;
|
||||
|
||||
/** 关联的问题编号列表(逗号分隔,如 AF-2026-001,AF-2026-002)。 */
|
||||
@Column(length = 2048)
|
||||
private String findingNos;
|
||||
|
||||
/** 关联的底稿编号列表(逗号分隔)。 */
|
||||
@Column(length = 2048)
|
||||
private String paperNos;
|
||||
|
||||
/** 报告编制人。 */
|
||||
private String preparedBy;
|
||||
|
||||
/** 审批人。 */
|
||||
private String approver;
|
||||
|
||||
/** 审批意见。 */
|
||||
@Column(length = 1024)
|
||||
private String approveComment;
|
||||
|
||||
/** 审批时间。 */
|
||||
private Instant approvedAt;
|
||||
|
||||
/** 发送时间。 */
|
||||
private Instant sentAt;
|
||||
|
||||
/** 接收人(管理层+被审计单位,逗号分隔)。 */
|
||||
private String recipients;
|
||||
|
||||
/**
|
||||
* 状态:草稿 / 待审批 / 已审批 / 已发送。
|
||||
*/
|
||||
private String status;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public String getReportNo() { return reportNo; }
|
||||
public void setReportNo(String reportNo) { this.reportNo = reportNo; }
|
||||
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
|
||||
public String getProjectName() { return projectName; }
|
||||
public void setProjectName(String projectName) { this.projectName = projectName; }
|
||||
|
||||
public String getAuditee() { return auditee; }
|
||||
public void setAuditee(String auditee) { this.auditee = auditee; }
|
||||
|
||||
public String getAuditType() { return auditType; }
|
||||
public void setAuditType(String auditType) { this.auditType = auditType; }
|
||||
|
||||
public String getAuditPeriod() { return auditPeriod; }
|
||||
public void setAuditPeriod(String auditPeriod) { this.auditPeriod = auditPeriod; }
|
||||
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
|
||||
public String getTemplateName() { return templateName; }
|
||||
public void setTemplateName(String templateName) { this.templateName = templateName; }
|
||||
|
||||
public String getConclusion() { return conclusion; }
|
||||
public void setConclusion(String conclusion) { this.conclusion = conclusion; }
|
||||
|
||||
public String getMajorFindings() { return majorFindings; }
|
||||
public void setMajorFindings(String majorFindings) { this.majorFindings = majorFindings; }
|
||||
|
||||
public String getRecommendations() { return recommendations; }
|
||||
public void setRecommendations(String recommendations) { this.recommendations = recommendations; }
|
||||
|
||||
public String getFindingNos() { return findingNos; }
|
||||
public void setFindingNos(String findingNos) { this.findingNos = findingNos; }
|
||||
|
||||
public String getPaperNos() { return paperNos; }
|
||||
public void setPaperNos(String paperNos) { this.paperNos = paperNos; }
|
||||
|
||||
public String getPreparedBy() { return preparedBy; }
|
||||
public void setPreparedBy(String preparedBy) { this.preparedBy = preparedBy; }
|
||||
|
||||
public String getApprover() { return approver; }
|
||||
public void setApprover(String approver) { this.approver = approver; }
|
||||
|
||||
public String getApproveComment() { return approveComment; }
|
||||
public void setApproveComment(String approveComment) { this.approveComment = approveComment; }
|
||||
|
||||
public Instant getApprovedAt() { return approvedAt; }
|
||||
public void setApprovedAt(Instant approvedAt) { this.approvedAt = approvedAt; }
|
||||
|
||||
public Instant getSentAt() { return sentAt; }
|
||||
public void setSentAt(Instant sentAt) { this.sentAt = sentAt; }
|
||||
|
||||
public String getRecipients() { return recipients; }
|
||||
public void setRecipients(String recipients) { this.recipients = recipients; }
|
||||
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 风险管理有效性审计评估(内控部·审计监察部·模块4)。
|
||||
*
|
||||
* 补全 PARTIAL 缺口:审计人员评估风险应对措施有效性,记录评估结论;
|
||||
* 发现风险管理缺陷时自动生成 AuditFinding 问题(通过 AuditRiskAuditController 联动)。
|
||||
*
|
||||
* 关联 RiskAssessment(法务合规风险库)中的风险条目,进行审计侧有效性评估。
|
||||
* status: 待评估 / 评估中 / 有效 / 部分有效 / 无效(→ 自动生成问题)
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_risk_assessment")
|
||||
public class AuditRiskAssessment {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 评估编号 ARA-yyyy-序号。 */
|
||||
private String assessNo;
|
||||
|
||||
/** 关联审计项目 id。 */
|
||||
private Long projectId;
|
||||
|
||||
/** 冗余项目名称。 */
|
||||
private String projectName;
|
||||
|
||||
/** 关联风险库中的风险 id(legal_risk_assessment)。 */
|
||||
private Long riskId;
|
||||
|
||||
/** 冗余风险名称/描述。 */
|
||||
private String riskName;
|
||||
|
||||
/** 风险类别(冗余自风险库)。 */
|
||||
private String riskCategory;
|
||||
|
||||
/** 风险等级(来自风险库:低/中/高/重大)。 */
|
||||
private String riskLevel;
|
||||
|
||||
/** 责任部门(冗余自风险库)。 */
|
||||
private String responsibleDept;
|
||||
|
||||
/** 原定应对措施(冗余自风险库)。 */
|
||||
@Column(length = 2048)
|
||||
private String originalMeasure;
|
||||
|
||||
/** 审计评估结论:有效 / 部分有效 / 无效。 */
|
||||
private String effectiveness;
|
||||
|
||||
/** 评估依据/发现的缺陷描述。 */
|
||||
@Column(length = 4096)
|
||||
private String assessmentDetail;
|
||||
|
||||
/** 整改建议。 */
|
||||
@Column(length = 2048)
|
||||
private String recommendation;
|
||||
|
||||
/** 评估人(审计员)。 */
|
||||
private String assessor;
|
||||
|
||||
/** 评估日期(YYYY-MM-DD)。 */
|
||||
private String assessDate;
|
||||
|
||||
/**
|
||||
* 状态:待评估 / 评估中 / 有效 / 部分有效 / 无效。
|
||||
* "无效"时自动生成 AuditFinding,findingId 回填。
|
||||
*/
|
||||
private String status;
|
||||
|
||||
/** 联动生成的审计发现 id(无效时自动派生)。 */
|
||||
private Long findingId;
|
||||
|
||||
/** 联动生成的审计发现编号(冗余)。 */
|
||||
private String findingNo;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public String getAssessNo() { return assessNo; }
|
||||
public void setAssessNo(String assessNo) { this.assessNo = assessNo; }
|
||||
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
|
||||
public String getProjectName() { return projectName; }
|
||||
public void setProjectName(String projectName) { this.projectName = projectName; }
|
||||
|
||||
public Long getRiskId() { return riskId; }
|
||||
public void setRiskId(Long riskId) { this.riskId = riskId; }
|
||||
|
||||
public String getRiskName() { return riskName; }
|
||||
public void setRiskName(String riskName) { this.riskName = riskName; }
|
||||
|
||||
public String getRiskCategory() { return riskCategory; }
|
||||
public void setRiskCategory(String riskCategory) { this.riskCategory = riskCategory; }
|
||||
|
||||
public String getRiskLevel() { return riskLevel; }
|
||||
public void setRiskLevel(String riskLevel) { this.riskLevel = riskLevel; }
|
||||
|
||||
public String getResponsibleDept() { return responsibleDept; }
|
||||
public void setResponsibleDept(String responsibleDept) { this.responsibleDept = responsibleDept; }
|
||||
|
||||
public String getOriginalMeasure() { return originalMeasure; }
|
||||
public void setOriginalMeasure(String originalMeasure) { this.originalMeasure = originalMeasure; }
|
||||
|
||||
public String getEffectiveness() { return effectiveness; }
|
||||
public void setEffectiveness(String effectiveness) { this.effectiveness = effectiveness; }
|
||||
|
||||
public String getAssessmentDetail() { return assessmentDetail; }
|
||||
public void setAssessmentDetail(String assessmentDetail) { this.assessmentDetail = assessmentDetail; }
|
||||
|
||||
public String getRecommendation() { return recommendation; }
|
||||
public void setRecommendation(String recommendation) { this.recommendation = recommendation; }
|
||||
|
||||
public String getAssessor() { return assessor; }
|
||||
public void setAssessor(String assessor) { this.assessor = assessor; }
|
||||
|
||||
public String getAssessDate() { return assessDate; }
|
||||
public void setAssessDate(String assessDate) { this.assessDate = assessDate; }
|
||||
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
|
||||
public Long getFindingId() { return findingId; }
|
||||
public void setFindingId(Long findingId) { this.findingId = findingId; }
|
||||
|
||||
public String getFindingNo() { return findingNo; }
|
||||
public void setFindingNo(String findingNo) { this.findingNo = findingNo; }
|
||||
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 审计工作底稿(内控部·审计监察部·审计项目实施管理)。
|
||||
* 每条底稿关联 {@link AuditProject},支持文字/表格/附件内容,
|
||||
* 版本受控(version 自增)、修改留痕(lastModifiedAt/lastModifiedBy),
|
||||
* 审计组长复核状态机(草稿→待复核→复核通过/退回)。
|
||||
*
|
||||
* 关键流程:
|
||||
* - 审计员 POST 新建底稿(草稿);
|
||||
* - PATCH /{id}/submit-review 提交审计组长复核(→ 待复核);
|
||||
* - PATCH /{id}/review 组长复核:通过(→ 复核通过)/ 退回(→ 草稿,退回意见填 reviewComment);
|
||||
* - PATCH /{id}/revise 修改底稿(版本号自增,旧版本记录快照摘要到 versionHistory),
|
||||
* 状态回草稿,需重新提交复核。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_work_paper")
|
||||
public class AuditWorkPaper {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 自动编号 WP-yyyy-序号。 */
|
||||
private String paperNo;
|
||||
|
||||
/** 关联审计项目 id。 */
|
||||
private Long projectId;
|
||||
|
||||
/** 冗余项目名称,便于列表展示。 */
|
||||
private String projectName;
|
||||
|
||||
/** 底稿标题。 */
|
||||
private String title;
|
||||
|
||||
/** 底稿类型(如:采购循环测试/资金循环测试/内控评估/固定资产盘点/其他)。 */
|
||||
private String paperType;
|
||||
|
||||
/** 编制人。 */
|
||||
private String preparedBy;
|
||||
|
||||
/** 底稿内容(文字 + 嵌入表格说明,附件通过 FileController 关联)。 */
|
||||
@Column(length = 8192)
|
||||
private String content;
|
||||
|
||||
/** 关联审计发现 id(可空,底稿可先建,后关联发现)。 */
|
||||
private Long findingId;
|
||||
|
||||
/**
|
||||
* 当前版本号,从 1 开始。PATCH /revise 时自增,旧版快照摘要追加到 versionHistory。
|
||||
*/
|
||||
private int version;
|
||||
|
||||
/**
|
||||
* 版本历史快照:格式 "V1: 标题(修改者, 时间)\nV2: ..." 简易追加留痕。
|
||||
* 完整历史如需查看可接 FileController 附件或 Archive 归档。
|
||||
*/
|
||||
@Column(length = 4096)
|
||||
private String versionHistory;
|
||||
|
||||
/** 草稿 / 待复核 / 复核通过 / 退回。 */
|
||||
private String status;
|
||||
|
||||
/** 复核人(审计组长)。 */
|
||||
private String reviewer;
|
||||
|
||||
/** 复核意见(通过/退回说明)。 */
|
||||
@Column(length = 2048)
|
||||
private String reviewComment;
|
||||
|
||||
/** 复核时间。 */
|
||||
private Instant reviewedAt;
|
||||
|
||||
/** 最后修改人。 */
|
||||
private String lastModifiedBy;
|
||||
|
||||
/** 最后修改时间(修改留痕)。 */
|
||||
private Instant lastModifiedAt;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public String getPaperNo() { return paperNo; }
|
||||
public void setPaperNo(String paperNo) { this.paperNo = paperNo; }
|
||||
|
||||
public Long getProjectId() { return projectId; }
|
||||
public void setProjectId(Long projectId) { this.projectId = projectId; }
|
||||
|
||||
public String getProjectName() { return projectName; }
|
||||
public void setProjectName(String projectName) { this.projectName = projectName; }
|
||||
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
|
||||
public String getPaperType() { return paperType; }
|
||||
public void setPaperType(String paperType) { this.paperType = paperType; }
|
||||
|
||||
public String getPreparedBy() { return preparedBy; }
|
||||
public void setPreparedBy(String preparedBy) { this.preparedBy = preparedBy; }
|
||||
|
||||
public String getContent() { return content; }
|
||||
public void setContent(String content) { this.content = content; }
|
||||
|
||||
public Long getFindingId() { return findingId; }
|
||||
public void setFindingId(Long findingId) { this.findingId = findingId; }
|
||||
|
||||
public int getVersion() { return version; }
|
||||
public void setVersion(int version) { this.version = version; }
|
||||
|
||||
public String getVersionHistory() { return versionHistory; }
|
||||
public void setVersionHistory(String versionHistory) { this.versionHistory = versionHistory; }
|
||||
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
|
||||
public String getReviewer() { return reviewer; }
|
||||
public void setReviewer(String reviewer) { this.reviewer = reviewer; }
|
||||
|
||||
public String getReviewComment() { return reviewComment; }
|
||||
public void setReviewComment(String reviewComment) { this.reviewComment = reviewComment; }
|
||||
|
||||
public Instant getReviewedAt() { return reviewedAt; }
|
||||
public void setReviewedAt(Instant reviewedAt) { this.reviewedAt = reviewedAt; }
|
||||
|
||||
public String getLastModifiedBy() { return lastModifiedBy; }
|
||||
public void setLastModifiedBy(String lastModifiedBy) { this.lastModifiedBy = lastModifiedBy; }
|
||||
|
||||
public Instant getLastModifiedAt() { return lastModifiedAt; }
|
||||
public void setLastModifiedAt(Instant lastModifiedAt) { this.lastModifiedAt = lastModifiedAt; }
|
||||
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,131 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 审计人员资质专长档案(内控部·审计监察部·模块1 审计计划与资源管理)。
|
||||
*
|
||||
* 补全 PARTIAL 缺口:
|
||||
* 「仅靠 teamMembers 字符串存储,无可查资质库;审计人员资质/专长/可用时间无独立实体」。
|
||||
*
|
||||
* 字段说明:
|
||||
* - employeeNo 工号(可关联 HR 模块)
|
||||
* - name 姓名(唯一)
|
||||
* - auditorType 审计师类型:内部审计师 / 外部专家 / 实习审计师
|
||||
* - specialties 专长方向(逗号分隔):财务审计/内控审计/IT审计/工程审计/舞弊调查 等
|
||||
* - certifications 资质证书(逗号分隔):CPA/CIA/CISA/CFE/注册造价师 等
|
||||
* - availableFrom 可用起始日(YYYY-MM-DD)
|
||||
* - availableTo 可用截止日(YYYY-MM-DD)
|
||||
* - workloadPct 当前工作负荷百分比(0-100);100=已满,<100=可接项目
|
||||
* - currentProject 当前承接项目编号(可空)
|
||||
* - interestConflictUnits 曾任职/有亲属关系的被审计单位(逗号分隔,独立性校验用)
|
||||
* - contactEmail 联系邮箱
|
||||
* - status 在职 / 停用
|
||||
* - remark 备注
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "audit_auditor_profile")
|
||||
public class AuditorProfile {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 工号(唯一)。 */
|
||||
@Column(name = "employee_no", unique = true)
|
||||
private String employeeNo;
|
||||
|
||||
/** 姓名。 */
|
||||
private String name;
|
||||
|
||||
/** 审计师类型:内部审计师 / 外部专家 / 实习审计师。 */
|
||||
private String auditorType;
|
||||
|
||||
/** 专长方向(逗号分隔)。 */
|
||||
private String specialties;
|
||||
|
||||
/** 资质证书(逗号分隔)。 */
|
||||
private String certifications;
|
||||
|
||||
/** 可用起始日(YYYY-MM-DD)。 */
|
||||
private String availableFrom;
|
||||
|
||||
/** 可用截止日(YYYY-MM-DD)。 */
|
||||
private String availableTo;
|
||||
|
||||
/** 当前工作负荷百分比(0-100)。 */
|
||||
private Integer workloadPct;
|
||||
|
||||
/** 当前承接项目编号(可空)。 */
|
||||
private String currentProject;
|
||||
|
||||
/**
|
||||
* 曾任职/有亲属关系的被审计单位(逗号分隔)。
|
||||
* 独立性校验:若派往与此字段有重叠的被审计单位,系统提示利益冲突。
|
||||
*/
|
||||
@Column(name = "interest_conflict_units")
|
||||
private String interestConflictUnits;
|
||||
|
||||
/** 联系邮箱。 */
|
||||
private String contactEmail;
|
||||
|
||||
/** 在职 / 停用。 */
|
||||
private String status;
|
||||
|
||||
/** 备注。 */
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public String getEmployeeNo() { return employeeNo; }
|
||||
public void setEmployeeNo(String employeeNo) { this.employeeNo = employeeNo; }
|
||||
|
||||
public String getName() { return name; }
|
||||
public void setName(String name) { this.name = name; }
|
||||
|
||||
public String getAuditorType() { return auditorType; }
|
||||
public void setAuditorType(String auditorType) { this.auditorType = auditorType; }
|
||||
|
||||
public String getSpecialties() { return specialties; }
|
||||
public void setSpecialties(String specialties) { this.specialties = specialties; }
|
||||
|
||||
public String getCertifications() { return certifications; }
|
||||
public void setCertifications(String certifications) { this.certifications = certifications; }
|
||||
|
||||
public String getAvailableFrom() { return availableFrom; }
|
||||
public void setAvailableFrom(String availableFrom) { this.availableFrom = availableFrom; }
|
||||
|
||||
public String getAvailableTo() { return availableTo; }
|
||||
public void setAvailableTo(String availableTo) { this.availableTo = availableTo; }
|
||||
|
||||
public Integer getWorkloadPct() { return workloadPct; }
|
||||
public void setWorkloadPct(Integer workloadPct) { this.workloadPct = workloadPct; }
|
||||
|
||||
public String getCurrentProject() { return currentProject; }
|
||||
public void setCurrentProject(String currentProject) { this.currentProject = currentProject; }
|
||||
|
||||
public String getInterestConflictUnits() { return interestConflictUnits; }
|
||||
public void setInterestConflictUnits(String interestConflictUnits) { this.interestConflictUnits = interestConflictUnits; }
|
||||
|
||||
public String getContactEmail() { return contactEmail; }
|
||||
public void setContactEmail(String contactEmail) { this.contactEmail = contactEmail; }
|
||||
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
|
||||
public String getRemark() { return remark; }
|
||||
public void setRemark(String remark) { this.remark = remark; }
|
||||
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import jakarta.persistence.UniqueConstraint;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* H16 流程办结自动联动台账 (automation_log). Each row records one downstream
|
||||
* effect fired by the workflow engine when a form instance reached 办结
|
||||
* (TriggerRuleEngine). Together with the (instanceId, ruleKey) idempotency key
|
||||
* this guarantees a rule fires at most once per instance even on a replayed
|
||||
* 办结 transition.
|
||||
*
|
||||
* action examples: 合同生效 / 生成待付付款单 / 回写合同已付款 / 新建项目 / 新建用印台账 / 新建供应商。
|
||||
* targetType is the downstream entity table (contract / payment / project /
|
||||
* seal_use / supplier); targetId is the affected row's id (may be null when the
|
||||
* rule was skipped because no matching主数据 was found). detail carries a
|
||||
* human-readable note; status (when present in detail) distinguishes 成功/跳过.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "automation_log",
|
||||
uniqueConstraints = @UniqueConstraint(
|
||||
name = "uk_automation_instance_rule",
|
||||
columnNames = { "instanceId", "ruleKey" }))
|
||||
public class AutomationLog {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** The办结 form instance that fired this rule. */
|
||||
private Long instanceId;
|
||||
|
||||
/** Snapshot of the instance title at trigger time (for ledger display). */
|
||||
private String instanceTitle;
|
||||
|
||||
/** Stable rule identifier — half of the (instanceId, ruleKey) idempotency key. */
|
||||
private String ruleKey;
|
||||
|
||||
/** What the engine did downstream (合同生效 / 生成待付付款单 / 新建项目 …). */
|
||||
private String action;
|
||||
|
||||
/** Downstream entity table: contract / payment / project / seal_use / supplier. */
|
||||
private String targetType;
|
||||
|
||||
/** Affected downstream row id (null when skipped — no matching主数据). */
|
||||
private Long targetId;
|
||||
|
||||
/** Human-readable detail / outcome note. */
|
||||
private String detail;
|
||||
|
||||
private Instant firedAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getInstanceId() {
|
||||
return instanceId;
|
||||
}
|
||||
|
||||
public void setInstanceId(Long instanceId) {
|
||||
this.instanceId = instanceId;
|
||||
}
|
||||
|
||||
public String getInstanceTitle() {
|
||||
return instanceTitle;
|
||||
}
|
||||
|
||||
public void setInstanceTitle(String instanceTitle) {
|
||||
this.instanceTitle = instanceTitle;
|
||||
}
|
||||
|
||||
public String getRuleKey() {
|
||||
return ruleKey;
|
||||
}
|
||||
|
||||
public void setRuleKey(String ruleKey) {
|
||||
this.ruleKey = ruleKey;
|
||||
}
|
||||
|
||||
public String getAction() {
|
||||
return action;
|
||||
}
|
||||
|
||||
public void setAction(String action) {
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public String getTargetType() {
|
||||
return targetType;
|
||||
}
|
||||
|
||||
public void setTargetType(String targetType) {
|
||||
this.targetType = targetType;
|
||||
}
|
||||
|
||||
public Long getTargetId() {
|
||||
return targetId;
|
||||
}
|
||||
|
||||
public void setTargetId(Long targetId) {
|
||||
this.targetId = targetId;
|
||||
}
|
||||
|
||||
public String getDetail() {
|
||||
return detail;
|
||||
}
|
||||
|
||||
public void setDetail(String detail) {
|
||||
this.detail = detail;
|
||||
}
|
||||
|
||||
public Instant getFiredAt() {
|
||||
return firedAt;
|
||||
}
|
||||
|
||||
public void setFiredAt(Instant firedAt) {
|
||||
this.firedAt = firedAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 银行账户台账(支付中心·银行账户与资金池管理)。
|
||||
* 统一管理集团及各分子公司在各家银行开立的所有账户(基本户/一般户/专户/外币户/保证金户)。
|
||||
* currency 默认人民币。status 正常/冻结/销户。
|
||||
* balance/frozenAmount/inTransitAmount 由每日余额同步或手工录入更新。
|
||||
* minReserve 最低备付金,低于此值系统自动预警。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "bank_account")
|
||||
public class BankAccount {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String accountName;
|
||||
|
||||
private String bankName;
|
||||
|
||||
private String accountNo;
|
||||
|
||||
private String companySubject;
|
||||
|
||||
private String currency;
|
||||
|
||||
private String purpose;
|
||||
|
||||
/** 账户类型:基本户 / 一般户 / 专户 / 外币户 / 保证金户。 */
|
||||
private String accountType;
|
||||
|
||||
/** 账户状态:正常 / 冻结 / 销户。 */
|
||||
private String status;
|
||||
|
||||
/** 授权人员(逗号分隔)。 */
|
||||
private String authPersonnel;
|
||||
|
||||
/** 开户日期(YYYY-MM-DD)。 */
|
||||
private String openDate;
|
||||
|
||||
/** 可用余额(元)。 */
|
||||
private BigDecimal balance = BigDecimal.ZERO;
|
||||
|
||||
/** 冻结金额(元)。 */
|
||||
private BigDecimal frozenAmount = BigDecimal.ZERO;
|
||||
|
||||
/** 在途资金(元)。 */
|
||||
private BigDecimal inTransitAmount = BigDecimal.ZERO;
|
||||
|
||||
/** 最低备付金(元,低于此值预警)。 */
|
||||
private BigDecimal minReserve = BigDecimal.ZERO;
|
||||
|
||||
/** 余额最后同步时间(YYYY-MM-DD HH:mm)。 */
|
||||
private String lastSyncTime;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getAccountName() {
|
||||
return accountName;
|
||||
}
|
||||
|
||||
public void setAccountName(String accountName) {
|
||||
this.accountName = accountName;
|
||||
}
|
||||
|
||||
public String getBankName() {
|
||||
return bankName;
|
||||
}
|
||||
|
||||
public void setBankName(String bankName) {
|
||||
this.bankName = bankName;
|
||||
}
|
||||
|
||||
public String getAccountNo() {
|
||||
return accountNo;
|
||||
}
|
||||
|
||||
public void setAccountNo(String accountNo) {
|
||||
this.accountNo = accountNo;
|
||||
}
|
||||
|
||||
public String getCompanySubject() {
|
||||
return companySubject;
|
||||
}
|
||||
|
||||
public void setCompanySubject(String companySubject) {
|
||||
this.companySubject = companySubject;
|
||||
}
|
||||
|
||||
public String getCurrency() {
|
||||
return currency;
|
||||
}
|
||||
|
||||
public void setCurrency(String currency) {
|
||||
this.currency = currency;
|
||||
}
|
||||
|
||||
public String getPurpose() {
|
||||
return purpose;
|
||||
}
|
||||
|
||||
public void setPurpose(String purpose) {
|
||||
this.purpose = purpose;
|
||||
}
|
||||
|
||||
public String getAccountType() {
|
||||
return accountType;
|
||||
}
|
||||
|
||||
public void setAccountType(String accountType) {
|
||||
this.accountType = accountType;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getAuthPersonnel() {
|
||||
return authPersonnel;
|
||||
}
|
||||
|
||||
public void setAuthPersonnel(String authPersonnel) {
|
||||
this.authPersonnel = authPersonnel;
|
||||
}
|
||||
|
||||
public String getOpenDate() {
|
||||
return openDate;
|
||||
}
|
||||
|
||||
public void setOpenDate(String openDate) {
|
||||
this.openDate = openDate;
|
||||
}
|
||||
|
||||
public BigDecimal getBalance() {
|
||||
return balance;
|
||||
}
|
||||
|
||||
public void setBalance(BigDecimal balance) {
|
||||
this.balance = balance;
|
||||
}
|
||||
|
||||
public BigDecimal getFrozenAmount() {
|
||||
return frozenAmount;
|
||||
}
|
||||
|
||||
public void setFrozenAmount(BigDecimal frozenAmount) {
|
||||
this.frozenAmount = frozenAmount;
|
||||
}
|
||||
|
||||
public BigDecimal getInTransitAmount() {
|
||||
return inTransitAmount;
|
||||
}
|
||||
|
||||
public void setInTransitAmount(BigDecimal inTransitAmount) {
|
||||
this.inTransitAmount = inTransitAmount;
|
||||
}
|
||||
|
||||
public BigDecimal getMinReserve() {
|
||||
return minReserve;
|
||||
}
|
||||
|
||||
public void setMinReserve(BigDecimal minReserve) {
|
||||
this.minReserve = minReserve;
|
||||
}
|
||||
|
||||
public String getLastSyncTime() {
|
||||
return lastSyncTime;
|
||||
}
|
||||
|
||||
public void setLastSyncTime(String lastSyncTime) {
|
||||
this.lastSyncTime = lastSyncTime;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,173 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 银行对账单(结算中心·银行对账)。一条 BankReconciliation 代表某账户某对账日的一次对账:
|
||||
* bankBalance 为银行流水余额(来自银行对账单 / 银企直连),bookBalance 为账面余额;
|
||||
* diff 为差额(银行余额 - 账面余额,由后端 {@link com.kaidi.oa.common.Money} 自动算并落库);
|
||||
* status 为 未对平 / 已对平(diff 为 0 时即对平)。
|
||||
* 银企直连自动取数为外部对接项,本表先支持手工 / 导入登记。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "stl_bank_reconciliation")
|
||||
public class BankReconciliation {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String code;
|
||||
|
||||
/** 银行账户名称 / 户名。 */
|
||||
private String bankAccount;
|
||||
|
||||
/** 银行账号。 */
|
||||
private String bankAccountNo;
|
||||
|
||||
/** 开户行。 */
|
||||
private String bankName;
|
||||
|
||||
/** 对账日(YYYY-MM-DD)。 */
|
||||
private String reconcileDate;
|
||||
|
||||
/** 银行流水余额。 */
|
||||
private BigDecimal bankBalance = BigDecimal.ZERO;
|
||||
|
||||
/** 账面余额。 */
|
||||
private BigDecimal bookBalance = BigDecimal.ZERO;
|
||||
|
||||
/** 差额 = 银行余额 - 账面余额(后端自动算)。 */
|
||||
private BigDecimal diff = BigDecimal.ZERO;
|
||||
|
||||
/** 未对平 / 已对平。 */
|
||||
private String status;
|
||||
|
||||
/** 差异说明 / 调节项备注。 */
|
||||
private String remark;
|
||||
|
||||
private String companySubject;
|
||||
|
||||
private String owner;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public String getBankAccount() {
|
||||
return bankAccount;
|
||||
}
|
||||
|
||||
public void setBankAccount(String bankAccount) {
|
||||
this.bankAccount = bankAccount;
|
||||
}
|
||||
|
||||
public String getBankAccountNo() {
|
||||
return bankAccountNo;
|
||||
}
|
||||
|
||||
public void setBankAccountNo(String bankAccountNo) {
|
||||
this.bankAccountNo = bankAccountNo;
|
||||
}
|
||||
|
||||
public String getBankName() {
|
||||
return bankName;
|
||||
}
|
||||
|
||||
public void setBankName(String bankName) {
|
||||
this.bankName = bankName;
|
||||
}
|
||||
|
||||
public String getReconcileDate() {
|
||||
return reconcileDate;
|
||||
}
|
||||
|
||||
public void setReconcileDate(String reconcileDate) {
|
||||
this.reconcileDate = reconcileDate;
|
||||
}
|
||||
|
||||
public BigDecimal getBankBalance() {
|
||||
return bankBalance;
|
||||
}
|
||||
|
||||
public void setBankBalance(BigDecimal bankBalance) {
|
||||
this.bankBalance = bankBalance;
|
||||
}
|
||||
|
||||
public BigDecimal getBookBalance() {
|
||||
return bookBalance;
|
||||
}
|
||||
|
||||
public void setBookBalance(BigDecimal bookBalance) {
|
||||
this.bookBalance = bookBalance;
|
||||
}
|
||||
|
||||
public BigDecimal getDiff() {
|
||||
return diff;
|
||||
}
|
||||
|
||||
public void setDiff(BigDecimal diff) {
|
||||
this.diff = diff;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public String getCompanySubject() {
|
||||
return companySubject;
|
||||
}
|
||||
|
||||
public void setCompanySubject(String companySubject) {
|
||||
this.companySubject = companySubject;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,212 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* A bidding project (投标项目) covering the full tender lifecycle.
|
||||
* projectType is one of 房建 / 市政 / 公路 / EPC / 咨询 / 环保设备 / 污水运营;
|
||||
* bidMethod is one of 公开招标 / 邀请招标 / 竞争性谈判; status is one of
|
||||
* 投标准备 / 已投标 / 中标 / 未中标 / 废标. May link to a CRM opportunity
|
||||
* (opportunityId) before winning, and to a project (projectId) after winning.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "bid")
|
||||
public class Bid {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String projectName;
|
||||
|
||||
private String bidNo;
|
||||
|
||||
private String tenderee;
|
||||
|
||||
private String projectType;
|
||||
|
||||
private BigDecimal controlPrice = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal bidAmount = BigDecimal.ZERO;
|
||||
|
||||
private BigDecimal deposit = BigDecimal.ZERO;
|
||||
|
||||
private String bidMethod;
|
||||
|
||||
private String status;
|
||||
|
||||
private String bidDate;
|
||||
|
||||
private String openDate;
|
||||
|
||||
private Long opportunityId;
|
||||
|
||||
private Long projectId;
|
||||
|
||||
private String owner;
|
||||
|
||||
private String orgUnit;
|
||||
|
||||
/**
|
||||
* 招标文件附件 ID(关联 StoredFile.id),办事处上传招标文件后直接回填到投标记录,
|
||||
* 解决"上传招标文件后只能分开引用"的缺口。物理列名 tender_file_id 规避 SQLite
|
||||
* 保留字冲突。
|
||||
*/
|
||||
@Column(name = "tender_file_id")
|
||||
private Long tenderFileId;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getProjectName() {
|
||||
return projectName;
|
||||
}
|
||||
|
||||
public void setProjectName(String projectName) {
|
||||
this.projectName = projectName;
|
||||
}
|
||||
|
||||
public String getBidNo() {
|
||||
return bidNo;
|
||||
}
|
||||
|
||||
public void setBidNo(String bidNo) {
|
||||
this.bidNo = bidNo;
|
||||
}
|
||||
|
||||
public String getTenderee() {
|
||||
return tenderee;
|
||||
}
|
||||
|
||||
public void setTenderee(String tenderee) {
|
||||
this.tenderee = tenderee;
|
||||
}
|
||||
|
||||
public String getProjectType() {
|
||||
return projectType;
|
||||
}
|
||||
|
||||
public void setProjectType(String projectType) {
|
||||
this.projectType = projectType;
|
||||
}
|
||||
|
||||
public BigDecimal getControlPrice() {
|
||||
return controlPrice;
|
||||
}
|
||||
|
||||
public void setControlPrice(BigDecimal controlPrice) {
|
||||
this.controlPrice = controlPrice;
|
||||
}
|
||||
|
||||
public BigDecimal getBidAmount() {
|
||||
return bidAmount;
|
||||
}
|
||||
|
||||
public void setBidAmount(BigDecimal bidAmount) {
|
||||
this.bidAmount = bidAmount;
|
||||
}
|
||||
|
||||
public BigDecimal getDeposit() {
|
||||
return deposit;
|
||||
}
|
||||
|
||||
public void setDeposit(BigDecimal deposit) {
|
||||
this.deposit = deposit;
|
||||
}
|
||||
|
||||
public String getBidMethod() {
|
||||
return bidMethod;
|
||||
}
|
||||
|
||||
public void setBidMethod(String bidMethod) {
|
||||
this.bidMethod = bidMethod;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getBidDate() {
|
||||
return bidDate;
|
||||
}
|
||||
|
||||
public void setBidDate(String bidDate) {
|
||||
this.bidDate = bidDate;
|
||||
}
|
||||
|
||||
public String getOpenDate() {
|
||||
return openDate;
|
||||
}
|
||||
|
||||
public void setOpenDate(String openDate) {
|
||||
this.openDate = openDate;
|
||||
}
|
||||
|
||||
public Long getOpportunityId() {
|
||||
return opportunityId;
|
||||
}
|
||||
|
||||
public void setOpportunityId(Long opportunityId) {
|
||||
this.opportunityId = opportunityId;
|
||||
}
|
||||
|
||||
public Long getProjectId() {
|
||||
return projectId;
|
||||
}
|
||||
|
||||
public void setProjectId(Long projectId) {
|
||||
this.projectId = projectId;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public String getOrgUnit() {
|
||||
return orgUnit;
|
||||
}
|
||||
|
||||
public void setOrgUnit(String orgUnit) {
|
||||
this.orgUnit = orgUnit;
|
||||
}
|
||||
|
||||
public Long getTenderFileId() {
|
||||
return tenderFileId;
|
||||
}
|
||||
|
||||
public void setTenderFileId(Long tenderFileId) {
|
||||
this.tenderFileId = tenderFileId;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
/**
|
||||
* 投标保证金台账(招投标与合同管理:记录投标保证金缴纳与退还)。
|
||||
* 状态机 status: 待缴纳 → 已缴纳 → 待退还 → 已退还;或 已缴纳/待退还 → 已没收。
|
||||
* 关联投标项目(bidId);占用资金 = 处于 已缴纳/待退还 状态的保证金累计(控制器聚合)。
|
||||
* expectRefundDate 预计退还日,退还超期由控制器按 ChronoUnit 算逾期天数并预警。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "bid_bond")
|
||||
public class BidBond {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 关联投标项目。 */
|
||||
private Long bidId;
|
||||
|
||||
private String projectName;
|
||||
|
||||
/** 招标人/收款方。 */
|
||||
private String payee;
|
||||
|
||||
private BigDecimal amount = BigDecimal.ZERO;
|
||||
|
||||
/** 缴纳方式:电汇 / 保函 / 保险 / 现金。 */
|
||||
private String payMethod;
|
||||
|
||||
/** 待缴纳 / 已缴纳 / 待退还 / 已退还 / 已没收。 */
|
||||
private String status;
|
||||
|
||||
private String payDate;
|
||||
|
||||
/** 预计退还日,YYYY-MM-DD。 */
|
||||
private String expectRefundDate;
|
||||
|
||||
/** 实际退还日,YYYY-MM-DD。 */
|
||||
private String refundDate;
|
||||
|
||||
private String owner;
|
||||
|
||||
private String orgUnit;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getBidId() {
|
||||
return bidId;
|
||||
}
|
||||
|
||||
public void setBidId(Long bidId) {
|
||||
this.bidId = bidId;
|
||||
}
|
||||
|
||||
public String getProjectName() {
|
||||
return projectName;
|
||||
}
|
||||
|
||||
public void setProjectName(String projectName) {
|
||||
this.projectName = projectName;
|
||||
}
|
||||
|
||||
public String getPayee() {
|
||||
return payee;
|
||||
}
|
||||
|
||||
public void setPayee(String payee) {
|
||||
this.payee = payee;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public String getPayMethod() {
|
||||
return payMethod;
|
||||
}
|
||||
|
||||
public void setPayMethod(String payMethod) {
|
||||
this.payMethod = payMethod;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getPayDate() {
|
||||
return payDate;
|
||||
}
|
||||
|
||||
public void setPayDate(String payDate) {
|
||||
this.payDate = payDate;
|
||||
}
|
||||
|
||||
public String getExpectRefundDate() {
|
||||
return expectRefundDate;
|
||||
}
|
||||
|
||||
public void setExpectRefundDate(String expectRefundDate) {
|
||||
this.expectRefundDate = expectRefundDate;
|
||||
}
|
||||
|
||||
public String getRefundDate() {
|
||||
return refundDate;
|
||||
}
|
||||
|
||||
public void setRefundDate(String refundDate) {
|
||||
this.refundDate = refundDate;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public String getOrgUnit() {
|
||||
return orgUnit;
|
||||
}
|
||||
|
||||
public void setOrgUnit(String orgUnit) {
|
||||
this.orgUnit = orgUnit;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,183 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 投标决策会(需求 §3「投标决策与任务分配」)。一次投标(bidId)对应一场决策会,
|
||||
* 技术/商务/成本/法务在线评审,记录结论。
|
||||
* decision:投标 / 不投标(弃标);
|
||||
* status:待决策 → 已决策(决策通过后由控制器自动生成投标任务清单 BidTask)。
|
||||
* 各维度意见以独立字段留痕(技术/商务/成本/法务),attendees 记录参会人。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "bid_decision")
|
||||
public class BidDecision {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 关联投标。 */
|
||||
private Long bidId;
|
||||
|
||||
private String bidNo;
|
||||
|
||||
private String projectName;
|
||||
|
||||
/** 决策会日期(YYYY-MM-DD)。 */
|
||||
private String meetingDate;
|
||||
|
||||
/** 参会人(逗号分隔)。 */
|
||||
private String attendees;
|
||||
|
||||
/** 技术评审意见。 */
|
||||
private String techOpinion;
|
||||
|
||||
/** 商务评审意见。 */
|
||||
private String bizOpinion;
|
||||
|
||||
/** 成本评审意见。 */
|
||||
private String costOpinion;
|
||||
|
||||
/** 法务评审意见。 */
|
||||
private String legalOpinion;
|
||||
|
||||
/** 投标 / 不投标。 */
|
||||
private String decision;
|
||||
|
||||
/** 待决策 / 已决策。 */
|
||||
private String status;
|
||||
|
||||
/** 已生成任务清单(避免重复生成)。 */
|
||||
private Boolean tasksGenerated = Boolean.FALSE;
|
||||
|
||||
private String owner;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Long getBidId() {
|
||||
return bidId;
|
||||
}
|
||||
|
||||
public void setBidId(Long bidId) {
|
||||
this.bidId = bidId;
|
||||
}
|
||||
|
||||
public String getBidNo() {
|
||||
return bidNo;
|
||||
}
|
||||
|
||||
public void setBidNo(String bidNo) {
|
||||
this.bidNo = bidNo;
|
||||
}
|
||||
|
||||
public String getProjectName() {
|
||||
return projectName;
|
||||
}
|
||||
|
||||
public void setProjectName(String projectName) {
|
||||
this.projectName = projectName;
|
||||
}
|
||||
|
||||
public String getMeetingDate() {
|
||||
return meetingDate;
|
||||
}
|
||||
|
||||
public void setMeetingDate(String meetingDate) {
|
||||
this.meetingDate = meetingDate;
|
||||
}
|
||||
|
||||
public String getAttendees() {
|
||||
return attendees;
|
||||
}
|
||||
|
||||
public void setAttendees(String attendees) {
|
||||
this.attendees = attendees;
|
||||
}
|
||||
|
||||
public String getTechOpinion() {
|
||||
return techOpinion;
|
||||
}
|
||||
|
||||
public void setTechOpinion(String techOpinion) {
|
||||
this.techOpinion = techOpinion;
|
||||
}
|
||||
|
||||
public String getBizOpinion() {
|
||||
return bizOpinion;
|
||||
}
|
||||
|
||||
public void setBizOpinion(String bizOpinion) {
|
||||
this.bizOpinion = bizOpinion;
|
||||
}
|
||||
|
||||
public String getCostOpinion() {
|
||||
return costOpinion;
|
||||
}
|
||||
|
||||
public void setCostOpinion(String costOpinion) {
|
||||
this.costOpinion = costOpinion;
|
||||
}
|
||||
|
||||
public String getLegalOpinion() {
|
||||
return legalOpinion;
|
||||
}
|
||||
|
||||
public void setLegalOpinion(String legalOpinion) {
|
||||
this.legalOpinion = legalOpinion;
|
||||
}
|
||||
|
||||
public String getDecision() {
|
||||
return decision;
|
||||
}
|
||||
|
||||
public void setDecision(String decision) {
|
||||
this.decision = decision;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public Boolean getTasksGenerated() {
|
||||
return tasksGenerated;
|
||||
}
|
||||
|
||||
public void setTasksGenerated(Boolean tasksGenerated) {
|
||||
this.tasksGenerated = tasksGenerated;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,192 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 投标保证金台账(需求 §3「投标保证金管理」)。每笔保证金对应一次投标(bidId)。
|
||||
* 生命周期 status:待缴纳 → 已缴纳 → 已退还(中标转履约保证金 / 未中标全额退)/ 逾期未退。
|
||||
* payMethod:银行转账 / 银行保函 / 电子保函 / 现金。
|
||||
* dueRefundDate 为约定退还日;逾期未退由预警接口分级催办。
|
||||
* 缴纳时在资金支付中心生成一张「投标保证金」付款单(paymentId),退还时回写 refundDate。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "bid_deposit")
|
||||
public class BidDeposit {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
private String code;
|
||||
|
||||
/** 关联投标。 */
|
||||
private Long bidId;
|
||||
|
||||
private String bidNo;
|
||||
|
||||
private String projectName;
|
||||
|
||||
/** 招标人 / 收款机构。 */
|
||||
private String tenderee;
|
||||
|
||||
private BigDecimal amount = BigDecimal.ZERO;
|
||||
|
||||
/** 银行转账 / 银行保函 / 电子保函 / 现金。 */
|
||||
private String payMethod;
|
||||
|
||||
/** 待缴纳 / 已缴纳 / 已退还 / 逾期未退。 */
|
||||
private String status;
|
||||
|
||||
/** 缴纳日期(YYYY-MM-DD)。 */
|
||||
private String payDate;
|
||||
|
||||
/** 约定退还日期(YYYY-MM-DD),用于逾期催办分级。 */
|
||||
private String dueRefundDate;
|
||||
|
||||
/** 实际退还日期(YYYY-MM-DD)。 */
|
||||
private String refundDate;
|
||||
|
||||
/** 缴纳时生成的资金支付中心付款单 id(联动结算/支付链)。 */
|
||||
private Long paymentId;
|
||||
|
||||
private String owner;
|
||||
|
||||
private String remark;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(Long id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getCode() {
|
||||
return code;
|
||||
}
|
||||
|
||||
public void setCode(String code) {
|
||||
this.code = code;
|
||||
}
|
||||
|
||||
public Long getBidId() {
|
||||
return bidId;
|
||||
}
|
||||
|
||||
public void setBidId(Long bidId) {
|
||||
this.bidId = bidId;
|
||||
}
|
||||
|
||||
public String getBidNo() {
|
||||
return bidNo;
|
||||
}
|
||||
|
||||
public void setBidNo(String bidNo) {
|
||||
this.bidNo = bidNo;
|
||||
}
|
||||
|
||||
public String getProjectName() {
|
||||
return projectName;
|
||||
}
|
||||
|
||||
public void setProjectName(String projectName) {
|
||||
this.projectName = projectName;
|
||||
}
|
||||
|
||||
public String getTenderee() {
|
||||
return tenderee;
|
||||
}
|
||||
|
||||
public void setTenderee(String tenderee) {
|
||||
this.tenderee = tenderee;
|
||||
}
|
||||
|
||||
public BigDecimal getAmount() {
|
||||
return amount;
|
||||
}
|
||||
|
||||
public void setAmount(BigDecimal amount) {
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
public String getPayMethod() {
|
||||
return payMethod;
|
||||
}
|
||||
|
||||
public void setPayMethod(String payMethod) {
|
||||
this.payMethod = payMethod;
|
||||
}
|
||||
|
||||
public String getStatus() {
|
||||
return status;
|
||||
}
|
||||
|
||||
public void setStatus(String status) {
|
||||
this.status = status;
|
||||
}
|
||||
|
||||
public String getPayDate() {
|
||||
return payDate;
|
||||
}
|
||||
|
||||
public void setPayDate(String payDate) {
|
||||
this.payDate = payDate;
|
||||
}
|
||||
|
||||
public String getDueRefundDate() {
|
||||
return dueRefundDate;
|
||||
}
|
||||
|
||||
public void setDueRefundDate(String dueRefundDate) {
|
||||
this.dueRefundDate = dueRefundDate;
|
||||
}
|
||||
|
||||
public String getRefundDate() {
|
||||
return refundDate;
|
||||
}
|
||||
|
||||
public void setRefundDate(String refundDate) {
|
||||
this.refundDate = refundDate;
|
||||
}
|
||||
|
||||
public Long getPaymentId() {
|
||||
return paymentId;
|
||||
}
|
||||
|
||||
public void setPaymentId(Long paymentId) {
|
||||
this.paymentId = paymentId;
|
||||
}
|
||||
|
||||
public String getOwner() {
|
||||
return owner;
|
||||
}
|
||||
|
||||
public void setOwner(String owner) {
|
||||
this.owner = owner;
|
||||
}
|
||||
|
||||
public String getRemark() {
|
||||
return remark;
|
||||
}
|
||||
|
||||
public void setRemark(String remark) {
|
||||
this.remark = remark;
|
||||
}
|
||||
|
||||
public Instant getCreatedAt() {
|
||||
return createdAt;
|
||||
}
|
||||
|
||||
public void setCreatedAt(Instant createdAt) {
|
||||
this.createdAt = createdAt;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 投标文件版本协同台账(需求 §3「投标文件编制」)。
|
||||
* 支持版本控制、在线批注、多人协同编制商务标/技术标/报价单。
|
||||
*
|
||||
* docPart: 商务标 / 技术标 / 报价单 / 资质材料 / 其他
|
||||
* status: 草稿 / 审核中 / 已定稿 / 已驳回
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "bid_doc_version")
|
||||
public class BidDocVersion {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 关联投标 bid.id。 */
|
||||
private Long bidId;
|
||||
|
||||
/** 文件组成部分:商务标 / 技术标 / 报价单 / 资质材料 / 其他。 */
|
||||
private String docPart;
|
||||
|
||||
/** 版本号(v1, v2, v1.1 等)。 */
|
||||
private String version;
|
||||
|
||||
/** 文件标题。 */
|
||||
private String docTitle;
|
||||
|
||||
/** 文件引用(上传后获得的 StoredFile 引用 id 或路径)。 */
|
||||
private String fileRef;
|
||||
|
||||
/** 在线批注内容(JSON 字符串或纯文本摘要)。 */
|
||||
private String annotations;
|
||||
|
||||
/** 参与协同编辑人员(逗号分隔 displayName)。 */
|
||||
private String collaborators;
|
||||
|
||||
/** 草稿 / 审核中 / 已定稿 / 已驳回。 */
|
||||
private String status;
|
||||
|
||||
/** 审核意见。 */
|
||||
private String reviewComment;
|
||||
|
||||
/** 提交人。 */
|
||||
private String submitter;
|
||||
|
||||
/** 提交时间。 */
|
||||
private Instant submittedAt;
|
||||
|
||||
/** 定稿时间(status=已定稿时写入)。 */
|
||||
private Instant finalizedAt;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
|
||||
public Long getBidId() { return bidId; }
|
||||
public void setBidId(Long bidId) { this.bidId = bidId; }
|
||||
|
||||
public String getDocPart() { return docPart; }
|
||||
public void setDocPart(String docPart) { this.docPart = docPart; }
|
||||
|
||||
public String getVersion() { return version; }
|
||||
public void setVersion(String version) { this.version = version; }
|
||||
|
||||
public String getDocTitle() { return docTitle; }
|
||||
public void setDocTitle(String docTitle) { this.docTitle = docTitle; }
|
||||
|
||||
public String getFileRef() { return fileRef; }
|
||||
public void setFileRef(String fileRef) { this.fileRef = fileRef; }
|
||||
|
||||
public String getAnnotations() { return annotations; }
|
||||
public void setAnnotations(String annotations) { this.annotations = annotations; }
|
||||
|
||||
public String getCollaborators() { return collaborators; }
|
||||
public void setCollaborators(String collaborators) { this.collaborators = collaborators; }
|
||||
|
||||
public String getStatus() { return status; }
|
||||
public void setStatus(String status) { this.status = status; }
|
||||
|
||||
public String getReviewComment() { return reviewComment; }
|
||||
public void setReviewComment(String reviewComment) { this.reviewComment = reviewComment; }
|
||||
|
||||
public String getSubmitter() { return submitter; }
|
||||
public void setSubmitter(String submitter) { this.submitter = submitter; }
|
||||
|
||||
public Instant getSubmittedAt() { return submittedAt; }
|
||||
public void setSubmittedAt(Instant submittedAt) { this.submittedAt = submittedAt; }
|
||||
|
||||
public Instant getFinalizedAt() { return finalizedAt; }
|
||||
public void setFinalizedAt(Instant finalizedAt) { this.finalizedAt = finalizedAt; }
|
||||
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
}
|
||||
@@ -0,0 +1,137 @@
|
||||
package com.kaidi.oa.domain;
|
||||
|
||||
import jakarta.persistence.Column;
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.GeneratedValue;
|
||||
import jakarta.persistence.GenerationType;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
/**
|
||||
* 投标知识库条目(咨询可研院·招投标管理·知识库补全)。
|
||||
*
|
||||
* 覆盖需求:
|
||||
* - 归档过往投标文件(bidFileRef);
|
||||
* - 报价策略(quoteStrategy);
|
||||
* - 评审记录(reviewNotes);
|
||||
* - 按项目类型/投标阶段/标签多维检索;
|
||||
* - 与 Bid(bidId)、Opportunity(opportunityId)、CslProject(cslProjectId)显式外键关联。
|
||||
*
|
||||
* entryType:标书范本 / 报价策略 / 评审记录 / 竞情分析 / 合规要点 / 其他。
|
||||
* phase:商机 / 投标准备 / 已投标 / 结果归档。
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "bid_knowledge_entry")
|
||||
public class BidKnowledgeEntry {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
private Long id;
|
||||
|
||||
/** 知识条目标题。 */
|
||||
private String title;
|
||||
|
||||
/** 条目类型:标书范本 / 报价策略 / 评审记录 / 竞情分析 / 合规要点 / 其他。 */
|
||||
private String entryType;
|
||||
|
||||
/** 投标阶段:商机 / 投标准备 / 已投标 / 结果归档。 */
|
||||
private String phase;
|
||||
|
||||
/** 关联的项目类型(与 Bid.projectType 一致)。 */
|
||||
private String projectType;
|
||||
|
||||
/** 专业领域:市政 / 交通 / 水利 / 建筑 / 环境 / 综合。 */
|
||||
private String field;
|
||||
|
||||
/** 适用地区 / 区域。 */
|
||||
private String region;
|
||||
|
||||
/** 标签(逗号分隔,便于多维检索)。 */
|
||||
private String tags;
|
||||
|
||||
/** 报价策略摘要(entryType=报价策略时填写)。 */
|
||||
@Column(length = 4000)
|
||||
private String quoteStrategy;
|
||||
|
||||
/** 评审记录(entryType=评审记录时填写)。 */
|
||||
@Column(length = 4000)
|
||||
private String reviewNotes;
|
||||
|
||||
/** 归档投标文件路径/引用(entryType=标书范本时填写)。 */
|
||||
@Column(length = 1000)
|
||||
private String bidFileRef;
|
||||
|
||||
/** 正文摘要 / 知识内容。 */
|
||||
@Column(length = 8000)
|
||||
private String content;
|
||||
|
||||
/** 关联投标 ID(bid 表)。 */
|
||||
private Long bidId;
|
||||
|
||||
/** 关联商机 ID(opportunity 表)。 */
|
||||
private Long opportunityId;
|
||||
|
||||
/** 关联咨询项目 ID(csl_project 表)。 */
|
||||
private Long cslProjectId;
|
||||
|
||||
/** 质量评分(1-5,录入者自评或评审打分)。 */
|
||||
private Integer qualityScore;
|
||||
|
||||
/** 录入人。 */
|
||||
private String creator;
|
||||
|
||||
/** 是否公开(公共知识库 vs. 项目私有)。 */
|
||||
private Boolean isPublic;
|
||||
|
||||
/** 浏览次数(被引用/查看)。 */
|
||||
private Integer viewCount;
|
||||
|
||||
private Instant createdAt;
|
||||
|
||||
private Instant updatedAt;
|
||||
|
||||
public Long getId() { return id; }
|
||||
public void setId(Long id) { this.id = id; }
|
||||
public String getTitle() { return title; }
|
||||
public void setTitle(String title) { this.title = title; }
|
||||
public String getEntryType() { return entryType; }
|
||||
public void setEntryType(String entryType) { this.entryType = entryType; }
|
||||
public String getPhase() { return phase; }
|
||||
public void setPhase(String phase) { this.phase = phase; }
|
||||
public String getProjectType() { return projectType; }
|
||||
public void setProjectType(String projectType) { this.projectType = projectType; }
|
||||
public String getField() { return field; }
|
||||
public void setField(String field) { this.field = field; }
|
||||
public String getRegion() { return region; }
|
||||
public void setRegion(String region) { this.region = region; }
|
||||
public String getTags() { return tags; }
|
||||
public void setTags(String tags) { this.tags = tags; }
|
||||
public String getQuoteStrategy() { return quoteStrategy; }
|
||||
public void setQuoteStrategy(String quoteStrategy) { this.quoteStrategy = quoteStrategy; }
|
||||
public String getReviewNotes() { return reviewNotes; }
|
||||
public void setReviewNotes(String reviewNotes) { this.reviewNotes = reviewNotes; }
|
||||
public String getBidFileRef() { return bidFileRef; }
|
||||
public void setBidFileRef(String bidFileRef) { this.bidFileRef = bidFileRef; }
|
||||
public String getContent() { return content; }
|
||||
public void setContent(String content) { this.content = content; }
|
||||
public Long getBidId() { return bidId; }
|
||||
public void setBidId(Long bidId) { this.bidId = bidId; }
|
||||
public Long getOpportunityId() { return opportunityId; }
|
||||
public void setOpportunityId(Long opportunityId) { this.opportunityId = opportunityId; }
|
||||
public Long getCslProjectId() { return cslProjectId; }
|
||||
public void setCslProjectId(Long cslProjectId) { this.cslProjectId = cslProjectId; }
|
||||
public Integer getQualityScore() { return qualityScore; }
|
||||
public void setQualityScore(Integer qualityScore) { this.qualityScore = qualityScore; }
|
||||
public String getCreator() { return creator; }
|
||||
public void setCreator(String creator) { this.creator = creator; }
|
||||
public Boolean getIsPublic() { return isPublic; }
|
||||
public void setIsPublic(Boolean isPublic) { this.isPublic = isPublic; }
|
||||
public Integer getViewCount() { return viewCount; }
|
||||
public void setViewCount(Integer viewCount) { this.viewCount = viewCount; }
|
||||
public Instant getCreatedAt() { return createdAt; }
|
||||
public void setCreatedAt(Instant createdAt) { this.createdAt = createdAt; }
|
||||
public Instant getUpdatedAt() { return updatedAt; }
|
||||
public void setUpdatedAt(Instant updatedAt) { this.updatedAt = updatedAt; }
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user