Compare commits

..

No commits in common. "main" and "v1.0.0.00002" have entirely different histories.

10 changed files with 33 additions and 1284 deletions

View File

@ -1,461 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="GoORMHelperCache">
<option name="schemaMapping">
<map>
<entry key="Api">
<value>
<set>
<option value="file://$PROJECT_DIR$/response/code.go" />
</set>
</value>
</entry>
<entry key="BaseConfig">
<value>
<set>
<option value="file://$PROJECT_DIR$/config/structs.go" />
</set>
</value>
</entry>
<entry key="Client">
<value>
<set>
<option value="file://$PROJECT_DIR$/mqtt/client/mqtt.go" />
</set>
</value>
</entry>
<entry key="Config">
<value>
<set>
<option value="file://$PROJECT_DIR$/ws/websocket.go" />
</set>
</value>
</entry>
<entry key="Connection">
<value>
<set>
<option value="file://$PROJECT_DIR$/ws/websocket.go" />
</set>
</value>
</entry>
<entry key="ConnectionPool">
<value>
<set>
<option value="file://$PROJECT_DIR$/tcp/tcp.go" />
</set>
</value>
</entry>
<entry key="Curd">
<value>
<set>
<option value="file://$PROJECT_DIR$/curd/curd.go" />
</set>
</value>
</entry>
<entry key="DataBaseConfig">
<value>
<set>
<option value="file://$PROJECT_DIR$/config/structs.go" />
</set>
</value>
</entry>
<entry key="JWTClaims">
<value>
<set>
<option value="file://$PROJECT_DIR$/utils/jwt.go" />
</set>
</value>
</entry>
<entry key="JwtConfig">
<value>
<set>
<option value="file://$PROJECT_DIR$/config/structs.go" />
</set>
</value>
</entry>
<entry key="Manager">
<value>
<set>
<option value="file://$PROJECT_DIR$/ws/websocket.go" />
</set>
</value>
</entry>
<entry key="Msg">
<value>
<set>
<option value="file://$PROJECT_DIR$/ws/websocket.go" />
</set>
</value>
</entry>
<entry key="NewsOne">
<value>
<set>
<option value="file://$PROJECT_DIR$/../gin_test/api/new.go" />
<option value="file://$PROJECT_DIR$/../gin_test/req/new.go" />
</set>
</value>
</entry>
<entry key="NewsSave">
<value>
<set>
<option value="file://$PROJECT_DIR$/../gin_test/api/new.go" />
<option value="file://$PROJECT_DIR$/../gin_test/req/new.go" />
</set>
</value>
</entry>
<entry key="Paginate">
<value>
<set>
<option value="file://$PROJECT_DIR$/curd/curd.go" />
</set>
</value>
</entry>
<entry key="ServerConfig">
<value>
<set>
<option value="file://$PROJECT_DIR$/config/structs.go" />
</set>
</value>
</entry>
<entry key="TCPServer">
<value>
<set>
<option value="file://$PROJECT_DIR$/tcp/tcp.go" />
</set>
</value>
</entry>
<entry key="TcpConnection">
<value>
<set>
<option value="file://$PROJECT_DIR$/tcp/tcpConfig.go" />
</set>
</value>
</entry>
<entry key="TcpMessage">
<value>
<set>
<option value="file://$PROJECT_DIR$/tcp/tcpConfig.go" />
</set>
</value>
</entry>
<entry key="TcpPoolConfig">
<value>
<set>
<option value="file://$PROJECT_DIR$/tcp/tcpConfig.go" />
</set>
</value>
</entry>
<entry key="response">
<value>
<set>
<option value="file://$PROJECT_DIR$/response/code.go" />
</set>
</value>
</entry>
</map>
</option>
<option name="scannedPathMapping">
<map>
<entry key="file://$PROJECT_DIR$/config/fun.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770100625919" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/config/index.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770011355476" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/config/structs.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770022381063" />
<option name="schema">
<list>
<option value="BaseConfig" />
<option value="ServerConfig" />
<option value="DataBaseConfig" />
<option value="JwtConfig" />
</list>
</option>
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/curd/curd.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770102564408" />
<option name="schema">
<list>
<option value="Paginate" />
<option value="Curd" />
</list>
</option>
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/database/database.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770258641711" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/database/index.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770025447924" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/database/migrate.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770101088203" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/log/index.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770011347706" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/log/log.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770011076335" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/main.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770025551953" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/middleware/middleware.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770108038945" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/mqtt/client/mqtt.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770025698340" />
<option name="schema">
<list>
<option value="Client" />
</list>
</option>
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/response/code.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770014431840" />
<option name="schema">
<list>
<option value="response" />
<option value="Api" />
</list>
</option>
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/server/server.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770026239951" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/tcp/example.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770025697304" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/tcp/tcp.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770025697304" />
<option name="schema">
<list>
<option value="TCPServer" />
<option value="ConnectionPool" />
</list>
</option>
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/tcp/tcpConfig.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770025697304" />
<option name="schema">
<list>
<option value="TcpPoolConfig" />
<option value="TcpConnection" />
<option value="TcpMessage" />
</list>
</option>
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/utils/file.go">
<value>
<ScannedPath>
<option name="lastModified" value="1769850381300" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/utils/jwt.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770090940817" />
<option name="schema">
<list>
<option value="JWTClaims" />
</list>
</option>
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/utils/ptr.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770016124510" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/valid/valid.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770197737203" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/ws/example.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770025697531" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/ws/websocket.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770018907536" />
<option name="schema">
<list>
<option value="Config" />
<option value="Connection" />
<option value="Manager" />
<option value="Msg" />
</list>
</option>
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/../gin_test/api/home.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770190095707" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/../gin_test/api/new.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770191206242" />
<option name="schema">
<list>
<option value="NewsOne" />
<option value="NewsSave" />
</list>
</option>
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/../gin_test/controller/home/houeRouter.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770191001042" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/../gin_test/controller/home/index.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770255253603" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/../gin_test/controller/home/router.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770190557848" />
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/../gin_test/req/new.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770186504009" />
<option name="schema">
<list>
<option value="NewsOne" />
<option value="NewsSave" />
</list>
</option>
</ScannedPath>
</value>
</entry>
<entry key="file://$PROJECT_DIR$/../gin_test/router/router.go">
<value>
<ScannedPath>
<option name="lastModified" value="1770191024510" />
</ScannedPath>
</value>
</entry>
</map>
</option>
<option name="tableStructMapping">
<map>
<entry key="api" value="Api" />
<entry key="base_config" value="BaseConfig" />
<entry key="client" value="Client" />
<entry key="config" value="Config" />
<entry key="connection" value="Connection" />
<entry key="connection_pool" value="ConnectionPool" />
<entry key="curd" value="Curd" />
<entry key="data_base_config" value="DataBaseConfig" />
<entry key="jwt_claims" value="JWTClaims" />
<entry key="jwt_config" value="JwtConfig" />
<entry key="manager" value="Manager" />
<entry key="msg" value="Msg" />
<entry key="news_one" value="NewsOne" />
<entry key="news_save" value="NewsSave" />
<entry key="paginate" value="Paginate" />
<entry key="response" value="response" />
<entry key="server_config" value="ServerConfig" />
<entry key="tcp_connection" value="TcpConnection" />
<entry key="tcp_message" value="TcpMessage" />
<entry key="tcp_pool_config" value="TcpPoolConfig" />
<entry key="tcp_server" value="TCPServer" />
</map>
</option>
<option name="lastTimeChecked" value="1770185677345" />
</component>
</project>

