工具函数与核心组件
本文汇总后端可被各业务复用的「基础/扩展能力」:util/ 工具包、core/ 核心组件、plugin/ 通用能力、通用控制器。二次开发最常直接调用这些设施,避免重复实现。
一、util 工具包(server/util/)
util 只放跨业务通用工具,邮件/短信/验证码/OSS 等插件能力位于 plugin/(见第三节)。
| 文件 / 目录 | 能力(真实签名) |
|---|---|
tools.go | ToolsUtil:MakeUuidV7()、StrMd5()、RandomString()、Random(min,max)、Contains[T comparable]、Round(val,n)、JsonToObj[T] / ObjToJson |
verify.go | VerifyUtil:VerifyJSON / VerifyBody / VerifyQuery / VerifyJSONArray / VerifyFile,绑定 + 校验一体,失败统一翻译为 response.ParamsValidError |
string.go | StringUtil:命名风格转换(ToSnakeCase 等),配合 golang.org/x/text/cases |
array.go | TreeItem[T] 泛型接口 + ListToTree[T](items, rootParentID):扁平列表 → 树结构(区域树 / 菜单树通用) |
cache.go | CacheUtil{Name}:以 Name:field 为键的 Redis 缓存封装,SetCache/GetCache,默认 1h TTL |
redis.go | RedisUtil(redisUtil):Set/Get/Del/Exists/HSet/HGet/HGetAll/LPush/RPop/BRPop/Expire/TTL 等常用原子操作,业务侧通用键值/哈希/队列操作入口 |
redis_lock.go | NewRedisLock(key, expire) + Lock()/Unlock():SET NX PX + Lua 校验持有的分布式锁,用于定时任务/防并发 |
jwt.go | 三实例:AdminJWTUtil(后台)、WebJWTUtil(C 端)、JWTUtil(兼容别名);UserClaims{ UserID, TokenVersion, TokenType };GenerateAccessToken/GenerateRefreshToken/ParseToken/… |
null_time.go | NullTimeUtil:面向 x_null.Time 的构造与格式化,常量 DateFormat / TimeFormat,方法 Now()、ParseTime(t)、TodayZero() 等 |
url.go | UrlUtil:HashUrl(id,name)、ToAbsoluteUrl(u)、GetFileExt(name)、BuildFileSavePath(name)(YYYYMMDD/HH/{id后2位}/{uuidv7}{ext})、ReplaceExt(path,newExt) |
ip.go | IP 归属地解析(ip2region) |
server.go | 服务器信息采集(CPU / 内存 / 磁盘) |
uaparser.go | User-Agent 解析(浏览器 / 系统 / 设备) |
convert_util/ | convert_util.Copy(&dst, src):结构体字段拷贝 + 类型转换(模型 → DTO 的默认手段) |
excel_util/ | Excel 导入 / 导出(详见 导入导出excel.md) |
img_util/ | 图片处理:缩放、水印、缩略图 |
file_util/ | 基于 os.OpenRoot 的安全文件读写:ReadFile/WriteFile/… + Close();根目录固定为运行目录,禁止 ../ 逃逸与绝对路径 |
validation/ | Gin 绑定 + 校验器集中配置(详见下) |
validation 包(util/validation/)
单一入口,bootstrap.go 启动时调用一次;对同一引擎幂等。
register.go→Register():从binding.Validator.Engine()拿到*validator.Validate,串起下面两步。null.go→register_null():RegisterCustomTypeFunc(validateValuer, x_null.String{}, x_null.Int64{}, x_null.Float64{}, x_null.Time{}),让binding:"required"等标签对可空类型依然生效。lang.go→ValidMsg(errs):中文错误翻译(go-playground/validator+utbundle),并对 map 结果排序保证同一错误顺序稳定。
典型用法
// 生成 UUIDv7
id := util.ToolsUtil.MakeUuidV7()
// 控制器:绑定 + 校验 + 统一失败出口(约定见 core/response)
if response.IsFail(c, util.VerifyUtil.VerifyQuery(c, &req)) { return }
data, err := XxxService.List(page, req)
response.JSON(c, data, err)
// Redis 分布式锁(定时任务防并发)
lock := util.NewRedisLock("cron:xxx", 10*time.Second)
if lock.Lock() {
defer lock.Unlock()
// 临界区
}二、core 核心组件(server/core/)
全局单例
| 文件 | 说明 |
|---|---|
db.go | GORM 初始化,暴露全局 core.DB(*gorm.DB) |
redis.go | Redis 客户端初始化,全局 core.Redis(*redis.Client,v9) |
logger.go | Zap 日志初始化,全局 core.Logger,支持分级与文件切割 |
queue.go | 队列门面:包初始化时基于 Redis 构造全局 core.Queue(queue.Queue)与 core.QueueDelay(queue_delay.DelayedQueue),业务侧直接复用,无需自行 new |
ws.go | WebSocket 门面,转发到 core/ws.Manager(详见 WebSocket.md) |
子包
| 目录 | 内容 |
|---|---|
core/request/ | PageReq{ PageNo int, PageSize int }(form:"pageNo,default=1" / pageSize,default=20,validate:"omitempty,gte=1" / gt=0,lte=60)——只有这两字段,Order/Sort 由业务需要时自行扩展 |
core/response/ | 统一响应与错误码(详见下) |
core/ws/ | client.go、manager.go、room.go、interface.go——连接、房间、按用户/房间/全局推送、集群广播 |
core/pubsub/ | interface.go、config.go、redis.go、registry.go——mitt 风格 On/Off/Emit,Redis 后端跨实例广播 |
core/queue/ | interface.go、config.go、redis.go——工作队列(BRPOP 并发消费),IoT iot:ingest 即走此通道 |
core/queue_delay/ | interface.go、config.go、redis.go——延迟/定时投递队列(Redis ZSET 到期轮询),用于订单超时、延时通知 |
统一响应(core/response)
约定:HTTP 状态码恒为 200,成败一律以 body.code 表达;失败响应不携带 data;未识别错误只落日志、对外返回统一文案。
// 响应结构
type Response struct { Code int `json:"code"`; Message string `json:"message"`; Data any `json:"data"` }
type PageResp struct { Count int64; PageNo int; PageSize int; Lists any }
// 出口函数
response.Ok(c, data...) // 成功(data 可选)
response.Send(c, code, msg, data) // 最底层发送
response.JSON(c, data, err) // 统一出口:err==nil 返回 data,否则翻译错误
response.IsFail(c, err) bool // 守卫式:err!=nil 直接响应失败并 return true
response.Fail(c, err) // 中间件等非 service 场景直接失败
response.FailMsg(c, msg) // 指定文案(默认 code=300)
response.NotFound(c, msg) / NoRoute(c) // 404
// service 侧错误构造
response.CheckErr(err, "统计规则链失败") // 记日志 + 上抛 SystemError(带文案)
response.CheckDBErr(err, "岗位不存在!", "详情获取失败") // 分流「记录不存在 / 其它 DB 错误」RespType 同时实现 error 接口,可 return response.AssertArgumentError.SetMessage("...") 从 service 直接上抛,由 JSON/IsFail 翻译成 {code, message}。预置码常用:Success(200) / Failed(300) / ParamsValidError(310) / AssertArgumentError(313) / TokenInvalid(333) / NoPermission(403) / SystemError(500) 等;Resolve(err) 还会自动识别 MySQL 1062/1048/1452/1451 与 gorm.ErrRecordNotFound。
三、plugin 通用能力(server/plugin/)
跨业务、可能替换服务商 / 引擎的能力统一以「插件」形式落地,util 不重复实现:
| 目录 | 能力 |
|---|---|
plugin/email/ | email.go SMTP 发送,code.go 邮箱验证码(限频 / Redis 存储),配套 app/queue/email_code.go 走异步队列投递 |
plugin/sms/ | code.go 短信验证码(服务商对接位) |
plugin/wechat/ | 微信开放能力封装 |
plugin/ipapk/ | parser.go ip2region 归属地解析(util/ip.go 的底层) |
plugin/aj-captcha-go/ | 行为验证码:滑块 / 文字点选,captcha_service + captcha_config + 内存/Redis 双缓存实现 |
plugin/storage/ | 文件存储引擎(见下节) |
四、存储引擎(plugin/storage/)
驱动工厂 + 单例,统一 StorageEngine 接口:
// plugin/storage/storage_interface.go
type StorageEngine interface {
PutObject(key string, data io.Reader, size int64) (string, error)
ObjectExists(key string) (bool, error) // 秒传判断
GetObjectURL(key string) (string, error)
// 分片上传
InitMultipartUpload(key string) (string, error)
UploadPart(key, uploadId string, partNumber int, data io.Reader, size int64) (string, error)
CompleteMultipartUpload(key, uploadId string, parts []MultipartPart) (string, error)
AbortMultipartUpload(key, uploadId string) error
ListParts(key, uploadId string) ([]MultipartPart, error)
}| 文件 | 角色 |
|---|---|
storage_factory.go | GetStorageEngine():sync.Once 单例,当前固定返回本地存储(storage_local.go),OSS 通过 storage_oss.go 预留 |
storage.go | 顶层 StorageDriver.Upload(file) 便捷入口 |
storage_local.go | 本地磁盘实现 |
storage_oss.go | 阿里云 OSS(S3 兼容)实现 |
presign.go | 预签名 URL(前端直传) |
cron_cleanup.go | 分片临时目录清理:CleanChunkTmpDir() 定时任务每小时整点调用,删除超过 ChunkExpireHour(默认 24h)的分片目录 |
- 存储路径:
util.UrlUtil.BuildFileSavePath(name)统一生成YYYYMMDD/HH/{uuidv7 后 2 位}/{uuidv7}{ext}(用 id 后 2 位分桶,缓解目录锁争抢)。 - 访问前缀:
/api/uploads/<hashId>/<fileName>,由util.UrlUtil.HashUrl拼接;路由routes/api.go做静态映射,实际文件流由common_controller/upload.go中的FileHandler.Serve按哈希 ID 读物理文件流式返回(自动 MIME、Cache-Control长缓存)。 - 秒传:
ObjectExists(key)。
详解见 存储引擎。
五、WebSocket 与事件总线
已独立成篇,这里只做入口指路:
- WebSocket:连接管理、按用户 / 房间 / 全局推送、集群广播、在线人数统计、连接强制关闭。IoT 大屏推送即基于此(房间
iot:device:{id}/iot:map:{spaceId})。 - 事件总线 pubsub:mitt 风格
On/Off/Emit,Redis 后端跨实例广播,与core/queue(工作队列)用途不同。
六、通用控制器(app/controller/admin_ctl/common_controller/)
| 文件 | 功能 |
|---|---|
upload.go | 文件上传(本地/OSS 驱动 + 秒传),并内含 FileHandler.Serve(/api/uploads/:id 文件流响应) |
upload_chunk.go | S3 风格分片直传:初始化 / 上传分片 / 完成 / 列举 / 中止 |
album.go | 相册分组与图片素材管理 |
captcha.go | 行为验证码获取 / 校验(plugin/aj-captcha-go) |
ge_tui.go | 个推移动推送(PushToSingleBatchCID,按 CID 批量推) |
index.go | 后台首页统计 |
WebSocket 升级处理器位于
app/controller/ws.go(controller 层),对接core/ws.Manager。
以上能力均可在新业务模块中直接复用,无需重复实现。