Files
bj_power/bj_power_wms/internal/handler/auth.go
T
SunYF 572fa975bc feat: 完成WMS系统权限体系与业务功能迭代
本提交完成了WMS系统的多维度优化升级:
1. 新增RBAC权限系统,支持角色/菜单/按钮三级权限管控
2. 重构库存盘点、物料管理、区域维护等模块的查询筛选与展示逻辑
3. 优化入库/出库/质检等业务流程,完善数据冗余与业务闭环
4. 移除旧装箱表,将装箱逻辑合并到出库主表
5. 新增前端权限判断工具、导出工具与端到端测试用例
6. 补充完善各类注释与数据库字段说明
2026-09-03 14:20:37 +08:00

127 lines
3.6 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package handler
import (
"net/http"
"strings"
"time"
"bj_power_wms/ent/user"
"bj_power_wms/internal/svc"
"golang.org/x/crypto/bcrypt"
)
func healthHandler(ctx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
_, err := ctx.EntClient.User.Query().First(ctx0())
if err == nil || strings.Contains(err.Error(), "no rows") {
ok(w, map[string]any{
"status": "UP",
"time": time.Now().Format("2006-01-02 15:04:05"),
"service": "bj_power_wms",
})
return
}
fail(w, http.StatusServiceUnavailable, "database unavailable")
}
}
func loginHandler(ctx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req struct {
Username string `json:"username"`
Password string `json:"password"`
}
if err := parseJSON(r, &req); err != nil {
fail(w, http.StatusBadRequest, "参数错误")
return
}
if req.Username == "" || req.Password == "" {
fail(w, http.StatusBadRequest, "用户名和密码必填")
return
}
u, err := ctx.EntClient.User.Query().
Where(user.UsernameEQ(req.Username)).
Only(ctx0())
if err != nil {
fail(w, http.StatusUnauthorized, "用户名或密码错误")
return
}
if !u.IsActive {
fail(w, http.StatusForbidden, "账号已停用")
return
}
if bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(req.Password)) != nil {
fail(w, http.StatusUnauthorized, "用户名或密码错误")
return
}
ctx.EntClient.User.UpdateOneID(u.ID).SetLastLoginAt(time.Now().Unix()).ExecX(ctx0())
token, expireAt, err := SignToken(ctx, u.ID, u.Username, u.RealName, u.Role)
if err != nil {
fail(w, http.StatusInternalServerError, "签发 token 失败")
return
}
ok(w, map[string]any{
"token": token,
"expireAt": expireAt,
"user": map[string]any{
"id": u.ID, "username": u.Username, "realName": u.RealName,
"role": u.Role, "roleId": u.RoleID, "dept": u.Dept,
"permissionCodes": permissionsForUser(ctx, u),
},
})
}
}
// changePasswordHandler POST /api/user/change-password(需登录 JWT
// 当前登录用户修改自己的密码;登录人由中间件写入的 X-Username 头确定。
func changePasswordHandler(ctx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
username := strings.TrimSpace(r.Header.Get("X-Username"))
if username == "" {
fail(w, http.StatusUnauthorized, "未登录或登录状态缺失")
return
}
var req struct {
OldPassword string `json:"oldPassword"`
NewPassword string `json:"newPassword"`
}
if err := parseJSON(r, &req); err != nil {
fail(w, http.StatusBadRequest, "参数错误")
return
}
if req.OldPassword == "" || req.NewPassword == "" {
fail(w, http.StatusBadRequest, "请输入旧密码和新密码")
return
}
if len(req.NewPassword) < 6 {
fail(w, http.StatusBadRequest, "新密码长度至少 6 位")
return
}
u, err := ctx.EntClient.User.Query().
Where(user.UsernameEQ(username)).
Only(ctx0())
if err != nil {
fail(w, http.StatusNotFound, "用户不存在")
return
}
if bcrypt.CompareHashAndPassword([]byte(u.Password), []byte(req.OldPassword)) != nil {
fail(w, http.StatusBadRequest, "旧密码不正确")
return
}
hash, err := bcrypt.GenerateFromPassword([]byte(req.NewPassword), bcrypt.DefaultCost)
if err != nil {
fail(w, http.StatusInternalServerError, "密码加密失败")
return
}
if err := ctx.EntClient.User.UpdateOneID(u.ID).SetPassword(string(hash)).Exec(ctx0()); err != nil {
fail(w, http.StatusInternalServerError, "密码保存失败")
return
}
ok(w, nil)
}
}