|
|
@@ -1,151 +0,0 @@
|
|
|
-package stage2
|
|
|
-
|
|
|
-// 本文件是 **A21 扇出**(RS-4)的**声明体**。
|
|
|
-//
|
|
|
-// 本轮只落"声明",不落种子 / 求值 / 单测 —— 分步提交,避免再出现"回合结束但零产出"。
|
|
|
-// ⚠ 因此 `rsFanoutCase()` **暂不注册**进 `ResultSetCases()`:它的 `Divergence` 求值还没实现,
|
|
|
-// 注册进去会让 `resultsets` 直接 FATAL(骨架已显式拒绝静默忽略,见 `compareResultSet` 开头)。
|
|
|
-//
|
|
|
-// # 分歧是什么
|
|
|
-//
|
|
|
-// `sp_user_expense_get_bill` 的赠送结余:
|
|
|
-//
|
|
|
-// SELECT SUM(activity_money), SUM(activity_money_used)
|
|
|
-// FROM (SELECT singed.activity_money, usedLog.activity_money_used
|
|
|
-// FROM temp_courses_activity_signed singed
|
|
|
-// RIGHT JOIN (SELECT activityid, SUM(activity_money_used) activity_money_used
|
|
|
-// FROM temp_courses_activity_used_log
|
|
|
-// WHERE create_time < cutoff GROUP BY activityid) usedLog
|
|
|
-// ON singed.activityid = usedLog.activityid) t1;
|
|
|
-//
|
|
|
-// `usedLog` 已按活动聚合成**一行**,再与 signed 逐行 JOIN ⇒ 同一活动有 k 条 signed 时,
|
|
|
-// 这一行被复制 k 份,`SUM(activity_money_used)` 被计入 **k 次**。
|
|
|
-// Go(`giveBalanceBefore`)按 activity 去重,used 只计 **1 次** —— 设计 §3.8-A21 的
|
|
|
-// **DUMP-BUGFIX:刻意不复刻**。
|
|
|
-//
|
|
|
-// # 两条硬约束(下一轮落地时用)
|
|
|
-//
|
|
|
-// 1. **k 必须先被断言再被使用**:机制式 `used×(k−1)` 里的 k 来自声明。若种子静默只落 1 条,
|
|
|
-// 该式退化为 0 ⇒ 两侧相等 ⇒ 判定行红,而归因会误指"缺陷被补回去了"。
|
|
|
-// 2. ⚠ **target 侧当前 schema 无法表达该状态**:`t_courses_activity_signed` 上有唯一键
|
|
|
-// `uk_user_activity(user_id, activity_id)`(实测插入第二条报 1062)。所以"两侧都种 k=2"
|
|
|
-// 在本库**做不到**;落地形态要与 team-lead 确认是"legacy-only 登记 + target 唯一键拒绝断言"
|
|
|
-// 还是别的。本文件的 per-side 期望按"target 能表达"的假设写,仅为把声明先固定下来。
|
|
|
-
|
|
|
-// 本 case 用独立的活动 id(与 RS-1/RS-2 的 9000xx 不重叠,报告里一眼能分开)。
|
|
|
-const rsFanoutActivityID int64 = 900011
|
|
|
-
|
|
|
-// 扇出条数 k。**它是声明**,不是"从库里读出来直接用"——库里读到的值只用于断言它。
|
|
|
-const rsFanoutK = 2
|
|
|
-
|
|
|
-// 种子的单一声明(金额单位:分)。
|
|
|
-const (
|
|
|
- rsFanoutSignedMoneyCents int64 = 10000 // 每条 signed 的 activity_money = 100.00
|
|
|
- rsFanoutUsedPrevCents int64 = 4000 // 期初前发生的 used = 40.00
|
|
|
- rsFanoutUsedEndCents int64 = 6500 // 期末前累计 used = 40.00 + 25.00 = 65.00
|
|
|
-)
|
|
|
-
|
|
|
-// rsFanoutCommonCols 是"仍必须相等"的 6 列(分歧面之外)。
|
|
|
-func rsFanoutCommonCols() []string {
|
|
|
- return []string{
|
|
|
- "previousBalance", "currentTotalIncome", "currentTotalActivityMoney",
|
|
|
- "currentTotalRecommendedAmount", "currentSpend", "currentBalance",
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-// rsFanoutDivergentCols 是"允许且要求不相等"的 3 列。
|
|
|
-func rsFanoutDivergentCols() []string {
|
|
|
- return []string{"previousGiveCost", "currentGiveCost", "currentUsedGiveCost"}
|
|
|
-}
|
|
|
-
|
|
|
-// rsFanoutExpected 由声明算出**两侧各自**应得的值(先算再跑,不跑出来再抄)。
|
|
|
-//
|
|
|
-// legacy:k 条 signed × (activity_money + used) —— used 被计入 k 次
|
|
|
-// target:Σ activity_money(k 条都算)+ used(只算 1 次)
|
|
|
-//
|
|
|
-// 其余 6 列两侧相同:期初 1000、窗口内只有一条 −100、无 type=100/107 流水。
|
|
|
-func rsFanoutExpected() (legacy, target map[string]int64) {
|
|
|
- signedSum := rsFanoutSignedMoneyCents * rsFanoutK
|
|
|
- usedPrev := rsFanoutUsedPrevCents
|
|
|
- usedEnd := rsFanoutUsedEndCents
|
|
|
-
|
|
|
- common := map[string]int64{
|
|
|
- "previousBalance": 100000,
|
|
|
- "currentTotalIncome": 0,
|
|
|
- "currentTotalActivityMoney": 0,
|
|
|
- "currentTotalRecommendedAmount": 0,
|
|
|
- "currentSpend": -10000,
|
|
|
- "currentBalance": 90000,
|
|
|
- }
|
|
|
-
|
|
|
- legacy = map[string]int64{
|
|
|
- "previousBalance": common["previousBalance"],
|
|
|
- "previousGiveCost": signedSum + usedPrev*rsFanoutK,
|
|
|
- "currentTotalIncome": common["currentTotalIncome"],
|
|
|
- "currentTotalActivityMoney": common["currentTotalActivityMoney"],
|
|
|
- "currentTotalRecommendedAmount": common["currentTotalRecommendedAmount"],
|
|
|
- "currentSpend": common["currentSpend"],
|
|
|
- "currentUsedGiveCost": (signedSum + usedPrev*rsFanoutK) - (signedSum + usedEnd*rsFanoutK),
|
|
|
- "currentBalance": common["currentBalance"],
|
|
|
- "currentGiveCost": signedSum + usedEnd*rsFanoutK,
|
|
|
- }
|
|
|
- target = map[string]int64{
|
|
|
- "previousBalance": common["previousBalance"],
|
|
|
- "previousGiveCost": signedSum + usedPrev,
|
|
|
- "currentTotalIncome": common["currentTotalIncome"],
|
|
|
- "currentTotalActivityMoney": common["currentTotalActivityMoney"],
|
|
|
- "currentTotalRecommendedAmount": common["currentTotalRecommendedAmount"],
|
|
|
- "currentSpend": common["currentSpend"],
|
|
|
- "currentUsedGiveCost": (signedSum + usedPrev) - (signedSum + usedEnd),
|
|
|
- "currentBalance": common["currentBalance"],
|
|
|
- "currentGiveCost": signedSum + usedEnd,
|
|
|
- }
|
|
|
- return legacy, target
|
|
|
-}
|
|
|
-
|
|
|
-// rsFanoutExpectedNorm 把一侧的期望渲染成 2 位小数字符串(比对口径)。
|
|
|
-func rsFanoutExpectedNorm(side map[string]int64) map[string]string {
|
|
|
- out := make(map[string]string, len(side))
|
|
|
- for k, v := range side {
|
|
|
- out[k] = yuan(v)
|
|
|
- }
|
|
|
- return out
|
|
|
-}
|
|
|
-
|
|
|
-// rsFanoutCase 是 RS-4 的声明体。
|
|
|
-//
|
|
|
-// ⚠ **暂不注册**(见文件头):`Divergence` 求值未实现,注册即 FATAL。
|
|
|
-func rsFanoutCase() ResultSetCase {
|
|
|
- lgExp, tgExp := rsFanoutExpected()
|
|
|
- return ResultSetCase{
|
|
|
- Name: "RS-4 账单·A21 扇出(同一活动 k 条报名 ⇒ used 重复计入)INTENTIONAL-STRICT",
|
|
|
- Note: "两侧**本来就不该相等**:legacy 把 used 计入 k 次(dump 缺陷),target 去重只计 1 次(刻意不复刻)。" +
|
|
|
- "绿的含义是「分歧已登记且双方符合各自声明」;两侧一旦相等就红(缺陷被补回 / target 被改坏)," +
|
|
|
- "分歧扩散到声明外的列也红",
|
|
|
- LegacyUserID: rsUserID,
|
|
|
- InstitutionID: rsInstID,
|
|
|
- CampusID: rsCampusID,
|
|
|
- // LegacyCall / Seed* / CallTarget / Preconditions / ExtraAssertions 下一轮补。
|
|
|
- Compare: func(lg ResultSet, tg TargetPayload) ResultSetInput {
|
|
|
- common := make(map[string]string, len(rsFanoutCommonCols()))
|
|
|
- for _, col := range rsFanoutCommonCols() {
|
|
|
- common[col] = yuan(lgExp[col])
|
|
|
- }
|
|
|
- return ResultSetInput{
|
|
|
- CaseName: "RS-4",
|
|
|
- Legacy: lg,
|
|
|
- ExpectRows: 1,
|
|
|
- Target: tg,
|
|
|
- Columns: rsBillColumns(), // 照抄 RS-2 的 9 列映射
|
|
|
- Expect: common, // 其余 6 列按相等处理
|
|
|
- Divergence: &Divergence{
|
|
|
- Reason: "A21 扇出:legacy 对同一活动的 k 条 signed 把 used 额重复计入 k 次;" +
|
|
|
- "target 按 activity 去重只计 1 次(dump 缺陷,刻意不复刻)",
|
|
|
- Columns: rsFanoutDivergentCols(),
|
|
|
- Legacy: rsFanoutExpectedNorm(lgExp),
|
|
|
- Target: rsFanoutExpectedNorm(tgExp),
|
|
|
- },
|
|
|
- }
|
|
|
- },
|
|
|
- }
|
|
|
-}
|