修复(#4): 统一 API 响应格式 — ResponseErrorBean 补 data 字段

Closes part of HIF-4 (Stage 2 前端集成反馈 F9 - 全站响应格式)

F9 (P1): 全站响应统一 {code, msg, data} 三字段固定 shape
- ResponseSuccessBean.Data 去掉 omitempty → 空 payload 也返 "data":null
- ResponseErrorBean 加 Data interface{} 字段 → 错误响应也带 "data":null
- App 端强类型 decoder 依赖固定字段 shape,缺 data 键会解码失败

回归护栏 3 用例:
- TestResponseErrorBean_HasDataField: 错误响应必须含 "data":null
- TestResponseSuccessBean_DataAlwaysPresent: Success(nil) 也含 "data":null(防 omitempty 回退)
- TestResponseSuccessBean_WithPayload: 正常 payload 序列化正确

影响面:全站所有响应(不止 lottery),JSON 加字段/字段值变 null 对现有 client 无 breaking(宽松 decoder 全兼容)
CI 全绿;单 file 改动 revert 一步搞定
This commit is contained in:
2026-07-12 19:55:35 -07:00
committed by GitHub
parent cce147108c
commit eac0137069
2 changed files with 73 additions and 4 deletions
+54
View File
@@ -0,0 +1,54 @@
// responseBean_test.go — HIF-4 F9 护栏:
// 所有 HTTP 响应必须是 {code, msg, data} 三字段固定 shapedata 为空时也要
// 显式 "null"(不是丢字段)。App 端强类型 decoder 依赖这个 shape。
package result
import (
"encoding/json"
"strings"
"testing"
)
// TestResponseErrorBean_HasDataField 断言错误响应的 JSON 一定包含 data 字段
// 且为 null。如果未来有人给 Data 加了 omitempty,或者删掉 Data 字段,这里会挂。
func TestResponseErrorBean_HasDataField(t *testing.T) {
body, err := json.Marshal(Error(40003, "User token is invalid"))
if err != nil {
t.Fatalf("marshal: %v", err)
}
s := string(body)
if !strings.Contains(s, `"data":null`) {
t.Fatalf("F9 regression: error response missing 'data:null', got %s", s)
}
// 顺带断言 code + msg 也在(防止未来无脑重构删字段)
if !strings.Contains(s, `"code":40003`) || !strings.Contains(s, `"msg":"User token is invalid"`) {
t.Fatalf("error response missing code/msg, got %s", s)
}
}
// TestResponseSuccessBean_DataAlwaysPresent 断言成功响应在 data 为 nil 时
// 也显式返回 "data":null(不是丢字段)。这是 F9 的另一半——如果有人未来把
// omitempty 加回去,只解决错误响应的对称性会被打破。
func TestResponseSuccessBean_DataAlwaysPresent(t *testing.T) {
body, err := json.Marshal(Success(nil))
if err != nil {
t.Fatalf("marshal: %v", err)
}
s := string(body)
if !strings.Contains(s, `"data":null`) {
t.Fatalf("F9 regression: Success(nil) missing 'data:null' (omitempty regression?), got %s", s)
}
}
// TestResponseSuccessBean_WithPayload 副断言:非 nil payload 时
// data 字段确实带上,且序列化正常。
func TestResponseSuccessBean_WithPayload(t *testing.T) {
body, err := json.Marshal(Success(map[string]any{"id": 42, "name": "abc"}))
if err != nil {
t.Fatalf("marshal: %v", err)
}
s := string(body)
if !strings.Contains(s, `"data":{`) || !strings.Contains(s, `"id":42`) {
t.Fatalf("success payload not serialized correctly, got %s", s)
}
}