|
|
@@ -96,9 +96,10 @@ func recommendCompare(c Case, lg, tg *SideResult) ([]Diff, []Assertion) {
|
|
|
asserts = append(asserts, Assertion{Kind: AssertionBusiness,
|
|
|
Name: "W4-B① 全局 `balance`:**两侧都 +m**(`t_user_expense.balance` 有 `DEFAULT 0.000`,建行后有值)",
|
|
|
Pass: delta(lb.Balance, lr.Balance) == want && delta(tb.Balance, tr.Balance) == want,
|
|
|
- Detail: beforeShape + fmt.Sprintf(" | legacy %+d(期望 %+d)| target %+d(期望 %+d)| m=%d。"+
|
|
|
+ Detail: beforeShape + fmt.Sprintf(" | 全局 balance:legacy %s → %s(Δ%+d,期望 %+d)| target %s → %s(Δ%+d,期望 %+d)| m=%d。"+
|
|
|
"这一条是**唯一两侧一致**的那句 —— 也是为什么只看全局会漏掉整个缺陷",
|
|
|
- delta(lb.Balance, lr.Balance), want, delta(tb.Balance, tr.Balance), want, want),
|
|
|
+ centsOf2(lb.Balance), centsOf2(lr.Balance), delta(lb.Balance, lr.Balance), want,
|
|
|
+ centsOf2(tb.Balance), centsOf2(tr.Balance), delta(tb.Balance, tr.Balance), want, want),
|
|
|
})
|
|
|
|
|
|
add("W4-B·推荐人 校区 Δbalance", fmt.Sprintf("%+d(NULL=%v)", delta(lbc.Balance, lrc.Balance), lrc.Nulls.Balance),
|
|
|
@@ -107,9 +108,13 @@ func recommendCompare(c Case, lg, tg *SideResult) ([]Diff, []Assertion) {
|
|
|
asserts = append(asserts, Assertion{Kind: AssertionBusiness,
|
|
|
Name: "W4-B② 校区 `balance`:legacy **仍是 NULL(没到账)** / target **+m 且有值**",
|
|
|
Pass: lrc.Nulls.Balance && delta(lbc.Balance, lrc.Balance) == 0 && !trc.Nulls.Balance && delta(tbc.Balance, trc.Balance) == want,
|
|
|
- Detail: beforeShape + fmt.Sprintf(" | legacy Δ=%+d NULL=%v | target Δ=%+d NULL=%v | m=%d。"+
|
|
|
+ Detail: beforeShape + fmt.Sprintf(" | 校区 balance:legacy %s(NULL=%v) → %s(NULL=%v)(Δ%+d)| "+
|
|
|
+ "target %s(NULL=%v) → %s(NULL=%v)(Δ%+d,期望 %+d)| m=%d。"+
|
|
|
"legacy 侧 `SET balance=balance+m` 在 NULL 上运算仍是 NULL ⇒ **校区一分没记**",
|
|
|
- delta(lbc.Balance, lrc.Balance), lrc.Nulls.Balance, delta(tbc.Balance, trc.Balance), trc.Nulls.Balance, want),
|
|
|
+ moneyOrNULL(lbc.Balance, lbc.Nulls.Balance), lbc.Nulls.Balance,
|
|
|
+ moneyOrNULL(lrc.Balance, lrc.Nulls.Balance), lrc.Nulls.Balance, delta(lbc.Balance, lrc.Balance),
|
|
|
+ moneyOrNULL(tbc.Balance, tbc.Nulls.Balance), tbc.Nulls.Balance,
|
|
|
+ moneyOrNULL(trc.Balance, trc.Nulls.Balance), trc.Nulls.Balance, delta(tbc.Balance, trc.Balance), want, want),
|
|
|
})
|
|
|
|
|
|
add("W4-B·推荐人 Δrecommended(全局/校区)",
|
|
|
@@ -124,13 +129,16 @@ func recommendCompare(c Case, lg, tg *SideResult) ([]Diff, []Assertion) {
|
|
|
delta(lb.Recommended, lr.Recommended) == 0 && delta(lbc.Recommended, lrc.Recommended) == 0 &&
|
|
|
!tr.Nulls.Recommended && !trc.Nulls.Recommended &&
|
|
|
delta(tb.Recommended, tr.Recommended) == want && delta(tbc.Recommended, trc.Recommended) == want,
|
|
|
- Detail: beforeShape + fmt.Sprintf(" | legacy 全局 %+d NULL=%v、校区 %+d NULL=%v | target 全局 %+d NULL=%v、校区 %+d NULL=%v。"+
|
|
|
+ Detail: beforeShape + fmt.Sprintf(" | 全局:legacy %s(NULL=%v) → %s(NULL=%v)(Δ%+d)| target %s → %s(Δ%+d,期望 %+d);"+
|
|
|
+ "校区:legacy %s(NULL=%v) → %s(NULL=%v)(Δ%+d)| target %s → %s(Δ%+d,期望 %+d)。"+
|
|
|
"⚠ **NULL 不是 0**:它表示「累计推荐金额从未被写成功过」,与「推荐金额确实是 0」在账上完全不同;"+
|
|
|
"按 Q1 口径 target 应为 0.00 的地方这里是 +m —— 这就是缺陷本身",
|
|
|
- delta(lb.Recommended, lr.Recommended), lr.Nulls.Recommended,
|
|
|
- delta(lbc.Recommended, lrc.Recommended), lrc.Nulls.Recommended,
|
|
|
- delta(tb.Recommended, tr.Recommended), tr.Nulls.Recommended,
|
|
|
- delta(tbc.Recommended, trc.Recommended), trc.Nulls.Recommended),
|
|
|
+ moneyOrNULL(lb.Recommended, lb.Nulls.Recommended), lb.Nulls.Recommended,
|
|
|
+ moneyOrNULL(lr.Recommended, lr.Nulls.Recommended), lr.Nulls.Recommended, delta(lb.Recommended, lr.Recommended),
|
|
|
+ centsOf2(tb.Recommended), centsOf2(tr.Recommended), delta(tb.Recommended, tr.Recommended), want,
|
|
|
+ moneyOrNULL(lbc.Recommended, lbc.Nulls.Recommended), lbc.Nulls.Recommended,
|
|
|
+ moneyOrNULL(lrc.Recommended, lrc.Nulls.Recommended), lrc.Nulls.Recommended, delta(lbc.Recommended, lrc.Recommended),
|
|
|
+ centsOf2(tbc.Recommended), centsOf2(trc.Recommended), delta(tbc.Recommended, trc.Recommended), want),
|
|
|
})
|
|
|
}
|
|
|
|
|
|
@@ -282,3 +290,23 @@ func signedStatus(s *SideResult, activityID int64) int32 {
|
|
|
// ① 单测 `TestDeltaOrderAndSelfSubtraction`(传同一个值必须得 0,且必须有别处断言非 0);
|
|
|
// ② 任何基于 Δ 的断言,同时断言 **Before 就是声明的基线**(§18.3 第 10 条)。
|
|
|
func delta(before, after int64) int64 { return after - before }
|
|
|
+
|
|
|
+// centsOf2 把分渲染成两位小数字符串(报告里给人读的"值",不是 Δ)。
|
|
|
+func centsOf2(c int64) string {
|
|
|
+ sign := ""
|
|
|
+ if c < 0 {
|
|
|
+ sign, c = "-", -c
|
|
|
+ }
|
|
|
+ return fmt.Sprintf("%s%d.%02d", sign, c/100, c%100)
|
|
|
+}
|
|
|
+
|
|
|
+// moneyOrNULL 渲染三态金额:isNull ⇒ "NULL"(**不是 0.00**)。
|
|
|
+//
|
|
|
+// 为什么必须区分:这一批里"NULL"与"0.00"是**两件不同的事** ——
|
|
|
+// NULL = 从没写过;0.00 = 写过且是 0。混在一起打印,读的人会把缺陷看成正常。
|
|
|
+func moneyOrNULL(c int64, isNull bool) string {
|
|
|
+ if isNull {
|
|
|
+ return "NULL"
|
|
|
+ }
|
|
|
+ return centsOf2(c)
|
|
|
+}
|