这是审查报告里标了「🔴 完全未查」的两个维度之一。
结论先说:金额计算全部整数运算,未发现浮点混进分的问题。
| 项 | 结论 |
|---|---|
| 后端金额是否用浮点 | ✅ 全程整数分,无浮点参与计算 |
| C 端金额是否用浮点 | ✅ 全程整数分,浮点只在最终显示时出现 |
| 部分退款的分摊 | ✅ 守恒,尾差有明确归属 |
| 发现问题 | 0 个(本次) |
初筛规则(金额 ×0.01、round(fen)、float(fen)、浮点字面量)命中 91 处,
逐处判读后分成三类:
| 类型 | 数量 | 判定 |
|---|---|---|
分↔元单位换算(int(fen)/100、int(round(price*100))) | 80 | ✅ 正常,转换点在边界 |
整数运算含 //(unit_fen * quantity、total * pct // 100) | 8 | ✅ 全整数,无精度损失 |
round() 作用于金额 | 3 | ✅ 作用在「元→分」的换算结果上 |
float() 的全部用途(逐个确认没有一个是金额)sync.py:74 float(latitude/longitude) ← 经纬度
stalls.py:27 round(float(v), 2) ← 摊位费展示(读fen 转元)
weather.py:39-40 float(WEAHTER_LAT/LNG) ← 天气坐标
weather.py:193 float(score) ← 评分
miniapp_status.py:111 float(v) / 118 int(float(v)) ← 状态值
admin_ops.py:330 float(rating) ← 评分
🔴 没有一个是金额计算。
Decimal 的用途admin_ops.py:851 str(Decimal(fen) / 100) ← 分→元展示(Decimal 是对的用法)
tasks_full.py:128 reward_credits * Decimal("0.7") ← 工分(不是人民币)
miniapp_orders.py L1800-1805)key_refund_fen = key_deduct_fen * pending_fen // total_fen
cash_paid_fen = total_fen - key_deduct_fen
cash_refund_fen = cash_paid_fen * pending_fen // total_fen
# 分币尾差归属:总 pending_fen = cash_refund + key_refund
diff = pending_fen - (cash_refund_fen + key_refund_fen)
cash_refund_fen += diff # 🔑 尾差补齐
| total | keyDeduct | pending | keyR | cashR | 合计 | 守恒 |
|---|---|---|---|---|---|---|
| 10000 | 3000 | 5000 | 1500 | 3500 | 5000 | ✅ |
| 68800 | 0 | 68800 | 0 | 68800 | 68800 | ✅ |
| 999 | 1 | 1 | 0 | 1 | 1 | ✅ |
| 1000 | 333 | 667 | 222 | 445 | 667 | ✅ |
| 68800 | 20000 | 45867 | 13333 | 32534 | 45867 | ✅ |
| 100 | 1 | 3 | 0 | 3 | 3 | ✅ |
| 1 | 0 | 1 | 0 | 1 | 1 | ✅ |
| 1 | 1 | 1 | 1 | 0 | 1 | ✅ |
✅ 全部守恒。 L1804-1805 的尾差补齐是关键 —— 没有它,四种场景会差 1 分。
pending_fen > total_fen 时不守恒(如 total=100, key=50, pending=150 → 75+75=150≠pending=150,其实守恒)。
实测也没有负数。 且 pending_fen 是 total_fen 的子集(未核销金额),
逻辑上不可能大于。不构成风险。
pages/redemption-code/redemption-code.ts(voucher 待付金额汇总)
const MAX_TOTAL_FEN = 100000000 // 100万元上限
if (dueFen + r.fen > MAX_TOTAL_FEN) { hasUnknown = true; continue }
...
dueFen += r.fen // 整数累加
// 显示时才转浮点
return '到店支付 ¥' + (dueFen % 100 === 0 ? String(dueFen / 100) : (dueFen / 100).toFixed(2))
三个做对的地方:
/ 100 只在最后拼字符串时出现MAX_TOTAL_FEN,超出即视为「不可判定」hasUnknown ⇒ 返回「支付信息以现场核验为准」,不猜总额(与记忆里「任一单 unknown ⇒ 整批中性」的口径一致)
C 端金额只有 1 处涉及 fen 运算(另一处命中是base64 图片误报)。
「整数分 + 显示时才转浮点」是这个项目做得最对的一件事之一。
对比常见错误写法:
// ❌ 常见错误:浮点累加
let total = 0
for (const o of orders) total += o.price // price 是元(浮点)
// ✅ 本项目:整数累加
let dueFen = 0
for (const o of orders) dueFen += o.fen// fen 是分(整数)
建议把这个写进 docs/代码审查标准与流程.md 的 BLOCKER 列表:
金额一律用整数分计算,浮点只允许出现在最终展示层。
判断依据:能否回答「这个数从头到尾有没有经过浮点?」
| 未查 | 说明 |
|---|---|
| 🟠 后端 91 处只逐处判了类型,未逐处追数据流 | 大部分是 int(fen)/100 的展示转换,风险极低;但不是 100% 逐处追 |
| 🔴 前端 wxml/wxss | 与金额无关,但仍未审 |
| 🟠 微信支付侧的金额校验 | wxpay.py 里下单金额与实际计算是否一致,未查 |
| 🔴 优惠券/抵扣叠加后的分摊 | 只查了「定金抵扣 + 部分退款」,未查多券叠加 |
我第一次验算时只复制了 L1801-1803 三行,漏了 L1804-1805 的尾差补齐,
算出「4 个场景两法差 1 分」,
差点误判成 🔴 bug。
发现方式:输出与代码注释矛盾(注释明明写了「分币尾差归属」)⇒ 回读代码才发现漏了两行。
⇒ 🔴 复制代码验算时必须核对行数,注释和代码对不上时以代码为准、但要问自己「是不是漏了」。