feat&refactor: 完成多模块功能迭代与配置优化

本次提交覆盖多个业务模块的功能完善与体验优化:
1.  **鉴权与配置调整**:
    - 统一JWT滑动续签逻辑,简化Token存储,移除RefreshToken相关冗余代码
    - 调整多项目配置文件中JWT过期时间为3600秒,统一会话闲置窗口
    - 工位配置放开1~12限制,改为仅校验大于0
2.  **术语统一替换**:全链路将"精密件"替换为"电气件",修正物料管理描述
3.  **功能新增**:
    - 新增工位类型、工艺路线与产线点位台账模块
    - 添加工艺PDF预览面板、工位终端代理转发接口
    - 新增操作日志按操作人列表筛选、工位登出日志记录
    - 新增PLC移料指令与产线点位状态管理
4.  **业务流程优化**:
    - 调整BOM物料删除校验逻辑,优化工单备料计算
    - 补充物料图号、检测单号等追溯字段
    - 完善工艺流程图与工位绑定关系说明
    - 优化前端页面文案与交互细节
5.  **代码规范与维护**:
    - 新增通用工具函数与前端静态资源
    - 整理路由权限与中间件逻辑
    - 修复部分接口与配置的不兼容问题
This commit is contained in:
SunYF
2026-09-10 16:59:15 +08:00
parent 6d8bb75696
commit 0ab21b1234
202 changed files with 21397 additions and 3172 deletions
@@ -1,10 +1,15 @@
package production
import (
"context"
"net/http"
"strconv"
"strings"
"bj_power_mes/common/httpx"
tokenx "bj_power_mes/common/token"
"bj_power_mes/ent"
"bj_power_mes/ent/user"
"bj_power_mes/internal/logic"
"bj_power_mes/internal/svc"
)
@@ -110,6 +115,9 @@ func ListSemiFlowsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
// ---------- 操作日志 ----------
// ListEventLogsHandler GET /api/v1/event-logs 操作日志分页查询
// 可见性:adminSUPER_ADMIN)可看全部并可按操作人筛选;普通用户强制只看自己的日志。
// operatorName:操作人中文名(users.name 批量映射,历史数据同样生效)。
func ListEventLogsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
@@ -117,12 +125,109 @@ func ListEventLogsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
pageSize := atoiDefault(q.Get("pageSize"), 20)
fromMs, _ := strconv.ParseInt(q.Get("from"), 10, 64)
toMs, _ := strconv.ParseInt(q.Get("to"), 10, 64)
// 当前身份:uid → user(历史数据 operator 中文名/用户名混杂,过滤需同时带两者)
uid := currentUid(r, svcCtx)
me, _ := svcCtx.EntClient.User.Get(context.Background(), uid)
meUsername, _ := r.Context().Value("username").(string)
if me == nil && meUsername != "" {
me, _ = svcCtx.EntClient.User.Query().Where(user.UsernameEQ(meUsername)).First(context.Background())
}
var candidates []string
isAdmin := false
if me != nil {
candidates = []string{me.Username, me.Name}
if rl, err := svcCtx.EntClient.Role.Get(context.Background(), me.RoleId); err == nil && rl.Code == "SUPER_ADMIN" {
isAdmin = true
}
}
explicitFilter := q.Get("operator")
if !isAdmin {
// 普通用户仅能查看自己的操作日志(用户名 + 中文名任一命中),忽略显式筛选
explicitFilter = ""
}
list, total, err := logic.New(svcCtx).ListEventLogs(r.Context(),
q.Get("orderNo"), q.Get("operator"), q.Get("eventType"), fromMs, toMs, page, pageSize)
q.Get("orderNo"), operatorCandidates(explicitFilter, candidates), q.Get("eventType"), fromMs, toMs, page, pageSize)
if err != nil {
httpx.Fail(w, 3308, err.Error())
return
}
httpx.Ok(w, map[string]any{"list": list, "total": total})
// 批量映射操作人中文名,缺档显示原用户名
nameMap := realNameMapOf(svcCtx, list)
rows := make([]map[string]any, 0, len(list))
for _, e := range list {
name := nameMap[e.Operator]
if name == "" {
name = e.Operator
}
rows = append(rows, map[string]any{
"id": e.ID,
"eventType": e.EventType,
"workOrderNo": e.WorkOrderNo,
"description": e.Description,
"entityType": e.EntityType,
"entityId": e.EntityId,
"operator": e.Operator,
"operatorName": name,
"payload": e.Payload,
"createdAt": e.CreatedAt.UnixMilli(),
})
}
httpx.Ok(w, map[string]any{"list": rows, "total": total, "isAdmin": isAdmin})
}
}
// currentUid 取当前登录用户ID:优先 JWT 上下文,缺失时回退解析 Bearer token
func currentUid(r *http.Request, svcCtx *svc.ServiceContext) int {
switch v := r.Context().Value("userId").(type) {
case float64:
return int(v)
case int:
return v
case int64:
return int(v)
}
auth := strings.TrimPrefix(r.Header.Get("Authorization"), "Bearer ")
if auth != "" && strings.HasPrefix(r.Header.Get("Authorization"), "Bearer ") {
if claims, err := tokenx.Parse(svcCtx.Config.Auth.AccessSecret, auth); err == nil {
return claims.UserId
}
}
return 0
}
// operatorCandidates 计算操作人过滤候选:admin 显式筛选时精确匹配输入值;否则取当前用户身份候选
func operatorCandidates(filter string, self []string) []string {
if filter != "" {
return []string{filter}
}
return self
}
// realNameMapOf 批量取操作人中文名(users.name)
func realNameMapOf(svcCtx *svc.ServiceContext, logs []*ent.EventLog) map[string]string {
set := make([]string, 0, 8)
seen := map[string]bool{}
for _, e := range logs {
if e.Operator != "" && !seen[e.Operator] {
seen[e.Operator] = true
set = append(set, e.Operator)
}
}
out := map[string]string{}
if len(set) == 0 {
return out
}
us, err := svcCtx.EntClient.User.Query().Where(user.UsernameIn(set...)).All(context.Background())
if err != nil {
return out
}
for _, u := range us {
if u.Name != "" {
out[u.Username] = u.Name
}
}
return out
}