Przeglądaj źródła

docs(ac15): §22 补 F31 的最小复现证据(不写修法)

- "真分歧"改为指向 F31(P1 / TARGET-DEFECT,影响 parseTimeParam 24 处调用),
  删掉上一版自行推测的三种处置方向 —— 修法已在台账定稿,本文只留证据
- 加「最小复现」:4 行 INSERT 种子 + 2 条 legacy CALL(60.000 / 110.000)
  + target 端点等价调用两行(handler 裸日期 90/0/20/20 vs 显式 RFC3339 -10/100/120/120)
- 明确标注:target 两行是**按 handler 解析口径直调 service** 的实测(同源,但不含鉴权/序列化层),
  避免读者以为走了真实 HTTP
- 保留并加粗那句:**RS-5 覆盖 service 语义,不能拿 RS-5 的绿证明端点 17 没问题**

门禁(全仓):go build ./... | go vet ./internal/academic/... |
go test ./internal/academic/dualrun/stage2/ -count=1 | resultsets 5 PASS / 0 FAIL。
Q 5 godzin temu
rodzic
commit
364cff5245
1 zmienionych plików z 34 dodań i 6 usunięć
  1. 34 6
      internal/academic/dualrun/stage2/README.md

+ 34 - 6
internal/academic/dualrun/stage2/README.md

@@ -1799,7 +1799,7 @@ target  MIN(start_time) 折平 ⇒ 60 + 60 = 120.00,两行都核完,不报
 | 本地 `2026-03-31 00:00:00` | −10.00 | 60.00 | 70.00 | 120.00 |
 | 本地 `2026-03-31 23:59:59` | −10.00 | 110.00 | 120.00 | 120.00 |
 
-### 由此发现的**真分歧**(未修,需定责)
+### 由此发现的**真分歧**(已登记 F31 / P1;修法见台账,本文只给证据)
 
 `handler.parseTimeParam` 用 `time.Parse("2006-01-02", …)` ⇒ **UTC 当日 00:00**,
 而 DB 的 `create_time` 以 `loc=Local` 读出(本机 +08:00)⇒ 同一个裸日期,target 的边界比本地口径**晚 8 小时**。
@@ -1814,8 +1814,36 @@ legacy 是字符串比较(无时区),等价于本地零点。实测一个
 差异机制:`from` 被解析成 03-01 **08:00 本地** ⇒ B(03-01 00:00)被挤进 `PrevBalance`、不计当期充值;
 `to` 被解析成 04-01 08:00 本地 ⇒ 多算 04-01 凌晨的流水。**不报错,只是数不同。**
 
-这一条**没有做成 case**(本轮边界:只测量 + 文档):它落在 handler 层(`parseTimeParam`),
-不是 SP/service 语义;要修得先定口径——三种可能:
-① handler 把裸日期解释为本地零点;② 契约只允许 RFC3339;③ service 侧统一归一。
-责任与处置需另定(候选缺陷,未入本文件结论)。RS-5 用的是**直接调 service + 本地 `to`**,
-所以 RS-5 覆盖的是 service 语义,**不覆盖 handler 的 UTC 解析**。
+这一条**没有做成 case**:它落在 handler 层(`parseTimeParam`),不是 SP/service 语义。
+已登记 **F31**(P1 / `TARGET-DEFECT`,影响 `parseTimeParam` 的 24 处调用),修法三步在台账里定稿,
+排期由台账维护者定 —— 本文不写修复方案,只留证据。
+
+⚠ **RS-5 覆盖的是 service 语义,不能拿 RS-5 的绿证明端点 17 没问题**:
+RS-5 直接调 `BillService.GetBill` 并传**本地** `to`;端点走的是 handler → `time.Parse`(UTC)。
+两者的边界不在同一时刻。
+
+#### 最小复现(4 笔种子 + 两个调用;只读)
+
+```sql
+-- ① 机制:临时表的 create_time 是 VARCHAR,不是 datetime
+--    go run ./cmd/spdump proc sp_user_expense_get_bill | grep -A6 'temp_user_expense_log'
+INSERT INTO t_user_expense_log (userid,type,money,create_time) VALUES
+  (880001,200,'-10.00','2026-02-28 12:00:00'),
+  (880001,100,'100.00','2026-03-01 00:00:00'),
+  (880001,101,'-30.00','2026-03-15 12:00:00'),
+  (880001,200, '50.00','2026-03-31 00:00:00');   -- D:恰在 to
+-- ② legacy:同一笔 D,两种 to 写法
+CALL sp_user_expense_get_bill(880001,'','2026-03-01','2026-03-31');            -- currentBalance=60.000
+CALL sp_user_expense_get_bill(880001,'','2026-03-01','2026-03-31 23:59:59');  -- currentBalance=110.000
+```
+
+```
+# ③ target:端点等价调用(handler 的裸日期解析 = UTC 零点;直调 service 实测,未走 HTTP)
+GET /student/bill?from=2026-03-01&to=2026-04-01
+  → prevBalance=90.00  recharge=0.00  flowExclusiveEnd=20.00  flowInclusiveEnd=20.00  total=2
+GET /student/bill?from=2026-03-01T00:00:00%2B08:00&to=2026-04-01T00:00:00%2B08:00
+  → prevBalance=-10.00 recharge=100.00 flowExclusiveEnd=120.00 flowInclusiveEnd=120.00 total=3
+```
+
+(数字来自 dev 实测;种子是合成用户 880001,跑完回滚。legacy 的两行 CALL 需在写入种子的事务内执行;
+target 两行是按 handler 的解析口径直调 service 得到的 —— 与端点返回同源,但不含鉴权/序列化层。)