5 Commits 47cba0c3c2 ... 10aea8caf1

Autor SHA1 Mensagem Data
  runningwater 10aea8caf1 feat: 分页请求参数校验 4 meses atrás
  runningwater 25f91e93a0 feat: 用户列表分页 4 meses atrás
  runningwater 5d6a0da021 feat: paginator 分页功能 4 meses atrás
  runningwater a789959009 fix: golangci-lint run fix 4 meses atrás
  runningwater 4376241d67 feat: 用户列表 4 meses atrás

+ 5 - 0
README.md

@@ -52,6 +52,7 @@ UNIQUE KEY `migration` (`migration`)
 
 #### 🐛 Bug 修复
 
+- Golangci-lint run fix
 - Readme.md
 - 删除测试的 testCommand
 - Panic Recovery
@@ -73,6 +74,10 @@ UNIQUE KEY `migration` (`migration`)
 
 #### 🚀 新功能
 
+- 分页请求参数校验
+- 用户列表分页
+- Paginator 分页功能
+- 用户列表
 - *(command)* Make seed 命令
 - *(command)* Make factory 命令
 - Facotry 和 Faker

+ 1 - 1
app/cmd/make/make_model.go

@@ -20,7 +20,7 @@ func runMakeModel(cmd *cobra.Command, args []string) {
 
 	// 目录, 例如: `app/models/user/`
 	dir := fmt.Sprintf("app/models/%s/", model.PackageName)
-	os.MkdirAll(dir, os.ModePerm)
+	_ = os.MkdirAll(dir, os.ModePerm)
 
 	// 替换变量
 	createFileFromTpl(dir+model.PackageName+"_model.go", "model/model", model)

+ 23 - 6
app/http/controllers/api/v1/users_controller.go

@@ -1,18 +1,35 @@
 package v1
 
 import (
-	"github.com/runningwater/gohub/pkg/auth"
-	"github.com/runningwater/gohub/pkg/response"
+    "github.com/runningwater/gohub/app/models/user"
+    "github.com/runningwater/gohub/app/requests"
+    "github.com/runningwater/gohub/pkg/auth"
+    "github.com/runningwater/gohub/pkg/response"
 
-	"github.com/gin-gonic/gin"
+    "github.com/gin-gonic/gin"
 )
 
 type UsersController struct {
-	BaseApiController
+    BaseApiController
 }
 
 // CurrentUser 当前登录用户信息
 func (ctrl *UsersController) CurrentUser(c *gin.Context) {
-	users := auth.CurrentUser(c)
-	response.Data(c, users)
+    users := auth.CurrentUser(c)
+    response.Data(c, users)
+}
+
+// Index 所有用户
+func (ctrl *UsersController) Index(c *gin.Context) {
+    // data := user.All()
+    // 输入参数校验
+    request := requests.PaginationRequest{}
+    if ok := requests.Validate(c, &request, requests.Pagination); !ok {
+        return
+    }
+    data, pager := user.Paginate(c, 2)
+    response.Data(c, gin.H{
+        "data":  data,
+        "pager": pager,
+    })
 }

+ 12 - 9
app/http/middlewares/recovery.go

@@ -1,6 +1,7 @@
 package middlewares
 
 import (
+	"errors"
 	"net"
 	"net/http/httputil"
 	"os"
@@ -8,9 +9,10 @@ import (
 	"time"
 
 	"github.com/gin-gonic/gin"
+	"go.uber.org/zap"
+
 	"github.com/runningwater/gohub/pkg/logger"
 	"github.com/runningwater/gohub/pkg/response"
-	"go.uber.org/zap"
 )
 
 // Recovery 使用 zap.Error() 来记录 panic 和 call stack
@@ -21,12 +23,13 @@ func Recovery() gin.HandlerFunc {
 			if err := recover(); err != nil {
 
 				// 获取用户的请求信息
-				httpRquest, _ := httputil.DumpRequest(c.Request, true)
+				request, _ := httputil.DumpRequest(c.Request, true)
 
 				// 链接中断,客户端中断连接为正常行为,不需要记录堆栈信息
 				var brokenPipe bool
 				if ne, ok := err.(*net.OpError); ok {
-					if se, ok := ne.Err.(*os.SyscallError); ok {
+					var se *os.SyscallError
+					if errors.As(ne.Err, &se) {
 						errStr := strings.ToLower(se.Error())
 						if strings.Contains(errStr, "broken pipe") || strings.Contains(errStr, "connection reset by peer") {
 							brokenPipe = true
@@ -38,19 +41,19 @@ func Recovery() gin.HandlerFunc {
 					logger.Error(c.Request.URL.Path,
 						zap.Time("time", time.Now()),
 						zap.Any("error", err),
-						zap.String("request", string(httpRquest)),
+						zap.String("request", string(request)),
 					)
-					c.Error(err.(error))
+					_ = c.Error(err.(error)) //nolint:errcheck
 					c.Abort()
 					// 链接已断开,无法写状态
 					return
 				}
 				// 如果不是链接中断的情况,记录堆栈信息
 				logger.Error("[Recovery from panic]",
-					zap.Time("time", time.Now()),              // 记录当前时间
-					zap.Any("error", err),                     // 记录错误信息
-					zap.String("request", string(httpRquest)), // 记录请求信息
-					zap.Stack("stack"),                        // 记录堆栈信息
+					zap.Time("time", time.Now()),           // 记录当前时间
+					zap.Any("error", err),                  // 记录错误信息
+					zap.String("request", string(request)), // 记录请求信息
+					zap.Stack("stack"),                     // 记录堆栈信息
 				)
 				// c.AbortWithStatusJSON(http.StatusInternalServerError, gin.H{
 				// 	"message": "服务器内部错误,请稍后再试",

+ 27 - 1
app/models/user/user_util.go

@@ -1,13 +1,21 @@
 package user
 
-import "github.com/runningwater/gohub/pkg/database"
+import (
+	"github.com/gin-gonic/gin"
 
+	"github.com/runningwater/gohub/pkg/app"
+	"github.com/runningwater/gohub/pkg/database"
+	"github.com/runningwater/gohub/pkg/paginator"
+)
+
+// IsEmailExist 检查邮箱是否已存在
 func IsEmailExist(email string) bool {
 	var count int64
 	database.DB.Model(&User{}).Where("email = ?", email).Count(&count)
 	return count > 0
 }
 
+// IsPhoneExist 检查手机号是否已存在
 func IsPhoneExist(phone string) bool {
 	var count int64
 	database.DB.Model(&User{}).Where("phone = ?", phone).Count(&count)
@@ -37,3 +45,21 @@ func Get(idStr string) (userModel User) {
 	database.DB.Where("id =?", idStr).First(&userModel)
 	return
 }
+
+// All 查询所有用户
+func All() (users []User) {
+	database.DB.Find(&users)
+	return
+}
+
+// Paginate 分页内容
+func Paginate(c *gin.Context, pageSize int) (users []User, paging paginator.Paging) {
+	paging = paginator.Paginate(
+		c,
+		database.DB.Model(User{}),
+		&users,
+		app.V1URL(database.TableName(&User{})),
+		pageSize,
+	)
+	return
+}

+ 37 - 0
app/requests/pagination_request.go

@@ -0,0 +1,37 @@
+// Author: simon (ynwdlxm@163.com)
+// Date: 2025/7/15 14:16
+// Desc: 分页请求验证器
+
+package requests
+
+import (
+    "github.com/gin-gonic/gin"
+    "github.com/thedevsaddam/govalidator"
+)
+
+type PaginationRequest struct {
+    Sort    string `valid:"sort" form:"sort"`
+    Order   string `valid:"order" form:"order"`
+    PerPage string `valid:"per_page" form:"per_page"`
+}
+
+func Pagination(data any, c *gin.Context) map[string][]string {
+    rules := govalidator.MapData{
+        "sort":     []string{"in:id,created_at,updated_at"},
+        "order":    []string{"in:asc,desc"},
+        "per_page": []string{"numeric_between:2,100"},
+    }
+    messages := govalidator.MapData{
+        "sort": []string{
+            "in:排序字段仅支持 id,created_at,updated_at",
+        },
+        "order": []string{
+            "in:排序规则仅支持 asc,desc",
+        },
+        "per_page": []string{
+            "numeric_between:每页条数的值介于 2~100 之间",
+        },
+    }
+
+    return validate(data, rules, messages)
+}

+ 8 - 7
app/requests/verify_code_request.go

@@ -2,15 +2,16 @@ package requests
 
 import (
 	"github.com/gin-gonic/gin"
-	"github.com/runningwater/gohub/pkg/captcha"
 	"github.com/thedevsaddam/govalidator"
+
+	"github.com/runningwater/gohub/pkg/captcha"
 )
 
 type VerifyCodeRequest struct {
-	Phone string `json:"phone,omitempy" valid:"phone"`
+	Phone string `json:"phone,omitempty" valid:"phone"`
 
-	CaptchaID     string `json:"captcha_id,omitempy" valid:"captcha_id"`
-	CaptchaAnswer string `json:"captcha_answer,omitempy" valid:"captcha_answer"`
+	CaptchaID     string `json:"captcha_id,omitempty" valid:"captcha_id"`
+	CaptchaAnswer string `json:"captcha_answer,omitempty" valid:"captcha_answer"`
 }
 
 func VerifyCodePhone(data any, c *gin.Context) map[string][]string {
@@ -48,10 +49,10 @@ func VerifyCodePhone(data any, c *gin.Context) map[string][]string {
 }
 
 type VerifyCodeEmailRequest struct {
-	Email string `json:"email,omitempy" valid:"email"`
+	Email string `json:"email,omitempty" valid:"email"`
 
-	CaptchaID     string `json:"captcha_id,omitempy" valid:"captcha_id"`
-	CaptchaAnswer string `json:"captcha_answer,omitempy" valid:"captcha_answer"`
+	CaptchaID     string `json:"captcha_id,omitempty" valid:"captcha_id"`
+	CaptchaAnswer string `json:"captcha_answer,omitempty" valid:"captcha_answer"`
 }
 
 func VerifyCodeEmail(data any, c *gin.Context) map[string][]string {

+ 21 - 0
config/paging.go

@@ -0,0 +1,21 @@
+// Author: simon (ynwdlxm@163.com)
+// Date: 2025/7/15 10:44
+// Desc: 分页设置
+
+package config
+
+import (
+	"github.com/runningwater/gohub/pkg/config"
+)
+
+func init() {
+	config.Add("paging", func() map[string]any {
+		return map[string]any{
+			"per_page":           10,         // 每页条数
+			"url_query_page":     "page",     // URL 中每页条数的参数
+			"url_query_sort":     "sort",     // URL 中排序的参数
+			"url_query_order":    "order",    // URL 中排序顺序的参数
+			"url_query_per_page": "per_page", // URL 中页码的参数
+		}
+	})
+}

+ 3 - 2
database/migrations/2025_06_03_101326_add_users_table.go

@@ -2,6 +2,7 @@ package migrations
 
 import (
 	"database/sql"
+
 	"gorm.io/gorm"
 
 	"github.com/runningwater/gohub/app/models"
@@ -22,11 +23,11 @@ func init() {
 	}
 
 	up := func(migrator gorm.Migrator, DB *sql.DB) {
-		migrator.AutoMigrate(&User{})
+		_ = migrator.AutoMigrate(&User{})
 	}
 
 	down := func(migrator gorm.Migrator, DB *sql.DB) {
-		migrator.DropTable(&User{})
+		_ = migrator.DropTable(&User{})
 	}
 
 	migrate.Add(up, down, "2025_06_03_101326_add_users_table")

+ 7 - 3
gohub.http

@@ -46,7 +46,7 @@ Content-Type: application/json
     "name":"summer",
     "password":"secret",
     "password_confirm":"secret",
-    "verify_code": "123123",
+    "verify_code": "439665",
     "phone": "00011059149"
 }
 
@@ -55,11 +55,11 @@ POST {{base_url}}/v1/auth/signup/using-email HTTP/1.1
 Content-Type: application/json
 
 {
-    "name":"summer2",
+    "name":"summer3",
     "password":"secret",
     "password_confirm":"secret",
     "verify_code": "123123",
-    "email": "summer@testing.com"
+    "email": "summer3@testing.com"
 }
 
 ### 登录用户
@@ -91,3 +91,7 @@ Authorization: Bearer {{access_token}}
 ### GET /current_user 当前登陆用户
 GET {{base_url}}/v1/auth/user HTTP/1.1
 Authorization: Bearer {{access_token}}
+
+
+### users
+GET {{base_url}}/v1/users HTTP/1.1

+ 2 - 1
http-client.env.json

@@ -1,5 +1,6 @@
 {
   "dev": {
-    "base_url": "http://127.0.0.1:3000"
+    "base_url": "http://127.0.0.1:3000",
+    "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyX2lkIjoiMSIsInVzZXJfbmFtZSI6InN1bW1lciIsImV4cGlyZV90aW1lIjoxNzU3NzMyNTk3LCJpc3MiOiJHb2h1YiIsInN1YiI6IjEiLCJhdWQiOlsic3VtbWVyIl0sImV4cCI6MTc1NzczMjU5NywibmJmIjoxNzUyNTQ4NTk3LCJpYXQiOjE3NTI1NDg1OTd9.4bE5RE1saBymOUuMewdAUKmy5U6AKM_tc4hQyH9lrk4"
   }
 }

+ 11 - 1
pkg/app/app.go

@@ -1,4 +1,4 @@
-// package app 应用信息包
+// Package app 应用信息包
 package app
 
 import (
@@ -28,3 +28,13 @@ func TimenowInTimezone() time.Time {
 	chinaTimeZone, _ := time.LoadLocation(timezone)
 	return time.Now().In(chinaTimeZone)
 }
+
+// URL 传参 path 拼接接点的 URL
+func URL(path string) string {
+	return config.GetString("app.url") + path
+}
+
+// V1URL 拼接 v1 版本的 URL
+func V1URL(path string) string {
+	return URL("/v1/" + path)
+}

+ 14 - 5
pkg/database/database.go

@@ -1,5 +1,3 @@
-// Project: go-orm
-// File: pkg/database/database.go
 // Package database 数据库连接
 package database
 
@@ -7,9 +5,10 @@ import (
 	"database/sql"
 	"fmt"
 
-	"github.com/runningwater/gohub/pkg/config"
 	"gorm.io/gorm"
 	gormlgger "gorm.io/gorm/logger"
+
+	"github.com/runningwater/gohub/pkg/config"
 )
 
 // DB 数据库连接实例
@@ -53,7 +52,7 @@ func DeleteAllTables() error {
 
 func deleteMySQLTables() error {
 	dbname := CurrentDatabase()
-	tables := []string{}
+	var tables []string
 
 	err := DB.Table("information_schema.tables").
 		Where("table_schema = ?", dbname).
@@ -78,7 +77,7 @@ func deleteMySQLTables() error {
 }
 
 func deleteAllSQLiteTables() error {
-	tables := []string{}
+	var tables []string
 
 	err := DB.Raw("SELECT name FROM sqlite_master WHERE type='table'").
 		Pluck("name", &tables).Error
@@ -95,3 +94,13 @@ func deleteAllSQLiteTables() error {
 
 	return nil
 }
+
+// TableName 获取表名
+func TableName(obj any) string {
+	stmt := &gorm.Statement{DB: DB}
+	err := stmt.Parse(obj)
+	if err != nil {
+		return ""
+	}
+	return stmt.Schema.Table
+}

+ 2 - 4
pkg/file/file.go

@@ -25,10 +25,8 @@ func Put(data []byte, to string) error {
 // 如果文件不存在,则返回 false
 func Exists(path string) bool {
 	_, err := os.Stat(path)
-	if os.IsNotExist(err) {
-		return false
-	}
-	return true
+
+	return !os.IsNotExist(err)
 }
 
 func NameWithoutExtension(fileName string) string {

+ 203 - 0
pkg/paginator/paginator.go

@@ -0,0 +1,203 @@
+// Package paginator 处理分页逻辑
+package paginator
+
+import (
+	"fmt"
+	"math"
+	"strings"
+
+	"github.com/gin-gonic/gin"
+	"github.com/spf13/cast"
+	"gorm.io/gorm"
+	"gorm.io/gorm/clause"
+
+	"github.com/runningwater/gohub/pkg/config"
+	"github.com/runningwater/gohub/pkg/logger"
+)
+
+// Paging 分页数据
+type Paging struct {
+	CurrentPage int    // 当前页
+	PageSize    int    // 每页条数
+	TotalPage   int    // 总页数
+	TotalCount  int64  // 总条数
+	NextPageURL string // 下一页的链接
+	PrevPageURL string // 上一页的链接
+}
+
+// Paginator 分页操作类
+type Paginator struct {
+	BaseURL     string // 用以拼接 URL
+	PageSize    int    // 每页条数
+	CurrentPage int    // 当前页
+	Offset      int    // 数据库读取数据时 Offset 的值
+	TotalCount  int64  // 总条数
+	TotalPage   int    // 总页数 = TotalCount/PerPage
+	Sort        string // 排序规则
+	Order       string // 排序顺序
+
+	query *gorm.DB     // db query 句柄
+	ctx   *gin.Context // gin context,方便调用
+}
+
+// Paginate 分页
+//
+//	 c —— gin.context 用来获取分页的 URL 参数
+//	 db —— GORM 查询句柄,用以查询数据集和获取数据总数
+//	 baseURL —— 用以分页链接
+//	 data —— 模型数组,传址获取数据
+//	 pageSize —— 每页条数,优先从 url 参数里取,否则使用 perPage 的值
+//	 用法:
+//
+//		   query := database.DB.Model(Topic{}).Where("category_id = ?", cid)
+//		   var topics []Topic
+//		   paging := paginator.Paginate(
+//		       c,
+//		       query,
+//		       &topics,
+//		       app.APIURL(database.TableName(&Topic{})),
+//		       perPage,
+//		   )
+func Paginate(c *gin.Context, db *gorm.DB, data any, baseURL string, pageSize int) Paging {
+
+	// 初始化 Paginator 实例
+	p := &Paginator{
+		query: db,
+		ctx:   c,
+	}
+	p.initProperties(pageSize, baseURL)
+
+	// 查询数据库
+	err := p.query.Preload(clause.Associations). // 读取关联
+							Order(p.Sort + " " + p.Order). // 排序
+							Limit(p.PageSize).
+							Offset(p.Offset).
+							Find(data).
+							Error
+
+	// 数据库出错
+	if err != nil {
+		logger.LogIf(err)
+		return Paging{}
+	}
+
+	return Paging{
+		CurrentPage: p.CurrentPage,
+		PageSize:    p.PageSize,
+		TotalPage:   p.TotalPage,
+		TotalCount:  p.TotalCount,
+		NextPageURL: p.getNextPageURL(),
+		PrevPageURL: p.getPrevPageURL(),
+	}
+}
+
+// 初始化分页必须用到的属性,基于这些属性查询数据库
+func (p *Paginator) initProperties(pageSize int, baseURL string) {
+
+	p.BaseURL = p.formatBaseURL(baseURL)
+	p.PageSize = p.getPageSize(pageSize)
+
+	// 排序参数(控制器中以验证过这些参数,可放心使用)
+	p.Order = p.ctx.DefaultQuery(config.Get("paging.url_query_order"), "asc")
+	p.Sort = p.ctx.DefaultQuery(config.Get("paging.url_query_sort"), "id")
+
+	p.TotalCount = p.getTotalCount()
+	p.TotalPage = p.getTotalPage()
+	p.CurrentPage = p.getCurrentPage()
+	p.Offset = (p.CurrentPage - 1) * p.PageSize
+}
+
+func (p *Paginator) getPageSize(pageSize int) int {
+	// 优先使用请求 per_page 参数
+	queryPerpage := p.ctx.Query(config.Get("paging.url_query_per_page"))
+	if len(queryPerpage) > 0 {
+		pageSize = cast.ToInt(queryPerpage)
+	}
+
+	// 没有传参,使用默认
+	if pageSize <= 0 {
+		pageSize = config.GetInt("paging.per_page")
+	}
+
+	return pageSize
+}
+
+// getCurrentPage 返回当前页码
+func (p *Paginator) getCurrentPage() int {
+	// 优先取用户请求的 page
+	page := cast.ToInt(p.ctx.Query(config.Get("paging.url_query_page")))
+	if page <= 0 {
+		// 默认为 1
+		page = 1
+	}
+	// TotalPage 等于 0 ,意味着数据不够分页
+	if p.TotalPage == 0 {
+		return 0
+	}
+	// 请求页数大于总页数,返回总页数
+	if page > p.TotalPage {
+		return p.TotalPage
+	}
+	return page
+}
+
+// getTotalCount 返回的是数据库里的条数
+func (p *Paginator) getTotalCount() int64 {
+	var count int64
+	if err := p.query.Count(&count).Error; err != nil {
+		return 0
+	}
+	return count
+}
+
+// getTotalPage 计算总页数
+func (p *Paginator) getTotalPage() int {
+	if p.TotalCount == 0 {
+		return 0
+	}
+	nums := int64(math.Ceil(float64(p.TotalCount) / float64(p.PageSize)))
+	if nums == 0 {
+		nums = 1
+	}
+	return int(nums)
+}
+
+// 兼容 URL 带与不带 `?` 的情况
+func (p *Paginator) formatBaseURL(baseURL string) string {
+	if strings.Contains(baseURL, "?") {
+		baseURL = baseURL + "&" + config.Get("paging.url_query_page") + "="
+	} else {
+		baseURL = baseURL + "?" + config.Get("paging.url_query_page") + "="
+	}
+	return baseURL
+}
+
+// 拼接分页链接
+func (p *Paginator) getPageLink(page int) string {
+	return fmt.Sprintf("%v%v&%s=%s&%s=%s&%s=%v",
+		p.BaseURL,
+		page,
+		config.Get("paging.url_query_sort"),
+		p.Sort,
+		config.Get("paging.url_query_order"),
+		p.Order,
+		config.Get("paging.url_query_per_page"),
+		p.PageSize,
+	)
+}
+
+// getNextPageURL 返回下一页的链接
+func (p *Paginator) getNextPageURL() string {
+	if p.TotalPage > p.CurrentPage {
+		return p.getPageLink(p.CurrentPage + 1)
+	}
+	return ""
+}
+
+// getPrevPageURL 返回下一页的链接
+func (p *Paginator) getPrevPageURL() string {
+	if p.CurrentPage <= 1 || p.CurrentPage > p.TotalPage {
+		return ""
+	}
+	return p.getPageLink(p.CurrentPage - 1)
+}

+ 15 - 12
pkg/sms/driver_aliyun.go

@@ -2,6 +2,7 @@ package sms
 
 import (
 	"encoding/json"
+	"errors"
 	"strings"
 
 	openapi "github.com/alibabacloud-go/darabonba-openapi/v2/client"
@@ -52,24 +53,26 @@ func (a *AliyunDriver) Send(phone string, message Message, config map[string]str
 	response, _e := sendSms(client, request, &util.RuntimeOptions{})
 	if _e != nil {
 		logger.ErrorString(moduleName, "发送失败", _e.Error())
-		var error = &tea.SDKError{}
-		if _t, ok := _e.(*tea.SDKError); ok {
-			error = _t
-		} else {
-			error.Message = tea.String(_e.Error())
+		var e = &tea.SDKError{}
+		var _t *tea.SDKError
+		if errors.As(_e, &_t) {
+			e = _t
 		}
 
 		// 错误消息
-		if error.Message != nil {
-			logger.DebugJSON(moduleName, "错误消息", tea.StringValue(error.Message))
+		if e.Message != nil {
+			logger.DebugJSON(moduleName, "错误消息", tea.StringValue(e.Message))
 		}
 
 		// 诊断地址
 		var data any
-		d := json.NewDecoder(strings.NewReader(tea.StringValue(error.Data)))
-		d.Decode(&data)
+		d := json.NewDecoder(strings.NewReader(tea.StringValue(e.Data)))
+		err := d.Decode(&data)
+		if err != nil {
+			return false
+		}
 		if m, ok := data.(map[string]any); ok {
-			recommend, _ := m["Recommend"]
+			recommend := m["Recommend"]
 			logger.DebugJSON(moduleName, "诊断地址", recommend)
 		}
 
@@ -89,14 +92,14 @@ func createClient() (*dysmsapi20170525.Client, error) {
 		return nil, err
 	}
 
-	config := &openapi.Config{
+	conf := &openapi.Config{
 		Credential:      credentials,
 		Endpoint:        tea.String("dysmsapi.aliyuncs.com"),
 		AccessKeyId:     tea.String(config.GetString("sms.aliyun.access_key_id")),
 		AccessKeySecret: tea.String(config.GetString("sms.aliyun.access_key_secret")),
 	}
 
-	return dysmsapi20170525.NewClient(config)
+	return dysmsapi20170525.NewClient(conf)
 }
 
 func sendSms(client *dysmsapi20170525.Client,

+ 6 - 2
routes/api.go

@@ -20,6 +20,8 @@ func RegisterAPIRoutes(router *gin.Engine) {
 	v1.Use(middlewares.LimitIP("200-H"))
 
 	{
+		uc := new(controllers.UsersController)
+
 		authGroup := v1.Group("/auth")
 		{
 			suc := new(auth.SignupController)
@@ -50,10 +52,12 @@ func RegisterAPIRoutes(router *gin.Engine) {
 			pc := new(auth.PasswordController)
 			// 使用手机重置密码
 			authGroup.POST("/password-reset/using-phone", middlewares.LimitPerRoute("20-H"), pc.ResetByPhone)
-
-			uc := new(controllers.UsersController)
 			// 当前登录用户信息
 			authGroup.GET("/user", middlewares.AuthJWT(), uc.CurrentUser)
 		}
+		userGroup := v1.Group("/users")
+		{
+			userGroup.GET("", uc.Index)
+		}
 	}
 }