Commit Graph

154 Commits

Author SHA1 Message Date
518a4248d0 refactor: 删除死代码 SaveUploadedFile(5bdf010 后已无调用方) 2026-06-22 02:41:39 +08:00
97adf54d6d fix: golangci-lint 零告警通过 (57→0) + gofumpt/goimports 全量格式化
## CI 修复 (P0/P1)

P0 — 编译阻塞:
  - interfaces.go: postUseCase ISP 接口移除不用的 Create/Update/Delete

P1 — 必须修复:
  - ST1000: 为 13 个包添加包注释 (common/config/model/service/...)
  - ST1005: redis_store.go 全部错误消息改为小写开头
  - errcheck (19处): defer Close()→闭包忽略, notifier.Create→_=, r.Run→检查error
  - errorlint (9处): switch-on-error→errors.Is 链, ==→errors.Is
  - ST1020/ST1022: 导出符号注释以符号名开头

P2 — 安全评审:
  - gosec (12处): G203/G301/G304/G306 添加 nolint 注释并附理由

P3 — 清理:
  - unused: 移除 hasUnicode/energyStore/current/energyAdminUseCase
  - gofumpt + goimports 全量格式化 (35+ 文件)
2026-06-22 02:27:35 +08:00
9a496d3055 feat: 密码强度多级校验 — low/medium/high/very_high
- site_settings 新增 password.strength 配置项
- 四级:low(len>=8) / medium(两类) / high(三类+无连续) / very_high(四类+无连续)
- 连续字符检查:重复/升序/降序/键盘水平/键盘垂直序列
- 注册和改密接口统一读取配置
- 前端 register.js 动态校验 + 管理后台安全设置区
2026-06-22 00:54:21 +08:00
e9b7401dc9 feat: 定时发布系统 — Phase 5 完成
- Post ScheduledAt 字段
- Scheduler goroutine 每分钟扫描到期文章
- Create/Update 校验最短 30min 定时
- Approve 审核晚于定时则即时发布
- public 列表+空间页过滤未到期文章
- 写文章页 datetime-local 选择器(min=now+30min)
- AutoMigrate 追加新表
2026-06-22 00:39:04 +08:00
0b8f6f890d feat: 分类系统 + 标签系统 — Phase 4 完成
分类:Model/Repo/Service/Controller + 管理后台树形页面 + 首页导航栏
标签:Model/Repo/Service/Controller + /tags/{slug} 落地页 + 写文章页输入
Post 加 CategoryID,Create/Update 支持分类和标签
SQL 迁移含 categories/tags/post_tags 表及预设未分类
2026-06-22 00:30:29 +08:00
977c52513a feat: 文章属性前端完成 — Phase 3
- 写文章页:可见性/类型/转载来源/创作声明/禁止转载 属性表单
- 类型切换联动(转载显示来源,原创显示禁止转载复选框)
- 文章详情页:私密角标、置顶标记、创作声明横幅、转载来源
- 管理后台:置顶/取消置顶操作按钮
- PostService Create/Update 签名扩展接受文章属性
- DTO/接口同步更新
2026-06-22 00:20:52 +08:00
9c452afb40 feat: 文章属性 — Post Model 扩展 + 置顶系统
- Post 新增 7 字段:Visibility/PostType/ReprintSource/Declaration/ReprintProhibited/PinType/PinnedAt
- 公开列表过滤 private 文章(管理员可见全部)
- 列表排序:全局置顶 > 按时间
- declarationLabel 模板函数(7 种创作声明)
- Pin/Unpin API(admin+,category/global 两种置顶类型)
2026-06-22 00:04:55 +08:00
fc57ed17d4 feat: 公告系统 — Phase 2 完成
- Model/Repo/Service/Controller 全栈实现
- CRUD + ListActive(生效时间范围过滤)
- 管理后台独立页面 /admin/announcements(列表+弹窗CRUD)
- 前台首页 Hero 下方公告栏(info/success/warning/error 四色)
- Moderator+ 权限可管理公告
2026-06-22 00:00:21 +08:00
1933ea86b4 feat: 注册准则阅读控制 + Footer 合规信息
准则阅读(三层控制):
- registration.show_guidelines — 弹窗/复选框模式
- registration.force_guidelines — 是否强制倒计时+滚动
- registration.guidelines_timer — 倒计时秒数

