|
@@ -12,7 +12,7 @@
|
|
|
// ③ 新建:2002/3104/1004/成功 → TestCreateActivityValidationAndCrossCampus
|
|
// ③ 新建:2002/3104/1004/成功 → TestCreateActivityValidationAndCrossCampus
|
|
|
// ④ 详情:3001/字段映射/空数组 → TestActivityDetailScopeAndShape
|
|
// ④ 详情:3001/字段映射/空数组 → TestActivityDetailScopeAndShape
|
|
|
// ⑤ 报名列表:2002/空列表 → TestListSignedsInvalidStatusIs2002
|
|
// ⑤ 报名列表:2002/空列表 → TestListSignedsInvalidStatusIs2002
|
|
|
-// ⑥ 更新:3140(numberSign)/3001 → TestUpdateActivityNumberSignIs3140AndCrossScopeIs3001
|
|
|
|
|
|
|
+// ⑥ 更新:2002(numberSign)/3001 → TestUpdateActivityNumberSignIs2002AndCrossScopeIs3001
|
|
|
// ⑦ 报名/取消:3105/跨表三列归零 → TestSignThenCancelCrossTableEffects
|
|
// ⑦ 报名/取消:3105/跨表三列归零 → TestSignThenCancelCrossTableEffects
|
|
|
// ⑧ 取消:3107 审核态零副作用 → TestCancelSignAuditedIs3107WithZeroSideEffects
|
|
// ⑧ 取消:3107 审核态零副作用 → TestCancelSignAuditedIs3107WithZeroSideEffects
|
|
|
// ⑨ 取消:3121 type 读不到 → TestCancelSignUnresolvableTypeIs3121
|
|
// ⑨ 取消:3121 type 读不到 → TestCancelSignUnresolvableTypeIs3121
|
|
@@ -296,6 +296,18 @@ func TestListActivitiesScopesAndDerives(t *testing.T) {
|
|
|
t.Fatalf("signed 应为 2(status<>0),实得 %d——"+
|
|
t.Fatalf("signed 应为 2(status<>0),实得 %d——"+
|
|
|
"若为 3 说明把 status=0(已取消)也统计进来了", first.Signed)
|
|
"若为 3 说明把 status=0(已取消)也统计进来了", first.Signed)
|
|
|
}
|
|
}
|
|
|
|
|
+ // 活动级 money(v1.6 契约补入):来自 `t_courses_activity.money`,两位小数字符串。
|
|
|
|
|
+ // 会变红的条件:① DTO 漏掉 Money 字段 ⇒ 得 "";② 用 float 输出 ⇒ 声明为 string 此处 unmarshal 失败;
|
|
|
|
|
+ // ③ 拿 signeds[].money 顶上 ⇒ 值可能仍是 100.00,故另用活动 102(money=0.00、无报名)区分层级。
|
|
|
|
|
+ if first.Money != "100.00" {
|
|
|
|
|
+ t.Fatalf("活动级 money 应为 \"100.00\"(← t_courses_activity.money),实得 %q", first.Money)
|
|
|
|
|
+ }
|
|
|
|
|
+ if stopped, ok := byID[102]; ok {
|
|
|
|
|
+ if stopped.Money != "0.00" {
|
|
|
|
|
+ t.Fatalf("无报名的停用活动 money 应为 \"0.00\"(活动级、与报名无关),实得 %q——"+
|
|
|
|
|
+ "若非 0.00 说明把 signeds[].money 当成了活动金额", stopped.Money)
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
if !first.Signable {
|
|
if !first.Signable {
|
|
|
t.Fatalf("活动 101 应为 signable(status=1、未满员、无时间窗约束)")
|
|
t.Fatalf("活动 101 应为 signable(status=1、未满员、无时间窗约束)")
|
|
|
}
|
|
}
|
|
@@ -316,8 +328,11 @@ type ActivityViewForTest struct {
|
|
|
Total int32 `json:"total"`
|
|
Total int32 `json:"total"`
|
|
|
NumberSign int32 `json:"numberSign"`
|
|
NumberSign int32 `json:"numberSign"`
|
|
|
Signed int32 `json:"signed"`
|
|
Signed int32 `json:"signed"`
|
|
|
- Signable bool `json:"signable"`
|
|
|
|
|
- Type int8 `json:"type"`
|
|
|
|
|
|
|
+ // Money 声明为 **string**:若后端下发数值(number)会在此处 unmarshal 失败 ⇒
|
|
|
|
|
+ // "金额必须是两位小数字符串"这条不用额外断言也守住了。
|
|
|
|
|
+ Money string `json:"money"`
|
|
|
|
|
+ Signable bool `json:"signable"`
|
|
|
|
|
+ Type int8 `json:"type"`
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// TestListActivitiesInvalidEnumsAre2002 type/status 过滤枚举越界 → **2002**(不是 2001、不是静默忽略)。
|
|
// TestListActivitiesInvalidEnumsAre2002 type/status 过滤枚举越界 → **2002**(不是 2001、不是静默忽略)。
|
|
@@ -505,12 +520,16 @@ func TestListSignedsInvalidStatusIs2002(t *testing.T) {
|
|
|
|
|
|
|
|
// ---------------------------------------------------------------- ⑥ 更新
|
|
// ---------------------------------------------------------------- ⑥ 更新
|
|
|
|
|
|
|
|
-// TestUpdateActivityNumberSignIs3140AndCrossScopeIs3001 更新:`numberSign` 传即 3140;跨机构 3001。
|
|
|
|
|
|
|
+// TestUpdateActivityNumberSignIs2002AndCrossScopeIs3001 更新:`numberSign` 传即 2002;跨机构 3001。
|
|
|
|
|
+//
|
|
|
|
|
+// 为什么是 2002 不是 3140(v1.6 契约更正):设计 §10.1 限定「3140 只收**参数之间**互相矛盾」;
|
|
|
|
|
+// "传了一个不可写字段"属**单一字段取值非法** ⇒ 2002。3140 若被塞这类判断,会退化成垃圾桶。
|
|
|
//
|
|
//
|
|
|
// 会变红的条件:
|
|
// 会变红的条件:
|
|
|
-// - 把 `req.NumberSign` 判断删掉(或用值类型接)⇒ 3140 变 200,且 DB 里 number_sign 被改写 → 红;
|
|
|
|
|
|
|
+// - 把 `req.NumberSign` 判断删掉(或用值类型接)⇒ 2002 变 200,且 DB 里 number_sign 被改写 → 红;
|
|
|
|
|
+// - 把错误码改回 3140 ⇒ 实得 3140 ≠ 2002 → 红(守住"契约 v1.6 的这一处更正");
|
|
|
// - 去掉 `UpdateActivity` 的 `RowsAffected==0` 守卫 ⇒ 更新别家机构活动返回 200 → 红。
|
|
// - 去掉 `UpdateActivity` 的 `RowsAffected==0` 守卫 ⇒ 更新别家机构活动返回 200 → 红。
|
|
|
-func TestUpdateActivityNumberSignIs3140AndCrossScopeIs3001(t *testing.T) {
|
|
|
|
|
|
|
+func TestUpdateActivityNumberSignIs2002AndCrossScopeIs3001(t *testing.T) {
|
|
|
db := newHandlerTestDB(t)
|
|
db := newHandlerTestDB(t)
|
|
|
app := newActivityTestApp(t, db)
|
|
app := newActivityTestApp(t, db)
|
|
|
seedActivityRow(t, db, model.CoursesActivity{
|
|
seedActivityRow(t, db, model.CoursesActivity{
|
|
@@ -524,11 +543,11 @@ func TestUpdateActivityNumberSignIs3140AndCrossScopeIs3001(t *testing.T) {
|
|
|
|
|
|
|
|
body := map[string]any{"type": 2, "title": "改标题", "money": "10.00", "status": 1, "numberSign": 7}
|
|
body := map[string]any{"type": 2, "title": "改标题", "money": "10.00", "status": 1, "numberSign": 7}
|
|
|
code, msg, _ := doJSON(t, app, "PUT", "/admin/activities/401", body)
|
|
code, msg, _ := doJSON(t, app, "PUT", "/admin/activities/401", body)
|
|
|
- if code != 3140 {
|
|
|
|
|
- t.Fatalf("numberSign 传即 3140(契约 §3.4 B 行 25),实得 %d(%s)", code, msg)
|
|
|
|
|
|
|
+ if code != 2002 {
|
|
|
|
|
+ t.Fatalf("numberSign 传即 2002(契约 §3.4 B 行 25,v1.6),实得 %d(%s)", code, msg)
|
|
|
}
|
|
}
|
|
|
if got := readActivityByID(t, db, 401).NumberSign; got != 2 {
|
|
if got := readActivityByID(t, db, 401).NumberSign; got != 2 {
|
|
|
- t.Fatalf("3140 被拒后 number_sign 必须一字不动(2),实得 %d", got)
|
|
|
|
|
|
|
+ t.Fatalf("2002 被拒后 number_sign 必须一字不动(2),实得 %d", got)
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
// 去掉 numberSign 后正常更新
|
|
// 去掉 numberSign 后正常更新
|