247 lines
8.3 KiB
Plaintext
247 lines
8.3 KiB
Plaintext
# MarketingSystemDataTool 项目规则
|
||
|
||
## 项目概述
|
||
|
||
这是一个营销系统和易码通数据工具,提供数据导出、模板管理、元数据查询等功能。
|
||
|
||
### 技术栈
|
||
- **后端**: Go 1.21,使用标准库 `net/http`,不使用第三方 Web 框架
|
||
- **前端**: Vue 3 (通过 CDN 引入) + Element Plus
|
||
- **数据库**: MySQL,支持多数据源(Marketing、YMT、Meta)
|
||
- **导出格式**: CSV、Excel (使用 excelize/v2)
|
||
- **配置**: YAML 配置文件 + 环境变量覆盖
|
||
|
||
## 项目结构
|
||
|
||
```
|
||
server/ # Go 后端代码
|
||
├── cmd/server/ # 主程序入口
|
||
├── internal/ # 内部包
|
||
│ ├── api/ # HTTP 路由和处理器
|
||
│ ├── config/ # 配置管理
|
||
│ ├── db/ # 数据库连接和池管理
|
||
│ ├── exporter/ # 数据导出逻辑
|
||
│ ├── logging/ # 日志工具
|
||
│ ├── models/ # 数据模型
|
||
│ ├── repo/ # 数据仓库层
|
||
│ ├── schema/ # 数据库模式定义
|
||
│ └── ymtcrypto/ # 加密工具
|
||
web/ # 前端静态文件
|
||
config/ # 非敏感配置
|
||
scripts/ # 开发和运维脚本
|
||
```
|
||
|
||
## Go 代码规范
|
||
|
||
### 架构模式
|
||
|
||
1. **Handler 函数模式**: 每个 API 端点使用 `Handler` 函数返回 `http.Handler`
|
||
```go
|
||
func ExportsHandler(meta, marketing, ymt *sql.DB) http.Handler {
|
||
api := &ExportsAPI{meta: meta, marketing: marketing, ymt: ymt}
|
||
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||
// 路由逻辑
|
||
})
|
||
}
|
||
```
|
||
|
||
2. **中间件链**: 使用函数式中间件,顺序为 `withAccess` -> `withTrace` -> Handler
|
||
```go
|
||
mux.Handle("/api/exports", withAccess(withTrace(ExportsHandler(...))))
|
||
```
|
||
|
||
3. **Context 传递**: 使用 `context.Context` 传递 trace_id、SQL、请求元数据等
|
||
- `TraceIDFrom(r)` - 获取 trace_id
|
||
- `WithSQL(r, sql)` - 设置 SQL 到 context
|
||
- `SQLFrom(r)` - 从 context 获取 SQL
|
||
- `MetaFrom(r)` - 获取请求元数据
|
||
|
||
### 代码风格
|
||
|
||
1. **标准库优先**: 优先使用 Go 标准库,避免引入不必要的第三方依赖
|
||
2. **包命名**: 使用小写单数形式,如 `api`, `config`, `db`
|
||
3. **函数命名**:
|
||
- Handler 函数使用 `XxxHandler` 命名
|
||
- 中间件函数使用 `withXxx` 命名
|
||
- 工具函数使用驼峰命名
|
||
4. **错误处理**:
|
||
- API 错误使用 `fail(w, r, status, msg)` 返回统一格式
|
||
- 成功响应使用 `ok(w, r, data)` 返回统一格式
|
||
- 启动错误使用 `log.Fatal()`
|
||
5. **数据库操作**:
|
||
- 使用 `database/sql` 标准库
|
||
- 支持连接池配置(通过环境变量)
|
||
- 多数据库实例:`metaDB` (模板/任务), `marketingDB` (营销数据), `ymtDB` (易码通数据)
|
||
|
||
### 响应格式
|
||
|
||
统一使用以下 JSON 响应结构:
|
||
```go
|
||
type resp struct {
|
||
Code int `json:"code"` // 0 表示成功,非 0 表示错误
|
||
Msg string `json:"msg"` // 消息
|
||
Data interface{} `json:"data"` // 数据
|
||
TraceID string `json:"trace_id"` // 追踪 ID
|
||
}
|
||
```
|
||
|
||
### 日志规范
|
||
|
||
1. **结构化日志**: 使用 `logging.JSON()` 记录结构化日志
|
||
2. **访问日志**: 在 `withAccess` 中间件中自动记录,包含 method、path、status、duration 等
|
||
3. **错误日志**: 在 `fail()` 函数中记录错误,包含 trace_id、文件位置、SQL(如果有)
|
||
4. **日志格式**: JSON 格式,包含 level、ts、trace_id、method、path 等字段
|
||
|
||
## API 设计规范
|
||
|
||
### 路由规则
|
||
|
||
1. **RESTful 风格**:
|
||
- `GET /api/templates` - 列表
|
||
- `POST /api/templates` - 创建
|
||
- `GET /api/templates/{id}` - 详情
|
||
- `PATCH /api/templates/{id}` - 更新
|
||
- `DELETE /api/templates/{id}` - 删除
|
||
|
||
2. **路径处理**: 使用 `strings.TrimPrefix()` 处理路径前缀,支持带或不带尾部斜杠
|
||
|
||
3. **CORS 支持**: 所有 API 通过 `withAccess` 中间件自动处理 CORS
|
||
|
||
### 请求处理
|
||
|
||
1. **参数解析**:
|
||
- GET 请求从 `r.URL.Query()` 获取参数
|
||
- POST 请求从 `r.Body` 读取 JSON
|
||
- 使用 `json.Decoder` 或 `json.Unmarshal` 解析
|
||
|
||
2. **用户身份**: 通过 `userId` 查询参数传递(支持 `userId`, `userid`, `user_id`)
|
||
|
||
3. **权限检查**: 在 Handler 内部根据业务逻辑进行权限验证
|
||
|
||
## 数据库操作规范
|
||
|
||
### 连接管理
|
||
|
||
1. **多数据源**:
|
||
- `metaDB`: 存储模板和任务元数据
|
||
- `marketingDB`: 营销系统数据
|
||
- `ymtDB`: 易码通数据
|
||
|
||
2. **连接池配置**: 通过环境变量配置(`YMT_DB_*`, `MARKETING_DB_*`, `YMT_TEST_DB_*`)
|
||
|
||
3. **DSN 格式**: 使用 `DB.DSN()` 方法生成,包含 `parseTime=True&loc=Local&charset=utf8mb4`
|
||
|
||
### Schema 抽象
|
||
|
||
1. **Schema 接口**: 使用 `schema.Schema` 接口抽象不同数据源的差异
|
||
2. **字段映射**: 通过 `MapField()` 方法将逻辑字段名映射到物理字段名
|
||
3. **表名映射**: 通过 `TableName()` 方法获取实际表名
|
||
4. **JOIN 构建**: 通过 `BuildJoins()` 方法构建必要的 JOIN 语句
|
||
|
||
### SQL 构建
|
||
|
||
1. **SQL Builder**: 使用 `exporter.BuildSQL()` 构建查询 SQL
|
||
2. **白名单机制**: 字段访问需要通过白名单验证
|
||
3. **参数化查询**: 使用 `?` 占位符,防止 SQL 注入
|
||
4. **时间过滤**: 营销系统的 order 表必须提供 `create_time_between` 过滤条件
|
||
|
||
## 配置管理规范
|
||
|
||
### 配置文件
|
||
|
||
1. **配置文件位置**:
|
||
- 优先使用 `server/config.yaml`
|
||
- 备选 `config.yaml`
|
||
|
||
2. **配置结构**:
|
||
```yaml
|
||
app:
|
||
port: "8077"
|
||
marketing_db:
|
||
host: "..."
|
||
port: "..."
|
||
user: "..."
|
||
password: "..."
|
||
name: "..."
|
||
ymt_db: {...}
|
||
ymt_test_db: {...}
|
||
ymt_key_decrypt_key_b64: "..."
|
||
```
|
||
|
||
3. **环境变量覆盖**:
|
||
- 支持通过环境变量覆盖配置
|
||
- 环境变量优先级高于配置文件
|
||
- 支持 `.env.local` 文件(不提交到版本控制)
|
||
|
||
## 前端代码规范
|
||
|
||
### Vue 3 使用
|
||
|
||
1. **CDN 引入**: 使用 CDN 方式引入 Vue 3 和 Element Plus,不打包
|
||
2. **组合式 API**: 使用 `setup()` 和 `reactive()` 进行状态管理
|
||
3. **API 调用**: 使用 `fetch()` 调用后端 API,base URL 为 `http://localhost:8077`
|
||
4. **用户 ID**: 从 URL 查询参数获取 `userId`
|
||
|
||
### 代码组织
|
||
|
||
1. **单文件应用**: 主要逻辑在 `web/main.js` 中
|
||
2. **样式分离**: 样式在 `web/styles.css` 中
|
||
3. **静态资源**: 第三方库在 `web/vendor/` 目录
|
||
|
||
## 错误处理规范
|
||
|
||
1. **API 错误**:
|
||
- HTTP 状态码:400 (Bad Request), 404 (Not Found), 500 (Internal Server Error)
|
||
- 响应体:`{code: 1, msg: "错误消息", data: null, trace_id: "..."}`
|
||
|
||
2. **日志记录**:
|
||
- 所有错误都记录到日志,包含 trace_id、文件位置、SQL(如果有)
|
||
- 使用 `failCat()` 记录分类错误
|
||
|
||
3. **错误消息**: 使用中文错误消息,清晰描述问题
|
||
|
||
## 导出功能规范
|
||
|
||
1. **导出格式**: 支持 CSV 和 Excel (xlsx)
|
||
2. **流式处理**: 使用 `exporter.Stream()` 进行流式导出,避免内存溢出
|
||
3. **文件存储**: 导出文件存储在 `storage/export/` 目录
|
||
4. **异步处理**: 导出任务异步执行,通过 job 状态跟踪进度
|
||
|
||
## 安全规范
|
||
|
||
1. **访问控制**: 通过 `withAccess` 中间件处理 CORS
|
||
2. **SQL 注入防护**: 使用参数化查询,不拼接 SQL 字符串
|
||
3. **字段白名单**: 字段访问必须通过白名单验证
|
||
4. **敏感信息**: 密码等敏感信息不记录到日志
|
||
|
||
## 开发建议
|
||
|
||
1. **新增 API**:
|
||
- 在 `internal/api/` 下创建新的 handler 文件
|
||
- 在 `router.go` 中注册路由
|
||
- 使用统一的响应格式和错误处理
|
||
|
||
2. **新增数据源**:
|
||
- 在 `internal/schema/` 下实现 `Schema` 接口
|
||
- 在 `schema.Get()` 中添加路由逻辑
|
||
|
||
3. **代码审查要点**:
|
||
- 是否使用参数化查询
|
||
- 是否通过字段白名单验证
|
||
- 是否使用统一的响应格式
|
||
- 是否记录必要的日志
|
||
|
||
4. **性能优化**:
|
||
- 大数据量导出使用流式处理
|
||
- 合理使用数据库连接池
|
||
- 避免 N+1 查询问题
|
||
|
||
## 注意事项
|
||
|
||
1. **不要使用第三方 Web 框架**: 坚持使用标准库 `net/http`
|
||
2. **保持代码简洁**: 避免过度抽象,保持代码可读性
|
||
3. **统一错误处理**: 所有错误都通过 `fail()` 函数返回
|
||
4. **日志完整性**: 确保关键操作都有日志记录
|
||
5. **配置灵活性**: 支持配置文件和环境变量两种方式
|
||
|