docs(guides): 完善框架文档并新增代码生成器说明

- 添加代码生成器完整使用说明文档,包含配置规则和自定义规则
- 新增 Common 工具类使用说明,介绍 Map/Slice/Obj 类型及转换函数
- 更新 QUICKSTART 文档中的配置项说明和数据库配置示例
- 完善请求参数获取方法,添加新版链式调用推荐用法
- 更新响应数据处理说明,包含错误码含义和自定义响应方法
- 优化中间件和 Session 操作的代码示例
- 修正路由路径参数获取的安全检查逻辑
- 更新 README 添加新文档链接索引
This commit is contained in:
hoteas 2026-01-22 22:13:05 +08:00
parent cf64276ab1
commit 8dac2aff66
4 changed files with 1211 additions and 30 deletions

View File

@ -20,6 +20,8 @@
| [快速上手指南](docs/QUICKSTART.md) | 5 分钟入门,安装配置、路由、中间件、基础数据库操作 |
| [HoTimeDB 使用说明](docs/HoTimeDB_使用说明.md) | 完整数据库 ORM 教程 |
| [HoTimeDB API 参考](docs/HoTimeDB_API参考.md) | 数据库 API 速查手册 |
| [Common 工具类](docs/Common_工具类使用说明.md) | Map/Slice/Obj 类型、类型转换、工具函数 |
| [代码生成器](docs/CodeGen_使用说明.md) | 自动 CRUD 代码生成、配置规则 |
## 安装

View File

