package base import ( "encoding/json" "github.com/gin-gonic/gin" "go_server/model/common/response" "gorm.io/gorm" "reflect" ) // gin 封装模型curd处理 // 使用范型 较为鸡肋 不建议使用 type ModelBaseHandler[T any] struct { } func NewBaseHandler[T any](t *T) *ModelBaseHandler[T] { return &ModelBaseHandler[T]{} } func (*ModelBaseHandler[T]) DeleteOne(c *gin.Context, db *gorm.DB) { req := new(BaseRequest) if err := c.ShouldBindJSON(req); err != nil { response.Resp(c, err.Error()) return } if req.Id == 0 { response.Resp(c, "id is zero") return } tempInfo := new(T) // 先通过Id获取记录' if err := db.First(&tempInfo, req.Id).Error; err != nil { response.Resp(c, err.Error()) return } exCmd := db.Model(&tempInfo).Delete(&tempInfo) if exCmd.RowsAffected != 1 { response.Resp(c, "delete fail") return } if exCmd.Error != nil { response.Resp(c, exCmd.Error.Error()) return } response.Resp(c) return } func (*ModelBaseHandler[T]) UpdateOne(c *gin.Context, db *gorm.DB) { req := new(BaseRequest) if err := c.BindJSON(req); err != nil { response.Resp(c, err.Error()) return } if req.Id == 0 { response.Resp(c, "id is zero") return } tempInfo := new(T) // 先通过Id获取记录' if err := db.First(&tempInfo, req.Id).Error; err != nil { response.Resp(c, err.Error()) return } // 构建 json tag -> 数据库列名 的映射,用 map 更新以支持零值字段 updateData := new(T) body, _ := json.Marshal(req.Data) if err := json.Unmarshal(body, updateData); err != nil { response.Resp(c, err.Error()) return } // 将 struct 转为 map,保留零值字段 updateMap := structToMapWithZero(updateData, req.Data) dbEx := db.Model(&tempInfo).Where("id", req.Id). Updates(updateMap) if dbEx.Error != nil { response.Resp(c, dbEx.Error.Error()) return } if dbEx.RowsAffected == 0 { response.Resp(c, "update fail") return } if err := db.First(&tempInfo, req.Id).Error; err != nil { response.Resp(c, err.Error()) return } response.Resp(c, map[string]any{ "info": tempInfo, "rowsAffected": dbEx.RowsAffected, }) return } func (*ModelBaseHandler[T]) Get(c *gin.Context, db *gorm.DB) { req := new(BaseRequest) if err := c.BindQuery(req); err != nil { response.Resp(c, err.Error()) return } if req.Id == 0 { response.Resp(c, "id is zero") return } tempInfo := new(T) // 先通过Id获取记录' if err := db.First(&tempInfo, req.Id).Error; err != nil { response.Resp(c, err.Error()) return } response.Resp(c, map[string]any{ "info": tempInfo, }) return } func (*ModelBaseHandler[T]) Create(c *gin.Context, db *gorm.DB) { req := new(BaseRequest) if err := c.BindJSON(req); err != nil { response.Resp(c, err.Error()) return } info := new(T) body, _ := json.Marshal(req.Data) if err := json.Unmarshal(body, info); err != nil { response.Resp(c, err.Error()) return } if err := db.Create(info).Error; err != nil { response.Resp(c, err.Error()) return } response.Resp(c, map[string]any{ "info": info, }) } func (*ModelBaseHandler[T]) List(c *gin.Context, db *gorm.DB) { req := NewListRequest[T]() if err := c.BindQuery(req); err != nil { response.Resp(c, err.Error()) return } resp, err := req.Query(db) if err != nil { response.Resp(c, err.Error()) return } response.Resp(c, resp) return } // structToMapWithZero 将 struct 转为 map,只包含前端实际传递的字段(含零值) // dataMap 是前端传来的原始 map,用于判断哪些字段是前端实际传递的 func structToMapWithZero(s any, dataMap map[string]any) map[string]any { result := make(map[string]any) v := reflect.ValueOf(s) if v.Kind() == reflect.Ptr { v = v.Elem() } t := v.Type() for i := 0; i < t.NumField(); i++ { field := t.Field(i) jsonTag := field.Tag.Get("json") if jsonTag == "" || jsonTag == "-" { continue } // 去掉 omitempty 等选项 if idx := len(jsonTag); idx > 0 { parts := jsonTag for j := 0; j < len(parts); j++ { if parts[j] == ',' { jsonTag = parts[:j] break } } } // 只更新前端实际传递的字段 if _, exists := dataMap[jsonTag]; !exists { continue } // 获取 gorm column tag 作为数据库列名 gormTag := field.Tag.Get("gorm") colName := "" if gormTag != "" { for _, part := range splitGormTag(gormTag) { if len(part) > 7 && part[:7] == "column:" { colName = part[7:] break } } } if colName == "" { colName = jsonTag } result[colName] = v.Field(i).Interface() } return result } func splitGormTag(tag string) []string { var parts []string start := 0 for i := 0; i <= len(tag); i++ { if i == len(tag) || tag[i] == ';' { parts = append(parts, tag[start:i]) start = i + 1 } } return parts }