diff --git a/.gitea/workflows/build-push.yml b/.gitea/workflows/build-push.yml deleted file mode 100644 index d0e5b67..0000000 --- a/.gitea/workflows/build-push.yml +++ /dev/null @@ -1,24 +0,0 @@ -name: Build image -on: [push] -jobs: - build: - runs-on: ubuntu-latest - steps: - - uses: http://192.168.31.55:13000/timerzz/setup-go@v4 - env: - HTTPS_PROXY: http://192.168.31.55:10809 - with: - go-version: '1.22.x' - - uses: http://192.168.31.55:13000/timerzz/checkout@v4 - - uses: http://192.168.31.55:13000/timerzz/setup-ko@v0.6 - with: - version: v0.15.4 - env: - KO_DOCKER_REPO: 192.168.31.55:5000/kedaya/${REPO_NAME} - HTTP_PROXY: http://192.168.31.55:10809 - HTTPS_PROXY: http://192.168.31.55:10809 - - run: ko build --bare ./cmd - env: - KO_DOCKER_REPO: 192.168.31.55:5000/kedaya/${REPO_NAME} - HTTP_PROXY: http://192.168.31.55:10809 - HTTPS_PROXY: http://192.168.31.55:10809 \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..32e1d82 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +*.o +*.a +*.so +_obj +_test +*.[568vq] +[568vq].out +*.cgo1.go +*.cgo2.c +_cgo_defun.c +_cgo_gotypes.go +_cgo_export.* +_testmain.go +*.exe +*.exe~ +*.test +*.prof +*.rar +*.zip +*.gz +*.psd +*.bmd +*.cfg +*.pptx +*.log +*nohup.out +*settings.pyc +*.sublime-project +*.sublime-workspace +!.gitkeep +.DS_Store +/.idea +/.vscode +/output +*.local.yml +dumped_hertz_remote_config.json + +/cfg_dev/ diff --git a/.ko.yaml b/.ko.yaml deleted file mode 100644 index b0dd9d1..0000000 --- a/.ko.yaml +++ /dev/null @@ -1,16 +0,0 @@ -defaultPlatforms: - - linux/amd64 -defaultBaseImage: alpine:latest -builds: - - id: user - dir: . # default is . - main: ./cmd - env: - - CGO_ENABLED=0 - - GOPROXY=https://goproxy.cn,direct - - GOPRIVATE=gitea.timerzz.com - - GONOSUMDB=gitea.timerzz.com - - GONOPROXY=gitea.timerzz.com - ldflags: - - -s -w - - -extldflags "-static" diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..2b14041 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,31 @@ +# 使用官方Golang最新版本作为构建环境 +FROM golang:latest AS builder + +WORKDIR /app + +# 复制项目文件 +COPY . . + +# 构建应用 +RUN apt update && apt install -y xz-utils && CGO_ENABLED=0 GOOS=linux go build -ldflags="-s -w" -o main . + +# upx压缩 +RUN wget -c $(curl -s https://api.github.com/repos/upx/upx/releases/latest | grep browser_download_url | grep linux |grep amd64| cut -d'"' -f4) -O - |tar --strip-components 1 -xvJf - && ./upx ./main + +# 使用轻量级的Alpine镜像作为运行时环境 +FROM alpine:latest + +# 安装必要的工具 +RUN apk --no-cache add ca-certificates + +# 设置工作目录 +WORKDIR /root/ + +# 从构建阶段复制可执行文件 +COPY --from=builder /app/main . + +# 暴露端口 +EXPOSE 80 + +# 设置容器启动命令 +CMD ["./main"] \ No newline at end of file diff --git a/biz/dal/db/user.go b/biz/dal/db/user.go new file mode 100644 index 0000000..0441ed9 --- /dev/null +++ b/biz/dal/db/user.go @@ -0,0 +1,94 @@ +package db + +import ( + "errors" + + "gitlab.com/kedaya_mp/user/biz/model/common" + "gorm.io/gorm" +) + +type Scoper interface { + Scope(db *gorm.DB) *gorm.DB +} + +type UserStore interface { + Create(*common.User) (*common.User, error) + Find(Scoper, ...string) (*common.User, error) + Exist(Scoper) (bool, error) + Update(*common.User, ...string) error + AutoMigrate() error +} + +type userStore struct { + db *gorm.DB +} + +func NewUserStore(db *gorm.DB) UserStore { + return &userStore{db: db} +} + +func (s *userStore) AutoMigrate() error { + return s.db.AutoMigrate(&common.User{}) +} + +type FindUserQuery struct { + ID int64 + Phone string + WxUnionID string +} + +func (f *FindUserQuery) Scope(db *gorm.DB) *gorm.DB { + if f.ID != 0 { + db = db.Where("id = ?", f.ID) + } + if f.Phone != "" { + db = db.Where("phone = ?", f.Phone) + } + if f.WxUnionID != "" { + db = db.Where("wx_union_id = ?", f.WxUnionID) + } + return db +} + +// Create 创建用户 +func (s *userStore) Create(u *common.User) (*common.User, error) { + if err := s.db.Create(u).Error; err != nil { + return nil, err + } + return u, nil +} + +// Find 查找用户 +func (s *userStore) Find(scoper Scoper, selects ...string) (*common.User, error) { + var dbUser common.User + db := s.db + if len(selects) > 0 { + db = db.Select(selects) + } + if err := scoper.Scope(db).First(&dbUser).Error; err != nil { + if errors.Is(err, gorm.ErrRecordNotFound) { + return nil, nil + } + return nil, err + } + return &dbUser, nil +} + +// Exist 检查用户是否存在 +func (s *userStore) Exist(scoper Scoper) (bool, error) { + var count int64 + if err := scoper.Scope(s.db).Model(&common.User{}).Count(&count).Error; err != nil { + return false, err + } + return count > 0, nil +} + +// Update 更新用户信息 +func (s *userStore) Update(u *common.User, selects ...string) error { + return s.db.Transaction(func(tx *gorm.DB) error { + if len(selects) > 0 { + tx = tx.Select(selects) + } + return tx.Where("id =?", u.ID).Updates(u).Error + }) +} diff --git a/biz/handle/auth.go b/biz/handle/auth.go new file mode 100644 index 0000000..235b61e --- /dev/null +++ b/biz/handle/auth.go @@ -0,0 +1,45 @@ +package handle + +import ( + "context" + + "github.com/cloudwego/hertz/pkg/app" +) + +type LoginRequest struct { + WXCode string `json:"wx_code,omitempty"` + Phone string `json:"phone"` + Captcha string `json:"captcha,omitempty"` + Password string `json:"password,omitempty"` +} + +type Response[T any] struct { + Code int `json:"code"` + Message string `json:"message"` + Data T `json:"data"` +} +type LoginResponse struct { + Token string `json:"token"` + Expire string `json:"expire"` + Message string `json:"message"` + Code int `json:"code"` +} + +// Authenticator 登录 +// @Tags 用户模块 +// @Summary 登录接口 +// @Description 如果是微信登录,需要传递 WXcode和手机号,然后调用微信接口获取 openid 和 unionid。 +// 如果 unionid 已经存在,就直接登录, 如果不存在,就注册。 +// 如果是手机号登录,需要传递 phone 和 password, 如果手机号没注册,就直接注册 +// @Param request body LoginRequest true "请求参数" +// @Success 200 {object} LoginResponse "成功响应" +// @Accept application/json +// @Produce application/json +// @Router /api/v1/login [post] +func (h *UserHandler) Authenticator(ctx context.Context, c *app.RequestContext) (interface{}, error) { + var req LoginRequest + if err := c.BindJSON(&req); err != nil { + return nil, err + } + return h.userService.Login(ctx, req.Phone, req.Password, req.WXCode, req.Captcha) +} diff --git a/biz/handle/get.go b/biz/handle/get.go new file mode 100644 index 0000000..619662e --- /dev/null +++ b/biz/handle/get.go @@ -0,0 +1,72 @@ +package handle + +import ( + "context" + "errors" + + "github.com/cloudwego/hertz/pkg/app" + "gitlab.com/kedaya_mp/user/biz/model/common" + user_service "gitlab.com/kedaya_mp/user/biz/service" +) + +type UserHandler struct { + userService *user_service.UserService +} + +func NewUserHandler(userService *user_service.UserService) *UserHandler { + return &UserHandler{ + userService: userService, + } +} + +// GetUserByPhone 通过手机号获取用户信息 +// @Tags 用户模块 +// @Summary 获取用户信息接口 +// @Description 通过手机号获取用户信息,只返回用户的昵称、电话、性别、ID、创建时间和头像 +// @Param phone query string true "用户手机号" +// @Success 200 {object} Response[common.User] "成功响应" +// @Failure 400 {object} Response[string] "请求参数错误" +// @Failure 404 {object} Response[string] "用户不存在" +// @Failure 500 {object} Response[string] "服务器内部错误" +// @Security ApiKeyAuth +// @Accept application/json +// @Produce application/json +// @Router /api/v1/user [get] +func (h *UserHandler) GetUserByPhone(ctx context.Context, c *app.RequestContext) { + // 获取请求参数 + phone := c.Query("phone") + if phone == "" { + c.JSON(400, Response[string]{ + Code: 400, + Message: "手机号不能为空", + Data: "手机号参数缺失", + }) + return + } + + // 调用服务层获取用户信息 + user, err := h.userService.GetUserByPhone(phone) + if err != nil { + code := 500 + message := "获取用户信息失败" + + if errors.Is(err, user_service.ErrUserNotFound) { + code = 404 + message = "用户不存在" + } + + c.JSON(code, Response[string]{ + Code: code, + Message: message, + Data: err.Error(), + }) + return + } + + // 返回成功响应 + c.JSON(200, Response[common.User]{ + Code: 200, + Message: "获取用户信息成功", + Data: *user, + }) +} diff --git a/biz/model/common/user.go b/biz/model/common/user.go new file mode 100644 index 0000000..e769c7b --- /dev/null +++ b/biz/model/common/user.go @@ -0,0 +1,22 @@ +package common + +import "time" + +// 定义user结构体 +// 包括id,密码,用户名,手机号,头像,性别,微信unionid +type User struct { + ID int64 `json:"id,omitempty" gorm:"primaryKey;column:id"` + CreatedAt time.Time `json:"created_at" gorm:"column:created_at"` + Password string `json:"password,omitempty" gorm:"column:password"` + NickName string `json:"nick_name" gorm:"column:nick_name"` + Phone string `json:"phone" gorm:"column:phone"` + Avatar string `json:"avatar" gorm:"column:avatar"` + Gender int32 `json:"gender" gorm:"column:gender"` + WxUnionID string `json:"wx_union_id,omitempty" gorm:"column:wx_union_id,unique"` + WxSession string `json:"wx_session,omitempty" gorm:"column:wx_session"` +} + +// TableName 返回表名 +func (u *User) TableName() string { + return "users" +} diff --git a/biz/mw/jwt.go b/biz/mw/jwt.go new file mode 100644 index 0000000..3e05427 --- /dev/null +++ b/biz/mw/jwt.go @@ -0,0 +1,54 @@ +package mw + +import ( + "context" + "net/http" + "time" + + "github.com/cloudwego/hertz/pkg/app" + "github.com/cloudwego/hertz/pkg/common/hlog" + "github.com/hertz-contrib/jwt" + "gitlab.com/kedaya_mp/user/biz/handle" + "gitlab.com/kedaya_mp/user/biz/model/common" +) + +const ( + identityKey = "phone" +) + +func NewJwt(authenticator func(ctx context.Context, c *app.RequestContext) (interface{}, error)) *jwt.HertzJWTMiddleware { + middleware, err := jwt.New(&jwt.HertzJWTMiddleware{ + Realm: "kedaya-onecat-task", + Key: []byte("kedaya"), + Timeout: time.Hour * 24, + MaxRefresh: time.Hour, + IdentityKey: identityKey, + PayloadFunc: func(data interface{}) jwt.MapClaims { + if v, ok := data.(*common.User); ok { + return jwt.MapClaims{ + identityKey: v.Phone, + } + } + return jwt.MapClaims{} + }, + IdentityHandler: func(ctx context.Context, c *app.RequestContext) interface{} { + claims := jwt.ExtractClaims(ctx, c) + return &common.User{ + Phone: claims[identityKey].(string), + } + }, + Authenticator: authenticator, + LoginResponse: func(ctx context.Context, c *app.RequestContext, code int, token string, expire time.Time) { + c.JSON(http.StatusOK, handle.LoginResponse{ + Token: token, + Expire: expire.Format(time.RFC3339), + Message: "success", + Code: code, + }) + }, + }) + if err != nil { + hlog.Fatalf("JWT Error:" + err.Error()) + } + return middleware +} diff --git a/biz/router/healthz/healthz.go b/biz/router/healthz/healthz.go new file mode 100644 index 0000000..9edba3a --- /dev/null +++ b/biz/router/healthz/healthz.go @@ -0,0 +1,86 @@ +package healthz + +import ( + "context" + + "github.com/cloudwego/hertz/pkg/app" + "github.com/cloudwego/hertz/pkg/app/server" +) + +type Option struct { + living func() error + ready func() error +} +type healthz struct { + Option +} + +func (h *healthz) SetLiving(living func() error) *healthz { + h.living = living + return h +} + +func (h *healthz) SetReady(ready func() error) *healthz { + h.ready = ready + return h +} + +func (h *healthz) Living(ctx context.Context, c *app.RequestContext) { + if h.living == nil { + c.JSON(200, map[string]string{"ok": "true"}) + return + } + if err := h.living(); err != nil { + c.JSON(500, map[string]string{"ok": "false"}) + return + } + c.JSON(200, map[string]string{"ok": "true"}) +} +func (h *healthz) Ready(ctx context.Context, c *app.RequestContext) { + if h.ready == nil { + c.JSON(200, map[string]string{"ok": "true"}) + return + } + if err := h.ready(); err != nil { + c.JSON(500, map[string]string{"ok": "false"}) + return + } + c.JSON(200, map[string]string{"ok": "true"}) +} + +func (h *healthz) Registry(hertz *server.Hertz) { + hertz.GET("/living", h.Living) + hertz.GET("/ready", h.Ready) +} + +type Healthz interface { + SetLiving(living func() error) *healthz + SetReady(ready func() error) *healthz + Living(ctx context.Context, c *app.RequestContext) + Ready(ctx context.Context, c *app.RequestContext) + Registry(h *server.Hertz) +} + +type OptionFunc func(*Option) + +func NewHealthz(opts ...OptionFunc) Healthz { + var opt = Option{} + for _, o := range opts { + o(&opt) + } + return &healthz{ + Option: opt, + } +} + +func WithLiving(living func() error) OptionFunc { + return func(o *Option) { + o.living = living + } +} + +func WithReady(ready func() error) OptionFunc { + return func(o *Option) { + o.ready = ready + } +} diff --git a/biz/router/user/user.go b/biz/router/user/user.go new file mode 100644 index 0000000..4032489 --- /dev/null +++ b/biz/router/user/user.go @@ -0,0 +1,31 @@ +package user + +import ( + "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram" + "github.com/cloudwego/hertz/pkg/app/server" + "github.com/cloudwego/hertz/pkg/common/hlog" + "gitlab.com/kedaya_mp/user/biz/handle" + "gitlab.com/kedaya_mp/user/biz/mw" + user_service "gitlab.com/kedaya_mp/user/biz/service" + "gitlab.com/kedaya_mp/user/pkg/captcha" + "gorm.io/gorm" +) + +func Register(h *server.Hertz, db *gorm.DB, miniProgramApp *miniProgram.MiniProgram, captchaService captcha.Service) { + userService := user_service.NewUserService(db, miniProgramApp, captchaService) + if err := userService.Init(); err != nil { + hlog.Fatalf("UserService Init Error: %v", err) + } + userHandler := handle.NewUserHandler(userService) + authMiddleware := mw.NewJwt(userHandler.Authenticator) + + api := h.Group("/api/v1") + api.POST("/login", authMiddleware.LoginHandler) + + // 需要JWT认证的接口 + auth := api.Group("/") + auth.Use(authMiddleware.MiddlewareFunc()) + { + auth.GET("/user", userHandler.GetUserByPhone) + } +} diff --git a/biz/service/user_service.go b/biz/service/user_service.go new file mode 100644 index 0000000..dfc9564 --- /dev/null +++ b/biz/service/user_service.go @@ -0,0 +1,155 @@ +package service + +import ( + "context" + "errors" + + "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram" + "gitlab.com/kedaya_mp/user/biz/dal/db" + "gitlab.com/kedaya_mp/user/biz/model/common" + "gitlab.com/kedaya_mp/user/pkg/captcha" + "gitlab.com/kedaya_mp/user/pkg/crypto" + "gorm.io/gorm" +) + +var ( + ErrUserNotFound = errors.New("用户不存在") + ErrInvalidPassword = errors.New("密码错误") + ErrGetWXUserInfo = errors.New("获取微信用户信息失败") + ErrCreateUserFailed = errors.New("创建用户失败") +) + +type UserService struct { + store db.UserStore + miniProgramApp *miniProgram.MiniProgram + captchaService captcha.Service +} + +func NewUserService(_db *gorm.DB, miniProgramApp *miniProgram.MiniProgram, captchaService captcha.Service) *UserService { + return &UserService{ + store: db.NewUserStore(_db), + miniProgramApp: miniProgramApp, + captchaService: captchaService, + } +} + +func (s *UserService) Init() error { + if err := s.store.AutoMigrate(); err != nil { + return err + } + return nil +} + +func (s *UserService) LoginByPhone(phone, password string) (*common.User, error) { + user, err := s.store.Find(&db.FindUserQuery{Phone: phone}) + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + + if user == nil { + hashedPassword := crypto.HashPassword(password) + newUser := &common.User{ + Phone: phone, + Password: hashedPassword, + } + + user, err = s.store.Create(newUser) + if err != nil { + return nil, ErrCreateUserFailed + } + return user, nil + } + + if !crypto.VerifyPassword(password, user.Password) { + return nil, ErrInvalidPassword + } + + return user, nil +} + +func (s *UserService) LoginByWechat(ctx context.Context, wxCode, phone string) (*common.User, error) { + authResult, err := s.miniProgramApp.Auth.Session(ctx, wxCode) + if err != nil { + return nil, ErrGetWXUserInfo + } + + user, err := s.store.Find(&db.FindUserQuery{WxUnionID: authResult.UnionID}) + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + + if user != nil { + if user.Phone != phone { + return nil, errors.New("手机号与已绑定的不符") + } + user.WxSession = authResult.SessionKey + if err = s.store.Update(user, "wx_session"); err != nil { + return nil, err + } + return user, nil + } + + newUser := &common.User{ + Phone: phone, + WxUnionID: authResult.UnionID, + } + + user, err = s.store.Create(newUser) + if err != nil { + return nil, ErrCreateUserFailed + } + + return user, nil +} + +func (s *UserService) LoginByPhoneAndCode(ctx context.Context, phone, captchaCode string) (*common.User, error) { + if err := s.captchaService.VerifyCode(ctx, phone, captchaCode); err != nil { + return nil, err + } + + user, err := s.store.Find(&db.FindUserQuery{Phone: phone}) + if err != nil && !errors.Is(err, gorm.ErrRecordNotFound) { + return nil, err + } + + if user == nil { + newUser := &common.User{ + Phone: phone, + } + user, err = s.store.Create(newUser) + if err != nil { + return nil, ErrCreateUserFailed + } + } + return user, nil +} + +func (s *UserService) Login(ctx context.Context, phone, password, wxCode, captcha string) (*common.User, error) { + if phone == "" { + return nil, errors.New("手机号不能为空") + } + + if wxCode != "" { + return s.LoginByWechat(ctx, wxCode, phone) + } + + if captcha != "" { + return s.LoginByPhoneAndCode(ctx, phone, captcha) + } + + if password == "" { + return nil, errors.New("密码不能为空") + } + return s.LoginByPhone(phone, password) +} + +func (s *UserService) GetUserByPhone(phone string) (*common.User, error) { + user, err := s.store.Find(&db.FindUserQuery{Phone: phone}, "created_at", "nick_name", "phone", "avatar", "gender") + if err != nil { + return nil, err + } + if user == nil { + return nil, ErrUserNotFound + } + return user, nil +} diff --git a/cmd/user.go b/cmd/user.go deleted file mode 100644 index caba66a..0000000 --- a/cmd/user.go +++ /dev/null @@ -1,53 +0,0 @@ -package main - -import ( - "context" - "flag" - "os" - "os/signal" - - "gitea.timerzz.com/kedaya_haitao/common/pkg/database" - "gitea.timerzz.com/kedaya_haitao/common/pkg/web" - "gitea.timerzz.com/kedaya_haitao/common/structs/storage" - "github.com/gofiber/fiber/v3" - "github.com/gofiber/fiber/v3/middleware/cors" - "github.com/gofiber/fiber/v3/middleware/recover" - - "github.com/golang/glog" -) - -func main() { - flag.Parse() - glog.Info(">>> BEGIN INIT<<<") - ctx, cancel := signal.NotifyContext(context.Background(), os.Kill, os.Interrupt) - defer cancel() - // 初始化数据库 - db, err := database.InitDefaultDatabase() - if err != nil { - glog.Fatalf("init database failed: %v", err) - } - // 初始化服务 - r := fiber.New(fiber.Config{ErrorHandler: web.ErrHandle}) - r.Use(cors.New(), recover.New()) - - stg := storage.NewStorage(db) - - svc := []web.Register{ - web.NewProbe(), - } - - for _, s := range svc { - s.Registry(r) - } - - port := os.Getenv("PORT") - if port == "" { - port = "80" - } - if err = r.Listen(":"+port, fiber.ListenConfig{ - EnablePrintRoutes: true, - GracefulContext: ctx, - }); err != nil { - glog.Warningf("service over: %v", err) - } -} diff --git a/docs/docs.go b/docs/docs.go new file mode 100644 index 0000000..6268ae1 --- /dev/null +++ b/docs/docs.go @@ -0,0 +1,228 @@ +// Package docs Code generated by swaggo/swag. DO NOT EDIT +package docs + +import "github.com/swaggo/swag" + +const docTemplate = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": { + "name": "oneCat-user" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/api/v1/login": { + "post": { + "description": "如果是微信登录,需要传递 WXcode和手机号,然后调用微信接口获取 openid 和 unionid。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户模块" + ], + "summary": "登录接口", + "parameters": [ + { + "description": "请求参数", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/user.LoginRequest" + } + } + ], + "responses": { + "200": { + "description": "成功响应", + "schema": { + "$ref": "#/definitions/user.LoginResponse" + } + } + } + } + }, + "/api/v1/user": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "通过手机号获取用户信息,只返回用户的昵称、电话、性别、ID、创建时间和头像", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户模块" + ], + "summary": "获取用户信息接口", + "parameters": [ + { + "type": "string", + "description": "用户手机号", + "name": "phone", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "成功响应", + "schema": { + "$ref": "#/definitions/user.Response-common_User" + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "$ref": "#/definitions/user.Response-string" + } + }, + "404": { + "description": "用户不存在", + "schema": { + "$ref": "#/definitions/user.Response-string" + } + }, + "500": { + "description": "服务器内部错误", + "schema": { + "$ref": "#/definitions/user.Response-string" + } + } + } + } + } + }, + "definitions": { + "common.User": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "gender": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "nick_name": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "wx_session": { + "type": "string" + }, + "wx_union_id": { + "type": "string" + } + } + }, + "user.LoginRequest": { + "type": "object", + "properties": { + "captcha": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "wx_code": { + "type": "string" + } + } + }, + "user.LoginResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "expire": { + "type": "string" + }, + "message": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "user.Response-common_User": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": { + "$ref": "#/definitions/common.User" + }, + "message": { + "type": "string" + } + } + }, + "user.Response-string": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + } +}` + +// SwaggerInfo holds exported Swagger Info so clients can modify it +var SwaggerInfo = &swag.Spec{ + Version: "1.0", + Host: "", + BasePath: "/", + Schemes: []string{"http"}, + Title: "用户和登录相关接口", + Description: "这是一个用户和登录相关的接口文档", + InfoInstanceName: "swagger", + SwaggerTemplate: docTemplate, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfo.InstanceName(), SwaggerInfo) +} diff --git a/docs/swagger.json b/docs/swagger.json new file mode 100644 index 0000000..d12ab61 --- /dev/null +++ b/docs/swagger.json @@ -0,0 +1,206 @@ +{ + "schemes": [ + "http" + ], + "swagger": "2.0", + "info": { + "description": "这是一个用户和登录相关的接口文档", + "title": "用户和登录相关接口", + "contact": { + "name": "oneCat-user" + }, + "license": { + "name": "Apache 2.0", + "url": "http://www.apache.org/licenses/LICENSE-2.0.html" + }, + "version": "1.0" + }, + "basePath": "/", + "paths": { + "/api/v1/login": { + "post": { + "description": "如果是微信登录,需要传递 WXcode和手机号,然后调用微信接口获取 openid 和 unionid。", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户模块" + ], + "summary": "登录接口", + "parameters": [ + { + "description": "请求参数", + "name": "request", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/user.LoginRequest" + } + } + ], + "responses": { + "200": { + "description": "成功响应", + "schema": { + "$ref": "#/definitions/user.LoginResponse" + } + } + } + } + }, + "/api/v1/user": { + "get": { + "security": [ + { + "ApiKeyAuth": [] + } + ], + "description": "通过手机号获取用户信息,只返回用户的昵称、电话、性别、ID、创建时间和头像", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "用户模块" + ], + "summary": "获取用户信息接口", + "parameters": [ + { + "type": "string", + "description": "用户手机号", + "name": "phone", + "in": "query", + "required": true + } + ], + "responses": { + "200": { + "description": "成功响应", + "schema": { + "$ref": "#/definitions/user.Response-common_User" + } + }, + "400": { + "description": "请求参数错误", + "schema": { + "$ref": "#/definitions/user.Response-string" + } + }, + "404": { + "description": "用户不存在", + "schema": { + "$ref": "#/definitions/user.Response-string" + } + }, + "500": { + "description": "服务器内部错误", + "schema": { + "$ref": "#/definitions/user.Response-string" + } + } + } + } + } + }, + "definitions": { + "common.User": { + "type": "object", + "properties": { + "avatar": { + "type": "string" + }, + "created_at": { + "type": "string" + }, + "gender": { + "type": "integer" + }, + "id": { + "type": "integer" + }, + "nick_name": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "wx_session": { + "type": "string" + }, + "wx_union_id": { + "type": "string" + } + } + }, + "user.LoginRequest": { + "type": "object", + "properties": { + "captcha": { + "type": "string" + }, + "password": { + "type": "string" + }, + "phone": { + "type": "string" + }, + "wx_code": { + "type": "string" + } + } + }, + "user.LoginResponse": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "expire": { + "type": "string" + }, + "message": { + "type": "string" + }, + "token": { + "type": "string" + } + } + }, + "user.Response-common_User": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": { + "$ref": "#/definitions/common.User" + }, + "message": { + "type": "string" + } + } + }, + "user.Response-string": { + "type": "object", + "properties": { + "code": { + "type": "integer" + }, + "data": { + "type": "string" + }, + "message": { + "type": "string" + } + } + } + } +} \ No newline at end of file diff --git a/docs/swagger.yaml b/docs/swagger.yaml new file mode 100644 index 0000000..5f3d27f --- /dev/null +++ b/docs/swagger.yaml @@ -0,0 +1,133 @@ +basePath: / +definitions: + common.User: + properties: + avatar: + type: string + created_at: + type: string + gender: + type: integer + id: + type: integer + nick_name: + type: string + password: + type: string + phone: + type: string + wx_session: + type: string + wx_union_id: + type: string + type: object + user.LoginRequest: + properties: + captcha: + type: string + password: + type: string + phone: + type: string + wx_code: + type: string + type: object + user.LoginResponse: + properties: + code: + type: integer + expire: + type: string + message: + type: string + token: + type: string + type: object + user.Response-common_User: + properties: + code: + type: integer + data: + $ref: '#/definitions/common.User' + message: + type: string + type: object + user.Response-string: + properties: + code: + type: integer + data: + type: string + message: + type: string + type: object +info: + contact: + name: oneCat-user + description: 这是一个用户和登录相关的接口文档 + license: + name: Apache 2.0 + url: http://www.apache.org/licenses/LICENSE-2.0.html + title: 用户和登录相关接口 + version: "1.0" +paths: + /api/v1/login: + post: + consumes: + - application/json + description: 如果是微信登录,需要传递 WXcode和手机号,然后调用微信接口获取 openid 和 unionid。 + parameters: + - description: 请求参数 + in: body + name: request + required: true + schema: + $ref: '#/definitions/user.LoginRequest' + produces: + - application/json + responses: + "200": + description: 成功响应 + schema: + $ref: '#/definitions/user.LoginResponse' + summary: 登录接口 + tags: + - 用户模块 + /api/v1/user: + get: + consumes: + - application/json + description: 通过手机号获取用户信息,只返回用户的昵称、电话、性别、ID、创建时间和头像 + parameters: + - description: 用户手机号 + in: query + name: phone + required: true + type: string + produces: + - application/json + responses: + "200": + description: 成功响应 + schema: + $ref: '#/definitions/user.Response-common_User' + "400": + description: 请求参数错误 + schema: + $ref: '#/definitions/user.Response-string' + "404": + description: 用户不存在 + schema: + $ref: '#/definitions/user.Response-string' + "500": + description: 服务器内部错误 + schema: + $ref: '#/definitions/user.Response-string' + security: + - ApiKeyAuth: [] + summary: 获取用户信息接口 + tags: + - 用户模块 +schemes: +- http +swagger: "2.0" diff --git a/go.mod b/go.mod index e785dec..98e797a 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,65 @@ -module gitea.timerzz.com/kedaya_haitao/user +module gitlab.com/kedaya_mp/user -go 1.22.5 +go 1.24.1 +require ( + github.com/ArtisanCloud/PowerWeChat/v3 v3.4.7 + github.com/caarlos0/env/v11 v11.3.1 + github.com/cloudwego/hertz v0.9.7 + github.com/hertz-contrib/cors v0.1.0 + github.com/hertz-contrib/jwt v1.0.4 + github.com/redis/go-redis/v9 v9.7.3 + github.com/swaggo/swag v1.16.4 + gorm.io/driver/mysql v1.5.7 + gorm.io/gorm v1.25.12 +) + +require ( + github.com/ArtisanCloud/PowerLibs/v3 v3.3.2 // indirect + github.com/KyleBanks/depth v1.2.1 // indirect + github.com/PuerkitoBio/purell v1.1.1 // indirect + github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 // indirect + github.com/bytedance/gopkg v0.1.0 // indirect + github.com/bytedance/sonic v1.13.2 // indirect + github.com/bytedance/sonic/loader v0.2.4 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/clbanning/mxj/v2 v2.7.0 // indirect + github.com/cloudwego/base64x v0.1.5 // indirect + github.com/cloudwego/netpoll v0.6.4 // indirect + github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect + github.com/elastic/pkcs8 v1.0.0 // indirect + github.com/fsnotify/fsnotify v1.7.0 // indirect + github.com/go-openapi/jsonpointer v0.19.5 // indirect + github.com/go-openapi/jsonreference v0.19.6 // indirect + github.com/go-openapi/spec v0.20.4 // indirect + github.com/go-openapi/swag v0.19.15 // indirect + github.com/go-sql-driver/mysql v1.7.0 // indirect + github.com/golang-jwt/jwt/v4 v4.5.2 // indirect + github.com/golang/protobuf v1.5.4 // indirect + github.com/jinzhu/inflection v1.0.0 // indirect + github.com/jinzhu/now v1.1.5 // indirect + github.com/josharian/intern v1.0.0 // indirect + github.com/klauspost/cpuid/v2 v2.2.9 // indirect + github.com/kr/pretty v0.3.0 // indirect + github.com/mailru/easyjson v0.7.6 // indirect + github.com/nyaruka/phonenumbers v1.0.55 // indirect + github.com/patrickmn/go-cache v2.1.0+incompatible // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/tidwall/gjson v1.18.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + github.com/twitchyliquid64/golang-asm v0.15.1 // indirect + go.opentelemetry.io/otel v1.35.0 // indirect + go.opentelemetry.io/otel/trace v1.35.0 // indirect + go.uber.org/multierr v1.11.0 // indirect + go.uber.org/zap v1.27.0 // indirect + golang.org/x/arch v0.8.0 // indirect + golang.org/x/crypto v0.37.0 // indirect + golang.org/x/net v0.25.0 // indirect + golang.org/x/sys v0.32.0 // indirect + golang.org/x/text v0.24.0 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect + google.golang.org/protobuf v1.35.2 // indirect + gopkg.in/yaml.v2 v2.4.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index e40ba23..4d230f6 100644 --- a/go.sum +++ b/go.sum @@ -1,83 +1,269 @@ -gitea.timerzz.com/kedaya_haitao/common v0.0.0-20240904073547-7677cd470a33 h1:faziFCeUdnDOsAse6iI6ZKXY68BBTgeQfoRS5ef8ag0= -gitea.timerzz.com/kedaya_haitao/common v0.0.0-20240904073547-7677cd470a33/go.mod h1:BIz+IMGznPiyLnV1+Ntw1zf8rEIcbymmGq+EfvDsSgE= -github.com/andybalholm/brotli v1.1.0 h1:eLKJA0d02Lf0mVpIDgYnqXcUn0GqVmEFny3VuID1U3M= -github.com/andybalholm/brotli v1.1.0/go.mod h1:sms7XGricyQI9K10gOSf56VKKWS4oLer58Q+mhRPtnY= +github.com/ArtisanCloud/PowerLibs/v3 v3.3.2 h1:IInr1YWwkhwOykxDqux1Goym0uFhrYwBjmgLnEwCLqs= +github.com/ArtisanCloud/PowerLibs/v3 v3.3.2/go.mod h1:xFGsskCnzAu+6rFEJbGVAlwhrwZPXAny6m7j71S/B5k= +github.com/ArtisanCloud/PowerWeChat/v3 v3.4.7 h1:oDQcU8+/CYvvuJ1nIrCc1j4sg1IfzjkscMoeo4nLIDA= +github.com/ArtisanCloud/PowerWeChat/v3 v3.4.7/go.mod h1:ybM3u4Lhso0X+ZsgoRCF4e5W1KT2fBc6plpjPZ2fop4= +github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= +github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs= +github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c= +github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA= +github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0= +github.com/bytedance/go-tagexpr/v2 v2.9.2/go.mod h1:5qsx05dYOiUXOUgnQ7w3Oz8BYs2qtM/bJokdLb79wRM= +github.com/bytedance/gopkg v0.0.0-20220413063733-65bf48ffb3a7/go.mod h1:2ZlV9BaUH4+NXIBF0aMdKKAnHTzqH+iMU4KUjAbL23Q= +github.com/bytedance/gopkg v0.1.0 h1:aAxB7mm1qms4Wz4sp8e1AtKDOeFLtdqvGiUe7aonRJs= +github.com/bytedance/gopkg v0.1.0/go.mod h1:FtQG3YbQG9L/91pbKSw787yBQPutC+457AvDW77fgUQ= +github.com/bytedance/mockey v1.2.1/go.mod h1:+Jm/fzWZAuhEDrPXVjDf/jLM2BlLXJkwk94zf2JZ3X4= +github.com/bytedance/mockey v1.2.12 h1:aeszOmGw8CPX8CRx1DZ/Glzb1yXvhjDh6jdFBNZjsU4= +github.com/bytedance/mockey v1.2.12/go.mod h1:3ZA4MQasmqC87Tw0w7Ygdy7eHIc2xgpZ8Pona5rsYIk= +github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= +github.com/bytedance/sonic v1.8.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= +github.com/bytedance/sonic v1.13.2 h1:8/H1FempDZqC4VqjptGo14QQlJx8VdZJegxs6wwfqpQ= +github.com/bytedance/sonic v1.13.2/go.mod h1:o68xyaF9u2gvVBuGHPlUVCy+ZfmNNO5ETf1+KgkJhz4= +github.com/bytedance/sonic/loader v0.1.1/go.mod h1:ncP89zfokxS5LZrJxl5z0UJcsk4M4yY2JpfqGeCtNLU= +github.com/bytedance/sonic/loader v0.2.4 h1:ZWCw4stuXUsn1/+zQDqeE7JKP+QO47tz7QCNan80NzY= +github.com/bytedance/sonic/loader v0.2.4/go.mod h1:N8A3vUdtUebEY2/VQC0MyhYeKUFosQU6FxH2JmUe6VI= +github.com/caarlos0/env/v11 v11.3.1 h1:cArPWC15hWmEt+gWk7YBi7lEXTXCvpaSdCiZE2X5mCA= +github.com/caarlos0/env/v11 v11.3.1/go.mod h1:qupehSf/Y0TUTsxKywqRt/vJjN5nz6vauiYEUUr8P4U= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/chenzhuoyu/base64x v0.0.0-20211019084208-fb5309c8db06/go.mod h1:DH46F32mSOjUmXrMHnKwZdA8wcEefY7UVqBKYGjpdQY= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/clbanning/mxj/v2 v2.7.0 h1:WA/La7UGCanFe5NpHF0Q3DNtnCsVoxbPKuyBNHWRyME= +github.com/clbanning/mxj/v2 v2.7.0/go.mod h1:hNiWqW14h+kc+MdF9C6/YoRfjEJoR3ou6tn/Qo+ve2s= +github.com/cloudwego/base64x v0.1.5 h1:XPciSp1xaq2VCSt6lF0phncD4koWyULpl5bUxbfCyP4= +github.com/cloudwego/base64x v0.1.5/go.mod h1:0zlkT4Wn5C6NdauXdJRhSKRlJvmclQ1hhJgA0rcu/8w= +github.com/cloudwego/hertz v0.6.2/go.mod h1:2em2hGREvCBawsTQcQxyWBGVlCeo+N1pp2q0HkkbwR0= +github.com/cloudwego/hertz v0.9.7 h1:tAVaiO+vTf+ZkQhvNhKbDJ0hmC4oJ7bzwDi1KhvhHy4= +github.com/cloudwego/hertz v0.9.7/go.mod h1:t6d7NcoQxPmETvzPMMIVPHMn5C5QzpqIiFsaavoLJYQ= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cloudwego/netpoll v0.3.1/go.mod h1:1T2WVuQ+MQw6h6DpE45MohSvDTKdy2DlzCx2KsnPI4E= +github.com/cloudwego/netpoll v0.6.4 h1:z/dA4sOTUQof6zZIO4QNnLBXsDFFFEos9OOGloR6kno= +github.com/cloudwego/netpoll v0.6.4/go.mod h1:BtM+GjKTdwKoC8IOzD08/+8eEn2gYoiNLipFca6BVXQ= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= 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/expr-lang/expr v1.16.9 h1:WUAzmR0JNI9JCiF0/ewwHB1gmcGw5wW7nWt8gc6PpCI= -github.com/expr-lang/expr v1.16.9/go.mod h1:8/vRC7+7HBzESEqt5kKpYXxrxkr31SaO8r40VO/1IT4= -github.com/gofiber/fiber/v3 v3.0.0-beta.3 h1:7Q2I+HsIqnIEEDB+9oe7Gadpakh6ZLhXpTYz/L20vrg= -github.com/gofiber/fiber/v3 v3.0.0-beta.3/go.mod h1:kcMur0Dxqk91R7p4vxEpJfDWZ9u5IfvrtQc8Bvv/JmY= -github.com/gofiber/utils/v2 v2.0.0-beta.4 h1:1gjbVFFwVwUb9arPcqiB6iEjHBwo7cHsyS41NeIW3co= -github.com/gofiber/utils/v2 v2.0.0-beta.4/go.mod h1:sdRsPU1FXX6YiDGGxd+q2aPJRMzpsxdzCXo9dz+xtOY= -github.com/golang/glog v1.2.1 h1:OptwRhECazUx5ix5TTWC3EZhsZEHWcYWY4FQHTIubm4= -github.com/golang/glog v1.2.1/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -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/henrylee2cn/ameda v1.4.10 h1:JdvI2Ekq7tapdPsuhrc4CaFiqw6QXFvZIULWJgQyCAk= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78= +github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc= +github.com/elastic/pkcs8 v1.0.0 h1:HhitlUKxhN288kcNcYkjW6/ouvuwJWd9ioxpjnD9jVA= +github.com/elastic/pkcs8 v1.0.0/go.mod h1:ipsZToJfq1MxclVTwpG7U/bgeDtf+0HkUiOxebk95+0= +github.com/fsnotify/fsnotify v1.5.4/go.mod h1:OVB6XrOHzAwXMpEM7uPOzcehqUV2UqJxmVXmkdnm1bU= +github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= +github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= +github.com/go-openapi/jsonreference v0.19.6 h1:UBIxjkht+AWIgYzCDSv2GN+E/togfwXUJFRTWhl2Jjs= +github.com/go-openapi/jsonreference v0.19.6/go.mod h1:diGHMEHg2IqXZGKxqyvWdfWU/aim5Dprw5bqpKkTvns= +github.com/go-openapi/spec v0.20.4 h1:O8hJrt0UMnhHcluhIdUgCLRWyM2x7QkBXRvOs7m+O1M= +github.com/go-openapi/spec v0.20.4/go.mod h1:faYFR1CvsJZ0mNsmsphTMSoRrNV3TEDoAM7FOEWeq8I= +github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= +github.com/go-openapi/swag v0.19.15 h1:D2NRCBzS9/pEY3gP9Nl8aDqGUcPFrwG2p+CNFrLyrCM= +github.com/go-openapi/swag v0.19.15/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= +github.com/go-playground/assert/v2 v2.0.1 h1:MsBgLAaY856+nPRTKrp3/OZK38U/wa0CcBYNjji3q3A= +github.com/go-playground/assert/v2 v2.0.1/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4= +github.com/go-sql-driver/mysql v1.7.0 h1:ueSltNNllEqE3qcWBTD0iQd3IpL/6U+mJxLkazJ7YPc= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= +github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +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/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY= +github.com/henrylee2cn/ameda v1.4.8/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= github.com/henrylee2cn/ameda v1.4.10/go.mod h1:liZulR8DgHxdK+MEwvZIylGnmcjzQ6N6f2PlWe7nEO4= -github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM= -github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk= -github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= -github.com/jackc/pgx/v5 v5.4.3 h1:cxFyXhxlvAifxnkKKdlxv8XqUf59tDlYjnV5YYfsJJY= -github.com/jackc/pgx/v5 v5.4.3/go.mod h1:Ig06C2Vu0t5qXC60W8sqIthScaEnFvojjj9dSljmHRA= +github.com/henrylee2cn/goutil v0.0.0-20210127050712-89660552f6f8/go.mod h1:Nhe/DM3671a5udlv2AdV2ni/MZzgfv2qrPL5nIi3EGQ= +github.com/hertz-contrib/cors v0.1.0 h1:PQ5mATygSMzTlYtfyMyHjobYoJeHKe2Qt3tcAOgbI6E= +github.com/hertz-contrib/cors v0.1.0/go.mod h1:VPReoq+Rvu/lZOfpp5CcX3x4mpZUc3EpSXBcVDcbvOc= +github.com/hertz-contrib/jwt v1.0.4 h1:PHddo1FDBpGHXx9nkhSwXamEyPNCkZCtszYXcRCD3q8= +github.com/hertz-contrib/jwt v1.0.4/go.mod h1:YntlFg4tdWw1CM5mELU00HbO8Gsa92xPd7EyrSYxAcg= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= 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/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= -github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU= +github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg= +github.com/klauspost/cpuid/v2 v2.2.9 h1:66ze0taIn2H33fBvCkXuv9BmCwDfafmiIVpKV9kKGuY= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/knz/go-libedit v1.10.1/go.mod h1:MZTVkCWyz0oBc7JOWP3wNAzd002ZbM/5hgShxwh4x8M= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= github.com/kr/pretty v0.3.0 h1:WgNl7dwNpEZ6jJ9k1snq4pZsg7DOEN8hP9Xw0Tsjwk0= github.com/kr/pretty v0.3.0/go.mod h1:640gp4NfQd8pI5XOwp5fnNeVWj67G7CFk/SaSQn7NBk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= -github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= -github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= -github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= -github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= +github.com/nyaruka/phonenumbers v1.0.55 h1:bj0nTO88Y68KeUQ/n3Lo2KgK7lM1hF7L9NFuwcCl3yg= +github.com/nyaruka/phonenumbers v1.0.55/go.mod h1:sDaTZ/KPX5f8qyV9qN+hIm+4ZBARJrupC6LuhshJq1U= +github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= +github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= -github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= -github.com/samber/lo v1.39.0 h1:4gTz1wUhNYLhFSKl6O+8peW0v2F4BCY034GRpU9WnuA= -github.com/samber/lo v1.39.0/go.mod h1:+m/ZKRl6ClXCE2Lgf3MsQlWfh4bn1bz6CXEOxnEXnEA= +github.com/redis/go-redis/v9 v9.7.3 h1:YpPyAayJV+XErNsatSElgRZZVCwXX9QzkKYNvO7x0wM= +github.com/redis/go-redis/v9 v9.7.3/go.mod h1:bGUrSggJ9X9GUmZpZNEOQKaANxSGgOEBRltRTZHSvrA= +github.com/rogpeppe/go-internal v1.6.1 h1:/FiVV8dS/e+YqF2JvO3yXRFbBLTIuSDkuC7aBOAvL+k= +github.com/rogpeppe/go-internal v1.6.1/go.mod h1:xXDCJY+GAPziupqXw64V24skbSoqbTEfhy4qGm1nDQc= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/smartystreets/goconvey v1.6.4/go.mod h1:syvi0/a8iFYH4r/RixwvyeAJjdLS9QV7WQ/tjFTllLA= 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 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +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.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg= -github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= -github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= -github.com/valyala/fasthttp v1.55.0 h1:Zkefzgt6a7+bVKHnu/YaYSOPfNYNisSVBo/unVCf8k8= -github.com/valyala/fasthttp v1.55.0/go.mod h1:NkY9JtkrpPKmgwV3HTaS2HWaJss9RSIsRVfcxxoHiOM= -github.com/valyala/tcplisten v1.0.0 h1:rBHj/Xf+E1tRGZyWIWwJDiRY0zc1Js+CV5DqwacVSA8= -github.com/valyala/tcplisten v1.0.0/go.mod h1:T0xQ8SeCZGxckz9qRXTfG43PvQ/mcWh7FwZEA7Ioqkc= -golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= -golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f h1:99ci1mjWVBWwJiEKYY6jWa4d2nTQVIEhZIptnrVb1XY= -golang.org/x/exp v0.0.0-20240416160154-fe59bbe5cc7f/go.mod h1:/lliqkxwWAhPjf5oSOIJup2XcqJaw8RGS6k3TGEc7GI= -golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= -golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= +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.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= +github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/swaggo/swag v1.16.4 h1:clWJtd9LStiG3VeijiCfOVODP6VpHtKdQy9ELFG3s1A= +github.com/swaggo/swag v1.16.4/go.mod h1:VBsHJRsDvfYvqoiMKnsdwhNV9LEMHgEDZcyVYX0sxPg= +github.com/tidwall/gjson v1.9.3/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.13.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= +github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI= +github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +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.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ= +go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y= +go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M= +go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE= +go.opentelemetry.io/otel/sdk v1.4.0 h1:LJE4SW3jd4lQTESnlpQZcBhQ3oci0U2MLR5uhicfTHQ= +go.opentelemetry.io/otel/sdk v1.4.0/go.mod h1:71GJPNJh4Qju6zJuYl1CrYtXbrgfau/M9UAggqiy1UE= +go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs= +go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/arch v0.0.0-20201008161808-52c3e6f60cff/go.mod h1:flIaEI6LNU6xOCD5PaJvn9wGP0agmIOqjrtsKGRguv4= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/arch v0.8.0 h1:3wRIsP3pM4yUptoR96otTUOXI367OS0+c9eeRi9doIc= +golang.org/x/arch v0.8.0/go.mod h1:FEVrYAQjsQXMVJ1nsMoVVXPZg6p2JE2mx8psSWTDQys= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= +golang.org/x/crypto v0.22.0/go.mod h1:vr6Su+7cTlO45qkww3VDJlzDn0ctJvRgYbC2NvXHt+M= +golang.org/x/crypto v0.37.0 h1:kJNSjF/Xp7kU0iB2Z+9viTPMW4EqqsrywMXLJOOsXSE= +golang.org/x/crypto v0.37.0/go.mod h1:vg+k43peMZ0pUMhYmVAWysMK35e6ioLh3wB8ZCAfbVc= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= +golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20210421230115-4e50805a0758/go.mod h1:72T/g9IO56b78aLF+1Kcs5dz7/ng1VjMUvfKvpfy+jM= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.0.0-20221014081412-f15817d10f9b/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= +golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= +golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44= +golang.org/x/net v0.25.0 h1:d/OCCoBEUq33pjydKrGQhw7IlUPI2Oylr+8qLx49kac= +golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.13.0 h1:AauUjRAJ9OSnvULf/ARrrVywoJDy0YS2AwQ98I37610= +golang.org/x/sync v0.13.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210420072515-93ed5bcd2bfe/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220110181412-a018aaa089fe/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220728004956-3c1f35247d10/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20= +golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= +golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk= +golang.org/x/term v0.19.0/go.mod h1:2CuTdWZ7KHSQwUzKva0cbMg6q2DMI3Mmxp+gKJbskEk= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= +golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0= +golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190328211700-ab21143f2384/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.27.1/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.35.2 h1:8Ar7bF+apOIoThw1EdZl0p1oWvMqTHmpA2fRTyZO8io= +google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/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/postgres v1.5.7 h1:8ptbNJTDbEmhdr62uReG5BGkdQyeasu/FZHxI0IMGnM= -gorm.io/driver/postgres v1.5.7/go.mod h1:3e019WlBaYI5o5LIdNV+LyxCMNtLOQETBXL2h4chKpA= -gorm.io/gorm v1.25.10 h1:dQpO+33KalOA+aFYGlK+EfxcI5MbO7EP2yYygwh9h+s= -gorm.io/gorm v1.25.10/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/driver/mysql v1.5.7 h1:MndhOPYOfEp2rHKgkZIhJ16eVUIRf2HmzgoPmh7FCWo= +gorm.io/driver/mysql v1.5.7/go.mod h1:sEtPWMiqiN1N1cMXoXmBbd8C6/l+TESwriotuRRpkDM= +gorm.io/gorm v1.25.7/go.mod h1:hbnx/Oo0ChWMn1BIhpy1oYozzpM15i4YPuHDmfYtwg8= +gorm.io/gorm v1.25.12 h1:I0u8i2hWQItBq1WfE0o2+WuL9+8L21K9e2HHSTE/0f8= +gorm.io/gorm v1.25.12/go.mod h1:xh7N7RHfYlNc5EmcI/El95gXusucDrQnHXe0+CgWcLQ= +nullprogram.com/x/optparse v1.0.0/go.mod h1:KdyPE+Igbe0jQUrVfMqDMeJQIJZEuyV7pjYmp6pbG50= +rsc.io/pdf v0.1.1/go.mod h1:n8OzWcQ6Sp37PL01nO98y4iUCRdTGarVfzxY20ICaU4= diff --git a/main.go b/main.go new file mode 100644 index 0000000..8424648 --- /dev/null +++ b/main.go @@ -0,0 +1,56 @@ +// Code generated by hertz generator. + +package main + +import ( + "os" + + "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram" + "github.com/cloudwego/hertz/pkg/app/server" + "github.com/hertz-contrib/cors" + "gitlab.com/kedaya_mp/user/biz/router/healthz" + "gitlab.com/kedaya_mp/user/biz/router/user" + "gitlab.com/kedaya_mp/user/pkg/captcha" + "gitlab.com/kedaya_mp/user/pkg/mysql" + "gitlab.com/kedaya_mp/user/pkg/wx" + "gorm.io/gorm" +) + +func register(h *server.Hertz, db *gorm.DB, miniProgramApp *miniProgram.MiniProgram, captchaService captcha.Service) { + user.Register(h, db, miniProgramApp, captchaService) + healthz.NewHealthz().Registry(h) +} + +// @title 用户和登录相关接口 +// @version 1.0 +// @description 这是一个用户和登录相关的接口文档 +// @contact.name oneCat-user +// @license.name Apache 2.0 +// @license.url http://www.apache.org/licenses/LICENSE-2.0.html + +// @BasePath / +// @schemes http +func main() { + port := ":80" + if os.Getenv("PORT") != "" { + port = os.Getenv("PORT") + } + + h := server.Default(server.WithHostPorts(port)) + h.Use(cors.New(cors.Config{ + AllowAllOrigins: true, + AllowMethods: []string{"GET", "POST"}, + ExposeHeaders: []string{"Content-Length"}, + AllowHeaders: []string{"Origin", "Authorization", "Content-Type"}, + AllowCredentials: true, + })) + + db := mysql.InitDB() + miniProgramApp := wx.NewWX() + captchaService := captcha.InitDefaultService() + defer captchaService.Close() + + register(h, db, miniProgramApp, captchaService) + + h.Spin() +} diff --git a/pkg/captcha/captcha.go b/pkg/captcha/captcha.go new file mode 100644 index 0000000..51a42a2 --- /dev/null +++ b/pkg/captcha/captcha.go @@ -0,0 +1,28 @@ +package captcha + +import ( + "fmt" + "math" + "math/rand/v2" + "time" +) + +type Generator interface { + Generate() string +} + +type stringCaptchaGenerator struct { + length int +} + +func NewStringCaptchaGenerator(length int) Generator { + return &stringCaptchaGenerator{ + length: length, + } +} + +func (s *stringCaptchaGenerator) Generate() string { + n := math.Pow(10, float64(s.length)) + r := rand.New(rand.NewPCG(uint64(time.Now().UnixNano()), 2)).IntN(int(n)) + return fmt.Sprintf("%0*d", s.length, r) +} diff --git a/pkg/captcha/init.go b/pkg/captcha/init.go new file mode 100644 index 0000000..b09e3c3 --- /dev/null +++ b/pkg/captcha/init.go @@ -0,0 +1,30 @@ +package captcha + +import ( + "github.com/caarlos0/env/v11" + "github.com/cloudwego/hertz/pkg/common/hlog" + "github.com/redis/go-redis/v9" +) + +type Config struct { + Address string `env:"CAPTCHA_REDIS_ADDRESS"` + Password string `env:"CAPTCHA_REDIS_PASSWORD"` + DB int `env:"CAPTCHA_REDIS_DB"` +} + +func InitDefaultService() Service { + var opt Config + if err := env.Parse(&opt); err != nil { + hlog.Fatalf("parse captcha config failed: %v", err) + } + rdb := redis.NewClient(&redis.Options{ + Addr: opt.Address, + Password: opt.Password, + DB: opt.DB, + }) + return NewSMSCaptchaService( + NewRedisStore(rdb), + // TODO 替换为真实的短信服务 + nil, + ) +} diff --git a/pkg/captcha/interface.go b/pkg/captcha/interface.go new file mode 100644 index 0000000..9234d0a --- /dev/null +++ b/pkg/captcha/interface.go @@ -0,0 +1,26 @@ +package captcha + +import ( + "context" + "errors" + "time" +) + +var ( + ErrCodeSendTooMany = errors.New("发送验证码太频繁") + ErrCodeVerifyFail = errors.New("验证码错误") +) + +// Service 发送和验证验证码 +type Service interface { + SendCode(ctx context.Context, phone string) error + VerifyCode(ctx context.Context, phone, code string) error + Close() error +} + +type Store interface { + Set(ctx context.Context, key string, value string, timeout time.Duration) error + Get(ctx context.Context, key string) (string, error) + Delete(ctx context.Context, key string) error + Close() error +} diff --git a/pkg/captcha/sms.go b/pkg/captcha/sms.go new file mode 100644 index 0000000..6e14ff8 --- /dev/null +++ b/pkg/captcha/sms.go @@ -0,0 +1,51 @@ +package captcha + +import ( + "context" + "time" + + "gitlab.com/kedaya_mp/user/pkg/sms" +) + +// 短信验证码 +type smsService struct { + store Store + sms sms.Service + generator Generator +} + +func NewSMSCaptchaService(store Store, sms sms.Service) Service { + return &smsService{ + store: store, + sms: sms, + generator: NewStringCaptchaGenerator(6), + } +} + +func (s *smsService) SendCode(ctx context.Context, phone string) error { + captcha := s.generator.Generate() + if err := s.sms.Send(ctx, phone, captcha); err != nil { + return err + } + return s.store.Set(ctx, phone, captcha, time.Minute*5) +} +func (s *smsService) VerifyCode(ctx context.Context, phone, code string) error { + // 从store中获取验证码 + captcha, _ := s.store.Get(ctx, phone) + // TODO 写一个测试用的验证码,后面接入真实的短信服务后,就可以删除这个if语句 + if code == "zhhg" { + return nil + } + if captcha == "" { + return ErrCodeVerifyFail + } + + if captcha != code { + return ErrCodeVerifyFail + } + _ = s.store.Delete(ctx, phone) + return nil +} +func (s *smsService) Close() error { + return s.store.Close() +} diff --git a/pkg/captcha/store.go b/pkg/captcha/store.go new file mode 100644 index 0000000..cf37440 --- /dev/null +++ b/pkg/captcha/store.go @@ -0,0 +1,28 @@ +package captcha + +import ( + "context" + "time" + + "github.com/redis/go-redis/v9" +) + +type redisStore struct { + client *redis.Client +} + +func NewRedisStore(client *redis.Client) Store { + return &redisStore{client: client} +} +func (s *redisStore) Set(ctx context.Context, key string, value string, timeout time.Duration) error { + return s.client.Set(ctx, key, value, timeout).Err() +} +func (s *redisStore) Get(ctx context.Context, key string) (string, error) { + return s.client.Get(ctx, key).Result() +} +func (s *redisStore) Delete(ctx context.Context, key string) error { + return s.client.Del(ctx, key).Err() +} +func (s *redisStore) Close() error { + return s.client.Close() +} diff --git a/pkg/crypto/password.go b/pkg/crypto/password.go new file mode 100644 index 0000000..a79c31e --- /dev/null +++ b/pkg/crypto/password.go @@ -0,0 +1,18 @@ +package crypto + +import ( + "crypto/sha256" + "encoding/hex" +) + +// HashPassword 使用 SHA-256 对密码进行加密 +func HashPassword(password string) string { + hash := sha256.New() + hash.Write([]byte(password)) + return hex.EncodeToString(hash.Sum(nil)) +} + +// VerifyPassword 验证密码是否匹配 +func VerifyPassword(password, hashedPassword string) bool { + return HashPassword(password) == hashedPassword +} diff --git a/pkg/mysql/db.go b/pkg/mysql/db.go new file mode 100644 index 0000000..a83c45a --- /dev/null +++ b/pkg/mysql/db.go @@ -0,0 +1,68 @@ +package mysql + +import ( + "fmt" + "log" + "os" + "time" + + "github.com/caarlos0/env/v11" + "github.com/cloudwego/hertz/pkg/common/hlog" + "gorm.io/driver/mysql" + "gorm.io/gorm" + "gorm.io/gorm/logger" + "gorm.io/gorm/schema" +) + +type Config struct { + Address string `env:"MYSQL_ADDRESS"` + User string `env:"MYSQL_USERNAME"` + Password string `env:"MYSQL_PASSWORD"` + DBName string `env:"MYSQL_DBNAME"` +} + +// InitDB 初始化数据库连接 +func InitDB() *gorm.DB { + var cfg Config + if err := env.Parse(&cfg); err != nil { + hlog.Fatalf("解析数据库环境变量失败: %v", err) + } + // 配置日志 + newLogger := logger.New( + log.New(os.Stdout, "\r\n", log.LstdFlags), + logger.Config{ + SlowThreshold: time.Second, // 慢SQL阈值 + LogLevel: logger.Warn, // 日志级别 + IgnoreRecordNotFoundError: true, // 忽略记录未找到错误 + Colorful: true, // 彩色输出 + }, + ) + + // 配置GORM + config := &gorm.Config{ + Logger: newLogger, + NamingStrategy: schema.NamingStrategy{ + SingularTable: true, // 使用单数表名 + }, + } + dsn := fmt.Sprintf("%s:%s@tcp(%s)/%s?charset=utf8mb4&parseTime=True&loc=Local", cfg.User, cfg.Password, cfg.Address, cfg.DBName) + // 连接数据库 + DB, err := gorm.Open(mysql.Open(dsn), config) + if err != nil { + hlog.Fatalf("连接数据库失败: %v", err) + } + + hlog.Info("数据库连接成功") + + // 获取底层SQL连接池 + sqlDB, err := DB.DB() + if err != nil { + hlog.Fatalf("获取数据库连接池失败: %v", err) + } + + // 设置连接池参数 + sqlDB.SetMaxIdleConns(10) // 最大空闲连接数 + sqlDB.SetMaxOpenConns(100) // 最大打开连接数 + sqlDB.SetConnMaxLifetime(time.Hour) // 连接最大生命周期 + return DB +} diff --git a/pkg/sms/sms.go b/pkg/sms/sms.go new file mode 100644 index 0000000..f63bc7b --- /dev/null +++ b/pkg/sms/sms.go @@ -0,0 +1,11 @@ +package sms + +import ( + "context" +) + +type Service interface { + Send(ctx context.Context, phone, msg string) error +} + +// todo实现发短信 diff --git a/pkg/wx/client.go b/pkg/wx/client.go new file mode 100644 index 0000000..3338e99 --- /dev/null +++ b/pkg/wx/client.go @@ -0,0 +1,38 @@ +package wx + +import ( + "github.com/ArtisanCloud/PowerWeChat/v3/src/kernel" + "github.com/ArtisanCloud/PowerWeChat/v3/src/miniProgram" + "github.com/caarlos0/env/v11" + "github.com/cloudwego/hertz/pkg/common/hlog" +) + +type WX struct { + AppID string `env:"WX_APPID"` + AppSecret string `env:"WX_APPSECRET"` + Address string `env:"WX_REDIS_ADDRESS"` + Password string `env:"WX_REDIS_PASSWORD"` + DB int `env:"WX_REDIS_DB"` +} + +// NewWX 初始化默认的微信客户端 +func NewWX() *miniProgram.MiniProgram { + var config WX + err := env.Parse(&config) + if err != nil { + hlog.Fatalf("加载微信客户端配置失败: %v", err) + } + mp, _err := miniProgram.NewMiniProgram(&miniProgram.UserConfig{ + AppID: config.AppID, + Secret: config.AppSecret, + Cache: kernel.NewRedisClient(&kernel.UniversalOptions{ + Addrs: []string{config.Address}, + Password: config.Password, + DB: config.DB, + }), + }) + if _err != nil { + hlog.Fatalf("初始化微信客户端失败: %v", _err) + } + return mp +} diff --git a/service/user_svc.go b/service/user_svc.go deleted file mode 100644 index 8ec81bd..0000000 --- a/service/user_svc.go +++ /dev/null @@ -1,26 +0,0 @@ -package service - -import ( - "gitea.timerzz.com/kedaya_haitao/common/pkg/web" - "gitea.timerzz.com/kedaya_haitao/common/structs/storage" -) - -type user struct { - storage *storage.Storage -} - -func Newuser(storage *storage.Storage) *user { - return &user{ - storage: storage, - } -} - -func (s *user) Registry(r fiber.Router) { - api := r.Group("/api/v2") -} - -func (s *user) List(c fiber.Ctx) error { - - - return c.JSON(web.NewResponse(web.NewListResponse(total, sellers))) -}