feat: 评论/帖子编辑历史与编辑标记,内容锁定
- 新增 smartDiff 差异计算与 EditHistoryModal 编辑历史弹窗,替换原 CommentEditHistoryModal - 新增 PostEditedMark 编辑标记展示 - 新增内容锁定机制(content_lock)防止并发编辑冲突 - 审核与通知服务适配
This commit is contained in:
@@ -59,6 +59,12 @@ const (
|
||||
// SettingKeyAnalyticsRetentionDays 访问明细保留天数;缺行=90
|
||||
SettingKeyAnalyticsRetentionDays = "analytics_retention_days"
|
||||
|
||||
// 内容锁定与旧帖回复(全部缺行=0 关闭)
|
||||
SettingKeyPostEditLockHours = "post_edit_lock_hours" // 帖子可编辑时长;0=不锁定
|
||||
SettingKeyCommentEditLockHours = "comment_edit_lock_hours" // 评论可编辑时长;0=不锁定
|
||||
SettingKeyNecroReplyAfterHours = "necro_reply_after_hours" // 超过该时长回复视为旧帖回复;0=关闭
|
||||
SettingKeyNecroReplyPenalty = "necro_reply_penalty" // 旧帖回复扣除积分;0=仅提醒不扣分
|
||||
|
||||
// 积分增长规则(正整数奖励;0=关闭/不限,缺行=默认值)
|
||||
SettingKeyPointsCheckinBase = "points_checkin_base" // 签到基础分;缺行=5
|
||||
SettingKeyPointsStreakEveryDays = "points_streak_every_days" // 每满 N 天连续签到触发加成;0=关闭;缺行=7
|
||||
@@ -100,6 +106,9 @@ const (
|
||||
MinAnalyticsRetentionDays = 7
|
||||
MaxAnalyticsRetentionDays = 365
|
||||
|
||||
// MaxContentLockHours 内容锁定/旧帖阈值小时数上限(一年)
|
||||
MaxContentLockHours = 8760
|
||||
|
||||
DefaultPointsCheckinBase = 5
|
||||
DefaultPointsStreakEveryDays = 7
|
||||
MaxPointsRule = 100 // 各积分规则值/上限的统一上界
|
||||
@@ -150,6 +159,12 @@ type PublicSiteSettings struct {
|
||||
AttachmentMaxMB int `json:"attachment_max_mb"`
|
||||
AttachmentMaxCount int `json:"attachment_max_count"`
|
||||
ImageMaxMB int `json:"image_max_mb"`
|
||||
|
||||
// 内容锁定与旧帖回复(0=关闭;公开保持规则透明)
|
||||
PostEditLockHours int `json:"post_edit_lock_hours"`
|
||||
CommentEditLockHours int `json:"comment_edit_lock_hours"`
|
||||
NecroReplyAfterHours int `json:"necro_reply_after_hours"`
|
||||
NecroReplyPenalty int `json:"necro_reply_penalty"`
|
||||
BgSiteURL string `json:"bg_site_url"`
|
||||
BgSiteMode string `json:"bg_site_mode"`
|
||||
BgAdminURL string `json:"bg_admin_url"`
|
||||
@@ -438,6 +453,27 @@ func (s *SettingService) Public() (PublicSiteSettings, error) {
|
||||
}
|
||||
out.ImageMaxMB = imgMB
|
||||
|
||||
postLock, err := s.PostEditLockHours()
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.PostEditLockHours = postLock
|
||||
commentLock, err := s.CommentEditLockHours()
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.CommentEditLockHours = commentLock
|
||||
necroAfter, err := s.NecroReplyAfterHours()
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.NecroReplyAfterHours = necroAfter
|
||||
necroPenalty, err := s.NecroReplyPenalty()
|
||||
if err != nil {
|
||||
return out, err
|
||||
}
|
||||
out.NecroReplyPenalty = necroPenalty
|
||||
|
||||
siteURL, err := s.BgSiteURL()
|
||||
if err != nil {
|
||||
return out, err
|
||||
@@ -941,6 +977,91 @@ func (s *SettingService) ImageMaxBytes() (int64, error) {
|
||||
return int64(mb) << 20, nil
|
||||
}
|
||||
|
||||
// getLockHours 读取小时数配置(缺行/非法=0 关闭,超上限钳制)
|
||||
func (s *SettingService) getLockHours(key string) (int, error) {
|
||||
v, found, err := s.getValue(key)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !found {
|
||||
return 0, nil
|
||||
}
|
||||
n, convErr := strconv.Atoi(strings.TrimSpace(v))
|
||||
if convErr != nil || n < 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if n > MaxContentLockHours {
|
||||
return MaxContentLockHours, nil
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (s *SettingService) setLockHours(key string, hours int) error {
|
||||
if hours < 0 || hours > MaxContentLockHours {
|
||||
return ErrInvalidSiteSetting
|
||||
}
|
||||
if hours == 0 {
|
||||
return s.deleteKey(key)
|
||||
}
|
||||
return s.putValue(key, strconv.Itoa(hours))
|
||||
}
|
||||
|
||||
// PostEditLockHours 帖子可编辑时长(小时)。缺行=0(不锁定)。
|
||||
func (s *SettingService) PostEditLockHours() (int, error) {
|
||||
return s.getLockHours(SettingKeyPostEditLockHours)
|
||||
}
|
||||
|
||||
func (s *SettingService) SetPostEditLockHours(hours int) error {
|
||||
return s.setLockHours(SettingKeyPostEditLockHours, hours)
|
||||
}
|
||||
|
||||
// CommentEditLockHours 评论可编辑时长(小时)。缺行=0(不锁定)。
|
||||
func (s *SettingService) CommentEditLockHours() (int, error) {
|
||||
return s.getLockHours(SettingKeyCommentEditLockHours)
|
||||
}
|
||||
|
||||
func (s *SettingService) SetCommentEditLockHours(hours int) error {
|
||||
return s.setLockHours(SettingKeyCommentEditLockHours, hours)
|
||||
}
|
||||
|
||||
// NecroReplyAfterHours 旧帖回复阈值(小时)。缺行=0(关闭)。
|
||||
func (s *SettingService) NecroReplyAfterHours() (int, error) {
|
||||
return s.getLockHours(SettingKeyNecroReplyAfterHours)
|
||||
}
|
||||
|
||||
func (s *SettingService) SetNecroReplyAfterHours(hours int) error {
|
||||
return s.setLockHours(SettingKeyNecroReplyAfterHours, hours)
|
||||
}
|
||||
|
||||
// NecroReplyPenalty 旧帖回复扣分。缺行=0(仅提醒不扣分)。
|
||||
func (s *SettingService) NecroReplyPenalty() (int, error) {
|
||||
v, found, err := s.getValue(SettingKeyNecroReplyPenalty)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if !found {
|
||||
return 0, nil
|
||||
}
|
||||
n, convErr := strconv.Atoi(strings.TrimSpace(v))
|
||||
if convErr != nil || n < 0 {
|
||||
return 0, nil
|
||||
}
|
||||
if n > MaxPointsRule {
|
||||
return MaxPointsRule, nil
|
||||
}
|
||||
return n, nil
|
||||
}
|
||||
|
||||
func (s *SettingService) SetNecroReplyPenalty(n int) error {
|
||||
if n < 0 || n > MaxPointsRule {
|
||||
return ErrInvalidSiteSetting
|
||||
}
|
||||
if n == 0 {
|
||||
return s.deleteKey(SettingKeyNecroReplyPenalty)
|
||||
}
|
||||
return s.putValue(SettingKeyNecroReplyPenalty, strconv.Itoa(n))
|
||||
}
|
||||
|
||||
// AnalyticsEnabled 访问统计采集开关。缺行视为开启。
|
||||
func (s *SettingService) AnalyticsEnabled() (bool, error) {
|
||||
v, found, err := s.getValue(SettingKeyAnalyticsEnabled)
|
||||
|
||||
Reference in New Issue
Block a user