wanshenmean
2026-03-27 c1aabd3aaa92b072591fc368d81ab2cc37a0aa14
路由变更
已添加4个文件
已修改2个文件
1087 ■■■■■ 文件已修改
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/Service/RouterService.cs 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/docs/superpowers/plans/2026-03-27-router-cache.md 239 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/docs/superpowers/plans/2026-03-27-router-service-audit.md 324 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/docs(superpowers)/specs/2026-03-27-task-logging-design.md 167 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockSerivce.cs 21 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
项目资料/设备协议/上位系统对接/陕西顷刻能源科技MES系统对接接口.md 329 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WCS/WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/Service/RouterService.cs
@@ -1,11 +1,5 @@
using SqlSugar;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using WIDESEAWCS_Common;
using WIDESEAWCS_Common.TaskEnum;
using WIDESEAWCS_Core;
using WIDESEAWCS_Core.BaseServices;
using WIDESEAWCS_Core.Enums;
@@ -538,6 +532,7 @@
            // è¿”回位置列表
            return positions;
        }
        /// <summary>
        /// èŽ·å–è·¯ç”±è¡¨ä¸­æ‰€æœ‰å®Œæ•´çš„è·¯ç”±ä¿¡æ¯(前端调用展示数据)。
        /// </summary>
Code/WCS/WIDESEAWCS_Server/docs/superpowers/plans/2026-03-27-router-cache.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,239 @@
# è·¯ç”±ç¼“存实现计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** åœ¨ RouterService ä¸­å¼•å…¥ HybridCacheService æ··åˆç¼“存,第一次查询加载全量路由到缓存,后续查询直接从缓存读取,AddRouters å†™å…¥åŽåŒæ­¥æ›´æ–°ç¼“存。
**Architecture:** æŒ‰ InOutType åˆ†å¼€ç¼“存两份(入口/出口),GetOrAdd æ‡’加载,写穿更新缓存,单实例部署无广播需求。
**Tech Stack:** ASP.NET Core 6.0, HybridCacheService, SqlSugar ORM
---
## æ–‡ä»¶å˜æ›´æ¦‚览
**修改文件:**
- `WIDESEAWCS_QuartzJob/Service/RouterService.cs` â€” å…¨éƒ¨ç¼“存逻辑改动
**不变:**
- `FindRoutesInMemory` â€” åªæ”¹è°ƒç”¨æ–¹ï¼Œç®—法逻辑不变
- `QueryAllPositions` â€” å·²æœ‰ç‹¬ç«‹ç¼“存,不依赖本方案
---
## Task 1: æ–°å¢ž GetAllRoutersFromCache ç§æœ‰æ–¹æ³•
**文件:**
- Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs`
在类成员变量声明区之后、现有方法之前,新增以下私有方法:
```csharp
/// <summary>
/// ä»Žç¼“存获取指定类型的全量路由数据,缓存不存在时自动从数据库加载并写入缓存
/// </summary>
/// <param name="routeType">路由类型(入口/出口)</param>
/// <returns>该类型的全部路由列表</returns>
private List<Dt_Router> GetAllRoutersFromCache(int routeType)
{
    string cacheKey = $"Router:AllRouters:{(routeType == RouterInOutType.In.ObjToInt() ? "In" : "Out")}";
    return _cacheService.GetOrAdd(
        cacheKey,
        () => BaseDal.QueryData(x => x.InOutType == routeType)
    );
}
---
## Task 2: æ”¹é€  QueryNextRoutes é‡è½½ï¼ˆ2个方法)
**文件:**
- Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs:48-98`
### QueryNextRoutes(string, string) â€” ç¬¬48-69行
原代码:
```csharp
List<Dt_Router> allRouters = BaseDal.QueryData(x => true);
routers = FindRoutesInMemory(startPosi, endPosi, allRouters, null);
```
改为:
```csharp
List<Dt_Router> allRouters = GetAllRoutersFromCache(RouterInOutType.In.ObjToInt());
// å…¥å£+出口都加载,FindRoutesInMemory å†…部按 routeType==null ä¸åšè¿‡æ»¤
List<Dt_Router> outRouters = GetAllRoutersFromCache(RouterInOutType.Out.ObjToInt());
allRouters.AddRange(outRouters);
routers = FindRoutesInMemory(startPosi, endPosi, allRouters, null);
```
### QueryNextRoutes(string, string, int) â€” ç¬¬78-99行
原代码:
```csharp
List<Dt_Router> allRouters = BaseDal.QueryData(x => x.InOutType == routeType);
routers = FindRoutesInMemory(startPosi, endPosi, allRouters, routeType);
```
改为:
```csharp
List<Dt_Router> allRouters = GetAllRoutersFromCache(routeType);
routers = FindRoutesInMemory(startPosi, endPosi, allRouters, routeType);
```
---
## Task 3: æ”¹é€  QueryNextRoute é‡è½½ï¼ˆ4个方法)
**文件:**
- Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs:189-314`
### QueryNextRoute(string) â€” ç¬¬189-205行
原代码:
```csharp
List<Dt_Router> routes = BaseDal.QueryData(x => x.StartPosi == startPosi, ...);
return routes.FirstOrDefault();
```
改为:
```csharp
List<Dt_Router> routes = GetAllRoutersFromCache(RouterInOutType.In.ObjToInt())
    .Where(x => x.StartPosi == startPosi)
    .ToList();
routes = routes.OrderByDescending(x => x.IsEnd).ToList();
return routes.FirstOrDefault();
```
### QueryNextRoute(string, int) â€” ç¬¬213-229行
原代码:
```csharp
List<Dt_Router> routes = BaseDal.QueryData(x => x.StartPosi == startPosi && x.InOutType == routeType, ...);
return routes.FirstOrDefault();
```
改为:
```csharp
List<Dt_Router> routes = GetAllRoutersFromCache(routeType)
    .Where(x => x.StartPosi == startPosi)
    .ToList();
routes = routes.OrderByDescending(x => x.IsEnd).ToList();
return routes.FirstOrDefault();
```
### QueryNextRoute(string, string, int) â€” ç¬¬238-272行
原代码(第255行):
```csharp
List<Dt_Router> allRouters = BaseDal.QueryData(x => x.InOutType == routeType);
```
改为:
```csharp
List<Dt_Router> allRouters = GetAllRoutersFromCache(routeType);
```
其余直接路由判断和 `FindRoutesInMemory` è°ƒç”¨é€»è¾‘不变。
### QueryNextRoute(string, string) â€” ç¬¬280-314行
原代码(第285行):
```csharp
List<Dt_Router> routes = BaseDal.QueryData(x => x.StartPosi == startPosi, ...);
```
改为(合并入口+出口两份缓存,与原方法全量查询语义一致):
```csharp
List<Dt_Router> inRoutes = GetAllRoutersFromCache(RouterInOutType.In.ObjToInt());
List<Dt_Router> outRoutes = GetAllRoutersFromCache(RouterInOutType.Out.ObjToInt());
List<Dt_Router> routes = inRoutes.Concat(outRoutes)
    .Where(x => x.StartPosi == startPosi)
    .ToList();
routes = routes.OrderByDescending(x => x.IsEnd).ToList();
```
然后复用已有的直接路由判断逻辑。
---
## Task 4: æ”¹é€  QueryRoutePath
**文件:**
- Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs:323-366`
第355行附近,原代码中 `while` å¾ªçŽ¯é‡Œæ¯æ¬¡è°ƒç”¨ `QueryNextRoute` çš„逻辑**不需要改动**(因为 `QueryNextRoute` å·²ç»åœ¨ Task3 ä¸­æ”¹é€ äº†ï¼‰ã€‚本任务的改动仅确认 `QueryRoutePath` æ–¹æ³•本身不再直接调用 `BaseDal.QueryData`,即不需要任何改动 â€”— å®ƒè°ƒç”¨çš„æ˜¯ `QueryNextRoute`,而后者已被改造。
**验证:** ç¡®è®¤ `QueryRoutePath` æ–¹æ³•体内没有直接调用 `BaseDal.QueryData`,只有对 `QueryNextRoute` çš„调用。
---
## Task 5: æ”¹é€  GetAllWholeRouters
**文件:**
- Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs:412-446`
原代码:
```csharp
List<Dt_Router> allRouters = BaseDal.QueryData(x => true);
```
改为:
```csharp
List<Dt_Router> inRouters = GetAllRoutersFromCache(RouterInOutType.In.ObjToInt());
List<Dt_Router> outRouters = GetAllRoutersFromCache(RouterInOutType.Out.ObjToInt());
List<Dt_Router> allRouters = inRouters.Concat(outRouters).ToList();
```
后续遍历 `dt_Routers`(取 `IsEnd == true` çš„路由)的逻辑不变。
---
## Task 6: æ”¹é€  AddRouters,写入 DB åŽæ›´æ–°ç¼“å­˜
**文件:**
- Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs:511-594`
在第568行附近,找到:
```csharp
// æŸ¥è¯¢æ•°æ®åº“中已有的路由信息
List<Dt_Router> dt_Routers = BaseDal.QueryData(x => x.InOutType == routerType);
```
这个 `dt_Routers` æ˜¯**添加之前**的已有数据,不包含本次新增的路由。在第586行 `BaseDal.AddData(routers)` **之后**、`content = WebResponseContent.Instance.OK()` **之前**,必须重新查询一次全量数据再写入缓存:
```csharp
// æ·»åŠ æ–°çš„è·¯ç”±ä¿¡æ¯
BaseDal.AddData(routers);
// é‡æ–°æŸ¥è¯¢å…¨é‡è·¯ç”±ï¼ˆæ­¤æ—¶æ‰åŒ…含新增的路由),再写入缓存
List<Dt_Router> updatedRouters = BaseDal.QueryData(x => x.InOutType == routerType);
string cacheKey = $"Router:AllRouters:{(routerType == RouterInOutType.In.ObjToInt() ? "In" : "Out")}";
_cacheService.Set(cacheKey, updatedRouters);
content = WebResponseContent.Instance.OK();
```
---
## Task 7: ç¼–译验证
**验证命令:**
```bash
dotnet build WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/WIDESEAWCS_QuartzJob.csproj
```
预期:无编译错误(CS0103/CS0019 ç­‰ç±»åž‹/语法错误全部修复后)
---
## å®žæ–½é¡ºåº
1. Task 1 â€” æ–°å¢ž `GetAllRoutersFromCache` ç§æœ‰æ–¹æ³•
2. Task 2 â€” æ”¹é€  `QueryNextRoutes` ä¸¤ä¸ªé‡è½½
3. Task 3 â€” æ”¹é€  `QueryNextRoute` å››ä¸ªé‡è½½
4. Task 4 â€” ç¡®è®¤ `QueryRoutePath` æ— éœ€æ”¹åŠ¨ï¼ˆå®ƒä¾èµ–å·²æ”¹é€ çš„ `QueryNextRoute`)
5. Task 5 â€” æ”¹é€  `GetAllWholeRouters`
6. Task 6 â€” æ”¹é€  `AddRouters` æ›´æ–°ç¼“å­˜
7. Task 7 â€” ç¼–译验证
Code/WCS/WIDESEAWCS_Server/docs/superpowers/plans/2026-03-27-router-service-audit.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,324 @@
# RouterService é€»è¾‘修复与新方法实现计划
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** ä¿®å¤ RouterService ä¸­çš„ 3 ä¸ªé€»è¾‘问题,新增 7 ä¸ªè·¯ç”±æŸ¥è¯¢/管理方法,并更新接口层。
**Architecture:** åœ¨çŽ°æœ‰ RouterService åŸºç¡€ä¸Šæ–°å¢žç¼“存管理方法和路由查询增强方法,保持现有 HybridCacheService ç¼“存架构不变。
**Tech Stack:** ASP.NET Core 6.0, HybridCacheService, SqlSugar ORM
---
## æ–‡ä»¶å˜æ›´æ¦‚览
**修改文件:**
- `WIDESEAWCS_QuartzJob/Service/RouterService.cs` â€” é€»è¾‘修复 + æ–°å¢žæ–¹æ³•实现
- `WIDESEAWCS_QuartzJob/Service/IRouterService.cs` â€” æŽ¥å£æ–°å¢žæ–¹æ³•签名 + åŽ»æŽ‰å¤šä½™ `public` ä¿®é¥°ç¬¦
---
## Task 1: ä¿®å¤ AddRouters ç¼“存一致性问题
**文件:**
- Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs:605-613`
**原代码:**
```csharp
BaseDal.AddData(routers);
List<Dt_Router> updatedRouters = BaseDal.QueryData(x => x.InOutType == routerType);
string cacheKey = $"Router:AllRouters:{(routerType == RouterInOutType.In.ObjToInt() ? "In" : "Out")}";
_cacheService.AddOrUpdate(cacheKey, updatedRouters);
content = WebResponseContent.Instance.OK();
```
**改为:**
```csharp
// æ·»åŠ æ–°çš„è·¯ç”±ä¿¡æ¯
BaseDal.AddData(routers);
// é‡æ–°æŸ¥è¯¢å…¨é‡è·¯ç”±ï¼ˆæ­¤æ—¶æ‰åŒ…含新增的路由),再写入缓存
List<Dt_Router> updatedRouters = BaseDal.QueryData(x => x.InOutType == routerType);
string cacheKey = $"Router:AllRouters:{(routerType == RouterInOutType.In.ObjToInt() ? "In" : "Out")}";
try
{
    _cacheService.AddOrUpdate(cacheKey, updatedRouters);
}
catch
{
    // ç¼“存更新失败时静默忽略,下次查询会从DB自动重建缓存
}
content = WebResponseContent.Instance.OK();
```
**说明**:用 try-catch åŒ…裹缓存更新操作,防止缓存写入失败时异常逃逸到外层被当作错误返回。DB å·²å†™å…¥æˆåŠŸï¼Œç¼“å­˜å¤±è´¥ä¸å½±å“ä¸šåŠ¡æ­£ç¡®æ€§ã€‚
---
## Task 2: ä¿®å¤ QueryAllPositions ç©º catch å—
**文件:**
- Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs:412-415`
**原代码:**
```csharp
catch
{
}
```
**改为:**
```csharp
catch (Exception ex)
{
    ConsoleHelper.WriteErrorLine($"RouterService.QueryAllPositions æŸ¥è¯¢å¤±è´¥: {ex.Message}");
}
```
**说明**:增加错误日志记录,便于排查问题。返回值仍为空列表,调用方行为不变。
---
## Task 3: ä¿®å¤ IRouterService æŽ¥å£ public ä¿®é¥°ç¬¦ + æ–°å¢žæ–¹æ³•签名
**文件:**
- Modify: `WIDESEAWCS_QuartzJob/Service/IRouterService.cs`
去掉以下行的 `public` ä¿®é¥°ç¬¦ï¼š
- ç¬¬40行:`public Dt_Router QueryNextRoute(string startPosi);` â†’ `Dt_Router QueryNextRoute(string startPosi);`
- ç¬¬48行
- ç¬¬57行
- ç¬¬65行
- ç¬¬74行
同时在接口末尾新增以下方法签名:
```csharp
/// <summary>
/// æ¸…除路由缓存
/// </summary>
void ClearRouterCache();
/// <summary>
/// æ ¹æ®è®¾å¤‡ç¼–号查询经过该设备的所有路由
/// </summary>
List<Dt_Router> QueryRoutersByDeviceCode(string deviceCode);
/// <summary>
/// åˆ¤æ–­ä¸¤ç‚¹ä¹‹é—´æ˜¯å¦å­˜åœ¨è·¯ç”±ï¼ˆå…¨ç±»åž‹ï¼‰
/// </summary>
bool ExistsRouter(string startPosi, string endPosi);
/// <summary>
/// åˆ¤æ–­ä¸¤ç‚¹ä¹‹é—´æ˜¯å¦å­˜åœ¨æŒ‡å®šç±»åž‹çš„路由
/// </summary>
bool ExistsRouter(string startPosi, string endPosi, int routeType);
/// <summary>
/// èŽ·å–å…¨é‡è·¯ç”±æ•°é‡ï¼ˆåˆå¹¶å…¥å£+出口)
/// </summary>
int GetRouterCount();
/// <summary>
/// èŽ·å–æŒ‡å®šç±»åž‹è·¯ç”±æ•°é‡
/// </summary>
int GetRouterCount(int routeType);
/// <summary>
/// æ‰¹é‡åˆ é™¤è·¯ç”±
/// </summary>
WebResponseContent DeleteRouters(List<long> routerIds);
```
---
## Task 4: å®žçް ClearRouterCache()
**文件:**
- Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs`
在 `GetAllRoutersFromCache` æ–¹æ³•之后新增:
```csharp
/// <summary>
/// æ¸…除所有路由缓存(入口和出口类型)
/// </summary>
public void ClearRouterCache()
{
    _cacheService.Remove("Router:AllRouters:In");
    _cacheService.Remove("Router:AllRouters:Out");
}
```
---
## Task 5: å®žçް QueryRoutersByDeviceCode
**文件:**
- Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs`
在 `ClearRouterCache` ä¹‹åŽæ–°å¢žï¼š
```csharp
/// <summary>
/// æ ¹æ®è®¾å¤‡ç¼–号查询经过该设备的所有路由(合并入口+出口类型)
/// </summary>
/// <param name="deviceCode">设备编号</param>
/// <returns>经过该设备的路由列表</returns>
public List<Dt_Router> QueryRoutersByDeviceCode(string deviceCode)
{
    List<Dt_Router> inRouters = GetAllRoutersFromCache(RouterInOutType.In.ObjToInt());
    List<Dt_Router> outRouters = GetAllRoutersFromCache(RouterInOutType.Out.ObjToInt());
    return inRouters.Concat(outRouters)
        .Where(x => x.ChildPosiDeviceCode == deviceCode)
        .ToList();
}
```
---
## Task 6: å®žçް ExistsRouter(两个重载)
**文件:**
- Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs`
在 `QueryRoutersByDeviceCode` ä¹‹åŽæ–°å¢žï¼š
```csharp
/// <summary>
/// åˆ¤æ–­ä¸¤ç‚¹ä¹‹é—´æ˜¯å¦å­˜åœ¨è·¯ç”±ï¼ˆå…¨ç±»åž‹ï¼‰
/// </summary>
public bool ExistsRouter(string startPosi, string endPosi)
{
    List<Dt_Router> inRouters = GetAllRoutersFromCache(RouterInOutType.In.ObjToInt());
    List<Dt_Router> outRouters = GetAllRoutersFromCache(RouterInOutType.Out.ObjToInt());
    var allRouters = inRouters.Concat(outRouters).ToList();
    var routes = FindRoutesInMemory(startPosi, endPosi, allRouters, null);
    return routes.Count > 0;
}
/// <summary>
/// åˆ¤æ–­ä¸¤ç‚¹ä¹‹é—´æ˜¯å¦å­˜åœ¨æŒ‡å®šç±»åž‹çš„路由
/// </summary>
public bool ExistsRouter(string startPosi, string endPosi, int routeType)
{
    List<Dt_Router> allRouters = GetAllRoutersFromCache(routeType);
    var routes = FindRoutesInMemory(startPosi, endPosi, allRouters, routeType);
    return routes.Count > 0;
}
```
---
## Task 7: å®žçް GetRouterCount(两个重载)
**文件:**
- Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs`
在 `ExistsRouter` ä¹‹åŽæ–°å¢žï¼š
```csharp
/// <summary>
/// èŽ·å–å…¨é‡è·¯ç”±æ•°é‡ï¼ˆå…¥å£+出口合计)
/// </summary>
public int GetRouterCount()
{
    int inCount = GetAllRoutersFromCache(RouterInOutType.In.ObjToInt()).Count;
    int outCount = GetAllRoutersFromCache(RouterInOutType.Out.ObjToInt()).Count;
    return inCount + outCount;
}
/// <summary>
/// èŽ·å–æŒ‡å®šç±»åž‹è·¯ç”±æ•°é‡
/// </summary>
public int GetRouterCount(int routeType)
{
    return GetAllRoutersFromCache(routeType).Count;
}
```
---
## Task 8: å®žçް DeleteRouters
**文件:**
- Modify: `WIDESEAWCS_QuartzJob/Service/RouterService.cs`
在 `AddRouters` æ–¹æ³•之后新增:
```csharp
/// <summary>
/// æ‰¹é‡åˆ é™¤æŒ‡å®šID的路由,删除后同步清除对应类型的缓存
/// </summary>
/// <param name="routerIds">待删除的路由ID列表</param>
/// <returns>返回处理结果</returns>
public WebResponseContent DeleteRouters(List<long> routerIds)
{
    WebResponseContent content = new WebResponseContent();
    try
    {
        if (routerIds == null || routerIds.Count == 0)
        {
            return content = WebResponseContent.Instance.Error("待删除的路由ID列表不能为空");
        }
        // æŸ¥è¯¢å¾…删除路由的类型(用于后续清除缓存)
        var routersToDelete = BaseDal.QueryData(x => routerIds.Contains(x.Id));
        if (routersToDelete.Count == 0)
        {
            return content = WebResponseContent.Instance.Error("未找到待删除的路由");
        }
        // è®°å½•涉及的类型(去重)
        var affectedTypes = routersToDelete.Select(x => x.InOutType).Distinct().ToList();
        // æ‰§è¡Œæ‰¹é‡åˆ é™¤
        BaseDal.DeleteData(routersToDelete);
        // æ¸…除受影响类型的缓存
        foreach (var routeType in affectedTypes)
        {
            string cacheKey = $"Router:AllRouters:{(routeType == RouterInOutType.In.ObjToInt() ? "In" : "Out")}";
            _cacheService.Remove(cacheKey);
        }
        content = WebResponseContent.Instance.OK();
    }
    catch (Exception ex)
    {
        content = WebResponseContent.Instance.Error(ex.Message);
    }
    return content;
}
```
---
## Task 9: ç¼–译验证
**验证命令:**
```bash
dotnet build WIDESEAWCS_Server/WIDESEAWCS_QuartzJob/WIDESEAWCS_QuartzJob.csproj
```
预期:0 errors
---
## å®žæ–½é¡ºåº
1. Task 1 â€” ä¿®å¤ AddRouters ç¼“存一致性
2. Task 2 â€” ä¿®å¤ QueryAllPositions ç©º catch å—
3. Task 3 â€” ä¿®å¤ IRouterService æŽ¥å£ï¼ˆåŽ»æŽ‰ public + æ–°å¢žç­¾åï¼‰
4. Task 4 â€” å®žçް ClearRouterCache
5. Task 5 â€” å®žçް QueryRoutersByDeviceCode
6. Task 6 â€” å®žçް ExistsRouter ä¸¤ä¸ªé‡è½½
7. Task 7 â€” å®žçް GetRouterCount ä¸¤ä¸ªé‡è½½
8. Task 8 â€” å®žçް DeleteRouters
9. Task 9 â€” ç¼–译验证
Code/WCS/docs(superpowers)/specs/2026-03-27-task-logging-design.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,167 @@
# ä»»åŠ¡æ—¥å¿—å¢žå¼ºè®¾è®¡æ–‡æ¡£
## æ¦‚è¿°
为 `RobotJob`、`ConveyorLineNewJob`、`StackerCraneJob` ä¸‰ä¸ªæ¨¡å—添加完善的日志记录功能。
## æ—¥å¿—规范
### æ—¥å¿—级别定义(严格按语义)
| çº§åˆ« | ç”¨é€” | ç¤ºä¾‹åœºæ™¯ |
|------|------|----------|
| `Info` | æ­£å¸¸æµç¨‹èŠ‚ç‚¹ | ä»»åŠ¡å¼€å§‹å¤„ç†ã€å‘½ä»¤ä¸‹å‘æˆåŠŸã€çŠ¶æ€è½¬æ¢å®Œæˆ |
| `Warn` | éœ€è¦å…³æ³¨ä½†éžé”™è¯¯ | å®¢æˆ·ç«¯æ–­è¿žé‡è¯•、站台不可用、版本冲突 |
| `Error` | å¼‚常/失败 | WMS è°ƒç”¨å¤±è´¥ã€å‘½ä»¤å‘送失败、任务查询失败 |
| `Debug` | è¯¦ç»†ä¿¡æ¯ | è½®è¯¢æ“ä½œã€ç¼“存读取(可选) |
### æ—¥å¿—输出方式
1. **ILogger<T>** - é€šè¿‡ä¾èµ–注入或父类传递
2. **QuartzLogger** - å¼‚步文件日志,同时记录
```csharp
// æ ‡å‡†æ—¥å¿—写法
_logger.LogInformation("消息内容");
QuartzLogger.Info("消息内容", source);
```
## ä¿®æ”¹æ¸…单
### RobotJob æ¨¡å—
#### RobotJob.cs
- çŠ¶æ€ï¼šå·²æœ‰ ILogger,无需修改
#### RobotClientManager.cs
- æ·»åŠ  `ILogger<RobotClientManager>`
- ä¿®æ­£æ–­è¿žæ—¥å¿—:Warn â†’ Info(客户端断开是正常流程)
- æ·»åŠ  EnsureClientSubscribed ä¸­çš„重试/不可用日志
#### RobotTaskProcessor.cs
- æ·»åŠ  `ILogger<RobotTaskProcessor>`
- ä¿®æ­£ SendSocketRobotPickAsync:Error â†’ Info(成功下发应记录 Info)
- æ·»åŠ  HandleInboundTaskAsync çš„ WMS è°ƒç”¨ç»“果日志
#### RobotStateManager.cs
- æ·»åŠ  `ILogger<RobotStateManager>`
- æž„造函数的 GetOrCreateState æ·»åŠ  Info æ—¥å¿—
- TryUpdateStateSafely æ·»åŠ ç‰ˆæœ¬å†²çª Warn æ—¥å¿—
#### RobotMessageHandler.cs
- å·²æœ‰ ILogger,已有良好日志,保持不变
#### RobotWorkflowOrchestrator.cs
- æ·»åŠ  `ILogger<RobotWorkflowOrchestrator>`
- ExecuteAsync æ·»åŠ çŠ¶æ€æœºå†³ç­–æ—¥å¿—ï¼ˆæ»¡è¶³æ¡ä»¶æ—¶è®°å½• Info)
- HandlePickFinishedStateAsync æ·»åŠ æ”¾è´§æŒ‡ä»¤ä¸‹å‘æ—¥å¿—
- HandlePutFinishedStateAsync æ·»åŠ å–è´§æŒ‡ä»¤ä¸‹å‘æ—¥å¿—
### ConveyorLineNewJob æ¨¡å—
#### CommonConveyorLineNewJob.cs
- æ·»åŠ  `ILogger<CommonConveyorLineNewJob>`
- Execute æ–¹æ³•:
  - å­è®¾å¤‡æ•°é‡ä¸º 0:Info
  - Parallel.For å¼€å§‹ï¼šDebug
  - å‘½ä»¤ä¸ºç©ºè·³è¿‡ï¼šDebug
  - WCS_ACK å¤„理:Debug
  - æ£€æŸ¥æ‰˜ç›˜ä½ç½®ï¼šInfo
  - PLC_STB æ£€æŸ¥ï¼šDebug
  - æ— æ¡ç è¯·æ±‚出库:Info
  - æœ‰ä»»åŠ¡å·å¤„ç†ä»»åŠ¡ï¼šInfo
  - å¼‚常捕获:Error
- ProcessTaskState æ–¹æ³•:添加各状态分支的入口日志
#### ConveyorLineDispatchHandler.cs
- æ·»åŠ  `ILogger<ConveyorLineDispatchHandler>`
- HeartBeat:Debug
- RequestInbound:Info(入库请求开始)
- RequestInNextAddress:Info(入库下一地址)
- ConveyorLineInFinish:Info(入库完成)
- RequestOutbound:Info(出库请求)
- RequestOutNextAddress:Info(出库下一地址)
- ConveyorLineOutFinish:Info(出库完成)
#### ConveyorLineTaskFilter.cs
- æ·»åŠ  `ILogger<ConveyorLineTaskFilter>`
- QueryPendingTask:Debug
- QueryExecutingTask:Debug
- RequestWmsTask:Info(WMS è¯·æ±‚)
#### ConveyorLineTargetAddressSelector.cs
- æ·»åŠ  `ILogger<ConveyorLineTargetAddressSelector>`
- HandleInboundNextAddress:Debug
- HandleOutboundNextAddress:Debug
- HandleDeviceRequest:Debug
- ProcessDeviceRequest:Debug
### StackerCraneJob æ¨¡å—
#### CommonStackerCraneJob.cs
- æ·»åŠ  `ILogger<CommonStackerCraneJob>`
- Execute æ–¹æ³•:
  - å‚数无效:Warn
  - äº‹ä»¶è®¢é˜…:Info
  - ä»»åŠ¡å®Œæˆæ£€æŸ¥ï¼šDebug
  - ä¸å¯å‘送任务:Debug
  - ä»»åŠ¡é€‰æ‹©ç»“æžœï¼šInfo(选中任务号或无任务)
  - å‘½ä»¤æž„建结果:Info
  - å‘½ä»¤å‘送结果:Info
  - å¼‚常捕获:Error
- CommonStackerCrane_StackerCraneTaskCompletedEventHandler:Info
- LoadConfig å¤±è´¥ï¼šWarn
#### StackerCraneTaskSelector.cs
- æ·»åŠ  `ILogger<StackerCraneTaskSelector>`
- SelectTask:Info(任务选择开始、选择结果)
- TrySelectOutboundTask:Debug
- IsOutTaskStationAvailable:Info(站台可用/不可用)
- TryAddTaskFromWms:Info
#### StackerCraneCommandBuilder.cs
- æ·»åŠ  `ILogger<StackerCraneCommandBuilder>`
- ConvertToStackerCraneTaskCommand:Info(命令类型、任务号)
- GetCommandType:Debug
- BuildInboundCommand:Info(入库命令构建)
- BuildOutboundCommand:Info(出库命令构建)
- BuildRelocationCommand:Info(移库命令构建)
- åœ°å€è§£æžå¤±è´¥ï¼šError
## ILogger ä¾èµ–传递方案
对于通过 `new` ç›´æŽ¥å®žä¾‹åŒ–的辅助类,通过父类构造函数传入 ILogger:
```csharp
// è¾…助类接收 ILogger
public class RobotStateManager
{
    private readonly ILogger _logger;
    public RobotStateManager(ICacheService cache, ILogger<RobotStateManager> logger)
    {
        _logger = logger;
    }
}
// Job åœ¨åˆ›å»ºè¾…助类时传入自己的 logger
public RobotJob(..., ILogger<RobotJob> logger)
{
    _stateManager = new RobotStateManager(cache, logger);
}
```
## éœ€ä¿®æ­£çš„æ—¥å¿—语义问题
| ä½ç½® | åŽŸå†™æ³• | ä¿®æ­£åŽ |
|------|--------|--------|
| RobotTaskProcessor.SendSocketRobotPickAsync | QuartzLogger.Error (成功时) | QuartzLogger.Info |
| RobotClientManager.EnsureClientSubscribed | QuartzLogger.Info (异常时) | QuartzLogger.Error |
| RobotClientManager.OnRobotReceived | QuartzLogger.Warn (断连) | QuartzLogger.Info |
## éªŒæ”¶æ ‡å‡†
1. æ‰€æœ‰å…³é”®ä¸šåŠ¡èŠ‚ç‚¹æœ‰ Info æ—¥å¿—
2. å¼‚常情况有 Error æ—¥å¿—并包含异常信息
3. éœ€è¦å…³æ³¨çš„æƒ…况有 Warn æ—¥å¿—
4. æ—¥å¿—同时输出到 ILogger å’Œ QuartzLogger
5. æ—¥å¿—消息清晰,包含关键上下文(如任务号、设备编码)
Code/WMS/WIDESEA_WMSServer/WIDESEA_StockService/StockSerivce.cs
@@ -102,6 +102,8 @@
        public async Task<WebResponseContent> GroupPalletAsync(StockDTO stock)
        {
            WebResponseContent content = new WebResponseContent();
            try
            {
            var now = DateTime.Now;
            var details = stock.Details.Select(item => new Dt_StockInfoDetail
            {
@@ -143,6 +145,11 @@
                return result ? content.OK("组盘成功") : content.Error("组盘失败");
            });
        }
            catch (Exception ex)
            {
                return content.Error($"组盘失败: {ex.Message}");
            }
        }
        /// <summary>
        /// æ¢ç›˜
@@ -150,6 +157,8 @@
        public async Task<WebResponseContent> ChangePalletAsync(StockDTO stock)
        {
            WebResponseContent content = new WebResponseContent();
            try
            {
            if (stock == null ||
                string.IsNullOrWhiteSpace(stock.TargetPalletNo) ||
                string.IsNullOrWhiteSpace(stock.SourcePalletNo) ||
@@ -200,6 +209,11 @@
                return content.OK("换盘成功");
            });
        }
            catch (Exception ex)
            {
                return content.Error($"换盘失败: {ex.Message}");
            }
        }
        /// <summary>
        /// æ‹†ç›˜
@@ -207,6 +221,8 @@
        public async Task<WebResponseContent> SplitPalletAsync(StockDTO stock)
        {
            WebResponseContent content = new WebResponseContent();
            try
            {
            if (stock == null || string.IsNullOrWhiteSpace(stock.SourcePalletNo))
                return content.Error("源托盘号不能为空");
@@ -239,6 +255,11 @@
                return content.OK("拆盘成功");
            });
        }
            catch (Exception ex)
            {
                return content.Error($"拆盘失败: {ex.Message}");
            }
        }
        /// <summary>
        /// å †åž›æœºæ¢ç›˜åŽæ›´æ–°åº“存信息(清空库位信息)
ÏîÄ¿×ÊÁÏ/É豸ЭÒé/ÉÏλϵͳ¶Ô½Ó/ÉÂÎ÷Çê¿ÌÄÜÔ´¿Æ¼¼MESϵͳ¶Ô½Ó½Ó¿Ú.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,329 @@
好的,我已经将您提供的五个接口文档优化为更清晰、结构更统一的Markdown格式。主要优化点包括:
1.  **统一格式**:将所有接口的说明、请求字段、响应字段等采用一致的标题层级和表格结构。
2.  **修正错误**:修正了“托盘出站”接口名称和“适用工序”中的明显笔误。
3.  **增强可读性**:使用加粗、代码块等Markdown元素,使关键信息和报文示例更突出。
4.  **补充说明**:在报文示例中,将`body`部分用JSON代码块高亮显示,便于阅读。
---
### **1.1. æ‰˜ç›˜ç”µèŠ¯ç»‘å®š**
#### **1.1.1. è§¦å‘条件**
1.  ç”¨äºŽç”µèŠ¯ç ç»‘å®šæ‰˜ç›˜ç ã€‚
#### **1.1.2. æŽ¥å£è¯´æ˜Ž**
| é¡¹ç›®         | å†…容                                     |
| :----------- | :--------------------------------------- |
| **接口名称** | æ‰˜ç›˜ç”µèŠ¯ç»‘å®šï¼ˆåœ¨åˆ¶å“&容器)              |
| **接口方式** | WebApi                                   |
| **请求方式** | POST                                     |
| **发送方**   | EQP                                      |
| **接收方**   | MES                                      |
| **接口地址** | `/EquipmentService/api/v1/BindContainer` |
| **适用工序** | ç”µèŠ¯ç ç»‘å®šæ‰˜ç›˜ç                          |
#### **1.1.3. è¯·æ±‚报文**
**Header å­—段**
| åºå· | å­—段            | å†…容        | æ•°æ®ç±»åž‹ | å¤‡æ³¨        |
| :--- | :-------------- | :---------- | :------- | :---------- |
| 1    | `Authorization` | MES认证信息 | STRING   | å€¼ç”±MES提供 |
**Body å­—段**
| åºå· | å­—段               | å†…容               | æ•°æ®ç±»åž‹                               | å¤‡æ³¨                   |
| :--- | :----------------- | :----------------- | :------------------------------------- | :--------------------- |
| 1    | `EquipmentCode`    | è®¾å¤‡ç¼–码           | STRING                                 |                        |
| 2    | `ResourceCode`     | èµ„源编码           | STRING                                 |                        |
| 3    | `LocalTime`        | è°ƒç”¨æœ¬åœ°æ—¶é—´       | DATETIME                               |                        |
| 4    | `ContainerCode`    | æ‰˜ç›˜ç              | STRING                                 |                        |
| 5    | `ContainerSfcList` | ç»‘定的电芯条码列表 | ARRAY OBJECT                           |                        |
| 6    | `OperationType`    | æ“ä½œç±»åž‹           | 0-默认<br>1-进站<br>2-出站<br>3-进出站 | æ¢æ‹˜æŸæ‰˜ç›˜ä¸Šä¼ 3,其余0 |
**`ContainerSfcList` æ•°æ®é›†å­—段**
| åºå· | å­—段       | å†…容     | æ•°æ®ç±»åž‹ | å¤‡æ³¨ |
| :--- | :--------- | :------- | :------- | :--- |
| 1    | `Sfc`      | ç”µèŠ¯ç    | STRING   |      |
| 2    | `Location` | ä½ç½®ä¿¡æ¯ | STRING   |      |
**报文示例**
```json
{
  "equipmentCode": "string",
  "resourceCode": "string",
  "localTime": "2024-03-01T03:12:29.265Z",
  "containerCode": "string",
  "containerSfcList": [
    {
      "sfc": "string",
      "location": "string"
    }
  ]
}
```
#### **1.1.4. å“åº”字段**
| å­—段   | å†…容     | æ•°æ®ç±»åž‹ | å¤‡æ³¨                  |
| :----- | :------- | :------- | :-------------------- |
| `code` | æ‰§è¡Œä»£ç  | INT      | 0: æˆåŠŸ<br>其他: å¤±è´¥ |
| `msg`  | è¿”回信息 | STRING   | åŒ…含具体的错误信息    |
---
### **1.2. æ‰˜ç›˜ç”µèŠ¯è§£ç»‘**
#### **1.2.1. è§¦å‘条件**
1.  ç”¨äºŽæ‰˜ç›˜ç è§£ç»‘电芯组。
#### **1.2.2. æŽ¥å£è¯´æ˜Ž**
| é¡¹ç›®         | å†…容                                       |
| :----------- | :----------------------------------------- |
| **接口名称** | æ‰˜ç›˜ç”µèŠ¯è§£ç»‘ï¼ˆåœ¨åˆ¶å“&容器)                |
| **接口方式** | WebApi                                     |
| **请求方式** | POST                                       |
| **发送方**   | EQP                                        |
| **接收方**   | MES                                        |
| **接口地址** | `/EquipmentService/api/v1/UnBindContainer` |
| **适用工序** | æ‰˜ç›˜ç è§£ç»‘电芯组                           |
#### **1.2.3. è¯·æ±‚报文**
**Header å­—段**
| åºå· | å­—段            | å†…容        | æ•°æ®ç±»åž‹ | å¤‡æ³¨        |
| :--- | :-------------- | :---------- | :------- | :---------- |
| 1    | `Authorization` | MES认证信息 | STRING   | å€¼ç”±MES提供 |
**Body å­—段**
| åºå· | å­—段            | å†…容         | æ•°æ®ç±»åž‹     | å¤‡æ³¨ |
| :--- | :-------------- | :----------- | :----------- | :--- |
| 1    | `EquipmentCode` | è®¾å¤‡ç¼–码     | STRING       |      |
| 2    | `ResourceCode`  | èµ„源编码     | STRING       |      |
| 3    | `LocalTime`     | è°ƒç”¨æœ¬åœ°æ—¶é—´ | DATETIME     |      |
| 4    | `ContainCode`   | æ‰˜ç›˜ç        | STRING       |      |
| 5    | `SfcList`       | ç”µèŠ¯æ¡ç ç»„   | ARRAY STRING |      |
**报文示例**
```json
{
  "equipmentCode": "string",
  "resourceCode": "string",
  "localTime": "2024-03-01T03:13:12.482Z",
  "containCode": "string",
  "sfcList": [
    "string"
  ]
}
```
#### **1.2.4. å“åº”字段**
| å­—段   | å†…容     | æ•°æ®ç±»åž‹ | å¤‡æ³¨                  |
| :----- | :------- | :------- | :-------------------- |
| `code` | æ‰§è¡Œä»£ç  | INT      | 0: æˆåŠŸ<br>其他: å¤±è´¥ |
| `msg`  | è¿”回信息 | STRING   | åŒ…含具体的错误信息    |
---
### **1.3. æ‰˜ç›˜NG电芯上报**
#### **1.3.1. è§¦å‘条件**
1.  æ‰˜ç›˜å­˜åœ¨NG条码,在拆盘或者OCV2, OCV3时,需要上报NG电芯。
#### **1.3.2. æŽ¥å£è¯´æ˜Ž**
| é¡¹ç›®         | å†…容                                         |
| :----------- | :------------------------------------------- |
| **接口名称** | æ¡ç ç»‘定(在制品&容器)                      |
| **接口方式** | WebApi                                       |
| **请求方式** | POST                                         |
| **发送方**   | EQP                                          |
| **接收方**   | MES                                          |
| **接口地址** | `/EquipmentService/api/v1/ContainerNgReport` |
| **适用工序** | æ‰˜ç›˜NG电芯上报                               |
#### **1.3.3. è¯·æ±‚报文**
**Header å­—段**
| åºå· | å­—段            | å†…容        | æ•°æ®ç±»åž‹ | å¤‡æ³¨        |
| :--- | :-------------- | :---------- | :------- | :---------- |
| 1    | `Authorization` | MES认证信息 | STRING   | å€¼ç”±MES提供 |
**Body å­—段**
| åºå· | å­—段            | å†…容                 | æ•°æ®ç±»åž‹     | å¤‡æ³¨ |
| :--- | :-------------- | :------------------- | :----------- | :--- |
| 1    | `EquipmentCode` | è®¾å¤‡ç¼–码             | STRING       |      |
| 2    | `ResourceCode`  | èµ„源编码             | STRING       |      |
| 3    | `LocalTime`     | è°ƒç”¨æœ¬åœ°æ—¶é—´         | DATETIME     |      |
| 4    | `ContainerCode` | æ‰˜ç›˜ç                | STRING       |      |
| 5    | `NgSfcList`     | ç»‘定NG的电芯条码列表 | ARRAY OBJECT |      |
**`NgSfcList` æ•°æ®é›†å­—段**
| åºå· | å­—段              | å†…容     | æ•°æ®ç±»åž‹ | å¤‡æ³¨ |
| :--- | :---------------- | :------- | :------- | :--- |
| 1    | `sfc`             | äº§å“æ¡ç  | STRING   |      |
| 2    | `ngCode`          | NG代码   | STRING   |      |
| 3    | `ngEquipmentCode` | NG设备   | STRING   |      |
| 4    | `ngResourceCode`  | NG资源   | STRING   |      |
**报文示例**
```json
{
  "equipmentCode": "string",
  "resourceCode": "string",
  "localTime": "2024-03-01T03:42:55.528Z",
  "containerCode": "string",
  "ngSfcList": [
    "string"
  ]
}
```
#### **1.3.4. å“åº”字段**
| å­—段   | å†…容     | æ•°æ®ç±»åž‹ | å¤‡æ³¨                  |
| :----- | :------- | :------- | :-------------------- |
| `code` | æ‰§è¡Œä»£ç  | INT      | 0: æˆåŠŸ<br>其他: å¤±è´¥ |
| `msg`  | è¿”回信息 | STRING   | åŒ…含具体的错误信息    |
---
### **1.4. æ‰˜ç›˜è¿›ç«™ï¼ˆå®¹å™¨è¿›ç«™ï¼‰**
#### **1.4.1. è§¦å‘条件**
1.  æ‰˜ç›˜è¿›ç«™ã€‚
#### **1.4.2. æŽ¥å£è¯´æ˜Ž**
| é¡¹ç›®         | å†…容                                          |
| :----------- | :-------------------------------------------- |
| **接口名称** | æ‰˜ç›˜è¿›ç«™ï¼ˆå®¹å™¨è¿›ç«™ï¼‰                          |
| **接口方式** | WebApi                                        |
| **请求方式** | POST                                          |
| **发送方**   | EQP                                           |
| **接收方**   | MES                                           |
| **接口地址** | `/EquipmentService/api/v1/InboundInContainer` |
| **适用工序** | æ‰˜ç›˜è¿›ç«™                                      |
#### **1.4.3. è¯·æ±‚报文**
**Header å­—段**
| åºå· | å­—段            | å†…容        | æ•°æ®ç±»åž‹ | å¤‡æ³¨        |
| :--- | :-------------- | :---------- | :------- | :---------- |
| 1    | `Authorization` | MES认证信息 | STRING   | å€¼ç”±MES提供 |
**Body å­—段**
| åºå· | å­—段            | å†…容         | æ•°æ®ç±»åž‹ | å¤‡æ³¨ |
| :--- | :-------------- | :----------- | :------- | :--- |
| 1    | `EquipmentCode` | è®¾å¤‡ç¼–码     | STRING   |      |
| 2    | `ResourceCode`  | èµ„源编码     | STRING   |      |
| 3    | `LocalTime`     | è°ƒç”¨æœ¬åœ°æ—¶é—´ | DATETIME |      |
| 4    | `ContainerCode` | æ‰˜ç›˜ç        | STRING   |      |
**报文示例**
```json
{
  "equipmentCode": "string",
  "resourceCode": "string",
  "localTime": "2024-03-01T03:43:42.144Z",
  "containerCode": "string"
}
```
#### **1.4.4. å“åº”字段**
| å­—段   | å†…容     | æ•°æ®ç±»åž‹ | å¤‡æ³¨                  |
| :----- | :------- | :------- | :-------------------- |
| `code` | æ‰§è¡Œä»£ç  | INT      | 0: æˆåŠŸ<br>其他: å¤±è´¥ |
| `msg`  | è¿”回信息 | STRING   | åŒ…含具体的错误信息    |
---
### **1.5. æ‰˜ç›˜å‡ºç«™ï¼ˆå®¹å™¨å‡ºç«™ï¼‰**
#### **1.5.1. è§¦å‘条件**
1.  æ‰˜ç›˜å‡ºç«™ã€‚
#### **1.5.2. æŽ¥å£è¯´æ˜Ž**
| é¡¹ç›®         | å†…容                                           |
| :----------- | :--------------------------------------------- |
| **接口名称** | æ‰˜ç›˜å‡ºç«™ï¼ˆå®¹å™¨å‡ºç«™ï¼‰                           |
| **接口方式** | WebApi                                         |
| **请求方式** | POST                                           |
| **发送方**   | EQP                                            |
| **接收方**   | MES                                            |
| **接口地址** | `/EquipmentService/api/v1/OutboundInContainer` |
| **适用工序** | æ‰˜ç›˜å‡ºç«™                                       |
#### **1.5.3. è¯·æ±‚报文**
**Header å­—段**
| åºå· | å­—段            | å†…容        | æ•°æ®ç±»åž‹ | å¤‡æ³¨        |
| :--- | :-------------- | :---------- | :------- | :---------- |
| 1    | `Authorization` | MES认证信息 | STRING   | å€¼ç”±MES提供 |
**Body å­—段**
| åºå· | å­—段            | å†…容         | æ•°æ®ç±»åž‹     | å¤‡æ³¨ |
| :--- | :-------------- | :----------- | :----------- | :--- |
| 1    | `EquipmentCode` | è®¾å¤‡ç¼–码     | STRING       |      |
| 2    | `ResourceCode`  | èµ„源编码     | STRING       |      |
| 3    | `LocalTime`     | è°ƒç”¨æœ¬åœ°æ—¶é—´ | DATETIME     |      |
| 4    | `ContainerCode` | æ‰˜ç›˜ç        | STRING       |      |
| 5    | `ParamList`     | äº§å“å‚数列表 | ARRAY OBJECT |      |
**`ParamList` æ•°æ®é›†å­—段**
| åºå· | å­—段             | å†…容     | æ•°æ®ç±»åž‹ | å¤‡æ³¨           |
| :--- | :--------------- | :------- | :------- | :------------- |
| 1    | `ParamCode`      | å‚数编码 | STRING   | å·¥è‰ºæä¾›       |
| 2    | `ParamValue`     | å‚数值   | STRING   |                |
| 3    | `CollectionTime` | æ—¶é—´æˆ³   | DATETIME | é‡‡é›†å‚数的时间 |
**报文示例**
```json
{
  "equipmentCode": "string",
  "resourceCode": "string",
  "localTime": "2024-03-01T03:43:42.144Z",
  "containerCode": "string",
  "paramList": [
    {
      "paramCode": "string",
      "paramValue": "string",
      "collectionTime": "2024-03-01T03:43:42.144Z"
    }
  ]
}
```
#### **1.5.4. å“åº”字段**
| å­—段   | å†…容     | æ•°æ®ç±»åž‹ | å¤‡æ³¨                  |
| :----- | :------- | :------- | :-------------------- |
| `code` | æ‰§è¡Œä»£ç  | INT      | 0: æˆåŠŸ<br>其他: å¤±è´¥ |
| `msg`  | è¿”回信息 | STRING   | åŒ…含具体的错误信息    |