Files
ERP/oa-backend/src/main/java/com/kaidi/oa/common/Money.java
T
QiufengandClaude Opus 4.8 5e51dc3f56 SNAPSHOT W7 已部署稳定态 — 凯迪ERP+OA一体化平台 (MET 73.3%)
恢复点(restore point)。别人改崩后可 git reset --hard 回到此提交。

== 此快照内容 ==
- 后端 oa-backend: 734 控制器 / 711 实体 (Spring Boot 3.2.5 + SQLite, 端口8091)
- 前端 modern-ui/app: Vue3+Vite, 约700页 (构建产物已在 oa-backend/src/main/resources/static)
- 数据库 oa-backend/data/oa.db: 含全部演示数据 (强制入库, 6.6MB)
- 交接文档 go.md + go-code-reference/endpoints/entities/database.md
- 多代理建设脚本 .claude/wf-*.js

== 状态 ==
- 对 凯迪科技ERP_20260507.xlsx 合规 MET ~73.3% (PARTIAL 75: 34可建+6种子/bug+35外部硬天花板)
- 安全: 5轮红队+5轮复检, default-deny分级鉴权, 连续零可利用
- W3~W7 累计补完436缺口; W8末轮(40缺口)为半成品(源码树可编译但未集成)
- 运行: cd oa-backend; java -jar build/libs/oa-backend-0.1.0.jar --server.port=8091; admin/123456

== 排除(gitignore, 可再生) ==
node_modules / oa-backend/build / .jdks / *.log / Backup-ERP-* / 弃用的OFBiz核心(只保留modern-ui)
完整文件夹备份见同目录 Backup-ERP-20260615-191517/ (含上述全部, 仅缺 node_modules)

时间戳: 20260615-191517

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-15 19:19:15 +08:00

82 lines
2.8 KiB
Java
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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 的 BigDecimalnull 视为 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/-InfinityJSON 里的 "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 是否 > bnull 当 0)。 */
public static boolean gt(BigDecimal a, BigDecimal b) {
return nz(a).compareTo(nz(b)) > 0;
}
/** a 是否 <= 0null 当 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);
}
}