优化Docker构建流程

- 添加UPX压缩步骤减小可执行文件体积
- 使用多阶段构建减小最终镜像大小
- 更新基础镜像到最新版本
This commit is contained in:
timerzz 2025-04-22 17:30:36 +08:00
parent ecff68fe8c
commit e7e3309896
29 changed files with 1854 additions and 177 deletions

View File

@ -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

38
.gitignore vendored Normal file
View File

@ -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/

View File

@ -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"

31
Dockerfile Normal file
View File

@ -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"]

94
biz/dal/db/user.go Normal file
View File

@ -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
})
}

45
biz/handle/auth.go Normal file
View File

@ -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)
}

72
biz/handle/get.go Normal file
View File

@ -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,
})
}

22
biz/model/common/user.go Normal file
View File

@ -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"
}

54
biz/mw/jwt.go Normal file
View File

@ -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
}

View File

@ -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
}
}

31
biz/router/user/user.go Normal file
View File

@ -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)
}
}

155
biz/service/user_service.go Normal file
View File

@ -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
}

View File

@ -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)
}
}

228
docs/docs.go Normal file
View File

@ -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)
}

206
docs/swagger.json Normal file
View File

@ -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"
}
}
}
}
}

133
docs/swagger.yaml Normal file
View File

@ -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"

65
go.mod
View File

@ -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
)

298
go.sum
View File

@ -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=

56
main.go Normal file
View File

@ -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()
}

28
pkg/captcha/captcha.go Normal file
View File

@ -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)
}

30
pkg/captcha/init.go Normal file
View File

@ -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,
)
}

26
pkg/captcha/interface.go Normal file
View File

@ -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
}

51
pkg/captcha/sms.go Normal file
View File

@ -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()
}

28
pkg/captcha/store.go Normal file
View File

@ -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()
}

18
pkg/crypto/password.go Normal file
View File

@ -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
}

68
pkg/mysql/db.go Normal file
View File

@ -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
}

11
pkg/sms/sms.go Normal file
View File

@ -0,0 +1,11 @@
package sms
import (
"context"
)
type Service interface {
Send(ctx context.Context, phone, msg string) error
}
// todo实现发短信

38
pkg/wx/client.go Normal file
View File

@ -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
}

View File

@ -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)))
}