feat(user): 添加用户分页列表接口及通用查询封装
This commit is contained in:
1 parent
50dc1b0f09
commit
1bca4808e9
11 files changed
+165
-10
No files matched your search
+4
-4
@@ -50,14 +50,14 @@ var userCreateCmd = &cobra.Command{
|
|||||||
|
|
||||||
ctx := context.Background()
|
ctx := context.Background()
|
||||||
|
|
||||||
// 1. 尝试查询 admin 角色
|
// 尝试查询 admin 角色
|
||||||
adminRole, err := db.Client.Role.Query().
|
adminRole, err := db.Client.Role.Query().
|
||||||
Where(role.CodeEQ("admin")).
|
Where(role.CodeEQ("admin")).
|
||||||
Only(ctx)
|
Only(ctx)
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if ent.IsNotFound(err) {
|
if ent.IsNotFound(err) {
|
||||||
// 2. 如果不存在 admin 角色,自动创建
|
// 如果不存在 admin 角色,自动创建
|
||||||
adminRole, err = db.Client.Role.Create().
|
adminRole, err = db.Client.Role.Create().
|
||||||
SetCode("admin").
|
SetCode("admin").
|
||||||
SetName("超级管理员").
|
SetName("超级管理员").
|
||||||
@@ -75,11 +75,11 @@ var userCreateCmd = &cobra.Command{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 3. 直接使用 db.Client 创建用户并挂载角色
|
// 直接使用 db.Client 创建用户并挂载角色
|
||||||
u, err := db.Client.User.Create().
|
u, err := db.Client.User.Create().
|
||||||
SetUsername(username).
|
SetUsername(username).
|
||||||
SetPassword(hashedPwd).
|
SetPassword(hashedPwd).
|
||||||
AddRoles(adminRole). // 💡 重点:挂载角色
|
AddRoles(adminRole).
|
||||||
Save(ctx)
|
Save(ctx)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
global.Log.Error("创建用户失败", zap.Error(err))
|
global.Log.Error("创建用户失败", zap.Error(err))
|
||||||
|
|||||||
@@ -1,9 +1,13 @@
|
|||||||
package api
|
package api
|
||||||
|
|
||||||
import "EnGin/internal/api/auth_api"
|
import (
|
||||||
|
"EnGin/internal/api/auth_api"
|
||||||
|
"EnGin/internal/api/user_api"
|
||||||
|
)
|
||||||
|
|
||||||
type Api struct {
|
type Api struct {
|
||||||
AuthApi auth_api.AuthApi
|
AuthApi auth_api.AuthApi
|
||||||
|
UserApi user_api.UserApi
|
||||||
}
|
}
|
||||||
|
|
||||||
// App 统一导出全局 API 实例
|
// App 统一导出全局 API 实例
|
||||||
|
|||||||
@@ -0,0 +1,4 @@
|
|||||||
|
package user_api
|
||||||
|
|
||||||
|
type UserApi struct {
|
||||||
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -11,7 +11,7 @@ type Config struct {
|
|||||||
Server ServerConfig `mapstructure:"server" yaml:"server"`
|
Server ServerConfig `mapstructure:"server" yaml:"server"`
|
||||||
Database DatabaseConfig `mapstructure:"database" yaml:"database"`
|
Database DatabaseConfig `mapstructure:"database" yaml:"database"`
|
||||||
Logger LoggerConfig `mapstructure:"logger" yaml:"logger"`
|
Logger LoggerConfig `mapstructure:"logger" yaml:"logger"`
|
||||||
Jwt JwtConfig `mapstructure:"logger" yaml:"jwt"`
|
Jwt JwtConfig `mapstructure:"jwt" yaml:"jwt"`
|
||||||
Redis RedisConfig `mapstructure:"redis" yaml:"redis"`
|
Redis RedisConfig `mapstructure:"redis" yaml:"redis"`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,7 +35,7 @@ func Auth(c *gin.Context) {
|
|||||||
func AuthAdmin(c *gin.Context) {
|
func AuthAdmin(c *gin.Context) {
|
||||||
accessToken := c.GetHeader("access_token")
|
accessToken := c.GetHeader("access_token")
|
||||||
if accessToken == "" {
|
if accessToken == "" {
|
||||||
res.Unauthorized("未携带 access_token", c)
|
res.Unauthorized("认证失败", c)
|
||||||
c.Abort()
|
c.Abort()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@@ -47,7 +47,7 @@ func AuthAdmin(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 💡 预加载用户的关联角色列表 (WithRoles)
|
// 预加载用户的关联角色列表 (WithRoles)
|
||||||
ctx := c.Request.Context()
|
ctx := c.Request.Context()
|
||||||
u, err := db.Client.User.Query().
|
u, err := db.Client.User.Query().
|
||||||
Where(user.ID(int(claims.UserID))).
|
Where(user.ID(int(claims.UserID))).
|
||||||
@@ -60,7 +60,7 @@ func AuthAdmin(c *gin.Context) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// 💡 遍历用户的角色列表,检查是否有 "admin" 角色代码
|
// 遍历用户的角色列表,检查是否有 "admin" 角色代码
|
||||||
isAdmin := false
|
isAdmin := false
|
||||||
for _, role := range u.Edges.Roles {
|
for _, role := range u.Edges.Roles {
|
||||||
if role.Code == "admin" {
|
if role.Code == "admin" {
|
||||||
|
|||||||
@@ -9,4 +9,5 @@ func RegisterRoutes(r *gin.Engine) {
|
|||||||
// 分组并调用子路由
|
// 分组并调用子路由
|
||||||
g := r.Group("api/v1")
|
g := r.Group("api/v1")
|
||||||
AuthRouter(g)
|
AuthRouter(g)
|
||||||
|
UserRouter(g)
|
||||||
}
|
}
|
||||||
@@ -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)
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
}
|
||||||
@@ -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()))
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -61,7 +61,7 @@ func OkWithMsg(msg string, c *gin.Context) {
|
|||||||
Ok(gin.H{}, Success, msg, c)
|
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{
|
Ok(map[string]any{
|
||||||
"list": list,
|
"list": list,
|
||||||
"count": count,
|
"count": count,
|
||||||
|
|||||||
Reference in new issue
Block a user