清除业务契约双读:抽奖/草稿/时间戳/邮件只认新格式,并删除 legacy 短代码改写。
Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
@@ -113,9 +113,6 @@ func (o *Operations) smtp(ctx context.Context, c MailConfig, p *mailPayload, id
|
||||
|
||||
const defaultMailSubject = "{{site_name}} · {{purpose}}"
|
||||
|
||||
// 旧版内置正文:读取时若仍是此值,升级为新默认模板。
|
||||
const legacyMailBody = "{{logo}}<h1>{{site_name}} · {{purpose}}</h1><p>您的验证码:</p><p><strong>{{code}}</strong></p><p>15 分钟内有效。如果不是您本人操作,请忽略本邮件。</p><p><a href=\"{{link}}\">前往站点完成{{purpose}}</a>(请粘贴以上验证码)</p>"
|
||||
|
||||
const defaultMailBody = `<div class="email-wrap" style="margin:0;padding:32px 16px;background:#f5f7fb;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;line-height:1.6;color:#1e293b;">
|
||||
<style>
|
||||
.email-wrap{margin:0;padding:32px 16px;background:#f5f7fb;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',Arial,sans-serif;line-height:1.6;color:#1e293b;}
|
||||
@@ -158,8 +155,7 @@ func (c *MailConfig) applyTemplateDefaults() {
|
||||
if strings.TrimSpace(c.SubjectTemplate) == "" {
|
||||
c.SubjectTemplate = defaultMailSubject
|
||||
}
|
||||
body := strings.TrimSpace(c.BodyTemplate)
|
||||
if body == "" || body == legacyMailBody {
|
||||
if strings.TrimSpace(c.BodyTemplate) == "" {
|
||||
c.BodyTemplate = defaultMailBody
|
||||
}
|
||||
}
|
||||
|
||||
@@ -589,10 +589,11 @@ func TestMailTemplateGuards(t *testing.T) {
|
||||
if cfg.BodyTemplate != defaultMailBody || cfg.SubjectTemplate != defaultMailSubject {
|
||||
t.Fatal("defaults not applied")
|
||||
}
|
||||
cfg.BodyTemplate = legacyMailBody
|
||||
customBody := "<p>{{code}}</p>"
|
||||
cfg.BodyTemplate = customBody
|
||||
cfg.applyTemplateDefaults()
|
||||
if cfg.BodyTemplate != defaultMailBody {
|
||||
t.Fatal("legacy default not upgraded")
|
||||
if cfg.BodyTemplate != customBody {
|
||||
t.Fatal("自定义正文不应被默认模板覆盖")
|
||||
}
|
||||
o := &Operations{cfg: &config.Config{SiteURL: "https://forum.example.com", DevMode: true}}
|
||||
if e := o.validate(nil, "mail", &MailConfig{Port: 465, TLS: "tls", Timeout: 10, Retention: 30, BodyTemplate: "<script></script><p>no code</p>"}); e == nil || !strings.Contains(e.Error(), "{{code}}") {
|
||||
|
||||
@@ -14,9 +14,9 @@ import (
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// 兼容旧引用:默认体积/个数(实际限额读 SettingService)
|
||||
// 附件默认体积/个数上限(实际限额读 SettingService;此处供校验与测试引用)
|
||||
const (
|
||||
FileMaxBytes = 20 << 20 // 默认 20 MiB(缺省配置)
|
||||
FileMaxBytes = 20 << 20 // 默认 20 MiB
|
||||
MaxPostAttachments = 10
|
||||
)
|
||||
|
||||
|
||||
@@ -49,7 +49,7 @@ type LotteryPrize struct {
|
||||
|
||||
type LotteryMeta struct {
|
||||
Prizes []LotteryPrize `json:"prizes"`
|
||||
Slots int `json:"slots"` // 由 prizes 数量合计推导,兼容旧数据
|
||||
Slots int `json:"slots"` // 由 prizes 数量合计,仅便于展示/校验
|
||||
Drawn bool `json:"drawn"`
|
||||
WinnerIDs []uint `json:"winner_ids"`
|
||||
Closed bool `json:"closed"`
|
||||
@@ -122,13 +122,7 @@ func parseLotteryMeta(raw string) (*LotteryMeta, error) {
|
||||
if err := json.Unmarshal([]byte(raw), &m); err != nil {
|
||||
return nil, errors.New("抽奖配置无效")
|
||||
}
|
||||
// 旧数据只有 slots:合成单一奖品项
|
||||
if len(m.Prizes) == 0 && m.Slots > 0 {
|
||||
m.Prizes = []LotteryPrize{{Name: "奖品", Count: m.Slots}}
|
||||
}
|
||||
if len(m.Prizes) > 0 {
|
||||
m.Slots = lotterySlotsFromPrizes(m.Prizes)
|
||||
}
|
||||
m.Slots = lotterySlotsFromPrizes(m.Prizes)
|
||||
return &m, nil
|
||||
}
|
||||
|
||||
@@ -592,16 +586,12 @@ func (s *PostService) buildQuestionState(post *model.Post, viewerID uint, canMan
|
||||
}
|
||||
floor := FloorNumber(s.db, post.ID, m.AcceptedCommentID)
|
||||
answer := s.loadAcceptedAnswer(post.ID, m.AcceptedCommentID, floor, viewerID, isMod)
|
||||
solvedAt := m.SolvedAt
|
||||
if m.Solved && solvedAt == "" && answer != nil && !answer.CreatedAt.IsZero() {
|
||||
solvedAt = answer.CreatedAt.UTC().Format(time.RFC3339) // 旧数据:无 solved_at 时用采纳评论时间
|
||||
}
|
||||
return &QuestionState{
|
||||
Solved: m.Solved,
|
||||
AcceptedCommentID: m.AcceptedCommentID,
|
||||
AcceptedFloor: floor,
|
||||
AcceptedAnswer: answer,
|
||||
SolvedAt: solvedAt,
|
||||
SolvedAt: m.SolvedAt,
|
||||
CanAccept: canManage && !m.Solved,
|
||||
CanSolve: canManage && !m.Solved,
|
||||
// 已采纳答案后不可重新打开;仅「手动标已解决」可撤回
|
||||
@@ -688,17 +678,13 @@ func (s *PostService) buildPollState(post *model.Post, viewerID uint, canClose b
|
||||
opts[i] = stat
|
||||
}
|
||||
voters := len(voterSet)
|
||||
closedAt := m.ClosedAt
|
||||
if m.Closed && closedAt == "" {
|
||||
closedAt = m.EndsAt // 旧数据:无 closed_at 时用截止时刻兜底
|
||||
}
|
||||
return &PollState{
|
||||
Options: opts,
|
||||
Multi: m.Multi,
|
||||
Closed: m.Closed,
|
||||
Anonymous: m.Anonymous,
|
||||
EndsAt: m.EndsAt,
|
||||
ClosedAt: closedAt,
|
||||
ClosedAt: m.ClosedAt,
|
||||
TotalVotes: total,
|
||||
Voters: voters,
|
||||
MyOptions: my,
|
||||
|
||||
Reference in New Issue
Block a user