diff --git a/MiGu.DB/Domains/Migrators/DataMigrators.cs b/MiGu.DB/Domains/Migrators/DataMigrators.cs index 8bdfece..6e3a643 100644 --- a/MiGu.DB/Domains/Migrators/DataMigrators.cs +++ b/MiGu.DB/Domains/Migrators/DataMigrators.cs @@ -29,35 +29,16 @@ public sealed class SimpleFieldsCarTypeBackfillMigrator : IDataMigrator } } -/// -/// 把库内旧状态字符串刷成规范枚举名(Available/Idle→Empty,Occupied→FullContainer)。 -/// 仅 Sqlite 表名/列名;绕过枚举 HasConversion(ExecuteUpdate + 字符串比较会 InvalidCast)。 -/// -public sealed class LegacyStatusNormalizationMigrator : IDataMigrator +/// 库区 LayoutMode 空值补 Flat(raw UPDATE,避开枚举 converter)。 +public sealed class AreaLayoutModeDefaultMigrator : IDataMigrator { public int Order => 15; - public string Name => "Wms.LegacyStatusNormalization"; + public string Name => "Wms.AreaLayoutModeDefault"; public async Task MigrateAsync(DbContext db, CancellationToken ct = default) { if (db is not MiGuDbContext) return; - await db.Database.ExecuteSqlRawAsync( - """ - UPDATE wms_storages - SET Status = 'Empty' - WHERE Status IN ('Available', 'Idle') - """, - ct); - - await db.Database.ExecuteSqlRawAsync( - """ - UPDATE wms_storages - SET Status = 'FullContainer' - WHERE Status = 'Occupied' - """, - ct); - await db.Database.ExecuteSqlRawAsync( """ UPDATE wms_areas @@ -98,7 +79,7 @@ public static class DataMigratorRegistration public static IServiceCollection AddMiGuDataMigrators(this IServiceCollection services) { services.AddSingleton(); - services.AddSingleton(); + services.AddSingleton(); services.AddSingleton(); return services; } diff --git a/MiGu.DB/Domains/Transport/TransportEntities.cs b/MiGu.DB/Domains/Transport/TransportEntities.cs index c071a36..a35a05b 100644 --- a/MiGu.DB/Domains/Transport/TransportEntities.cs +++ b/MiGu.DB/Domains/Transport/TransportEntities.cs @@ -96,9 +96,3 @@ public static class WmsReservationStatuses public const WmsReservationStatus Released = WmsReservationStatus.Released; public const WmsReservationStatus Expired = WmsReservationStatus.Expired; } - -public static class WmsDispatchStatuses -{ - public const WmsDispatchStatus Dispatched = WmsDispatchStatus.Dispatched; - public const WmsDispatchStatus Failed = WmsDispatchStatus.Failed; -} diff --git a/MiGu.DB/Domains/Transport/TransportEnums.cs b/MiGu.DB/Domains/Transport/TransportEnums.cs index 06621d4..014477c 100644 --- a/MiGu.DB/Domains/Transport/TransportEnums.cs +++ b/MiGu.DB/Domains/Transport/TransportEnums.cs @@ -27,9 +27,3 @@ public enum WmsReservationStatus Released, Expired } - -public enum WmsDispatchStatus -{ - Dispatched, - Failed -} diff --git a/MiGu.DB/Domains/Wms/WmsEnums.cs b/MiGu.DB/Domains/Wms/WmsEnums.cs index 55f3bae..e5bf428 100644 --- a/MiGu.DB/Domains/Wms/WmsEnums.cs +++ b/MiGu.DB/Domains/Wms/WmsEnums.cs @@ -2,7 +2,6 @@ namespace MiGu.DB.Domains.Wms; /// /// WMS 状态/类型枚举。列以字符串存储(见约定 HasConversion<string>),成员名即库内合法值。 -/// 旧别名(Available/Idle/Occupied 等)不进入枚举,由 LegacyStatusNormalizationMigrator 刷库。 /// public enum AreaLayoutMode { diff --git a/MiGu.DB/Domains/Wms/WmsStatusHelpers.cs b/MiGu.DB/Domains/Wms/WmsStatusHelpers.cs index 23d6e5a..5961b1d 100644 --- a/MiGu.DB/Domains/Wms/WmsStatusHelpers.cs +++ b/MiGu.DB/Domains/Wms/WmsStatusHelpers.cs @@ -33,17 +33,8 @@ public static class StorageStatuses public static readonly HashSet All = new() { Empty, EmptyContainer, FullContainer, Disabled }; - /// - /// 写路径归一:兼容历史字符串 Available/Idle/Occupied。 - /// 库内残留旧值须靠 DataMigrator 刷掉,不能依赖 converter 读侧归一(WHERE 会漏行)。 - /// - public static StorageStatus Normalize(string? status) => status switch - { - "Available" or "Idle" => Empty, - "Occupied" => FullContainer, - _ when Enum.TryParse(status, true, out var e) && All.Contains(e) => e, - _ => Empty - }; + public static StorageStatus ParseOr(string? value, StorageStatus fallback = StorageStatus.Empty) => + Enum.TryParse(value, true, out var e) && All.Contains(e) ? e : fallback; } public static class ContainerStatuses diff --git a/MiGu.DB/MIGRATIONS.md b/MiGu.DB/MIGRATIONS.md index ec1f1ea..2ca301d 100644 --- a/MiGu.DB/MIGRATIONS.md +++ b/MiGu.DB/MIGRATIONS.md @@ -94,7 +94,7 @@ dotnet ef migrations add <迁移名称> ` ### 3.4 应用到本地库 -启动 `MiGu.Server` 即可(`EnsurePlatformDatabaseAsync` → `MigrateMiGuDbAsync`),或: +启动 `MiGu.Server` 即可(`Program.cs` 直接调用 `MigrateMiGuDbAsync`),或: ```powershell dotnet ef database update ` @@ -151,7 +151,7 @@ dotnet ef database update <目标迁移名> ` → **[MiGu.Server/README.md · 数据库启动流程](../MiGu.Server/README.md#数据库启动流程)** -摘要:`EnsurePlatformDatabaseAsync` → `MigrateMiGuDbAsync`;`Database:SchemaMode` 为 `EnsureCreated` 或 `Migrate`;之后按需跑 `IDataMigrator`。 +摘要:`MigrateMiGuDbAsync`;`Database:SchemaMode` 为 `EnsureCreated` 或 `Migrate`;之后按需跑 `IDataMigrator`。 **Migrate 基线:** 已有表、无 History 时,会把**全部** pending Migration 写入 `__EFMigrationsHistory`(假定 EnsureCreated 库已对齐模型 tip)。发版前若开发期改过模型,须先 `migrations add` 再切 `Migrate`。 @@ -165,7 +165,7 @@ dotnet ef database update <目标迁移名> ` | 刷旧枚举字符串、回填列 | 新增 `IDataMigrator`,注册到 `AddMiGuDataMigrators` | | 开发机整库清空重来 | 删 `data/platform.db*` 后启动(等同空库 Migrate);**不要**在生产用 EnsureDeleted | -注意:带 `HasConversion` 的枚举列,用 `ExecuteUpdate` + 原始字符串比较可能触发转换异常;旧值刷库需绕过 converter(参见 `LegacyStatusNormalizationMigrator`)。 +注意:带 `HasConversion` 的枚举列,用 `ExecuteUpdate` + 原始字符串比较可能触发转换异常;空 LayoutMode 等特例用 raw UPDATE(参见 `AreaLayoutModeDefaultMigrator`)。 --- diff --git a/MiGu.DB/README.md b/MiGu.DB/README.md index 451fd30..debc756 100644 --- a/MiGu.DB/README.md +++ b/MiGu.DB/README.md @@ -17,7 +17,7 @@ ```csharp builder.Services.AddPlatformPersistence(builder.Configuration); // 内部 AddMiGuDb -await app.Services.EnsurePlatformDatabaseAsync(); // 内部 MigrateMiGuDbAsync +await app.Services.MigrateMiGuDbAsync(); ``` **启动流程、SchemaMode、开发/发版配置说明见 [MiGu.Server/README.md · 数据库启动流程](../MiGu.Server/README.md#数据库启动流程)。** @@ -27,7 +27,6 @@ await app.Services.EnsurePlatformDatabaseAsync(); // 内部 Migrat ## 实体与枚举 - 业务状态字段为 **enum**,约定自动 `HasConversion()`(严格 1:1,不做读侧归一)。 -- 旧库值(如 `Available`/`Idle`)由 `LegacyStatusNormalizationMigrator` 一次性刷成规范名。 - `Version` 为乐观并发令牌;软删走全局 `HasQueryFilter`。 - 复数辅助类(`StorageStatuses`、`LocationKinds`…)负责 API 字符串解析与集合校验。 diff --git a/MiGu.Server/Controllers/FleetController.cs b/MiGu.Server/Controllers/FleetController.cs index cea75a9..1ada520 100644 --- a/MiGu.Server/Controllers/FleetController.cs +++ b/MiGu.Server/Controllers/FleetController.cs @@ -15,9 +15,9 @@ public sealed class FleetController : ControllerBase private readonly FleetHealthService _health; private readonly CdmTaskSyncer _cdmSyncer; private readonly AlarmCollector _alarmCollector; - private readonly PlatformDbContext _db; + private readonly MiGuDbContext _db; - public FleetController(FleetHealthService health, CdmTaskSyncer cdmSyncer, AlarmCollector alarmCollector, PlatformDbContext db) + public FleetController(FleetHealthService health, CdmTaskSyncer cdmSyncer, AlarmCollector alarmCollector, MiGuDbContext db) { _health = health; _cdmSyncer = cdmSyncer; diff --git a/MiGu.Server/Controllers/WmsController.cs b/MiGu.Server/Controllers/WmsController.cs index 908c615..feccc1a 100644 --- a/MiGu.Server/Controllers/WmsController.cs +++ b/MiGu.Server/Controllers/WmsController.cs @@ -192,10 +192,6 @@ public sealed class WmsController : ControllerBase public Task> ContainerMaterials([FromQuery] string? q, [FromQuery] Guid? containerId) => _service.ContainerMaterials(q, containerId); - [HttpPost("container-materials")] - public async Task SaveContainerMaterial([FromBody] BindMaterialRequest req) => - Ok(await _service.BindMaterial(req, User.ActorName())); - [HttpPost("container-materials/bind")] public async Task BindMaterial([FromBody] BindMaterialRequest req) => Ok(await _service.BindMaterial(req, User.ActorName())); diff --git a/MiGu.Server/Dashboard/DashboardShortcutService.cs b/MiGu.Server/Dashboard/DashboardShortcutService.cs index 7e3f668..9343b1e 100644 --- a/MiGu.Server/Dashboard/DashboardShortcutService.cs +++ b/MiGu.Server/Dashboard/DashboardShortcutService.cs @@ -7,7 +7,7 @@ namespace MiGu.Server.Dashboard; public sealed class DashboardShortcutService { - private readonly PlatformDbContext _db; + private readonly MiGuDbContext _db; private readonly RbacStore _rbac; private static readonly JsonSerializerOptions JsonOpts = new() @@ -15,7 +15,7 @@ public sealed class DashboardShortcutService PropertyNamingPolicy = JsonNamingPolicy.CamelCase }; - public DashboardShortcutService(PlatformDbContext db, RbacStore rbac) + public DashboardShortcutService(MiGuDbContext db, RbacStore rbac) { _db = db; _rbac = rbac; diff --git a/MiGu.Server/Fleet/AlarmCollector.cs b/MiGu.Server/Fleet/AlarmCollector.cs index 6c2e919..6defd45 100644 --- a/MiGu.Server/Fleet/AlarmCollector.cs +++ b/MiGu.Server/Fleet/AlarmCollector.cs @@ -76,7 +76,7 @@ public sealed class AlarmCollector try { using var scope = _scopeFactory.CreateScope(); - var db = scope.ServiceProvider.GetRequiredService(); + var db = scope.ServiceProvider.GetRequiredService(); await ReconcileAsync(db, current, ct); } catch (Exception ex) @@ -161,7 +161,7 @@ public sealed class AlarmCollector } } - private static async Task ReconcileAsync(PlatformDbContext db, Dictionary current, CancellationToken ct) + private static async Task ReconcileAsync(MiGuDbContext db, Dictionary current, CancellationToken ct) { var now = DateTimeOffset.UtcNow; var active = await db.VehicleAlarms.Where(a => a.Status == "active").ToListAsync(ct); diff --git a/MiGu.Server/Fleet/CdmTaskSync.cs b/MiGu.Server/Fleet/CdmTaskSync.cs index 1609426..7c7342a 100644 --- a/MiGu.Server/Fleet/CdmTaskSync.cs +++ b/MiGu.Server/Fleet/CdmTaskSync.cs @@ -80,7 +80,7 @@ public sealed class CdmTaskSyncer try { using var scope = _scopeFactory.CreateScope(); - var db = scope.ServiceProvider.GetRequiredService(); + var db = scope.ServiceProvider.GetRequiredService(); await UpsertAsync(db, dtos, ct); } catch (Exception ex) @@ -126,7 +126,7 @@ public sealed class CdmTaskSyncer } } - private static async Task UpsertAsync(PlatformDbContext db, IReadOnlyList dtos, CancellationToken ct) + private static async Task UpsertAsync(MiGuDbContext db, IReadOnlyList dtos, CancellationToken ct) { var valid = dtos.Where(d => !string.IsNullOrWhiteSpace(d.id)).ToList(); if (valid.Count == 0) return; diff --git a/MiGu.Server/GlobalUsings.Db.cs b/MiGu.Server/GlobalUsings.Db.cs index b4c7f3b..c3811e3 100644 --- a/MiGu.Server/GlobalUsings.Db.cs +++ b/MiGu.Server/GlobalUsings.Db.cs @@ -1,5 +1,5 @@ -global using PlatformDbContext = MiGu.DB.Kernel.Context.MiGuDbContext; -global using EntityBase = MiGu.DB.Kernel.Entities.AggregateRoot; +global using MiGu.DB.Kernel.Context; +global using MiGu.DB.Kernel.Entities; global using SimpleField = MiGu.DB.Domains.SimpleFields.SimpleField; global using UserDashboardShortcut = MiGu.DB.Domains.Dashboard.UserDashboardShortcut; global using Warehouse = MiGu.DB.Domains.Wms.Warehouse; diff --git a/MiGu.Server/Persistence/PlatformPersistence.cs b/MiGu.Server/Persistence/PlatformPersistence.cs index 5750a3b..a176778 100644 --- a/MiGu.Server/Persistence/PlatformPersistence.cs +++ b/MiGu.Server/Persistence/PlatformPersistence.cs @@ -21,8 +21,4 @@ public static class PlatformPersistence services.AddScoped(); return services; } - - /// 启动建库/迁移入口(内部即 MigrateMiGuDbAsync:Migrate + 基线 + DataMigrator)。 - public static Task EnsurePlatformDatabaseAsync(this IServiceProvider services) - => services.MigrateMiGuDbAsync(); } diff --git a/MiGu.Server/Program.cs b/MiGu.Server/Program.cs index 8dd4939..e0d93cc 100644 --- a/MiGu.Server/Program.cs +++ b/MiGu.Server/Program.cs @@ -8,6 +8,7 @@ using MiGu.Server.Configs; using MiGu.Server.Launcher; using MiGu.Server.OpenApi; using MiGu.Server.Ota; +using MiGu.DB.Kernel.Hosting; using MiGu.Server.Persistence; using Yarp.ReverseProxy.Transforms; @@ -279,7 +280,7 @@ builder.Services.AddHostedService(); builder.Services.AddHttpClient(nameof(WatchDogClient)); var app = builder.Build(); -await app.Services.EnsurePlatformDatabaseAsync(); +await app.Services.MigrateMiGuDbAsync(); // 启动期主动构造 JwtIssuer / InternalTokenStore:让 secret 校验日志在请求来之前打印。 _ = app.Services.GetRequiredService(); diff --git a/MiGu.Server/README.md b/MiGu.Server/README.md index 73727f7..6a94aef 100644 --- a/MiGu.Server/README.md +++ b/MiGu.Server/README.md @@ -110,8 +110,8 @@ AddPlatformPersistence(configuration) # 注册 AddMiGuDb + Wms/SimpleFields builder.Build() │ ▼ -EnsurePlatformDatabaseAsync() # → MiGu.DB.MigrateMiGuDbAsync() - │ # Schema 初始化 + 可选 IDataMigrator +MigrateMiGuDbAsync() # Schema 初始化 + 可选 IDataMigrator + │ ▼ … 其余中间件 … UseMiddleware # 请求级写入 IActorContext(审计戳) diff --git a/MiGu.Server/SimpleFields/SimpleFieldService.cs b/MiGu.Server/SimpleFields/SimpleFieldService.cs index e32e214..47bb27b 100644 --- a/MiGu.Server/SimpleFields/SimpleFieldService.cs +++ b/MiGu.Server/SimpleFields/SimpleFieldService.cs @@ -6,9 +6,9 @@ namespace MiGu.Server.SimpleFields; public sealed class SimpleFieldService { - private readonly PlatformDbContext _db; + private readonly MiGuDbContext _db; - public SimpleFieldService(PlatformDbContext db) => _db = db; + public SimpleFieldService(MiGuDbContext db) => _db = db; public async Task> ListAsync(string? fieldType = null, string? carType = null, string? q = null) { diff --git a/MiGu.Server/Wms/WmsModels.cs b/MiGu.Server/Wms/WmsModels.cs index 7464f33..0bb0d60 100644 --- a/MiGu.Server/Wms/WmsModels.cs +++ b/MiGu.Server/Wms/WmsModels.cs @@ -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); diff --git a/MiGu.Server/Wms/WmsReferenceValidator.cs b/MiGu.Server/Wms/WmsReferenceValidator.cs index b435a31..72a8870 100644 --- a/MiGu.Server/Wms/WmsReferenceValidator.cs +++ b/MiGu.Server/Wms/WmsReferenceValidator.cs @@ -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; } diff --git a/MiGu.Server/Wms/WmsService.cs b/MiGu.Server/Wms/WmsService.cs index e39324f..7e563ab 100644 --- a/MiGu.Server/Wms/WmsService.cs +++ b/MiGu.Server/Wms/WmsService.cs @@ -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().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().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().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().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().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().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().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(Guid id, long? version, string actor) where T : EntityBase + public async Task DeleteEntity(Guid id, long? version, string actor) + where T : class, IEntity, ISoftDeletable, IVersioned, ILockable, IAuditable { - var entity = await FindEditable(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().SoftDeleteAsync(id, version); await _uow.SaveChangesAsync(); } @@ -497,8 +495,7 @@ public sealed class WmsService } else { - EnsureVersion(current, req.Version); - EnsureUnlocked(current); + await Repo().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().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; } - /// 兼容旧接口:忽略数量,按实体绑定。 - public Task 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().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> LocationHistory(Guid? containerId = null) { var rows = await (containerId.HasValue @@ -857,16 +808,13 @@ public sealed class WmsService }); } + private IEditableRepository Repo() + where T : class, IEntity, ISoftDeletable, IVersioned, ILockable + => _services.GetRequiredService>(); + private Task FindEditable(Guid id, long? version) where T : class, IEntity, ISoftDeletable, IVersioned, ILockable - => _services.GetRequiredService>().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().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(IQueryable query, System.Linq.Expressions.Expression> predicate, string message) - { - if (await query.AnyAsync(predicate)) throw new InvalidOperationException(message); - } - private string NormalizeExtend(string? extend) { if (string.IsNullOrWhiteSpace(extend)) return "{}"; diff --git a/MiGu.Server/Wms/WmsStatusAliases.cs b/MiGu.Server/Wms/WmsStatusAliases.cs index b5be817..ad4578e 100644 --- a/MiGu.Server/Wms/WmsStatusAliases.cs +++ b/MiGu.Server/Wms/WmsStatusAliases.cs @@ -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; diff --git a/MiGu.Server/Wms/WmsTransportPlanner.cs b/MiGu.Server/Wms/WmsTransportPlanner.cs index f978bab..926b532 100644 --- a/MiGu.Server/Wms/WmsTransportPlanner.cs +++ b/MiGu.Server/Wms/WmsTransportPlanner.cs @@ -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 PreviewAsync(WmsTransportRequest request) { diff --git a/MiGu.Server/Wms/WmsTransportRuleService.cs b/MiGu.Server/Wms/WmsTransportRuleService.cs index d207288..fbd6fbc 100644 --- a/MiGu.Server/Wms/WmsTransportRuleService.cs +++ b/MiGu.Server/Wms/WmsTransportRuleService.cs @@ -7,12 +7,12 @@ namespace MiGu.Server.Wms; public sealed class WmsTransportRuleService { - private readonly PlatformDbContext _db; + private readonly MiGuDbContext _db; private readonly IEditableRepository _rules; private readonly IUnitOfWork _uow; public WmsTransportRuleService( - PlatformDbContext db, + MiGuDbContext db, IEditableRepository 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(""); diff --git a/MiGu.Server/Wms/WmsTransportTaskService.cs b/MiGu.Server/Wms/WmsTransportTaskService.cs index fb842f5..c387f2f 100644 --- a/MiGu.Server/Wms/WmsTransportTaskService.cs +++ b/MiGu.Server/Wms/WmsTransportTaskService.cs @@ -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; diff --git a/frontends/apps/simple-platform-vue/src/types/wms.ts b/frontends/apps/simple-platform-vue/src/types/wms.ts index 7e664a5..9b02643 100644 --- a/frontends/apps/simple-platform-vue/src/types/wms.ts +++ b/frontends/apps/simple-platform-vue/src/types/wms.ts @@ -206,7 +206,6 @@ export interface MasterDataPayload { code: string name: string type: string - status: string enabled: boolean sortOrder: number isLock: boolean diff --git a/frontends/apps/simple-platform-vue/src/views/admin/WarehouseManagementView.vue b/frontends/apps/simple-platform-vue/src/views/admin/WarehouseManagementView.vue index 12794ee..1eb5f81 100644 --- a/frontends/apps/simple-platform-vue/src/views/admin/WarehouseManagementView.vue +++ b/frontends/apps/simple-platform-vue/src/views/admin/WarehouseManagementView.vue @@ -563,7 +563,7 @@ async function submitDialog() { ...masterForm, barcode: containerBarcode.value, length: 0, width: 0, height: 0, - status: 'EmptyMaterial' + status: containers.value.find((c) => c.id === masterForm.id)?.status ?? 'EmptyMaterial' }) } else if (kind === 'storage') await wmsApi.saveStorage(storageForm) @@ -683,7 +683,6 @@ function baseMaster(row?: WarehouseArea | Container): MasterDataPayload { code: row?.code ?? '', name: row?.name ?? '', type: 'type' in (row ?? {}) ? (row as WarehouseArea).type : (row as Container | undefined)?.containerType ?? 'Box', - status: (row as Container | undefined)?.status ?? 'Idle', enabled: row?.enabled ?? true, sortOrder: (row as WarehouseArea | undefined)?.sortOrder ?? 0 }