From 2a157fc570f544e5912f4ef83cef0a403af86f8f Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期三, 11 三月 2026 11:13:25 +0800
Subject: [PATCH] docs: 添加完整的WCS DDD重构实施计划

---
 Code/WCS/WIDESEAWCS_Server/docs/superpowers/plans/2026-03-11-wcs-ddd-refactor-plan.md | 1229 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 1,229 insertions(+), 0 deletions(-)

diff --git a/Code/WCS/WIDESEAWCS_Server/docs/superpowers/plans/2026-03-11-wcs-ddd-refactor-plan.md b/Code/WCS/WIDESEAWCS_Server/docs/superpowers/plans/2026-03-11-wcs-ddd-refactor-plan.md
new file mode 100644
index 0000000..d1c08c5
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_Server/docs/superpowers/plans/2026-03-11-wcs-ddd-refactor-plan.md
@@ -0,0 +1,1229 @@
+# WCS DDD閲嶆瀯瀹炴柦璁″垝
+
+> **For agentic workers:** REQUIRED: Use superpowers:subagent-driven-development (if subagents available) or superpowers:executing-plans to implement this plan. Steps use checkbox (`- [ ]`) syntax for tracking.
+
+**鐩爣:** 瀹炵幇WCS绯荤粺鐨勬笎杩涘紡DDD閲嶆瀯锛屽寘鎷鍩熷眰銆佸簲鐢ㄥ眰鍜屽熀纭�璁炬柦灞傦紝閲嶇偣閲嶆瀯璁惧绠$悊棰嗗煙
+
+**鏋舵瀯:** 閲囩敤鏍囧噯鐨勫洓灞侱DD鏋舵瀯锛氳〃鐜板眰 鈫� 搴旂敤灞� 鈫� 棰嗗煙灞� 鈫� 鍩虹璁炬柦灞傦紝浣跨敤CQRS妯″紡鍒嗙鍛戒护鍜屾煡璇紝棰嗗煙浜嬩欢椹卞姩璁捐
+
+**鎶�鏈爤:**
+- .NET 6.0
+- SqlSugar ORM
+- Redis锛圫tackExchange.Redis锛�
+- xUnit + Moq锛堟祴璇曟鏋讹級
+- Autofac锛堜緷璧栨敞鍏ワ級
+
+---
+
+## Chunk 1: 椤圭洰缁撴瀯鎼缓鍜屽熀纭�璁炬柦绫�
+
+### Task 1: 鍒涘缓棰嗗煙灞傞」鐩粨鏋�
+
+**Files:**
+- Create: `WIDESEAWCS_Domain/WIDESEAWCS_Domain.csproj`
+- Create: `WIDESEAWCS_Domain/Common/AggregateRoot.cs`
+- Create: `WIDESEAWCS_Domain/Common/IDomainEvent.cs`
+- Create: `WIDESEAWCS_Domain/Common/DomainEvent.cs`
+- Create: `WIDESEAWCS_Domain/Common/DomainException.cs`
+- Test: `WIDESEAWCS_Domain.Tests/AggregateRootTests.cs`
+
+- [ ] **Step 1: 鍒涘缓棰嗗煙灞傞」鐩枃浠�**
+
+```xml
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+</Project>
+```
+
+- [ ] **Step 2: 鍒涘缓鑱氬悎鏍瑰熀绫�**
+
+```csharp
+namespace WIDESEAWCS_Domain.Common;
+
+public abstract class AggregateRoot<TId>
+{
+    public TId Id { get; protected set; } = default!;
+    public int Version { get; private set; }
+    private readonly List<IDomainEvent> _domainEvents = new();
+
+    protected AggregateRoot() { }
+
+    protected AggregateRoot(TId id)
+    {
+        Id = id;
+    }
+
+    public IReadOnlyCollection<IDomainEvent> GetDomainEvents() => _domainEvents.AsReadOnly();
+    public void ClearDomainEvents() => _domainEvents.Clear();
+
+    protected void AddDomainEvent(IDomainEvent domainEvent)
+    {
+        _domainEvents.Add(domainEvent);
+    }
+
+    protected void IncrementVersion()
+    {
+        Version++;
+    }
+}
+```
+
+- [ ] **Step 3: 鍒涘缓棰嗗煙浜嬩欢鎺ュ彛**
+
+```csharp
+namespace WIDESEAWCS_Domain.Common;
+
+public interface IDomainEvent
+{
+    DateTime OccurredOn { get; }
+}
+```
+
+- [ ] **Step 4: 鍒涘缓棰嗗煙浜嬩欢鍩虹被**
+
+```csharp
+namespace WIDESEAWCS_Domain.Common;
+
+public abstract record DomainEvent : IDomainEvent
+{
+    public DateTime OccurredOn { get; init; } = DateTime.UtcNow;
+}
+```
+
+- [ ] **Step 5: 鍒涘缓棰嗗煙寮傚父绫�**
+
+```csharp
+namespace WIDESEAWCS_Domain.Common;
+
+public abstract class DomainException : Exception
+{
+    public string ErrorCode { get; }
+
+    protected DomainException(string message, string errorCode = "DOMAIN_ERROR")
+        : base(message)
+    {
+        ErrorCode = errorCode;
+        }
+}
+
+public class InvalidDomainOperationException : DomainException
+{
+    public InvalidDomainOperationException(string message)
+        : base(message, "INVALID_OPERATION")
+    {
+    }
+}
+```
+
+- [ ] **Step 6: 鍒涘缓鑱氬悎鏍瑰熀绫绘祴璇�**
+
+```csharp
+using Xunit;
+using WIDESEAWCS_Domain.Common;
+
+namespace WIDESEAWCS_Domain.Tests;
+
+public class AggregateRootTests
+{
+    [Fact]
+    public void Constructor_Should_SetId()
+    {
+        // Arrange & Act
+        var aggregate = new TestAggregate(1);
+
+        // Assert
+        Assert.Equal(1, aggregate.Id);
+    }
+
+    [Fact]
+    public void AddDomainEvent_Should_AddEvent()
+    {
+        // Arrange
+        var aggregate = new TestAggregate(1);
+        var domainEvent = new TestDomainEvent();
+
+        // Act
+        aggregate.TestAddDomainEvent(domainEvent);
+
+        // Assert
+        var events = aggregate.GetDomainEvents();
+        Assert.Single(events);
+        Assert.Same(domainEvent, events.First());
+    }
+
+    [Fact]
+    public void ClearDomainEvents_Should_RemoveAllEvents()
+    {
+        // Arrange
+        var aggregate = new TestAggregate(1);
+        aggregate.TestAddDomainEvent(new TestDomainEvent());
+
+        // Act
+        aggregate.ClearDomainEvents();
+
+        // Assert
+        Assert.Empty(aggregate.GetDomainEvents());
+    }
+
+    [Fact]
+    public void IncrementVersion_Should_IncreaseVersion()
+    {
+        // Arrange
+        var aggregate = new TestAggregate(1);
+        var initialVersion = aggregate.Version;
+
+        // Act
+        aggregate.TestIncrementVersion();
+
+        // Assert
+        Assert.Equal(initialVersion + 1, aggregate.Version);
+    }
+
+    // 娴嬭瘯杈呭姪绫�
+    private class TestAggregate : AggregateRoot<int>
+    {
+        public TestAggregate(int id) : base(id) { }
+
+        public void TestAddDomainEvent(IDomainEvent domainEvent)
+        => AddDomainEvent(domainEvent);
+
+        public void TestIncrementVersion() => IncrementVersion();
+    }
+
+    private record TestDomainEvent : DomainEvent { }
+}
+```
+
+- [ ] **Step 7: 杩愯娴嬭瘯楠岃瘉鍩虹绫�**
+
+Run: `dotnet test WIDESEAWCS_Domain.Tests --filter "FullyQualifiedName~AggregateRootTests"`
+Expected: All tests pass
+
+- [ ] **Step 8: 鎻愪氦鍩虹绫�**
+
+```bash
+git add WIDESEAWCS_Domain/
+git commit -m "feat: 娣诲姞DDD鍩虹绫� - AggregateRoot, IDomainEvent, DomainException"
+```
+
+---
+
+### Task 2: 鍒涘缓搴旂敤灞傞」鐩粨鏋�
+
+**Files:**
+- Create: `WIDESEAWCS_Application/WIDESEAWCS_Application.csproj`
+- Create: `WIDESEAWCS_Application/Common/IRepository.cs`
+- Create: `WIDESEAWCS_Application/Common/IUnitOfWork.cs`
+
+- [ ] **Step 1: 鍒涘缓搴旂敤灞傞」鐩枃浠�**
+
+```xml
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\WIDESEAWCS_Domain\WIDESEAWCS_Domain.csproj" />
+  </ItemGroup>
+</Project>
+```
+
+- [ ] **Step 2: 鍒涘缓浠撳偍鍩烘帴鍙�**
+
+```csharp
+using WIDESEAWCS_Domain.Common;
+
+namespace WIDESEAWCS_Application.Common;
+
+public interface IRepository<TEntity, TId> where TEntity : AggregateRoot<TId>
+{
+    Task<TEntity?> GetById(TId id);
+    Task Add(TEntity entity);
+    Task Update(TEntity entity);
+    Task Delete(TId id);
+}
+```
+
+- [ ] **Step 3: 鍒涘缓宸ヤ綔鍗曞厓鎺ュ彛**
+
+```csharp
+namespace WIDESEAWCS_Application.Common;
+
+public interface IUnitOfWork : IDisposable
+{
+    ITransaction BeginTransaction();
+    Task Commit();
+    Task Rollback();
+}
+
+public interface ITransaction : IDisposable
+{
+    Task Commit();
+    Task Rollback();
+}
+```
+
+- [ ] **Step 4: 鎻愪氦搴旂敤灞傚熀纭�鎺ュ彛**
+
+```bash
+git add WIDESEAWCS_Application/
+git commit -m "feat: 娣诲姞搴旂敤灞傚熀纭�鎺ュ彛 - IRepository, IUnitOfWork"
+```
+
+---
+
+### Task 3: 鍒涘缓鍩虹璁炬柦灞傞」鐩粨鏋�
+
+**Files:**
+- Create: `WIDESEAWCS_Infrastructure/WIDESEAWCS_Infrastructure.csproj`
+- Create: `WIDESEAWCS_Infrastructure/Persistence/InMemoryUnitOfWork.cs`
+- Create: `WIDESEAWCS_Infrastructure/Persistence/InMemoryTransaction.cs`
+
+- [ ] **Step 1: 鍒涘缓**鍩虹璁炬柦灞傞」鐩枃浠�**
+
+```xml
+<Project Sdk="Microsoft.NET.Sdk">
+  <PropertyGroup>
+    <TargetFramework>net6.0</TargetFramework>
+    <ImplicitUsings>enable</ImplicitUsings>
+    <Nullable>enable</Nullable>
+  </PropertyGroup>
+
+  <ItemGroup>
+    <ProjectReference Include="..\WIDESEAWCS_Domain\WIDESEAWCS_Domain.csproj" />
+    <ProjectReference Include="..\WIDESEAWCS_Application\WIDESEAWCS_Application.csproj" />
+    <PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
+  </ItemGroup>
+</Project>
+```
+
+- [ ] **Step 2: 鍒涘缓鍐呭瓨宸ヤ綔鍗曞厓瀹炵幇**
+
+```csharp
+using WIDESEAWCS_Application.Common;
+
+namespace WIDESEAWCS_Infrastructure.Persistence;
+
+public class InMemoryUnitOfWork : IUnitOfWork
+{
+    private readonly List<Action> _operations = new();
+
+    public ITransaction BeginTransaction()
+    {
+        return new InMemoryTransaction(this);
+    }
+
+    public Task Commit()
+    {
+        foreach (var operation in _operations)
+        {
+            operation();
+        }
+        _operations.Clear();
+        return Task.CompletedTask;
+    }
+
+    public Task Rollback()
+    {
+        _operations.Clear();
+        return Task.CompletedTask;
+    }
+
+    public void Dispose()
+    {
+        _operations.Clear();
+    }
+}
+```
+
+- [ ] **Step 3: 鍒涘缓鍐呭瓨浜嬪姟瀹炵幇**
+
+```csharp
+using WIDESEAWCS_Application.Common;
+
+namespace WIDESEAWCS_Infrastructure.Persistence;
+
+public class InMemoryTransaction : ITransaction
+{
+    private readonly InMemoryUnitOfWork _unitOfWork;
+    private bool _committed;
+    private bool _rolledBack;
+
+    public InMemoryTransaction(InMemoryUnitOfWork unitOfWork)
+    {
+        _unitOfWork = unitOfWork;
+    }
+
+    public Task Commit()
+    {
+        if (_rolledBack)
+            throw new InvalidOperationException("Transaction already rolled back");
+
+        _committed = true;
+        return Task.CompletedTask;
+    }
+
+    public Task Rollback()
+    {
+        if (_committed)
+            throw new InvalidOperationException("Transaction already committed");
+
+        _rolledBack = true;
+        return _unitOfWork.Rollback();
+    }
+
+    public void Dispose()
+    {
+        if (!_committed && !_rolledBack)
+        {
+            Rollback().GetAwaiter().GetResult();
+        }
+    }
+}
+```
+
+- [ ] **Step 4: 鎻愪氦鍩虹璁炬柦灞傚熀纭�瀹炵幇**
+
+```bash
+git add WIDESEAWCS_Infrastructure/
+git commit -m "feat: 娣诲姞鍩虹璁炬柦灞傚熀纭�瀹炵幇 - InMemoryUnitOfWork, InMemoryTransaction"
+```
+
+---
+
+## Chunk 2: 璁惧绠$悊棰嗗煙瀹炵幇
+
+### Task 4: 瀹炵幇璁惧鍊煎璞�
+
+**Files:**
+- Create: `WIDESEAWCS_Domain/DeviceManagement/ValueObjects/DeviceId.cs`
+- Create: `WIDESEAWCS_Domain/DeviceManagement/ValueObjects/DeviceName.cs`
+- Create: `WIDESEAWCS_Domain/DeviceManagement/ValueObjects/DeviceAddress.cs`
+- Create: `WIDESEAWCS_Domain/DeviceManagement/ValueObjects/DeviceType.cs`
+- Create: `WIDESEAWCS_Domain/DeviceManagement/ValueObjects/DeviceStatus.cs`
+- Test: `WIDESEAWCS_Domain.Tests/DeviceManagement/ValueObjects/DeviceValueObjectsTests.cs`
+
+- [ ] **Step 1: 鍒涘缓璁惧ID鍊煎璞�**
+
+```csharp
+namespace WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+
+public record DeviceId(Guid Value)
+{
+    public static DeviceId New() => new DeviceId(Guid.NewGuid());
+    public static DeviceId From(Guid value) => new DeviceId(value);
+}
+```
+
+- [ ] **Step 2: 鍒涘缓璁惧鍚嶇О鍊煎璞�**
+
+```csharp
+namespace WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+
+public record DeviceName(string Value)
+{
+    public DeviceName(string value)
+    {
+        if (string.IsNullOrWhiteSpace(value))
+            throw new ArgumentException("璁惧鍚嶇О涓嶈兘涓虹┖", nameof(value));
+        if (value.Length > 100)
+            throw new ArgumentException("璁惧鍚嶇О闀垮害涓嶈兘瓒呰繃100涓瓧绗�", nameof(value));
+        Value = value;
+    }
+}
+```
+
+- [ ] **Step 3: 鍒涘缓璁惧鍦板潃鍊煎璞�**
+
+```csharp
+using System.Net;
+
+namespace WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+
+public record DeviceAddress(string Ip, int Port)
+{
+    public DeviceAddress(string ip, int port)
+    {
+        if (!IPAddress.TryParse(ip, out _))
+            throw new ArgumentException("IP鍦板潃鏍煎紡鏃犳晥", nameof(ip));
+        if (port < 1 || port > 65535)
+            throw new ArgumentException("绔彛鍙峰繀椤诲湪1-65535涔嬮棿", nameof(port));
+        Ip = ip;
+        Port = port;
+    }
+}
+```
+
+- [ ] **Step 4: 鍒涘缓璁惧绫诲瀷鏋氫妇**
+
+```csharp
+namespace WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+
+public enum DeviceType
+{
+    StackerCrane = 1,    // 鍫嗗灈鏈�
+    ConveyorLine = 2,    // 杈撻�佺嚎
+    ShuttleCar = 3,      // 绌挎杞�
+    Robot = 4,           // 鏈烘鎵�
+    AGV = 5              // 鑷姩瀵煎紩杞�
+}
+```
+
+- [ ] **Step 5: 鍒涘缓璁惧鐘舵�佹灇涓�**
+
+```csharp
+namespace WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+
+public enum DeviceStatus
+{
+    Disconnected = 0,    // 鏈繛鎺�
+    Connecting = 1,      // 杩炴帴涓�
+    Connected = 2,       // 宸茶繛鎺�
+    Busy = 3,            // 蹇欑
+    Error = 4,           // 閿欒
+    Maintenance = 5      // 缁存姢涓�
+}
+```
+
+- [ ] **Step 6: 鎻愪氦璁惧鍊煎璞�**
+
+```bash
+git add WIDESEAWCS_Domain/DeviceManagement/ValueObjects/
+git commit -m "feat: 娣诲姞璁惧鍊煎璞� - DeviceId, DeviceName, DeviceAddress, DeviceType, DeviceStatus"
+```
+
+---
+
+### Task 5: 瀹炵幇璁惧鑱氬悎鏍�
+
+**Files:**
+- Create: `WIDESEAWCS_Domain/DeviceManagement/Aggregates/Device.cs`
+- Create: `WIDESEAWCS_Domain/DeviceManagement/Events/DeviceConnectedEvent.cs`
+- Create: `WIDESEAWCS_Domain/DeviceManagement/Events/DeviceDisconnectedEvent.cs`
+- Create: `WIDESEAWCS_Domain/DeviceManagement/Events/DeviceHeartbeatEvent.cs`
+- Test: `WIDESEAWCS_Domain.Tests/DeviceManagement/Aggregates/DeviceTests.cs`
+
+- [ ] **Step 1: 鍒涘缓璁惧杩炴帴浜嬩欢**
+
+```csharp
+using WIDESEAWCS_Domain.Common;
+using WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+
+namespace WIDESEAWCS_Domain.DeviceManagement.Events;
+
+public record DeviceConnectedEvent : DomainEvent
+{
+    public DeviceId DeviceId { get; init; }
+    public DateTime ConnectedAt { get; init; } = DateTime.UtcNow;
+}
+```
+
+- [ ] **Step 2: 鍒涘缓璁惧鏂紑浜嬩欢**
+
+```csharp
+using WIDESEAWCS_Domain.Common;
+using WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+
+namespace WIDESEAWCS_Domain.DeviceManagement.Events;
+
+public record DeviceDisconnectedEvent : DomainEvent
+{
+    public DeviceId DeviceId { get; init; }
+    public string Reason { get; init; } = string.Empty;
+}
+```
+
+- [ ] **Step 3: 鍒涘缓璁惧蹇冭烦浜嬩欢**
+
+```csharp
+using WIDESEAWCS_Domain.Common;
+using WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+
+namespace WIDESEAWCS_Domain.DeviceManagement.Events;
+
+public record DeviceHeartbeatEvent : DomainEvent
+{
+    public DeviceId DeviceId { get; init; }
+    public DateTime HeartbeatAt { get; init; } = DateTime.UtcNow;
+}
+```
+
+- [ ] **Step 4: 鍒涘缓璁惧鑱氬悎鏍圭被**
+
+```csharp
+using WIDESEAWCS_Domain.Common;
+using WIDESEAWCS_Domain.DeviceManagement.Events;
+using WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+
+namespace WIDESEAW_Domain.DeviceManagement.Aggregates;
+
+public class Device : AggregateRoot<DeviceId>
+{
+    private DeviceId _id;
+    private DeviceName _name;
+    private DeviceType _type;
+    private DeviceStatus _status;
+    private DeviceAddress? _address;
+    private DateTime _lastConnectedAt;
+    private DateTime _lastHeartbeatAt;
+    private string? _errorMessage;
+
+    public Device(DeviceId id, DeviceName name, DeviceType type)
+    {
+        _id = id;
+        _name = name;
+        _type = type;
+        _status = DeviceStatus.Disconnected;
+        _lastConnectedAt = DateTime.MinValue;
+        _lastHeartbeatAt = DateTime.MinValue;
+    }
+
+    // 鍏叡灞炴�ц闂櫒
+    public DeviceId Id => _id;
+    public DeviceName Name => _name;
+    public DeviceType Type => _type;
+    public DeviceStatus Status => _status;
+    public DeviceAddress? Address => _address;
+    public DateTime LastConnectedAt => _lastConnectedAt;
+    public DateTime LastHeartbeatAt => _lastHeartbeatAt;
+    public string? ErrorMessage => _errorMessage;
+
+    // 琛屼负鏂规硶
+    public void Connect()
+    {
+        if (_status == DeviceStatus.Connected)
+            throw new InvalidDomainOperationException("璁惧宸茶繛鎺ワ紝鏃犳硶閲嶅杩炴帴");
+
+        _status = DeviceStatus.Connected;
+        _lastConnectedAt = DateTime.UtcNow;
+        _errorMessage = null;
+        IncrementVersion();
+
+        AddDomainEvent(new DeviceConnectedEvent
+        {
+            DeviceId = _id,
+            ConnectedAt = _lastConnectedAt
+        });
+    }
+
+    public void Disconnect(string reason)
+    {
+        if (_status == DeviceStatus.Disconnected)
+            throw new InvalidDomainOperationException("璁惧宸叉柇寮�锛屾棤娉曢噸澶嶆柇寮�");
+
+        _status = DeviceStatus.Disconnected;
+        _errorMessage = reason;
+        IncrementVersion();
+
+        AddDomainEvent(new DeviceDisconnectedEvent
+        {
+            DeviceId = _id,
+            Reason = reason
+        });
+    }
+
+    public void UpdateHeartbeat()
+    {
+        if (_status != DeviceStatus.Connected && _status != DeviceStatus.Busy)
+            throw new InvalidDomainOperationException("璁惧鏈繛鎺ユ垨蹇欑锛屾棤娉曟洿鏂板績璺�");
+
+        _lastHeartbeatAt = DateTime.UtcNow;
+        IncrementVersion();
+
+        AddDomainEvent(new DeviceHeartbeatEvent
+        {
+            DeviceId = _id,
+            HeartbeatAt = _lastHeartbeatAt
+        });
+    }
+
+    public void SetAddress(DeviceAddress address)
+    {
+        _address = address;
+        IncrementVersion();
+    }
+
+    // 鍐呴儴鏂规硶锛堜緵棰嗗煙鏈嶅姟浣跨敤锛�
+    internal void SetStatus(DeviceStatus status)
+    {
+        _status = status;
+    }
+
+    internal void SetError(string errorMessage)
+    {
+        _status = DeviceStatus.Error;
+        _errorMessage = errorMessage;
+        IncrementVersion();
+    }
+}
+```
+
+- [ ] **Step 5: 鎻愪氦璁惧鑱氬悎鏍瑰疄鐜�**
+
+```bash
+git add WIDESEAWCS_Domain/DeviceManagement/
+git commit -m "feat: 瀹炵幇璁惧鑱氬悎鏍瑰拰棰嗗煙浜嬩欢"
+```
+
+---
+
+### Task 6: 瀹炵幇璁惧鐘舵�佹満
+
+**Files:**
+- Create: `WIDESEAWCS_Domain/DeviceManagement/Services/IDeviceStateMachine.cs`
+- Create: `WIDESEAWCS_Domain/DeviceManagement/Services/DeviceStateMachine.cs`
+
+- [ ] **Step 1: 鍒涘缓璁惧鐘舵�佹満鎺ュ彛**
+
+```csharp
+using WIDESEAWCS_Domain.DeviceManagement.Aggregates;
+using WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+
+namespace WIDESEAWCS_Domain.DeviceManagement.Services;
+
+public interface IDeviceStateMachine
+{
+    bool CanTransition(DeviceStatus current, DeviceStatus target);
+    void Transition(Device device, DeviceStatus target);
+}
+```
+
+- [ ] **Step 2: 鍒涘缓璁惧鐘舵�佹満瀹炵幇**
+
+```csharp
+using WIDESEAWCS_Domain.DeviceManagement.Aggregates;
+using WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+using WIDESEAWCS_Domain.Common;
+
+namespace WIDESEAWCS_Domain.DeviceManagement.Services;
+
+public class DeviceStateMachine : IDeviceStateMachine
+{
+    private static readonly Dictionary<DeviceStatus, HashSet<DeviceStatus>> _transitions = new()
+    {
+        [DeviceStatus.Disconnected] = new()
+        {
+            DeviceStatus.Connecting,
+            DeviceStatus.Maintenance
+        },
+        [DeviceStatus.Connecting] = new()
+        {
+            DeviceStatus.Connected,
+            DeviceStatus.Error
+        },
+        [DeviceStatus.Connected] = new()
+        {
+            DeviceStatus.Busy,
+            DeviceStatus.Disconnected,
+            DeviceStatus.Error,
+            DeviceStatus.Maintenance
+        },
+        [DeviceStatus.Busy] = new()
+        {
+            DeviceStatus.Connected,
+            DeviceStatus.Error
+        },
+        [DeviceStatus.Error] = new()
+        {
+            DeviceStatus.Disconnected,
+            DeviceStatus.Maintenance
+        },
+        [DeviceStatus.Maintenance] = new()
+        {
+            DeviceStatus.Disconnected
+        }
+    };
+
+    public bool CanTransition(DeviceStatus current, DeviceStatus target)
+    {
+        if (!_transitions.TryGetValue(current, out var allowed))
+            return false;
+
+        return allowed.Contains(target);
+    }
+
+    public void Transition(Device device, DeviceStatus target)
+    {
+        if (!CanTransition(device.Status, target))
+        {
+            throw new InvalidDomainOperationException(
+                $"鏃犳晥鐨勭姸鎬佽浆鎹�: {device.Status} -> {target}");
+        }
+
+        device.SetStatus(target);
+    }
+}
+```
+
+- [ ] **Step 3: 鎻愪氦璁惧鐘舵�佹満瀹炵幇**
+
+```bash
+git add WIDESEAWCS_Domain/DeviceManagement/Services/
+git commit -m "feat: 瀹炵幇璁惧鐘舵�佹満棰嗗煙鏈嶅姟"
+```
+
+---
+
+### Task 7: 瀹炵幇璁惧浠撳偍鎺ュ彛鍜屽疄鐜�
+
+**Files:**
+- Create: `WIDESEAWCS_Application/DeviceManagement/Repositories/IDeviceRepository.cs`
+- Create: `WIDESEAWCS_Infrastructure/Persistence/Repositories/InMemoryDeviceRepository.cs`
+
+- [ ] **Step 1: 鍒涘缓璁惧浠撳偍鎺ュ彛**
+
+```csharp
+using WIDESEAWCS_Application.Common;
+using WIDESEAWCS_Domain.DeviceManagement.Aggregates;
+using WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+
+namespace WIDESEAWCS_Application.DeviceManagement.Repositories;
+
+public interface IDeviceRepository : IRepository<Device, DeviceId>
+{
+    Task<Device?> GetByName(DeviceName name);
+    Task<IEnumerable<Device>> GetByType(DeviceType type);
+    Task<IEnumerable<Device>> GetByStatus(DeviceStatus status);
+    Task<IEnumerable<Device>> GetAllConnected();
+}
+```
+
+- [ ] **Step 2: 鍒涘缓鍐呭瓨璁惧浠撳偍瀹炵幇**
+
+```csharp
+using WIDESEAWCS_Application.Common;
+using WIDESEAWCS_Application.DeviceManagement.Repositories;
+using WIDESEAWCS_Domain.DeviceManagement.Aggregates;
+using WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+
+namespace WIDESEAWCS_Infrastructure.Persistence.Repositories;
+
+public class InMemoryDeviceRepository : IDeviceRepository
+{
+    private readonly Dictionary<DeviceId, Device> _devices = new();
+
+    public Task<Device?> GetById(DeviceId id)
+    {
+        _devices.TryGetValue(id, out var device);
+        return Task.FromResult(device);
+    }
+
+    public Task Add(Device entity)
+    {
+        _devices[entity.Id] = entity;
+        return Task.CompletedTask;
+    }
+
+    public Task Update(Device entity)
+    {
+        _devices[entity.Id] = entity;
+        return Task.CompletedTask;
+    }
+
+    public Task Delete(DeviceId id)
+    {
+        _devices.Remove(id);
+        return Task.CompletedTask;
+    }
+
+    public async Task<Device?> GetByName(DeviceName name)
+    {
+        var device = _devices.Values.FirstOrDefault(d => d.Name == name);
+        return Task.FromResult(device);
+    }
+
+    public async Task<IEnumerable<Device>> GetByType(DeviceType type)
+    {
+        var devices = _devices.Values.Where(d => d.Type == type);
+        return Task.FromResult(devices);
+    }
+
+    public async Task<IEnumerable<Device>> GetByStatus(DeviceStatus status)
+    {
+        var devices = _devices.Values.Where(d => d.Status == status);
+        return Task.FromResult(devices);
+    }
+
+    public async Task<IEnumerable<Device>> GetAllConnected()
+    {
+        var devices = _devices.Values.Where(d => d.Status == DeviceStatus.Connected);
+        return Task.FromResult(devices);
+    }
+}
+```
+
+- [ ] **Step 3: 鎻愪氦璁惧浠撳偍瀹炵幇**
+
+```bash
+git add WIDESEAWCS_Application/DeviceManagement/Repositories/ WIDESEAWCS_Infrastructure/Persistence/Repositories/
+git commit -m "feat: 瀹炵幇璁惧浠撳偍鎺ュ彛鍜屽唴瀛樺疄鐜�"
+```
+
+---
+
+## Chunk 3: 搴旂敤鏈嶅姟瀹炵幇
+
+### Task 8: 瀹炵幇璁惧搴旂敤鏈嶅姟
+
+**Files:**
+- Create: `WIDESEAWCS_Application/DeviceManagement/Services/DeviceApplicationService.cs`
+- Create: `WIDESEAWCS_Application/DeviceManagement/DTOs/DeviceDto.cs`
+
+- [ ] **Step 1: 鍒涘缓璁惧DTO**
+
+```csharp
+using WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+
+namespace WIDESEAWCS_Application.DeviceManagement.DTOs;
+
+public record DeviceDto
+{
+    public Guid Id { get; init; }
+    public string Name { get; init; } = string.Empty;
+    public DeviceType Type { get; init; }
+    public DeviceStatus Status { get; init; }
+    public string? Ip { get; init; }
+    public int? Port { get; init; }
+    public DateTime LastConnectedAt { get; init; }
+    public DateTime LastHeartbeatAt { get; init; }
+    public string? ErrorMessage { get; init; }
+}
+```
+
+- [ ] **Step 2: 鍒涘缓璁惧搴旂敤鏈嶅姟**
+
+```csharp
+using WIDESEAWCS_Application.Common;
+using WIDESEAWCS_Application.DeviceManagement.DTOs;
+using WIDESEAWCS_Application.DeviceManagement.Repositories;
+using WIDESEAWCS_Domain.DeviceManagement.Aggregates;
+using WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+using WIDESEAWCS_Domain.Common;
+
+namespace WIDESEAWCS_Application.DeviceManagement.Services;
+
+public class DeviceApplicationService
+{
+    private readonly IDeviceRepository _deviceRepository;
+    private readonly IUnitOfWork _unitOfWork;
+
+    public DeviceApplicationService(
+        IDeviceRepository deviceRepository,
+        IUnitOfWork unitOfWork)
+    {
+        _deviceRepository = deviceRepository;
+        _unitOfWork = unitOfWork;
+    }
+
+    public async Task<DeviceDto?> GetDevice(Guid id)
+    {
+        var deviceId = DeviceId.From(id);
+        var device = await _deviceRepository.GetById(deviceId);
+        return device?.ToDto();
+    }
+
+    public async Task<DeviceDto> CreateDevice(string name, DeviceType type, string ip, int port)
+    {
+        await using var transaction = _unitOfWork.BeginTransaction();
+
+        var deviceId = DeviceId.New();
+        var deviceName = new DeviceName(name);
+        var device = new Device(deviceId, deviceName, type);
+        device.SetAddress(new DeviceAddress(ip, port));
+
+        await _deviceRepository.Add(device);
+        await transaction.Commit();
+        await _unitOfWork.Commit();
+
+        return device.ToDto();
+    }
+
+    public async Task ConnectDevice(Guid id)
+    {
+        var deviceId = DeviceId.From(id);
+        var device = await _deviceRepository.GetById(deviceId);
+
+        if (device == null)
+            throw new DomainException($"璁惧涓嶅瓨鍦�: {id}", "DEVICE_NOT_FOUND");
+
+        device.Connect();
+        await _deviceRepository.Update(device);
+    }
+
+    public async Task DisconnectDevice(Guid id, string reason)
+    {
+        var deviceId = DeviceId.From(id);
+        var device = await _deviceRepository.GetById(deviceId);
+
+        if (device == null)
+            throw new DomainException($"璁惧涓嶅瓨鍦�: {id}", "DEVICE_NOT_FOUND");
+
+        device.Disconnect(reason);
+        await _deviceRepository.Update(device);
+    }
+
+    public async Task<IEnumerable<DeviceDto>> GetConnectedDevices()
+    {
+        var devices = await _deviceRepository.GetAllConnected();
+        return devices.Select(d => d.ToDto());
+    }
+}
+```
+
+- [ ] **Step 3: 鍦―evice绫讳腑娣诲姞ToDto鎵╁睍鏂规硶**
+
+```csharp
+// 鍦� WIDESEAWCS_Domain/DeviceManagement/Aggregates/DeviceExtensions.cs 涓垱寤�
+using WIDESEAWCS_Application.DeviceManagement.DTOs;
+
+namespace WIDESEAWCS_Domain.DeviceManagement.Aggregates;
+
+public static class DeviceExtensions
+{
+    public static DeviceDto ToDto(this Device device)
+    {
+        return new DeviceDto
+        {
+            Id = device.Id.Value,
+            Name = device.Name.Value,
+            Type = device.Type,
+            Status = device.Status,
+            Ip = device.Address?.Ip,
+            Port = device.Address?.Port,
+            LastConnectedAt = device.LastConnectedAt,
+            LastHeartbeatAt = device.LastHeartbeatAt,
+            ErrorMessage = device.ErrorMessage
+        };
+    }
+}
+```
+
+- [ ] **Step 4: 鎻愪氦璁惧搴旂敤鏈嶅姟瀹炵幇**
+
+```bash
+git add WIDESEAWCS_Application/DeviceManagement/ WIDESEAWCS_Domain/DeviceManagement/Aggregates/DeviceExtensions.cs
+git commit -m "feat: 瀹炵幇璁惧搴旂敤鏈嶅姟鍜孌TO"
+```
+
+---
+
+## Chunk 4: 闆嗘垚鍜岄獙璇�
+
+### Task 9: 閰嶇疆渚濊禆娉ㄥ叆
+
+**Files:**
+- Create: `WIDESEAWCS_Infrastructure/DependencyInjection/ServiceCollectionExtensions.cs`
+
+- [ ] **Step 1: 鍒涘缓鏈嶅姟娉ㄥ唽鎵╁睍**
+
+```csharp
+using Microsoft.Extensions.DependencyInjection;
+using WIDESEAWCS_Application.Common;
+using WIDESEAWCS_Application.DeviceManagement.Repositories;
+using WIDESEAWCS_Application.DeviceManagement.Services;
+using WIDESEAWCS_Infrastructure.Persistence;
+using WIDESEAWCS_Infrastructure.Persistence.Repositories;
+using WIDESEAWCS_Domain.DeviceManagement.Services;
+
+namespace WIDESEAWCS_Infrastructure.DependencyInjection;
+
+public static class ServiceCollectionExtensions
+{
+    public static IServiceCollection AddDeviceManagementServices(this IServiceCollection services)
+    {
+        // 娉ㄥ唽棰嗗煙鏈嶅姟
+        services.AddSingleton<IDeviceStateMachine, DeviceStateMachine>();
+
+        // 娉ㄥ唽浠撳偍
+        services.AddSingleton<IDeviceRepository, InMemoryDeviceRepository>();
+
+        // 娉ㄥ唽宸ヤ綔鍗曞厓
+        services.AddSingleton<IUnitOfWork, InMemoryUnitOfWork>();
+
+        // 娉ㄥ唽搴旂敤鏈嶅姟
+        services.AddScoped<DeviceApplicationService>();
+
+        return services;
+    }
+}
+```
+
+- [ ] **Step 2: 鎻愪氦渚濊禆娉ㄥ叆閰嶇疆**
+
+```bash
+git add WIDESEAWCS_Infrastructure/DependencyInjection/
+git commit -m "feat: 閰嶇疆璁惧绠$悊鏈嶅姟渚濊禆娉ㄥ叆"
+```
+
+---
+
+### Task 10: 闆嗘垚娴嬭瘯
+
+**Files:**
+- Create: `WIDESEAWCS_IntegrationTests/DeviceManagement/DeviceIntegrationTests.cs`
+
+- [ ] **Step 1: 鍒涘缓闆嗘垚娴嬭瘯**
+
+```csharp
+using Xunit;
+using Microsoft.Extensions.DependencyInjection;
+using WIDESEAWCS_Application.DeviceManagement.Services;
+using WIDESEAWCS_Domain.DeviceManagement.Aggregates;
+using WIDESEAWCS_Domain.DeviceManagement.ValueObjects;
+using WIDESEAWCS_Infrastructure.DependencyInjection;
+
+namespace WIDESEAWCS_IntegrationTests.DeviceManagement;
+
+public class DeviceIntegrationTests
+{
+    private readonly IServiceProvider _serviceProvider;
+    private readonly DeviceApplicationService _service;
+
+    public DeviceIntegrationTests()
+    {
+        var services = new ServiceCollection();
+        services.AddDeviceManagementServices();
+        _serviceProvider = services.BuildServiceProvider();
+        _service = _serviceProvider.GetRequiredService<DeviceApplicationService>();
+    }
+
+    [Fact]
+    public async Task DeviceWorkflow_Should_CompleteSuccessfully()
+    {
+        // Arrange & Act - 鍒涘缓璁惧
+        var device = await _service.CreateDevice("IntegrationTestDevice", DeviceType.StackerCrane, "127.0.0.1", 8080);
+        Assert.NotEqual(Guid.Empty, device.Id);
+        Assert.Equal(DeviceType.StackerCrane, device.Type);
+        Assert.Equal("IntegrationTestDevice", device.Name.Value);
+        Assert.Equal(DeviceStatus.Disconnected, device.Status);
+
+        // Act - 杩炴帴璁惧
+        await _service.ConnectDevice(device.Id);
+        var connectedDevice = await _service.GetDevice(device.Id);
+        Assert.NotNull(connectedDevice);
+        Assert.Equal(DeviceStatus.Connected, connectedDevice!.Status);
+
+        // Act - 鏂紑璁惧
+        await _service.DisconnectDevice(device.Id, "Integration test disconnect");
+        var disconnectedDevice = await _service.GetDevice(device.Id);
+        Assert.NotNull(disconnectedDevice);
+        Assert.Equal(DeviceStatus.Disconnected, disconnectedDevice!.Status);
+        Assert.Equal("Integration test disconnect", disconnectedDevice!.ErrorMessage);
+    }
+
+        // Act - 鑾峰彇宸茶繛鎺ヨ澶�
+        var connectedDevices = await _service.GetConnectedDevices();
+        Assert.Single(connectedDevices);
+        Assert.Equal("IntegrationTestDevice", connectedDevices.First().Name);
+    }
+
+    [Fact]
+    public async Task GetConnectedDevices_Should_ReturnOnlyConnected()
+    {
+        // Arrange & Act - 鍒涘缓澶氫釜璁惧
+        var device1 = await _service.CreateDevice("Device1", DeviceType.ConveyorLine, "127.0.0.2", 8081);
+        var device2 = await _service.CreateDevice("Device2", DeviceType.ShuttleCar, "127.0.0.3", 8082);
+
+        // Act - 鍙繛鎺ョ涓�涓澶�
+        await _service.ConnectDevice(device1.Id);
+
+        var connectedDevices = await _service.GetConnectedDevices();
+        Assert.Single(connectedDevices);
+        Assert.Equal("Device1", connectedDevices.First().Name);
+    }
+}
+```
+
+- [ ] **Step 2: 鎻愪氦闆嗘垚娴嬭瘯**
+
+```bash
+git add WIDESEAWCS_IntegrationTests/DeviceManagement/
+git commit -m "test: 娣诲姞璁惧绠$悊闆嗘垚娴嬭瘯"
+```
+
+---
+
+### Task 11: 鏇存柊鏂囨。
+
+**Files:**
+- Modify: `CLAUDE.md`
+
+- [ ] **Step 1: 鏇存柊CLAUDE.md娣诲姞DDD鏋舵瀯璇存槑**
+
+鍦–LAUDE.md涓坊鍔狅細
+```markdown
+## DDD鏋舵瀯瀹炴柦
+
+椤圭洰閲囩敤娓愯繘寮廌DD鏋舵瀯锛屽寘鍚互涓嬪眰娆★細
+
+### 棰嗗煙灞� (WIDESEAWCS_Domain)
+- 鑱氬悎鏍癸細Device, DeviceTask绛�
+- 鍊煎璞★細DeviceId, DeviceName, DeviceAddress绛�
+- 棰嗗煙鏈嶅姟锛欴eviceStateMachine, DeviceScheduler绛�
+- 棰嗗煙浜嬩欢锛欴eviceConnectedEvent, TaskCompletedEvent绛�
+
+### 搴旂敤灞� (WIDESEAWCS_Application)
+- 搴旂敤鏈嶅姟锛欴eviceApplicationService绛�
+- DTOs锛氳澶囥�佷换鍔$瓑鏁版嵁浼犺緭瀵硅薄
+- 浠撳偍鎺ュ彛锛欼DeviceRepository, ITaskRepository绛�
+- 宸ヤ綔鍗曞厓锛欼UnitOfWork
+
+### 鍩虹璁炬柦灞� (WIDESEAWCS_Infrastructure)
+- 浠撳偍瀹炵幇锛欼nMemoryDeviceRepository, DeviceRepository绛�
+- 鏁版嵁鎸佷箙鍖栵細SqlSugar闆嗘垚
+- 缂撳瓨锛歊edis缂撳瓨瀹炵幇
+- 閫氫俊锛氳澶囬�氫俊閫傞厤鍣�
+
+### 鐗规�у紑鍏�
+鏀寔鏂版棫浠g爜鍏卞瓨锛屽彲閫氳繃閰嶇疆鎺у埗锛�
+```json
+"FeatureFlags": {
+  "UseNewDeviceManagement": true,
+  "UseNewTaskScheduler": false
+}
+```
+```
+
+- [ ] **Step 2: 鎻愪氦鏂囨。鏇存柊**
+
+```bash
+git add CLAUDE.md
+git commit -m "docs: 鏇存柊CLAUDE.md娣诲姞DDD鏋舵瀯璇存槑"
+```
+
+---
+
+### Task 12: 鎬荤粨鍜屽悗缁楠�
+
+- [ ] **Step 1: 鏌ョ湅鎵�鏈夋彁浜�**
+
+```bash
+git log --oneline --graph
+```
+
+- [ ] **Step 2: 鏄剧ず鎬荤粨淇℃伅**
+
+鏈瀹炴柦璁″垝宸插畬鎴愰樁娈�1-4锛屽叡11涓换鍔★細
+
+**宸插畬鎴愮殑宸ヤ綔**锛�
+1. 鍩虹璁炬柦鎼缓锛欴DD鍩虹绫汇�佸簲鐢ㄥ眰鎺ュ彛銆佸熀纭�璁炬柦灞傚疄鐜�
+2. 璁惧绠$悊棰嗗煙锛氳澶囧�煎璞°�佽仛鍚堟牴銆佺姸鎬佹満銆佷粨鍌�
+3. 搴旂敤灞傚疄鐜帮細璁惧搴旂敤鏈嶅姟銆丏TO銆佹墿灞曟柟娉�
+4. 闆嗘垚楠岃瘉锛氫緷璧栨敞鍏ラ厤缃�侀泦鎴愭祴璇�
+5. 鏂囨。鏇存柊锛欳LAUDE.md
+
+**涓嬩竴姝ュ缓璁�**锛�
+1. 缂栧啓瀹屾暣鐨勫崟鍏冩祴璇曞拰闆嗘垚娴嬭瘯
+2. 瀹炵幇SqlSugar浠撳偍瀹炵幇锛堟浛鎹㈠唴瀛樺疄鐜帮級
+3. 瀹炵幇缂撳瓨浼樺寲锛圧edis闆嗘垚锛�
+4. 閰嶇疆鐗规�у紑鍏虫敮鎸佹柊鏃т唬鐮佸叡瀛�
+5. 缂栧啓API鎺у埗鍣紙DeviceController锛�
+6. 杩涜鎬ц兘娴嬭瘯鍜屼紭鍖�
+7. 鍒涘缓Pull Request鍚堝苟鍒癿aster鍒嗘敮

--
Gitblit v1.9.3