**新增功能:**
用户名编辑:输入框替换静态文本,白名单验证(中文/英文/数字/下划线/连字符),前端计数器(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
136 lines
5.5 KiB
Markdown
136 lines
5.5 KiB
Markdown
# 目录结构与分层规范
|
||
|
||
## 总览
|
||
|
||
```
|
||
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/` 子包
|
||
|
||
```go
|
||
// ✅ 正确:瘦控制器
|
||
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/` 目录
|