feat(user): 添加用户分页列表接口及通用查询封装

This commit is contained in:
unclesam committed 2026-07-07 23:42:14 +08:00
1 parent 50dc1b0f09
commit 1bca4808e9
11 files changed
+165 -10

No files matched your search

+4 -4
View File
@@ -50,14 +50,14 @@ var userCreateCmd = &cobra.Command{
ctx := context.Background()
// 1. 尝试查询 admin 角色
// 尝试查询 admin 角色
adminRole, err := db.Client.Role.Query().
Where(role.CodeEQ("admin")).
Only(ctx)
if err != nil {
if ent.IsNotFound(err) {
// 2. 如果不存在 admin 角色,自动创建
// 如果不存在 admin 角色,自动创建
adminRole, err = db.Client.Role.Create().
SetCode("admin").
SetName("超级管理员").
@@ -75,11 +75,11 @@ var userCreateCmd = &cobra.Command{
}
}
// 3. 直接使用 db.Client 创建用户并挂载角色
// 直接使用 db.Client 创建用户并挂载角色
u, err := db.Client.User.Create().
SetUsername(username).
SetPassword(hashedPwd).
AddRoles(adminRole). // 💡 重点:挂载角色
AddRoles(adminRole).
Save(ctx)
if err != nil {
global.Log.Error("创建用户失败", zap.Error(err))
+5 -1
View File
@@ -1,9 +1,13 @@
package api
import "EnGin/internal/api/auth_api"
import (
"EnGin/internal/api/auth_api"
"EnGin/internal/api/user_api"
)
type Api struct {
AuthApi auth_api.AuthApi
UserApi user_api.UserApi
}
// App 统一导出全局 API 实例
+4
View File
@@ -0,0 +1,4 @@
package user_api
type UserApi struct {
}
+53
View File
@@ -0,0 +1,53 @@
package user_api
import (
"EnGin/internal/global"
"EnGin/internal/middleware"
"EnGin/internal/service/user_ser"
"EnGin/internal/utils/res"
"time"
"github.com/gin-gonic/gin"
"go.uber.org/zap"
)
type UserListRequest struct {
Page int `form:"page"`
Limit int `form:"limit"`
UsernameKey string `form:"username_key"` // 可选模糊搜索
}
type UserListResponse struct {
Username string `json:"username"`
Email string `json:"email"`
IP string `json:"ip"`
CreateAt time.Time `json:"create_at"`
}
func (UserApi) UserListView(c *gin.Context) {
cr := middleware.GetBind[UserListRequest](c)
list, count, err := user_ser.GetUserListWithPage(
c.Request.Context(),
cr.UsernameKey,
cr.Page,
cr.Limit,
)
if err != nil {
global.Log.Error("查询用户列表失败", zap.Error(err))
res.InternalError("获取用户列表失败", c)
return
}
var users = make([]UserListResponse, 0, count)
for _, u := range list {
users = append(users, UserListResponse{
Username: u.Username,
Email: u.Email,
IP: u.IP,
CreateAt: u.CreatedAt,
})
}
res.OkWithList(users, count, c)
}
+1 -1
View File
@@ -11,7 +11,7 @@ type Config struct {
Server ServerConfig `mapstructure:"server" yaml:"server"`
Database DatabaseConfig `mapstructure:"database" yaml:"database"`
Logger LoggerConfig `mapstructure:"logger" yaml:"logger"`
Jwt JwtConfig `mapstructure:"logger" yaml:"jwt"`
Jwt JwtConfig `mapstructure:"jwt" yaml:"jwt"`
Redis RedisConfig `mapstructure:"redis" yaml:"redis"`
}
+3 -3
View File
@@ -35,7 +35,7 @@ func Auth(c *gin.Context) {
func AuthAdmin(c *gin.Context) {
accessToken := c.GetHeader("access_token")
if accessToken == "" {
res.Unauthorized("未携带 access_token", c)
res.Unauthorized("认证失败", c)
c.Abort()
return
}
@@ -47,7 +47,7 @@ func AuthAdmin(c *gin.Context) {
return
}
// 💡 预加载用户的关联角色列表 (WithRoles)
// 预加载用户的关联角色列表 (WithRoles)
ctx := c.Request.Context()
u, err := db.Client.User.Query().
Where(user.ID(int(claims.UserID))).
@@ -60,7 +60,7 @@ func AuthAdmin(c *gin.Context) {
return
}
// 💡 遍历用户的角色列表,检查是否有 "admin" 角色代码
// 遍历用户的角色列表,检查是否有 "admin" 角色代码
isAdmin := false
for _, role := range u.Edges.Roles {
if role.Code == "admin" {
+1
View File
@@ -9,4 +9,5 @@ func RegisterRoutes(r *gin.Engine) {
// 分组并调用子路由
g := r.Group("api/v1")
AuthRouter(g)
UserRouter(g)
}
+17
View File
@@ -0,0 +1,17 @@
package router
import (
"EnGin/internal/api"
"EnGin/internal/api/user_api"
"EnGin/internal/middleware"
"github.com/gin-gonic/gin"
)
func UserRouter(g *gin.RouterGroup) {
app := api.App.UserApi
g.GET("users",
middleware.BindQueryMiddleware[user_api.UserListRequest],
middleware.AuthAdmin,
app.UserListView)
}
+49
View File
@@ -0,0 +1,49 @@
package common
import "context"
// EntQuery 定义满足 Ent 所有查询构建器的泛型接口
// Q 为具体的查询器类型(如 *ent.UserQuery),T 为返回的实体类型(如 *ent.User)
type EntQuery[Q any, T any] interface {
Limit(int) Q
Offset(int) Q
Count(ctx context.Context) (int, error)
All(ctx context.Context) ([]T, error)
}
// QueryOptions 通用列表查询配置项
type QueryOptions[Q any] struct {
Page int
Limit int
Query func(Q) Q // Query 闭包:在这里传入具体的过滤条件(Where)、排序(Order)和预加载(With)
}
func QueryList[Q EntQuery[Q, T], T any](ctx context.Context, query Q, opts QueryOptions[Q]) (list []T, count int, err error) {
if opts.Query != nil {
query = opts.Query(query)
}
count, err = query.Count(ctx)
if err != nil {
return nil, 0, err
}
page := opts.Page
if page <= 0 {
page = 1
}
limit := opts.Limit
if limit <= 0 {
limit = 10 // 默认每页 10 条
}
offset := (page - 1) * limit
list, err = query.Limit(limit).Offset(offset).All(ctx)
if err != nil {
return nil, 0, err
}
return list, count, nil
}
+27
View File
@@ -0,0 +1,27 @@
package user_ser
import (
"EnGin/internal/db"
"EnGin/internal/ent"
"EnGin/internal/ent/user"
"EnGin/internal/service/common"
"context"
"entgo.io/ent/dialect/sql"
)
func GetUserListWithPage(ctx context.Context, usernameKey string, page, limit int) ([]*ent.User, int, error) {
return common.QueryList(ctx, db.Client.User.Query(), common.QueryOptions[*ent.UserQuery]{
Page: page,
Limit: limit,
Query: func(q *ent.UserQuery) *ent.UserQuery {
// 模糊匹配(编译期安全,杜绝拼写错误)
if usernameKey != "" {
q = q.Where(user.UsernameContains(usernameKey))
}
// 预加载关联角色,并按照创建时间倒序
return q.WithRoles().Order(user.ByCreatedAt(sql.OrderDesc()))
},
})
}
+1 -1
View File
@@ -61,7 +61,7 @@ func OkWithMsg(msg string, c *gin.Context) {
Ok(gin.H{}, Success, msg, c)
}
func OkWithList(list any, count int64, c *gin.Context) {
func OkWithList(list any, count int, c *gin.Context) {
Ok(map[string]any{
"list": list,
"count": count,