@ -0,0 +1,468 @@
# HoTime 代码生成器使用说明
`code` 包提供了 HoTime 框架的自动代码生成功能,能够根据数据库表结构自动生成 CRUD 接口代码和配置文件。
## 目录
- [功能概述](#功能概述)
- [配置说明](#配置说明)
- [使用方法](#使用方法)
- [生成规则](#生成规则)
- [自定义规则](#自定义规则)
- [生成的代码结构](#生成的代码结构)
---
## 功能概述
代码生成器可以:
1. **自动读取数据库表结构** - 支持 MySQL 和 SQLite
2. **生成 CRUD 接口** - 增删改查、搜索、分页
3. **生成配置文件** - 表字段配置、菜单配置、权限配置
4. **智能字段识别** - 根据字段名自动识别类型和权限
5. **支持表关联** - 自动识别外键关系
---
## 配置说明
`config.json` 中配置代码生成:
```json
{
"codeConfig": [
{
"table": "admin",
"config": "config/admin.json",
"configDB": "config/adminDB.json",
"rule": "config/rule.json",
"name": "",
"mode": 0
}
]
}
```
### 配置项说明
| 配置项 | 必须 | 说明 |
|--------|------|------|
| `table` | ✅ | 用户表名,用于权限控制的基准表 |
| `config` | ✅ | 接口描述配置文件路径 |
| `configDB` | ❌ | 数据库结构配置输出路径,有则每次自动生成 |
| `rule` | ❌ | 字段规则配置文件,无则使用默认规则 |
| `name` | ❌ | 生成代码的包名和目录名,空则使用内嵌模式 |
| `mode` | ❌ | 0=内嵌代码模式1=生成代码模式 |
### 运行模式
- **mode=0内嵌模式**:不生成独立代码文件,使用框架内置的通用控制器
- **mode=1生成模式**:为每张表生成独立的 Go 控制器文件
---
## 使用方法
### 1. 基础配置
```json
{
"mode": 2,
"codeConfig": [
{
"table": "admin",
"config": "config/admin.json",
"rule": "config/rule.json",
"mode": 0
}
]
}
```
### 2. 启动应用
```go
package main
import (
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
)
func main() {
app := Init("config/config.json")
// 代码生成器在 Init 时自动执行
// 会读取数据库结构并生成配置
app.Run(Router{
// 路由配置
})
}
```
### 3. 开发模式
`config.json` 中设置 `"mode": 2`(开发模式)时:
- 自动读取数据库表结构
- 自动生成/更新配置文件
- 自动生成代码(如果 codeConfig.mode=1
---
## 生成规则
### 默认字段规则
代码生成器内置了一套默认的字段识别规则:
| 字段名 | 列表显示 | 新增 | 编辑 | 详情 | 类型 |
|--------|----------|------|------|------|------|
| `id` | ✅ | ❌ | ❌ | ✅ | number |
| `name` | ✅ | ✅ | ✅ | ✅ | text |
| `status` | ✅ | ✅ | ✅ | ✅ | select |
| `create_time` | ❌ | ❌ | ❌ | ✅ | time |
| `modify_time` | ✅ | ❌ | ❌ | ✅ | time |
| `password` | ❌ | ✅ | ✅ | ❌ | password |
| `image/img/avatar` | ❌ | ✅ | ✅ | ✅ | image |
| `file` | ❌ | ✅ | ✅ | ✅ | file |
| `content/info` | ❌ | ✅ | ✅ | ✅ | textArea |
| `parent_id` | ✅ | ✅ | ✅ | ✅ | number |
| `parent_ids/index` | ❌ | ❌ | ❌ | ❌ | index |
| `delete` | ❌ | ❌ | ❌ | ❌ | - |
### 数据类型映射
数据库字段类型自动映射:
| 数据库类型 | 生成类型 |
|------------|----------|
| `int`, `integer`, `float`, `double`, `decimal` | number |
| `char`, `varchar`, `text`, `blob` | text |
| `date`, `datetime`, `time`, `timestamp`, `year` | time |
### 字段备注解析
支持从数据库字段备注中提取信息:
```sql
-- 字段备注格式: 标签名:选项1-名称1,选项2-名称2 {提示信息}
-- 例如:
status TINYINT COMMENT '状态:0-禁用,1-启用 {用户账号状态}'
```
生成的配置:
```json
{
"name": "status",
"label": "状态",
"type": "select",
"ps": "用户账号状态",
"options": [
{"name": "禁用", "value": "0"},
{"name": "启用", "value": "1"}
]
}
```
---
## 自定义规则
### rule.json 配置
创建 `config/rule.json` 自定义字段规则:
```json
[
{
"name": "id",
"list": true,
"add": false,
"edit": false,
"info": true,
"must": false,
"strict": true,
"type": ""
},
{
"name": "status",
"list": true,
"add": true,
"edit": true,
"info": true,
"must": false,
"strict": false,
"type": "select"
},
{
"name": "user.special_field",
"list": true,
"add": true,
"edit": true,
"info": true,
"type": "text",
"strict": true
}
]
```
### 规则字段说明
| 字段 | 说明 |
|------|------|
| `name` | 字段名,支持 `表名.字段名` 格式精确匹配 |
| `list` | 是否在列表中显示 |
| `add` | 是否在新增表单中显示 |
| `edit` | 是否在编辑表单中显示 |
| `info` | 是否在详情中显示 |
| `must` | 是否必填 |
| `strict` | 是否严格匹配字段名false 则模糊匹配) |
| `type` | 字段类型(覆盖自动识别) |
### 字段类型
| 类型 | 说明 |
|------|------|
| `text` | 普通文本输入 |
| `textArea` | 多行文本 |
| `number` | 数字输入 |
| `select` | 下拉选择 |
| `time` | 时间选择器 |
| `unixTime` | Unix 时间戳 |
| `image` | 图片上传 |
| `file` | 文件上传 |
| `password` | 密码输入 |
| `money` | 金额(带格式化) |
| `index` | 索引字段(不显示) |
| `tree` | 树形选择 |
| `form` | 表单配置 |
| `auth` | 权限配置 |
---
## 生成的代码结构
### 内嵌模式 (mode=0)
不生成代码文件,使用框架内置控制器,只生成配置文件:
```
config/
├── admin.json # 接口配置
├── adminDB.json # 数据库结构配置(可选)
└── rule.json # 字段规则
```
### 生成模式 (mode=1)
生成独立的控制器代码:
```
admin/ # 生成的包目录
├── init.go # 包初始化和路由注册
├── user.go # user 表控制器
├── role.go # role 表控制器
└── ... # 其他表控制器
```
### 生成的控制器结构
```go
package admin
var userCtr = Ctr{
"info": func(that *Context) {
// 查询单条记录
},
"add": func(that *Context) {
// 新增记录
},
"update": func(that *Context) {
// 更新记录
},
"remove": func(that *Context) {
// 删除记录
},
"search": func(that *Context) {
// 搜索列表(分页)
},
}
```
---
## 配置文件结构
### admin.json 示例
```json
{
"name": "admin",
"label": "管理平台",
"menus": [
{
"label": "系统管理",
"name": "sys",
"icon": "Setting",
"menus": [
{
"label": "用户管理",
"table": "user",
"auth": ["show", "add", "delete", "edit", "info", "download"]
},
{
"label": "角色管理",
"table": "role",
"auth": ["show", "add", "delete", "edit", "info"]
}
]
}
],
"tables": {
"user": {
"label": "用户",
"table": "user",
"auth": ["show", "add", "delete", "edit", "info", "download"],
"columns": [
{"name": "id", "type": "number", "label": "ID"},
{"name": "name", "type": "text", "label": "用户名"},
{"name": "status", "type": "select", "label": "状态",
"options": [{"name": "禁用", "value": "0"}, {"name": "启用", "value": "1"}]}
],
"search": [
{"type": "search", "name": "keyword", "label": "请输入关键词"},
{"type": "search", "name": "daterange", "label": "时间段"}
]
}
}
}
```
---
## 外键关联
### 自动识别
代码生成器会自动识别 `_id` 结尾的字段作为外键:
```sql
-- user 表
CREATE TABLE user (
id INT PRIMARY KEY,
name VARCHAR(50),
role_id INT, -- 自动关联 role 表
org_id INT -- 自动关联 org 表
);
```
生成的配置会包含 `link``value` 字段:
```json
{
"name": "role_id",
"type": "number",
"label": "角色",
"link": "role",
"value": "name"
}
```
### 树形结构
`parent_id` 字段会被识别为树形结构的父级关联:
```json
{
"name": "parent_id",
"type": "number",
"label": "上级",
"link": "org",
"value": "name"
}
```
---
## 权限控制
### 数据权限
配置 `flow` 实现数据权限控制:
```json
{
"flow": {
"order": {
"table": "order",
"stop": false,
"sql": {
"user_id": "id"
}
}
}
}
```
- `stop`: 是否禁止修改该表
- `sql`: 数据过滤条件,`user_id = 当前用户.id`
### 操作权限
每张表可配置的权限:
| 权限 | 说明 |
|------|------|
| `show` | 查看列表 |
| `add` | 新增 |
| `edit` | 编辑 |
| `delete` | 删除 |
| `info` | 查看详情 |
| `download` | 下载导出 |
---
## 最佳实践
### 1. 开发流程
1. 设置 `config.json``mode: 2`(开发模式)
2. 设计数据库表结构,添加字段备注
3. 启动应用,自动生成配置
4. 检查生成的配置文件,按需调整
5. 生产环境改为 `mode: 0`
### 2. 字段命名规范
```sql
-- 推荐的命名方式
id -- 主键
name -- 名称
status -- 状态(自动识别为 select
create_time -- 创建时间
modify_time -- 修改时间
xxx_id -- 外键关联
parent_id -- 树形结构父级
avatar -- 头像(自动识别为 image
content -- 内容(自动识别为 textArea
```
### 3. 自定义扩展
如果默认规则不满足需求,可以:
1. 修改 `rule.json` 添加自定义规则
2. 使用 `mode=1` 生成代码后手动修改
3. 在生成的配置文件中直接调整字段属性
---
## 相关文档
- [快速上手指南](QUICKSTART.md)
- [HoTimeDB 使用说明](HoTimeDB_使用说明.md)
- [Common 工具类使用说明](Common_工具类使用说明.md)

View File

@ -0,0 +1,484 @@
# HoTime Common 工具类使用说明
`common` 包提供了 HoTime 框架的核心数据类型和工具函数,包括 `Map``Slice``Obj` 类型及丰富的类型转换函数。
## 目录
- [核心数据类型](#核心数据类型)
- [Map 类型](#map-类型)
- [Slice 类型](#slice-类型)
- [Obj 类型](#obj-类型)
- [类型转换函数](#类型转换函数)
- [工具函数](#工具函数)
- [错误处理](#错误处理)
---
## 核心数据类型
### Map 类型
`Map``map[string]interface{}` 的别名,提供了丰富的链式调用方法。
```go
import . "code.hoteas.com/golang/hotime/common"
// 创建 Map
data := Map{
"name": "张三",
"age": 25,
"score": 98.5,
"active": true,
"tags": Slice{"Go", "Web"},
}
```
#### 获取值方法
```go
// 获取字符串
name := data.GetString("name") // "张三"
// 获取整数
age := data.GetInt("age") // 25
age64 := data.GetInt64("age") // int64(25)
// 获取浮点数
score := data.GetFloat64("score") // 98.5
// 获取布尔值
active := data.GetBool("active") // true
// 获取嵌套 Map
info := data.GetMap("info") // 返回 Map 类型
// 获取 Slice
tags := data.GetSlice("tags") // 返回 Slice 类型
// 获取时间
createTime := data.GetTime("create_time") // 返回 *time.Time
// 获取原始值
raw := data.Get("name") // interface{}
```
#### 向上取整方法
```go
// 向上取整获取整数
ceilInt := data.GetCeilInt("score") // 99
ceilInt64 := data.GetCeilInt64("score") // int64(99)
ceilFloat := data.GetCeilFloat64("score") // 99.0
```
#### 操作方法
```go
// 添加/修改值
data.Put("email", "test@example.com")
// 删除值
data.Delete("email")
// 转换为 JSON 字符串
jsonStr := data.ToJsonString()
// 从 JSON 字符串解析
data.JsonToMap(`{"key": "value"}`)
```
#### 有序遍历
```go
// 按 key 字母顺序遍历
data.RangeSort(func(k string, v interface{}) bool {
fmt.Printf("%s: %v\n", k, v)
return false // 返回 true 则终止遍历
})
```
#### 转换为结构体
```go
type User struct {
Name string
Age int64
Score float64
}
var user User
data.ToStruct(&user) // 传入指针,字段名首字母大写匹配
```
---
### Slice 类型
`Slice``[]interface{}` 的别名,提供类似 Map 的链式调用方法。
```go
// 创建 Slice
list := Slice{
Map{"id": 1, "name": "Alice"},
Map{"id": 2, "name": "Bob"},
"text",
123,
}
```
#### 获取值方法
```go
// 按索引获取值(类型转换)
str := list.GetString(2) // "text"
num := list.GetInt(3) // 123
num64 := list.GetInt64(3) // int64(123)
f := list.GetFloat64(3) // 123.0
b := list.GetBool(3) // true (非0为true)
// 获取嵌套类型
item := list.GetMap(0) // Map{"id": 1, "name": "Alice"}
subList := list.GetSlice(0) // 尝试转换为 Slice
// 获取原始值
raw := list.Get(0) // interface{}
// 获取时间
t := list.GetTime(0) // *time.Time
```
#### 向上取整方法
```go
ceilInt := list.GetCeilInt(3)
ceilInt64 := list.GetCeilInt64(3)
ceilFloat := list.GetCeilFloat64(3)
```
#### 操作方法
```go
// 修改指定位置的值
list.Put(0, "new value")
// 转换为 JSON 字符串
jsonStr := list.ToJsonString()
```
---
### Obj 类型
`Obj` 是一个通用的对象包装器,用于链式类型转换,常用于 `Context` 方法的返回值。
```go
type Obj struct {
Data interface{} // 原始数据
Error // 错误信息
}
```
#### 使用示例
```go
obj := &Obj{Data: "123"}
// 链式类型转换
i := obj.ToInt() // 123
i64 := obj.ToInt64() // int64(123)
f := obj.ToFloat64() // 123.0
s := obj.ToStr() // "123"
b := obj.ToBool() // true
// 复杂类型转换
m := obj.ToMap() // 尝试转换为 Map
sl := obj.ToSlice() // 尝试转换为 Slice
arr := obj.ToMapArray() // 转换为 []Map
// 获取原始值
raw := obj.ToObj() // interface{}
// 获取时间
t := obj.ToTime() // *time.Time
// 向上取整
ceil := obj.ToCeilInt()
ceil64 := obj.ToCeilInt64()
ceilF := obj.ToCeilFloat64()
```
#### 在 Context 中的应用
```go
func handler(that *Context) {
// ReqData 返回 *Obj支持链式调用
userId := that.ReqData("user_id").ToInt()
name := that.ReqData("name").ToStr()
// Session 也返回 *Obj
adminId := that.Session("admin_id").ToInt64()
}
```
---
## 类型转换函数
`common` 包提供了一系列全局类型转换函数。
### 基础转换
```go
// 转字符串
str := ObjToStr(123) // "123"
str := ObjToStr(3.14) // "3.14"
str := ObjToStr(Map{"a": 1}) // JSON 格式字符串
// 转整数
i := ObjToInt("123") // 123
i64 := ObjToInt64("123") // int64(123)
// 转浮点数
f := ObjToFloat64("3.14") // 3.14
// 转布尔
b := ObjToBool(1) // true
b := ObjToBool(0) // false
// 转 Map
m := ObjToMap(`{"a": 1}`) // Map{"a": 1}
m := ObjToMap(someStruct) // 结构体转 Map
// 转 Slice
s := ObjToSlice(`[1, 2, 3]`) // Slice{1, 2, 3}
// 转 []Map
arr := ObjToMapArray(slice) // []Map
```
### 向上取整转换
```go
// 向上取整后转整数
ceil := ObjToCeilInt(3.2) // 4
ceil64 := ObjToCeilInt64(3.2) // int64(4)
ceilF := ObjToCeilFloat64(3.2) // 4.0
```
### 时间转换
```go
// 自动识别多种格式
t := ObjToTime("2024-01-15 10:30:00") // *time.Time
t := ObjToTime("2024-01-15") // *time.Time
t := ObjToTime(1705298400) // Unix 秒
t := ObjToTime(1705298400000) // Unix 毫秒
t := ObjToTime(1705298400000000) // Unix 微秒
```
### 字符串转换
```go
// 字符串转 Map
m := StrToMap(`{"key": "value"}`)
// 字符串转 Slice
s := StrToSlice(`[1, 2, 3]`)
// 字符串转 int
i, err := StrToInt("123")
// 字符串数组格式转换
jsonArr := StrArrayToJsonStr("a1,a2,a3") // "[a1,a2,a3]"
strArr := JsonStrToStrArray("[a1,a2,a3]") // ",a1,a2,a3,"
```
### 错误处理
所有转换函数支持可选的错误参数:
```go
var e Error
i := ObjToInt("abc", &e)
if e.GetError() != nil {
// 处理转换错误
}
```
---
## 工具函数
### 字符串处理
```go
// 字符串截取(支持中文)
str := Substr("Hello世界", 0, 7) // "Hello世"
str := Substr("Hello", -2, 2) // "lo" (负数从末尾计算)
// 首字母大写
upper := StrFirstToUpper("hello") // "Hello"
// 查找最后出现位置
idx := IndexLastStr("a.b.c", ".") // 3
// 字符串相似度Levenshtein 距离)
dist := StrLd("hello", "hallo", true) // 1 (忽略大小写)
```
### 时间处理
```go
// 时间转字符串
str := Time2Str(time.Now()) // "2024-01-15 10:30:00"
str := Time2Str(time.Now(), 1) // "2024-01"
str := Time2Str(time.Now(), 2) // "2024-01-15"
str := Time2Str(time.Now(), 3) // "2024-01-15 10"
str := Time2Str(time.Now(), 4) // "2024-01-15 10:30"
str := Time2Str(time.Now(), 5) // "2024-01-15 10:30:00"
// 特殊格式
str := Time2Str(time.Now(), 12) // "01-15"
str := Time2Str(time.Now(), 14) // "01-15 10:30"
str := Time2Str(time.Now(), 34) // "10:30"
str := Time2Str(time.Now(), 35) // "10:30:00"
```
### 加密与随机
```go
// MD5 加密
hash := Md5("password") // 32位小写MD5
// 随机数
r := Rand(3) // 3位随机数 (0-999)
r := RandX(10, 100) // 10-100之间的随机数
```
### 数学计算
```go
// 四舍五入保留小数
f := Round(3.14159, 2) // 3.14
f := Round(3.145, 2) // 3.15
```
### 深拷贝
```go
// 深拷贝 Map/Slice递归复制
original := Map{"a": Map{"b": 1}}
copied := DeepCopyMap(original).(Map)
// 修改副本不影响原始数据
copied.GetMap("a")["b"] = 2
// original["a"]["b"] 仍然是 1
```
---
## 错误处理
### Error 类型
```go
type Error struct {
Logger *logrus.Logger // 可选的日志记录器
error // 内嵌错误
}
```
### 使用示例
```go
var e Error
// 设置错误
e.SetError(errors.New("something wrong"))
// 获取错误
if err := e.GetError(); err != nil {
fmt.Println(err)
}
// 配合日志自动记录
e.Logger = logrusLogger
e.SetError(errors.New("will be logged"))
```
### 在类型转换中使用
```go
var e Error
data := Map{"count": "abc"}
count := data.GetInt("count", &e)
if e.GetError() != nil {
// 转换失败count = 0
fmt.Println("转换失败:", e.GetError())
}
```
---
## 最佳实践
### 1. 链式调用处理请求数据
```go
func handler(that *Context) {
// 推荐:使用 Obj 链式调用
userId := that.ReqData("user_id").ToInt()
page := that.ReqData("page").ToInt()
if page < 1 {
page = 1
}
// 处理 Map 数据
user := that.Db.Get("user", "*", Map{"id": userId})
if user != nil {
name := user.GetString("name")
age := user.GetInt("age")
}
}
```
### 2. 安全的类型转换
```go
// 带错误检查的转换
var e Error
data := someMap.GetInt("key", &e)
if e.GetError() != nil {
// 使用默认值
data = 0
}
// 简单场景直接转换(失败返回零值)
data := someMap.GetInt("key") // 失败返回 0
```
### 3. 处理数据库查询结果
```go
// 查询返回 Map
user := that.Db.Get("user", "*", Map{"id": 1})
if user != nil {
name := user.GetString("name")
createTime := user.GetTime("create_time")
}
// 查询返回 []Map
users := that.Db.Select("user", "*", Map{"status": 1})
for _, u := range users {
fmt.Printf("ID: %d, Name: %s\n", u.GetInt("id"), u.GetString("name"))
}
```
---
## 相关文档
- [快速上手指南](QUICKSTART.md)
- [HoTimeDB 使用说明](HoTimeDB_使用说明.md)
- [代码生成器使用说明](CodeGen_使用说明.md)

View File

@ -52,7 +52,8 @@ func main() {
"port": "3306",
"name": "your_database",
"user": "root",
"password": "your_password"
"password": "your_password",
"prefix": ""
}
},
"cache": {
@ -65,13 +66,98 @@ func main() {
}
```
| 配置项 | 说明 |
|--------|------|
| `port` | 服务端口 |
| `mode` | 0=生产, 1=测试, 2=开发(输出SQL) |
| `tpt` | 静态文件目录 |
| `db` | 数据库配置 |
| `cache` | 缓存配置 |
### 配置项说明
| 配置项 | 默认值 | 说明 |
|--------|--------|------|
| `port` | 80 | HTTP 服务端口0 为不启用 |
| `tlsPort` | - | HTTPS 端口,需配合 tlsCert/tlsKey |
| `tlsCert` | - | HTTPS 证书路径 |
| `tlsKey` | - | HTTPS 密钥路径 |
| `mode` | 0 | 0=生产, 1=测试, 2=开发(输出SQL) |
| `tpt` | tpt | 静态文件目录 |
| `sessionName` | HOTIME | Session Cookie 名称 |
| `modeRouterStrict` | false | 路由大小写敏感false=忽略大小写 |
| `crossDomain` | - | 跨域设置,空=不开启auto=智能开启,或指定域名 |
| `logFile` | - | 日志文件路径,如 `logs/20060102.txt` |
| `logLevel` | 0 | 日志等级0=关闭1=打印 |
| `webConnectLogShow` | true | 是否显示访问日志 |
| `defFile` | ["index.html"] | 目录默认访问文件 |
### 数据库配置
```json
{
"db": {
"mysql": {
"host": "127.0.0.1",
"port": "3306",
"name": "database_name",
"user": "root",
"password": "password",
"prefix": "app_",
"slave": {
"host": "127.0.0.1",
"port": "3306",
"name": "database_name",
"user": "root",
"password": "password"
}
},
"sqlite": {
"path": "config/data.db",
"prefix": ""
}
}
}
```
> MySQL 配置 `slave` 项即启用主从读写分离
### 缓存配置
```json
{
"cache": {
"memory": {
"db": true,
"session": true,
"timeout": 7200
},
"redis": {
"host": "127.0.0.1",
"port": 6379,
"password": "",
"db": true,
"session": true,
"timeout": 1296000
},
"db": {
"db": true,
"session": true,
"timeout": 2592000
}
}
}
```
缓存优先级: **Memory > Redis > DB**,自动穿透与回填
### 错误码配置
```json
{
"error": {
"1": "内部系统异常",
"2": "访问权限异常",
"3": "请求参数异常",
"4": "数据处理异常",
"5": "数据结果异常"
}
}
```
> 自定义错误码建议从 10 开始
## 路由系统
@ -89,31 +175,134 @@ appIns.Run(Router{
})
```
### 获取请求参数
### 路由路径
```go
// 获取路由信息
module := that.RouterString[0] // 模块
controller := that.RouterString[1] // 控制器
action := that.RouterString[2] // 方法
// 完整请求路径
fullPath := that.HandlerStr // 如 /app/user/login
```
## 请求参数获取
### 新版推荐方法(支持链式调用)
```go
// 获取 URL 查询参数 (?id=1)
id := that.ReqParam("id").ToInt()
name := that.ReqParam("name").ToStr()
// 获取表单参数 (POST form-data / x-www-form-urlencoded)
username := that.ReqForm("username").ToStr()
age := that.ReqForm("age").ToInt()
// 获取 JSON Body 参数 (POST application/json)
data := that.ReqJson("data").ToMap()
items := that.ReqJson("items").ToSlice()
// 统一获取(自动判断来源,优先级: JSON > Form > URL
userId := that.ReqData("user_id").ToInt()
status := that.ReqData("status").ToStr()
```
### 类型转换方法
```go
obj := that.ReqData("key")
obj.ToStr() // 转字符串
obj.ToInt() // 转 int
obj.ToInt64() // 转 int64
obj.ToFloat64() // 转 float64
obj.ToBool() // 转 bool
obj.ToMap() // 转 Map
obj.ToSlice() // 转 Slice
obj.Data // 获取原始值interface{}
```
### 文件上传
```go
// 单文件上传
file, header, err := that.ReqFile("avatar")
if err == nil {
defer file.Close()
// header.Filename - 文件名
// header.Size - 文件大小
}
// 多文件上传(批量)
files, err := that.ReqFiles("images")
if err == nil {
for _, fh := range files {
file, _ := fh.Open()
defer file.Close()
// 处理每个文件
}
}
```
### 传统方法(兼容)
```go
// GET/POST 参数
name := that.Req.FormValue("name")
// JSON Body
that.Req.ParseForm()
data := that.Req.PostForm
// URL 参数
id := that.Req.URL.Query().Get("id")
// 获取路径参数
module := that.RouterPath[0] // 模块
controller := that.RouterPath[1] // 控制器
action := that.RouterPath[2] // 方法
// 请求头
token := that.Req.Header.Get("Authorization")
```
### 响应数据
## 响应数据
### Display 方法
```go
// 标准 JSON 响应
that.Display(0, Map{"data": "value"}) // 成功 code=0
that.Display(1, "错误信息") // 失败 code=1
// 成功响应 (status=0)
that.Display(0, Map{"user": user, "token": token})
// 输出: {"status":0, "result":{"user":..., "token":...}}
// 错误响应 (status>0)
that.Display(1, "系统内部错误")
// 输出: {"status":1, "result":{"type":"内部系统异常", "msg":"系统内部错误"}, "error":{...}}
that.Display(2, "请先登录")
// 输出: {"status":2, "result":{"type":"访问权限异常", "msg":"请先登录"}, "error":{...}}
that.Display(3, "参数不能为空")
// 输出: {"status":3, "result":{"type":"请求参数异常", "msg":"参数不能为空"}, "error":{...}}
```
### 错误码含义
| 错误码 | 类型 | 使用场景 |
|--------|------|----------|
| 0 | 成功 | 请求成功 |
| 1 | 内部系统异常 | 环境配置、文件权限等基础运行环境错误 |
| 2 | 访问权限异常 | 未登录或登录异常 |
| 3 | 请求参数异常 | 参数不足、类型错误等 |
| 4 | 数据处理异常 | 数据库操作或第三方请求返回异常 |
| 5 | 数据结果异常 | 无法返回要求的格式 |
### 自定义响应
```go
// 自定义 Header
that.Resp.Header().Set("Content-Type", "application/json")
// 直接写入
that.Resp.Write([]byte("raw data"))
// 自定义响应函数
that.RespFunc = func() {
// 自定义响应逻辑
}
```
## 中间件
@ -121,6 +310,11 @@ that.Resp.Write([]byte("raw data"))
```go
// 全局中间件(请求拦截)
appIns.SetConnectListener(func(that *Context) bool {
// 放行登录接口
if len(that.RouterString) >= 3 && that.RouterString[2] == "login" {
return false
}
// 检查登录状态
if that.Session("user_id").Data == nil {
that.Display(2, "请先登录")
@ -135,9 +329,13 @@ appIns.SetConnectListener(func(that *Context) bool {
```go
// Session 操作
that.Session("user_id", 123) // 设置
userId := that.Session("user_id") // 获取
userId := that.Session("user_id") // 获取 *Obj
that.Session("user_id", nil) // 删除
// 链式获取
id := that.Session("user_id").ToInt64()
name := that.Session("username").ToStr()
// 通用缓存
that.Cache("key", "value") // 设置
data := that.Cache("key") // 获取
@ -160,6 +358,12 @@ user := that.Db.Get("user", "*", Map{"id": 1})
// 插入
id := that.Db.Insert("user", Map{"name": "test", "age": 18})
// 批量插入
affected := that.Db.Inserts("user", []Map{
{"name": "user1", "age": 20},
{"name": "user2", "age": 25},
})
// 更新
rows := that.Db.Update("user", Map{"name": "new"}, Map{"id": 1})
@ -205,6 +409,18 @@ success := that.Db.Action(func(tx db.HoTimeDB) bool {
> **更多数据库操作**:参见 [HoTimeDB 使用说明](HoTimeDB_使用说明.md)
## 日志记录
```go
// 创建操作日志(自动插入 logs 表)
that.Log = Map{
"type": "login",
"action": "用户登录",
"data": Map{"phone": phone},
}
// 框架会自动添加 time, admin_id/user_id, ip 等字段
```
## 扩展功能
| 功能 | 路径 | 说明 |
@ -233,7 +449,7 @@ func main() {
// 登录检查中间件
appIns.SetConnectListener(func(that *Context) bool {
// 放行登录接口
if that.RouterPath[2] == "login" {
if len(that.RouterString) >= 3 && that.RouterString[2] == "login" {
return false
}
if that.Session("user_id").Data == nil {
@ -247,16 +463,21 @@ func main() {
"api": {
"user": {
"login": func(that *Context) {
phone := that.Req.FormValue("phone")
password := that.Req.FormValue("password")
phone := that.ReqData("phone").ToStr()
password := that.ReqData("password").ToStr()
if phone == "" || password == "" {
that.Display(3, "手机号和密码不能为空")
return
}
user := that.Db.Get("user", "*", Map{
"phone": phone,
"password": password,
"password": Md5(password),
})
if user == nil {
that.Display(1, "账号或密码错误")
that.Display(3, "账号或密码错误")
return
}
@ -271,15 +492,15 @@ func main() {
},
"list": func(that *Context) {
page := that.Req.FormValue("page")
if page == "" {
page = "1"
page := that.ReqData("page").ToInt()
if page == 0 {
page = 1
}
users := that.Db.Table("user").
Where("status", 1).
Order("id DESC").
Page(ObjToInt(page), 10).
Page(page, 10).
Select("id,name,phone,created_at")
total := that.Db.Count("user", Map{"status": 1})
@ -287,8 +508,14 @@ func main() {
that.Display(0, Map{
"list": users,
"total": total,
"page": page,
})
},
"logout": func(that *Context) {
that.Session("user_id", nil)
that.Display(0, "退出成功")
},
},
},
})