- 在 HoTimeCache 中新增 SessionsGet、SessionsSet 和 SessionsDelete 方法,支持批量获取、设置和删除 Session 缓存 - 优化缓存逻辑,减少数据库写入次数,提升性能 - 更新文档,详细说明批量操作的使用方法和性能对比 - 添加调试日志记录,便于追踪批量操作的执行情况
16 KiB
16 KiB
HoTime 快速上手指南
5 分钟入门 HoTime 框架。
安装
go get code.hoteas.com/golang/hotime
最小示例
package main
import (
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
)
func main() {
appIns := Init("config/config.json")
appIns.Run(Router{
"app": {
"test": {
"hello": func(that *Context) {
that.Display(0, Map{"message": "Hello World"})
},
},
},
})
}
访问: http://localhost:8081/app/test/hello
配置文件
创建 config/config.json:
{
"port": "8081",
"mode": 2,
"sessionName": "HOTIME",
"tpt": "tpt",
"defFile": ["index.html", "index.htm"],
"db": {
"mysql": {
"host": "localhost",
"port": "3306",
"name": "your_database",
"user": "root",
"password": "your_password",
"prefix": ""
}
},
"cache": {
"memory": {
"db": true,
"session": true,
"timeout": 7200
}
}
}
配置项说明
| 配置项 | 默认值 | 说明 |
|---|---|---|
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"] | 目录默认访问文件 |
数据库配置
{
"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项即启用主从读写分离
缓存配置
{
"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,
"history": false,
"mode": "compatible"
}
}
}
缓存优先级: Memory > Redis > DB,自动穿透与回填
DB 缓存配置说明
| 配置项 | 默认值 | 说明 |
|---|---|---|
db |
false | 是否缓存数据库查询 |
session |
true | 是否缓存 Session |
timeout |
2592000 | 过期时间(秒) |
history |
false | 是否记录缓存历史,开启后每次新增/修改缓存都会记录到历史表 |
mode |
compatible | 缓存表模式,见下表 |
缓存表模式 (mode):
| 模式 | 说明 |
|---|---|
compatible |
默认。兼容模式:写新表,新表无数据时回退读老表;写入时自动删除老表同 key 记录;删除时同时删两表。适合从老版本平滑升级,老数据自然过期消亡 |
new |
只使用新表 hotime_cache,启动时自动迁移老表 cached 数据。老表保留由人工删除,不再被读写 |
升级建议:从老版本升级时,建议先使用
compatible模式运行一段时间(让老数据自然过期),确认无问题后再切换到new模式
从老版本升级时,建议使用
compatible模式平滑过渡,待老表数据消亡后切换到new模式
错误码配置
{
"error": {
"1": "内部系统异常",
"2": "访问权限异常",
"3": "请求参数异常",
"4": "数据处理异常",
"5": "数据结果异常"
}
}
自定义错误码建议从 10 开始
路由系统
HoTime 使用三层路由结构:模块/控制器/方法
appIns.Run(Router{
"模块名": {
"控制器名": {
"方法名": func(that *Context) {
// 处理逻辑
},
},
},
})
路由路径
// 获取路由信息
module := that.RouterString[0] // 模块
controller := that.RouterString[1] // 控制器
action := that.RouterString[2] // 方法
// 完整请求路径
fullPath := that.HandlerStr // 如 /app/user/login
请求参数获取
新版推荐方法(支持链式调用)
// 获取 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()
类型转换方法
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{})
文件上传
// 单文件上传
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()
// 处理每个文件
}
}
传统方法(兼容)
// GET/POST 参数
name := that.Req.FormValue("name")
// URL 参数
id := that.Req.URL.Query().Get("id")
// 请求头
token := that.Req.Header.Get("Authorization")
响应数据
Display 方法
// 成功响应 (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 | 数据结果异常 | 无法返回要求的格式 |
自定义响应
// 自定义 Header
that.Resp.Header().Set("Content-Type", "application/json")
// 直接写入
that.Resp.Write([]byte("raw data"))
// 自定义响应函数
that.RespFunc = func() {
// 自定义响应逻辑
}
中间件
// 全局中间件(请求拦截)
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, "请先登录")
return true // 返回 true 终止请求
}
return false // 返回 false 继续处理
})
Session 与缓存
// Session 操作
that.Session("user_id", 123) // 设置
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") // 获取
that.Cache("key", nil) // 删除
批量操作(性能优化)
当需要同时操作多个 Session 字段时,使用批量操作可显著提升性能:
// SessionsSet - 批量设置(N个字段只触发1次数据库写入)
that.SessionsSet(Map{
"user_id": userId,
"username": "张三",
"login_time": time.Now().Unix(),
"role": "admin",
})
// SessionsGet - 批量获取(1次调用获取多个字段)
result := that.SessionsGet("user_id", "username", "role")
// result = Map{"user_id": 123, "username": "张三", "role": "admin"}
userId := ObjToInt64(result["user_id"], nil)
username := ObjToStr(result["username"])
// SessionsDelete - 批量删除(N个字段只触发1次数据库写入)
that.SessionsDelete("token", "temp_code", "verify_expire")
性能对比:
| 操作方式 | 设置10个字段 | 数据库写入次数 |
|---|---|---|
逐个调用 Session() |
10次调用 | 10次 |
使用 SessionsSet() |
1次调用 | 1次 |
| 操作方式 | 获取10个字段 | 缓存查询次数 |
|---|---|---|
逐个调用 Session() |
10次调用 | 10次 |
使用 SessionsGet() |
1次调用 | 1次 |
💡 最佳实践:当一次性操作 3 个以上字段时,建议使用批量操作
三级缓存自动运作:Memory → Redis → Database
数据库操作(简要)
基础 CRUD
// 查询列表
users := that.Db.Select("user", "*", Map{"status": 1})
// 查询单条
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})
// 删除
rows := that.Db.Delete("user", Map{"id": 1})
链式查询
users := that.Db.Table("user").
LeftJoin("order", "user.id=order.user_id").
Where("status", 1).
And("age[>]", 18).
Order("id DESC").
Page(1, 10).
Select("*")
条件语法速查
| 语法 | 说明 | 示例 |
|---|---|---|
key |
等于 | "id": 1 |
key[>] |
大于 | "age[>]": 18 |
key[<] |
小于 | "age[<]": 60 |
key[>=] |
大于等于 | "age[>=]": 18 |
key[<=] |
小于等于 | "age[<=]": 60 |
key[!] |
不等于 | "status[!]": 0 |
key[~] |
LIKE | "name[~]": "test" |
key[<>] |
BETWEEN | "age[<>]": Slice{18, 60} |
key |
IN | "id": Slice{1, 2, 3} |
事务
success := that.Db.Action(func(tx db.HoTimeDB) bool {
tx.Update("user", Map{"balance[#]": "balance - 100"}, Map{"id": 1})
tx.Insert("order", Map{"user_id": 1, "amount": 100})
return true // 返回 true 提交,false 回滚
})
更多数据库操作:参见 HoTimeDB 使用说明
日志记录
// 创建操作日志(自动插入 logs 表)
that.Log = Map{
"type": "login",
"action": "用户登录",
"data": Map{"phone": phone},
}
// 框架会自动添加 time, admin_id/user_id, ip 等字段
扩展功能
| 功能 | 路径 | 说明 |
|---|---|---|
| 微信支付/公众号/小程序 | dri/wechat/ |
微信全套 SDK |
| 阿里云服务 | dri/aliyun/ |
企业认证等 |
| 腾讯云服务 | dri/tencent/ |
企业认证等 |
| 文件上传 | dri/upload/ |
文件上传处理 |
| 文件下载 | dri/download/ |
文件下载处理 |
| MongoDB | dri/mongodb/ |
MongoDB 驱动 |
| RSA 加解密 | dri/rsa/ |
RSA 加解密工具 |
完整示例
package main
import (
"time"
. "code.hoteas.com/golang/hotime"
. "code.hoteas.com/golang/hotime/common"
)
func main() {
appIns := Init("config/config.json")
// 登录检查中间件
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, "请先登录")
return true
}
return false
})
appIns.Run(Router{
"api": {
"user": {
"login": func(that *Context) {
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": Md5(password),
})
if user == nil {
that.Display(3, "账号或密码错误")
return
}
// 使用批量设置,一次写入多个字段
that.SessionsSet(Map{
"user_id": user.GetInt64("id"),
"username": user.GetString("name"),
"login_time": time.Now().Unix(),
})
that.Display(0, Map{"user": user})
},
"info": func(that *Context) {
// 使用批量获取,一次读取多个字段
sess := that.SessionsGet("user_id", "username", "login_time")
userId := ObjToInt64(sess["user_id"], nil)
user := that.Db.Get("user", "*", Map{"id": userId})
that.Display(0, Map{
"user": user,
"login_time": sess["login_time"],
})
},
"list": func(that *Context) {
page := that.ReqData("page").ToInt()
if page == 0 {
page = 1
}
users := that.Db.Table("user").
Where("status", 1).
Order("id DESC").
Page(page, 10).
Select("id,name,phone,created_at")
total := that.Db.Count("user", Map{"status": 1})
that.Display(0, Map{
"list": users,
"total": total,
"page": page,
})
},
"logout": func(that *Context) {
// 使用批量删除,一次清除多个字段
that.SessionsDelete("user_id", "username", "login_time")
that.Display(0, "退出成功")
},
},
},
})
}
下一步:
- HoTimeDB 使用说明 - 完整数据库教程
- HoTimeDB API 参考 - API 速查手册