add: some user api and all api desc

This commit is contained in:
wwweww
2026-02-27 19:17:01 +08:00
parent a0c720eb2f
commit 5930fb0dde
156 changed files with 9457 additions and 1086 deletions
+2 -2
View File
@@ -40,7 +40,7 @@ func StructToStruct(src, dst interface{}) error
```go
import "app/common/converter"
// 单个 model 转 pb
// 单个 models 转 pb
user, _ := m.FindOne(ctx, userId)
pbUser := &pb.Users{}
converter.StructToStruct(user, pbUser)
@@ -63,7 +63,7 @@ func SliceToSlice(src interface{}, dstSliceType interface{}) (interface{}, error
**示例:**
```go
// 多个 model 转 pb
// 多个 models 转 pb
users := []*models.Users{user1, user2, user3}
pbUsersIface, _ := converter.SliceToSlice(users, []*pb.Users{})
pbUsers := pbUsersIface.([]*pb.Users)
+207 -207
View File
@@ -1,207 +1,207 @@
package converter
import (
"database/sql"
"reflect"
"time"
)
// StructToStruct 通用结构体转换函数,利用反射将源结构体的字段值复制到目标结构体
// src: 源结构体(通常是 model)
// dst: 目标结构体(通常是 pb),必须是指针
// 支持的自动转换:
// - time.Time -> int64 (Unix 时间戳)
// - sql.NullTime -> int64 (如果有效)
// - sql.NullInt64 -> int64
// - sql.NullString -> string
// - 相同名称和兼容类型的字段
func StructToStruct(src, dst interface{}) error {
if src == nil {
return nil
}
srcVal := reflect.ValueOf(src)
dstVal := reflect.ValueOf(dst)
// 确保 dst 是指针
if dstVal.Kind() != reflect.Ptr {
return newError("destination must be a pointer")
}
dstVal = dstVal.Elem()
// 如果 src 是指针,解引用
if srcVal.Kind() == reflect.Ptr {
srcVal = srcVal.Elem()
}
// 都必须是结构体
if srcVal.Kind() != reflect.Struct || dstVal.Kind() != reflect.Struct {
return newError("both source and destination must be structs")
}
srcType := srcVal.Type()
// 遍历源结构体的所有字段
for i := 0; i < srcVal.NumField(); i++ {
srcField := srcVal.Field(i)
srcFieldName := srcType.Field(i).Name
// 在目标结构体中查找同名字段
dstField := dstVal.FieldByName(srcFieldName)
if !dstField.IsValid() || !dstField.CanSet() {
continue
}
// 进行类型转换和赋值
if err := assignValue(srcField, dstField); err != nil {
continue // 如果单个字段转换失败,继续处理其他字段
}
}
return nil
}
// assignValue 尝试将源字段值赋给目标字段
func assignValue(srcField, dstField reflect.Value) error {
// 如果是可直接赋值的类型
if srcField.Type() == dstField.Type() {
dstField.Set(srcField)
return nil
}
srcType := srcField.Type()
dstType := dstField.Type()
// 处理 time.Time -> int64 的转换
if srcType == reflect.TypeOf(time.Time{}) && dstType.Kind() == reflect.Int64 {
t := srcField.Interface().(time.Time)
dstField.SetInt(t.Unix())
return nil
}
// 处理 sql.NullTime -> int64 的转换
if srcType == reflect.TypeOf(sql.NullTime{}) && dstType.Kind() == reflect.Int64 {
nt := srcField.Interface().(sql.NullTime)
if nt.Valid {
dstField.SetInt(nt.Time.Unix())
}
return nil
}
// 处理 sql.NullTime -> time.Time 的转换
if srcType == reflect.TypeOf(sql.NullTime{}) && dstType == reflect.TypeOf(time.Time{}) {
nt := srcField.Interface().(sql.NullTime)
if nt.Valid {
dstField.Set(reflect.ValueOf(nt.Time))
}
return nil
}
// 处理 sql.NullInt64 -> int64 的转换
if srcType == reflect.TypeOf(sql.NullInt64{}) && dstType.Kind() == reflect.Int64 {
ni := srcField.Interface().(sql.NullInt64)
if ni.Valid {
dstField.SetInt(ni.Int64)
}
return nil
}
// 处理 sql.NullString -> string 的转换
if srcType == reflect.TypeOf(sql.NullString{}) && dstType.Kind() == reflect.String {
ns := srcField.Interface().(sql.NullString)
if ns.Valid {
dstField.SetString(ns.String)
}
return nil
}
// 处理 sql.NullBool -> bool 的转换
if srcType == reflect.TypeOf(sql.NullBool{}) && dstType.Kind() == reflect.Bool {
nb := srcField.Interface().(sql.NullBool)
if nb.Valid {
dstField.SetBool(nb.Bool)
}
return nil
}
// 处理 int -> int64 的转换
if srcType.Kind() == reflect.Int && dstType.Kind() == reflect.Int64 {
dstField.SetInt(int64(srcField.Int()))
return nil
}
// 处理 int64 -> int 的转换
if srcType.Kind() == reflect.Int64 && dstType.Kind() == reflect.Int {
dstField.SetInt(srcField.Int())
return nil
}
// 处理 string -> string(某些情况下可能存在复制)
if srcType.Kind() == reflect.String && dstType.Kind() == reflect.String {
dstField.SetString(srcField.String())
return nil
}
// 处理 bool -> bool
if srcType.Kind() == reflect.Bool && dstType.Kind() == reflect.Bool {
dstField.SetBool(srcField.Bool())
return nil
}
return newError("unsupported type conversion from " + srcType.String() + " to " + dstType.String())
}
// SliceToSlice 通用切片转换函数,使用 StructToStruct 转换每个元素
func SliceToSlice(src interface{}, dstSliceType interface{}) (interface{}, error) {
srcVal := reflect.ValueOf(src)
// src 必须是切片
if srcVal.Kind() != reflect.Slice {
return nil, newError("source must be a slice")
}
// 获取原始 dst slice type
dstSliceVal := reflect.ValueOf(dstSliceType)
if dstSliceVal.Kind() != reflect.Slice {
return nil, newError("dstSliceType must be a slice type")
}
dstSliceElemType := dstSliceVal.Type().Elem()
// 创建新的目标切片
dstSlice := reflect.MakeSlice(dstSliceVal.Type(), srcVal.Len(), srcVal.Len())
// 逐个转换元素
for i := 0; i < srcVal.Len(); i++ {
srcElem := srcVal.Index(i)
dstElem := reflect.New(dstSliceElemType)
// 如果 src 元素是指针,需要解引用
if srcElem.Kind() == reflect.Ptr {
srcElem = srcElem.Elem()
}
// 转换单个元素
dstElemIface := dstElem.Interface()
if err := StructToStruct(srcElem.Interface(), dstElemIface); err != nil {
return nil, err
}
dstSlice.Index(i).Set(dstElem.Elem())
}
return dstSlice.Interface(), nil
}
type Error struct {
msg string
}
func (e *Error) Error() string {
return e.msg
}
func newError(msg string) error {
return &Error{msg: msg}
}
package converter
import (
"database/sql"
"reflect"
"time"
)
// StructToStruct 通用结构体转换函数,利用反射将源结构体的字段值复制到目标结构体
// src: 源结构体(通常是 models
// dst: 目标结构体(通常是 pb),必须是指针
// 支持的自动转换:
// - time.Time -> int64 (Unix 时间戳)
// - sql.NullTime -> int64 (如果有效)
// - sql.NullInt64 -> int64
// - sql.NullString -> string
// - 相同名称和兼容类型的字段
func StructToStruct(src, dst interface{}) error {
if src == nil {
return nil
}
srcVal := reflect.ValueOf(src)
dstVal := reflect.ValueOf(dst)
// 确保 dst 是指针
if dstVal.Kind() != reflect.Ptr {
return newError("destination must be a pointer")
}
dstVal = dstVal.Elem()
// 如果 src 是指针,解引用
if srcVal.Kind() == reflect.Ptr {
srcVal = srcVal.Elem()
}
// 都必须是结构体
if srcVal.Kind() != reflect.Struct || dstVal.Kind() != reflect.Struct {
return newError("both source and destination must be structs")
}
srcType := srcVal.Type()
// 遍历源结构体的所有字段
for i := 0; i < srcVal.NumField(); i++ {
srcField := srcVal.Field(i)
srcFieldName := srcType.Field(i).Name
// 在目标结构体中查找同名字段
dstField := dstVal.FieldByName(srcFieldName)
if !dstField.IsValid() || !dstField.CanSet() {
continue
}
// 进行类型转换和赋值
if err := assignValue(srcField, dstField); err != nil {
continue // 如果单个字段转换失败,继续处理其他字段
}
}
return nil
}
// assignValue 尝试将源字段值赋给目标字段
func assignValue(srcField, dstField reflect.Value) error {
// 如果是可直接赋值的类型
if srcField.Type() == dstField.Type() {
dstField.Set(srcField)
return nil
}
srcType := srcField.Type()
dstType := dstField.Type()
// 处理 time.Time -> int64 的转换
if srcType == reflect.TypeOf(time.Time{}) && dstType.Kind() == reflect.Int64 {
t := srcField.Interface().(time.Time)
dstField.SetInt(t.Unix())
return nil
}
// 处理 sql.NullTime -> int64 的转换
if srcType == reflect.TypeOf(sql.NullTime{}) && dstType.Kind() == reflect.Int64 {
nt := srcField.Interface().(sql.NullTime)
if nt.Valid {
dstField.SetInt(nt.Time.Unix())
}
return nil
}
// 处理 sql.NullTime -> time.Time 的转换
if srcType == reflect.TypeOf(sql.NullTime{}) && dstType == reflect.TypeOf(time.Time{}) {
nt := srcField.Interface().(sql.NullTime)
if nt.Valid {
dstField.Set(reflect.ValueOf(nt.Time))
}
return nil
}
// 处理 sql.NullInt64 -> int64 的转换
if srcType == reflect.TypeOf(sql.NullInt64{}) && dstType.Kind() == reflect.Int64 {
ni := srcField.Interface().(sql.NullInt64)
if ni.Valid {
dstField.SetInt(ni.Int64)
}
return nil
}
// 处理 sql.NullString -> string 的转换
if srcType == reflect.TypeOf(sql.NullString{}) && dstType.Kind() == reflect.String {
ns := srcField.Interface().(sql.NullString)
if ns.Valid {
dstField.SetString(ns.String)
}
return nil
}
// 处理 sql.NullBool -> bool 的转换
if srcType == reflect.TypeOf(sql.NullBool{}) && dstType.Kind() == reflect.Bool {
nb := srcField.Interface().(sql.NullBool)
if nb.Valid {
dstField.SetBool(nb.Bool)
}
return nil
}
// 处理 int -> int64 的转换
if srcType.Kind() == reflect.Int && dstType.Kind() == reflect.Int64 {
dstField.SetInt(int64(srcField.Int()))
return nil
}
// 处理 int64 -> int 的转换
if srcType.Kind() == reflect.Int64 && dstType.Kind() == reflect.Int {
dstField.SetInt(srcField.Int())
return nil
}
// 处理 string -> string(某些情况下可能存在复制)
if srcType.Kind() == reflect.String && dstType.Kind() == reflect.String {
dstField.SetString(srcField.String())
return nil
}
// 处理 bool -> bool
if srcType.Kind() == reflect.Bool && dstType.Kind() == reflect.Bool {
dstField.SetBool(srcField.Bool())
return nil
}
return newError("unsupported type conversion from " + srcType.String() + " to " + dstType.String())
}
// SliceToSlice 通用切片转换函数,使用 StructToStruct 转换每个元素
func SliceToSlice(src interface{}, dstSliceType interface{}) (interface{}, error) {
srcVal := reflect.ValueOf(src)
// src 必须是切片
if srcVal.Kind() != reflect.Slice {
return nil, newError("source must be a slice")
}
// 获取原始 dst slice type
dstSliceVal := reflect.ValueOf(dstSliceType)
if dstSliceVal.Kind() != reflect.Slice {
return nil, newError("dstSliceType must be a slice type")
}
dstSliceElemType := dstSliceVal.Type().Elem()
// 创建新的目标切片
dstSlice := reflect.MakeSlice(dstSliceVal.Type(), srcVal.Len(), srcVal.Len())
// 逐个转换元素
for i := 0; i < srcVal.Len(); i++ {
srcElem := srcVal.Index(i)
dstElem := reflect.New(dstSliceElemType)
// 如果 src 元素是指针,需要解引用
if srcElem.Kind() == reflect.Ptr {
srcElem = srcElem.Elem()
}
// 转换单个元素
dstElemIface := dstElem.Interface()
if err := StructToStruct(srcElem.Interface(), dstElemIface); err != nil {
return nil, err
}
dstSlice.Index(i).Set(dstElem.Elem())
}
return dstSlice.Interface(), nil
}
type Error struct {
msg string
}
func (e *Error) Error() string {
return e.msg
}
func newError(msg string) error {
return &Error{msg: msg}
}
+96
View File
@@ -0,0 +1,96 @@
package contextx
import (
"context"
"errors"
"github.com/zeromicro/go-zero/core/logx"
)
var (
ERRILLEGALUSER = errors.New("illegal user")
ERRILLEGALTOKEN = errors.New("illegal token")
ERRILLEGALREQUESTID = errors.New("illegal request id")
ERRILLEGALISADMIN = errors.New("illegal is_admin")
)
func WithRequestId(c context.Context, requestId string) context.Context {
return context.WithValue(c, "request_id", requestId)
}
func RequestIdFrom(c context.Context) (string, error) {
requestID, ok := c.Value("request_id").(string)
if !ok {
return "", errors.New("request_id not found in context")
}
return requestID, nil
}
func WithToken(c context.Context, token string) context.Context {
return context.WithValue(c, "token", token)
}
func TokenFrom(c context.Context) (string, error) {
token, ok := c.Value("token").(string)
if !ok {
return "", errors.New("token not found in context")
}
return token, nil
}
func WithUserID(c context.Context, id int64) context.Context {
return context.WithValue(c, "user_id", id)
}
func UserIDFrom(c context.Context) (int64, error) {
if userID, ok := c.Value("user_id").(int64); !ok {
return 0, errors.New("user_id not found in context")
} else {
return userID, nil
}
}
// request_id is used for tracing and logging, not for authentication or authorization,
// so it can be set by clients or generated by the server.
func WithRequestID(c context.Context, requestID string) context.Context {
return context.WithValue(c, "request_id", requestID)
}
func RequestIDFrom(c context.Context) (string, error) {
if requestID, ok := c.Value("request_id").(string); !ok {
return "", errors.New("request_id not found in context")
} else {
return requestID, nil
}
}
func WithIsAdmin(c context.Context, isAdmin bool) context.Context {
return context.WithValue(c, "is_admin", isAdmin)
}
func IsAdminFrom(c context.Context) (bool, error) {
if isAdmin, ok := c.Value("is_admin").(bool); !ok {
return false, errors.New("is_admin not found in context")
} else {
return isAdmin, nil
}
}
func AdminIdFrom(c context.Context) (adminId int64, err error) {
adminId, err = UserIDFrom(c)
if err != nil {
logx.Errorf("get user id from context: %v", err)
return 0, ERRILLEGALUSER
}
isAdmin, err := IsAdminFrom(c)
if err != nil {
logx.Errorf("get isAdmin from context: %v", err)
return 0, ERRILLEGALUSER
}
if !isAdmin {
logx.Errorf("user %d is not admin", adminId)
return 0, ERRILLEGALUSER
}
return
}
+30
View File
@@ -0,0 +1,30 @@
package httpx
import (
"net/http"
"strconv"
)
func GetUserIdFromHeader(header http.Header) (int64, error) {
id := header.Get("x-auth-user-id")
if id == "" {
return 0, http.ErrNoCookie
}
intId, err := strconv.ParseInt(id, 10, 64)
if err != nil {
return 0, err
}
return intId, nil
}
func GetUserIsAdminFromHeader(header http.Header) (bool, error) {
isAdmin := header.Get("x-auth-is-admin")
if isAdmin == "" {
return false, nil
}
boolIsAdmin, err := strconv.ParseBool(isAdmin)
if err != nil {
return false, err
}
return boolIsAdmin, nil
}
@@ -1,25 +1,25 @@
package utils
import (
"golang.org/x/crypto/bcrypt"
)
const (
// bcrypt 密钥成本
bcryptCost = bcrypt.DefaultCost
)
// HashPassword 对密码进行哈希加密
func HashPassword(password string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
if err != nil {
return "", err
}
return string(hash), nil
}
// VerifyPassword 验证密码是否正确
func VerifyPassword(hashedPassword, password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
return err == nil
}
package pwdUtils
import (
"golang.org/x/crypto/bcrypt"
)
const (
// bcrypt 密钥成本
bcryptCost = bcrypt.DefaultCost
)
// HashPassword 对密码进行哈希加密
func HashPassword(password string) (string, error) {
hash, err := bcrypt.GenerateFromPassword([]byte(password), bcryptCost)
if err != nil {
return "", err
}
return string(hash), nil
}
// VerifyPassword 验证密码是否正确
func VerifyPassword(hashedPassword, password string) bool {
err := bcrypt.CompareHashAndPassword([]byte(hashedPassword), []byte(password))
return err == nil
}
@@ -1,4 +1,4 @@
package utils
package responses
import "encoding/json"