fix: 审查修复波次(P0x4 + P1x30+)

- domain/rules: 计分与排名解耦、破纪录按组别、tieBreak 生效、调整分不丢、移除 aggregate 假能力
- realtime: 新建 internal/dto,快照三项契约对齐,/ws 只认会话身份
- httpapi: 流水幂等、standings 整表载荷、广播接线、强制改密、XFF 防伪造、records 写入、审计、赛事包内容、发布前备份、result.points、实例列表
- web: FormData 上传、YAML 解析、快照归一化、大屏解析层、bootstrap 降级、访客判定、主题切换
- console: 优雅关闭握手、中文化、免验证改管理员账号、选屏与日志轮转修复
- docs: 用户文档三份 + README
- 主代理: 破纪录加分接线(levels 未加载导致恒 0)
This commit is contained in:
AskaEth committed 2026-09-09 21:06:40 +08:00
1 parent 8dae63ee70
commit 7f9701c465
61 files changed
+3844 -333

No files matched your search

+47
View File
@@ -181,6 +181,10 @@ func TestParseRejectsBadInput(t *testing.T) {
{"通道引用不存在的模板", strings.Replace(validPackYAML, "profile: individual_top6 }", "profile: ghost_top6 }", 1), ErrValidation},
{"排序键未知", strings.Replace(validPackYAML, "tieBreak: [points, gold, silver, bronze, teamCode]", "tieBreak: [points, platinum]", 1), ErrValidation},
{"启用破纪录却没有档位", strings.Replace(validPackYAML, "recordBonus:\n enabled: false\n mode: max\n levels:\n - { code: meet, name: 赛会纪录, points: 2 }", "recordBonus:\n enabled: true\n mode: max\n levels: []", 1), ErrValidation},
// aggregate 的汇总口径(avg/max/min/count)没有落库列,已从类型里移除;写了必须报错而不是静默丢弃。
{"通道 aggregate 口径已移除", strings.Replace(validPackYAML, "{ scope: team, basis: manual, profile: team_top6 }", "{ scope: team, basis: aggregate, profile: team_top6, aggregate: avg }", 1), nil},
// 联队归属未实现,规则包里不允许出现相关配置。
{"联队归属未实现", strings.Replace(validPackYAML, "standings:\n", "standings:\n unionAttribution: split\n", 1), nil},
}
for _, tc := range cases {
@@ -283,6 +287,9 @@ func TestProfileDomainConversion(t *testing.T) {
if in.IndividualToTeamWeight != 1 || in.ExcludeTeamEventMedals {
t.Errorf("榜单口径转换错误: %+v", in)
}
if !reflect.DeepEqual(in.TieBreak, pack.Standings.TieBreak) {
t.Errorf("tie-break 未传入聚合输入: %+v", in.TieBreak)
}
rb := pack.RecordBonus.ToDomain()
if rb.Enabled || rb.Mode != domain.RecordBonusMax || len(rb.Levels) != 1 {
@@ -292,3 +299,43 @@ func TestProfileDomainConversion(t *testing.T) {
t.Errorf("破纪录配置往返不一致: %+v", got)
}
}
// TestRecordBonusFromSettings 验证上层能从 settings 键值对读到破纪录档位:
// 只读 enabled/mode 而不读 levels,会让破纪录加分永远算 0。
func TestRecordBonusFromSettings(t *testing.T) {
settings := map[string]string{
settingRecordBonusEnabled: "true",
settingRecordBonusMode: "sum",
settingRecordBonusLevels: `[{"code":"meet","name":"赛会纪录","points":2},{"code":"city","name":"市级纪录","points":5}]`,
}
cfg := RecordBonusFromSettings(settings)
if !cfg.Enabled || cfg.Mode != domain.RecordBonusSum {
t.Fatalf("开关/口径错误: %+v", cfg)
}
if len(cfg.Levels) != 2 {
t.Fatalf("档位数 = %d,期望 2(levels 必须被解析)", len(cfg.Levels))
}
if cfg.Levels[1].Code != "city" || cfg.Levels[1].Points != 5 {
t.Fatalf("档位内容错误: %+v", cfg.Levels[1])
}
t.Run("缺省值", func(t *testing.T) {
cfg := RecordBonusFromSettings(map[string]string{})
if cfg.Enabled || cfg.Mode != domain.RecordBonusMax || len(cfg.Levels) != 0 {
t.Fatalf("缺省配置错误: %+v", cfg)
}
})
}
// TestRecordBonusSettingsRoundTrip 验证规则包写入 settings 后还能原样读回档位。
func TestRecordBonusSettingsRoundTrip(t *testing.T) {
pack, err := Parse([]byte(validPackYAML))
if err != nil {
t.Fatalf("Parse: %v", err)
}
got := RecordBonusFromSettings(settingsFromPack(pack))
want := pack.RecordBonus.ToDomain()
if !reflect.DeepEqual(got, want) {
t.Fatalf("settings 往返后破纪录配置不一致:\n%+v\n%+v", got, want)
}
}