docs(guides): 完善框架文档并新增代码生成器说明
- 添加代码生成器完整使用说明文档,包含配置规则和自定义规则 - 新增 Common 工具类使用说明,介绍 Map/Slice/Obj 类型及转换函数 - 更新 QUICKSTART 文档中的配置项说明和数据库配置示例 - 完善请求参数获取方法,添加新版链式调用推荐用法 - 更新响应数据处理说明,包含错误码含义和自定义响应方法 - 优化中间件和 Session 操作的代码示例 - 修正路由路径参数获取的安全检查逻辑 - 更新 README 添加新文档链接索引
This commit is contained in:
parent
cf64276ab1
commit
8dac2aff66
@ -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 代码生成、配置规则 |
|
||||
|
||||
## 安装
|
||||
|
||||
|
||||
468
docs/CodeGen_使用说明.md
Normal file
468
docs/CodeGen_使用说明.md
Normal 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)
|
||||
484
docs/Common_工具类使用说明.md
Normal file
484
docs/Common_工具类使用说明.md
Normal 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)
|
||||
@ -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, "退出成功")
|
||||
},
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user