feat: 排行榜改为单表(延迟/下载/上传同列),点击列头动态排序,测速后自动高亮我的成绩(含真实名次)
This commit is contained in:
@@ -234,6 +234,8 @@ func (h *Handler) Result(c *gin.Context) {
|
||||
// ClientIP 打码展示;ServerIP 为来源 IP(内网 IP 完整返回,供站长调试区分设备)
|
||||
type RankItem struct {
|
||||
ID uint `json:"id"`
|
||||
Rank int `json:"rank"` // 名次(含附加记录的真实名次)
|
||||
IsMine bool `json:"is_mine"` // 是否请求方指定的记录(前端高亮"我的成绩")
|
||||
ClientIP string `json:"client_ip"`
|
||||
ServerIP string `json:"server_ip"`
|
||||
IsPrivateIP bool `json:"is_private_ip"` // ClientIP 是否为内网(探测失败回退场景)
|
||||
@@ -278,18 +280,68 @@ func toRankItems(rows []db.SpeedTestResult) []RankItem {
|
||||
return items
|
||||
}
|
||||
|
||||
// Rankings 排行榜:下载榜 / 上传榜 / 延迟榜 + 总测试次数
|
||||
// Rankings 排行榜:单表多指标,按 sort/order 动态排序;
|
||||
// include_id 指定"我的记录"——不在榜内时附加到末尾(带真实名次与 is_mine 标记),供前端高亮
|
||||
func (h *Handler) Rankings(c *gin.Context) {
|
||||
download, _ := h.stores.Results.TopDownload(rankLimit)
|
||||
upload, _ := h.stores.Results.TopUpload(rankLimit)
|
||||
latency, _ := h.stores.Results.TopLatency(rankLimit)
|
||||
total, _ := h.stores.Results.Count()
|
||||
sortField := c.DefaultQuery("sort", "download")
|
||||
order := c.DefaultQuery("order", "desc")
|
||||
|
||||
// 列名白名单,防注入
|
||||
var col string
|
||||
switch sortField {
|
||||
case "upload":
|
||||
col = "upload_mbps"
|
||||
case "latency":
|
||||
col = "latency_ms"
|
||||
default:
|
||||
sortField, col = "download", "download_mbps"
|
||||
}
|
||||
asc := order == "asc"
|
||||
|
||||
rows, _ := h.stores.Results.TopBy(col, asc, rankLimit)
|
||||
items := toRankItems(rows)
|
||||
for i := range items {
|
||||
items[i].Rank = i + 1
|
||||
}
|
||||
|
||||
// 附加"我的记录"(不在榜内时)
|
||||
if idStr := strings.TrimSpace(c.Query("include_id")); idStr != "" {
|
||||
if id, err := strconv.ParseUint(idStr, 10, 64); err == nil && id > 0 {
|
||||
inList := false
|
||||
for _, it := range items {
|
||||
if uint64(it.ID) == id {
|
||||
inList = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !inList {
|
||||
if rec, err := h.stores.Results.GetByID(uint(id)); err == nil {
|
||||
item := toRankItems([]db.SpeedTestResult{*rec})[0]
|
||||
var val float64
|
||||
switch col {
|
||||
case "upload_mbps":
|
||||
val = rec.UploadMbps
|
||||
case "latency_ms":
|
||||
val = rec.LatencyMs
|
||||
default:
|
||||
val = rec.DownloadMbps
|
||||
}
|
||||
if better, err := h.stores.Results.CountBetter(col, asc, val); err == nil {
|
||||
item.Rank = int(better) + 1 // 真实名次
|
||||
}
|
||||
item.IsMine = true
|
||||
items = append(items, item)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
total, _ := h.stores.Results.Count()
|
||||
c.Header("Cache-Control", "no-store")
|
||||
c.JSON(http.StatusOK, gin.H{
|
||||
"total": total,
|
||||
"download": toRankItems(download),
|
||||
"upload": toRankItems(upload),
|
||||
"latency": toRankItems(latency),
|
||||
"total": total,
|
||||
"sort": sortField,
|
||||
"order": order,
|
||||
"items": items,
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user