Files
SunYF 132c3ed6bd docs(test): 更新电表箱装配业务回归测试文档
- 将文档从代码审计任务转换为端到端业务回归测试链路
- 重新组织文档结构为链路总览、示例数据基线和详细步骤
- 添加完整的业务场景清单涵盖WMS/MES/工位终端三大系统
- 定义统一的测试数据包括产品型号、物料清单、工位派工等
- 提供详细的步骤断言和数量等式验证方法
- 建立贯穿全链路的数据流向和状态变更追踪体系
2026-09-21 10:48:53 +08:00

210 lines
5.7 KiB
Go
Raw Permalink 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 mesclient MES 内部接口客户端(备料单拉取/状态回写,AGV 配送用)。
package mesclient
import (
"bytes"
"context"
"encoding/json"
"errors"
"fmt"
"net/http"
"time"
)
// Client MES 内部接口客户端
type Client struct {
baseURL string
token string
hc *http.Client
}
func New(baseURL, token string) *Client {
if baseURL == "" {
baseURL = "http://127.0.0.1:8888"
}
return &Client{
baseURL: baseURL,
token: token,
hc: &http.Client{Timeout: 6 * time.Second},
}
}
// MaterialRequest MES 备料单行
type MaterialRequest struct {
ID int `json:"id"`
RequestNo string `json:"requestNo"`
OrderNo string `json:"orderNo"`
PlanDate string `json:"planDate"`
MaterialCode string `json:"materialCode"`
MaterialName string `json:"materialName"`
Unit string `json:"unit"`
ManageMode string `json:"manageMode"`
ReqQty float64 `json:"reqQty"`
Status string `json:"status"`
TargetDock string `json:"targetDock"`
Operator string `json:"operator"`
}
// ListMaterialRequests 拉取备料单(含 target_dock
// status 空=全部;传 PENDING 可只取待发料。
func (c *Client) ListMaterialRequests(ctx context.Context, status string) ([]MaterialRequest, error) {
u := c.baseURL + "/api/internal/material-requests"
q := ""
if status != "" {
q = "?status=" + status
}
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u+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()
var env struct {
Code int `json:"code"`
Data []MaterialRequest `json:"data"`
Msg string `json:"message"`
}
dec := json.NewDecoder(resp.Body)
// MES 的 data 可能是 [object] 数组
if err := dec.Decode(&env); err != nil {
return nil, err
}
if env.Data == nil {
return []MaterialRequest{}, nil
}
return env.Data, nil
}
// ProducibleItem MES 实时计算的可生产数量行
type ProducibleItem struct {
ProductCode string `json:"productCode"`
ProductName string `json:"productName"`
ProducibleQty int `json:"producibleQty"`
ShortText string `json:"shortText"`
ComputedAt int64 `json:"computedAt"`
}
// ProducibleDemand MES 实时计算的未来5天物料需求行
type ProducibleDemand struct {
MaterialCode string `json:"materialCode"`
Future5Qty int `json:"future5Qty"`
}
// ProducibleResult MES /api/internal/producible 返回体
type ProducibleResult struct {
Items []ProducibleItem `json:"items"`
Demands []ProducibleDemand `json:"demands"`
ComputedAt int64 `json:"computedAt"`
}
// FetchProducible 实时向 MES 拉取「可生产数量 + 未来5天物料需求」。
// 两系统之间不做定时/批量同步:WMS 打开页面时按需拉一次,MES 现算现返。
func (c *Client) FetchProducible(ctx context.Context) (*ProducibleResult, error) {
u := c.baseURL + "/api/internal/producible"
req, err := http.NewRequestWithContext(ctx, http.MethodGet, u, 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()
var env struct {
Code int `json:"code"`
Data ProducibleResult `json:"data"`
Msg string `json:"message"`
}
if err := json.NewDecoder(resp.Body).Decode(&env); err != nil {
return nil, err
}
if env.Code != 0 {
msg := env.Msg
if msg == "" {
msg = "MES 返回失败"
}
return nil, errors.New(msg)
}
return &env.Data, nil
}
// DockRow MES 接驳台主数据行(dock 真源已迁 MES,WMS 仅只读取用)。
type DockRow struct {
ID int64 `json:"id"`
DockCode string `json:"dockCode"`
Name string `json:"name"`
DockType string `json:"dockType"`
StationNo int `json:"stationNo"`
Status string `json:"status"`
HasPallet bool `json:"hasPallet"`
PalletRef string `json:"palletRef"`
}
// ListDocks 拉取 MES 接驳台主数据(dock 真源在 MES)。
// WMS 出库目标工位下拉 / AGV 页接驳台列表均读此接口,仓库不再本地维护 dock 表。
func (c *Client) ListDocks(ctx context.Context) ([]DockRow, error) {
req, err := http.NewRequestWithContext(ctx, http.MethodGet, c.baseURL+"/api/internal/docks", 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()
var env struct {
Code int `json:"code"`
Data struct {
List []DockRow `json:"list"`
} `json:"data"`
Msg string `json:"message"`
}
if err := json.NewDecoder(resp.Body).Decode(&env); err != nil {
return nil, err
}
if env.Code != 0 {
msg := env.Msg
if msg == "" {
msg = "MES 返回失败"
}
return nil, errors.New(msg)
}
if env.Data.List == nil {
return []DockRow{}, nil
}
return env.Data.List, nil
}
// MarkStatus 回写备料单状态(DELIVERING / DONE
func (c *Client) MarkStatus(ctx context.Context, requestNo, status string) error {
b, _ := json.Marshal(map[string]string{"requestNo": requestNo, "status": status})
req, err := http.NewRequestWithContext(ctx, http.MethodPost, c.baseURL+"/api/internal/material-request/status", 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
}
type RespError struct {
Status int
}
func (e *RespError) Error() string {
return "mes call failed with status " + fmt.Sprintf("%d", e.Status)
}