feat: 完成MES工位终端系统全量功能迭代
本次提交包含以下核心变更: 1. 新增按钮级权限控制框架与前端权限校验 2. 新增工位管理、工艺流程、巡检终端等核心业务模块 3. 完善用户体系,支持工位终端专属登录权限 4. 新增文件上传下载、报表查询等配套功能 5. 修复多系统兼容性与交互体验问题 6. 完成所有文档与配置项的规范化更新
This commit is contained in:
@@ -10,6 +10,7 @@ import (
|
||||
"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"
|
||||
)
|
||||
@@ -181,15 +182,118 @@ func (s *Service) ChangePassword(ctx context.Context, userId int, req ChangePass
|
||||
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"`
|
||||
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 且不能是纯数字
|
||||
@@ -222,17 +326,37 @@ func (s *Service) CreateUser(ctx context.Context, req UserReq) error {
|
||||
if status == "" {
|
||||
status = "ENABLED"
|
||||
}
|
||||
return s.ctx.EntClient.User.Create().
|
||||
create := s.ctx.EntClient.User.Create().
|
||||
SetUsername(req.Username).
|
||||
SetPassword(string(hash)).
|
||||
SetName(req.Name).
|
||||
SetRoleId(req.RoleId).
|
||||
SetStatus(status).
|
||||
Exec(ctx)
|
||||
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)
|
||||
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
|
||||
@@ -240,10 +364,52 @@ func (s *Service) UpdateUser(ctx context.Context, req UserReq) error {
|
||||
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)
|
||||
}
|
||||
return u.Exec(ctx)
|
||||
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 {
|
||||
@@ -253,8 +419,35 @@ func (s *Service) DeleteUser(ctx context.Context, id int) error {
|
||||
return s.ctx.EntClient.User.DeleteOneID(id).Exec(ctx)
|
||||
}
|
||||
|
||||
func (s *Service) ListUsers(ctx context.Context) ([]*ent.User, error) {
|
||||
return s.ctx.EntClient.User.Query().Order(ent.Asc(user.FieldID)).All(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 {
|
||||
|
||||
Reference in New Issue
Block a user