**新增功能:**
用户名编辑:输入框替换静态文本,白名单验证(中文/英文/数字/下划线/连字符),前端计数器(n/16),utf8对齐PG VARCHAR,XSS防控。
个性签名编辑:Textarea,128字符上限,实时计数器。
头像上传管线:校验→解码→裁切→CatmullRom缩放→WebP二分编码≤100KB→原子写入(.tmp→os.Rename)。限制5MB,128-3840px,JPEG/PNG/WebP。输出512x512 WebP。文件名 {uid}_{timestamp}.webp。清理旧头像。
自定义裁切弹窗:浅色主题,固定裁切框+图片平移/缩放(1×-3×滚轮),box-shadow遮罩,三等分网格。坐标映射pan/zoom→原图像素→subImage。
CSP修复:img-src允许data:URI(FileReader预览)。
**文件变更:**
修改: README, docs/{development,structure}.md, go.{mod,sum}, cmd/server/main.go, controller/settings, middleware/security, router, templates/settings/{index.html,css}
新增: internal/service/avatar_service.go, docs/settings.md
5.5 KiB
5.5 KiB
目录结构与分层规范
总览
METAZONE.FAN/
├── cmd/server/main.go # 入口:初始化→启动,只做编排,不写业务
├── internal/ # 内部包(不可外部 import)
│ ├── config/ # 配置加载(结构体 + yaml/file)
│ ├── router/ # 路由注册(按路由组拆文件)
│ ├── controller/ # 控制器(瘦)— 接参数 → 调服务 → 返回
│ │ ├── interfaces.go # 控制器层接口定义(ISP)
│ │ └── admin/ # 后台子包(功能多,独立拆)
│ ├── service/ # 业务逻辑(厚)— 核心逻辑、校验、编排
│ │ ├── repository.go # Service 层仓储接口定义(ISP)
│ │ └── avatar_service.go # 头像上传处理管线
│ ├── repository/ # 数据访问(DAO)— 纯数据库操作
│ ├── model/ # 数据模型 — 纯结构体 + DTO
│ ├── middleware/ # 中间件 — 每个中间件一个文件
│ ├── theme/ # 模板/主题管理 — 加载、切换、静态资源
│ └── common/ # 公共工具 — 响应格式、分页、校验、错误码
├── templates/ # 前端模板 — 多主题支持
│ ├── MetaLab-2026/ # 当前主题
│ │ ├── theme.json # 主题元信息
│ │ ├── guidelines.html # 准则等长文本(非模板,纯内容文件)
│ │ ├── html/ # 页面模板
│ │ │ ├── layout/ # 布局组件(header/nav/footer)
│ │ │ ├── home/ post/ comment/ user/ topic/ settings/
│ │ │ └── partials/ # 可复用组件(卡片/分页/侧栏)
│ │ └── static/ # 静态资源(css/js/img)
│ │ ├── css/
│ │ │ ├── common.css # 全局样式 + 导航
│ │ │ ├── home.css auth.css settings.css
│ │ └── js/
│ ├── MetaLab-2027/ # 未来主题(相同结构)
│ └── system/ # 系统模板(email 等,与主题无关)
├── storage/ # 运行时数据(.gitignore)
│ ├── uploads/avatars/ # 用户头像(WebP)
│ └── logs/
└── docs/ # 项目文档
各层职责与边界
Controller(控制器)
- 只做三件事:绑定请求参数 → 调用 Service → 返回响应
- 不允许直接调用 Repository
- 不允许写 if-else 业务分支逻辑
- 单文件 ≤ 120 行
- 每个领域实体一个文件,后台独立
admin/子包
// ✅ 正确:瘦控制器
func (c *PostController) Detail(ctx *gin.Context) {
id := ctx.Param("id")
post, err := c.postService.GetDetail(ctx, id)
if err != nil {
common.Error(ctx, common.ErrNotFound, "帖子不存在")
return
}
common.Ok(ctx, post)
}
Service(业务逻辑)
- 承载所有业务规则:权限校验、数据组装、事务编排
- 可以调用多个 Repository
- 单文件 ≤ 300 行
- 方法命名语义化:
Create/Update/Delete/GetDetail/ListByTopic
Repository(数据访问)
- 纯数据库操作:增删改查,不含业务逻辑
- 单文件 ≤ 200 行
- 不引用 Controller 或 Service 层
Model(模型)
- 纯结构体定义 + DTO
- 单文件 ≤ 80 行
common.go放 BaseModel(ID/CreatedAt/UpdatedAt)dto.go放请求/响应传输对象
Middleware(中间件)
- 每个中间件一个文件,≤ 60 行
- 按关注点注册到路由组上,不要逐个路由单独加
Router(路由)
- 按路由组拆文件:
web.go(页面)、api.go(API)、admin.go(后台) - 只做路由映射,不写 handler 逻辑
Theme(主题管理)
loader.go:模板文件加载、内容文件读取manager.go:主题扫描、切换、获取当前resolver.go:静态资源路径解析(/static/→ 当前主题 static 目录)theme.go:主题接口定义 + ThemeMeta 结构体
Common(公共工具)
response.go:统一 JSON 响应格式error_code.go:错误码常量validator.go:参数校验pagination.go:分页工具
import 规则
允许的依赖方向(从上到下,不可反向):
controller → service → repository → model
controller → common
service → common + repository + model
middleware → common + service(可选)
router → controller + middleware
theme → 无(独立工具包)
- Controller 不可 import Repository
- Controller 不可 import 另一个 Controller
- Service 不可 import Controller
- Repository 不可 import Service
模板目录规范
- 每个主题
templates/{主题名}/下有独立完整的html/和static/ - 主题之间互不引用、互不依赖
- 长文本内容(准则、关于页面等)放主题根目录(
guidelines.html),不在html/下 - 这些内容文件不是模板(不含
{{}}语法),由 Controller 读取后注入 - 系统级模板(email 等)放
templates/system/,与主题无关 - 切换主题时,Gin 引擎重新指向新主题的
html/目录