口径(用户确认): - 个人分按个人成绩排名给,**不计入单位总分**(下沉到通道级开关,不再动全局系数) - 团体成绩 = 每队成员成绩的**平均值** → 排名(并列顺延)→ 团体模板给分 → 计入单位总分 - 平均值口径是**小项级单选项**:默认「只算有效成绩」;「全部报名人员计入」= 分母为报名条数、 没成绩按 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.8 KiB
Go
51 lines
1.8 KiB
Go
package dto
|
|
|
|
import "git.yeij.top/AskaEth/Scoreboard/server/internal/domain"
|
|
|
|
// Event 是 GET /api/events 与 snapshot.events 的线上结构(列表口径,不含组别与通道)。
|
|
//
|
|
// 与 REST 的 eventDTO 逐字段对齐:scheduledAt/venue/remark 可空、sortOrder 必填,
|
|
// 不能沿用 domain.Event 的 omitempty,否则前端会同时遇到「字段缺失」和「null」。
|
|
type Event struct {
|
|
ID int64 `json:"id"`
|
|
SportID int64 `json:"sportId"`
|
|
Code string `json:"code"`
|
|
Name string `json:"name"`
|
|
EventType string `json:"eventType"`
|
|
ResultMode string `json:"resultMode"`
|
|
ResultType string `json:"resultType"`
|
|
Compare string `json:"compare"`
|
|
RecordEligible bool `json:"recordEligible"`
|
|
Scored bool `json:"scored"`
|
|
Multiplier float64 `json:"multiplier"`
|
|
Status string `json:"status"`
|
|
ScheduledAt *string `json:"scheduledAt"`
|
|
Venue *string `json:"venue"`
|
|
SortOrder int `json:"sortOrder"`
|
|
Remark *string `json:"remark"`
|
|
AvgMode string `json:"avgMode"`
|
|
}
|
|
|
|
// EventFromDomain 把领域小项转换成线上结构。
|
|
func EventFromDomain(e domain.Event) Event {
|
|
return Event{
|
|
ID: e.ID,
|
|
SportID: e.SportID,
|
|
Code: e.Code,
|
|
Name: e.Name,
|
|
EventType: string(e.EventType),
|
|
ResultMode: string(e.ResultMode),
|
|
ResultType: string(e.ResultType),
|
|
Compare: string(e.Compare),
|
|
RecordEligible: e.RecordEligible,
|
|
Scored: e.Scored,
|
|
Multiplier: e.Multiplier,
|
|
Status: string(e.Status),
|
|
ScheduledAt: nilIfEmpty(e.ScheduledAt),
|
|
Venue: nilIfEmpty(e.Venue),
|
|
SortOrder: e.SortOrder,
|
|
Remark: nilIfEmpty(e.Remark),
|
|
AvgMode: string(e.AvgMode),
|
|
}
|
|
}
|