Files
hotime/docs/ROADMAP_改进规划.md
T
hoteas 6b689f3a1b chore(logging): 更新日志重定向与捕获功能
- 在 .gitignore 中添加调试日志文件的忽略规则,避免不必要的调试信息被提交
- 修改 application.go 中的 stdout 重定向逻辑,使用 log.CaptureStream 以支持更灵活的日志捕获
- 更新 README 文档,增加对日志重定向功能的说明
2026-07-13 07:45:51 +08:00

282 lines
8.0 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# HoTime 改进规划
> 相关规范:[DatabaseDesign](DatabaseDesign_数据库设计规范.md) · [CodeGen](CodeGen_代码生成.md) · [Testing](Testing_API测试框架.md) · [文档索引](README.md)
本文档记录 HoTime 框架的待改进项和设计思考,供后续版本迭代参考。
---
## 一、备注语法优化
### 现状
当前字段备注语法使用空格、冒号、大括号组合:
```sql
`status` int COMMENT '状态:0-正常,1-异常 这是数据库备注{这是前端提示}'
```
### 问题
- 多种分隔符混用,不够直观
- 显示名称如需包含特殊字符可能冲突
### 改进方向
使用 `|` 作为统一分隔符,保持干净清爽:
```sql
-- 方案 A:简洁直观
`status` int COMMENT '状态|0-正常,1-异常|请选择状态'
-- 解析:显示名称|选项|提示
-- 方案 B:保持兼容,空格后内容仍作为数据库备注
`status` int COMMENT '状态|0-正常,1-异常|请选择状态 这是数据库备注'
```
### 待确认
- [ ] 是否需要保留空格后的数据库备注功能
- [ ] 如果只有提示没有选项,如何表示(如 `名称||请输入名称`
- [ ] 是否向后兼容旧语法
---
## 二、SQLite 备注支持
### 现状
SQLite 不支持表/字段备注,需要手动在配置文件中设置。
### 问题
SQLite 项目配置工作量大,不够"快速开发"。
### 待探索方案
1. **配置文件方案(当前)**:通过 admin.json 手动配置,学习成本可接受
2. **轻量级方案**:考虑是否有更简单的替代方案,但不增加复杂度
### 暂时结论
当前方案虽不完美,但符合"简单好用"原则,暂不改动。
---
## 三、软删除支持
### 现状
框架暂不支持软删除。
### 设计考量
软删除存在以下问题:
- 数据安全性:软删除的数据仍可被恢复或误用
- 查询复杂度:所有查询需要额外过滤条件
- 存储膨胀:删除的数据持续占用空间
### 待探索方案
1. **可选软删除**:通过配置决定某张表是否启用软删除
2. **日志表方案**:独立的操作日志表,只写不删不改
- 记录所有增删改操作
- 原表正常物理删除
- 需要时可从日志恢复
3. **归档表方案**:删除时移动到归档表
### 倾向方案
日志表方案更符合数据安全和审计需求:
```sql
CREATE TABLE `_operation_log` (
`id` int AUTO_INCREMENT,
`table_name` varchar(100) COMMENT '操作表名',
`record_id` int COMMENT '记录ID',
`operation` varchar(20) COMMENT '操作类型:insert,update,delete',
`old_data` text COMMENT '操作前数据(JSON',
`new_data` text COMMENT '操作后数据(JSON',
`operator_id` int COMMENT '操作人ID',
`operator_table` varchar(100) COMMENT '操作人表名',
`create_time` datetime COMMENT '操作时间',
PRIMARY KEY (`id`)
) COMMENT='操作日志';
```
### 待确认
- [ ] 是否所有表都记录日志
- [ ] 日志保留策略(永久/定期归档)
- [ ] 是否提供恢复接口
---
## 四、多对多关联表增强
### 现状
关联表(如 `user_role`)按普通表处理,生成标准 CRUD。
### 可能的增强
1. **自动识别**:只有两个 `_id` 外键的表识别为关联表
2. **专用接口**:生成关联管理接口(批量绑定/解绑)
3. **级联查询**:自动生成带关联数据的查询
### 待确认
- [ ] 是否需要此功能
- [ ] 如何保持简单性
---
## 五、自动填充字段扩展
### 现状
自动填充:
- `create_time`:新增时自动填充当前时间
- `modify_time`:新增/编辑时自动填充当前时间
### 可能的扩展
| 字段 | 填充时机 | 填充内容 |
|------|----------|----------|
| create_by | 新增 | 当前用户ID |
| modify_by | 新增/编辑 | 当前用户ID |
| create_ip | 新增 | 客户端IP |
### 待确认
- [ ] 是否需要扩展
- [ ] 字段命名规范
---
## 六、版本控制/乐观锁
### 现状
`version` 字段规则存在但未启用。
### 待确认
- [ ] 是否需要支持乐观锁
- [ ] 如果不需要,是否从默认规则中移除
---
---
## 七、测试框架:响应字段断言(部分匹配)
### 现状
`Post` / `Get` 等终端方法目前只支持断言响应体中的 `status``msg` 字段:
```go
a.JSON(Map{"phone": phone, "password": "123456"}).Post("正常登录", 0)
a.JSON(Map{"phone": phone, "password": "123456"}).Post("密码为空", 4, "用户名或密码不能为空")
```
如需验证响应 `result` 内部的具体字段,只能手动写:
```go
resp := a.JSON(Map{...}).Post("查询列表", 0)
if resp.GetBody().GetMap("result").GetInt("total") != 10 {
resp.Fail("total 应为 10")
}
```
这种写法冗长、可读性差,且失败时无法显示期望值与实际值的对比。
### 改进方向
在终端方法签名中增加**可选的响应字段部分匹配参数**:
```go
// 现有签名(不变)
func (c *ApiCase) Post(desc string, status int, msg ...string) *ApiResponse
// 新增重载或扩展参数,支持传入期望的 result 字段(Map 类型)
a.JSON(Map{...}).Post("查询列表", 0, Map{"total": 10})
a.JSON(Map{...}).Post("查询列表", 0, Map{"total": 10, "list": notEmpty})
a.JSON(Map{...}).Post("正常登录", 0, Map{"token": notEmpty})
```
### 核心设计原则
**部分匹配,而非精确匹配**
- 只验证期望 Map 中列出的字段,响应中额外的字段不报错
- 避免因响应新增字段导致测试频繁失败,降低维护成本
- 精确匹配适合快照测试场景,但与"快速精准"目标相悖,不引入
### 支持的断言值类型
| 断言值 | 含义 | 示例 |
|--------|------|------|
| 具体值(string/int/bool | 字段等于该值 | `Map{"total": 10}` |
| `notEmpty`(特殊常量) | 字段存在且非空/非零 | `Map{"token": notEmpty}` |
| 嵌套 Map | 递归部分匹配子对象 | `Map{"user": Map{"id": 1}}` |
### 预期写法
```go
// 断言 total 值
a.Query(Map{"page": "1"}).Get("查询列表", 0, Map{"total": 5})
// 断言 token 字段存在且非空(不关心具体值)
resp := a.JSON(Map{"phone": "138...", "password": "123456"}).Post("正常登录", 0, Map{"token": notEmpty})
// 组合:status + msg + result 字段三合一
a.JSON(Map{"phone": ""}).Post("手机号为空", 4, "手机号不能为空") // 只验 status+msg
a.JSON(Map{"phone": "138..."}).Post("正常注册", 0, Map{"id": notEmpty}) // 验 status + result.id
```
### 失败输出格式
失败时应显示期望值和实际值对比,便于快速定位:
```
--- FAIL: TestApi/app/user/login/正常登录
期望 result.token 非空,实际值为 ""
期望 result.total = 10,实际值为 0
```
### 实现要点
- 参数类型可设计为 `...interface{}`,兼容现有 `msg ...string` 的调用方式,通过类型断言区分
- `notEmpty` 可定义为框架内的特殊常量(如 `var notEmpty = struct{}{}`
- 断言逻辑复用 `ApiResponse` 已有的 `GetBody()` 链路,递归遍历期望 Map
### 待确认
- [ ] 终端方法签名如何兼容:新增第四参数 `result Map`,还是利用 `...interface{}` 统一
- [ ] `notEmpty` 常量的导出名称和定义位置
- [ ] 嵌套 Map 递归断言是否纳入首批实现范围
- [ ] 失败输出是否需要完整响应体 dump
---
## 改进优先级
| 优先级 | 改进项 | 状态 |
|--------|--------|------|
| 高 | 备注语法优化(`\|` 分隔符) | 待设计 |
| 高 | 测试框架:响应字段部分匹配断言 | 待实现 |
| 中 | 软删除/日志表支持 | 待设计 |
| 低 | 多对多关联表增强 | 待评估 |
| 低 | 自动填充字段扩展 | 待评估 |
---
## 更新记录
| 日期 | 内容 |
|------|------|
| 2026-01-24 | 初始版本,记录分析结果和待改进项 |
| 2026-03-15 | 新增测试框架响应字段断言改进规划(七) |
| 2026-03-20 | 完成达梦(DM8)数据库完整支持:Dialect 适配(MERGE INTO Upsert、双引号标识符)、LastInsertId、保留字自动引号、schema 正确处理、代码生成器 DM 兼容、缓存表自动建表、测试框架 DM 适配 |