From b54877f2400f752f5b9a1c8aef39c4aa9e851ac5 Mon Sep 17 00:00:00 2001
From: wanshenmean <cathay_xy@163.com>
Date: 星期三, 11 三月 2026 09:54:19 +0800
Subject: [PATCH] docs: 添加WCS系统DDD重构设计文档
---
Code/WCS/WIDESEAWCS_Server/docs/superpowers/specs/2026-03-11-wcs-ddd-refactor-design.md | 840 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
1 files changed, 840 insertions(+), 0 deletions(-)
diff --git a/Code/WCS/WIDESEAWCS_Server/docs/superpowers/specs/2026-03-11-wcs-ddd-refactor-design.md b/Code/WCS/WIDESEAWCS_Server/docs/superpowers/specs/2026-03-11-wcs-ddd-refactor-design.md
new file mode 100644
index 0000000..2dfea6f
--- /dev/null
+++ b/Code/WCS/WIDESEAWCS_Server/docs/superpowers/specs/2026-03-11-wcs-ddd-refactor-design.md
@@ -0,0 +1,840 @@
+# WCS绯荤粺娓愯繘寮廌DD閲嶆瀯璁捐鏂囨。
+
+**鍒涘缓鏃ユ湡**: 2026-03-11
+**鐗堟湰**: 1.0
+**鑼冨洿**: 鍏ㄧ郴缁熼噸鏋勶紙娓愯繘寮廌DD锛�
+
+## 1. 鏋舵瀯鎬昏
+
+鍩轰簬鎮ㄧ殑闇�姹傦紝鎴戝皢鎻愬嚭涓�涓�傚悎2鍛ㄦ椂闂寸獥鍙g殑娓愯繘寮廌DD閲嶆瀯鏂规銆�
+
+```
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 琛ㄧ幇灞� (API) 鈹�
+鈹� WIDESEAWCS_Server (ASP.NET Core Controllers) 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹�
+ 鈻�
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 搴旂敤灞� 鈹�
+鈹� 搴旂敤鏈嶅姟 / DTO / 鍛戒护 / 鏌ヨ 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹�
+ 鈻�
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 棰嗗煙灞� (鏂板) 鈹�
+鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹� 璁惧绠$悊棰嗗煙 鈹� 鈹�
+鈹� 鈹� 鈥� 鑱氬悎鏍�: 璁惧, 璁惧缁�, 璁惧浠诲姟 鈹� 鈹�
+鈹� 鈹� 鈥� 瀹炰綋: 璁惧鐘舵��, 璁惧浣嶇疆, 璁惧灞炴�� 鈹� 鈹�
+鈹� 鈹� 鈥� 鍊煎璞�: 璁惧ID, 鍦板潃, 鐘舵�佹灇涓� 鈹� 鈹�
+鈹� 鈹� 鈥� 棰嗗煙鏈嶅姟: 璁惧璋冨害鍣�, 鐘舵�佹満 鈹� 鈹�
+鈹� 鈹� 鈥� 浠撳偍鎺ュ彛: IDeviceRepository, IDeviceTaskRepository 鈹� 鈹�
+鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹� 浠撳偍浣滀笟棰嗗煙 鈹� 鈹�
+鈹� 鈹� 鈥� 鑱氬悎鏍�: 浣滀笟, 鎵樼洏, 搴撲綅 鈹� 鈹�
+鈹� 鈹� 鈥� 棰嗗煙浜嬩欢: 浣滀笟瀹屾垚, 浣滀笟寮傚父 鈹� 鈹�
+鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹� 鈹� 绯荤粺闆嗘垚棰嗗煙 鈹� 鈹�
+鈹� 鈹� 鈥� 闆嗘垚鏈嶅姟: WMS闆嗘垚, MES闆嗘垚, ERP闆嗘垚 鈹� 鈹�
+鈹� 鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹� 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+ 鈹�
+ 鈻�
+鈹屸攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+鈹� 鍩虹璁炬柦灞� 鈹�
+鈹� 鈥� 浠撳偍瀹炵幇 (SqlSugar ORM) 鈹�
+鈹� 鈥� 閫氫俊椹卞姩 (HslCommunication) 鈹�
+鈹� 鈥� 缂撳瓨鏈嶅姟 (Redis) 鈹�
+鈹� 鈥� 浠诲姟璋冨害 (Quartz.NET) 鈹�
+鈹� 鈥� 鏁版嵁搴撹闂� (SqlSugar) 鈹�
+鈹斺攢鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�鈹�
+```
+
+---
+
+## 2. 棰嗗煙灞傝璁� - 璁惧绠$悊棰嗗煙锛堟牳蹇冿級
+
+### 2.1 鑱氬悎鏍硅璁�
+
+```csharp
+// 璁惧鑱氬悎鏍�
+public class Device : AggregateRoot<DeviceId>
+{
+ // 鍩烘湰灞炴��
+ private DeviceId _id;
+ private DeviceName _name;
+ private DeviceType _type;
+ private DeviceStatus _status;
+ private DeviceAddress _address;
+ private List<DeviceProperty> _properties;
+
+ // 鐘舵�佺鐞�
+ private DateTime _lastConnectedAt;
+ private DateTime _lastHeartbeatAt;
+ private string _errorMessage;
+
+ // 棰嗗煙浜嬩欢
+ private List<IDomainEvent> _domainEvents = new();
+
+ // 琛屼负鏂规硶
+ public void Connect()
+ {
+ if (_status == DeviceStatus.Connected)
+ throw new DomainException("璁惧宸茶繛鎺�");
+
+ _status = DeviceStatus.Connected;
+ _lastConnectedAt = DateTime.UtcNow;
+ _domainEvents.Add(new DeviceConnectedEvent(_id));
+ }
+
+ public void Disconnect(string reason)
+ {
+ _status = DeviceStatus.Disconnected;
+ _errorMessage = reason;
+ _domainEvents.Add(new DeviceDisconnectedEvent(_id, reason));
+ }
+
+ public void UpdateHeartbeat()
+ {
+ _lastHeartbeatAt = DateTime.UtcNow;
+ _domainEvents.Add(new DeviceHeartbeatEvent(_id));
+ }
+
+ public void SetProperty(string key, string value)
+ {
+ var property = _properties.FirstOrDefault(p => p.Key == key);
+ if (property != null)
+ property.UpdateValue(value);
+ else
+ _properties.Add(DeviceProperty.Create(key, value));
+ }
+
+ public IReadOnlyCollection<IDomainEvent> GetDomainEvents() => _domainEvents.AsReadOnly();
+ public void ClearDomainEvents() => _domainEvents.Clear();
+}
+
+// 璁惧缁勮仛鍚堟牴
+public class DeviceGroup : AggregateRoot<DeviceGroupId>
+{
+ private DeviceGroupId _id;
+ private DeviceGroupName _name;
+ private List<DeviceId> _deviceIds;
+ private GroupStrategy _strategy;
+ private int _currentIndex;
+
+ public void AddDevice(DeviceId deviceId)
+ {
+ if (_deviceIds.Contains(deviceId))
+ throw new DomainException("璁惧宸插湪缁勫唴");
+ _deviceIds.Add(deviceId);
+ }
+
+ public void RemoveDevice(DeviceId deviceId)
+ {
+ _deviceIds.Remove(deviceId);
+ }
+
+ // 杞绛栫暐
+ public DeviceId GetNextDevice()
+ {
+ if (!_deviceIds.Any())
+ throw new DomainException("璁惧缁勪负绌�");
+
+ switch (_strategy)
+ {
+ case GroupStrategy.RoundRobin:
+ return _deviceIds[_currentIndex++ % _deviceIds.Count];
+ case GroupStrategy.Random:
+ return _deviceIds[new Random().Next(_deviceIds.Count)];
+ default:
+ return _deviceIds[0];
+ }
+ }
+}
+
+// 璁惧浠诲姟鑱氬悎鏍�
+public class DeviceTask : AggregateRoot<DeviceTaskId>
+{
+ private DeviceTaskId _id;
+ private DeviceId _deviceId;
+ private TaskType _type;
+ private TaskStatus _status;
+ private TaskPriority _priority;
+ private TaskPayload _payload;
+ private DateTime _createdAt;
+ private DateTime? _startedAt;
+ private DateTime? _completedAt;
+ private string? _errorMessage;
+ private List<IDomainEvent> _domainEvents = new();
+
+ public void Start()
+ {
+ if (_status != TaskStatus.Pending)
+ throw new DomainException("浠诲姟鐘舵�佷笉姝g‘");
+
+ _status = TaskStatus.Running;
+ _startedAt = DateTime.UtcNow;
+ _domainEvents.Add(new TaskStartedEvent(_id, _deviceId));
+ }
+
+ public void Complete()
+ {
+ if (_status != TaskStatus.Running)
+ throw new DomainException("浠诲姟鏈湪杩愯涓�");
+
+ _status = TaskStatus.Completed;
+ _completedAt = DateTime.UtcNow;
+ _domainEvents.Add(new TaskCompletedEvent(_id, _deviceId));
+ }
+
+ public void Fail(string errorMessage)
+ {
+ _status = TaskStatus.Failed;
+ _errorMessage = errorMessage;
+ _completedAt = DateTime.UtcNow;
+ _domainEvents.Add(new TaskFailedEvent(_id, _deviceId, errorMessage));
+ }
+
+ public IReadOnlyCollection<IDomainEvent> GetDomainEvents() => _domainEvents.AsReadOnly();
+ public void ClearDomainEvents() => _domainEvents.Clear();
+}
+```
+
+### 2.2 鍊煎璞¤璁�
+
+```csharp
+// 璁惧ID鍊煎璞�
+public record DeviceId(Guid Value)
+{
+ public static DeviceId New() => new DeviceId(Guid.NewGuid());
+ public static DeviceId From(Guid value) => new DeviceId(value);
+}
+
+// 璁惧鍚嶇О鍊煎璞�
+public record DeviceName(string Value)
+{
+ public DeviceName(string value)
+ {
+ if (string.IsNullOrWhiteSpace(value) || value.Length > 100)
+ throw new ArgumentException("璁惧鍚嶇О鏃犳晥");
+ Value = value;
+ }
+}
+
+// 璁惧绫诲瀷鏋氫妇
+public enum DeviceType
+{
+ StackerCrane, // 鍫嗗灈鏈�
+ ConveyorLine, // 杈撻�佺嚎
+ ShuttleCar, // 绌挎杞�
+ Robot, // 鏈烘鎵�
+ AGV // 鑷姩瀵煎紩杞�
+}
+
+// 璁惧鐘舵�佹灇涓�
+public enum DeviceStatus
+{
+ Disconnected, // 鏈繛鎺�
+ Connecting, // 杩炴帴涓�
+ Connected, // 宸茶繛鎺�
+ Busy, // 蹇欑
+ Error, // 閿欒
+ Maintenance // 缁存姢涓�
+}
+
+// 璁惧鍦板潃鍊煎璞�
+public record DeviceAddress(string Ip, int Port)
+{
+ public DeviceAddress(string ip, int port)
+ {
+ if (!IPAddress.TryParse(ip, out _))
+ throw new ArgumentException("IP鍦板潃鏃犳晥");
+ if (port < 1 || port > 65535)
+ throw new ArgumentException("绔彛鏃犳晥");
+ Ip = ip;
+ Port = port;
+ }
+}
+```
+
+### 2.3 棰嗗煙鏈嶅姟璁捐
+
+```csharp
+// 璁惧璋冨害棰嗗煙鏈嶅姟
+public interface IDeviceScheduler
+{
+ Task<DeviceTask> CreateTask(DeviceId deviceId, TaskType type, TaskPayload payload);
+ Task<DeviceTask> AssignTask(DeviceId deviceId);
+ Task CompleteTask(DeviceTaskId taskId);
+ Task FailTask(DeviceTaskId taskId, string errorMessage);
+}
+
+public class DeviceScheduler : IDeviceScheduler
+{
+ private readonly IDeviceTaskRepository _taskRepository;
+ private readonly IDeviceRepository _deviceRepository;
+ private readonly ITaskQueue _taskQueue;
+
+ public async Task<DeviceTask> CreateTask(DeviceId deviceId, TaskType type, TaskPayload payload)
+ {
+ var device = await _deviceRepository.GetById(deviceId);
+ if (device == null)
+ throw new DomainException("璁惧涓嶅瓨鍦�");
+
+ if (device.Status != DeviceStatus.Connected)
+ throw new DomainException("璁惧鏈繛鎺�");
+
+ var task = DeviceTask.Create(deviceId, type, payload);
+ await _taskRepository.Add(task);
+ await _taskQueue.Enqueue(task);
+
+ return task;
+ }
+
+ public async Task<DeviceTask> AssignTask(DeviceId deviceId)
+ {
+ var tasks = await _taskRepository.GetPendingTasksForDevice(deviceId);
+ if (!tasks.Any())
+ return null;
+
+ var task = tasks.OrderBy(t => t.Priority).First();
+ task.Start();
+ await _taskRepository.Update(task);
+
+ return task;
+ }
+}
+
+// 璁惧鐘舵�佹満棰嗗煙鏈嶅姟
+public interface IDeviceStateMachine
+{
+ bool CanTransition(DeviceStatus current, DeviceStatus target);
+ void Transition(Device device, DeviceStatus target);
+}
+
+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)
+ {
+ return _transitions.TryGetValue(current, out var allowed) && allowed.Contains(target);
+ }
+
+ public void Transition(Device device, DeviceStatus target)
+ {
+ if (!CanTransition(device.Status, target))
+ throw new DomainException($"鏃犳晥鐨勭姸鎬佽浆鎹�: {device.Status} -> {target}");
+
+ device.SetStatus(target);
+ }
+}
+```
+
+### 2.4 浠撳偍鎺ュ彛璁捐
+
+```csharp
+// 璁惧浠撳偍鎺ュ彛
+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();
+}
+
+// 璁惧浠诲姟浠撳偍鎺ュ彛
+public interface IDeviceTaskRepository : IRepository<DeviceTask, DeviceTaskId>
+{
+ Task<IEnumerable<DeviceTask>> GetPendingTasksForDevice(DeviceId deviceId);
+ Task<IEnumerable<DeviceTask>> GetRunningTasksForDevice(DeviceId deviceId);
+ Task<IEnumerable<DeviceTask>> GetTasksByStatus(TaskStatus status);
+}
+```
+
+---
+
+## 3. 搴旂敤灞傝璁�
+
+### 3.1 搴旂敤鏈嶅姟璁捐
+
+```csharp
+// 璁惧搴旂敤鏈嶅姟
+public class DeviceApplicationService
+{
+ private readonly IDeviceRepository _deviceRepository;
+ private readonly IDeviceScheduler _deviceScheduler;
+ private readonly IUnitOfWork _unitOfWork;
+
+ public async Task<DeviceDto> GetDevice(DeviceId id)
+ {
+ var device = await _deviceRepository.GetById(id);
+ return device.ToDto();
+ }
+
+ public async Task<DeviceTaskDto> CreateTask(CreateDeviceTaskCommand command)
+ {
+ await using var transaction = await _unitOfWork.BeginTransaction();
+
+ var deviceId = DeviceId.From(command.DeviceId);
+ var task = await _deviceScheduler.CreateTask(
+ deviceId,
+ command.Type,
+ new TaskPayload(command.Payload)
+ );
+
+ await _unitOfWork.Commit();
+ return task.ToDto();
+ }
+
+ public async Task CompleteTask(CompleteDeviceTaskCommand command)
+ {
+ var taskId = DeviceTaskId.From(command.TaskId);
+ await _deviceScheduler.CompleteTask(taskId);
+ }
+}
+
+// 鍛戒护鍜屾煡璇㈠璞�
+public record CreateDeviceTaskCommand(Guid DeviceId, TaskType Type, string Payload);
+public record CompleteDeviceTaskCommand(Guid TaskId);
+
+// DTO
+public record DeviceDto(Guid Id, string Name, DeviceType Type, DeviceStatus Status);
+public record DeviceTaskDto(Guid Id, Guid DeviceId, TaskType Type, TaskStatus Status);
+```
+
+---
+
+## 4. 鍩虹璁炬柦灞傞噸鏋�
+
+### 4.1 浠撳偍瀹炵幇
+
+```csharp
+// 璁惧浠撳偍瀹炵幇
+public class DeviceRepository : IDeviceRepository
+{
+ private readonly ISqlSugarClient _db;
+ private readonly ICacheService _cache;
+
+ public DeviceRepository(ISqlSugarClient db, ICacheService cache)
+ {
+ _db = db;
+ _cache = cache;
+ }
+
+ public async Task<Device?> GetById(DeviceId id)
+ {
+ // 鍏堟煡缂撳瓨
+ var cached = await _cache.Get<Device>($"device:{id.Value}");
+ if (cached != null)
+ return cached;
+
+ // 鏌ユ暟鎹簱
+ var entity = await _db.Queryable<Dt_DeviceInfo>()
+ .Where(d => d.DeviceId == id.Value)
+ .FirstAsync();
+
+ if (entity == null) return null;
+
+ // 杞崲涓洪鍩熸ā鍨�
+ var device = MapToDomain(entity);
+
+ // 鍐欏叆缂撳瓨
+ await _cache.Set($"device:{id.Value}", device, TimeSpan.FromMinutes(5));
+
+ return device;
+ }
+
+ public async Task Add(Device device)
+ {
+ var entity = MapToEntity(device);
+ await _db.Insertable(entity).ExecuteCommandAsync();
+
+ // 娓呴櫎鐩稿叧缂撳瓨
+ await _cache.RemoveByPrefix("device:");
+ }
+
+ private Device MapToDomain(Dt_DeviceInfo entity) { /* ... */ }
+ private Dt_DeviceInfo MapToEntity(Device domain) { /* ... */ }
+}
+```
+
+### 4.2 閫氫俊椹卞姩灏佽
+
+```csharp
+// 璁惧閫氫俊閫傞厤鍣ㄦ帴鍙�
+public interface IDeviceCommunicatorAdapter
+{
+ Task<bool> Connect(DeviceAddress address);
+ Task<bool> Disconnect();
+ Task<DeviceData> ReadData(string address);
+ Task WriteData(string address, object value);
+ Task<bool> IsConnected();
+}
+
+// Siemens PLC閫傞厤鍣�
+public class SiemensCommunicatorAdapter : IDeviceCommunicatorAdapter
+{
+ private readonly SiemensS7Net _plc;
+
+ public SiemensCommunicatorAdapter(DeviceAddress address)
+ {
+ _plc = new SiemensS7Net(SiemensPLCS.S1200, address.Ip);
+ _plc.ConnectTimeOut = 5000;
+ }
+
+ public async Task<bool> Connect(DeviceAddress address)
+ {
+ var result = await Task.Run(() => _plc.ConnectServer());
+ return result.IsSuccess;
+ }
+
+ public async Task<DeviceData> ReadData(string address)
+ {
+ var result = await Task.Run(() => _plc.Read(address));
+ if (!result.IsSuccess)
+ throw new CommunicationException($"璇诲彇澶辫触: {result.Message}");
+
+ return new DeviceData(result.Content);
+ }
+
+ // ... 鍏朵粬鏂规硶
+}
+```
+
+---
+
+## 5. 鍒嗗眰娓呮櫚鍖�
+
+### 5.1 椤圭洰缁撴瀯閲嶇粍
+
+```
+WIDESEAWCS_Domain/
+鈹溾攢鈹� DeviceManagement/
+鈹� 鈹溾攢鈹� Aggregates/
+鈹� 鈹� 鈹溾攢鈹� Device.cs
+鈹� 鈹� 鈹溾攢鈹� DeviceGroup.cs
+鈹� 鈹� 鈹斺攢鈹� DeviceTask.cs
+鈹� 鈹溾攢鈹� Entities/
+鈹� 鈹� 鈹溾攢鈹� DeviceStatus.cs
+鈹� 鈹� 鈹溾攢鈹� DeviceLocation.cs
+鈹� 鈹� 鈹斺攢鈹� DeviceProperty.cs
+鈹� 鈹溾攢鈹� ValueObjects/
+鈹� 鈹� 鈹溾攢鈹� DeviceId.cs
+鈹� 鈹� 鈹溾攢鈹� DeviceName.cs
+鈹� 鈹� 鈹溾攢鈹� DeviceAddress.cs
+鈹� 鈹� 鈹斺攢鈹� TaskPayload.cs
+鈹� 鈹溾攢鈹� Services/
+鈹� 鈹� 鈹溾攢鈹� IDeviceScheduler.cs
+鈹� 鈹� 鈹溾攢鈹� DeviceScheduler.cs
+鈹� 鈹� 鈹溾攢鈹� IDeviceStateMachine.cs
+鈹� 鈹� 鈹斺攢鈹� DeviceStateMachine.cs
+鈹� 鈹溾攢鈹� Repositories/
+鈹� 鈹� 鈹溾攢鈹� IDeviceRepository.cs
+鈹� 鈹� 鈹斺攢鈹� IDeviceTaskRepository.cs
+鈹� 鈹斺攢鈹� Events/
+鈹� 鈹溾攢鈹� DeviceConnectedEvent.cs
+鈹� 鈹斺攢鈹� TaskCompletedEvent.cs
+鈹斺攢鈹� WarehouseOperations/
+ 鈹斺攢鈹� ...
+
+WIDESEAWCS_Application/
+鈹溾攢鈹� Services/
+鈹� 鈹斺攢鈹� DeviceApplicationService.cs
+鈹溾攢鈹� Commands/
+鈹� 鈹斺攢鈹� CreateDeviceTaskCommand.cs
+鈹溾攢鈹� Queries/
+鈹� 鈹斺攢鈹� GetDeviceQuery.cs
+鈹斺攢鈹� DTOs/
+ 鈹斺攢鈹� DeviceDto.cs
+
+WIDESEAWCS_Infrastructure/
+鈹溾攢鈹� Persistence/
+鈹� 鈹溾攢鈹� Repositories/
+鈹� 鈹� 鈹溾攢鈹� DeviceRepository.cs
+鈹� 鈹� 鈹斺攢鈹� DeviceTaskRepository.cs
+鈹� 鈹斺攢鈹� SqlSugar/
+鈹� 鈹斺攢鈹� DbContext.cs
+鈹溾攢鈹� Communication/
+鈹� 鈹溾攢鈹� Adapters/
+鈹� 鈹� 鈹溾攢鈹� SiemensCommunicatorAdapter.cs
+鈹� 鈹� 鈹溾攢鈹� OmronCommunicatorAdapter.cs
+鈹� 鈹� 鈹斺攢鈹� ModbusCommunicatorAdapter.cs
+鈹� 鈹斺攢鈹� IDeviceCommunicatorAdapter.cs
+鈹溾攢鈹� Caching/
+鈹� 鈹斺攢鈹� RedisCacheService.cs
+鈹斺攢鈹� Scheduling/
+ 鈹斺攢鈹� QuartzJobManager.cs
+
+WIDESEAWCS_Server/
+鈹斺攢鈹� Controllers/
+ 鈹斺攢鈹� DeviceController.cs
+```
+
+---
+
+## 6. 鎬ц兘浼樺寲绛栫暐
+
+### 6.1 缂撳瓨浼樺寲
+
+```csharp
+// 澶氱骇缂撳瓨绛栫暐
+public class HybridCacheService : ICacheService
+{
+ private readonly IMemoryCache _l1Cache;
+ private readonly IConnectionMultiplexer _redis;
+ private readonly IDistributedCache _l2Cache;
+
+ public async Task<T?> Get<T>(string key)
+ {
+ // L1缂撳瓨
+ if (_l1Cache.TryGetValue(key, out T l1Value))
+ return l1Value;
+
+ // L2缂撳瓨
+ var l2Value = await _l2Cache.GetStringAsync(key);
+ if (l2Value != null)
+ {
+ var value = JsonSerializer.Deserialize<T>(l2Value);
+ _l1Cache.Set(key, value, TimeSpan.FromMinutes(1));
+ return value;
+ }
+
+ return default;
+ }
+
+ public async Task Set<T>(string key, T value, TimeSpan? expiry = null)
+ {
+ _l1Cache.Set(key, value, TimeSpan.FromMinutes(1));
+
+ var serialized = JsonSerializer.Serialize(value);
+ await _l2Cache.SetStringAsync(key, serialized, expiry);
+ }
+}
+```
+
+### 6.2 鏁版嵁搴撴煡璇紭鍖�
+
+```csharp
+// 鎵归噺鏌ヨ浼樺寲
+public class DeviceRepository : IDeviceRepository
+{
+ public async Task<IDictionary<DeviceId, Device>> GetByIds(IEnumerable<DeviceId> ids)
+ {
+ var idList = ids.Select(id => id.Value).ToList();
+
+ // 鎵归噺鏌ヨ
+ var entities = await _db.Queryable<Dt_DeviceInfo>()
+ .In(idList)
+ .ToListAsync();
+
+ return entities.ToDictionary(
+ e => DeviceId.From(e.DeviceId),
+ e => MapToDomain(e)
+ );
+ }
+
+ // 寤惰繜鏌ヨ
+ public async Task<IEnumerable<Device>> QueryActiveDevices()
+ {
+ return await _db.Queryable<Dt_DeviceInfo>()
+ .Where(d => d.Status != DeviceStatus.Disconnected)
+ .OrderBy(d => d.Name)
+ .Select(d => MapToDomain(d))
+ .ToListAsync();
+ }
+}
+```
+
+---
+
+## 7. 閿欒澶勭悊鍜屾棩蹇楃粺涓�
+
+### 7.1 缁熶竴寮傚父澶勭悊
+
+```csharp
+// 棰嗗煙寮傚父鍩虹被
+public abstract class DomainException : Exception
+{
+ public string ErrorCode { get; }
+
+ protected DomainException(string message, string errorCode = "DOMAIN_ERROR")
+ : base(message)
+ {
+ ErrorCode = errorCode;
+ }
+}
+
+// 搴旂敤寮傚父
+public class ApplicationException : Exception
+{
+ public string ErrorCode { get; }
+
+ public ApplicationException(string message, string errorCode = "APP_ERROR")
+ : base(message)
+ {
+ ErrorCode = errorCode;
+ }
+}
+
+// 鍏ㄥ眬寮傚父澶勭悊涓棿浠�
+public class GlobalExceptionHandlerMiddleware
+{
+ public async Task InvokeAsync(HttpContext context, RequestDelegate next)
+ {
+ try
+ {
+ await next(context);
+ }
+ catch (DomainException ex)
+ {
+ context.Response.StatusCode = 400;
+ await context.Response.WriteAsJsonAsync(new
+ {
+ error = ex.ErrorCode,
+ message = ex.Message
+ });
+ }
+ catch (ApplicationException ex)
+ {
+ context.Response.StatusCode = 500;
+ await context.Response.WriteAsJsonAsync(new
+ {
+ error = ex.ErrorCode,
+ message = ex.Message
+ });
+ }
+ }
+}
+```
+
+### 7.2 缁撴瀯鍖栨棩蹇�
+
+```csharp
+// 棰嗗煙浜嬩欢鏃ュ織
+public class DomainEventLogger : IDomainEventDispatcher
+{
+ private readonly ILogger<DomainEventLogger> _logger;
+
+ public async Task Dispatch(IDomainEvent domainEvent)
+ {
+ _logger.LogInformation("棰嗗煙浜嬩欢: {EventType}, 鏁版嵁: {EventData}",
+ domainEvent.GetType().Name,
+ JsonSerializer.Serialize(domainEvent));
+
+ // 鍙戦�佸埌浜嬩欢鎬荤嚎
+ }
+}
+
+// 璁惧鎿嶄綔鏃ュ織
+public class DeviceOperationLogger : IDeviceOperationLogger
+{
+ public void LogConnect(DeviceId deviceId, bool success, string? error = null)
+ {
+ if (success)
+ {
+ _logger.LogInformation("璁惧杩炴帴鎴愬姛: {DeviceId}", deviceId.Value);
+ }
+ else
+ {
+ _logger.LogError("璁惧杩炴帴澶辫触: {DeviceId}, 閿欒: {Error}",
+ deviceId.Value, error);
+ }
+ }
+}
+```
+
+---
+
+## 8. 瀹炴柦璁″垝锛�2鍛級
+
+### 绗�1鍛細棰嗗煙灞傛惌寤�
+
+| 澶╂暟 | 浠诲姟 |
+|------|------|
+| Day 1-2 | 鍒涘缓棰嗗煙椤圭洰缁撴瀯锛屽畾涔夊�煎璞″拰鑱氬悎鏍� |
+| Day 3-4 | 瀹炵幇棰嗗煙鏈嶅姟鍜岀姸鎬佹満 |
+| Day 5 | 鍒涘缓浠撳偍鎺ュ彛锛屽畾涔夐鍩熶簨浠� |
+
+### 绗�2鍛細搴旂敤灞傚拰鍩虹璁炬柦灞�
+
+| 澶╂暟 | 浠诲姟 |
+|------|------|
+| Day 1-2 | 瀹炵幇搴旂敤鏈嶅姟鍜孌TO |
+| Day 3 | 鍒涘缓浠撳偍瀹炵幇锛岃縼绉绘暟鎹闂�昏緫 |
+| Day 4 | 灏佽閫氫俊椹卞姩锛屼紭鍖栫紦瀛樼瓥鐣� |
+| Day 5 | 缁熶竴閿欒澶勭悊鍜屾棩蹇楋紝闆嗘垚娴嬭瘯 |
+
+---
+
+## 9. 璁捐鍐崇瓥璁板綍
+
+### 9.1 涓轰粈涔堥�夋嫨娓愯繘寮廌DD鑰岄潪瀹屾暣DDD閲嶆瀯锛�
+
+**鍐崇瓥**锛氶�夋嫨娓愯繘寮廌DD閲嶆瀯鑰岄潪瀹屾暣DDD閲嶆瀯
+
+**鐞嗙敱**锛�
+1. 鏃堕棿绾︽潫锛�2鍛ㄥ唴鏃犳硶瀹屾垚瀹屾暣DDD閲嶆瀯
+2. 椋庨櫓鎺у埗锛氭笎杩涘紡闄嶄綆椋庨櫓锛屽彲鍒嗛樁娈甸獙璇�
+3. 绯荤粺绋冲畾鎬э細淇濇寔鐜版湁鍔熻兘鍙敤
+4. 璧勬簮鏁堢巼锛氫紭鍏堥噸鏋勬牳蹇冮鍩�
+
+### 9.2 涓轰粈涔堝皢璁惧绠$悊浣滀负鏍稿績棰嗗煙锛�
+
+**鍐崇瓥**锛氬皢璁惧绠$悊浣滀负绗竴涓噸鏋勭殑鏍稿績棰嗗煙
+
+**鐞嗙敱**锛�
+1. 涓氬姟浼樺厛绾э細璁惧绠$悊鏄疻CS绯荤粺鐨勬牳蹇�
+2. 浠g爜澶嶆潅搴︼細璁惧绠$悊鍖呭惈鐘舵�併�佽皟搴︺�侀�氫俊绛夊鏉傞�昏緫
+3. 浼樺寲绌洪棿澶э細璁惧绠$悊瀛樺湪杈冨鎬ц兘鍜岃�﹀悎闂
+4. 褰卞搷鑼冨洿骞匡細璁惧绠$悊鏄叾浠栦笟鍔$殑鍩虹
+
+### 9.3 涓轰粈涔堥噰鐢–QRS妯″紡锛�
+
+**鍐崇瓥**锛氬湪搴旂敤灞傞噰鐢–QRS妯″紡
+
+**鐞嗙敱**锛�
+1. 鍏虫敞鐐瑰垎绂伙細鍛戒护鍜屾煡璇㈣亴璐f竻鏅�
+2. 鎬ц兘浼樺寲锛氬彲鍒嗗埆浼樺寲璇诲啓鎿嶄綔
+3. 鎵╁睍鎬уソ锛氫究浜庢坊鍔犵紦瀛樸�佷簨浠跺鐞�
+
+---
+
+## 10. 闈炲姛鑳芥�ч渶姹�
+
+### 10.1 鎬ц兘瑕佹眰
+- 璁惧鐘舵�佹煡璇㈠搷搴旀椂闂� < 100ms
+- 浠诲姟鍒涘缓鍝嶅簲鏃堕棿 < 200ms
+- 鏀寔鑷冲皯100涓苟鍙戣澶囪繛鎺�
+
+### 10.2 鍙淮鎶ゆ�ц姹�
+- 浠g爜瑕嗙洊鐜� > 80%
+- 鍦堝鏉傚害 < 10
+- 閬靛惊鍗曚竴鑱岃矗鍘熷垯
+
+### 10.3 鍙墿灞曟�ц姹�
+- 鏀寔鏂板璁惧绫诲瀷鏃犻渶淇敼鏍稿績浠g爜
+- 鏀寔鏂板浠诲姟绫诲瀷
+- 鏀寔澶氱閫氫俊鍗忚鎵╁睍
+
+---
+
+## 11. 椋庨櫓璇勪及
+
+| 椋庨櫓 | 鍙兘鎬� | 褰卞搷 | 缂撹В鎺柦 |
+|------|--------|------|----------|
+| 鏃堕棿涓嶈冻 | 楂� | 楂� | 鍒嗛樁娈靛疄鏂斤紝浼樺厛鏍稿績鍔熻兘 |
+| 鏁版嵁杩佺Щ闂 | 涓� | 楂� | 鍏呭垎娴嬭瘯锛屼繚鐣欏洖婊氭柟妗� |
+| 鎬ц兘涓嬮檷 | 涓� | 涓� | 鎬ц兘鍩哄噯娴嬭瘯锛岀洃鎺т紭鍖� |
+| 鍥㈤槦閫傚簲 | 浣� | 涓� | 鎻愪緵鍩硅锛屾枃妗e畬鍠� |
--
Gitblit v1.9.3