- 安全:移除 CSP 中 esm.sh/cdnjs.cloudflare.com,highlight.js 主题已本地化 73 个文件 - 安全:StatusBanned 分支补 dummy hash 防时序攻击 - 安全:手写 constantTimeEq 替换为 crypto/subtle.ConstantTimeCompare - Bug:锁定操作消息已删除→已锁定 - YAGNI:删除 12 个空预留模板目录 - KISS:删除 common.CheckPassword 薄封装,统一用 bcrypt 调用 - KISS:删除 tokenCtrl 别名字段,api.go 统一用 authCtrl - RateLimiter:check()+recordFail 闭包模式重构为 try() 原子操作,消除竞态 - RateLimiter:新增 ClearIP() 方法,注册成功时清除 IP 计数 - 文档:修正 audit_service.go 注释编号跳跃(3→5→4) - 文档:修正 deps_core.go 过清理→过期清理
93 lines
3.5 KiB
Go
93 lines
3.5 KiB
Go
package model
|
||
|
||
// =============================================================================
|
||
// Shortcode — Markdown 扩展语法
|
||
//
|
||
// 用法:在 MD 正文中插入 [zone:类型:参数],渲染时替换为对应 UI 卡片。
|
||
//
|
||
// 支持的 shortcode:
|
||
// [zone:event:活动ID] → 官方活动卡片(标题、时间、封面、链接)
|
||
// [zone:game:游戏slug] → 游戏信息卡片(名称、封面、类型)
|
||
// [zone:poll:投票ID] → 投票卡片(※后端 API 开发中)
|
||
// [zone:resource:资源ID] → 资源卡片(※后端 API 开发中)
|
||
//
|
||
// 扩展现有类型:在 ShortcodeRegistry 中注册新类型 + 实现 Renderer 即可。
|
||
// =============================================================================
|
||
|
||
import "regexp"
|
||
|
||
// =============================================================================
|
||
// 语法常量
|
||
// =============================================================================
|
||
|
||
// ShortcodePattern 匹配所有 [zone:type:params] 模式的 shortcode
|
||
// 捕获组:$1=类型, $2=参数
|
||
var ShortcodePattern = regexp.MustCompile(`\[zone:(\w+):([^\]]+)\]`)
|
||
|
||
// =============================================================================
|
||
// 已注册的 Shortcode 类型
|
||
// =============================================================================
|
||
|
||
// ShortcodeType 定义一种 shortcode 的类型标识
|
||
type ShortcodeType string
|
||
|
||
const (
|
||
ShortcodeEvent ShortcodeType = "event" // [zone:event:活动ID]
|
||
ShortcodeGame ShortcodeType = "game" // [zone:game:游戏slug]
|
||
ShortcodePoll ShortcodeType = "poll" // [zone:poll:投票ID] ※后端 API 开发中
|
||
ShortcodeResource ShortcodeType = "resource" // [zone:resource:资源ID] ※后端 API 开发中
|
||
)
|
||
|
||
// allTypes 所有已定义的 shortcode 类型,添加新类型时在这里追加
|
||
var allTypes = []ShortcodeType{
|
||
ShortcodeEvent,
|
||
ShortcodeGame,
|
||
ShortcodePoll,
|
||
ShortcodeResource,
|
||
}
|
||
|
||
// IsValidType 检查给定类型是否已注册
|
||
func IsValidType(t string) bool {
|
||
for _, st := range allTypes {
|
||
if string(st) == t {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
// =============================================================================
|
||
// 解析结果
|
||
// =============================================================================
|
||
|
||
// Shortcode 一次解析的结果
|
||
type Shortcode struct {
|
||
Raw string // 原始文本,如 "[zone:event:abc123]"
|
||
Type ShortcodeType // 类型
|
||
Params string // 参数(ID/slug 等)
|
||
}
|
||
|
||
// ShortcodeResult 整个 body 的解析结果
|
||
type ShortcodeResult struct {
|
||
// ProcessedBody 替换后的 body(shortcode → HTML 占位符),可直接传给模板渲染
|
||
ProcessedBody string
|
||
// Shortcodes 本次解析到的所有 shortcode 列表
|
||
Shortcodes []Shortcode
|
||
}
|
||
|
||
// =============================================================================
|
||
// 卡片渲染数据 DTO
|
||
// =============================================================================
|
||
|
||
// ShortcodeCard 渲染一张卡片的通用数据
|
||
// 前端根据 Type 决定具体 UI 组件,字段按需填充
|
||
type ShortcodeCard struct {
|
||
Type ShortcodeType `json:"type"`
|
||
ID string `json:"id"` // 业务 ID(活动ID / 游戏slug)
|
||
Title string `json:"title,omitempty"`
|
||
Cover string `json:"cover,omitempty"`
|
||
Desc string `json:"desc,omitempty"`
|
||
URL string `json:"url,omitempty"`
|
||
Extra string `json:"extra,omitempty"` // 扩展字段(时间、标签等,JSON string)
|
||
}
|