View File

@ -57,8 +57,8 @@ func init() {
func SetDefault() {
viper.Set("SERVER.addr", "127.0.0.1:8080")
viper.Set("SERVER.mode", "release")
viper.Set("DATABASE.type", "sqlite")
viper.Set("DATABASE.dns", gfile.Join(gfile.Pwd(), "db", "database.db"))
viper.Set("DATABASE.type", "mysql")
viper.Set("DATABASE.dns", "user:pass@tcp(127.0.0.1:3306)/dbname?charset=utf8mb4&parseTime=True&loc=Local")
viper.Set("JWT.secret", "SET-YOUR-SECRET")
viper.Set("JWT.expire", 86400)
}

View File

@ -1,614 +0,0 @@
package utils
import (
"context"
"errors"
"fmt"
"reflect"
"strings"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
// 定义上下文别名,保持原代码风格
type ctx = context.Context
// IDao -------------------------- 核心接口定义 --------------------------
// IDao GORM 版本的Dao接口提供GORM DB实例和表相关信息
type IDao interface {
DB() *gorm.DB // 返回GORM的DB实例
Table() string // 返回表名
PrimaryKey() string // 返回主键字段名id
Ctx(ctx context.Context) *gorm.DB // 绑定上下文的DB实例
Transaction(ctx context.Context, f func(ctx context.Context, tx *gorm.DB) error) error // 事务方法
}
// Paginate -------------------------- 分页结构体定义 --------------------------
// Paginate 分页参数结构体(补全原代码中缺失的定义,保持功能完整)
type Paginate struct {
Page int // 页码从1开始
Limit int // 每页条数
}
// -------------------------- 全局常量 --------------------------
// 分页相关字段,用于清理请求参数
var pageInfo = []string{
"page",
"size",
"num",
"limit",
"pagesize",
"pageSize",
"page_size",
"pageNum",
"pagenum",
"page_num",
}
// Curd -------------------------- 泛型CURD核心结构体 --------------------------
// Curd GORM 版本的泛型CURD封装R为对应的模型结构体
type Curd[R any] struct {
Dao IDao
}
// -------------------------- 工具方法:字段名转换(保持原代码的命名风格转换) --------------------------
// caseConvert 字段名风格转换(下划线 <-> 小驼峰)
func caseConvert(key string, toSnake bool) string {
if toSnake {
// 驼峰转下划线参考GORM的命名策略
return schema.NamingStrategy{}.ColumnName("", key)
}
// 下划线转小驼峰
var result strings.Builder
upperNext := false
for i, c := range key {
if c == '_' && i < len(key)-1 {
upperNext = true
continue
}
if upperNext {
result.WriteRune(rune(strings.ToUpper(string(c))[0]))
upperNext = false
} else {
result.WriteRune(c)
}
}
return result.String()
}
// BuildWhere -------------------------- 原BuildWhere对应实现构建查询条件map --------------------------
func (c Curd[R]) BuildWhere(req any, changeWhere any, subWhere any, removeFields []string, isSnake ...bool) map[string]any {
// 默认使用小写下划线方式
toSnake := true
if len(isSnake) > 0 && !isSnake[0] {
toSnake = false
}
// 1. 转换req为map并清理无效数据
reqMap := convToMap(req)
cleanedReq := make(map[string]any)
for k, v := range reqMap {
// 清理空值
if isEmpty(v) {
continue
}
// 清理分页字段
if strInArray(pageInfo, k) {
continue
}
// 清理指定移除字段
if len(removeFields) > 0 && strInArray(removeFields, k) {
continue
}
// 转换字段名风格并存入
cleanedReq[caseConvert(k, toSnake)] = v
}
// 2. 处理changeWhere修改查询操作符eq -> gt
if changeWhere != nil {
changeMap := convToMap(changeWhere)
for k, v := range changeMap {
// 跳过不存在于cleanedReq的字段
if _, exists := cleanedReq[k]; !exists {
continue
}
// 跳过指定移除的字段
if len(removeFields) > 0 && strInArray(removeFields, k) {
continue
}
vMap := convToMap(v)
value, hasValue := vMap["value"]
op, hasOp := vMap["op"]
if hasValue {
// 存在操作符则重构字段名GORM支持 "字段名 >" 这种格式作为where key
if hasOp && op != "" {
newKey := fmt.Sprintf("%s %s", k, op)
delete(cleanedReq, k)
cleanedReq[newKey] = value
} else {
cleanedReq[k] = value
}
}
}
}
// 3. 字段名风格最终转换(确保一致性)
resultMap := make(map[string]any)
for k, v := range cleanedReq {
// 拆分字段名和操作符
parts := strings.SplitN(k, " ", 2)
fieldName := parts[0]
opStr := ""
if len(parts) == 2 {
opStr = parts[1]
}
// 转换字段名风格
convertedField := caseConvert(fieldName, toSnake)
// 重构带操作符的key
if opStr != "" {
resultMap[fmt.Sprintf("%s %s", convertedField, opStr)] = v
} else {
resultMap[convertedField] = v
}
}
// 4. 合并subWhere附加条件
if subWhere != nil {
subMap := convToMap(subWhere)
for k, v := range subMap {
resultMap[caseConvert(k, toSnake)] = v
}
}
return resultMap
}
// BuildMap -------------------------- 原BuildMap对应实现构建变更条件map --------------------------
func (c Curd[R]) BuildMap(op string, value any, field ...string) map[string]any {
res := map[string]any{
"op": op,
"field": "",
"value": value,
}
if len(field) > 0 {
res["field"] = field[0]
}
return res
}
// ClearField -------------------------- 原ClearField对应实现清理请求参数并返回有效map --------------------------
func (c Curd[R]) ClearField(req any, delField []string, subField ...map[string]any) map[string]any {
reqMap := convToMap(req)
resultMap := make(map[string]any)
// 过滤无效数据和指定删除字段
for k, v := range reqMap {
if isEmpty(v) {
continue
}
if strInArray(pageInfo, k) {
continue
}
if len(delField) > 0 && strInArray(delField, k) {
continue
}
resultMap[k] = v
}
// 合并附加字段
if len(subField) > 0 && subField[0] != nil {
for k, v := range subField[0] {
resultMap[k] = v
}
}
return resultMap
}
// ClearFieldPage -------------------------- 原ClearFieldPage对应实现清理参数+分页查询 --------------------------
func (c Curd[R]) ClearFieldPage(ctx ctx, req any, delField []string, where any, page *Paginate, order any, with bool) (items []*R, total int64, err error) {
// 1. 清理请求参数
filterMap := c.ClearField(req, delField)
// 2. 初始化GORM查询
db := c.Dao.Ctx(ctx)
if with {
db = db.Preload("*") // GORM 关联查询全部对应GF的WithAll()
}
// 3. 构建查询条件
db = db.Model(new(R)).Where(filterMap)
if where != nil {
db = db.Where(where)
}
// 4. 排序
if order != nil {
db = db.Order(order)
}
// 5. 统计总数
if err = db.Count(&total).Error; err != nil {
return nil, 0, err
}
// 6. 分页查询
if page != nil && page.Limit > 0 {
offset := (page.Page - 1) * page.Limit
db = db.Offset(offset).Limit(page.Limit)
}
// 7. 执行查询
err = db.Find(&items).Error
return
}
// ClearFieldList -------------------------- 原ClearFieldList对应实现清理参数+列表查询(不分页) --------------------------
func (c Curd[R]) ClearFieldList(ctx ctx, req any, delField []string, where any, order any, with bool) (items []*R, err error) {
filterMap := c.ClearField(req, delField)
db := c.Dao.Ctx(ctx).Model(new(R))
if with {
db = db.Preload("*")
}
if where != nil {
db = db.Where(where)
}
if order != nil {
db = db.Order(order)
}
err = db.Where(filterMap).Find(&items).Error
return
}
// ClearFieldOne -------------------------- 原ClearFieldOne对应实现清理参数+单条查询 --------------------------
func (c Curd[R]) ClearFieldOne(ctx ctx, req any, delField []string, where any, order any, with bool) (item *R, err error) {
item = new(R)
filterMap := c.ClearField(req, delField)
db := c.Dao.Ctx(ctx).Model(item)
if with {
db = db.Preload("*")
}
if where != nil {
db = db.Where(where)
}
if order != nil {
db = db.Order(order)
}
err = db.Where(filterMap).First(item).Error
// 处理记录不存在的情况GORM会返回ErrRecordNotFound
if errors.Is(err, gorm.ErrRecordNotFound) {
panic("未找到数据")
}
return
}
// Value -------------------------- 原Value对应实现查询单个字段值 --------------------------
func (c Curd[R]) Value(ctx ctx, where any, field any) (interface{}, error) {
var result interface{}
db := c.Dao.Ctx(ctx).Model(new(R)).Where(where)
// 处理字段参数
if field != nil {
fieldStr, ok := field.(string)
if !ok || fieldStr == "" {
fieldStr = "*"
}
db = db.Select(fieldStr)
} else {
db = db.Select("*")
}
// 执行查询(取第一条记录的指定字段)
err := db.First(&result).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
panic("未找到数据")
}
return result, err
}
// DeletePri -------------------------- 原DeletePri对应实现按主键删除 --------------------------
func (c Curd[R]) DeletePri(ctx ctx, primaryKey any) error {
db := c.Dao.Ctx(ctx).Model(new(R))
// 按主键字段构建查询
pk := c.Dao.PrimaryKey()
if pk == "" {
panic("主键字段未配置")
}
return db.Where(fmt.Sprintf("%s = ?", pk), primaryKey).Delete(new(R)).Error
}
// DeleteWhere -------------------------- 原DeleteWhere对应实现按条件删除 --------------------------
func (c Curd[R]) DeleteWhere(ctx ctx, where any) error {
return c.Dao.Ctx(ctx).Model(new(R)).Where(where).Delete(new(R)).Error
}
// Sum -------------------------- 原Sum对应实现字段求和 --------------------------
func (c Curd[R]) Sum(ctx ctx, where any, field string) float64 {
var sum float64
if field == "" {
panic("求和字段不能为空")
}
err := c.Dao.Ctx(ctx).Model(new(R)).Where(where).Select(fmt.Sprintf("SUM(%s) as sum", field)).Scan(&sum).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
panic("未找到数据")
}
return sum
}
// ArrayField -------------------------- 原ArrayField对应实现查询指定字段数组 --------------------------
func (c Curd[R]) ArrayField(ctx ctx, where any, field any) []interface{} {
var result []interface{}
db := c.Dao.Ctx(ctx).Model(new(R)).Where(where)
// 处理字段参数
if field != nil {
fieldStr, ok := field.(string)
if !ok || fieldStr == "" {
fieldStr = "*"
}
db = db.Select(fieldStr)
} else {
db = db.Select("*")
}
// 执行查询
err := db.Find(&result).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
panic("未找到数据")
}
return result
}
// FindPri -------------------------- 原FindPri对应实现按主键查询单条记录 --------------------------
func (c Curd[R]) FindPri(ctx ctx, primaryKey any, with bool) (model *R) {
model = new(R)
db := c.Dao.Ctx(ctx).Model(model)
pk := c.Dao.PrimaryKey()
if pk == "" {
panic("主键字段未配置")
}
if with {
db = db.Preload("*")
}
// 按主键查询
err := db.Where(fmt.Sprintf("%s = ?", pk), primaryKey).First(model).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
panic("未找到数据")
}
return
}
// -------------------------- 原First对应实现按条件查询第一条记录 --------------------------
func (c Curd[R]) First(ctx ctx, where any, order any, with bool) (model *R) {
model = new(R)
db := c.Dao.Ctx(ctx).Model(model)
if with {
db = db.Preload("*")
}
if where != nil {
db = db.Where(where)
}
if order != nil {
db = db.Order(order)
}
err := db.First(model).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
panic("未找到数据")
}
return
}
// -------------------------- 原Exists对应实现判断记录是否存在 --------------------------
func (c Curd[R]) Exists(ctx ctx, where any) (exists bool) {
var count int64
err := c.Dao.Ctx(ctx).Model(new(R)).Where(where).Count(&count).Error
if err != nil {
panic(fmt.Sprintf("Exists查询错误: %v", err))
}
return count > 0
}
// -------------------------- 原All对应实现查询所有符合条件的记录 --------------------------
func (c Curd[R]) All(ctx ctx, where any, order any, with bool) (items []*R) {
db := c.Dao.Ctx(ctx).Model(new(R))
if with {
db = db.Preload("*")
}
if where != nil {
db = db.Where(where)
}
if order != nil {
db = db.Order(order)
}
err := db.Find(&items).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
panic(fmt.Sprintf("All查询错误: %v", err))
}
return
}
// -------------------------- 原Count对应实现统计记录总数 --------------------------
func (c Curd[R]) Count(ctx ctx, where any) (count int64) {
err := c.Dao.Ctx(ctx).Model(new(R)).Where(where).Count(&count).Error
if errors.Is(err, gorm.ErrRecordNotFound) {
panic(fmt.Sprintf("Count查询错误: %v", err))
}
return
}
// -------------------------- 原Save对应实现新增/更新记录对应GORM的Save --------------------------
func (c Curd[R]) Save(ctx ctx, data any) {
err := c.Dao.Ctx(ctx).Model(new(R)).Create(data).Error
if err != nil {
panic(fmt.Sprintf("Save保存错误: %v", err))
}
}
// -------------------------- 原Update对应实现按条件更新记录 --------------------------
func (c Curd[R]) Update(ctx ctx, where any, data any) (count int64) {
result := c.Dao.Ctx(ctx).Model(new(R)).Where(where).Updates(data)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
panic(fmt.Sprintf("Update更新错误: %v", result.Error.Error()))
}
return result.RowsAffected
}
// -------------------------- 原UpdatePri对应实现按主键更新记录 --------------------------
func (c Curd[R]) UpdatePri(ctx ctx, primaryKey any, data any) (count int64) {
db := c.Dao.Ctx(ctx).Model(new(R))
pk := c.Dao.PrimaryKey()
if pk == "" {
panic("主键字段未配置")
}
result := db.Where(fmt.Sprintf("%s = ?", pk), primaryKey).Updates(data)
if errors.Is(result.Error, gorm.ErrRecordNotFound) {
panic(fmt.Sprintf("UpdatePri更新错误: %v", result.Error.Error()))
}
return result.RowsAffected
}
// -------------------------- 原Paginate对应实现分页查询 --------------------------
func (c Curd[R]) Paginate(ctx context.Context, where any, p Paginate, with bool, order any) (items []*R, total int64) {
db := c.Dao.Ctx(ctx).Model(new(R))
// 1. 构建查询条件
if where != nil {
db = db.Where(where)
}
// 2. 统计总数
if err := db.Count(&total).Error; err != nil {
panic(fmt.Sprintf("Paginate查询错误: %v", err))
}
// 3. 关联查询
if with {
db = db.Preload("*")
}
// 4. 排序
if order != nil {
db = db.Order(order)
}
// 5. 分页offset = (页码-1)*每页条数)
if p.Limit > 0 {
offset := (p.Page - 1) * p.Limit
db = db.Offset(offset).Limit(p.Limit)
}
// 6. 执行查询
err := db.Find(&items).Error
if err != nil || errors.Is(err, gorm.ErrRecordNotFound) {
panic(fmt.Sprintf("Paginate查询错误: %v", err))
}
return
}
// -------------------------- 内部辅助工具函数 --------------------------
// convToMap 将任意类型转换为map[string]any简化版适配常见场景
func convToMap(v any) map[string]any {
if v == nil {
return make(map[string]any)
}
val := reflect.ValueOf(v)
// 处理指针类型
if val.Kind() == reflect.Ptr {
val = val.Elem()
}
// 只处理结构体和map类型
if val.Kind() != reflect.Struct && val.Kind() != reflect.Map {
return make(map[string]any)
}
result := make(map[string]any)
if val.Kind() == reflect.Map {
// 处理map类型
for _, key := range val.MapKeys() {
keyStr, ok := key.Interface().(string)
if !ok {
continue
}
result[keyStr] = val.MapIndex(key).Interface()
}
} else {
// 处理结构体类型
typ := val.Type()
for i := 0; i < val.NumField(); i++ {
field := typ.Field(i)
fieldVal := val.Field(i)
// 获取json标签作为key优先否则用字段名
jsonTag := field.Tag.Get("json")
if jsonTag == "" || jsonTag == "-" {
jsonTag = field.Name
} else {
// 分割json标签忽略omitempty等选项
jsonTag = strings.Split(jsonTag, ",")[0]
}
result[jsonTag] = fieldVal.Interface()
}
}
return result
}
// isEmpty 判断值是否为空
func isEmpty(v any) bool {
if v == nil {
return true
}
val := reflect.ValueOf(v)
switch val.Kind() {
case reflect.String:
return val.String() == ""
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
return val.Int() == 0
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
return val.Uint() == 0
case reflect.Float32, reflect.Float64:
return val.Float() == 0
case reflect.Bool:
return !val.Bool()
case reflect.Slice, reflect.Array, reflect.Map, reflect.Chan:
return val.Len() == 0
case reflect.Ptr, reflect.Interface:
return val.IsNil()
default:
return false
}
}
// strInArray 判断字符串是否在数组中
func strInArray(arr []string, str string) bool {
for _, v := range arr {
if strings.EqualFold(v, str) { // 忽略大小写比较
return true
}
}
return false
}

