SyncMapPointsCommandHandler.cs
8.46 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
using MassTransit;
using MassTransit.Mediator;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Rcs.Application.Common;
using Rcs.Application.MessageBus.Commands;
using Rcs.Application.Shared;
using Rcs.Cyaninetech.Services;
using Rcs.Domain.Entities;
using Rcs.Domain.Extensions;
using Rcs.Domain.Repositories;
using Rcs.Domain.Settings;
namespace Rcs.Infrastructure.MessageBus.Handlers.Commands;
/// <summary>
/// 同步地图资源命令处理器
/// @author zzy
/// </summary>
public class SyncMapPointsCommandHandler : IConsumer<SyncMapPointsCommand>
{
private readonly ILogger<SyncMapPointsCommandHandler> _logger;
private readonly IMapRepository _mapRepository;
private readonly IMapNodeRepository _mapNodeRepository;
private readonly IStorageAreaRepository _storageAreaRepository;
private readonly IStorageLocationRepository _storageLocationRepository;
private readonly ILanYinService _lanYinService;
public SyncMapPointsCommandHandler(
ILogger<SyncMapPointsCommandHandler> logger,
IMapRepository mapRepository,
IMapNodeRepository mapNodeRepository,
IStorageAreaRepository storageAreaRepository,
IStorageLocationRepository storageLocationRepository,
ILanYinService lanYinService)
{
_logger = logger;
_mapRepository = mapRepository;
_mapNodeRepository = mapNodeRepository;
_storageAreaRepository = storageAreaRepository;
_storageLocationRepository = storageLocationRepository;
_lanYinService = lanYinService;
}
public async Task Consume(ConsumeContext<SyncMapPointsCommand> context)
{
var command = context.Message;
try
{
var map = await _mapRepository.GetByIdAsync(command.MapId, context.CancellationToken);
if (map == null)
{
await context.RespondAsync(ApiResponse.Failed($"未找到ID为 {command.MapId} 的地图"));
return;
}
if (string.IsNullOrWhiteSpace(map.PointsUrl))
throw new BusinessException("请先维护节点资源URL");
// 获取地图资源信息
var locations = await _lanYinService.GetLocationsAsync(map.PointsUrl);
var remoteNodeCodes = locations.Select(l => l.id).ToHashSet();
// 获取当前地图所有节点,用于后续删除判断
var existingNodes = await _mapNodeRepository.GetByMapIdAsync(command.MapId, context.CancellationToken);
// 先同步库区(以location.area为库区编码)
var areaDict = new Dictionary<string, StorageArea>();
var areaCodes = locations.Select(l => l.area).Where(a => !string.IsNullOrWhiteSpace(a)).Distinct();
foreach (var areaCode in areaCodes)
{
var storageArea = await _storageAreaRepository.GetByAreaCodeAsync(areaCode, context.CancellationToken);
if (storageArea == null)
{
storageArea = new StorageArea
{
AreaId = Guid.NewGuid(),
AreaCode = areaCode,
AreaName = areaCode,
IsActive = true,
CreatedAt = DateTime.Now
};
await _storageAreaRepository.AddAsync(storageArea, context.CancellationToken);
}
areaDict[areaCode] = storageArea;
}
// 循环将locations插入到MapNode
foreach (var location in locations)
{
var useStatus = ParseUseStatus(location.use_status);
var existingNode = await _mapNodeRepository.GetByMapIdAndNodeCodeAsync(
command.MapId, location.id, context.CancellationToken);
Guid nodeId;
if (existingNode != null)
{
// 更新现有节点
nodeId = existingNode.NodeId;
existingNode.NodeName = location.alias;
existingNode.X = (location.position?.x != null ? (double)location.position.x : 0) * 1000;
existingNode.Y = (location.position?.y != null ? (double)location.position.y : 0) * 1000;
await _mapNodeRepository.UpdateAsync(existingNode, context.CancellationToken);
}
else
{
// 创建新节点
nodeId = Guid.NewGuid();
var newNode = new MapNode
{
NodeId = nodeId,
MapId = command.MapId,
NodeCode = location.id,
NodeName = location.id,
X = (location.position?.x != null ? (double)location.position.x : 0) * 1000,
Y = (location.position?.y != null ? (double)location.position.y : 0) * 1000,
Type = MapNodeTYPE.store,
Active = true,
CreatedAt = DateTime.Now
};
await _mapNodeRepository.AddAsync(newNode, context.CancellationToken);
}
// 同步库位(跳过没有库区的)
if (string.IsNullOrWhiteSpace(location.area) || !areaDict.TryGetValue(location.area, out var area))
continue;
var existingLocation = await _storageLocationRepository.GetByLocationCodeAsync(location.id, context.CancellationToken);
if (existingLocation != null)
{
existingLocation.LocationName = location.alias;
existingLocation.Status = ParseStorageLocationStatus(useStatus);
existingLocation.UpdatedAt = DateTime.Now;
await _storageLocationRepository.UpdateAsync(existingLocation, context.CancellationToken);
}
else
{
var newLocation = new StorageLocation
{
LocationId = Guid.NewGuid(),
AreaId = area.AreaId,
MapNodeId = nodeId,
LocationCode = location.id,
LocationName = location.alias,
Status = ParseStorageLocationStatus(useStatus),
IsActive = true,
CreatedAt = DateTime.Now
};
await _storageLocationRepository.AddAsync(newLocation, context.CancellationToken);
}
}
// 删除远程不存在的节点及关联库位
foreach (var node in existingNodes)
{
if (!remoteNodeCodes.Contains(node.NodeCode))
{
// 先删除关联的库位
var locationToDelete = await _storageLocationRepository.GetByLocationCodeAsync(node.NodeCode, context.CancellationToken);
if (locationToDelete != null)
await _storageLocationRepository.DeleteAsync(locationToDelete, context.CancellationToken);
// 再删除节点
await _mapNodeRepository.DeleteAsync(node, context.CancellationToken);
}
}
await context.RespondAsync(ApiResponse.Successful("同步地图资源成功"));
}
catch (Exception ex)
{
_logger.LogError(ex, "同步地图资源失败: MapId={MapId}", command.MapId);
await context.RespondAsync(ApiResponse.Failed($"同步失败: {ex.Message}"));
}
}
/// <summary>
/// 解析使用状态字符串为枚举
/// @author zzy
/// </summary>
private static MapNodeUseStatus? ParseUseStatus(string? status)
{
return status switch
{
"use" => MapNodeUseStatus.use,
"free" => MapNodeUseStatus.free,
"pre_use" => MapNodeUseStatus.pre_use,
_ => null
};
}
/// <summary>
/// 将节点使用状态转换为库位状态
/// @author zzy
/// </summary>
private static StorageLocationStatus ParseStorageLocationStatus(MapNodeUseStatus? useStatus)
{
return useStatus switch
{
MapNodeUseStatus.use => StorageLocationStatus.Occupied,
MapNodeUseStatus.pre_use => StorageLocationStatus.Reserved,
_ => StorageLocationStatus.Empty
};
}
}