口径(用户确认): - 个人分按个人成绩排名给,**不计入单位总分**(下沉到通道级开关,不再动全局系数) - 团体成绩 = 每队成员成绩的**平均值** → 排名(并列顺延)→ 团体模板给分 → 计入单位总分 - 平均值口径是**小项级单选项**:默认「只算有效成绩」;「全部报名人员计入」= 分母为报名条数、 没成绩按 0 加入分子;在**成绩录入页**就地切换并立即保存 服务端: - 迁移 0017:score_ledger 加 count_to_team(重建,无子表);event_score_channels 加 count_to_team; events 加 avg_mode(默认 valid) - 规则包 Channel 加 countToTeam(缺省 true);channelTemplates 支持 individual_team → 默认 athlete(result, 不计入单位) + team(aggregate 队内平均) - 计分引擎:SumByTeam 跳过 count_to_team=0 的流水;实现 basis=aggregate 的队内平均团体排名 (valid 分母=有有效成绩人数;all 分母=报名人数;某队 0 有效成绩不参与) 前端: - 计分通道页:每行通道「计入单位总分」勾选 + basis 下拉「按队内平均」 - 成绩录入页:仅有队内平均通道的小项显示「平均值口径」单选(默认只算有效成绩),改动即存 - 录入页「实时积分预览」按新口径对齐(原先按主体求和且双通道重复计入,与发布后不一致) - 大屏/管理端团体成绩只显示数值,不出现「平均」字样 自检:服务端 12 包全绿;前端 vue-tsc + build、node --test 134/134;console 16 文件通过。
51 lines
1.5 KiB
Go
51 lines
1.5 KiB
Go
package domain
|
|
|
|
import "testing"
|
|
|
|
func TestSumByTeam(t *testing.T) {
|
|
ledger := []LedgerEntry{
|
|
ledgerFor(1, 10),
|
|
ledgerForAthlete(1, 101, 10),
|
|
ledgerFor(2, 6),
|
|
}
|
|
got := SumByTeam(ledger, 1)
|
|
if !nearlyEqual(got[1].Total, 20) || !nearlyEqual(got[1].TeamOnly, 10) || !nearlyEqual(got[1].Individual, 10) {
|
|
t.Fatalf("单位1汇总 = %+v,期望 总分20 / 单位10 / 个人10", got[1])
|
|
}
|
|
if !nearlyEqual(got[2].Total, 6) {
|
|
t.Fatalf("单位2汇总 = %+v,期望 6", got[2])
|
|
}
|
|
|
|
half := SumByTeam(ledger, 0.5)
|
|
if !nearlyEqual(half[1].Total, 15) {
|
|
t.Fatalf("系数 0.5 时单位1总分 = %v,期望 15", half[1].Total)
|
|
}
|
|
}
|
|
|
|
func TestSumByAthlete(t *testing.T) {
|
|
ledger := []LedgerEntry{
|
|
ledgerForAthlete(1, 101, 10),
|
|
ledgerForAthlete(1, 101, 2),
|
|
ledgerFor(1, 100), // 仅记单位,不进个人榜
|
|
}
|
|
got := SumByAthlete(ledger)
|
|
if len(got) != 1 || !nearlyEqual(got[101], 12) {
|
|
t.Fatalf("个人汇总 = %+v,期望只有 101:12", got)
|
|
}
|
|
}
|
|
|
|
// 「个人+团体」项目的个人通道:分只记个人,不进单位总分。
|
|
func TestSumByTeamSkipsPersonalOnly(t *testing.T) {
|
|
ledger := []LedgerEntry{
|
|
ledgerFor(1, 10),
|
|
ledgerForPersonal(1, 101, 8),
|
|
}
|
|
got := SumByTeam(ledger, 1)
|
|
if !nearlyEqual(got[1].Total, 10) || !nearlyEqual(got[1].Individual, 0) {
|
|
t.Fatalf("单位1汇总 = %+v,期望总分 10 且个人分不进单位", got[1])
|
|
}
|
|
if !nearlyEqual(SumByAthlete(ledger)[101], 8) {
|
|
t.Fatalf("个人榜应保留该流水:%v", SumByAthlete(ledger))
|
|
}
|
|
}
|