View File

@ -2,16 +2,13 @@ package database
import (
"database/sql"
"fmt"
"git.magicany.cc/black1552/gin-base/config"
"git.magicany.cc/black1552/gin-base/log"
"github.com/glebarez/sqlite"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/os/gfile"
"gorm.io/driver/mysql"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
"gorm.io/gorm/schema"
)
var (
@ -19,7 +16,7 @@ var (
Db *gorm.DB
err error
sqlDb *sql.DB
dns = config.GetConfigValue("database.dns", gfile.Join(gfile.Pwd(), "db", "database.db"))
dns = config.GetConfigValue("database.dns", "")
)
func init() {
@ -27,7 +24,7 @@ func init() {
log.Error("gormDns未配置", "请检查配置文件")
return
}
switch config.GetConfigValue("database.type", "sqlite").String() {
switch config.GetConfigValue("database.type", "mysql").String() {
case "mysql":
log.Info("使用mysql数据库")
mysqlInit()
@ -35,13 +32,7 @@ func init() {
log.Info("使用sqlite数据库")
sqliteInit()
}
Db, err = gorm.Open(Type, &gorm.Config{
SkipDefaultTransaction: true,
// 命名策略:保持与模型一致,避免字段/表名转换问题
NamingStrategy: schema.NamingStrategy{
SingularTable: true, // 表名禁用复数形式(例如 User 对应 user 表,而非 users
},
})
Db, err = gorm.Open(Type, &gorm.Config{})
if err != nil {
log.Error("数据库连接失败: ", err)
return
@ -59,7 +50,7 @@ func init() {
func mysqlInit() {
Type = mysql.New(mysql.Config{
DSN: dns.String(),
DSN: config.GetConfigValue("database.dns", "").String(),
DefaultStringSize: 255, // string 类型字段的默认长度
DisableDatetimePrecision: true, // 禁用 datetime 精度MySQL 5.6 之前的数据库不支持
DontSupportRenameIndex: true, // 重命名索引时采用删除并新建的方式MySQL 5.7 之前的数据库和 MariaDB 不支持重命名索引
@ -68,12 +59,5 @@ func mysqlInit() {
}
func sqliteInit() {
if !gfile.Exists(dns.String()) {
_, err = gfile.Create(dns.String())
if err != nil {
log.Error("创建数据库文件失败: ", err)
return
}
}
Type = sqlite.Open(fmt.Sprintf("%s?cache=shared&mode=rwc&_busy_timeout=10000&_fk=1&_journal=WAL&_sync=FULL", dns.String()))
Type = sqlite.Open(config.GetConfigValue("database.dns", "").String())
}

View File

@ -10,6 +10,7 @@ func SetAutoMigrate(models ...interface{}) {
log.Error("数据库连接失败")
return
}
Db = Db.Set("gorm:table_options", "ENGINE=InnoDB")
err := Db.AutoMigrate(models...)
if err != nil {
log.Error("数据库迁移失败", err)

29
go.mod
View File

@ -6,16 +6,12 @@ require (
github.com/eclipse/paho.mqtt.golang v1.5.1
github.com/fsnotify/fsnotify v1.9.0
github.com/gin-gonic/gin v1.7.7
github.com/glebarez/sqlite v1.11.0
github.com/go-playground/locales v0.13.0
github.com/go-playground/universal-translator v0.17.0
github.com/go-playground/validator/v10 v10.4.2
github.com/gogf/gf/v2 v2.10.0
github.com/golang-jwt/jwt/v5 v5.3.1
github.com/gorilla/websocket v1.5.3
github.com/spf13/viper v1.21.0
gopkg.in/natefinch/lumberjack.v2 v2.2.1
gorm.io/driver/mysql v1.6.0
gorm.io/driver/sqlite v1.6.0
gorm.io/gorm v1.31.1
)
@ -23,33 +19,34 @@ require (
filippo.io/edwards25519 v1.1.0 // indirect
github.com/BurntSushi/toml v1.5.0 // indirect
github.com/clbanning/mxj/v2 v2.7.0 // indirect
github.com/dustin/go-humanize v1.0.1 // indirect
github.com/emirpasic/gods/v2 v2.0.0-alpha // indirect
github.com/fatih/color v1.18.0 // indirect
github.com/gin-contrib/sse v0.1.0 // indirect
github.com/glebarez/go-sqlite v1.21.2 // indirect
github.com/go-logr/logr v1.4.3 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-playground/locales v0.13.0 // indirect
github.com/go-playground/universal-translator v0.17.0 // indirect
github.com/go-playground/validator/v10 v10.4.1 // indirect
github.com/go-sql-driver/mysql v1.8.1 // indirect
github.com/go-viper/mapstructure/v2 v2.4.0 // indirect
github.com/golang/protobuf v1.3.5 // indirect
github.com/golang/protobuf v1.3.3 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/grokify/html-strip-tags-go v0.1.0 // indirect
github.com/jinzhu/inflection v1.0.0 // indirect
github.com/jinzhu/now v1.1.5 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/leodido/go-urn v1.2.4 // indirect
github.com/json-iterator/go v1.1.9 // indirect
github.com/leodido/go-urn v1.2.0 // indirect
github.com/magiconair/properties v1.8.10 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mattn/go-runewidth v0.0.16 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/mattn/go-sqlite3 v1.14.22 // indirect
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 // indirect
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 // indirect
github.com/olekukonko/errors v1.1.0 // indirect
github.com/olekukonko/ll v0.0.9 // indirect
github.com/olekukonko/tablewriter v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.4 // indirect
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect
github.com/rivo/uniseg v0.2.0 // indirect
github.com/sagikazarmark/locafero v0.11.0 // indirect
github.com/sourcegraph/conc v0.3.1-0.20240121214520-5f936abd7ae8 // indirect
@ -57,7 +54,7 @@ require (
github.com/spf13/cast v1.10.0 // indirect
github.com/spf13/pflag v1.0.10 // indirect
github.com/subosito/gotenv v1.6.0 // indirect
github.com/ugorji/go/codec v1.1.14 // indirect
github.com/ugorji/go/codec v1.1.7 // indirect
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
go.opentelemetry.io/otel v1.38.0 // indirect
go.opentelemetry.io/otel/metric v1.38.0 // indirect
@ -71,8 +68,4 @@ require (
golang.org/x/text v0.33.0 // indirect
gopkg.in/yaml.v2 v2.2.8 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
modernc.org/libc v1.22.6 // indirect
modernc.org/mathutil v1.5.0 // indirect
modernc.org/memory v1.5.0 // indirect
modernc.org/sqlite v1.23.1 // indirect
)

53
go.sum
View File

@ -7,8 +7,6 @@ github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eclipse/paho.mqtt.golang v1.5.1 h1:/VSOv3oDLlpqR2Epjn1Q7b2bSTplJIeV2ISgCl2W7nE=
github.com/eclipse/paho.mqtt.golang v1.5.1/go.mod h1:1/yJCneuyOoCOzKSsOTUc0AJfpsItBGWvYpBLimhArU=
github.com/emirpasic/gods/v2 v2.0.0-alpha h1:dwFlh8pBg1VMOXWGipNMRt8v96dKAIvBehtCt6OtunU=
@ -23,10 +21,6 @@ github.com/gin-contrib/sse v0.1.0 h1:Y/yl/+YNO8GZSjAhjMsSuLt29uWRFHdHYUb5lYOV9qE
github.com/gin-contrib/sse v0.1.0/go.mod h1:RHrZQHXnP2xjPF+u1gW/2HnVO7nvIa9PG3Gm+fLHvGI=
github.com/gin-gonic/gin v1.7.7 h1:3DoBmSbJbZAWqXJC3SLjAPfutPJJRN1U5pALB7EeTTs=
github.com/gin-gonic/gin v1.7.7/go.mod h1:axIBovoeJpVj8S3BwE0uPMTeReE4+AfFtqpqaZ1qq1U=
github.com/glebarez/go-sqlite v1.21.2 h1:3a6LFC4sKahUunAmynQKLZceZCOzUthkRkEAl9gAXWo=
github.com/glebarez/go-sqlite v1.21.2/go.mod h1:sfxdZyhQjTM2Wry3gVYWaW072Ri1WMdWJi0k6+3382k=
github.com/glebarez/sqlite v1.11.0 h1:wSG0irqzP6VurnMEpFGer5Li19RpIRi2qvQz++w0GMw=
github.com/glebarez/sqlite v1.11.0/go.mod h1:h8/o8j5wiAsqSPoWELDUdJXhjAhsVliSn7bWZjOhrgQ=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
@ -38,25 +32,19 @@ github.com/go-playground/locales v0.13.0 h1:HyWk6mgj5qFqCT5fjGBuRArbVDfE4hi8+e8c
github.com/go-playground/locales v0.13.0/go.mod h1:taPMhCMXrRLJO55olJkUXHZBHCxTMfnGwq/HNwmWNS8=
github.com/go-playground/universal-translator v0.17.0 h1:icxd5fm+REJzpZx7ZfpaD876Lmtgy7VtROAbHHXk8no=
github.com/go-playground/universal-translator v0.17.0/go.mod h1:UkSxE5sNxxRwHyU+Scu5vgOQjsIJAF8j9muTVoKLVtA=
github.com/go-playground/validator/v10 v10.4.1 h1:pH2c5ADXtd66mxoE0Zm9SUhxE20r7aM3F26W0hOn+GE=
github.com/go-playground/validator/v10 v10.4.1/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-playground/validator/v10 v10.4.2 h1:RqFe5MzGf2UOFhxQYnjHabHOT6CLbYWkeXOfcXB7fsM=
github.com/go-playground/validator/v10 v10.4.2/go.mod h1:nlOn6nFhuKACm19sB/8EGNn9GlaMV7XkbRSipzJ0Ii4=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
github.com/gogf/gf/v2 v2.10.0 h1:rzDROlyqGMe/eM6dCalSR8dZOuMIdLhmxKSH1DGhbFs=
github.com/gogf/gf/v2 v2.10.0/go.mod h1:Svl1N+E8G/QshU2DUbh/3J/AJauqCgUnxHurXWR4Qx0=
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/golang/protobuf v1.3.3 h1:gyjaxf+svBWX08ZjK86iN9geUJF0H6gp2IRKX6Nf6/I=
github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw=
github.com/golang/protobuf v1.3.5 h1:F768QJ1E9tib+q5Sc8MkdJi1RxLTbRcTf8LJV56aRls=
github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ=
github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
@ -67,16 +55,14 @@ github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/json-iterator/go v1.1.9 h1:9yzud/Ht36ygwatGx56VwCZtlI/2AD15T1X2sjSuGns=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.2.0 h1:hpXL4XnriNwQ/ABnpepYM/1vCLWNDfUNts8dX3xTG6Y=
github.com/leodido/go-urn v1.2.0/go.mod h1:+8+nEpDfqqsY+g338gtMEUOtuK+4dEMhiQEgxpxOKII=
github.com/leodido/go-urn v1.2.4 h1:XlAE/cm/ms7TE/VMVoduSpNBoyc2dOxHs5MZSwAN63Q=
github.com/leodido/go-urn v1.2.4/go.mod h1:7ZrI8mTSeBSHl/UaRyKQW1qZeMgak41ANeCNaVckg+4=
github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE=
github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0=
github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA=
@ -87,12 +73,12 @@ github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWE
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o4kU=
github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421 h1:ZqeYNhU3OHLH3mGKHDcjJRFFRrJa6eAM5H+CtDdOsPc=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742 h1:Esafd1046DLDQ0W1YjYsBW+p8U2u7vzgW2SQVmlNazg=
github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/olekukonko/errors v1.1.0 h1:RNuGIh15QdDenh+hNvKrJkmxxjV4hcS50Db478Ou5sM=
github.com/olekukonko/errors v1.1.0/go.mod h1:ppzxA5jBKcO1vIpCXQ9ZqgDh8iwODz6OXIGKU8r5m4Y=
github.com/olekukonko/ll v0.0.9 h1:Y+1YqDfVkqMWuEQMclsF9HUR5+a82+dxJuL1HHSRpxI=
@ -103,9 +89,6 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/remyoudompheng/bigfft v0.0.0-20200410134404-eec4a21b6bb0/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE=
github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
@ -123,22 +106,15 @@ github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3A
github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
github.com/ugorji/go v1.1.7/go.mod h1:kZn38zHttfInRq0xu/PH0az30d+z6vm202qpg1oXVMw=
github.com/ugorji/go v1.1.14/go.mod h1:8lpbXS0IfYpSwsLmwWThGZ7Qm2dNUIb0/kcsxHbwNpM=
github.com/ugorji/go/codec v1.1.7 h1:2SvQaVZ1ouYrrKKwoSk2pzd4A9evlKJb9oTL+OaLUSs=
github.com/ugorji/go/codec v1.1.7/go.mod h1:Ax+UKWsSmolVDwsd+7N3ZtXu+yMGCf907BLYF3GoBXY=
github.com/ugorji/go/codec v1.1.14 h1:3kmVO19VOoEBcborYacgFopagfu1s1+73nR7uRuN0lM=
github.com/ugorji/go/codec v1.1.14/go.mod h1:CburFl4ZXbHpzvp1gzm2I8tpnFk9XPHOJwIheJViqKk=
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8=
@ -184,18 +160,11 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs
gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
gorm.io/driver/sqlite v1.6.0 h1:WHRRrIiulaPiPFmDcod6prc4l2VGVWHz80KspNsxSfQ=
gorm.io/driver/sqlite v1.6.0/go.mod h1:AO9V1qIQddBESngQUKWL9yoH93HIeA1X6V633rBwyT8=
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
modernc.org/libc v1.22.6 h1:cbXU8R+A6aOjRuhsFh3nbDWXO/Hs4ClJRXYB11KmPDo=
modernc.org/libc v1.22.6/go.mod h1:jj+Z7dTNX8fBScMVNRAYZ/jF91K8fdT2hYMThc3YjBY=
modernc.org/mathutil v1.5.0 h1:rV0Ko/6SfM+8G+yKiyI830l3Wuz1zRutdslNoQ0kfiQ=
modernc.org/mathutil v1.5.0/go.mod h1:mZW8CKdRPY1v87qxC/wUdX5O1qDzXMP5TH3wjfpga6E=
modernc.org/memory v1.5.0 h1:N+/8c5rE6EqugZwHii4IFsaJ7MUhoWX07J5tC/iI5Ds=
modernc.org/memory v1.5.0/go.mod h1:PkUhL0Mugw21sHPeskwZW4D6VscE/GQJOnIpCnW6pSU=
modernc.org/sqlite v1.23.1 h1:nrSBg4aRQQwq59JpvGEQ15tNxoO5pX/kUjcRNwSAGQM=
modernc.org/sqlite v1.23.1/go.mod h1:OrDj17Mggn6MhE+iPbBNf7RGKODDE9NFT0f3EwDzJqk=

View File

@ -12,17 +12,8 @@ func ErrorHandler() gin.HandlerFunc {
return func(c *gin.Context) {
defer func() {
if err := recover(); err != nil {
res := response.Error(c).SetCode(http.StatusInternalServerError)
switch e := err.(type) {
case string:
res = res.SetMsg(e)
case error:
res = res.SetMsg(e.Error())
default:
res = res.SetMsg("服务器内部异常,请稍后重试")
}
res.End()
log.Error("发生panic=》", "path", c.Request.URL.Path, ",method:", c.Request.Method, ",", err)
log.Error("发生panic", "path", c.Request.URL.Path, "method", c.Request.Method, err)
response.Error(c).SetCode(http.StatusInternalServerError).SetMsg("服务器内部错误").End()
c.Abort()
}
}()

View File

@ -1,70 +0,0 @@
package utils
import (
"errors"
"time"
"github.com/golang-jwt/jwt/v5"
)
var (
ErrTokenInvalid = errors.New("token无效")
ErrTokenExpired = errors.New("token已过期")
)
// JWTClaims JWT声明
type JWTClaims struct {
UserID int `json:"user_id"`
Username string `json:"username"`
jwt.RegisteredClaims
}
// GenerateToken 生成JWT Token
func GenerateToken(userID int, username, secret string, expiresInHours int) (string, error) {
claims := JWTClaims{
UserID: userID,
Username: username,
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(time.Now().Add(time.Duration(expiresInHours) * time.Hour)),
IssuedAt: jwt.NewNumericDate(time.Now()),
NotBefore: jwt.NewNumericDate(time.Now()),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
return token.SignedString([]byte(secret))
}
// ParseToken 解析JWT Token
func ParseToken(tokenString, secret string) (*JWTClaims, error) {
token, err := jwt.ParseWithClaims(tokenString, &JWTClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("签名方法无效")
}
return []byte(secret), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(*JWTClaims); ok && token.Valid {
return claims, nil
}
return nil, ErrTokenInvalid
}
// ValidateToken 验证Token是否有效
func ValidateToken(tokenString, secret string) error {
claims, err := ParseToken(tokenString, secret)
if err != nil {
return err
}
if claims.ExpiresAt != nil && claims.ExpiresAt.Time.Before(time.Now()) {
return ErrTokenExpired
}
return nil
}

View File

@ -1,44 +0,0 @@
package valid
import (
"github.com/gin-gonic/gin"
"github.com/gogf/gf/v2/errors/gerror"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/util/gconv"
)
// ValidToStruct 验证参数并返回结构体
func ValidToStruct[T any](c *gin.Context) (object *T) {
obj := new(T)
if err := c.Bind(obj); err != nil {
panic(err)
}
if err := g.Validator().Data(obj).Run(c); err != nil {
panic(gerror.Current(err).Error())
}
return obj
}
// ValidToMap 验证参数并返回结构体
func ValidToMap[T any](c *gin.Context) (object map[string]any) {
obj := new(T)
if err := c.Bind(obj); err != nil {
panic(err)
}
if err := g.Validator().Data(obj).Run(c); err != nil {
panic(gerror.Current(err).Error())
}
return gconv.Map(obj)
}
// ValidToStructAndMap 验证参数并返回map
func ValidToStructAndMap[T any](c *gin.Context) (stru *T, object map[string]any) {
obj := new(T)
if err := c.Bind(obj); err != nil {
panic(err)
}
if err := g.Validator().Data(obj).Run(c); err != nil {
panic(gerror.Current(err).Error())
}
return obj, gconv.Map(obj)
}