Footer 合规(8 字段,空值不显示,链接纯手写):
- site.operator_name / site.icp_number / site.icp_license
- site.police_number / site.wenwangwen / site.algorithm_filing
- site.privacy_url / site.terms_url
- 新增 safeHTML 模板函数输出不转义 HTML
2026-06-21 23:50:52 +08:00
0014bdb9ff feat: 品牌CMS配置化 + cookie_secure配置
- config.yaml 新增 server.cookie_secure 配置项
- site_settings 新增 site_name/site_tagline/contact_email 站点信息
- SiteInfo 结构体扩展,支持运行时从 DB 读取站点品牌信息
- cookie.go 使用 cfg.Server.CookieSecure 替代 Mode!=debug 判断
- SetSessionCookie/ClearSessionCookie 支持 siteSettings 运行时覆盖
- 模板替换硬编码 MetaLab → {{.SiteName}}(nav/header/footer/home/login/register/guidelines/admin,12+处)
- 管理后台站点设置页新增站点名称/标语/联系邮箱输入项
- 页脚版权年份改为动态 CurrentYear
- 注册成功消息 CMS 化(后端+前端)
2026-06-21 23:34:14 +08:00
a2e8242c21 chore: 全局包名路径从 metalab 更改为 mce 2026-06-21 16:55:46 +08:00
8bf6ac9e51 feat: @提及发送通知 + 通知点击定位高亮
- 新增 NotifyCommentMention 通知类型,归属 mention 分类 TAB
- parseMentions 中向被@用户发送通知(排除自己和重复通知)
- 消息中心 mention 通知可点击跳转至文章页面
- 通过 mention 通知进入时,来源评论置顶并持久半透明高亮
- 普通评论链接保持原有短暂高亮行为不变
2026-06-03 13:21:27 +08:00
2ea9788ec7 fix: 关注状态 API 添加 Optional 认证中间件
- /api/users/:uid/follow-status 等公开 GET 接口缺少 Optional 认证
- 导致 GetGinUser 无法获取当前用户 uid,始终返回 i_follow=false
- 关注按钮加载时无法显示正确初始状态(始终显示"关注")
- 三条 GET 接口统一加入 authMdw.Optional() 组
- 不影响未登录用户(未登录依然返回 false)
2026-06-03 13:11:56 +08:00
864a488ee0 feat: @搜索优先展示已关注用户,结果限制最多5条
- SearchUsersByLevel SQL新增LEFT JOIN user_follows,按已关注优先+exp降序
- SearchUsers/followerUID参数贯穿repo→service→controller
- 继续输入时前端自动重新搜索新关键词
2026-06-03 01:15:38 +08:00
58aabb9bf0 fix: 临时登录cookie改用maxAge=IdleTimeout替代session模式
- rememberMe=false时maxAge从0改为IdleTimeout秒数(默认2h),与Redis TTL一致
- 解决服务器重启后部分浏览器(尤其隐私窗口)将session cookie视为失效的问题
- 关浏览器后依然自动清除(maxAge到期 + Redis TTL双保险)
2026-06-03 01:07:47 +08:00
ae15025b1d feat: @提及支持改名后链接跟随UID + 显示名自动更新
- comment_mentions新增original_username列,创建时记录原始@用户名
- Mentions map改为{original_username: {uid, name}}结构,name为当前显示名
- 用户改名后:旧@文本仍能匹配original_username,链接跟随uid不变,显示名自动更新
- 前端renderMentions适配新结构
2026-06-03 01:00:44 +08:00
be8ce04334 fix: 修复@提及uid始终为0的BUG + 空@提示优化
- SearchUsersByLevel SQL id列别名改为uid,修复GORM字段名映射导致uid=0
- @空输入时悬浮窗显示'输入以查找用户'而非空白
- 清理历史脏数据(uid=0的mention记录)
2026-06-03 00:55:51 +08:00
4ac4f64f33 feat: @提及功能优化 + 默认头像改用SVG图标
- 评论区有效@提及渲染为蓝色链接跳转/space/{uid},无效@保持纯文本
- 输入@触发悬浮窗,根据输入实时搜索用户(含头像+等级)
- 无匹配时显示未搜索到用户,选中后插入为@用户名 格式(尾部空格)
- 悬浮窗定位于输入光标上方,不遮挡输入内容
- 后端Comment新增Mentions字段,批量加载提及映射供前端判断
- SearchUsers/parseMentions 统一限制LV2+用户
- 所有默认头像由首字符文字改为统一SVG人形图标(nav/home/space/mention等7处)
2026-06-03 00:49:24 +08:00
2d05da6709 fix: 赋能弹窗已满时不再弹出 + API 补上 post_energize_total 字段
- 赋能按钮点击时先查已赋能总量,>=20 直接 showToast 不弹窗
- 修复控制响应缺失 post_energize_total 字段导致前端永远走到 showOverlay
- fetch 失败/.catch 改为 showToast 而非 showOverlay
2026-06-03 00:23:35 +08:00
1af9fba291 feat: 赋能溢出截断 + 前端按已赋能量禁用按钮
- 单篇赋能上限由硬拒绝改为溢出截断,类似每日经验处理
- 前端弹窗时查询文章已赋能总量,>=10 禁用重赋,>=20 全部禁用
- GetEnergyInfo 支持 ?post_id= 返回 post_energize_total
- 文案精简为'赋能成功!+N 经验'等短句
2026-06-03 00:13:03 +08:00
fcff28ab60 feat: Studio 概览/分析补上阅读量 + 详情页 meta 行改版
- StudioOverview 新增 TotalViews 字段,GetOverviewByUserID 查询含 SUM(views_count)
- 新增 ViewTrendStore 接口和 AggregateViewsByAuthor,UNION ALL 合并已登录+访客阅读日志按日期聚合
- 数据分析页面新增阅读量趋势折线图(绿色,Chart.js)
- 概览和数据分析页面新增总阅读量统计卡片
- 详情页 meta 行改为标签格式:作者/阅读/点赞/收藏/评论/发布时间
- 移除详情页公开状态徽章,已锁定+理由仅作者可见
2026-06-02 23:26:48 +08:00
ea2f196ff7 feat: 文章锁定/解锁时触发系统通知
- 新增 NotifyPostLocked/NotifyPostUnlocked 通知类型
- Lock/Unlock 方法执行后通过 notifier 发送通知给作者
- 锁定时通知含锁定理由,解锁时通知已解除锁定
- 新增类型归入稿件审核分类和 system tab
2026-06-02 22:53:46 +08:00
a806532d25 fix: 锁定按钮改为随时可用,不联动审核状态
- 前端:锁定按钮仅受 IsLocked 控制,移除 status!=pending 的条件
- 后端:Lock 方法移除 pending 状态的阻止,锁定状态独立
2026-06-02 22:31:34 +08:00
ca21ae7216 fix: 修复头像审核旧图裂开问题 + CSP 配置修复
- 头像提交审核不再删除旧文件,审核通过后才删除旧头像文件
- 头像审核被拒时删除已上传的新头像文件,保留旧头像
- CSP style-src 移除 nonce 以启用 unsafe-inline(JS 动态样式需要)
- CSP script-src 移除 nonce 改用 unsafe-inline + unsafe-eval(Vditor 编辑器和动态样式需要)
2026-06-02 22:26:01 +08:00
7d944cfa35 fix: 修复更名退款逻辑、审核列表显示及更名消耗确认提示
- 首次更名审核被拒不再退款,首次免费未扣费不产生退款
- 审核列表提交者列改用 UID,当前用户名列显示当前用户名
- 更名消耗提示改为确认弹窗,首次/非首次动态显示不同文案
2026-06-02 22:09:41 +08:00
543325105c fix: 修复管理后台样式丢失 + CSP inline style 违规
- 评论管理页:创建 comments.css,controller 传递 ExtraCSS/ExtraJS,模板移除所有 inline style,JS 改用 CSS 类
- 修复 comments.js 加载顺序:通过 ExtraJS 在 common.js 之后加载,解决 api is not defined
- base.html:SVG inline style 移至 common.css
- dashboard/index.html:3 处 inline style 替换为 CSS 类
- audit/index.html:modal 移除冗余 style="display:none"
- fund_logs/logs:label inline style 移至 energy.css
- common.css 新增多个 CSS 规则
- energy.css 新增 .filter-bar label 规则
2026-06-02 21:37:52 +08:00
72c08f3689 feat: 社区准则改为数据库存储
- AuthController 注入 SiteSettings 依赖,RegisterPage 优先从 DB 读取准则
- DB 中 site.guidelines 为空时 fallback 到静态 guidelines.html 文件
- 管理后台站点设置页新增「内容管理」分区,textarea 编辑社区准则
- 添加准则编辑区垂直布局样式
2026-06-02 20:17:23 +08:00
95e1728c6e feat: GetTrends 趋势查询并行化 (errgroup)
- 使用 golang.org/x/sync/errgroup 将 4 个独立 DB 查询并行执行
- Energy/Comments/Likes/Favorites 趋势查询不再串行阻塞
- golang.org/x/sync 由 indirect 改为 direct 依赖
2026-06-02 19:37:43 +08:00
ab26704049 fix: PostsPage GetOverview 错误不再忽略
- studio_page_controller.go PostsPage 中 GetOverview 调用不再用 _ 忽略 error
- 错误时记录日志,模板侧已有 {{if .Overview}} 空值保护,页面正常降级渲染
2026-06-02 19:33:05 +08:00
99d16190a8 fix: UID 类型混用统一(uint ↔ string)
- Comment.ReplyToUID string→uint, 移除 varchar(36) gorm 约束
- CommentMention.UID string→uint, 移除 varchar(36) gorm 约束
- UserSearchResult.UID string→uint
- 移除 SearchUsersByLevel 中 strconv.FormatUint 转换
- 删除 GetUserUIDByID 方法,CreateReply 直接使用 parent.UserID
- fillReplyToNames 使用 map[uint]string 替代 map[string]string
- 更新 commentStore 接口声明
- 修复前端 mention 下拉 show.html 中 u.id→u.uid
2026-06-02 19:26:44 +08:00
5911cee01c refactor: BaseModel 主键列名统一 uid→id
- BaseModel 移除 gorm column:uid 和 json:uid 标签,统一为 id
- 更新 8 个 repository 文件中 28 处 raw SQL 列引用 (uid→id)
- 更新 3 个前端 JS 文件中 14 处 API 响应字段引用 (uid→id)
- 添加数据库迁移 SQL 脚本 (docs/migrations/001_uid_to_id.sql)
2026-06-02 19:17:57 +08:00
7fdea90ded feat: 敏感操作增加IP维度限流
- RateLimiter 新增 AllowSensitive 方法(1分钟5次,超限封禁5分钟)
- 新增 SensitiveRateLimit Gin 中间件
- 改密(PUT /api/settings/password)增加限流保护
- 注销(POST /api/settings/delete-account)增加限流保护
- 签到(POST /api/level/checkin)增加限流保护
2026-06-02 19:03:04 +08:00
6aacbbab1c feat: 密码强度校验增强,要求大写+小写+数字
- validatePassword 从"字母+数字"升级为"大写字母+小写字母+数字"
- 拆分 pwLetter 为 pwLower([a-z]) 和 pwUpper([A-Z])
- 更新 ErrWeakPassword 错误提示
- 同步更新注册页和修改密码页的前端校验及提示文案
2026-06-02 18:56:08 +08:00
77d8562916 fix: 添加 HSTS 响应头
- SecurityHeaders 中间件增加 Strict-Transport-Security 头
- 仅在 release 模式下启用,避免开发环境 localhost 证书问题
2026-06-02 18:33:59 +08:00
66a802beb7 fix: 添加 posts 复合索引 idx_posts_user_status
- 为 UserID 和 Status 列添加复合索引 idx_posts_user_status,优化 user_id + status 联合查询性能
2026-06-02 17:24:45 +08:00
593b52096f feat: 首页增加内存缓存(30s TTL)
- 新增 internal/cache/home_cache.go,实现带 TTL 的首页文章列表内存缓存
- frontend.go 首页 handler 优先读取缓存,未命中/过期时查询 DB 并回填
- PostService 注入失效回调,在 Create/Update/Delete/Approve 后自动失效缓存
- 减少首页每次请求都查 DB 的开销
2026-06-02 17:20:59 +08:00
3e0fed80ce refactor: Controller 不直接读 Session Exp,新增 CanCreatePost 方法
- 新增 userExpReader 最小接口(ISP),PostService 通过它读取用户经验值
- PostService 新增 CanCreatePost(userID uint) bool,封装 LV0 投稿权限判断
- StudioController 替换内联 c.Get("exp") 检查为调用 ctrl.postService.CanCreatePost(uid)
- 更新 DI 注入链:NewPostService 增加 userRepo 参数
2026-06-02 16:52:44 +08:00
3df7b2e672 fix: Service 层移除直接持有的 *gorm.DB,引入 Repository Transaction 方法
- ReactionStore/FollowStore/EnergyStore/FavoriteStore 接口新增 Transaction 方法
- EnergyStore.Transaction 支持跨仓库事务(EnergyStore + FundStore)
- ReactionRepo/FollowRepo/FavoriteRepo/EnergyRepo 实现 Transaction 方法
- ReactionRepo 新增 GetPostLikesCount 方法,消除 Service 直查 DB
- ReactionService/FollowService/FavoriteService/EnergyService 移除 db 字段
- EnergyRepo 通过 SetFundStore 注入 FundStore 用于跨仓库事务
- 更新 deps_extra.go 构造函数调用
2026-06-02 16:46:44 +08:00
1cb7a832f1 fix: CSRF Token 日志脱敏
- 移除 CSF 中间件日志中敏感 token 值的打印(移除 %q)
- MISMATCH 日志仅保留路径和长度信息
- OK 日志移除,避免生产环境中泄露 CSRF token
2026-06-02 16:32:20 +08:00
53f18ca6ee fix: CSP 策略移除 unsafe-inline,改用 nonce 机制
- SecurityHeaders 中间件生成随机 nonce,注入 context
- BuildPageData/BuildAdminPageData 将 CSPNonce 传递给模板
- 所有 19 个模板文件 <script> 标签添加 nonce 属性
- 移除所有内联事件处理(onclick/onerror),改用事件监听
- 移除所有 javascript:void(0) 链接,改用 # 号 + preventDefault
- utils.js 添加全局 avatar 图片加载失败回退处理
- common.js 登出按钮添加 preventDefault
- audit.js 关闭弹窗按钮改用事件监听
2026-06-02 16:10:23 +08:00
9477155bcf refactor: 文件行数超标拆解(7个文件→14个文件,全部≤200行)
- 移动本地接口定义到 interfaces.go 和 admin/interfaces.go
- favorite_controller.go → favorite_item_controller.go
- space_controller.go → space_loaders.go
- settings_controller.go → settings_api_audit.go
- admin_energy_controller.go → admin_energy_api_controller.go
- admin_post_controller.go → admin_post_action_controller.go
- comment_repo.go → comment_mention_repo.go
- post_repo.go → post_stats_repo.go
2026-06-02 15:58:52 +08:00
ff9886f08b refactor: 文件行数超标拆解(Service + Controller 9/16 完成)
- Service 层:energy_service → energize/operations/admin/query 四文件
- Service 层:post_service → helpers/audit + 核心 CRUD 保留
- Service 层:favorite_service → items + 核心文件夹操作保留
- Service 层:auth_service → password + 核心注册/登录保留
- Service 层:notification_service → notify + 核心列表/已读保留
- Service 层:audit_service → review + 核心列表/详情保留
- Controller 层:studio_controller → page/write/api/post_api/action 五文件
- Controller 层:post_controller → page/api/upload 三文件
- Controller 层:comment_controller → list/write/action/upload 四文件
- 清理未使用导入(notification_service.go 移除 fmt)
2026-06-02 15:45:54 +08:00
7cdb4d6698 refactor: GetOverview 5 次独立查询合并为单次 GROUP BY
- 新增 PostRepo.GetOverviewByUserID 单次 CASE WHEN + GROUP BY 查询
- Service 层 GetOverview 从 5 次 DB 查询减少为 1 次
- 接口 postStore 新增 GetOverviewByUserID 声明
2026-06-02 15:25:17 +08:00
166e01c7c3 fix: 实现空间 TotalReads 统计,修复 selectMention 闭包
- P0-2: posts/show.html selectMention 闭包 var→let 修复
- P0-3: GetUserStats 增加 SUM(views_count) 统计,替换硬编码 int64(0)
- 更新 Fix_List.md 标注 P0-1/2/3 已修复
2026-06-02 15:21:37 +08:00
b9d90fe7ec feat: 新增文章阅读量计数(已登录去重 + 访客防刷)
- Post 模型新增 ViewsCount 冗余计数字段
- 新建 PostReadLog 表(user_id + post_id 唯一索引),已登录用户每篇文章只计一次
- 新建 PostGuestReadLog 表(visitor_id + post_id 唯一索引),访客基于 Cookie 标识去重
- Repository 层新增 RecordRead / RecordGuestRead / IncrementViewsCount
- Service 层编排去重-计数逻辑,计数失败不影响页面渲染
- Controller 层 ShowPage + ShowAPI 触发计数,仅 approved 状态生效
- 双层防刷:唯一索引去重 + 2 秒冷却间隔
- 访客 Cookie (visitor_id) 基于 crypto/rand 生成,30 天有效期
2026-06-02 00:02:48 +08:00
c6da3932f3 fix: 修复 Space 模板缺少 formatCount/str 函数导致的启动 panic
- 在 theme/loader.go FuncMap 中添加 formatCount 和 str 函数
- formatCount 将 int64 格式化为中文计数(如 2.5万)
- 控制器 ActiveFolderID 改为 uint 类型,避免模板中 str 类型比较
- 控制器传递 ActiveFolder 对象,移除模板内查找逻辑
- 清理旧模板替换残留的孤儿 HTML/SVG 代码
2026-06-01 21:51:17 +08:00
d9eff2df08 refactor: 重构 Space 页面为统一多 Tab 布局 + 移除独立关注/粉丝子页面
- 后端:SpaceController 支持 5 个 Tab(文章/关注/粉丝/收藏/设置),统一处理本人与访客视图
- 后端:注入 FavoriteService 支持收藏夹 Tab,新增用户统计数据查询
- 后端:新增 PUT /api/space/privacy 隐私设置 API 端点
- 后端:移除 FollowPageController 及 /space/:uid/followers|following 路由
- 前端:完全重写 space/index.html,顶部信息栏 + Tab 导航 + 左侧栏 + 主内容区
- 前端:重写 space.css 适配新布局(Profile Header/Tab Bar/文章/收藏/设置)
- 清理:删除独立的 follow HTML 模板和 CSS 文件
- 模型:User 表新增 follower_list_public 隐私字段
2026-06-01 21:48:01 +08:00
797e9c15f4 refactor: space页面改为卡片式关注列表布局
- 重写 space/index.html:左右分栏,左侧导航栏 + 右侧卡片网格
- 自己空间:显示全部关注用户卡片(头像/用户名/简介/已关注标签)+ 设置入口
- 他人空间:简化侧栏(Ta的关注/粉丝),隐私关闭时显示空状态插图
- Model UserFollow 新增头像/简介联表字段
- Repository ListFollowing/ListFollowers SELECT 增加 avatar/bio
- Controller ShowSpace 注入 FollowService,获取关注列表数据
- space.css 完全重写为卡片网格布局 + 响应式适配
- follow.css 清理旧 .follow-btn/.follow-links 无用样式
2026-06-01 21:18:04 +08:00
db2f9206be fix: 修复关注/粉丝列表 JOIN users 表列名错误 u.id → u.uid
- ListFollowers/ListFollowing 中 JOIN users 表使用了 u.id,但 users 表主键列名为 uid
- 导致关注/粉丝列表查不到数据,列表为空但与计数器不一致
2026-06-01 20:56:27 +08:00
a0df66587b fix: 修复关注/粉丝列表隐私检查假阳性导致本人也无法访问
- follow_repo.go: GetFollowListPublic 改用显式 WHERE uid=? 避免 GORM 主键解析潜在问题
- follow_service.go: GetFollowListPublic 失败时默认公开并记录日志,而非返回错误
- follow_controller.go: 错误兜底时 Accessible 默认 true,避免误锁用户
2026-06-01 20:22:50 +08:00