移除历史状态归一化及相关遗留逻辑
- 移除 LegacyStatusNormalizationMigrator,仅保留 AreaLayoutModeDefaultMigrator - 删除 WmsDispatchStatus 枚举、常量和全局 using - 统一所有 DbContext 注入为 MiGuDbContext,移除 PlatformDbContext - 服务实体操作统一用 IEditableRepository<T>,移除本地实现 - 移除 WmsService 的 MigrateLegacyAsync 方法 - 精简接口模型,移除部分字段和请求体 - 前端保存容器时 status 字段取自已有数据,移除写死默认值 - 更新文档,去除旧说明,统一数据库初始化流程
This commit is contained in:
@@ -20,7 +20,7 @@ public sealed record ContainerMaterialSnapshot(
|
||||
public abstract record CommonRequest(Guid? Id, long? Version, bool IsLock, string Remark, string Extend);
|
||||
|
||||
public sealed record MasterDataRequest(
|
||||
Guid? Id, long? Version, string Code, string Name, string Type, string Status, bool Enabled, int SortOrder,
|
||||
Guid? Id, long? Version, string Code, string Name, string Type, bool Enabled, int SortOrder,
|
||||
bool IsLock, string Remark, string Extend) : CommonRequest(Id, Version, IsLock, Remark, Extend);
|
||||
|
||||
public sealed record AreaRequest(
|
||||
@@ -57,8 +57,3 @@ public sealed record ContainerLocationRequest(
|
||||
public sealed record BindMaterialRequest(
|
||||
Guid? Id, long? Version, Guid ContainerId, Guid MaterialId, string Source, string Reason,
|
||||
bool IsLock, string Remark, string Extend) : CommonRequest(Id, Version, IsLock, Remark, Extend);
|
||||
|
||||
public sealed record ContainerMaterialRequest(
|
||||
Guid? Id, long? Version, Guid ContainerId, Guid MaterialId, decimal Quantity, string BatchNo, string SerialNo,
|
||||
string Status, DateTimeOffset? LoadedAt, DateTimeOffset? UnloadedAt, string Source, string Reason,
|
||||
bool IsLock, string Remark, string Extend) : CommonRequest(Id, Version, IsLock, Remark, Extend);
|
||||
|
||||
@@ -5,9 +5,9 @@ namespace MiGu.Server.Wms;
|
||||
|
||||
public sealed class WmsReferenceValidator
|
||||
{
|
||||
private readonly PlatformDbContext _db;
|
||||
private readonly MiGuDbContext _db;
|
||||
|
||||
public WmsReferenceValidator(PlatformDbContext db)
|
||||
public WmsReferenceValidator(MiGuDbContext db)
|
||||
{
|
||||
_db = db;
|
||||
}
|
||||
|
||||
@@ -10,14 +10,14 @@ namespace MiGu.Server.Wms;
|
||||
|
||||
public sealed class WmsService
|
||||
{
|
||||
private readonly PlatformDbContext _db;
|
||||
private readonly MiGuDbContext _db;
|
||||
private readonly IUnitOfWork _uow;
|
||||
private readonly IServiceProvider _services;
|
||||
private readonly WmsReferenceValidator _refs;
|
||||
private readonly JsonSerializerOptions _json = new(JsonSerializerDefaults.Web);
|
||||
|
||||
public WmsService(
|
||||
PlatformDbContext db,
|
||||
MiGuDbContext db,
|
||||
IUnitOfWork uow,
|
||||
IServiceProvider services,
|
||||
WmsReferenceValidator refs)
|
||||
@@ -158,7 +158,7 @@ public sealed class WmsService
|
||||
StampCreate(entity, actor);
|
||||
_db.Warehouses.Add(entity);
|
||||
}
|
||||
await EnsureUnique(_db.Warehouses, x => x.Code == req.Code && x.Id != entity.Id, "仓库编码已存在");
|
||||
await Repo<Warehouse>().EnsureUniqueAsync(x => x.Code == req.Code && x.Id != entity.Id, "仓库编码已存在");
|
||||
entity.Code = req.Code.Trim();
|
||||
entity.Name = req.Name.Trim();
|
||||
entity.Type = req.Type.TrimOr("Default");
|
||||
@@ -185,7 +185,7 @@ public sealed class WmsService
|
||||
StampCreate(entity, actor);
|
||||
_db.WarehouseAreas.Add(entity);
|
||||
}
|
||||
await EnsureUnique(_db.WarehouseAreas, x => x.Code == req.Code && x.Id != entity.Id, "库区编码已存在");
|
||||
await Repo<WarehouseArea>().EnsureUniqueAsync(x => x.Code == req.Code && x.Id != entity.Id, "库区编码已存在");
|
||||
entity.WarehouseId = warehouseId;
|
||||
entity.Code = req.Code.Trim();
|
||||
entity.Name = req.Name.Trim();
|
||||
@@ -214,7 +214,7 @@ public sealed class WmsService
|
||||
StampCreate(entity, actor);
|
||||
_db.Storages.Add(entity);
|
||||
}
|
||||
await EnsureUnique(_db.Storages, x => x.Code == req.Code && x.Id != entity.Id, "库位编码已存在");
|
||||
await Repo<Storage>().EnsureUniqueAsync(x => x.Code == req.Code && x.Id != entity.Id, "库位编码已存在");
|
||||
entity.AreaId = req.AreaId;
|
||||
entity.Code = req.Code.Trim();
|
||||
entity.Name = req.Name.Trim();
|
||||
@@ -227,7 +227,7 @@ public sealed class WmsService
|
||||
entity.SiteCode = req.SiteCode.TrimOr(entity.SiteId);
|
||||
entity.Barcode = req.Barcode.TrimOr("");
|
||||
entity.Capacity = 1;
|
||||
var status = StorageStatuses.Normalize(req.Status);
|
||||
var status = StorageStatuses.ParseOr(req.Status);
|
||||
if (status == StorageStatuses.Disabled || req.Enabled == false)
|
||||
entity.Status = req.Enabled ? status : StorageStatuses.Disabled;
|
||||
else if (!string.IsNullOrWhiteSpace(req.Status) && StorageStatuses.All.Contains(status))
|
||||
@@ -326,7 +326,7 @@ public sealed class WmsService
|
||||
StampCreate(entity, actor);
|
||||
_db.Containers.Add(entity);
|
||||
}
|
||||
await EnsureUnique(_db.Containers, x => x.Code == req.Code && x.Id != entity.Id, "容器编码已存在");
|
||||
await Repo<Container>().EnsureUniqueAsync(x => x.Code == req.Code && x.Id != entity.Id, "容器编码已存在");
|
||||
entity.AreaId = req.AreaId;
|
||||
entity.Code = req.Code.Trim();
|
||||
entity.Name = req.Name.Trim();
|
||||
@@ -353,7 +353,7 @@ public sealed class WmsService
|
||||
StampCreate(entity, actor);
|
||||
_db.MaterialTypes.Add(entity);
|
||||
}
|
||||
await EnsureUnique(_db.MaterialTypes, x => x.Code == req.Code && x.Id != entity.Id, "物料类型编码已存在");
|
||||
await Repo<MaterialType>().EnsureUniqueAsync(x => x.Code == req.Code && x.Id != entity.Id, "物料类型编码已存在");
|
||||
entity.Code = req.Code.Trim();
|
||||
entity.Name = req.Name.Trim();
|
||||
entity.Spec = req.Spec.TrimOr("");
|
||||
@@ -378,9 +378,9 @@ public sealed class WmsService
|
||||
StampCreate(entity, actor);
|
||||
_db.Materials.Add(entity);
|
||||
}
|
||||
await EnsureUnique(_db.Materials, x => x.Code == req.Code && x.Id != entity.Id, "物料编码已存在");
|
||||
await Repo<Material>().EnsureUniqueAsync(x => x.Code == req.Code && x.Id != entity.Id, "物料编码已存在");
|
||||
if (!string.IsNullOrWhiteSpace(req.Barcode))
|
||||
await EnsureUnique(_db.Materials, x => x.Barcode == req.Barcode && x.Id != entity.Id, "物料条码已存在");
|
||||
await Repo<Material>().EnsureUniqueAsync(x => x.Barcode == req.Barcode && x.Id != entity.Id, "物料条码已存在");
|
||||
entity.Code = req.Code.Trim();
|
||||
entity.Name = req.Name.Trim();
|
||||
entity.TypeCode = req.TypeCode.TrimOr("");
|
||||
@@ -425,9 +425,9 @@ public sealed class WmsService
|
||||
await _uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public async Task DeleteEntity<T>(Guid id, long? version, string actor) where T : EntityBase
|
||||
public async Task DeleteEntity<T>(Guid id, long? version, string actor)
|
||||
where T : class, IEntity<Guid>, ISoftDeletable, IVersioned, ILockable, IAuditable
|
||||
{
|
||||
var entity = await FindEditable<T>(id, version);
|
||||
if (typeof(T) == typeof(WarehouseArea))
|
||||
{
|
||||
if (await _db.Storages.AnyAsync(x => x.AreaId == id))
|
||||
@@ -444,10 +444,8 @@ public sealed class WmsService
|
||||
if (await _db.ContainerMaterials.AnyAsync(x => x.MaterialId == id))
|
||||
throw new InvalidOperationException("物料仍在绑定中,不能删除");
|
||||
}
|
||||
entity.IsDeleted = true;
|
||||
entity.DeletedAt = DateTimeOffset.UtcNow;
|
||||
entity.DeletedBy = actor;
|
||||
entity.UpdatedBy = actor;
|
||||
|
||||
await Repo<T>().SoftDeleteAsync(id, version);
|
||||
await _uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
@@ -497,8 +495,7 @@ public sealed class WmsService
|
||||
}
|
||||
else
|
||||
{
|
||||
EnsureVersion(current, req.Version);
|
||||
EnsureUnlocked(current);
|
||||
await Repo<ContainerLocation>().GetEditableAsync(current.Id, req.Version);
|
||||
}
|
||||
|
||||
current.LocationType = locationType;
|
||||
@@ -540,7 +537,7 @@ public sealed class WmsService
|
||||
{
|
||||
var current = await _db.ContainerLocations.FirstOrDefaultAsync(x => x.ContainerId == containerId)
|
||||
?? throw new InvalidOperationException("容器当前位置不存在");
|
||||
EnsureUnlocked(current);
|
||||
await Repo<ContainerLocation>().GetEditableAsync(current.Id, null);
|
||||
Guid? fromStorageId = null;
|
||||
if (current.LocationType == ContainerLocationTypes.Storage && Guid.TryParse(current.LocationId, out var fs))
|
||||
fromStorageId = fs;
|
||||
@@ -614,15 +611,9 @@ public sealed class WmsService
|
||||
return current;
|
||||
}
|
||||
|
||||
/// <summary>兼容旧接口:忽略数量,按实体绑定。</summary>
|
||||
public Task<ContainerMaterial> SaveContainerMaterial(ContainerMaterialRequest req, string actor) =>
|
||||
BindMaterial(new BindMaterialRequest(req.Id, req.Version, req.ContainerId, req.MaterialId, req.Source, req.Reason, req.IsLock, req.Remark, req.Extend), actor);
|
||||
|
||||
public async Task UnbindMaterial(Guid bindingId, string actor, string reason = "", bool archive = false)
|
||||
{
|
||||
var current = await _db.ContainerMaterials.FirstOrDefaultAsync(x => x.Id == bindingId)
|
||||
?? throw new InvalidOperationException("绑定不存在");
|
||||
EnsureUnlocked(current);
|
||||
var current = await Repo<ContainerMaterial>().GetEditableAsync(bindingId, null);
|
||||
var material = await _db.Materials.FirstOrDefaultAsync(x => x.Id == current.MaterialId);
|
||||
var before = Snapshot(current);
|
||||
var now = DateTimeOffset.UtcNow;
|
||||
@@ -718,46 +709,6 @@ public sealed class WmsService
|
||||
return wh;
|
||||
}
|
||||
|
||||
public async Task MigrateLegacyAsync(string actor = "system")
|
||||
{
|
||||
var wh = await EnsureDefaultWarehouseAsync(actor);
|
||||
var areas = await _db.WarehouseAreas.Where(x => x.WarehouseId == Guid.Empty).ToListAsync();
|
||||
foreach (var a in areas)
|
||||
a.WarehouseId = wh.Id;
|
||||
|
||||
var storages = await _db.Storages.ToListAsync();
|
||||
foreach (var s in storages)
|
||||
{
|
||||
if (s.LevelNo <= 0) s.LevelNo = 1;
|
||||
if (s.DepthNo <= 0) s.DepthNo = 1;
|
||||
if (string.IsNullOrWhiteSpace(s.SiteCode)) s.SiteCode = s.SiteId;
|
||||
if (!StorageStatuses.All.Contains(s.Status))
|
||||
s.Status = StorageStatuses.Empty;
|
||||
if (!s.Enabled) s.Status = StorageStatuses.Disabled;
|
||||
}
|
||||
|
||||
var containers = await _db.Containers.ToListAsync();
|
||||
foreach (var c in containers)
|
||||
{
|
||||
if (!ContainerStatuses.All.Contains(c.Status))
|
||||
c.Status = ContainerStatuses.EmptyMaterial;
|
||||
}
|
||||
|
||||
var binds = await _db.ContainerMaterials.ToListAsync();
|
||||
foreach (var b in binds)
|
||||
{
|
||||
b.Quantity = 1;
|
||||
if (b.BoundAt == default) b.BoundAt = b.LoadedAt == default ? DateTimeOffset.UtcNow : b.LoadedAt;
|
||||
if (!ContainerMaterialStatuses.ActiveBind.Contains(b.Status))
|
||||
b.Status = ContainerMaterialStatuses.Bound;
|
||||
}
|
||||
|
||||
await _uow.SaveChangesAsync();
|
||||
|
||||
foreach (var s in storages.Where(x => x.Status != StorageStatuses.Disabled))
|
||||
await SyncOccupancyStatus(storageId: s.Id);
|
||||
}
|
||||
|
||||
public async Task<List<ContainerLocationHistory>> LocationHistory(Guid? containerId = null)
|
||||
{
|
||||
var rows = await (containerId.HasValue
|
||||
@@ -857,16 +808,13 @@ public sealed class WmsService
|
||||
});
|
||||
}
|
||||
|
||||
private IEditableRepository<T> Repo<T>()
|
||||
where T : class, IEntity<Guid>, ISoftDeletable, IVersioned, ILockable
|
||||
=> _services.GetRequiredService<IEditableRepository<T>>();
|
||||
|
||||
private Task<T> FindEditable<T>(Guid id, long? version)
|
||||
where T : class, IEntity<Guid>, ISoftDeletable, IVersioned, ILockable
|
||||
=> _services.GetRequiredService<IEditableRepository<T>>().GetEditableAsync(id, version);
|
||||
|
||||
private static void EnsureVersion(EntityBase entity, long? version)
|
||||
{
|
||||
if (version.HasValue && entity.Version != version.Value)
|
||||
throw new MiGu.DB.Abstractions.Exceptions.ConcurrencyConflictException(
|
||||
entity.GetType().Name, entity.Id, version);
|
||||
}
|
||||
=> Repo<T>().GetEditableAsync(id, version);
|
||||
|
||||
private static bool IsUniqueConstraintViolation(DbUpdateException ex)
|
||||
{
|
||||
@@ -882,19 +830,13 @@ public sealed class WmsService
|
||||
return false;
|
||||
}
|
||||
|
||||
private static void EnsureUnlocked(EntityBase entity)
|
||||
{
|
||||
if (entity.IsLock)
|
||||
throw new MiGu.DB.Abstractions.Exceptions.EntityLockedException(entity.GetType().Name, entity.Id);
|
||||
}
|
||||
|
||||
private static void StampCreate(EntityBase entity, string actor)
|
||||
private static void StampCreate(AggregateRoot entity, string actor)
|
||||
{
|
||||
entity.CreatedBy = actor;
|
||||
entity.UpdatedBy = actor;
|
||||
}
|
||||
|
||||
private void ApplyCommon(EntityBase entity, CommonRequest req, string actor)
|
||||
private void ApplyCommon(AggregateRoot entity, CommonRequest req, string actor)
|
||||
{
|
||||
entity.IsLock = req.IsLock;
|
||||
entity.Remark = req.Remark.TrimOr("");
|
||||
@@ -902,11 +844,6 @@ public sealed class WmsService
|
||||
entity.UpdatedBy = actor;
|
||||
}
|
||||
|
||||
private static async Task EnsureUnique<T>(IQueryable<T> query, System.Linq.Expressions.Expression<Func<T, bool>> predicate, string message)
|
||||
{
|
||||
if (await query.AnyAsync(predicate)) throw new InvalidOperationException(message);
|
||||
}
|
||||
|
||||
private string NormalizeExtend(string? extend)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(extend)) return "{}";
|
||||
|
||||
@@ -16,4 +16,3 @@ global using WmsDefaults = MiGu.DB.Domains.Wms.WmsDefaults;
|
||||
global using WmsTransportTaskStatuses = MiGu.DB.Domains.Transport.WmsTransportTaskStatuses;
|
||||
global using WmsTransportTriggerTypes = MiGu.DB.Domains.Transport.WmsTransportTriggerTypes;
|
||||
global using WmsReservationStatuses = MiGu.DB.Domains.Transport.WmsReservationStatuses;
|
||||
global using WmsDispatchStatuses = MiGu.DB.Domains.Transport.WmsDispatchStatuses;
|
||||
|
||||
@@ -6,10 +6,10 @@ namespace MiGu.Server.Wms;
|
||||
|
||||
public sealed class WmsTransportPlanner
|
||||
{
|
||||
private readonly PlatformDbContext _db;
|
||||
private readonly MiGuDbContext _db;
|
||||
private readonly JsonSerializerOptions _json = new(JsonSerializerDefaults.Web);
|
||||
|
||||
public WmsTransportPlanner(PlatformDbContext db) => _db = db;
|
||||
public WmsTransportPlanner(MiGuDbContext db) => _db = db;
|
||||
|
||||
public async Task<TransportCandidatePreview> PreviewAsync(WmsTransportRequest request)
|
||||
{
|
||||
|
||||
@@ -7,12 +7,12 @@ namespace MiGu.Server.Wms;
|
||||
|
||||
public sealed class WmsTransportRuleService
|
||||
{
|
||||
private readonly PlatformDbContext _db;
|
||||
private readonly MiGuDbContext _db;
|
||||
private readonly IEditableRepository<WmsTransportRule> _rules;
|
||||
private readonly IUnitOfWork _uow;
|
||||
|
||||
public WmsTransportRuleService(
|
||||
PlatformDbContext db,
|
||||
MiGuDbContext db,
|
||||
IEditableRepository<WmsTransportRule> rules,
|
||||
IUnitOfWork uow)
|
||||
{
|
||||
@@ -71,13 +71,13 @@ public sealed class WmsTransportRuleService
|
||||
await _uow.SaveChangesAsync();
|
||||
}
|
||||
|
||||
private static void StampCreate(EntityBase entity, string actor)
|
||||
private static void StampCreate(AggregateRoot entity, string actor)
|
||||
{
|
||||
entity.CreatedBy = actor;
|
||||
entity.UpdatedBy = actor;
|
||||
}
|
||||
|
||||
private static void ApplyCommon(EntityBase entity, CommonRequest req, string actor)
|
||||
private static void ApplyCommon(AggregateRoot entity, CommonRequest req, string actor)
|
||||
{
|
||||
entity.IsLock = req.IsLock;
|
||||
entity.Remark = req.Remark.TrimOr("");
|
||||
|
||||
@@ -6,14 +6,14 @@ namespace MiGu.Server.Wms;
|
||||
|
||||
public sealed class WmsTransportTaskService
|
||||
{
|
||||
private readonly PlatformDbContext _db;
|
||||
private readonly MiGuDbContext _db;
|
||||
private readonly WmsTransportPlanner _planner;
|
||||
private readonly WmsService _wms;
|
||||
private readonly IWmsDispatchAdapter _dispatch;
|
||||
private readonly JsonSerializerOptions _json = new(JsonSerializerDefaults.Web);
|
||||
|
||||
public WmsTransportTaskService(
|
||||
PlatformDbContext db,
|
||||
MiGuDbContext db,
|
||||
WmsTransportPlanner planner,
|
||||
WmsService wms,
|
||||
IWmsDispatchAdapter dispatch)
|
||||
@@ -331,7 +331,7 @@ public sealed class WmsTransportTaskService
|
||||
GeneratedAt = DateTimeOffset.UtcNow
|
||||
};
|
||||
|
||||
private static void StampCreate(EntityBase entity, string actor)
|
||||
private static void StampCreate(AggregateRoot entity, string actor)
|
||||
{
|
||||
entity.CreatedBy = actor;
|
||||
entity.UpdatedBy = actor;
|
||||
|
||||
Reference in New Issue
Block a user