Răsfoiți Sursa

feat(api): 添加管理员相关接口和处理逻辑

- 新增管理员登录、菜单获取、角色管理、API管理、用户管理等接口
- 实现相应的处理逻辑,包括参数解析、服务调用、错误处理等
- 添加必要的请求和响应结构体定义
fusu 1 lună în urmă
părinte
comite
0306fb5c96

+ 228 - 0
api/v1/admin.go

@@ -0,0 +1,228 @@
+package v1
+
+type LoginRequest struct {
+	Username string `json:"username" binding:"required" example:"1234@gmail.com"`
+	Password string `json:"password" binding:"required" example:"123456"`
+}
+type LoginResponseData struct {
+	AccessToken string `json:"accessToken"`
+}
+type LoginResponse struct {
+	Response
+	Data LoginResponseData
+}
+
+type AdminUserDataItem struct {
+	ID        uint     `json:"id"`
+	Username  string   `json:"username" binding:"required" example:"张三"`
+	Nickname  string   `json:"nickname" binding:"required" example:"小Baby"`
+	Password  string   `json:"password" binding:"required" example:"123456"`
+	Email     string   `json:"email" binding:"required,email" example:"1234@gmail.com"`
+	Phone     string   `form:"phone" binding:"" example:"1858888888"`
+	Roles     []string `json:"roles" example:""`
+	UpdatedAt string   `json:"updatedAt"`
+	CreatedAt string   `json:"createdAt"`
+}
+type GetAdminUsersRequest struct {
+	Page     int    `form:"page" binding:"required" example:"1"`
+	PageSize int    `form:"pageSize" binding:"required" example:"10"`
+	Username string `json:"username" binding:"" example:"张三"`
+	Nickname string `json:"nickname" binding:"" example:"小Baby"`
+	Phone    string `form:"phone" binding:"" example:"1858888888"`
+	Email    string `form:"email" binding:"" example:"1234@gmail.com"`
+}
+type GetAdminUserResponseData struct {
+	ID        uint     `json:"id"`
+	Username  string   `json:"username" example:"张三"`
+	Nickname  string   `json:"nickname" example:"小Baby"`
+	Password  string   `json:"password" example:"123456"`
+	Email     string   `json:"email" example:"1234@gmail.com"`
+	Phone     string   `form:"phone" example:"1858888888"`
+	Roles     []string `json:"roles" example:""`
+	UpdatedAt string   `json:"updatedAt"`
+	CreatedAt string   `json:"createdAt"`
+}
+type GetAdminUserResponse struct {
+	Response
+	Data GetAdminUserResponseData
+}
+type GetAdminUsersResponseData struct {
+	List  []AdminUserDataItem `json:"list"`
+	Total int64               `json:"total"`
+}
+type GetAdminUsersResponse struct {
+	Response
+	Data GetAdminUsersResponseData
+}
+type AdminUserCreateRequest struct {
+	Username string   `json:"username" binding:"required" example:"张三"`
+	Nickname string   `json:"nickname" binding:"" example:"小Baby"`
+	Password string   `json:"password" binding:"required" example:"123456"`
+	Email    string   `json:"email" binding:"" example:"1234@gmail.com"`
+	Phone    string   `form:"phone" binding:"" example:"1858888888"`
+	Roles    []string `json:"roles" example:""`
+}
+type AdminUserUpdateRequest struct {
+	ID       uint     `json:"id"`
+	Username string   `json:"username" binding:"required" example:"张三"`
+	Nickname string   `json:"nickname" binding:"" example:"小Baby"`
+	Password string   `json:"password" binding:"" example:"123456"`
+	Email    string   `json:"email" binding:"" example:"1234@gmail.com"`
+	Phone    string   `form:"phone" binding:"" example:"1858888888"`
+	Roles    []string `json:"roles" example:""`
+}
+type AdminUserDeleteRequest struct {
+	ID uint `form:"id" binding:"required" example:"1"`
+}
+
+type MenuDataItem struct {
+	ID         uint   `json:"id,omitempty"`         // 唯一id,使用整数表示
+	ParentID   uint   `json:"parentId,omitempty"`   // 父级菜单的id,使用整数表示
+	Weight     int    `json:"weight"`               // 排序权重
+	Path       string `json:"path"`                 // 地址
+	Title      string `json:"title"`                // 展示名称
+	Name       string `json:"name,omitempty"`       // 同路由中的name,唯一标识
+	Component  string `json:"component,omitempty"`  // 绑定的组件
+	Locale     string `json:"locale,omitempty"`     // 本地化标识
+	Icon       string `json:"icon,omitempty"`       // 图标,使用字符串表示
+	Redirect   string `json:"redirect,omitempty"`   // 重定向地址
+	KeepAlive  bool   `json:"keepAlive,omitempty"`  // 是否保活
+	HideInMenu bool   `json:"hideInMenu,omitempty"` // 是否保活
+	URL        string `json:"url,omitempty"`        // iframe模式下的跳转url,不能与path重复
+	UpdatedAt  string `json:"updatedAt,omitempty"`  // 是否保活
+}
+type GetMenuResponseData struct {
+	List []MenuDataItem `json:"list"`
+}
+
+type GetMenuResponse struct {
+	Response
+	Data GetMenuResponseData
+}
+
+type MenuCreateRequest struct {
+	ParentID   uint   `json:"parentId,omitempty"`   // 父级菜单的id,使用整数表示
+	Weight     int    `json:"weight"`               // 排序权重
+	Path       string `json:"path"`                 // 地址
+	Title      string `json:"title"`                // 展示名称
+	Name       string `json:"name,omitempty"`       // 同路由中的name,唯一标识
+	Component  string `json:"component,omitempty"`  // 绑定的组件
+	Locale     string `json:"locale,omitempty"`     // 本地化标识
+	Icon       string `json:"icon,omitempty"`       // 图标,使用字符串表示
+	Redirect   string `json:"redirect,omitempty"`   // 重定向地址
+	KeepAlive  bool   `json:"keepAlive,omitempty"`  // 是否保活
+	HideInMenu bool   `json:"hideInMenu,omitempty"` // 是否保活
+	URL        string `json:"url,omitempty"`        // iframe模式下的跳转url,不能与path重复
+
+}
+type MenuUpdateRequest struct {
+	ID         uint   `json:"id,omitempty"`         // 唯一id,使用整数表示
+	ParentID   uint   `json:"parentId,omitempty"`   // 父级菜单的id,使用整数表示
+	Weight     int    `json:"weight"`               // 排序权重
+	Path       string `json:"path"`                 // 地址
+	Title      string `json:"title"`                // 展示名称
+	Name       string `json:"name,omitempty"`       // 同路由中的name,唯一标识
+	Component  string `json:"component,omitempty"`  // 绑定的组件
+	Locale     string `json:"locale,omitempty"`     // 本地化标识
+	Icon       string `json:"icon,omitempty"`       // 图标,使用字符串表示
+	Redirect   string `json:"redirect,omitempty"`   // 重定向地址
+	KeepAlive  bool   `json:"keepAlive,omitempty"`  // 是否保活
+	HideInMenu bool   `json:"hideInMenu,omitempty"` // 是否保活
+	URL        string `json:"url,omitempty"`        // iframe模式下的跳转url,不能与path重复
+	UpdatedAt  string `json:"updatedAt"`
+}
+type MenuDeleteRequest struct {
+	ID uint `form:"id"` // 唯一id,使用整数表示
+}
+type GetRoleListRequest struct {
+	Page     int    `form:"page" binding:"required" example:"1"`
+	PageSize int    `form:"pageSize" binding:"required" example:"10"`
+	Sid      string `form:"sid" binding:"" example:"1"`
+	Name     string `form:"name" binding:"" example:"Admin"`
+}
+type RoleDataItem struct {
+	ID        uint   `json:"id"`
+	Name      string `json:"name"`
+	Sid       string `json:"sid"`
+	UpdatedAt string `json:"updatedAt"`
+	CreatedAt string `json:"createdAt"`
+}
+type GetRolesResponseData struct {
+	List  []RoleDataItem `json:"list"`
+	Total int64          `json:"total"`
+}
+type GetRolesResponse struct {
+	Response
+	Data GetRolesResponseData
+}
+type RoleCreateRequest struct {
+	Sid  string `form:"sid" binding:"required" example:"1"`
+	Name string `form:"name" binding:"required" example:"Admin"`
+}
+type RoleUpdateRequest struct {
+	ID   uint   `form:"id" binding:"required" example:"1"`
+	Sid  string `form:"sid" binding:"required" example:"1"`
+	Name string `form:"name" binding:"required" example:"Admin"`
+}
+type RoleDeleteRequest struct {
+	ID uint `form:"id" binding:"required" example:"1"`
+}
+type PermissionCreateRequest struct {
+	Sid  string `form:"sid" binding:"required" example:"1"`
+	Name string `form:"name" binding:"required" example:"Admin"`
+}
+type GetApisRequest struct {
+	Page     int    `form:"page" binding:"required" example:"1"`
+	PageSize int    `form:"pageSize" binding:"required" example:"10"`
+	Group    string `form:"group" binding:"" example:"权限管理"`
+	Name     string `form:"name" binding:"" example:"菜单列表"`
+	Path     string `form:"path" binding:"" example:"/v1/test"`
+	Method   string `form:"method" binding:"" example:"GET"`
+}
+type ApiDataItem struct {
+	ID        uint   `json:"id"`
+	Name      string `json:"name"`
+	Path      string `json:"path"`
+	Method    string `json:"method"`
+	Group     string `json:"group"`
+	UpdatedAt string `json:"updatedAt"`
+	CreatedAt string `json:"createdAt"`
+}
+type GetApisResponseData struct {
+	List   []ApiDataItem `json:"list"`
+	Total  int64         `json:"total"`
+	Groups []string      `json:"groups"`
+}
+type GetApisResponse struct {
+	Response
+	Data GetApisResponseData
+}
+type ApiCreateRequest struct {
+	Group  string `form:"group" binding:"" example:"权限管理"`
+	Name   string `form:"name" binding:"" example:"菜单列表"`
+	Path   string `form:"path" binding:"" example:"/v1/test"`
+	Method string `form:"method" binding:"" example:"GET"`
+}
+type ApiUpdateRequest struct {
+	ID     uint   `form:"id" binding:"required" example:"1"`
+	Group  string `form:"group" binding:"" example:"权限管理"`
+	Name   string `form:"name" binding:"" example:"菜单列表"`
+	Path   string `form:"path" binding:"" example:"/v1/test"`
+	Method string `form:"method" binding:"" example:"GET"`
+}
+type ApiDeleteRequest struct {
+	ID uint `form:"id" binding:"required" example:"1"`
+}
+type GetUserPermissionsData struct {
+	List []string `json:"list"`
+}
+type GetRolePermissionsRequest struct {
+	Role string `form:"role" binding:"required" example:"admin"`
+}
+type GetRolePermissionsData struct {
+	List []string `json:"list"`
+}
+type UpdateRolePermissionRequest struct {
+	Role string   `form:"role" binding:"required" example:"admin"`
+	List []string `form:"list" binding:"required" example:""`
+}

