- 服务端补下发:dto.EventResultRow 与 athleteStandingDTO 各加 logoPath, 分别取「该行单位」与「该运动员所属单位」的 teams.logo_path;空串统一收敛为 null - 大屏抽出 TeamLogo.vue(1em / object-fit:contain / 加载失败保留占位、@load 复原), 原先在积分榜与团体奖牌榜各写一遍的逻辑收敛成一份,四处复用 - 分项成绩页:单位列名字前显示 logo;个人积分榜:运动员姓名前显示其单位 logo - guards/mock 同步解析与补字段;补 11 条测试(类型解析 + SFC 模板断言) 顺带修:管理端示例数据 athleteStandings 缺新必填字段导致的 vue-tsc 报错。 自检:服务端 11 包全绿;前端 vue-tsc + build、node --test 80/80。
90 lines
3.4 KiB
Go
90 lines
3.4 KiB
Go
package httpapi_test
|
|
|
|
import (
|
|
"net/http"
|
|
"testing"
|
|
)
|
|
|
|
// 个人榜的单位展示字段必须取全量单位表:停用但有积分的单位也要显示名称与简称。
|
|
func TestAthleteStandingsUsesFullTeamTable(t *testing.T) {
|
|
env := newTestEnv(t)
|
|
ids := env.seed(t)
|
|
env.createUser("recorder", "recorder-pass", "recorder")
|
|
client, _ := env.client()
|
|
env.login(client, "recorder", "recorder-pass")
|
|
|
|
env.do(client, http.MethodPut, "/api/results/draft", map[string]any{
|
|
"eventId": ids.event,
|
|
"results": []map[string]any{
|
|
{"entryId": ids.entryA, "resultValue": 11.58, "unit": "s", "status": "ok"},
|
|
{"entryId": ids.entryB, "resultValue": 11.92, "unit": "s", "status": "ok"},
|
|
},
|
|
}, true)
|
|
if status, env2 := env.do(client, http.MethodPost, "/api/results/publish", map[string]any{
|
|
"eventId": ids.event,
|
|
}, true); status != http.StatusOK {
|
|
t.Fatalf("发布应 200,得到 %d %+v", status, env2.Error)
|
|
}
|
|
env.exec(t, `UPDATE teams SET active = 0 WHERE id = ?`, ids.teamB)
|
|
|
|
_, env2 := env.do(client, http.MethodGet, "/api/standings/athletes", nil, false)
|
|
rows := decodeData[[]map[string]any](t, env2)
|
|
seen := map[int64]map[string]any{}
|
|
for _, row := range rows {
|
|
seen[int64(row["athleteId"].(float64))] = row
|
|
}
|
|
|
|
active := seen[ids.athleteA]
|
|
if active == nil || active["teamName"] != "第一分公司" || active["shortName"] != "一司" {
|
|
t.Fatalf("在职单位的个人行应带单位名与简称: %+v", active)
|
|
}
|
|
stopped := seen[ids.athB]
|
|
if stopped == nil {
|
|
t.Fatalf("停用单位的运动员仍应在个人榜上: %+v", rows)
|
|
}
|
|
if stopped["teamName"] != "第二分公司" || stopped["shortName"] != "二司" {
|
|
t.Fatalf("停用单位的个人行应补齐单位名与简称,得到 %+v", stopped)
|
|
}
|
|
}
|
|
|
|
// 个人榜的 logoPath 取「运动员所属单位」的 logo:姓名前显示的是单位标识,不是运动员头像。
|
|
func TestAthleteStandingsIncludesTeamLogo(t *testing.T) {
|
|
env := newTestEnv(t)
|
|
ids := env.seed(t)
|
|
env.createUser("recorder", "recorder-pass", "recorder")
|
|
client, _ := env.client()
|
|
env.login(client, "recorder", "recorder-pass")
|
|
|
|
env.do(client, http.MethodPut, "/api/results/draft", map[string]any{
|
|
"eventId": ids.event,
|
|
"results": []map[string]any{
|
|
{"entryId": ids.entryA, "resultValue": 11.58, "unit": "s", "status": "ok"},
|
|
{"entryId": ids.entryB, "resultValue": 11.92, "unit": "s", "status": "ok"},
|
|
},
|
|
}, true)
|
|
if status, env2 := env.do(client, http.MethodPost, "/api/results/publish", map[string]any{
|
|
"eventId": ids.event,
|
|
}, true); status != http.StatusOK {
|
|
t.Fatalf("发布应 200,得到 %d %+v", status, env2.Error)
|
|
}
|
|
env.exec(t, `UPDATE teams SET logo_path = ? WHERE id = ?`, "/uploads/teams/a.png", ids.teamA)
|
|
// 历史数据里 logo 可能是空串,同样按「无 logo」下发 null。
|
|
env.exec(t, `UPDATE teams SET logo_path = '' WHERE id = ?`, ids.teamB)
|
|
|
|
_, env2 := env.do(client, http.MethodGet, "/api/standings/athletes", nil, false)
|
|
rows := decodeData[[]map[string]any](t, env2)
|
|
seen := map[int64]map[string]any{}
|
|
for _, row := range rows {
|
|
seen[int64(row["athleteId"].(float64))] = row
|
|
}
|
|
|
|
withLogo := seen[ids.athleteA]
|
|
if withLogo == nil || withLogo["logoPath"] != "/uploads/teams/a.png" {
|
|
t.Fatalf("有 logo 的单位,个人行应带该单位的 logo: %+v", withLogo)
|
|
}
|
|
noLogo, ok := seen[ids.athB]["logoPath"]
|
|
if !ok || noLogo != nil {
|
|
t.Fatalf("无 logo 的单位,个人行 logoPath 应为 null,得到 %+v", seen[ids.athB])
|
|
}
|
|
}
|