601 lines
16 KiB
Go
601 lines
16 KiB
Go
package system
|
||
|
||
import (
|
||
"context"
|
||
"encoding/json"
|
||
"fmt"
|
||
|
||
"kra/internal/biz/system"
|
||
"kra/internal/server/middleware"
|
||
"kra/pkg/jwt"
|
||
|
||
"github.com/go-kratos/kratos/v2/errors"
|
||
"github.com/go-kratos/kratos/v2/transport/http"
|
||
)
|
||
|
||
// UserService 用户服务
|
||
type UserService struct {
|
||
uc *system.UserUsecase
|
||
jwtPkg *jwt.JWT
|
||
}
|
||
|
||
// NewUserService 创建用户服务
|
||
func NewUserService(uc *system.UserUsecase, jwtPkg *jwt.JWT) *UserService {
|
||
return &UserService{uc: uc, jwtPkg: jwtPkg}
|
||
}
|
||
|
||
// LoginRequest 登录请求
|
||
type LoginRequest struct {
|
||
Username string `json:"username"`
|
||
Password string `json:"password"`
|
||
Captcha string `json:"captcha"`
|
||
CaptchaId string `json:"captchaId"`
|
||
}
|
||
|
||
// LoginResponse 登录响应
|
||
type LoginResponse struct {
|
||
User *UserInfo `json:"user"`
|
||
Token string `json:"token"`
|
||
ExpiresAt int64 `json:"expiresAt"`
|
||
}
|
||
|
||
// UserInfo 用户信息
|
||
type UserInfo struct {
|
||
ID uint `json:"id"`
|
||
UUID string `json:"uuid"`
|
||
Username string `json:"username"`
|
||
NickName string `json:"nickName"`
|
||
SideMode string `json:"sideMode"`
|
||
HeaderImg string `json:"headerImg"`
|
||
BaseColor string `json:"baseColor"`
|
||
AuthorityId uint `json:"authorityId"`
|
||
Phone string `json:"phone"`
|
||
Email string `json:"email"`
|
||
Enable int `json:"enable"`
|
||
OriginSetting json.RawMessage `json:"originSetting,omitempty"`
|
||
Authority *AuthorityInfo `json:"authority,omitempty"`
|
||
Authorities []*AuthorityInfo `json:"authorities,omitempty"`
|
||
}
|
||
|
||
// AuthorityInfo 角色信息
|
||
type AuthorityInfo struct {
|
||
AuthorityId uint `json:"authorityId"`
|
||
AuthorityName string `json:"authorityName"`
|
||
ParentId *uint `json:"parentId,omitempty"`
|
||
DefaultRouter string `json:"defaultRouter"`
|
||
}
|
||
|
||
// Login 用户登录
|
||
func (s *UserService) Login(ctx context.Context, req *LoginRequest) (*LoginResponse, error) {
|
||
user, err := s.uc.Login(ctx, req.Username, req.Password)
|
||
if err != nil {
|
||
return nil, errors.Unauthorized("LOGIN_FAILED", err.Error())
|
||
}
|
||
|
||
if user.Enable != 1 {
|
||
return nil, errors.Forbidden("USER_DISABLED", "用户被禁止登录")
|
||
}
|
||
|
||
// 生成 JWT token
|
||
claims := s.jwtPkg.CreateClaims(jwt.BaseClaims{
|
||
UUID: user.UUID.String(),
|
||
ID: uint(user.ID),
|
||
Username: user.Username,
|
||
NickName: user.NickName,
|
||
AuthorityID: user.AuthorityId,
|
||
})
|
||
token, err := s.jwtPkg.CreateToken(claims.BaseClaims)
|
||
if err != nil {
|
||
return nil, errors.InternalServer("TOKEN_ERROR", "生成token失败")
|
||
}
|
||
|
||
return &LoginResponse{
|
||
User: toUserInfo(user),
|
||
Token: token,
|
||
ExpiresAt: claims.ExpiresAt.UnixMilli(),
|
||
}, nil
|
||
}
|
||
|
||
// RegisterRequest 注册请求
|
||
type RegisterRequest struct {
|
||
Username string `json:"username"`
|
||
Password string `json:"password"`
|
||
NickName string `json:"nickName"`
|
||
HeaderImg string `json:"headerImg"`
|
||
AuthorityId uint `json:"authorityId"`
|
||
AuthorityIds []uint `json:"authorityIds"`
|
||
Phone string `json:"phone"`
|
||
Email string `json:"email"`
|
||
Enable int `json:"enable"`
|
||
}
|
||
|
||
// Register 用户注册
|
||
func (s *UserService) Register(ctx context.Context, req *RegisterRequest) (*UserInfo, error) {
|
||
user := &system.User{
|
||
Username: req.Username,
|
||
Password: req.Password,
|
||
NickName: req.NickName,
|
||
HeaderImg: req.HeaderImg,
|
||
AuthorityId: req.AuthorityId,
|
||
Phone: req.Phone,
|
||
Email: req.Email,
|
||
Enable: req.Enable,
|
||
}
|
||
|
||
created, err := s.uc.Register(ctx, user)
|
||
if err != nil {
|
||
return nil, errors.BadRequest("REGISTER_FAILED", err.Error())
|
||
}
|
||
|
||
return toUserInfo(created), nil
|
||
}
|
||
|
||
// ChangePasswordRequest 修改密码请求
|
||
type ChangePasswordRequest struct {
|
||
Password string `json:"password"`
|
||
NewPassword string `json:"newPassword"`
|
||
}
|
||
|
||
// ChangePassword 修改密码
|
||
func (s *UserService) ChangePassword(ctx context.Context, userID uint, req *ChangePasswordRequest) error {
|
||
return s.uc.ChangePassword(ctx, userID, req.Password, req.NewPassword)
|
||
}
|
||
|
||
// ResetPasswordRequest 重置密码请求
|
||
type ResetPasswordRequest struct {
|
||
ID uint `json:"id"`
|
||
Password string `json:"password"`
|
||
}
|
||
|
||
// ResetPassword 重置密码
|
||
func (s *UserService) ResetPassword(ctx context.Context, req *ResetPasswordRequest) error {
|
||
return s.uc.ResetPassword(ctx, req.ID, req.Password)
|
||
}
|
||
|
||
// GetUserInfo 获取用户信息
|
||
func (s *UserService) GetUserInfo(ctx context.Context, uuid string) (*UserInfo, error) {
|
||
user, err := s.uc.GetUserInfo(ctx, uuid)
|
||
if err != nil {
|
||
return nil, errors.NotFound("USER_NOT_FOUND", err.Error())
|
||
}
|
||
return toUserInfo(user), nil
|
||
}
|
||
|
||
// GetUserListRequest 获取用户列表请求
|
||
type GetUserListRequest struct {
|
||
Page int `json:"page"`
|
||
PageSize int `json:"pageSize"`
|
||
Username string `json:"username"`
|
||
NickName string `json:"nickName"`
|
||
Phone string `json:"phone"`
|
||
Email string `json:"email"`
|
||
}
|
||
|
||
// GetUserListResponse 获取用户列表响应
|
||
type GetUserListResponse struct {
|
||
List []*UserInfo `json:"list"`
|
||
Total int64 `json:"total"`
|
||
Page int `json:"page"`
|
||
PageSize int `json:"pageSize"`
|
||
}
|
||
|
||
// GetUserList 获取用户列表
|
||
func (s *UserService) GetUserList(ctx context.Context, req *GetUserListRequest) (*GetUserListResponse, error) {
|
||
filters := make(map[string]string)
|
||
if req.Username != "" {
|
||
filters["username"] = req.Username
|
||
}
|
||
if req.NickName != "" {
|
||
filters["nick_name"] = req.NickName
|
||
}
|
||
if req.Phone != "" {
|
||
filters["phone"] = req.Phone
|
||
}
|
||
if req.Email != "" {
|
||
filters["email"] = req.Email
|
||
}
|
||
|
||
users, total, err := s.uc.GetUserList(ctx, req.Page, req.PageSize, filters)
|
||
if err != nil {
|
||
return nil, errors.InternalServer("LIST_ERROR", err.Error())
|
||
}
|
||
|
||
list := make([]*UserInfo, len(users))
|
||
for i, u := range users {
|
||
list[i] = toUserInfo(u)
|
||
}
|
||
|
||
return &GetUserListResponse{
|
||
List: list,
|
||
Total: total,
|
||
Page: req.Page,
|
||
PageSize: req.PageSize,
|
||
}, nil
|
||
}
|
||
|
||
// SetUserInfoRequest 设置用户信息请求(管理员用)
|
||
type SetUserInfoRequest struct {
|
||
ID uint `json:"id"`
|
||
NickName string `json:"nickName"`
|
||
HeaderImg string `json:"headerImg"`
|
||
Phone string `json:"phone"`
|
||
Email string `json:"email"`
|
||
Enable int `json:"enable"`
|
||
AuthorityIds []uint `json:"authorityIds"`
|
||
}
|
||
|
||
// SetUserInfo 设置用户信息
|
||
func (s *UserService) SetUserInfo(ctx context.Context, adminAuthorityID uint, req *SetUserInfoRequest) error {
|
||
// 如果提供了AuthorityIds,先设置用户角色(与GVA保持一致)
|
||
if len(req.AuthorityIds) > 0 {
|
||
if err := s.uc.SetUserAuthorities(ctx, adminAuthorityID, req.ID, req.AuthorityIds); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
|
||
user := &system.User{
|
||
ID: req.ID,
|
||
NickName: req.NickName,
|
||
HeaderImg: req.HeaderImg,
|
||
Phone: req.Phone,
|
||
Email: req.Email,
|
||
Enable: req.Enable,
|
||
}
|
||
return s.uc.SetUserInfo(ctx, user)
|
||
}
|
||
|
||
// SetSelfInfoRequest 设置自己信息请求(用户用)
|
||
type SetSelfInfoRequest struct {
|
||
NickName string `json:"nickName"`
|
||
HeaderImg string `json:"headerImg"`
|
||
Phone string `json:"phone"`
|
||
Email string `json:"email"`
|
||
SideMode string `json:"sideMode"`
|
||
BaseColor string `json:"baseColor"`
|
||
}
|
||
|
||
// SetSelfInfo 设置自己的信息
|
||
func (s *UserService) SetSelfInfo(ctx context.Context, userID uint, req *SetSelfInfoRequest) error {
|
||
user := &system.User{
|
||
ID: userID,
|
||
NickName: req.NickName,
|
||
HeaderImg: req.HeaderImg,
|
||
Phone: req.Phone,
|
||
Email: req.Email,
|
||
SideMode: req.SideMode,
|
||
BaseColor: req.BaseColor,
|
||
}
|
||
return s.uc.SetSelfInfo(ctx, user)
|
||
}
|
||
|
||
// SetSelfSetting 设置自己的配置
|
||
func (s *UserService) SetSelfSetting(ctx context.Context, userID uint, setting json.RawMessage) error {
|
||
return s.uc.SetSelfSetting(ctx, userID, setting)
|
||
}
|
||
|
||
// DeleteUser 删除用户
|
||
func (s *UserService) DeleteUser(ctx context.Context, id uint) error {
|
||
return s.uc.DeleteUser(ctx, id)
|
||
}
|
||
|
||
// SetUserAuthorityRequest 设置用户角色请求
|
||
type SetUserAuthorityRequest struct {
|
||
AuthorityId uint `json:"authorityId"`
|
||
}
|
||
|
||
// SetUserAuthorityResponse 设置用户角色响应
|
||
type SetUserAuthorityResponse struct {
|
||
Token string `json:"token"`
|
||
ExpiresAt int64 `json:"expiresAt"`
|
||
}
|
||
|
||
// SetUserAuthority 设置用户角色(切换角色)- 返回新token
|
||
func (s *UserService) SetUserAuthority(ctx context.Context, userID uint, uuid string, username string, nickName string, req *SetUserAuthorityRequest) (*SetUserAuthorityResponse, error) {
|
||
if err := s.uc.SetUserAuthority(ctx, userID, req.AuthorityId); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
// 生成新的JWT token(与GVA保持一致)
|
||
claims := s.jwtPkg.CreateClaims(jwt.BaseClaims{
|
||
UUID: uuid,
|
||
ID: userID,
|
||
Username: username,
|
||
NickName: nickName,
|
||
AuthorityID: req.AuthorityId,
|
||
})
|
||
token, err := s.jwtPkg.CreateToken(claims.BaseClaims)
|
||
if err != nil {
|
||
return nil, errors.InternalServer("TOKEN_ERROR", "生成token失败")
|
||
}
|
||
|
||
return &SetUserAuthorityResponse{
|
||
Token: token,
|
||
ExpiresAt: claims.ExpiresAt.UnixMilli(),
|
||
}, nil
|
||
}
|
||
|
||
// SetUserAuthoritiesRequest 设置用户多角色请求
|
||
type SetUserAuthoritiesRequest struct {
|
||
ID uint `json:"id"`
|
||
AuthorityIds []uint `json:"authorityIds"`
|
||
}
|
||
|
||
// SetUserAuthorities 设置用户多角色
|
||
func (s *UserService) SetUserAuthorities(ctx context.Context, adminAuthorityID uint, req *SetUserAuthoritiesRequest) error {
|
||
return s.uc.SetUserAuthorities(ctx, adminAuthorityID, req.ID, req.AuthorityIds)
|
||
}
|
||
|
||
// 转换函数
|
||
func toUserInfo(u *system.User) *UserInfo {
|
||
info := &UserInfo{
|
||
ID: u.ID,
|
||
UUID: u.UUID.String(),
|
||
Username: u.Username,
|
||
NickName: u.NickName,
|
||
SideMode: u.SideMode,
|
||
HeaderImg: u.HeaderImg,
|
||
BaseColor: u.BaseColor,
|
||
AuthorityId: u.AuthorityId,
|
||
Phone: u.Phone,
|
||
Email: u.Email,
|
||
Enable: u.Enable,
|
||
OriginSetting: u.OriginSetting,
|
||
}
|
||
|
||
if u.Authority != nil {
|
||
info.Authority = &AuthorityInfo{
|
||
AuthorityId: u.Authority.AuthorityId,
|
||
AuthorityName: u.Authority.AuthorityName,
|
||
ParentId: u.Authority.ParentId,
|
||
DefaultRouter: u.Authority.DefaultRouter,
|
||
}
|
||
}
|
||
|
||
if len(u.Authorities) > 0 {
|
||
info.Authorities = make([]*AuthorityInfo, len(u.Authorities))
|
||
for i, a := range u.Authorities {
|
||
info.Authorities[i] = &AuthorityInfo{
|
||
AuthorityId: a.AuthorityId,
|
||
AuthorityName: a.AuthorityName,
|
||
ParentId: a.ParentId,
|
||
DefaultRouter: a.DefaultRouter,
|
||
}
|
||
}
|
||
}
|
||
|
||
return info
|
||
}
|
||
|
||
// RegisterRoutes 注册路由
|
||
func (s *UserService) RegisterRoutes(srv *http.Server) {
|
||
r := srv.Route("/")
|
||
|
||
// 公开接口
|
||
r.POST("/base/login", s.handleLogin)
|
||
|
||
// 需要认证的接口
|
||
r.POST("/user/register", s.handleRegister)
|
||
r.POST("/user/changePassword", s.handleChangePassword)
|
||
r.POST("/user/resetPassword", s.handleResetPassword)
|
||
r.GET("/user/getUserInfo", s.handleGetUserInfo)
|
||
r.POST("/user/getUserList", s.handleGetUserList)
|
||
r.PUT("/user/setUserInfo", s.handleSetUserInfo)
|
||
r.PUT("/user/setSelfInfo", s.handleSetSelfInfo)
|
||
r.PUT("/user/setSelfSetting", s.handleSetSelfSetting)
|
||
r.DELETE("/user/deleteUser", s.handleDeleteUser)
|
||
r.POST("/user/setUserAuthority", s.handleSetUserAuthority)
|
||
r.POST("/user/setUserAuthorities", s.handleSetUserAuthorities)
|
||
}
|
||
|
||
// HTTP Handlers
|
||
func (s *UserService) handleLogin(ctx http.Context) error {
|
||
var req LoginRequest
|
||
if err := ctx.Bind(&req); err != nil {
|
||
return err
|
||
}
|
||
resp, err := s.Login(ctx, &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return ctx.Result(200, map[string]any{
|
||
"code": 0,
|
||
"msg": "登录成功",
|
||
"data": resp,
|
||
})
|
||
}
|
||
|
||
func (s *UserService) handleRegister(ctx http.Context) error {
|
||
var req RegisterRequest
|
||
if err := ctx.Bind(&req); err != nil {
|
||
return err
|
||
}
|
||
resp, err := s.Register(ctx, &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return ctx.Result(200, map[string]any{
|
||
"code": 0,
|
||
"msg": "注册成功",
|
||
"data": resp,
|
||
})
|
||
}
|
||
|
||
func (s *UserService) handleChangePassword(ctx http.Context) error {
|
||
var req ChangePasswordRequest
|
||
if err := ctx.Bind(&req); err != nil {
|
||
return err
|
||
}
|
||
userID := middleware.GetUserID(ctx)
|
||
if userID == 0 {
|
||
return errors.Unauthorized("UNAUTHORIZED", "请先登录")
|
||
}
|
||
if err := s.ChangePassword(ctx, userID, &req); err != nil {
|
||
return err
|
||
}
|
||
return ctx.Result(200, map[string]any{
|
||
"code": 0,
|
||
"msg": "修改成功",
|
||
})
|
||
}
|
||
|
||
func (s *UserService) handleResetPassword(ctx http.Context) error {
|
||
var req ResetPasswordRequest
|
||
if err := ctx.Bind(&req); err != nil {
|
||
return err
|
||
}
|
||
if err := s.ResetPassword(ctx, &req); err != nil {
|
||
return err
|
||
}
|
||
return ctx.Result(200, map[string]any{
|
||
"code": 0,
|
||
"msg": "重置成功",
|
||
})
|
||
}
|
||
|
||
func (s *UserService) handleGetUserInfo(ctx http.Context) error {
|
||
claims, ok := middleware.GetClaims(ctx)
|
||
if !ok {
|
||
return errors.Unauthorized("UNAUTHORIZED", "请先登录")
|
||
}
|
||
resp, err := s.GetUserInfo(ctx, claims.UUID)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return ctx.Result(200, map[string]any{
|
||
"code": 0,
|
||
"msg": "获取成功",
|
||
"data": map[string]any{"userInfo": resp},
|
||
})
|
||
}
|
||
|
||
func (s *UserService) handleGetUserList(ctx http.Context) error {
|
||
var req GetUserListRequest
|
||
if err := ctx.Bind(&req); err != nil {
|
||
return err
|
||
}
|
||
resp, err := s.GetUserList(ctx, &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
return ctx.Result(200, map[string]any{
|
||
"code": 0,
|
||
"msg": "获取成功",
|
||
"data": resp,
|
||
})
|
||
}
|
||
|
||
func (s *UserService) handleSetUserInfo(ctx http.Context) error {
|
||
var req SetUserInfoRequest
|
||
if err := ctx.Bind(&req); err != nil {
|
||
return err
|
||
}
|
||
adminAuthorityID := middleware.GetAuthorityID(ctx)
|
||
if adminAuthorityID == 0 {
|
||
return errors.Unauthorized("UNAUTHORIZED", "请先登录")
|
||
}
|
||
if err := s.SetUserInfo(ctx, adminAuthorityID, &req); err != nil {
|
||
return err
|
||
}
|
||
return ctx.Result(200, map[string]any{
|
||
"code": 0,
|
||
"msg": "设置成功",
|
||
})
|
||
}
|
||
|
||
func (s *UserService) handleSetSelfInfo(ctx http.Context) error {
|
||
var req SetSelfInfoRequest
|
||
if err := ctx.Bind(&req); err != nil {
|
||
return err
|
||
}
|
||
userID := middleware.GetUserID(ctx)
|
||
if userID == 0 {
|
||
return errors.Unauthorized("UNAUTHORIZED", "请先登录")
|
||
}
|
||
if err := s.SetSelfInfo(ctx, userID, &req); err != nil {
|
||
return err
|
||
}
|
||
return ctx.Result(200, map[string]any{
|
||
"code": 0,
|
||
"msg": "设置成功",
|
||
})
|
||
}
|
||
|
||
func (s *UserService) handleSetSelfSetting(ctx http.Context) error {
|
||
var req json.RawMessage
|
||
if err := ctx.Bind(&req); err != nil {
|
||
return err
|
||
}
|
||
userID := middleware.GetUserID(ctx)
|
||
if userID == 0 {
|
||
return errors.Unauthorized("UNAUTHORIZED", "请先登录")
|
||
}
|
||
if err := s.SetSelfSetting(ctx, userID, req); err != nil {
|
||
return err
|
||
}
|
||
return ctx.Result(200, map[string]any{
|
||
"code": 0,
|
||
"msg": "设置成功",
|
||
})
|
||
}
|
||
|
||
func (s *UserService) handleDeleteUser(ctx http.Context) error {
|
||
var req struct {
|
||
ID uint `json:"id"`
|
||
}
|
||
if err := ctx.Bind(&req); err != nil {
|
||
return err
|
||
}
|
||
// 不能删除自己
|
||
userID := middleware.GetUserID(ctx)
|
||
if userID == req.ID {
|
||
return errors.BadRequest("DELETE_SELF", "删除失败, 无法删除自己")
|
||
}
|
||
if err := s.DeleteUser(ctx, req.ID); err != nil {
|
||
return err
|
||
}
|
||
return ctx.Result(200, map[string]any{
|
||
"code": 0,
|
||
"msg": "删除成功",
|
||
})
|
||
}
|
||
|
||
func (s *UserService) handleSetUserAuthority(ctx http.Context) error {
|
||
var req SetUserAuthorityRequest
|
||
if err := ctx.Bind(&req); err != nil {
|
||
return err
|
||
}
|
||
claims, ok := middleware.GetClaims(ctx)
|
||
if !ok {
|
||
return errors.Unauthorized("UNAUTHORIZED", "请先登录")
|
||
}
|
||
resp, err := s.SetUserAuthority(ctx, claims.BaseClaims.ID, claims.UUID, claims.Username, claims.NickName, &req)
|
||
if err != nil {
|
||
return err
|
||
}
|
||
// 设置响应头(与GVA保持一致)
|
||
ctx.Response().Header().Set("new-token", resp.Token)
|
||
ctx.Response().Header().Set("new-expires-at", fmt.Sprintf("%d", resp.ExpiresAt/1000))
|
||
return ctx.Result(200, map[string]any{
|
||
"code": 0,
|
||
"msg": "修改成功",
|
||
})
|
||
}
|
||
|
||
func (s *UserService) handleSetUserAuthorities(ctx http.Context) error {
|
||
var req SetUserAuthoritiesRequest
|
||
if err := ctx.Bind(&req); err != nil {
|
||
return err
|
||
}
|
||
adminAuthorityID := middleware.GetAuthorityID(ctx)
|
||
if adminAuthorityID == 0 {
|
||
return errors.Unauthorized("UNAUTHORIZED", "请先登录")
|
||
}
|
||
if err := s.SetUserAuthorities(ctx, adminAuthorityID, &req); err != nil {
|
||
return err
|
||
}
|
||
return ctx.Result(200, map[string]any{
|
||
"code": 0,
|
||
"msg": "修改成功",
|
||
})
|
||
}
|