- M3 认证/权限/审计/限流 + 全部 REST 接口 - M4 管理面板(MIUIx + 成绩录入/积分榜下钻/大屏控制) - M5a 大屏页面(六页 + 明细覆盖层 + 缩放适配) - M6 WebSocket 实时同步中心 - M7 xlsx 导出 + 赛事包 zip + 备份 - 规则包 YAML 导入导出(差异预览) - 字体 vendor 子集化(4 套 x2 字重) - 集成:realtime/export/rules 接入 main+httpapi;backup 子命令 - 修复:中间件包装 ResponseWriter 丢失 Hijacker,导致 WS 握手 501
72 lines
2.0 KiB
Go
72 lines
2.0 KiB
Go
package rules
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"fmt"
|
|
"io"
|
|
|
|
"gopkg.in/yaml.v3"
|
|
)
|
|
|
|
// 导出文件的注释头:规则包可能含赛事敏感信息,不进版本库(rules/README.md)。
|
|
const header = `# 积分规则包 · scoreboard-rules v1
|
|
# 由系统导出,可手工编辑后再导入;导入前请先预览差异。
|
|
# 注意:本文件可能含赛事敏感信息,请勿提交到版本库。
|
|
`
|
|
|
|
// Parse 解析并校验规则包 YAML。未知字段一律报错,避免拼错的配置被静默忽略。
|
|
func Parse(data []byte) (*RulePack, error) {
|
|
if len(bytes.TrimSpace(data)) == 0 {
|
|
return nil, fmt.Errorf("%w: 内容为空", ErrNotRulePack)
|
|
}
|
|
|
|
dec := yaml.NewDecoder(bytes.NewReader(data))
|
|
dec.KnownFields(true)
|
|
|
|
var pack RulePack
|
|
if err := dec.Decode(&pack); err != nil {
|
|
if errors.Is(err, io.EOF) {
|
|
return nil, fmt.Errorf("%w: 内容为空", ErrNotRulePack)
|
|
}
|
|
return nil, fmt.Errorf("解析规则包 YAML: %w", err)
|
|
}
|
|
|
|
// 规则包只允许一个文档;多文档容易让人以为后面的也会生效。
|
|
var extra any
|
|
if err := dec.Decode(&extra); err == nil {
|
|
return nil, fmt.Errorf("%w: 只允许一个 YAML 文档", ErrNotRulePack)
|
|
} else if !errors.Is(err, io.EOF) {
|
|
return nil, fmt.Errorf("解析规则包 YAML: %w", err)
|
|
}
|
|
|
|
canonical := pack.Canonical()
|
|
if err := canonical.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
return &canonical, nil
|
|
}
|
|
|
|
// Marshal 把规则包序列化成带注释头的 YAML;输出是规范化后的结果,可安全往返。
|
|
func Marshal(pack *RulePack) ([]byte, error) {
|
|
if pack == nil {
|
|
return nil, fmt.Errorf("%w: 规则包为空", ErrNotRulePack)
|
|
}
|
|
canonical := pack.Canonical()
|
|
if err := canonical.Validate(); err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
var buf bytes.Buffer
|
|
buf.WriteString(header)
|
|
enc := yaml.NewEncoder(&buf)
|
|
enc.SetIndent(2)
|
|
if err := enc.Encode(&canonical); err != nil {
|
|
return nil, fmt.Errorf("序列化规则包 YAML: %w", err)
|
|
}
|
|
if err := enc.Close(); err != nil {
|
|
return nil, fmt.Errorf("序列化规则包 YAML: %w", err)
|
|
}
|
|
return buf.Bytes(), nil
|
|
}
|