chore: 批量完成项目多模块迭代优化
1. 废弃半成品库存表并统一库存主表 2. 修复列表排序与搜索大小写不敏感问题 3. 新增用户最近登录时间、工单BOM名称字段 4. 完善角色管理、工位选择器功能 5. 增加拧紧数据补录、成品自动回流WMS能力 6. 统一导出时间范围校验规则 7. 丰富物料/备料单筛选条件 8. 调整菜单与权限命名适配产品型号业务
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package wmsclient
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
@@ -40,6 +41,15 @@ func (c *Client) SemiOutbound(ctx context.Context, sn, orderNo string, doneProce
|
||||
})
|
||||
}
|
||||
|
||||
// FinishedInbound 成品完工回流入库:调 WMS /api/internal/finished/inbound。
|
||||
// 成品 SN 由 MES 系统生成(工件SN),统一库存主表 category=3;失败返回 err 由调用方降级。
|
||||
func (c *Client) FinishedInbound(ctx context.Context, productCode, productName, sn, orderNo, operator string) error {
|
||||
return c.post(ctx, "/api/internal/finished/inbound", map[string]any{
|
||||
"productCode": productCode, "productName": productName, "sn": sn,
|
||||
"orderNo": orderNo, "snSource": "AUTO", "operator": operator,
|
||||
})
|
||||
}
|
||||
|
||||
// MaterialExists 批量校验物料编码是否存在于 WMS 物料档案。
|
||||
// 返回 WMS 中不存在的编码列表(全部存在则为空)。WMS 响应 {code,message,data:{missing:[]}}。
|
||||
// 失败(网络/超时/非0响应)返回 err,由调用方决定降级策略。
|
||||
@@ -74,6 +84,157 @@ func (c *Client) MaterialExists(ctx context.Context, codes []string) ([]string,
|
||||
return body.Data.Missing, nil
|
||||
}
|
||||
|
||||
// ProductTypeRow 产品型号档案行(WMS materials item_type=3)
|
||||
type ProductTypeRow struct {
|
||||
ID int `json:"id"`
|
||||
Code string `json:"code"`
|
||||
Name string `json:"name"`
|
||||
Category string `json:"category"`
|
||||
Remark string `json:"remark"`
|
||||
IsActive bool `json:"isActive"`
|
||||
CreatedAt int64 `json:"createdAt"`
|
||||
UpdatedAt int64 `json:"updatedAt"`
|
||||
}
|
||||
|
||||
// get 调 WMS GET 接口并解出 {code,data}
|
||||
func (c *Client) get(ctx context.Context, path string, out any) error {
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+path, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("X-API-TOKEN", c.token)
|
||||
resp, err := c.hc.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= 300 {
|
||||
return &RespError{Status: resp.StatusCode}
|
||||
}
|
||||
var body struct {
|
||||
Code int `json:"code"`
|
||||
Data any `json:"data"`
|
||||
}
|
||||
if err := json.NewDecoder(resp.Body).Decode(&body); err != nil {
|
||||
return err
|
||||
}
|
||||
if body.Code != 0 {
|
||||
return &RespError{Status: resp.StatusCode}
|
||||
}
|
||||
if out != nil && body.Data != nil {
|
||||
b, _ := json.Marshal(body.Data)
|
||||
return json.Unmarshal(b, out)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// putJSON 调 WMS PUT 接口
|
||||
func (c *Client) putJSON(ctx context.Context, path string, body any) error {
|
||||
b, err := json.Marshal(body)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPut, c.baseURL+path, bytes.NewReader(b))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
req.Header.Set("Content-Type", "application/json")
|
||||
req.Header.Set("X-API-TOKEN", c.token)
|
||||
resp, err := c.hc.Do(req)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= 300 {
|
||||
return &RespError{Status: resp.StatusCode}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// ProductTypes 全量产品型号(item_type=3,id asc,新建在底部)。keyword/isActive 可空。
|
||||
func (c *Client) ProductTypes(ctx context.Context, keyword, isActive string) ([]ProductTypeRow, error) {
|
||||
q := "/api/internal/product-types?"
|
||||
if keyword != "" {
|
||||
q += "keyword=" + keyword + "&"
|
||||
}
|
||||
if isActive != "" {
|
||||
q += "isActive=" + isActive
|
||||
}
|
||||
var list []ProductTypeRow
|
||||
if err := c.get(ctx, q, &list); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return list, nil
|
||||
}
|
||||
|
||||
// ProductTypePage 分页+搜索(产品型号管理页真分页)
|
||||
func (c *Client) ProductTypePage(ctx context.Context, keyword, isActive string, page, pageSize int) (int64, []ProductTypeRow, error) {
|
||||
q := fmt.Sprintf("/api/internal/product-types?page=%d&pageSize=%d", page, pageSize)
|
||||
if keyword != "" {
|
||||
q += "&keyword=" + keyword
|
||||
}
|
||||
if isActive != "" {
|
||||
q += "&isActive=" + isActive
|
||||
}
|
||||
var data struct {
|
||||
Total int64 `json:"total"`
|
||||
List []ProductTypeRow `json:"list"`
|
||||
}
|
||||
if err := c.get(ctx, q, &data); err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
return data.Total, data.List, nil
|
||||
}
|
||||
|
||||
// CreateProductType 新建产品型号(code 可空由 WMS 自动生成 PROD-日期-流水)
|
||||
func (c *Client) CreateProductType(ctx context.Context, code, name, category, remark string, isActive bool) error {
|
||||
return c.post(ctx, "/api/internal/product-types", map[string]any{
|
||||
"code": code, "name": name, "category": category, "remark": remark, "isActive": isActive,
|
||||
})
|
||||
}
|
||||
|
||||
// UpdateProductType 修改产品型号(编码不可改)
|
||||
func (c *Client) UpdateProductType(ctx context.Context, id int, name, category, remark string, isActive bool) error {
|
||||
return c.putJSON(ctx, "/api/internal/product-types", map[string]any{
|
||||
"id": id, "name": name, "category": category, "remark": remark, "isActive": isActive,
|
||||
})
|
||||
}
|
||||
|
||||
// DeleteProductType 删除产品型号(引用保护由调用方与 WMS 双侧校验)
|
||||
func (c *Client) DeleteProductType(ctx context.Context, id int) error {
|
||||
return c.post(ctx, "/api/internal/product-types/delete", map[string]any{"id": id})
|
||||
}
|
||||
|
||||
// ExportProductTypes 拉取产品型号导出 xlsx 字节流(WMS 端执行统一导出时间规则)
|
||||
func (c *Client) ExportProductTypes(ctx context.Context, keyword, isActive, startDate, endDate string) ([]byte, error) {
|
||||
q := "/api/internal/product-types/export?"
|
||||
if keyword != "" {
|
||||
q += "keyword=" + keyword + "&"
|
||||
}
|
||||
if isActive != "" {
|
||||
q += "isActive=" + isActive + "&"
|
||||
}
|
||||
q += "startDate=" + startDate + "&endDate=" + endDate
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+q, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
req.Header.Set("X-API-TOKEN", c.token)
|
||||
resp, err := c.hc.Do(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
if resp.StatusCode >= 300 {
|
||||
return nil, &RespError{Status: resp.StatusCode}
|
||||
}
|
||||
buf := new(bytes.Buffer)
|
||||
if _, err := buf.ReadFrom(resp.Body); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf.Bytes(), nil
|
||||
}
|
||||
|
||||
// DisplayOverview 拉取 WMS 免登录看板总览 /api/display/overview。
|
||||
// 用于看板「仓储动态」屏。WMS 不可达时返回 err,由调用方降级为空对象(看板其余屏不受影响)。
|
||||
func (c *Client) DisplayOverview(ctx context.Context) (map[string]any, error) {
|
||||
|
||||
Reference in New Issue
Block a user