+ 0 - 11
api/v1/user.go

@@ -5,17 +5,6 @@ type RegisterRequest struct {
 	Password string `json:"password" binding:"required" example:"123456"`
 }
 
-type LoginRequest struct {
-	Email    string `json:"email" binding:"required,email" example:"1234@gmail.com"`
-	Password string `json:"password" binding:"required" example:"123456"`
-}
-type LoginResponseData struct {
-	AccessToken string `json:"accessToken"`
-}
-type LoginResponse struct {
-	Response
-	Data LoginResponseData
-}
 
 type UpdateProfileRequest struct {
 	Nickname string `json:"nickname" example:"alan"`

+ 4 - 0
cmd/server/wire/wire.go

@@ -28,6 +28,7 @@ var repositorySet = wire.NewSet(
 	repository.NewRabbitMQ,
 	repository.NewRepository,
 	repository.NewTransaction,
+	repository.NewAdminRepository,
 	repository.NewUserRepository,
 	repository.NewGameShieldRepository,
 	repository.NewGameShieldPublicIpRepository,
@@ -44,12 +45,14 @@ var repositorySet = wire.NewSet(
 	repository.NewGlobalLimitRepository,
 	repository.NewGatewayGroupRepository,
 	repository.NewGateWayGroupIpRepository,
+
 )
 
 var serviceSet = wire.NewSet(
 	service.NewService,
 	service.NewAoDunService,
 	service.NewUserService,
+	service.NewAdminService,
 	service.NewGameShieldService,
 	
 	service.NewGameShieldPublicIpService,
@@ -77,6 +80,7 @@ var serviceSet = wire.NewSet(
 var handlerSet = wire.NewSet(
 	handler.NewHandler,
 	handler.NewUserHandler,
+	handler.NewAdminHandler,
 	handler.NewGameShieldHandler,
 	handler.NewGameShieldPublicIpHandler,
 	handler.NewWebForwardingHandler,

+ 7 - 4
cmd/server/wire/wire_gen.go

@@ -85,7 +85,10 @@ func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), err
 	gatewayGroupService := service.NewGatewayGroupService(serviceService, gatewayGroupRepository, requiredService, parserService)
 	globalLimitService := service.NewGlobalLimitService(serviceService, globalLimitRepository, duedateService, crawlerService, viperViper, requiredService, parserService, hostService, tcpLimitService, udpLimitService, webLimitService, gatewayGroupService, hostRepository, gatewayGroupRepository)
 	globalLimitHandler := handler.NewGlobalLimitHandler(handlerHandler, globalLimitService)
-	httpServer := server.NewHTTPServer(logger, viperViper, jwtJWT, syncedEnforcer, limiterLimiter, handlerFunc, userHandler, gameShieldHandler, gameShieldBackendHandler, webForwardingHandler, webLimitHandler, tcpforwardingHandler, udpForWardingHandler, tcpLimitHandler, udpLimitHandler, globalLimitHandler)
+	adminRepository := repository.NewAdminRepository(repositoryRepository)
+	adminService := service.NewAdminService(serviceService, adminRepository)
+	adminHandler := handler.NewAdminHandler(handlerHandler, adminService)
+	httpServer := server.NewHTTPServer(logger, viperViper, jwtJWT, syncedEnforcer, limiterLimiter, handlerFunc, userHandler, gameShieldHandler, gameShieldBackendHandler, webForwardingHandler, webLimitHandler, tcpforwardingHandler, udpForWardingHandler, tcpLimitHandler, udpLimitHandler, globalLimitHandler, adminHandler)
 	appApp := newApp(httpServer)
 	return appApp, func() {
 		cleanup()
@@ -94,11 +97,11 @@ func NewWire(viperViper *viper.Viper, logger *log.Logger) (*app.App, func(), err
 
 // wire.go:
 
-var repositorySet = wire.NewSet(repository.NewDB, repository.NewCasbinEnforcer, repository.NewMongoClient, repository.NewMongoDB, repository.NewRabbitMQ, repository.NewRepository, repository.NewTransaction, repository.NewUserRepository, repository.NewGameShieldRepository, repository.NewGameShieldPublicIpRepository, repository.NewWebForwardingRepository, repository.NewTcpforwardingRepository, repository.NewUdpForWardingRepository, repository.NewGameShieldUserIpRepository, repository.NewWebLimitRepository, repository.NewTcpLimitRepository, repository.NewUdpLimitRepository, repository.NewGameShieldBackendRepository, repository.NewGameShieldSdkIpRepository, repository.NewHostRepository, repository.NewGlobalLimitRepository, repository.NewGatewayGroupRepository, repository.NewGateWayGroupIpRepository)
+var repositorySet = wire.NewSet(repository.NewDB, repository.NewCasbinEnforcer, repository.NewMongoClient, repository.NewMongoDB, repository.NewRabbitMQ, repository.NewRepository, repository.NewTransaction, repository.NewAdminRepository, repository.NewUserRepository, repository.NewGameShieldRepository, repository.NewGameShieldPublicIpRepository, repository.NewWebForwardingRepository, repository.NewTcpforwardingRepository, repository.NewUdpForWardingRepository, repository.NewGameShieldUserIpRepository, repository.NewWebLimitRepository, repository.NewTcpLimitRepository, repository.NewUdpLimitRepository, repository.NewGameShieldBackendRepository, repository.NewGameShieldSdkIpRepository, repository.NewHostRepository, repository.NewGlobalLimitRepository, repository.NewGatewayGroupRepository, repository.NewGateWayGroupIpRepository)
 
-var serviceSet = wire.NewSet(service.NewService, service.NewAoDunService, service.NewUserService, service.NewGameShieldService, service.NewGameShieldPublicIpService, service.NewDuedateService, service.NewFormatterService, service.NewParserService, service.NewRequiredService, service.NewCrawlerService, service.NewWebForwardingService, service.NewTcpforwardingService, service.NewUdpForWardingService, service.NewGameShieldUserIpService, service.NewWebLimitService, service.NewTcpLimitService, service.NewUdpLimitService, service.NewGameShieldBackendService, service.NewGameShieldSdkIpService, service.NewHostService, service.NewGlobalLimitService, service.NewGatewayGroupService, service.NewWafFormatterService, service.NewGateWayGroupIpService)
+var serviceSet = wire.NewSet(service.NewService, service.NewAoDunService, service.NewUserService, service.NewAdminService, service.NewGameShieldService, service.NewGameShieldPublicIpService, service.NewDuedateService, service.NewFormatterService, service.NewParserService, service.NewRequiredService, service.NewCrawlerService, service.NewWebForwardingService, service.NewTcpforwardingService, service.NewUdpForWardingService, service.NewGameShieldUserIpService, service.NewWebLimitService, service.NewTcpLimitService, service.NewUdpLimitService, service.NewGameShieldBackendService, service.NewGameShieldSdkIpService, service.NewHostService, service.NewGlobalLimitService, service.NewGatewayGroupService, service.NewWafFormatterService, service.NewGateWayGroupIpService)
 
-var handlerSet = wire.NewSet(handler.NewHandler, handler.NewUserHandler, handler.NewGameShieldHandler, handler.NewGameShieldPublicIpHandler, handler.NewWebForwardingHandler, handler.NewTcpforwardingHandler, handler.NewUdpForWardingHandler, handler.NewGameShieldUserIpHandler, handler.NewWebLimitHandler, handler.NewTcpLimitHandler, handler.NewUdpLimitHandler, handler.NewGameShieldBackendHandler, handler.NewGameShieldSdkIpHandler, handler.NewHostHandler, handler.NewGlobalLimitHandler, handler.NewGatewayGroupHandler, handler.NewGateWayGroupIpHandler)
+var handlerSet = wire.NewSet(handler.NewHandler, handler.NewUserHandler, handler.NewAdminHandler, handler.NewGameShieldHandler, handler.NewGameShieldPublicIpHandler, handler.NewWebForwardingHandler, handler.NewTcpforwardingHandler, handler.NewUdpForWardingHandler, handler.NewGameShieldUserIpHandler, handler.NewWebLimitHandler, handler.NewTcpLimitHandler, handler.NewUdpLimitHandler, handler.NewGameShieldBackendHandler, handler.NewGameShieldSdkIpHandler, handler.NewHostHandler, handler.NewGlobalLimitHandler, handler.NewGatewayGroupHandler, handler.NewGateWayGroupIpHandler)
 
 // 限流器依赖集
 var limiterSet = wire.NewSet(limiter.NewLimiter, middleware.NewRateLimitMiddleware)

+ 561 - 0
internal/handler/admin.go

@@ -0,0 +1,561 @@
+package handler
+
+import (
+	"github.com/gin-gonic/gin"
+	v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
+	"github.com/go-nunu/nunu-layout-advanced/internal/service"
+	"net/http"
+)
+
+type AdminHandler struct {
+	*Handler
+	adminService service.AdminService
+}
+
+func NewAdminHandler(
+	handler *Handler,
+	adminService service.AdminService,
+) *AdminHandler {
+	return &AdminHandler{
+		Handler:      handler,
+		adminService: adminService,
+	}
+}
+
+// Login godoc
+// @Summary 账号登录
+// @Schemes
+// @Description
+// @Tags 用户模块
+// @Accept json
+// @Produce json
+// @Param request body v1.LoginRequest true "params"
+// @Success 200 {object} v1.LoginResponse
+// @Router /v1/login [post]
+func (h *AdminHandler) Login(ctx *gin.Context) {
+	var req v1.LoginRequest
+	if err := ctx.ShouldBindJSON(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+
+	token, err := h.adminService.Login(ctx, &req)
+	if err != nil {
+		v1.HandleError(ctx, http.StatusUnauthorized, v1.ErrUnauthorized, nil)
+		return
+	}
+	v1.HandleSuccess(ctx, v1.LoginResponseData{
+		AccessToken: token,
+	})
+}
+
+// GetMenus godoc
+// @Summary 获取用户菜单
+// @Schemes
+// @Description 获取当前用户的菜单列表
+// @Tags 菜单模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Success 200 {object} v1.GetMenuResponse
+// @Router /v1/menus [get]
+func (h *AdminHandler) GetMenus(ctx *gin.Context) {
+	data, err := h.adminService.GetMenus(ctx, GetUserIdFromCtx(ctx))
+	if err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	// 过滤权限菜单
+	v1.HandleSuccess(ctx, data)
+}
+
+// GetAdminMenus godoc
+// @Summary 获取管理员菜单
+// @Schemes
+// @Description 获取管理员菜单列表
+// @Tags 菜单模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Success 200 {object} v1.GetMenuResponse
+// @Router /v1/admin/menus [get]
+func (h *AdminHandler) GetAdminMenus(ctx *gin.Context) {
+	data, err := h.adminService.GetAdminMenus(ctx)
+	if err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	// 过滤权限菜单
+	v1.HandleSuccess(ctx, data)
+}
+
+// GetUserPermissions godoc
+// @Summary 获取用户权限
+// @Schemes
+// @Description 获取当前用户的权限列表
+// @Tags 权限模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Success 200 {object} v1.GetUserPermissionsData
+// @Router /v1/admin/user/permissions [get]
+func (h *AdminHandler) GetUserPermissions(ctx *gin.Context) {
+	data, err := h.adminService.GetUserPermissions(ctx, GetUserIdFromCtx(ctx))
+	if err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	// 过滤权限菜单
+	v1.HandleSuccess(ctx, data)
+}
+
+// GetRolePermissions godoc
+// @Summary 获取角色权限
+// @Schemes
+// @Description 获取指定角色的权限列表
+// @Tags 权限模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param role query string true "角色名称"
+// @Success 200 {object} v1.GetRolePermissionsData
+// @Router /v1/admin/role/permissions [get]
+func (h *AdminHandler) GetRolePermissions(ctx *gin.Context) {
+	var req v1.GetRolePermissionsRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	data, err := h.adminService.GetRolePermissions(ctx, req.Role)
+	if err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+	v1.HandleSuccess(ctx, data)
+}
+
+// UpdateRolePermission godoc
+// @Summary 更新角色权限
+// @Schemes
+// @Description 更新指定角色的权限列表
+// @Tags 权限模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param request body v1.UpdateRolePermissionRequest true "参数"
+// @Success 200 {object} v1.Response
+// @Router /v1/admin/role/permissions [put]
+func (h *AdminHandler) UpdateRolePermission(ctx *gin.Context) {
+	var req v1.UpdateRolePermissionRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	err := h.adminService.UpdateRolePermission(ctx, &req)
+	if err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+	v1.HandleSuccess(ctx, nil)
+}
+
+// MenuUpdate godoc
+// @Summary 更新菜单
+// @Schemes
+// @Description 更新菜单信息
+// @Tags 菜单模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param request body v1.MenuUpdateRequest true "参数"
+// @Success 200 {object} v1.Response
+// @Router /v1/admin/menu [put]
+func (h *AdminHandler) MenuUpdate(ctx *gin.Context) {
+	var req v1.MenuUpdateRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	if err := h.adminService.MenuUpdate(ctx, &req); err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+	v1.HandleSuccess(ctx, nil)
+}
+
+// MenuCreate godoc
+// @Summary 创建菜单
+// @Schemes
+// @Description 创建新的菜单
+// @Tags 菜单模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param request body v1.MenuCreateRequest true "参数"
+// @Success 200 {object} v1.Response
+// @Router /v1/admin/menu [post]
+func (h *AdminHandler) MenuCreate(ctx *gin.Context) {
+	var req v1.MenuCreateRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	if err := h.adminService.MenuCreate(ctx, &req); err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+	v1.HandleSuccess(ctx, nil)
+}
+
+// MenuDelete godoc
+// @Summary 删除菜单
+// @Schemes
+// @Description 删除指定菜单
+// @Tags 菜单模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param id query uint true "菜单ID"
+// @Success 200 {object} v1.Response
+// @Router /v1/admin/menu [delete]
+func (h *AdminHandler) MenuDelete(ctx *gin.Context) {
+	var req v1.MenuDeleteRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	if err := h.adminService.MenuDelete(ctx, req.ID); err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+
+	}
+	v1.HandleSuccess(ctx, nil)
+}
+
+// GetRoles godoc
+// @Summary 获取角色列表
+// @Schemes
+// @Description 获取角色列表
+// @Tags 角色模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param page query int true "页码"
+// @Param pageSize query int true "每页数量"
+// @Param sid query string false "角色ID"
+// @Param name query string false "角色名称"
+// @Success 200 {object} v1.GetRolesResponse
+// @Router /v1/admin/roles [get]
+func (h *AdminHandler) GetRoles(ctx *gin.Context) {
+	var req v1.GetRoleListRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	data, err := h.adminService.GetRoles(ctx, &req)
+	if err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+
+	v1.HandleSuccess(ctx, data)
+}
+
+// RoleCreate godoc
+// @Summary 创建角色
+// @Schemes
+// @Description 创建新的角色
+// @Tags 角色模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param request body v1.RoleCreateRequest true "参数"
+// @Success 200 {object} v1.Response
+// @Router /v1/admin/role [post]
+func (h *AdminHandler) RoleCreate(ctx *gin.Context) {
+	var req v1.RoleCreateRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	if err := h.adminService.RoleCreate(ctx, &req); err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+	v1.HandleSuccess(ctx, nil)
+}
+
+// RoleUpdate godoc
+// @Summary 更新角色
+// @Schemes
+// @Description 更新角色信息
+// @Tags 角色模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param request body v1.RoleUpdateRequest true "参数"
+// @Success 200 {object} v1.Response
+// @Router /v1/admin/role [put]
+func (h *AdminHandler) RoleUpdate(ctx *gin.Context) {
+	var req v1.RoleUpdateRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	if err := h.adminService.RoleUpdate(ctx, &req); err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+	v1.HandleSuccess(ctx, nil)
+}
+
+// RoleDelete godoc
+// @Summary 删除角色
+// @Schemes
+// @Description 删除指定角色
+// @Tags 角色模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param id query uint true "角色ID"
+// @Success 200 {object} v1.Response
+// @Router /v1/admin/role [delete]
+func (h *AdminHandler) RoleDelete(ctx *gin.Context) {
+	var req v1.RoleDeleteRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	if err := h.adminService.RoleDelete(ctx, req.ID); err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+	v1.HandleSuccess(ctx, nil)
+}
+
+// GetApis godoc
+// @Summary 获取API列表
+// @Schemes
+// @Description 获取API列表
+// @Tags API模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param page query int true "页码"
+// @Param pageSize query int true "每页数量"
+// @Param group query string false "API分组"
+// @Param name query string false "API名称"
+// @Param path query string false "API路径"
+// @Param method query string false "请求方法"
+// @Success 200 {object} v1.GetApisResponse
+// @Router /v1/admin/apis [get]
+func (h *AdminHandler) GetApis(ctx *gin.Context) {
+	var req v1.GetApisRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	data, err := h.adminService.GetApis(ctx, &req)
+	if err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+
+	v1.HandleSuccess(ctx, data)
+}
+
+// ApiCreate godoc
+// @Summary 创建API
+// @Schemes
+// @Description 创建新的API
+// @Tags API模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param request body v1.ApiCreateRequest true "参数"
+// @Success 200 {object} v1.Response
+// @Router /v1/admin/api [post]
+func (h *AdminHandler) ApiCreate(ctx *gin.Context) {
+	var req v1.ApiCreateRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	if err := h.adminService.ApiCreate(ctx, &req); err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+	v1.HandleSuccess(ctx, nil)
+}
+
+// ApiUpdate godoc
+// @Summary 更新API
+// @Schemes
+// @Description 更新API信息
+// @Tags API模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param request body v1.ApiUpdateRequest true "参数"
+// @Success 200 {object} v1.Response
+// @Router /v1/admin/api [put]
+func (h *AdminHandler) ApiUpdate(ctx *gin.Context) {
+	var req v1.ApiUpdateRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	if err := h.adminService.ApiUpdate(ctx, &req); err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+	v1.HandleSuccess(ctx, nil)
+}
+
+// ApiDelete godoc
+// @Summary 删除API
+// @Schemes
+// @Description 删除指定API
+// @Tags API模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param id query uint true "API ID"
+// @Success 200 {object} v1.Response
+// @Router /v1/admin/api [delete]
+func (h *AdminHandler) ApiDelete(ctx *gin.Context) {
+	var req v1.ApiDeleteRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	if err := h.adminService.ApiDelete(ctx, req.ID); err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+	v1.HandleSuccess(ctx, nil)
+}
+
+// AdminUserUpdate godoc
+// @Summary 更新管理员用户
+// @Schemes
+// @Description 更新管理员用户信息
+// @Tags 用户模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param request body v1.AdminUserUpdateRequest true "参数"
+// @Success 200 {object} v1.Response
+// @Router /v1/admin/user [put]
+func (h *AdminHandler) AdminUserUpdate(ctx *gin.Context) {
+	var req v1.AdminUserUpdateRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	if err := h.adminService.AdminUserUpdate(ctx, &req); err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+	v1.HandleSuccess(ctx, nil)
+}
+
+// AdminUserCreate godoc
+// @Summary 创建管理员用户
+// @Schemes
+// @Description 创建新的管理员用户
+// @Tags 用户模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param request body v1.AdminUserCreateRequest true "参数"
+// @Success 200 {object} v1.Response
+// @Router /v1/admin/user [post]
+func (h *AdminHandler) AdminUserCreate(ctx *gin.Context) {
+	var req v1.AdminUserCreateRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	if err := h.adminService.AdminUserCreate(ctx, &req); err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+	v1.HandleSuccess(ctx, nil)
+}
+
+// AdminUserDelete godoc
+// @Summary 删除管理员用户
+// @Schemes
+// @Description 删除指定管理员用户
+// @Tags 用户模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param id query uint true "用户ID"
+// @Success 200 {object} v1.Response
+// @Router /v1/admin/user [delete]
+func (h *AdminHandler) AdminUserDelete(ctx *gin.Context) {
+	var req v1.AdminUserDeleteRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	if err := h.adminService.AdminUserDelete(ctx, req.ID); err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+
+	}
+	v1.HandleSuccess(ctx, nil)
+}
+
+// GetAdminUsers godoc
+// @Summary 获取管理员用户列表
+// @Schemes
+// @Description 获取管理员用户列表
+// @Tags 用户模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Param page query int true "页码"
+// @Param pageSize query int true "每页数量"
+// @Param username query string false "用户名"
+// @Param nickname query string false "昵称"
+// @Param phone query string false "手机号"
+// @Param email query string false "邮箱"
+// @Success 200 {object} v1.GetAdminUsersResponse
+// @Router /v1/admin/users [get]
+func (h *AdminHandler) GetAdminUsers(ctx *gin.Context) {
+	var req v1.GetAdminUsersRequest
+	if err := ctx.ShouldBind(&req); err != nil {
+		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
+		return
+	}
+	data, err := h.adminService.GetAdminUsers(ctx, &req)
+	if err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+
+	v1.HandleSuccess(ctx, data)
+}
+
+// GetAdminUser godoc
+// @Summary 获取管理用户信息
+// @Schemes
+// @Description
+// @Tags 用户模块
+// @Accept json
+// @Produce json
+// @Security Bearer
+// @Success 200 {object} v1.GetAdminUserResponse
+// @Router /v1/admin/user [get]
+func (h *AdminHandler) GetAdminUser(ctx *gin.Context) {
+	data, err := h.adminService.GetAdminUser(ctx, GetUserIdFromCtx(ctx))
+	if err != nil {
+		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
+		return
+	}
+
+	v1.HandleSuccess(ctx, data)
+}

+ 2 - 2
internal/handler/handler.go

@@ -17,10 +17,10 @@ func NewHandler(
 		logger: logger,
 	}
 }
-func GetUserIdFromCtx(ctx *gin.Context) string {
+func GetUserIdFromCtx(ctx *gin.Context) uint {
 	v, exists := ctx.Get("claims")
 	if !exists {
-		return ""
+		return 0
 	}
 	return v.(*jwt.MyCustomClaims).UserId
 }

+ 6 - 109
internal/handler/user.go

@@ -2,10 +2,7 @@ package handler
 
 import (
 	"github.com/gin-gonic/gin"
-	"github.com/go-nunu/nunu-layout-advanced/api/v1"
 	"github.com/go-nunu/nunu-layout-advanced/internal/service"
-	"go.uber.org/zap"
-	"net/http"
 )
 
 type UserHandler struct {
@@ -13,116 +10,16 @@ type UserHandler struct {
 	userService service.UserService
 }
 
-func NewUserHandler(handler *Handler, userService service.UserService) *UserHandler {
+func NewUserHandler(
+	handler *Handler,
+	userService service.UserService,
+) *UserHandler {
 	return &UserHandler{
 		Handler:     handler,
 		userService: userService,
 	}
 }
 
-// Register godoc
-// @Summary 用户注册
-// @Schemes
-// @Description 目前只支持邮箱登录
-// @Tags 用户模块
-// @Accept json
-// @Produce json
-// @Param request body v1.RegisterRequest true "params"
-// @Success 200 {object} v1.Response
-// @Router /register [post]
-func (h *UserHandler) Register(ctx *gin.Context) {
-	req := new(v1.RegisterRequest)
-	if err := ctx.ShouldBindJSON(req); err != nil {
-		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
-		return
-	}
-
-	if err := h.userService.Register(ctx, req); err != nil {
-		h.logger.WithContext(ctx).Error("userService.Register error", zap.Error(err))
-		v1.HandleError(ctx, http.StatusInternalServerError, err, nil)
-		return
-	}
-
-	v1.HandleSuccess(ctx, nil)
-}
-
-// Login godoc
-// @Summary 账号登录
-// @Schemes
-// @Description
-// @Tags 用户模块
-// @Accept json
-// @Produce json
-// @Param request body v1.LoginRequest true "params"
-// @Success 200 {object} v1.LoginResponse
-// @Router /login [post]
-func (h *UserHandler) Login(ctx *gin.Context) {
-	var req v1.LoginRequest
-	if err := ctx.ShouldBindJSON(&req); err != nil {
-		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
-		return
-	}
-
-	token, err := h.userService.Login(ctx, &req)
-	if err != nil {
-		v1.HandleError(ctx, http.StatusUnauthorized, v1.ErrUnauthorized, nil)
-		return
-	}
-	v1.HandleSuccess(ctx, v1.LoginResponseData{
-		AccessToken: token,
-	})
-}
-
-// GetProfile godoc
-// @Summary 获取用户信息
-// @Schemes
-// @Description
-// @Tags 用户模块
-// @Accept json
-// @Produce json
-// @Security Bearer
-// @Success 200 {object} v1.GetProfileResponse
-// @Router /user [get]
-func (h *UserHandler) GetProfile(ctx *gin.Context) {
-	userId := GetUserIdFromCtx(ctx)
-	if userId == "" {
-		v1.HandleError(ctx, http.StatusUnauthorized, v1.ErrUnauthorized, nil)
-		return
-	}
-
-	user, err := h.userService.GetProfile(ctx, userId)
-	if err != nil {
-		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
-		return
-	}
-
-	v1.HandleSuccess(ctx, user)
-}
-
-// UpdateProfile godoc
-// @Summary 修改用户信息
-// @Schemes
-// @Description
-// @Tags 用户模块
-// @Accept json
-// @Produce json
-// @Security Bearer
-// @Param request body v1.UpdateProfileRequest true "params"
-// @Success 200 {object} v1.Response
-// @Router /user [put]
-func (h *UserHandler) UpdateProfile(ctx *gin.Context) {
-	userId := GetUserIdFromCtx(ctx)
+func (h *UserHandler) GetUsers(ctx *gin.Context) {
 
-	var req v1.UpdateProfileRequest
-	if err := ctx.ShouldBindJSON(&req); err != nil {
-		v1.HandleError(ctx, http.StatusBadRequest, v1.ErrBadRequest, nil)
-		return
-	}
-
-	if err := h.userService.UpdateProfile(ctx, userId, &req); err != nil {
-		v1.HandleError(ctx, http.StatusInternalServerError, v1.ErrInternalServerError, nil)
-		return
-	}
-
-	v1.HandleSuccess(ctx, nil)
-}
+}

+ 3 - 3
internal/middleware/jwt.go

@@ -2,7 +2,7 @@ package middleware
 
 import (
 	"github.com/gin-gonic/gin"
-	"github.com/go-nunu/nunu-layout-advanced/api/v1"
+	v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/jwt"
 	"github.com/go-nunu/nunu-layout-advanced/pkg/log"
 	"go.uber.org/zap"
@@ -67,6 +67,6 @@ func NoStrictAuth(j *jwt.JWT, logger *log.Logger) gin.HandlerFunc {
 
 func recoveryLoggerFunc(ctx *gin.Context, logger *log.Logger) {
 	if userInfo, ok := ctx.MustGet("claims").(*jwt.MyCustomClaims); ok {
-		logger.WithValue(ctx, zap.String("UserId", userInfo.UserId))
+		logger.WithValue(ctx, zap.Any("UserId", userInfo.UserId))
 	}
-}
+}

+ 327 - 0
internal/repository/admin.go

@@ -0,0 +1,327 @@
+package repository
+
+import (
+	"context"
+	"fmt"
+	"github.com/duke-git/lancet/v2/convertor"
+	v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
+	"github.com/go-nunu/nunu-layout-advanced/internal/model"
+	"go.uber.org/zap"
+	"strings"
+)
+
+type AdminRepository interface {
+	GetAdminUsers(ctx context.Context, req *v1.GetAdminUsersRequest) ([]model.AdminUser, int64, error)
+	GetAdminUser(ctx context.Context, uid uint) (model.AdminUser, error)
+	GetAdminUserByUsername(ctx context.Context, username string) (model.AdminUser, error)
+	AdminUserUpdate(ctx context.Context, m *model.AdminUser) error
+	AdminUserCreate(ctx context.Context, m *model.AdminUser) error
+	AdminUserDelete(ctx context.Context, id uint) error
+
+	GetUserPermissions(ctx context.Context, uid uint) ([][]string, error)
+	GetUserRoles(ctx context.Context, uid uint) ([]string, error)
+	GetRolePermissions(ctx context.Context, role string) ([][]string, error)
+	UpdateRolePermission(ctx context.Context, role string, permissions map[string]struct{}) error
+	UpdateUserRoles(ctx context.Context, uid uint, roles []string) error
+	DeleteUserRoles(ctx context.Context, uid uint) error
+
+	GetMenuList(ctx context.Context) ([]model.Menu, error)
+	MenuUpdate(ctx context.Context, m *model.Menu) error
+	MenuCreate(ctx context.Context, m *model.Menu) error
+	MenuDelete(ctx context.Context, id uint) error
+
+	GetRoles(ctx context.Context, req *v1.GetRoleListRequest) ([]model.Role, int64, error)
+	RoleUpdate(ctx context.Context, m *model.Role) error
+	RoleCreate(ctx context.Context, m *model.Role) error
+	RoleDelete(ctx context.Context, id uint) error
+	CasbinRoleDelete(ctx context.Context, role string) error
+	GetRole(ctx context.Context, id uint) (model.Role, error)
+	GetRoleBySid(ctx context.Context, sid string) (model.Role, error)
+
+	GetApis(ctx context.Context, req *v1.GetApisRequest) ([]model.Api, int64, error)
+	GetApiGroups(ctx context.Context) ([]string, error)
+	ApiUpdate(ctx context.Context, m *model.Api) error
+	ApiCreate(ctx context.Context, m *model.Api) error
+	ApiDelete(ctx context.Context, id uint) error
+}
+
+func NewAdminRepository(
+	repository *Repository,
+) AdminRepository {
+	return &adminRepository{
+		Repository: repository,
+	}
+}
+
+type adminRepository struct {
+	*Repository
+}
+
+func (r *adminRepository) CasbinRoleDelete(ctx context.Context, role string) error {
+	_, err := r.e.DeleteRole(role)
+	return err
+}
+
+func (r *adminRepository) GetRole(ctx context.Context, id uint) (model.Role, error) {
+	m := model.Role{}
+	return m, r.DB(ctx).Where("id = ?", id).First(&m).Error
+}
+func (r *adminRepository) GetRoleBySid(ctx context.Context, sid string) (model.Role, error) {
+	m := model.Role{}
+	return m, r.DB(ctx).Where("sid = ?", sid).First(&m).Error
+}
+
+func (r *adminRepository) DeleteUserRoles(ctx context.Context, uid uint) error {
+	_, err := r.e.DeleteRolesForUser(convertor.ToString(uid))
+	return err
+}
+func (r *adminRepository) UpdateUserRoles(ctx context.Context, uid uint, roles []string) error {
+	if len(roles) == 0 {
+		_, err := r.e.DeleteRolesForUser(convertor.ToString(uid))
+		return err
+	}
+	old, err := r.e.GetRolesForUser(convertor.ToString(uid))
+	if err != nil {
+		return err
+	}
+	oldMap := make(map[string]struct{})
+	newMap := make(map[string]struct{})
+	for _, v := range old {
+		oldMap[v] = struct{}{}
+	}
+	for _, v := range roles {
+		newMap[v] = struct{}{}
+	}
+	addRoles := make([]string, 0)
+	delRoles := make([]string, 0)
+
+	for key, _ := range oldMap {
+		if _, exists := newMap[key]; !exists {
+			delRoles = append(delRoles, key)
+		}
+	}
+	for key, _ := range newMap {
+		if _, exists := oldMap[key]; !exists {
+			addRoles = append(addRoles, key)
+		}
+	}
+	if len(addRoles) == 0 && len(delRoles) == 0 {
+		return nil
+	}
+	for _, role := range delRoles {
+		if _, err := r.e.DeleteRoleForUser(convertor.ToString(uid), role); err != nil {
+			r.logger.WithContext(ctx).Error("DeleteRoleForUser error", zap.Error(err))
+			return err
+		}
+	}
+
+	_, err = r.e.AddRolesForUser(convertor.ToString(uid), addRoles)
+	return err
+}
+
+func (r *adminRepository) GetAdminUserByUsername(ctx context.Context, username string) (model.AdminUser, error) {
+	m := model.AdminUser{}
+	return m, r.DB(ctx).Where("username = ?", username).First(&m).Error
+}
+
+func (r *adminRepository) GetAdminUsers(ctx context.Context, req *v1.GetAdminUsersRequest) ([]model.AdminUser, int64, error) {
+	var list []model.AdminUser
+	var total int64
+	scope := r.DB(ctx).Model(&model.AdminUser{})
+	if req.Username != "" {
+		scope = scope.Where("username LIKE ?", "%"+req.Username+"%")
+	}
+	if req.Nickname != "" {
+		scope = scope.Where("nickname LIKE ?", "%"+req.Nickname+"%")
+	}
+	if req.Email != "" {
+		scope = scope.Where("email LIKE ?", "%"+req.Email+"%")
+	}
+	if req.Phone != "" {
+		scope = scope.Where("phone LIKE ?", "%"+req.Phone+"%")
+	}
+	if err := scope.Count(&total).Error; err != nil {
+		return nil, total, err
+	}
+	if err := scope.Offset((req.Page - 1) * req.PageSize).Limit(req.PageSize).Order("id DESC").Find(&list).Error; err != nil {
+		return nil, total, err
+	}
+	return list, total, nil
+}
+
+func (r *adminRepository) GetAdminUser(ctx context.Context, uid uint) (model.AdminUser, error) {
+	m := model.AdminUser{}
+	return m, r.DB(ctx).Where("id = ?", uid).First(&m).Error
+}
+
+func (r *adminRepository) AdminUserUpdate(ctx context.Context, m *model.AdminUser) error {
+	return r.DB(ctx).Where("id = ?", m.ID).Save(m).Error
+}
+
+func (r *adminRepository) AdminUserCreate(ctx context.Context, m *model.AdminUser) error {
+	return r.DB(ctx).Create(m).Error
+}
+
+func (r *adminRepository) AdminUserDelete(ctx context.Context, id uint) error {
+	return r.DB(ctx).Where("id = ?", id).Delete(&model.AdminUser{}).Error
+}
+
+func (r *adminRepository) UpdateRolePermission(ctx context.Context, role string, newPermSet map[string]struct{}) error {
+	if len(newPermSet) == 0 {
+		return nil
+	}
+	// 获取当前角色的所有权限
+	oldPermissions, err := r.e.GetPermissionsForUser(role)
+	if err != nil {
+		return err
+	}
+
+	// 将旧权限转换为 map 方便查找
+	oldPermSet := make(map[string]struct{})
+	for _, perm := range oldPermissions {
+		if len(perm) == 3 {
+			oldPermSet[strings.Join([]string{perm[1], perm[2]}, model.PermSep)] = struct{}{}
+		}
+	}
+
+	// 找出需要删除的权限
+	var removePermissions [][]string
+	for key, _ := range oldPermSet {
+		if _, exists := newPermSet[key]; !exists {
+			removePermissions = append(removePermissions, strings.Split(key, model.PermSep))
+		}
+	}
+
+	// 找出需要添加的权限
+	var addPermissions [][]string
+	for key, _ := range newPermSet {
+		if _, exists := oldPermSet[key]; !exists {
+			addPermissions = append(addPermissions, strings.Split(key, model.PermSep))
+		}
+
+	}
+
+	// 先移除多余的权限(使用 DeletePermissionForUser 逐条删除)
+	for _, perm := range removePermissions {
+		_, err := r.e.DeletePermissionForUser(role, perm...)
+		if err != nil {
+			return fmt.Errorf("移除权限失败: %v", err)
+		}
+	}
+
+	// 再添加新的权限
+	if len(addPermissions) > 0 {
+		_, err = r.e.AddPermissionsForUser(role, addPermissions...)
+		if err != nil {
+			return fmt.Errorf("添加新权限失败: %v", err)
+		}
+	}
+
+	return nil
+}
+
+func (r *adminRepository) GetApiGroups(ctx context.Context) ([]string, error) {
+	res := make([]string, 0)
+	if err := r.DB(ctx).Model(&model.Api{}).Group("`group`").Pluck("`group`", &res).Error; err != nil {
+		return nil, err
+	}
+	return res, nil
+}
+
+func (r *adminRepository) GetApis(ctx context.Context, req *v1.GetApisRequest) ([]model.Api, int64, error) {
+	var list []model.Api
+	var total int64
+	scope := r.DB(ctx).Model(&model.Api{})
+	if req.Name != "" {
+		scope = scope.Where("name LIKE ?", "%"+req.Name+"%")
+	}
+	if req.Group != "" {
+		scope = scope.Where("`group` LIKE ?", "%"+req.Group+"%")
+	}
+	if req.Path != "" {
+		scope = scope.Where("path LIKE ?", "%"+req.Path+"%")
+	}
+	if req.Method != "" {
+		scope = scope.Where("method = ?", req.Method)
+	}
+	if err := scope.Count(&total).Error; err != nil {
+		return nil, total, err
+	}
+	if err := scope.Offset((req.Page - 1) * req.PageSize).Limit(req.PageSize).Order("`group` ASC").Find(&list).Error; err != nil {
+		return nil, total, err
+	}
+	return list, total, nil
+}
+
+func (r *adminRepository) ApiUpdate(ctx context.Context, m *model.Api) error {
+	return r.DB(ctx).Where("id = ?", m.ID).Save(m).Error
+}
+
+func (r *adminRepository) ApiCreate(ctx context.Context, m *model.Api) error {
+	return r.DB(ctx).Create(m).Error
+}
+
+func (r *adminRepository) ApiDelete(ctx context.Context, id uint) error {
+	return r.DB(ctx).Where("id = ?", id).Delete(&model.Api{}).Error
+}
+
+func (r *adminRepository) GetUserPermissions(ctx context.Context, uid uint) ([][]string, error) {
+	return r.e.GetImplicitPermissionsForUser(convertor.ToString(uid))
+
+}
+func (r *adminRepository) GetRolePermissions(ctx context.Context, role string) ([][]string, error) {
+	return r.e.GetPermissionsForUser(role)
+}
+func (r *adminRepository) GetUserRoles(ctx context.Context, uid uint) ([]string, error) {
+	return r.e.GetRolesForUser(convertor.ToString(uid))
+}
+func (r *adminRepository) MenuUpdate(ctx context.Context, m *model.Menu) error {
+	return r.DB(ctx).Where("id = ?", m.ID).Save(m).Error
+}
+
+func (r *adminRepository) MenuCreate(ctx context.Context, m *model.Menu) error {
+	return r.DB(ctx).Save(m).Error
+}
+
+func (r *adminRepository) MenuDelete(ctx context.Context, id uint) error {
+	return r.DB(ctx).Where("id = ?", id).Delete(&model.Menu{}).Error
+}
+
+func (r *adminRepository) GetMenuList(ctx context.Context) ([]model.Menu, error) {
+	var menuList []model.Menu
+	if err := r.DB(ctx).Order("weight DESC").Find(&menuList).Error; err != nil {
+		return nil, err
+	}
+	return menuList, nil
+}
+
+func (r *adminRepository) RoleUpdate(ctx context.Context, m *model.Role) error {
+	return r.DB(ctx).Where("id = ?", m.ID).UpdateColumn("name", m.Name).Error
+}
+
+func (r *adminRepository) RoleCreate(ctx context.Context, m *model.Role) error {
+	return r.DB(ctx).Create(m).Error
+}
+
+func (r *adminRepository) RoleDelete(ctx context.Context, id uint) error {
+	return r.DB(ctx).Where("id = ?", id).Delete(&model.Role{}).Error
+}
+
+func (r *adminRepository) GetRoles(ctx context.Context, req *v1.GetRoleListRequest) ([]model.Role, int64, error) {
+	var list []model.Role
+	var total int64
+	scope := r.DB(ctx).Model(&model.Role{})
+	if req.Name != "" {
+		scope = scope.Where("name LIKE ?", "%"+req.Name+"%")
+	}
+	if req.Sid != "" {
+		scope = scope.Where("sid = ?", req.Sid)
+	}
+	if err := scope.Count(&total).Error; err != nil {
+		return nil, total, err
+	}
+	if err := scope.Offset((req.Page - 1) * req.PageSize).Limit(req.PageSize).Find(&list).Error; err != nil {
+		return nil, total, err
+	}
+	return list, total, nil
+}

+ 5 - 41
internal/repository/user.go

@@ -2,24 +2,18 @@ package repository
 
 import (
 	"context"
-	"errors"
-	v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
 	"github.com/go-nunu/nunu-layout-advanced/internal/model"
-	"gorm.io/gorm"
 )
 
 type UserRepository interface {
-	Create(ctx context.Context, user *model.User) error
-	Update(ctx context.Context, user *model.User) error
-	GetByID(ctx context.Context, id string) (*model.User, error)
-	GetByEmail(ctx context.Context, email string) (*model.User, error)
+	GetUser(ctx context.Context, id int64) (*model.User, error)
 }
 
 func NewUserRepository(
-	r *Repository,
+	repository *Repository,
 ) UserRepository {
 	return &userRepository{
-		Repository: r,
+		Repository: repository,
 	}
 }
 
@@ -27,38 +21,8 @@ type userRepository struct {
 	*Repository
 }
 
-func (r *userRepository) Create(ctx context.Context, user *model.User) error {
-	if err := r.DB(ctx).Create(user).Error; err != nil {
-		return err
-	}
-	return nil
-}
-
-func (r *userRepository) Update(ctx context.Context, user *model.User) error {
-	if err := r.DB(ctx).Save(user).Error; err != nil {
-		return err
-	}
-	return nil
-}
-
-func (r *userRepository) GetByID(ctx context.Context, userId string) (*model.User, error) {
+func (r *userRepository) GetUser(ctx context.Context, id int64) (*model.User, error) {
 	var user model.User
-	if err := r.DB(ctx).Where("user_id = ?", userId).First(&user).Error; err != nil {
-		if errors.Is(err, gorm.ErrRecordNotFound) {
-			return nil, v1.ErrNotFound
-		}
-		return nil, err
-	}
-	return &user, nil
-}
 
-func (r *userRepository) GetByEmail(ctx context.Context, email string) (*model.User, error) {
-	var user model.User
-	if err := r.DB(ctx).Where("email = ?", email).First(&user).Error; err != nil {
-		if errors.Is(err, gorm.ErrRecordNotFound) {
-			return nil, nil
-		}
-		return nil, err
-	}
 	return &user, nil
-}
+}

+ 34 - 9
internal/server/http.go

@@ -33,6 +33,7 @@ func NewHTTPServer(
 	tcpLimitHandler *handler.TcpLimitHandler,
 	udpLimitHandler *handler.UdpLimitHandler,
 	globalLimitHandler *handler.GlobalLimitHandler,
+	adminHandler *handler.AdminHandler,
 
 ) *http.Server {
 	gin.SetMode(gin.DebugMode)
@@ -75,11 +76,11 @@ func NewHTTPServer(
 
 			// 登录API限流
 			loginConfig := limiterInstance.GetAPIConfig("login")
-			noAuthRouter.POST("/login", middleware.IPRateLimitMiddleware(loginConfig), userHandler.Login)
+			noAuthRouter.POST("/login", middleware.IPRateLimitMiddleware(loginConfig), adminHandler.Login)
 
-			// 注册API限流
-			registerConfig := limiterInstance.GetAPIConfig("register")
-			noAuthRouter.POST("/register", middleware.IPRateLimitMiddleware(registerConfig), userHandler.Register)
+			//// 注册API限流
+			//registerConfig := limiterInstance.GetAPIConfig("register")
+			//noAuthRouter.POST("/register", middleware.IPRateLimitMiddleware(registerConfig), userHandler.Register)
 
 			// 创建IP白名单实例
 			ipAllowlist := middleware.NewIPAllowlist(conf, logger)
@@ -126,15 +127,39 @@ func NewHTTPServer(
 			noAuthRouter.POST("/globalLimit/delete", ipAllowlistMiddleware, globalLimitHandler.DeleteGlobalLimit)
 		}
 		// Non-strict permission routing group
-		noStrictAuthRouter := v1.Group("/").Use(middleware.NoStrictAuth(jwt, logger))
-		{
-			noStrictAuthRouter.GET("/user", userHandler.GetProfile)
-		}
+		//noStrictAuthRouter := v1.Group("/").Use(middleware.NoStrictAuth(jwt, logger))
+		//{
+		//	noStrictAuthRouter.GET("/user", userHandler.GetProfile)
+		//}
 
 		// Strict permission routing group
 		strictAuthRouter := v1.Group("/").Use(middleware.StrictAuth(jwt, logger), middleware.AuthMiddleware(e))
 		{
-			strictAuthRouter.PUT("/user", userHandler.UpdateProfile)
+			strictAuthRouter.GET("/users", userHandler.GetUsers)
+
+			strictAuthRouter.GET("/menus", adminHandler.GetMenus)
+			strictAuthRouter.GET("/admin/menus", adminHandler.GetAdminMenus)
+			strictAuthRouter.POST("/admin/menu", adminHandler.MenuCreate)
+			strictAuthRouter.PUT("/admin/menu", adminHandler.MenuUpdate)
+			strictAuthRouter.DELETE("/admin/menu", adminHandler.MenuDelete)
+
+			strictAuthRouter.GET("/admin/users", adminHandler.GetAdminUsers)
+			strictAuthRouter.GET("/admin/user", adminHandler.GetAdminUser)
+			strictAuthRouter.PUT("/admin/user", adminHandler.AdminUserUpdate)
+			strictAuthRouter.POST("/admin/user", adminHandler.AdminUserCreate)
+			strictAuthRouter.DELETE("/admin/user", adminHandler.AdminUserDelete)
+			strictAuthRouter.GET("/admin/user/permissions", adminHandler.GetUserPermissions)
+			strictAuthRouter.GET("/admin/role/permissions", adminHandler.GetRolePermissions)
+			strictAuthRouter.PUT("/admin/role/permission", adminHandler.UpdateRolePermission)
+			strictAuthRouter.GET("/admin/roles", adminHandler.GetRoles)
+			strictAuthRouter.POST("/admin/role", adminHandler.RoleCreate)
+			strictAuthRouter.PUT("/admin/role", adminHandler.RoleUpdate)
+			strictAuthRouter.DELETE("/admin/role", adminHandler.RoleDelete)
+
+			strictAuthRouter.GET("/admin/apis", adminHandler.GetApis)
+			strictAuthRouter.POST("/admin/api", adminHandler.ApiCreate)
+			strictAuthRouter.PUT("/admin/api", adminHandler.ApiUpdate)
+			strictAuthRouter.DELETE("/admin/api", adminHandler.ApiDelete)
 		}
 	}
 

+ 466 - 0
internal/service/admin.go

@@ -0,0 +1,466 @@
+package service
+
+import (
+	"context"
+	"errors"
+	"github.com/duke-git/lancet/v2/convertor"
+	v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
+	"github.com/go-nunu/nunu-layout-advanced/internal/model"
+	"github.com/go-nunu/nunu-layout-advanced/internal/repository"
+	"go.uber.org/zap"
+	"golang.org/x/crypto/bcrypt"
+	"gorm.io/gorm"
+
+	"strings"
+	"time"
+)
+
+type AdminService interface {
+	Login(ctx context.Context, req *v1.LoginRequest) (string, error)
+	GetAdminUsers(ctx context.Context, req *v1.GetAdminUsersRequest) (*v1.GetAdminUsersResponseData, error)
+	GetAdminUser(ctx context.Context, uid uint) (*v1.GetAdminUserResponseData, error)
+	AdminUserUpdate(ctx context.Context, req *v1.AdminUserUpdateRequest) error
+	AdminUserCreate(ctx context.Context, req *v1.AdminUserCreateRequest) error
+	AdminUserDelete(ctx context.Context, id uint) error
+
+	GetUserPermissions(ctx context.Context, uid uint) (*v1.GetUserPermissionsData, error)
+	GetRolePermissions(ctx context.Context, role string) (*v1.GetRolePermissionsData, error)
+	UpdateRolePermission(ctx context.Context, req *v1.UpdateRolePermissionRequest) error
+
+	GetAdminMenus(ctx context.Context) (*v1.GetMenuResponseData, error)
+	GetMenus(ctx context.Context, uid uint) (*v1.GetMenuResponseData, error)
+	MenuUpdate(ctx context.Context, req *v1.MenuUpdateRequest) error
+	MenuCreate(ctx context.Context, req *v1.MenuCreateRequest) error
+	MenuDelete(ctx context.Context, id uint) error
+
+	GetRoles(ctx context.Context, req *v1.GetRoleListRequest) (*v1.GetRolesResponseData, error)
+	RoleUpdate(ctx context.Context, req *v1.RoleUpdateRequest) error
+	RoleCreate(ctx context.Context, req *v1.RoleCreateRequest) error
+	RoleDelete(ctx context.Context, id uint) error
+
+	GetApis(ctx context.Context, req *v1.GetApisRequest) (*v1.GetApisResponseData, error)
+	ApiUpdate(ctx context.Context, req *v1.ApiUpdateRequest) error
+	ApiCreate(ctx context.Context, req *v1.ApiCreateRequest) error
+	ApiDelete(ctx context.Context, id uint) error
+}
+
+func NewAdminService(
+	service *Service,
+	adminRepository repository.AdminRepository,
+) AdminService {
+	return &adminService{
+		Service:         service,
+		adminRepository: adminRepository,
+	}
+}
+
+type adminService struct {
+	*Service
+	adminRepository repository.AdminRepository
+}
+
+func (s *adminService) GetAdminUser(ctx context.Context, uid uint) (*v1.GetAdminUserResponseData, error) {
+	user, err := s.adminRepository.GetAdminUser(ctx, uid)
+	if err != nil {
+		return nil, err
+	}
+	roles, _ := s.adminRepository.GetUserRoles(ctx, uid)
+
+	return &v1.GetAdminUserResponseData{
+		Email:     user.Email,
+		ID:        user.ID,
+		Username:  user.Username,
+		Nickname:  user.Nickname,
+		Phone:     user.Phone,
+		Roles:     roles,
+		CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"),
+		UpdatedAt: user.UpdatedAt.Format("2006-01-02 15:04:05"),
+	}, nil
+}
+
+func (s *adminService) Login(ctx context.Context, req *v1.LoginRequest) (string, error) {
+	user, err := s.adminRepository.GetAdminUserByUsername(ctx, req.Username)
+	if err != nil {
+		if err == gorm.ErrRecordNotFound {
+			return "", v1.ErrUnauthorized
+		}
+		return "", v1.ErrInternalServerError
+	}
+
+	err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
+	if err != nil {
+		return "", err
+	}
+	token, err := s.jwt.GenToken(user.ID, time.Now().Add(time.Hour*24*90))
+	if err != nil {
+		return "", err
+	}
+
+	return token, nil
+}
+
+func (s *adminService) GetAdminUsers(ctx context.Context, req *v1.GetAdminUsersRequest) (*v1.GetAdminUsersResponseData, error) {
+	list, total, err := s.adminRepository.GetAdminUsers(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	data := &v1.GetAdminUsersResponseData{
+		List:  make([]v1.AdminUserDataItem, 0),
+		Total: total,
+	}
+	for _, user := range list {
+		roles, err := s.adminRepository.GetUserRoles(ctx, user.ID)
+		if err != nil {
+			s.logger.Error("GetUserRoles error", zap.Error(err))
+			continue
+		}
+		data.List = append(data.List, v1.AdminUserDataItem{
+			Email:     user.Email,
+			ID:        user.ID,
+			Nickname:  user.Nickname,
+			Username:  user.Username,
+			Phone:     user.Phone,
+			Roles:     roles,
+			CreatedAt: user.CreatedAt.Format("2006-01-02 15:04:05"),
+			UpdatedAt: user.UpdatedAt.Format("2006-01-02 15:04:05"),
+		})
+	}
+	return data, nil
+}
+
+func (s *adminService) AdminUserUpdate(ctx context.Context, req *v1.AdminUserUpdateRequest) error {
+	old, _ := s.adminRepository.GetAdminUser(ctx, req.ID)
+	if req.Password != "" {
+		hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
+		if err != nil {
+			return err
+		}
+		req.Password = string(hash)
+	} else {
+		req.Password = old.Password
+	}
+	err := s.adminRepository.UpdateUserRoles(ctx, req.ID, req.Roles)
+	if err != nil {
+		return err
+	}
+	return s.adminRepository.AdminUserUpdate(ctx, &model.AdminUser{
+		Model: gorm.Model{
+			ID: req.ID,
+		},
+		Email:    req.Email,
+		Nickname: req.Nickname,
+		Phone:    req.Phone,
+		Username: req.Username,
+	})
+
+}
+
+func (s *adminService) AdminUserCreate(ctx context.Context, req *v1.AdminUserCreateRequest) error {
+	hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
+	if err != nil {
+		return err
+	}
+	req.Password = string(hash)
+	err = s.adminRepository.AdminUserCreate(ctx, &model.AdminUser{
+		Email:    req.Email,
+		Nickname: req.Nickname,
+		Phone:    req.Phone,
+		Username: req.Username,
+		Password: req.Password,
+	})
+	if err != nil {
+		return err
+	}
+	user, err := s.adminRepository.GetAdminUserByUsername(ctx, req.Username)
+	if err != nil {
+		return err
+	}
+	err = s.adminRepository.UpdateUserRoles(ctx, user.ID, req.Roles)
+	if err != nil {
+		return err
+	}
+	return err
+
+}
+
+func (s *adminService) AdminUserDelete(ctx context.Context, id uint) error {
+	// 删除用户角色
+	err := s.adminRepository.DeleteUserRoles(ctx, id)
+	if err != nil {
+		return err
+	}
+	return s.adminRepository.AdminUserDelete(ctx, id)
+}
+
+func (s *adminService) UpdateRolePermission(ctx context.Context, req *v1.UpdateRolePermissionRequest) error {
+	permissions := map[string]struct{}{}
+	for _, v := range req.List {
+		perm := strings.Split(v, model.PermSep)
+		if len(perm) == 2 {
+			permissions[v] = struct{}{}
+		}
+
+	}
+	return s.adminRepository.UpdateRolePermission(ctx, req.Role, permissions)
+}
+
+func (s *adminService) GetApis(ctx context.Context, req *v1.GetApisRequest) (*v1.GetApisResponseData, error) {
+	list, total, err := s.adminRepository.GetApis(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	groups, err := s.adminRepository.GetApiGroups(ctx)
+	if err != nil {
+		return nil, err
+	}
+	data := &v1.GetApisResponseData{
+		List:   make([]v1.ApiDataItem, 0),
+		Total:  total,
+		Groups: groups,
+	}
+	for _, api := range list {
+		data.List = append(data.List, v1.ApiDataItem{
+			CreatedAt: api.CreatedAt.Format("2006-01-02 15:04:05"),
+			Group:     api.Group,
+			ID:        api.ID,
+			Method:    api.Method,
+			Name:      api.Name,
+			Path:      api.Path,
+			UpdatedAt: api.UpdatedAt.Format("2006-01-02 15:04:05"),
+		})
+	}
+	return data, nil
+}
+
+func (s *adminService) ApiUpdate(ctx context.Context, req *v1.ApiUpdateRequest) error {
+	return s.adminRepository.ApiUpdate(ctx, &model.Api{
+		Group:  req.Group,
+		Method: req.Method,
+		Name:   req.Name,
+		Path:   req.Path,
+		Model: gorm.Model{
+			ID: req.ID,
+		},
+	})
+}
+
+func (s *adminService) ApiCreate(ctx context.Context, req *v1.ApiCreateRequest) error {
+	return s.adminRepository.ApiCreate(ctx, &model.Api{
+		Group:  req.Group,
+		Method: req.Method,
+		Name:   req.Name,
+		Path:   req.Path,
+	})
+}
+
+func (s *adminService) ApiDelete(ctx context.Context, id uint) error {
+	return s.adminRepository.ApiDelete(ctx, id)
+}
+
+func (s *adminService) GetUserPermissions(ctx context.Context, uid uint) (*v1.GetUserPermissionsData, error) {
+	data := &v1.GetUserPermissionsData{
+		List: []string{},
+	}
+	list, err := s.adminRepository.GetUserPermissions(ctx, uid)
+	if err != nil {
+		return nil, err
+	}
+	for _, v := range list {
+		if len(v) == 3 {
+			data.List = append(data.List, strings.Join([]string{v[1], v[2]}, model.PermSep))
+		}
+	}
+	return data, nil
+}
+func (s *adminService) GetRolePermissions(ctx context.Context, role string) (*v1.GetRolePermissionsData, error) {
+	data := &v1.GetRolePermissionsData{
+		List: []string{},
+	}
+	list, err := s.adminRepository.GetRolePermissions(ctx, role)
+	if err != nil {
+		return nil, err
+	}
+	for _, v := range list {
+		if len(v) == 3 {
+			data.List = append(data.List, strings.Join([]string{v[1], v[2]}, model.PermSep))
+		}
+	}
+	return data, nil
+}
+
+func (s *adminService) MenuUpdate(ctx context.Context, req *v1.MenuUpdateRequest) error {
+	return s.adminRepository.MenuUpdate(ctx, &model.Menu{
+		Component:  req.Component,
+		Icon:       req.Icon,
+		KeepAlive:  req.KeepAlive,
+		HideInMenu: req.HideInMenu,
+		Locale:     req.Locale,
+		Weight:     req.Weight,
+		Name:       req.Name,
+		ParentID:   req.ParentID,
+		Path:       req.Path,
+		Redirect:   req.Redirect,
+		Title:      req.Title,
+		URL:        req.URL,
+		Model: gorm.Model{
+			ID: req.ID,
+		},
+	})
+}
+
+func (s *adminService) MenuCreate(ctx context.Context, req *v1.MenuCreateRequest) error {
+	return s.adminRepository.MenuCreate(ctx, &model.Menu{
+		Component:  req.Component,
+		Icon:       req.Icon,
+		KeepAlive:  req.KeepAlive,
+		HideInMenu: req.HideInMenu,
+		Locale:     req.Locale,
+		Weight:     req.Weight,
+		Name:       req.Name,
+		ParentID:   req.ParentID,
+		Path:       req.Path,
+		Redirect:   req.Redirect,
+		Title:      req.Title,
+		URL:        req.URL,
+	})
+}
+
+func (s *adminService) MenuDelete(ctx context.Context, id uint) error {
+	return s.adminRepository.MenuDelete(ctx, id)
+}
+
+func (s *adminService) GetMenus(ctx context.Context, uid uint) (*v1.GetMenuResponseData, error) {
+	menuList, err := s.adminRepository.GetMenuList(ctx)
+	if err != nil {
+		s.logger.WithContext(ctx).Error("GetMenuList error", zap.Error(err))
+		return nil, err
+	}
+	data := &v1.GetMenuResponseData{
+		List: make([]v1.MenuDataItem, 0),
+	}
+	// 获取权限的菜单
+	permissions, err := s.adminRepository.GetUserPermissions(ctx, uid)
+	if err != nil {
+		return nil, err
+	}
+	menuPermMap := map[string]struct{}{}
+	for _, permission := range permissions {
+		// 防呆设置,超管可以看到所有菜单
+		if convertor.ToString(uid) == model.AdminUserID {
+			menuPermMap[strings.TrimPrefix(permission[1], model.MenuResourcePrefix)] = struct{}{}
+		} else {
+			if len(permission) == 3 && strings.HasPrefix(permission[1], model.MenuResourcePrefix) {
+				menuPermMap[strings.TrimPrefix(permission[1], model.MenuResourcePrefix)] = struct{}{}
+			}
+		}
+	}
+
+	for _, menu := range menuList {
+		if _, ok := menuPermMap[menu.Path]; ok {
+			data.List = append(data.List, v1.MenuDataItem{
+				ID:         menu.ID,
+				Name:       menu.Name,
+				Title:      menu.Title,
+				Path:       menu.Path,
+				Component:  menu.Component,
+				Redirect:   menu.Redirect,
+				KeepAlive:  menu.KeepAlive,
+				HideInMenu: menu.HideInMenu,
+				Locale:     menu.Locale,
+				Weight:     menu.Weight,
+				Icon:       menu.Icon,
+				ParentID:   menu.ParentID,
+				UpdatedAt:  menu.UpdatedAt.Format("2006-01-02 15:04:05"),
+				URL:        menu.URL,
+			})
+		}
+	}
+	return data, nil
+}
+func (s *adminService) GetAdminMenus(ctx context.Context) (*v1.GetMenuResponseData, error) {
+	menuList, err := s.adminRepository.GetMenuList(ctx)
+	if err != nil {
+		s.logger.WithContext(ctx).Error("GetMenuList error", zap.Error(err))
+		return nil, err
+	}
+	data := &v1.GetMenuResponseData{
+		List: make([]v1.MenuDataItem, 0),
+	}
+	for _, menu := range menuList {
+		data.List = append(data.List, v1.MenuDataItem{
+			ID:         menu.ID,
+			Name:       menu.Name,
+			Title:      menu.Title,
+			Path:       menu.Path,
+			Component:  menu.Component,
+			Redirect:   menu.Redirect,
+			KeepAlive:  menu.KeepAlive,
+			HideInMenu: menu.HideInMenu,
+			Locale:     menu.Locale,
+			Weight:     menu.Weight,
+			Icon:       menu.Icon,
+			ParentID:   menu.ParentID,
+			UpdatedAt:  menu.UpdatedAt.Format("2006-01-02 15:04:05"),
+			URL:        menu.URL,
+		})
+	}
+	return data, nil
+}
+
+func (s *adminService) RoleUpdate(ctx context.Context, req *v1.RoleUpdateRequest) error {
+	return s.adminRepository.RoleUpdate(ctx, &model.Role{
+		Name: req.Name,
+		Sid:  req.Sid,
+		Model: gorm.Model{
+			ID: req.ID,
+		},
+	})
+}
+
+func (s *adminService) RoleCreate(ctx context.Context, req *v1.RoleCreateRequest) error {
+	_, err := s.adminRepository.GetRoleBySid(ctx, req.Sid)
+	if err != nil {
+		if errors.Is(err, gorm.ErrRecordNotFound) {
+			return s.adminRepository.RoleCreate(ctx, &model.Role{
+				Name: req.Name,
+				Sid:  req.Sid,
+			})
+		} else {
+			return err
+		}
+	}
+	return nil
+}
+
+func (s *adminService) RoleDelete(ctx context.Context, id uint) error {
+	old, err := s.adminRepository.GetRole(ctx, id)
+	if err != nil {
+		return err
+	}
+	if err := s.adminRepository.CasbinRoleDelete(ctx, old.Sid); err != nil {
+		return err
+	}
+	return s.adminRepository.RoleDelete(ctx, id)
+}
+
+func (s *adminService) GetRoles(ctx context.Context, req *v1.GetRoleListRequest) (*v1.GetRolesResponseData, error) {
+	list, total, err := s.adminRepository.GetRoles(ctx, req)
+	if err != nil {
+		return nil, err
+	}
+	data := &v1.GetRolesResponseData{
+		List:  make([]v1.RoleDataItem, 0),
+		Total: total,
+	}
+	for _, role := range list {
+		data.List = append(data.List, v1.RoleDataItem{
+			ID:        role.ID,
+			Name:      role.Name,
+			Sid:       role.Sid,
+			UpdatedAt: role.UpdatedAt.Format("2006-01-02 15:04:05"),
+			CreatedAt: role.CreatedAt.Format("2006-01-02 15:04:05"),
+		})
+
+	}
+	return data, nil
+}

+ 8 - 92
internal/service/user.go

@@ -2,113 +2,29 @@ package service
 
 import (
 	"context"
-	v1 "github.com/go-nunu/nunu-layout-advanced/api/v1"
 	"github.com/go-nunu/nunu-layout-advanced/internal/model"
 	"github.com/go-nunu/nunu-layout-advanced/internal/repository"
-	"golang.org/x/crypto/bcrypt"
-	"time"
 )
 
 type UserService interface {
-	Register(ctx context.Context, req *v1.RegisterRequest) error
-	Login(ctx context.Context, req *v1.LoginRequest) (string, error)
-	GetProfile(ctx context.Context, userId string) (*v1.GetProfileResponseData, error)
-	UpdateProfile(ctx context.Context, userId string, req *v1.UpdateProfileRequest) error
+	GetUser(ctx context.Context, id int64) (*model.User, error)
 }
 
 func NewUserService(
 	service *Service,
-	userRepo repository.UserRepository,
+	userRepository repository.UserRepository,
 ) UserService {
 	return &userService{
-		userRepo: userRepo,
-		Service:  service,
+		Service:        service,
+		userRepository: userRepository,
 	}
 }
 
 type userService struct {
-	userRepo repository.UserRepository
 	*Service
+	userRepository repository.UserRepository
 }
 
-func (s *userService) Register(ctx context.Context, req *v1.RegisterRequest) error {
-	// check username
-	user, err := s.userRepo.GetByEmail(ctx, req.Email)
-	if err != nil {
-		return v1.ErrInternalServerError
-	}
-	if err == nil && user != nil {
-		return v1.ErrEmailAlreadyUse
-	}
-
-	hashedPassword, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost)
-	if err != nil {
-		return err
-	}
-	// Generate user ID
-	userId, err := s.sid.GenString()
-	if err != nil {
-		return err
-	}
-	user = &model.User{
-		UserId:   userId,
-		Email:    req.Email,
-		Password: string(hashedPassword),
-	}
-	// Transaction demo
-	err = s.tm.Transaction(ctx, func(ctx context.Context) error {
-		// Create a user
-		if err = s.userRepo.Create(ctx, user); err != nil {
-			return err
-		}
-		// TODO: other repo
-		return nil
-	})
-	return err
-}
-
-func (s *userService) Login(ctx context.Context, req *v1.LoginRequest) (string, error) {
-	user, err := s.userRepo.GetByEmail(ctx, req.Email)
-	if err != nil || user == nil {
-		return "", v1.ErrUnauthorized
-	}
-
-	err = bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(req.Password))
-	if err != nil {
-		return "", err
-	}
-	token, err := s.jwt.GenToken(user.UserId, time.Now().Add(time.Hour*24*90))
-	if err != nil {
-		return "", err
-	}
-
-	return token, nil
-}
-
-func (s *userService) GetProfile(ctx context.Context, userId string) (*v1.GetProfileResponseData, error) {
-	user, err := s.userRepo.GetByID(ctx, userId)
-	if err != nil {
-		return nil, err
-	}
-
-	return &v1.GetProfileResponseData{
-		UserId:   user.UserId,
-		Nickname: user.Nickname,
-	}, nil
-}
-
-func (s *userService) UpdateProfile(ctx context.Context, userId string, req *v1.UpdateProfileRequest) error {
-	user, err := s.userRepo.GetByID(ctx, userId)
-	if err != nil {
-		return err
-	}
-
-	user.Email = req.Email
-	user.Nickname = req.Nickname
-
-	if err = s.userRepo.Update(ctx, user); err != nil {
-		return err
-	}
-
-	return nil
-}
+func (s *userService) GetUser(ctx context.Context, id int64) (*model.User, error) {
+	return s.userRepository.GetUser(ctx, id)
+}

+ 3 - 3
pkg/jwt/jwt.go

@@ -14,7 +14,7 @@ type JWT struct {
 }
 
 type MyCustomClaims struct {
-	UserId string
+	UserId uint
 	jwt.RegisteredClaims
 }
 
@@ -22,7 +22,7 @@ func NewJwt(conf *viper.Viper) *JWT {
 	return &JWT{key: []byte(conf.GetString("security.jwt.key"))}
 }
 
-func (j *JWT) GenToken(userId string, expiresAt time.Time) (string, error) {
+func (j *JWT) GenToken(userId uint, expiresAt time.Time) (string, error) {
 	token := jwt.NewWithClaims(jwt.SigningMethodHS256, MyCustomClaims{
 		UserId: userId,
 		RegisteredClaims: jwt.RegisteredClaims{
@@ -60,4 +60,4 @@ func (j *JWT) ParseToken(tokenString string) (*MyCustomClaims, error) {
 	} else {
 		return nil, err
 	}
-}
+}