package domain import "testing" func bonusConfig(mode RecordBonusMode) RecordBonusConfig { return RecordBonusConfig{ Enabled: true, Mode: mode, Levels: []RecordLevel{ {Code: "meet", Name: "赛会纪录", Points: 2}, {Code: "city", Name: "市级纪录", Points: 5}, {Code: "province", Name: "省级纪录", Points: 8}, }, } } func TestRecordBonusDisabled(t *testing.T) { cfg := bonusConfig(RecordBonusMax) cfg.Enabled = false got := RecordBonusPoints(cfg, []RecordBreak{{EntryID: 1, Level: "meet", State: RecordBreakApproved}}) if got != nil { t.Fatalf("未启用时 = %+v,期望 nil", got) } } func TestRecordBonusStateFilter(t *testing.T) { cases := []struct { name string state RecordBreakState want float64 }{ {"已通过计入", RecordBreakApproved, 2}, {"待审核不计入", RecordBreakPending, 0}, {"已驳回不计入", RecordBreakRejected, 0}, {"状态空值不计入", "", 0}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got := RecordBonusPoints(bonusConfig(RecordBonusMax), []RecordBreak{{EntryID: 1, Level: "meet", State: tc.state}}) if tc.want == 0 { if len(got) != 0 { t.Fatalf("结果 = %+v,期望空", got) } return } if len(got) != 1 || !nearlyEqual(got[0].Points, tc.want) { t.Fatalf("结果 = %+v,期望 %v 分", got, tc.want) } }) } } func TestRecordBonusUnknownLevel(t *testing.T) { got := RecordBonusPoints(bonusConfig(RecordBonusMax), []RecordBreak{{EntryID: 1, Level: "galaxy", State: RecordBreakApproved}}) if len(got) != 0 { t.Fatalf("未配置档位 = %+v,期望空(不能凭空加分)", got) } } func TestRecordBonusModes(t *testing.T) { breaks := []RecordBreak{ {ID: 1, EntryID: 1, Level: "meet", State: RecordBreakApproved}, {ID: 2, EntryID: 1, Level: "city", State: RecordBreakApproved}, {ID: 3, EntryID: 1, Level: "province", State: RecordBreakApproved}, } cases := []struct { name string mode RecordBonusMode want float64 }{ {"max 取最高档", RecordBonusMax, 8}, {"sum 累加", RecordBonusSum, 15}, {"模式空值按 max", "", 8}, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { got := RecordBonusPoints(bonusConfig(tc.mode), breaks) if len(got) != 1 || !nearlyEqual(got[0].Points, tc.want) { t.Fatalf("结果 = %+v,期望 %v", got, tc.want) } }) } } func TestRecordBonusGroupsByEntry(t *testing.T) { breaks := []RecordBreak{ {ID: 1, EntryID: 2, Level: "meet", State: RecordBreakApproved}, {ID: 2, EntryID: 1, Level: "city", State: RecordBreakApproved}, {ID: 3, EntryID: 1, Level: "meet", State: RecordBreakApproved}, } got := RecordBonusPoints(bonusConfig(RecordBonusMax), breaks) if len(got) != 2 { t.Fatalf("条数 = %d,期望 2(按报名项分组)", len(got)) } if got[0].EntryID != 1 || got[1].EntryID != 2 { t.Fatalf("顺序 = %+v,期望按 EntryID 升序", got) } if !nearlyEqual(got[0].Points, 5) || !nearlyEqual(got[1].Points, 2) { t.Fatalf("分值 = %v/%v,期望 5/2", got[0].Points, got[1].Points) } } func TestRecordBonusDedupLevel(t *testing.T) { breaks := []RecordBreak{ {ID: 1, EntryID: 1, Level: "meet", State: RecordBreakApproved}, {ID: 2, EntryID: 1, Level: "meet", State: RecordBreakApproved}, } got := RecordBonusPoints(bonusConfig(RecordBonusSum), breaks) if len(got) != 1 || !nearlyEqual(got[0].Points, 2) { t.Fatalf("重复档位 = %+v,期望只计一次 2 分", got) } if len(got[0].Levels) != 1 { t.Fatalf("档位列表 = %v,期望去重", got[0].Levels) } } func TestRecordBonusNoBreaks(t *testing.T) { if got := RecordBonusPoints(bonusConfig(RecordBonusMax), nil); got != nil { t.Fatalf("无破纪录登记 = %+v,期望 nil", got) } } func TestRecordBonusLedger(t *testing.T) { entries, results := fixture(lineup(2)) base := EventScoreInput{ Event: individualEvent(1), Category: testCategory(), Channels: []ScoreChannel{athleteResultChannel(1)}, Profiles: map[int64]ScoringProfile{1: individualTop6()}, Entries: entries, Results: results, RecordBreaks: []RecordBreak{{ ID: 1, EventID: 1, EntryID: 1, AthleteID: 101, TeamID: 1001, Level: "meet", State: RecordBreakApproved, }}, } t.Run("未启用不产生加分流水", func(t *testing.T) { cfg := bonusConfig(RecordBonusMax) cfg.Enabled = false base.RecordBonus = &cfg es := mustScore(t, base) for _, e := range es.Ledger { if e.SourceType == SourceRecordBonus { t.Fatalf("不应有加分流水: %+v", e) } } }) t.Run("启用后归属到人", func(t *testing.T) { cfg := bonusConfig(RecordBonusMax) base.RecordBonus = &cfg es := mustScore(t, base) var found bool for _, e := range es.Ledger { if e.SourceType != SourceRecordBonus { continue } found = true if e.TeamID != 1001 || e.AthleteID != 101 || !nearlyEqual(e.Points, 2) { t.Fatalf("加分流水 = %+v,期望 1001/101 记 2 分", e) } } if !found { t.Fatal("未产生加分流水") } }) t.Run("联队按份额拆分加分", func(t *testing.T) { ps := []participant{{ entryID: 1, athleteID: 101, teamID: 1001, value: 1, shares: []TeamShare{{TeamID: 1001, Weight: 3}, {TeamID: 1002, Weight: 2}}, }} e2, r2 := fixture(ps) cfg := bonusConfig(RecordBonusMax) es := mustScore(t, EventScoreInput{ Event: individualEvent(1), Category: testCategory(), Channels: []ScoreChannel{athleteResultChannel(1)}, Profiles: map[int64]ScoringProfile{1: individualTop6()}, Entries: e2, Results: r2, RecordBonus: &cfg, RecordBreaks: []RecordBreak{{ ID: 1, EventID: 1, EntryID: 1, AthleteID: 101, TeamID: 1001, Level: "meet", State: RecordBreakApproved, }}, Options: ScoreOptions{UnionAttribution: UnionSplit}, }) var bonus float64 for _, e := range es.Ledger { if e.SourceType == SourceRecordBonus && e.TeamID == 1002 { bonus += e.Points } } if !nearlyEqual(bonus, 0.8) { t.Fatalf("单位1002 的加分 = %v,期望 2 × 0.4 = 0.8", bonus) } }) } // TestRecordBonusLedgerFiltersByCategory 验证按「小项+组别」过滤: // 上层逐组别调用 ScoreEvent,别的组别的破纪录登记不能在本组别重复记一次加分。 func TestRecordBonusLedgerFiltersByCategory(t *testing.T) { entries, results := fixture(lineup(2)) entries[1].CategoryID = 2 // entry2 属于另一个组别 cfg := bonusConfig(RecordBonusMax) es := mustScore(t, EventScoreInput{ Event: individualEvent(1), Category: testCategory(), Channels: []ScoreChannel{athleteResultChannel(1)}, Profiles: map[int64]ScoringProfile{1: individualTop6()}, Entries: entries, Results: results, RecordBonus: &cfg, RecordBreaks: []RecordBreak{ {ID: 1, EventID: 1, EntryID: 1, AthleteID: 101, TeamID: 1001, Level: "meet", State: RecordBreakApproved}, {ID: 2, EventID: 1, EntryID: 2, AthleteID: 102, TeamID: 1002, Level: "meet", State: RecordBreakApproved}, }, }) var thisCategory, otherCategory int for _, e := range es.Ledger { if e.SourceType != SourceRecordBonus { continue } switch e.EntryID { case 1: thisCategory++ case 2: otherCategory++ } } if thisCategory != 1 { t.Fatalf("本组别加分流水 = %d 条,期望 1", thisCategory) } if otherCategory != 0 { t.Fatalf("别的组别的加分不应记入本组别,实际 %d 条", otherCategory) } }