wanshenmean
2026-03-06 ff967e1e9fa01b5c499d87a9583daf28b89279da
添加自动出库任务实现计划

- 包含10个详细的任务步骤
- 每个步骤包含具体的代码和命令
- 遵循TDD原则和频繁提交
已添加1个文件
729 ■■■■■ 文件已修改
Code/WMS/WIDESEA_WMSServer/docs/plans/2026-03-06-auto-outbound-task-implementation.md 729 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
Code/WMS/WIDESEA_WMSServer/docs/plans/2026-03-06-auto-outbound-task-implementation.md
¶Ô±ÈÐÂÎļþ
@@ -0,0 +1,729 @@
# è‡ªåŠ¨å‡ºåº“ä»»åŠ¡åŽå°æœåŠ¡å®žçŽ°è®¡åˆ’
> **For Claude:** REQUIRED SUB-SKILL: Use superpowers:executing-plans to implement this plan task-by-task.
**目标:** å®žçŽ°ä¸€ä¸ªåŽå°æœåŠ¡ï¼Œè‡ªåŠ¨æ£€æµ‹åˆ°æœŸåº“å­˜å¹¶åˆ›å»ºå‡ºåº“ä»»åŠ¡é€šçŸ¥ WCS ç³»ç»Ÿ
**架构:** ä½¿ç”¨ BackgroundService æ¨¡å¼å®žçŽ°å®šæ—¶ä»»åŠ¡ï¼Œé€šè¿‡ ITaskService æŸ¥è¯¢åˆ°æœŸåº“存、创建任务并通知 WCS
**技术栈:** .NET 6, BackgroundService, SqlSugar, Autofac, appsettings.json é…ç½®
---
## å‰ç½®æ£€æŸ¥
在开始实现前,请确认:
- é¡¹ç›®ä½äºŽ: `d:\Git\ShanMeiXinNengYuan\Code\WMS\WIDESEA_WMSServer`
- ä¸»é¡¹ç›®æ–‡ä»¶: `WIDESEA_WMSServer\WIDESEA_WMSServer.csproj`
- å·²é˜…读设计文档: `docs/plans/2026-03-06-auto-outbound-task-design.md`
---
## Task 1: åˆ›å»ºé…ç½®æ¨¡åž‹ç±»
**Files:**
- Create: `WIDESEA_Core/Core/AutoOutboundTaskOptions.cs`
**Step 1: åˆ›å»ºé…ç½®æ¨¡åž‹ç±»**
创建文件 `WIDESEA_Core/Core/AutoOutboundTaskOptions.cs`:
```csharp
using System.Collections.Generic;
namespace WIDESEA_Core.Core
{
    /// <summary>
    /// è‡ªåŠ¨å‡ºåº“ä»»åŠ¡é…ç½®é€‰é¡¹
    /// </summary>
    public class AutoOutboundTaskOptions
    {
        /// <summary>
        /// æ˜¯å¦å¯ç”¨è‡ªåŠ¨å‡ºåº“ä»»åŠ¡
        /// </summary>
        public bool Enable { get; set; } = true;
        /// <summary>
        /// æ£€æŸ¥é—´éš”(秒)
        /// </summary>
        public int CheckIntervalSeconds { get; set; } = 300;
        /// <summary>
        /// æŒ‰å··é“前缀配置目标地址
        /// </summary>
        public Dictionary<string, string> TargetAddresses { get; set; }
            = new()
            {
                { "GW", "10081" },
                { "CW", "10080" }
            };
    }
}
```
**Step 2: æäº¤é…ç½®æ¨¡åž‹ç±»**
```bash
git add WIDESEA_Core/Core/AutoOutboundTaskOptions.cs
git commit -m "feat: æ·»åŠ è‡ªåŠ¨å‡ºåº“ä»»åŠ¡é…ç½®æ¨¡åž‹ç±»"
```
---
## Task 2: æ›´æ–° appsettings.json é…ç½®
**Files:**
- Modify: `WIDESEA_WMSServer/appsettings.json`
**Step 1: æ·»åŠ é…ç½®èŠ‚**
在 `appsettings.json` ä¸­æ·»åŠ  `AutoOutboundTask` é…ç½®èŠ‚ï¼ˆåœ¨ `WebSocketPort` åŽé¢ï¼‰:
```json
{
  ...,
  "WebSocketPort": 9296,
  "AutoOutboundTask": {
    "Enable": true,
    "CheckIntervalSeconds": 300,
    "TargetAddresses": {
      "GW": "10081",
      "CW": "10080"
    }
  }
}
```
**Step 2: æäº¤é…ç½®æ›´æ–°**
```bash
git add WIDESEA_WMSServer/appsettings.json
git commit -m "config: æ·»åŠ è‡ªåŠ¨å‡ºåº“ä»»åŠ¡é…ç½®"
```
---
## Task 3: åœ¨ ITaskService æŽ¥å£ä¸­æ·»åŠ æ–¹æ³•å®šä¹‰
**Files:**
- Modify: `WIDESEA_ITaskInfoService/ITaskService.cs`
**Step 1: æ·»åŠ æŽ¥å£æ–¹æ³•**
在 `ITaskService.cs` ä¸­æ·»åŠ æ–¹æ³•å®šä¹‰ï¼ˆåœ¨æ–‡ä»¶æœ«å°¾ï¼Œ`}` ä¹‹å‰ï¼‰:
```csharp
/// <summary>
/// è‡ªåŠ¨åˆ›å»ºå‡ºåº“ä»»åŠ¡ - æŸ¥è¯¢åˆ°æœŸåº“存并创建任务
/// </summary>
/// <returns>包含创建结果的响应对象</returns>
Task<WebResponseContent> CreateAutoOutboundTasksAsync();
```
**Step 2: æäº¤æŽ¥å£æ›´æ–°**
```bash
git add WIDESEA_ITaskInfoService/ITaskService.cs
git commit -m "feat: æ·»åŠ è‡ªåŠ¨å‡ºåº“ä»»åŠ¡åˆ›å»ºæŽ¥å£æ–¹æ³•"
```
---
## Task 4: åœ¨ TaskService ä¸­å®žçŽ°æ ¸å¿ƒé€»è¾‘
**Files:**
- Modify: `WIDESEA_TaskInfoService/TaskService.cs`
**Step 1: æ·»åŠ ä¾èµ–æ³¨å…¥å­—æ®µ**
在 `TaskService` ç±»çš„私有字段区域(第 20-25 è¡Œé™„近)添加:
```csharp
private readonly IConfiguration _configuration;
```
修改构造函数签名(第 37-48 è¡Œï¼‰ï¼Œæ·»åŠ  `IConfiguration` å‚æ•°:
```csharp
public TaskService(
    IRepository<Dt_Task> BaseDal,
    IMapper mapper,
    IStockInfoService stockInfoService,
    ILocationInfoService locationInfoService,
    HttpClientHelper httpClientHelper,
    IConfiguration configuration) : base(BaseDal)
{
    _mapper = mapper;
    _stockInfoService = stockInfoService;
    _locationInfoService = locationInfoService;
    _httpClientHelper = httpClientHelper;
    _configuration = configuration;
}
```
**Step 2: æ·»åŠ åœ°å€æ˜ å°„è¾…åŠ©æ–¹æ³•**
在 `TaskService` ç±»ä¸­æ·»åŠ ç§æœ‰æ–¹æ³•ï¼ˆåœ¨ `GetTasksByPalletCodeAsync` æ–¹æ³•后面,`#endregion` ä¹‹å‰ï¼‰:
```csharp
/// <summary>
/// æ ¹æ®å··é“确定目标地址
/// </summary>
private string DetermineTargetAddress(string roadway, Dictionary<string, string> addressMap)
{
    if (string.IsNullOrWhiteSpace(roadway))
        return "10080"; // é»˜è®¤åœ°å€
    foreach (var kvp in addressMap)
    {
        if (roadway.Contains(kvp.Key))
            return kvp.Value;
    }
    return "10080"; // é»˜è®¤åœ°å€
}
```
**Step 3: å®žçŽ°è‡ªåŠ¨å‡ºåº“ä»»åŠ¡åˆ›å»ºæ–¹æ³•**
在 `TaskService` ç±»çš„ `#endregion WCS逻辑处理` ä¹‹å‰æ·»åŠ å®Œæ•´æ–¹æ³•:
```csharp
/// <summary>
/// è‡ªåŠ¨åˆ›å»ºå‡ºåº“ä»»åŠ¡ - æŸ¥è¯¢åˆ°æœŸåº“存并创建任务
/// </summary>
public async Task<WebResponseContent> CreateAutoOutboundTasksAsync()
{
    try
    {
        // 1. æŸ¥è¯¢åˆ°æœŸåº“å­˜
        var expiredStocks = await _stockInfoService.Repository
            .QueryAsync(s => s.OutboundDate <= DateTime.Now
                && s.StockStatus == StockStatusEmun.入库完成.GetHashCode());
        if (expiredStocks == null || !expiredStocks.Any())
        {
            return WebResponseContent.Instance.OK("无到期库存需要处理");
        }
        // åŠ è½½ä½ç½®è¯¦æƒ…
        foreach (var stock in expiredStocks)
        {
            if (stock.LocationId > 0)
            {
                stock.LocationDetails = await _locationInfoService.Repository
                    .GetFirstAsync(s => s.Id == stock.LocationId);
            }
        }
        // è¿‡æ»¤æœ‰ä½ç½®ä¸”位置有库存的记录
        expiredStocks = expiredStocks
            .Where(s => s.LocationDetails != null
                && s.LocationDetails.LocationStatus == LocationStatusEnum.InStock.GetHashCode())
            .ToList();
        if (!expiredStocks.Any())
        {
            return WebResponseContent.Instance.OK("无符合条件的到期库存");
        }
        // 2. æ£€æŸ¥å·²å­˜åœ¨çš„任务
        var palletCodes = expiredStocks.Select(s => s.PalletCode).ToList();
        var existingTasks = await Repository.QueryAsync(t =>
            palletCodes.Contains(t.PalletCode)
            && (t.TaskStatus == TaskStatusEnum.New.GetHashCode()
                || t.TaskStatus == TaskStatusEnum.Executing.GetHashCode()
                || t.TaskStatus == TaskInStatusEnum.InNew.GetHashCode()));
        var processedPallets = existingTasks.Select(t => t.PalletCode).ToHashSet();
        // 3. ç­›é€‰éœ€è¦å¤„理的库存
        var stocksToProcess = expiredStocks
            .Where(s => !processedPallets.Contains(s.PalletCode))
            .ToList();
        if (!stocksToProcess.Any())
        {
            return WebResponseContent.Instance.OK("所有到期库存已存在任务");
        }
        // 4. èŽ·å–é…ç½®çš„ç›®æ ‡åœ°å€æ˜ å°„
        var targetAddressMap = _configuration.GetSection("AutoOutboundTask:TargetAddresses")
            .Get<Dictionary<string, string>>()
            ?? new Dictionary<string, string>();
        // 5. æ‰¹é‡åˆ›å»ºä»»åŠ¡
        var taskList = new List<Dt_Task>();
        foreach (var stock in stocksToProcess)
        {
            // æ ¹æ®å··é“确定目标地址
            var targetAddress = DetermineTargetAddress(
                stock.LocationDetails?.RoadwayNo ?? "",
                targetAddressMap);
            var task = new Dt_Task
            {
                WarehouseId = stock.WarehouseId,
                PalletCode = stock.PalletCode,
                PalletType = stock.PalletType,
                SourceAddress = stock.LocationCode,
                CurrentAddress = stock.LocationCode,
                NextAddress = targetAddress,
                TargetAddress = targetAddress,
                Roadway = stock.LocationDetails?.RoadwayNo ?? "",
                TaskType = TaskTypeEnum.Outbound.GetHashCode(),
                TaskStatus = TaskStatusEnum.New.GetHashCode(),
                Grade = 1,
                TaskNum = await Repository.GetTaskNo(),
                Creater = "system_auto"
            };
            taskList.Add(task);
        }
        var addResult = await BaseDal.AddDataAsync(taskList) > 0;
        if (!addResult)
        {
            return WebResponseContent.Instance.Error($"批量创建任务失败,共 {taskList.Count} ä¸ªä»»åŠ¡");
        }
        // 6. é€šçŸ¥ WCS(异步,不影响主流程)
        _ = Task.Run(async () =>
        {
            foreach (var task in taskList)
            {
                try
                {
                    var wmstaskDto = _mapper.Map<WMSTaskDTO>(task);
                    await _httpClientHelper.Post<WebResponseContent>(
                        "http://logistics-service/api/logistics/notifyoutbound",
                        JsonSerializer.Serialize(wmstaskDto));
                }
                catch (Exception ex)
                {
                    // WCS é€šçŸ¥å¤±è´¥ä¸å½±å“ä»»åŠ¡åˆ›å»ºï¼Œè®°å½•æ—¥å¿—å³å¯
                    Console.WriteLine($"WCS é€šçŸ¥å¤±è´¥ï¼Œä»»åŠ¡ç¼–å·: {task.TaskNum}, é”™è¯¯: {ex.Message}");
                }
            }
        });
        return WebResponseContent.Instance.OK($"成功创建 {taskList.Count} ä¸ªå‡ºåº“任务", taskList.Count);
    }
    catch (Exception ex)
    {
        return WebResponseContent.Instance.Error($"自动创建出库任务失败: {ex.Message}");
    }
}
```
**Step 4: æäº¤å®žçް**
```bash
git add WIDESEA_TaskInfoService/TaskService.cs
git commit -m "feat: å®žçŽ°è‡ªåŠ¨å‡ºåº“ä»»åŠ¡åˆ›å»ºæ–¹æ³•"
```
---
## Task 5: åˆ›å»ºåŽå°æœåŠ¡ç±»
**Files:**
- Create: `WIDESEA_WMSServer/BackgroundServices/AutoOutboundTaskBackgroundService.cs`
**Step 1: åˆ›å»º BackgroundServices ç›®å½•**
```bash
mkdir -p WIDESEA_WMSServer/BackgroundServices
```
**Step 2: åˆ›å»ºåŽå°æœåŠ¡ç±»**
创建文件 `WIDESEA_WMSServer/BackgroundServices/AutoOutboundTaskBackgroundService.cs`:
```csharp
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using System;
using System.Threading;
using System.Threading.Tasks;
using WIDESEA_Core.Core;
using WIDESEA_ITaskInfoService;
namespace WIDESEA_WMSServer.BackgroundServices
{
    /// <summary>
    /// è‡ªåŠ¨å‡ºåº“ä»»åŠ¡åŽå°æœåŠ¡
    /// å®šæœŸæ£€æŸ¥åˆ°æœŸåº“存并创建出库任务
    /// </summary>
    public class AutoOutboundTaskBackgroundService : BackgroundService
    {
        private readonly ILogger<AutoOutboundTaskBackgroundService> _logger;
        private readonly ITaskService _taskService;
        private readonly AutoOutboundTaskOptions _options;
        public AutoOutboundTaskBackgroundService(
            ILogger<AutoOutboundTaskBackgroundService> logger,
            ITaskService taskService,
            IOptions<AutoOutboundTaskOptions> options)
        {
            _logger = logger;
            _taskService = taskService;
            _options = options.Value;
        }
        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {
            _logger.LogInformation("自动出库任务后台服务已启动");
            if (!_options.Enable)
            {
                _logger.LogInformation("自动出库任务功能已禁用,服务退出");
                return;
            }
            _logger.LogInformation("自动出库任务检查间隔: {Seconds} ç§’", _options.CheckIntervalSeconds);
            while (!stoppingToken.IsCancellationRequested)
            {
                try
                {
                    _logger.LogDebug("开始检查到期库存...");
                    var result = await _taskService.CreateAutoOutboundTasksAsync();
                    if (result.Status)
                    {
                        _logger.LogInformation("到期库存检查完成: {Message}", result.Message);
                    }
                    else
                    {
                        _logger.LogWarning("到期库存检查失败: {Message}", result.Message);
                    }
                }
                catch (Exception ex)
                {
                    _logger.LogError(ex, "自动出库任务创建过程中发生异常");
                }
                var delay = TimeSpan.FromSeconds(_options.CheckIntervalSeconds);
                _logger.LogDebug("等待 {Seconds} ç§’后进行下次检查", delay.TotalSeconds);
                await Task.Delay(delay, stoppingToken);
            }
            _logger.LogInformation("自动出库任务后台服务已停止");
        }
        public override async Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("正在停止自动出库任务后台服务...");
            await base.StopAsync(cancellationToken);
        }
    }
}
```
**Step 3: æäº¤åŽå°æœåŠ¡ç±»**
```bash
git add WIDESEA_WMSServer/BackgroundServices/AutoOutboundTaskBackgroundService.cs
git commit -m "feat: æ·»åŠ è‡ªåŠ¨å‡ºåº“ä»»åŠ¡åŽå°æœåŠ¡"
```
---
## Task 6: æ³¨å†ŒæœåŠ¡å’Œé…ç½®
**Files:**
- Modify: `WIDESEA_WMSServer/Program.cs`
**Step 1: æ·»åŠ é…ç½®æ³¨å†Œ**
在 `Program.cs` ä¸­çš„ `builder.Services.AddAllOptionRegister();` ä¹‹åŽæ·»åŠ :
```csharp
builder.Services.Configure<AutoOutboundTaskOptions>(
    builder.Configuration.GetSection("AutoOutboundTask"));
```
**Step 2: æ·»åŠ åŽå°æœåŠ¡æ³¨å†Œ**
在 `Program.cs` ä¸­çš„ `builder.Services.AddInitializationHostServiceSetup();` ä¹‹åŽæ·»åŠ :
```csharp
builder.Services.AddHostedService<AutoOutboundTaskBackgroundService>();
```
**Step 3: æ·»åŠ å‘½åç©ºé—´å¼•ç”¨**
在 `Program.cs` æ–‡ä»¶é¡¶éƒ¨çš„ using åŒºåŸŸæ·»åŠ :
```csharp
using WIDESEA_WMSServer.BackgroundServices;
using WIDESEA_Core.Core;
```
**Step 4: æäº¤ Program.cs æ›´æ–°**
```bash
git add WIDESEA_WMSServer/Program.cs
git commit -m "config: æ³¨å†Œè‡ªåŠ¨å‡ºåº“ä»»åŠ¡åŽå°æœåŠ¡å’Œé…ç½®"
```
---
## Task 7: ç¼–译验证
**Step 1: ç¼–译项目**
```bash
cd WIDESEA_WMSServer
dotnet build --configuration Release
```
预期输出: ç¼–译成功,无错误
**Step 2: æ£€æŸ¥ç¼–译输出**
确认输出包含:
```
Build succeeded.
    0 Warning(s)
    0 Error(s)
```
**Step 3: å¦‚果有错误,根据错误信息修复**
常见问题:
- ç¼ºå°‘ using å¼•用 â†’ æ·»åŠ ç›¸åº”çš„ namespace
- ç±»åž‹ä¸åŒ¹é… â†’ æ£€æŸ¥å±žæ€§åç§°å’Œç±»åž‹æ˜¯å¦æ­£ç¡®
---
## Task 8: æ‰‹åŠ¨æµ‹è¯•
**Step 1: å‡†å¤‡æµ‹è¯•数据**
在数据库中执行 SQL,创建一条测试库存记录:
```sql
-- ç¡®ä¿æœ‰ä¸€ä¸ªå·²å…¥åº“的库存记录,OutboundDate è®¾ç½®ä¸ºè¿‡åŽ»æ—¶é—´
UPDATE Dt_StockInfo
SET OutboundDate = DATEADD(MINUTE, -5, GETDATE())
WHERE PalletCode = 'TEST001' AND StockStatus = 1; -- 1 = å…¥åº“完成
-- å¦‚果没有测试记录,插入一条
INSERT INTO Dt_StockInfo (PalletCode, PalletType, LocationId, LocationCode, WarehouseId, StockStatus, OutboundDate, CreateDate)
VALUES ('TEST001', 1, 1, '01-01-01', 1, 1, DATEADD(MINUTE, -5, GETDATE()), GETDATE())
```
**Step 2: å¯åŠ¨åº”ç”¨ç¨‹åº**
```bash
cd WIDESEA_WMSServer
dotnet run
```
**Step 3: è§‚察控制台输出**
预期看到类似日志:
```
info: WIDESEA_WMSServer.BackgroundServices.AutoOutboundTaskBackgroundService[0]
      è‡ªåŠ¨å‡ºåº“ä»»åŠ¡åŽå°æœåŠ¡å·²å¯åŠ¨
info: WIDESEA_WMSServer.BackgroundServices.AutoOutboundTaskBackgroundService[0]
      è‡ªåŠ¨å‡ºåº“ä»»åŠ¡æ£€æŸ¥é—´éš”: 300 ç§’
...
info: WIDESEA_WMSServer.BackgroundServices.AutoOutboundTaskBackgroundService[0]
      åˆ°æœŸåº“存检查完成: æˆåŠŸåˆ›å»º X ä¸ªå‡ºåº“任务
```
**Step 4: éªŒè¯æ•°æ®åº“**
查询任务表确认任务已创建:
```sql
SELECT * FROM Dt_Task WHERE PalletCode = 'TEST001' AND Creater = 'system_auto'
```
**Step 5: æµ‹è¯•完成,清理测试数据**
```sql
-- åˆ é™¤æµ‹è¯•任务
DELETE FROM Dt_Task WHERE PalletCode = 'TEST001'
-- åˆ é™¤æµ‹è¯•库存
DELETE FROM Dt_StockInfo WHERE PalletCode = 'TEST001'
```
---
## Task 9: é…ç½®éªŒè¯æµ‹è¯•
**Step 1: æµ‹è¯•禁用功能**
修改 `appsettings.json`:
```json
"AutoOutboundTask": {
  "Enable": false,
  ...
}
```
**Step 2: å¯åŠ¨åº”ç”¨éªŒè¯**
```bash
dotnet run
```
预期输出应包含: `自动出库任务功能已禁用,服务退出`
**Step 3: æ¢å¤é…ç½®**
```json
"AutoOutboundTask": {
  "Enable": true,
  ...
}
```
**Step 4: æµ‹è¯•不同的检查间隔**
修改为 10 ç§’方便观察:
```json
"CheckIntervalSeconds": 10
```
启动应用,观察检查是否按 10 ç§’间隔执行
**Step 5: æäº¤æµ‹è¯•结论**
创建测试说明文件 `WIDESEA_WMSServer/BackgroundServices/README.md`:
```markdown
# è‡ªåŠ¨å‡ºåº“ä»»åŠ¡åŽå°æœåŠ¡
## åŠŸèƒ½è¯´æ˜Ž
本服务自动检测到期库存并创建出库任务。
## é…ç½®
在 `appsettings.json` ä¸­é…ç½®:
```json
{
  "AutoOutboundTask": {
    "Enable": true,
    "CheckIntervalSeconds": 300,
    "TargetAddresses": {
      "GW": "10081",
      "CW": "10080"
    }
  }
}
```
## æµ‹è¯•方法
1. è®¾ç½®åº“存的 OutboundDate ä¸ºè¿‡åŽ»æ—¶é—´
2. å¯åŠ¨åº”ç”¨ï¼Œè§‚å¯Ÿæ—¥å¿—
3. ç¡®è®¤ä»»åŠ¡å·²åˆ›å»ºåˆ°æ•°æ®åº“
```
**Step 6: æäº¤æµ‹è¯•文档**
```bash
git add WIDESEA_WMSServer/BackgroundServices/README.md
git commit -m "docs: æ·»åŠ åŽå°æœåŠ¡æµ‹è¯•è¯´æ˜Ž"
```
---
## Task 10: æœ€ç»ˆéªŒè¯å’Œæ–‡æ¡£
**Step 1: è¿è¡Œå®Œæ•´ç¼–译**
```bash
cd ..
dotnet build WIDESEA_WMSServer/WIDESEA_WMSServer.csproj --configuration Release
```
**Step 2: ç¡®è®¤æ‰€æœ‰æ–‡ä»¶å·²æäº¤**
```bash
git status
```
应该看到: `nothing to commit, working tree clean`
**Step 3: æŸ¥çœ‹æäº¤åŽ†å²**
```bash
git log --oneline -10
```
**Step 4: åˆ›å»ºåŠŸèƒ½æ€»ç»“**
更新设计文档,添加实施状态:
在 `docs/plans/2026-03-06-auto-outbound-task-design.md` æœ«å°¾æ·»åŠ :
```markdown
## å®žæ–½çŠ¶æ€
- [x] è®¾è®¡å®Œæˆ
- [x] é…ç½®æ¨¡åž‹ç±»åˆ›å»º
- [x] TaskService æ–¹æ³•实现
- [x] åŽå°æœåŠ¡ç±»åˆ›å»º
- [x] Program.cs æ³¨å†Œå®Œæˆ
- [x] ç¼–译验证通过
- [x] æ‰‹åŠ¨æµ‹è¯•å®Œæˆ
**实施日期**: 2026-03-06
**实施人**: Claude Code
```
**Step 5: æäº¤æ–‡æ¡£æ›´æ–°**
```bash
git add docs/plans/2026-03-06-auto-outbound-task-design.md
git commit -m "docs: æ›´æ–°è®¾è®¡æ–‡æ¡£å®žæ–½çŠ¶æ€"
```
---
## å®Œæˆæ£€æŸ¥æ¸…单
- [ ] æ‰€æœ‰ä»£ç æ–‡ä»¶å·²åˆ›å»º
- [ ] æ‰€æœ‰ä»£ç å·²ç¼–译通过
- [ ] é…ç½®æ–‡ä»¶å·²æ›´æ–°
- [ ] æ‰‹åŠ¨æµ‹è¯•å·²å®Œæˆ
- [ ] æ—¥å¿—输出符合预期
- [ ] æ•°æ®åº“验证通过
- [ ] æ–‡æ¡£å·²æ›´æ–°
- [ ] æ‰€æœ‰æ›´æ”¹å·²æäº¤åˆ° git
---
## æ•…障排查
### ç¼–译错误
**问题**: æ‰¾ä¸åˆ°ç±»åž‹æˆ–命名空间
**解决**: ç¡®ä¿å·²æ·»åŠ æ­£ç¡®çš„ using å¼•用
**问题**: ä¾èµ–注入失败
**解决**: æ£€æŸ¥ Program.cs ä¸­çš„æœåŠ¡æ³¨å†Œé¡ºåº
### è¿è¡Œæ—¶é”™è¯¯
**问题**: åŽå°æœåŠ¡æ²¡æœ‰å¯åŠ¨
**解决**:
1. æ£€æŸ¥ `appsettings.json` ä¸­ `Enable` æ˜¯å¦ä¸º `true`
2. æŸ¥çœ‹å¯åŠ¨æ—¥å¿—ä¸­çš„é”™è¯¯ä¿¡æ¯
**问题**: æ²¡æœ‰åˆ›å»ºä»»åŠ¡
**解决**:
1. ç¡®è®¤åº“存记录的 `OutboundDate` å·²è¿‡æœŸ
2. ç¡®è®¤åº“存状态为"入库完成"
3. æ£€æŸ¥æ˜¯å¦å·²å­˜åœ¨ç›¸åŒæ‰˜ç›˜çš„任务
**问题**: WCS é€šçŸ¥å¤±è´¥
**解决**:
1. æ£€æŸ¥ WCS æœåŠ¡æ˜¯å¦è¿è¡Œ
2. éªŒè¯é€šçŸ¥åœ°å€æ˜¯å¦æ­£ç¡®
3. æ³¨æ„: WCS é€šçŸ¥å¤±è´¥ä¸å½±å“ä»»åŠ¡åˆ›å»º