移除历史状态归一化及相关遗留逻辑

- 移除 LegacyStatusNormalizationMigrator,仅保留 AreaLayoutModeDefaultMigrator
- 删除 WmsDispatchStatus 枚举、常量和全局 using
- 统一所有 DbContext 注入为 MiGuDbContext,移除 PlatformDbContext
- 服务实体操作统一用 IEditableRepository<T>,移除本地实现
- 移除 WmsService 的 MigrateLegacyAsync 方法
- 精简接口模型,移除部分字段和请求体
- 前端保存容器时 status 字段取自已有数据,移除写死默认值
- 更新文档,去除旧说明,统一数据库初始化流程
This commit is contained in:
2026-08-03 09:41:03 +08:00
parent 1f72488a8d
commit 125372b157
26 changed files with 63 additions and 183 deletions
+24 -87
View File
@@ -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 "{}";