package logic import ( "context" "errors" "time" tokenx "bj_power_mes/common/token" "bj_power_mes/ent" "bj_power_mes/ent/permission" "bj_power_mes/ent/role" "bj_power_mes/ent/user" "bj_power_mes/ent/userstation" "golang.org/x/crypto/bcrypt" ) // ------ 请求/返回 DTO ------ type LoginReq struct { Username string `json:"username"` Password string `json:"password"` } type LoginResp struct { AccessToken string `json:"accessToken"` RefreshToken string `json:"refreshToken"` AccessExpire int64 `json:"accessExpire"` } type RefreshReq struct { RefreshToken string `json:"refreshToken"` } type UserInfoResp struct { UserId int `json:"userId"` Username string `json:"username"` Name string `json:"name"` RoleId int `json:"roleId"` RoleCode string `json:"roleCode"` RoleName string `json:"roleName"` Menus []permissionItem `json:"menus"` PermissionCodes []string `json:"permissionCodes"` } type ChangePasswordReq struct { OldPassword string `json:"oldPassword"` NewPassword string `json:"newPassword"` } type permissionItem struct { Code string `json:"code"` Name string `json:"name"` Path string `json:"path"` Icon string `json:"icon"` } // currentToken 生成 access + refresh 双 token func (s *Service) issueTokens(usr *ent.User, roleEntity *ent.Role) (LoginResp, error) { claims := tokenx.Claims{UserId: usr.ID, Username: usr.Username, Name: usr.Name, RoleId: usr.RoleId} if roleEntity != nil { claims.RoleCode = roleEntity.Code claims.RoleName = roleEntity.Name } else { claims.RoleCode = "" claims.RoleName = "" } access, err := tokenx.Issue(s.ctx.Config.Auth.AccessSecret, time.Duration(s.ctx.Config.Auth.AccessExpire)*time.Second, claims) if err != nil { return LoginResp{}, err } refresh, err := tokenx.Issue(s.ctx.Config.Auth.AccessSecret, time.Duration(s.ctx.Config.Auth.RefreshExpire)*time.Second, claims) if err != nil { return LoginResp{}, err } return LoginResp{AccessToken: access, RefreshToken: refresh, AccessExpire: s.ctx.Config.Auth.AccessExpire}, nil } // Login 登录 func (s *Service) Login(ctx context.Context, req LoginReq) (LoginResp, error) { var resp LoginResp usr, err := s.ctx.EntClient.User.Query().Where(user.Username(req.Username)).First(ctx) if err != nil { return resp, errors.New("用户名或密码错误") } if usr.Status != "ENABLED" { return resp, errors.New("账号已禁用") } if bcrypt.CompareHashAndPassword([]byte(usr.Password), []byte(req.Password)) != nil { return resp, errors.New("用户名或密码错误") } roleEntity, _ := s.ctx.EntClient.Role.Get(ctx, usr.RoleId) return s.issueTokens(usr, roleEntity) } // Refresh 刷新访问令牌(滑动续签) func (s *Service) Refresh(ctx context.Context, req RefreshReq) (LoginResp, error) { var resp LoginResp claims, err := tokenx.Parse(s.ctx.Config.Auth.AccessSecret, req.RefreshToken) if err != nil { return resp, errors.New("刷新令牌无效或已过期") } usr, err := s.ctx.EntClient.User.Get(ctx, claims.UserId) if err != nil { return resp, errors.New("用户不存在") } if usr.Status != "ENABLED" { return resp, errors.New("账号已禁用") } roleEntity, _ := s.ctx.EntClient.Role.Get(ctx, usr.RoleId) return s.issueTokens(usr, roleEntity) } // UserInfo 当前用户信息 + 菜单权限 func (s *Service) UserInfo(ctx context.Context, userId int) (UserInfoResp, error) { var out UserInfoResp usr, err := s.ctx.EntClient.User.Get(ctx, userId) if err != nil { return out, err } out.UserId = usr.ID out.Username = usr.Username out.Name = usr.Name out.RoleId = usr.RoleId roleEntity, err := s.ctx.EntClient.Role.Get(ctx, usr.RoleId) if err != nil { roleEntity = nil } if roleEntity != nil { out.RoleCode = roleEntity.Code out.RoleName = roleEntity.Name out.PermissionCodes = roleEntity.PermissionCodes } // 取所拥有权限对应的菜单 perms, err := s.ctx.EntClient.Permission.Query(). Where(permission.Type("MENU")). Order(ent.Asc(permission.FieldSort)).All(ctx) if err != nil { return out, nil } owned := map[string]bool{} for _, c := range out.PermissionCodes { owned[c] = true } for _, p := range perms { if roleEntity == nil { continue } if roleEntity.Code == "SUPER_ADMIN" || owned[p.Code] || owned["*"] { out.Menus = append(out.Menus, permissionItem{Code: p.Code, Name: p.Name, Path: p.Path, Icon: p.Icon}) } } return out, nil } // ChangePassword 当前登录用户修改自己的密码 func (s *Service) ChangePassword(ctx context.Context, userId int, req ChangePasswordReq) error { if userId <= 0 { return errors.New("未登录") } if req.OldPassword == "" || req.NewPassword == "" { return errors.New("请输入旧密码与新密码") } if len(req.NewPassword) < 6 { return errors.New("新密码长度至少 6 位") } if err := checkPasswordStrong(req.NewPassword); err != nil { return err } usr, err := s.ctx.EntClient.User.Get(ctx, userId) if err != nil { return errors.New("用户不存在") } if bcrypt.CompareHashAndPassword([]byte(usr.Password), []byte(req.OldPassword)) != nil { return errors.New("旧密码不正确") } hash, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost) if err != nil { return err } return s.ctx.EntClient.User.UpdateOneID(userId).SetPassword(string(hash)).Exec(ctx) } // ------ 工位终端登录(块1:用户体系来源于 MES,终端登录改调本接口) ------ type StationLoginReq struct { Username string `json:"username"` Password string `json:"password"` StationNo int `json:"stationNo"` } type StationLoginResp struct { AccessToken string `json:"accessToken"` ExpireAt int64 `json:"expireAt"` User UserInfoResp `json:"user"` Stations []int `json:"stations"` StationNo int `json:"stationNo"` } // StationLogin 工位终端登录:校验 用户名 + 工位终端密码 + 工位权限 func (s *Service) StationLogin(ctx context.Context, req StationLoginReq) (StationLoginResp, error) { var resp StationLoginResp if req.Username == "" || req.Password == "" { return resp, errors.New("用户名和密码必填") } usr, err := s.ctx.EntClient.User.Query().Where(user.Username(req.Username)).First(ctx) if err != nil { return resp, errors.New("用户名或密码错误") } if usr.Status != "ENABLED" { return resp, errors.New("账号已禁用") } if !usr.CanLoginWorkstation { return resp, errors.New("该账号未开通工位终端登录权限") } if usr.WorkstationPassword == "" || bcrypt.CompareHashAndPassword([]byte(usr.WorkstationPassword), []byte(req.Password)) != nil { return resp, errors.New("用户名或密码错误") } stations := s.UserStations(ctx, usr.ID) if len(stations) == 0 { for i := 1; i <= 12; i++ { stations = append(stations, i) } } if req.StationNo > 0 { allowed := false for _, no := range stations { if no == req.StationNo { allowed = true break } } if !allowed { return resp, errors.New("该账号无此工位的操作权限") } } roleEntity, _ := s.ctx.EntClient.Role.Get(ctx, usr.RoleId) claims := tokenx.Claims{UserId: usr.ID, Username: usr.Username, Name: usr.Name, RoleId: usr.RoleId, StationNo: req.StationNo} if roleEntity != nil { claims.RoleCode = roleEntity.Code claims.RoleName = roleEntity.Name } access, err := tokenx.Issue(s.ctx.Config.Auth.AccessSecret, time.Duration(s.ctx.Config.Auth.AccessExpire)*time.Second, claims) if err != nil { return resp, err } resp.AccessToken = access resp.ExpireAt = time.Now().Add(time.Duration(s.ctx.Config.Auth.AccessExpire) * time.Second).Unix() resp.User = UserInfoResp{UserId: usr.ID, Username: usr.Username, Name: usr.Name, RoleId: usr.RoleId} if roleEntity != nil { resp.User.RoleCode = roleEntity.Code resp.User.RoleName = roleEntity.Name } resp.Stations = stations resp.StationNo = req.StationNo return resp, nil } // StationChangePassword 工位终端修改自己的工位终端密码 func (s *Service) StationChangePassword(ctx context.Context, userId int, oldPwd, newPwd string) error { if userId <= 0 { return errors.New("未登录") } if oldPwd == "" || newPwd == "" { return errors.New("请输入原密码与新密码") } if err := checkPasswordStrong(newPwd); err != nil { return err } usr, err := s.ctx.EntClient.User.Get(ctx, userId) if err != nil { return errors.New("用户不存在") } if !usr.CanLoginWorkstation { return errors.New("该账号未开通工位终端登录权限") } if bcrypt.CompareHashAndPassword([]byte(usr.WorkstationPassword), []byte(oldPwd)) != nil { return errors.New("原工位终端密码不正确") } hash, _ := bcrypt.GenerateFromPassword([]byte(newPwd), bcrypt.DefaultCost) return s.ctx.EntClient.User.UpdateOneID(userId).SetWorkstationPassword(string(hash)).Exec(ctx) } // ------ 用户 / 角色 / 权限 管理 ------ type UserReq struct { Id int `json:"id"` Username string `json:"username"` Password string `json:"password"` Name string `json:"name"` RoleId int `json:"roleId"` Status string `json:"status"` CanLoginWorkstation bool `json:"canLoginWorkstation"` WorkstationPassword string `json:"workstationPassword"` Stations []int `json:"stations"` // 允许操作的工位号列表 } // checkPasswordStrong 校验密码:长度>=6 且不能是纯数字 func checkPasswordStrong(pwd string) error { if len(pwd) < 6 { return errors.New("密码长度至少 6 位") } allDigit := true for _, r := range pwd { if r < '0' || r > '9' { allDigit = false break } } if allDigit { return errors.New("密码不能是纯数字") } return nil } func (s *Service) CreateUser(ctx context.Context, req UserReq) error { if err := checkPasswordStrong(req.Password); err != nil { return err } hash, err := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) if err != nil { return err } status := req.Status if status == "" { status = "ENABLED" } create := s.ctx.EntClient.User.Create(). SetUsername(req.Username). SetPassword(string(hash)). SetName(req.Name). SetRoleId(req.RoleId). SetStatus(status). SetCanLoginWorkstation(req.CanLoginWorkstation) if req.CanLoginWorkstation { wsPwd := req.WorkstationPassword if wsPwd == "" { wsPwd = req.Password // 默认复用登录密码 } if err := checkPasswordStrong(wsPwd); err != nil { return errors.New("工位终端密码" + err.Error()) } wsHash, err := bcrypt.GenerateFromPassword([]byte(wsPwd), bcrypt.DefaultCost) if err != nil { return err } create.SetWorkstationPassword(string(wsHash)) } usr, err := create.Save(ctx) if err != nil { return err } return s.replaceUserStations(ctx, usr.ID, req.Stations) } func (s *Service) UpdateUser(ctx context.Context, req UserReq) error { u := s.ctx.EntClient.User.UpdateOneID(req.Id).SetRoleId(req.RoleId).SetName(req.Name). SetCanLoginWorkstation(req.CanLoginWorkstation) if req.Password != "" { if err := checkPasswordStrong(req.Password); err != nil { return err } hash, _ := bcrypt.GenerateFromPassword([]byte(req.Password), bcrypt.DefaultCost) u.SetPassword(string(hash)) } if req.CanLoginWorkstation { if req.WorkstationPassword != "" { if err := checkPasswordStrong(req.WorkstationPassword); err != nil { return errors.New("工位终端密码" + err.Error()) } wsHash, _ := bcrypt.GenerateFromPassword([]byte(req.WorkstationPassword), bcrypt.DefaultCost) u.SetWorkstationPassword(string(wsHash)) } } else { u.SetWorkstationPassword("") } if req.Status != "" { u.SetStatus(req.Status) } if err := u.Exec(ctx); err != nil { return err } return s.replaceUserStations(ctx, req.Id, req.Stations) } // replaceUserStations 全量替换用户-工位关联 func (s *Service) replaceUserStations(ctx context.Context, userId int, stations []int) error { if _, err := s.ctx.EntClient.UserStation.Delete().Where(userstation.UserId(userId)).Exec(ctx); err != nil { return err } for _, no := range stations { if no <= 0 { continue } if err := s.ctx.EntClient.UserStation.Create(). SetUserId(userId).SetStationNo(no).Exec(ctx); err != nil { return err } } return nil } // UserStations 查询用户绑定的工位号 func (s *Service) UserStations(ctx context.Context, userId int) []int { rows, _ := s.ctx.EntClient.UserStation.Query(). Where(userstation.UserId(userId)).All(ctx) out := make([]int, 0, len(rows)) for _, r := range rows { out = append(out, r.StationNo) } return out } func (s *Service) DeleteUser(ctx context.Context, id int) error { if id == 1 { return errors.New("不能删除超级管理员") } return s.ctx.EntClient.User.DeleteOneID(id).Exec(ctx) } // UserVO 用户列表返回体(含工位终端属性与允许操作工位) type UserVO struct { Id int `json:"id"` Username string `json:"username"` Name string `json:"name"` RoleId int `json:"roleId"` Status string `json:"status"` CanLoginWorkstation bool `json:"canLoginWorkstation"` Stations []int `json:"stations"` // 允许登录/操作的工位号(空=全部工位) } func (s *Service) ListUsers(ctx context.Context) ([]UserVO, error) { usrs, err := s.ctx.EntClient.User.Query().Order(ent.Asc(user.FieldID)).All(ctx) if err != nil { return nil, err } out := make([]UserVO, 0, len(usrs)) for _, u := range usrs { out = append(out, UserVO{ Id: u.ID, Username: u.Username, Name: u.Name, RoleId: u.RoleId, Status: u.Status, CanLoginWorkstation: u.CanLoginWorkstation, Stations: s.UserStations(ctx, u.ID), }) } return out, nil } type RoleReq struct { Id int `json:"id"` Name string `json:"name"` Code string `json:"code"` Remark string `json:"remark"` PermissionCodes []string `json:"permissionCodes"` } func (s *Service) CreateRole(ctx context.Context, req RoleReq) error { return s.ctx.EntClient.Role.Create(). SetName(req.Name).SetCode(req.Code).SetRemark(req.Remark). SetPermissionCodes(req.PermissionCodes).Exec(ctx) } func (s *Service) UpdateRole(ctx context.Context, req RoleReq) error { return s.ctx.EntClient.Role.UpdateOneID(req.Id). SetName(req.Name).SetCode(req.Code).SetRemark(req.Remark). SetPermissionCodes(req.PermissionCodes).Exec(ctx) } func (s *Service) DeleteRole(ctx context.Context, id int) error { // 保护:不可删除超级管理员角色 r, err := s.ctx.EntClient.Role.Get(ctx, id) if err != nil { return errors.New("角色不存在") } if r.Code == "SUPER_ADMIN" { return errors.New("超级管理员角色不允许删除") } // 保护:仍有关联用户的角色不可删除(避免产生无主用户/失去全部管理员) cnt, err := s.ctx.EntClient.User.Query(). Where(user.RoleId(id)).Count(ctx) if err == nil && cnt > 0 { return errors.New("该角色下仍有用户,请先移除相关用户") } return s.ctx.EntClient.Role.DeleteOneID(id).Exec(ctx) } func (s *Service) ListRoles(ctx context.Context) ([]*ent.Role, error) { return s.ctx.EntClient.Role.Query().Order(ent.Asc(role.FieldID)).All(ctx) } type PermissionReq struct { Id int `json:"id"` Code string `json:"code"` Name string `json:"name"` Type string `json:"type"` Path string `json:"path"` Icon string `json:"icon"` Sort int `json:"sort"` Remark string `json:"remark"` } func (s *Service) CreatePermission(ctx context.Context, req PermissionReq) error { return s.ctx.EntClient.Permission.Create(). SetCode(req.Code).SetName(req.Name).SetType(req.Type). SetPath(req.Path).SetIcon(req.Icon).SetSort(req.Sort).SetRemark(req.Remark).Exec(ctx) } func (s *Service) UpdatePermission(ctx context.Context, req PermissionReq) error { return s.ctx.EntClient.Permission.UpdateOneID(req.Id). SetCode(req.Code).SetName(req.Name).SetType(req.Type). SetPath(req.Path).SetIcon(req.Icon).SetSort(req.Sort).SetRemark(req.Remark).Exec(ctx) } func (s *Service) DeletePermission(ctx context.Context, id int) error { return s.ctx.EntClient.Permission.DeleteOneID(id).Exec(ctx) } func (s *Service) ListPermissions(ctx context.Context) ([]*ent.Permission, error) { return s.ctx.EntClient.Permission.Query().Order(ent.Asc(permission.FieldSort), ent.Asc(permission.FieldID)).All(ctx) }