chore: 批量完成项目多模块迭代优化

1. 废弃半成品库存表并统一库存主表
2. 修复列表排序与搜索大小写不敏感问题
3. 新增用户最近登录时间、工单BOM名称字段
4. 完善角色管理、工位选择器功能
5. 增加拧紧数据补录、成品自动回流WMS能力
6. 统一导出时间范围校验规则
7. 丰富物料/备料单筛选条件
8. 调整菜单与权限命名适配产品型号业务
This commit is contained in:
SunYF
2026-09-08 11:44:04 +08:00
parent 06689970e2
commit e852c7cd37
96 changed files with 4528 additions and 542 deletions
@@ -14,6 +14,7 @@ func SaveBomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req struct {
ProductCode string `json:"productCode"`
BomName string `json:"bomName"` // 同型号多份 BOM 并存时的名称(空=「默认」)
Items []logic.BomItemReq `json:"items"`
}
if err := httpx.ParseJSON(r, &req); err != nil {
@@ -24,7 +25,7 @@ func SaveBomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
httpx.BadRequest(w, "请选择产品编码")
return
}
if err := logic.New(svcCtx).SaveBom(r.Context(), req.ProductCode, req.Items, operator(r, "")); err != nil {
if err := logic.New(svcCtx).SaveBom(r.Context(), req.ProductCode, req.BomName, req.Items, operator(r, "")); err != nil {
httpx.Fail(w, 3101, err.Error())
return
}
@@ -34,7 +35,7 @@ func SaveBomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
func ListBomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
data, err := logic.New(svcCtx).ListBom(r.Context(), r.URL.Query().Get("productCode"))
data, err := logic.New(svcCtx).ListBom(r.Context(), r.URL.Query().Get("productCode"), r.URL.Query().Get("bomName"))
if err != nil {
httpx.Fail(w, 3102, err.Error())
return
@@ -43,6 +44,36 @@ func ListBomHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
}
}
// ListBomNamesHandler GET /bom/names?productCode= — 型号下并存的 BOM 名称列表(工单建单选 BOM 用)
func ListBomNamesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
data, err := logic.New(svcCtx).ListBomNames(r.Context(), r.URL.Query().Get("productCode"))
if err != nil {
httpx.Fail(w, 3107, err.Error())
return
}
httpx.Ok(w, data)
}
}
// DeleteBomItemHandler POST /bom/item/delete {id} — 移除 BOM 中一条物料(按行主键)
func DeleteBomItemHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req struct {
Id int `json:"id"`
}
if err := httpx.ParseJSON(r, &req); err != nil || req.Id <= 0 {
httpx.BadRequest(w, "缺少 id")
return
}
if err := logic.New(svcCtx).DeleteBomItem(r.Context(), req.Id, operator(r, "")); err != nil {
httpx.Fail(w, 3108, err.Error())
return
}
httpx.OkMessage(w, "已移除", nil)
}
}
// ---------- 备料单 ----------
// GenerateMaterialHandler 按日排产生成备料单(生成前校验 BOM 物料在 WMS 档案存在)
@@ -72,7 +103,9 @@ func GenerateMaterialHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
func ListMaterialRequestsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
data, err := logic.New(svcCtx).ListMaterialRequests(r.Context(), q.Get("orderNo"), q.Get("planDate"), q.Get("status"))
data, err := logic.New(svcCtx).ListMaterialRequestsFiltered(r.Context(),
q.Get("orderNo"), q.Get("planDate"), q.Get("status"),
q.Get("materialCode"), q.Get("materialName"), q.Get("targetDock"))
if err != nil {
httpx.Fail(w, 3104, err.Error())
return
@@ -95,12 +95,13 @@ func ScanReportHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
func ScanRecordsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
data, err := logic.New(svcCtx).ListScanRecords(r.Context(), q.Get("sn"), q.Get("orderNo"))
data, total, err := logic.New(svcCtx).ListScanRecords(r.Context(), q.Get("sn"), q.Get("orderNo"),
atoiDefault(q.Get("page"), 1), atoiDefault(q.Get("pageSize"), 20))
if err != nil {
httpx.Fail(w, 3206, err.Error())
return
}
httpx.Ok(w, data)
httpx.Ok(w, map[string]any{"list": data, "total": total})
}
}
@@ -121,3 +122,30 @@ func ProcessStepsHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
httpx.Ok(w, data)
}
}
// TorqueManualAddHandler POST /torque/manual-add 拧紧数据补录(设备漏传/手工修正)
func TorqueManualAddHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req struct {
Sn string `json:"sn"`
WorkOrderNo string `json:"workOrderNo"`
ScrewNo string `json:"screwNo"`
StationNo string `json:"stationNo"`
Strain float64 `json:"strain"`
Angle float64 `json:"angle"`
Result string `json:"result"`
Reason string `json:"reason"`
}
if err := httpx.ParseJSON(r, &req); err != nil {
httpx.BadRequest(w, "请求体解析失败")
return
}
if err := logic.New(svcCtx).AddTorqueRecord(r.Context(), req.Sn, req.WorkOrderNo, req.ScrewNo,
req.StationNo, req.Strain, req.Angle, req.Result, req.Reason, operator(r, "系统管理员")); err != nil {
httpx.Fail(w, 3205, err.Error())
return
}
httpx.OkMessage(w, "补录成功", nil)
}
}
@@ -2,6 +2,7 @@ package production
import (
"net/http"
"net/url"
"strconv"
"strings"
@@ -18,11 +19,11 @@ func pathID(r *http.Request) int {
return n
}
// ---------- 产品型 ----------
// ---------- 产品型 ----------
func ListProductTypesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
data, err := logic.New(svcCtx).ListProductTypes(r.Context())
data, _, err := logic.New(svcCtx).ListProductTypes(r.Context())
if err != nil {
httpx.Fail(w, 3001, err.Error())
return
@@ -31,6 +32,25 @@ func ListProductTypesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
}
}
// ListProductTypesPageHandler GET /product-types/page 产品型号管理页真分页+搜索
// 响应 {total,list,page,pageSize,wmsOnline}
func ListProductTypesPageHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
page := atoiDefault(q.Get("page"), 1)
pageSize := atoiDefault(q.Get("pageSize"), 20)
total, list, wmsOnline, err := logic.New(svcCtx).ListProductTypesPage(r.Context(),
q.Get("keyword"), q.Get("isActive"), page, pageSize)
if err != nil {
httpx.Fail(w, 3001, err.Error())
return
}
httpx.Ok(w, map[string]any{
"total": total, "list": list, "page": page, "pageSize": pageSize, "wmsOnline": wmsOnline,
})
}
}
func CreateProductTypeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
var req logic.ProductTypeReq
@@ -71,6 +91,22 @@ func DeleteProductTypeHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
}
}
// ExportProductTypesHandler GET /product-types/export 转发 WMS xlsx 导出
func ExportProductTypesHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
q := r.URL.Query()
b, err := svcCtx.Wms.ExportProductTypes(r.Context(),
q.Get("keyword"), q.Get("isActive"), q.Get("startDate"), q.Get("endDate"))
if err != nil {
httpx.Fail(w, 3002, "导出失败(WMS 不可达):"+err.Error())
return
}
w.Header().Set("Content-Type", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
w.Header().Set("Content-Disposition", "attachment; filename*=UTF-8''"+url.PathEscape("产品型号.xlsx"))
_, _ = w.Write(b)
}
}
// ---------- 工单 ----------
func ListWorkOrdersHandler(svcCtx *svc.ServiceContext) http.HandlerFunc {