| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- using System.Diagnostics;
- using Admin.NET.Plugin.AiDOP.ChatBI;
- using Admin.NET.Plugin.AiDOP.Entity;
- using Admin.NET.Plugin.AiDOP.Entity.S0.Manufacturing;
- using Admin.NET.Plugin.AiDOP.Entity.S0.Quality;
- using Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
- using Admin.NET.Plugin.AiDOP.Entity.S0.Supply;
- using Admin.NET.Plugin.AiDOP.Entity.S8;
- using Admin.NET.Plugin.AiDOP.Entity.S8.OrderFlow;
- using Admin.NET.Plugin.AiDOP.Order;
- using Admin.NET.Plugin.AiDOP.DataPlatform.Executors;
- using Admin.NET.Plugin.AiDOP.DataPlatform.HotWatch;
- using Admin.NET.Plugin.AiDOP.Entity.DataPlatform;
- using Admin.NET.Plugin.AiDOP.Infrastructure;
- using Admin.NET.Plugin.AiDOP.MaterialWarehouse;
- using Admin.NET.Plugin.AiDOP.WorkOrder;
- using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.AspNetCore.Mvc;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.Hosting;
- using SqlSugar;
- namespace Admin.NET.Plugin.AiDOP;
- /// <summary>
- /// AiDOP 插件启动配置(按需扩展 DI)
- /// </summary>
- [AppStartup(900)]
- public class Startup : AppStartup
- {
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddHttpClient();
- services.AddHttpClient("AidopChatBI.DeepSeek");
- services.AddScoped<DeepSeekChatClient>();
- services.AddScoped<ChatBIService>();
- services.AddConfigurableOptions<S8ActiveFlowWatchOptions>();
- services.AddConfigurableOptions<S8MasterDataOptions>();
- services.AddConfigurableOptions<AidopPickBillOptions>();
- services.AddConfigurableOptions<AidopStockOptions>();
- services.AddConfigurableOptions<AidopInventoryOptions>();
- services.AddTransient<IInventoryBalanceReader, InventoryBalanceReader>();
- services.AddTransient<IInventoryTransactionReader, StdInventoryTransactionReader>();
- // WP9:IPickBillCreator 唯一实现为 Facade(避免 Local/Mes 多实现竞注)
- services.AddTransient<IPickBillCreator, PickBillCreatorFacade>();
- services.AddScoped<AdoS0ExceptionFilter>();
- services.AddScoped<AdoS0ResultFilter>();
- services.AddScoped<AdoS0ReferenceChecker>();
- services.Configure<MvcOptions>(options =>
- {
- options.Filters.AddService<AdoS0ExceptionFilter>();
- options.Filters.AddService<AdoS0ResultFilter>();
- });
- // MDP 出站事件驱动:入队 Pulse → 立即推送;Period Job 仅兜底
- services.AddHostedService<MdpOutboxPushWorker>();
- services.AddHostedService<MdpHotWatchWorker>();
- services.AddHostedService<Admin.NET.Plugin.AiDOP.DataPlatform.S1Refresh.S1DashboardRebuildWorker>();
- services.AddHostedService<Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.ModuleRebuildWorker>();
- services.AddSingleton<Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.IModuleRebuildCapability, Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.ModuleRebuildCapability>();
- services.AddTransient<Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.IModuleRebuildJobStore, Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.ModuleRebuildJobStore>();
- services.AddTransient<Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.IModuleRebuildLock, Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.ModuleRebuildLock>();
- services.AddTransient<Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.IModuleRebuildHandler, Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.S1ModuleRebuildHandler>();
- services.AddTransient<Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.IModuleRebuildHandler, Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.S2ModuleRebuildHandler>();
- services.AddTransient<Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.IModuleRebuildHandler, Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.S3ModuleRebuildHandler>();
- services.AddTransient<Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.IModuleRebuildHandler, Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.S4ModuleRebuildHandler>();
- services.AddTransient<Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.IModuleRebuildHandler, Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.S5ModuleRebuildHandler>();
- services.AddTransient<Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.IModuleRebuildHandler, Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.S6ModuleRebuildHandler>();
- services.AddTransient<Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.IModuleRebuildHandler, Admin.NET.Plugin.AiDOP.DataPlatform.MdpRebuild.S7ModuleRebuildHandler>();
- // S8-DATASET-FOUNDATION-HARDENING-2:S8 取数底座的接口映射显式注册
- // (与上方 IModuleRebuildLock 同一先例:本项目不依赖 ITransient 自动按接口注册)。
- services.AddTransient<Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.IS8DatasetCatalog, Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.S8DatasetCatalog>();
- // S8-STANDARD-DATASET-HARD-CUTOVER-1:IS8LegacySqlDataProvider 及其实现已物理删除。
- // S8-RULE01-PURCHASE-DELIVERY-A:接入第一个真实业务数据集 PURCHASE_DELIVERY。
- // Catalog 有定义 + Registry 有实现,Enable Gate 才判 READY:
- // 缺定义 → 规则连保存都过不了(dataset_not_defined);
- // 缺实现 → 可存草稿但不可启用(dataset_provider_not_registered)。
- // 两个接口都必须显式注册(同上,不依赖 ITransient 自动按接口注册)。
- services.AddTransient<Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.IS8DatasetDefinitionSource, Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.Providers.S8BusinessDatasetDefinitions>();
- services.AddTransient<Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.IS8MonitoringDataProvider, Admin.NET.Plugin.AiDOP.Service.S8.Rules.DataAccess.Providers.S8PurchaseDeliveryDataProvider>();
- // S8-RULE-GOVERNANCE-BATCH1:规则定义目录。业务语义从此只存在于代码里,
- // ado_s8_watch_rule 上的同名列降级为 provisioning 维护的只读投影。
- //
- // 注册为 **Singleton**(数据集目录是 Transient):目录构造期会做完整性校验,
- // 而 Transient 的失败形态是「首次解析该服务的那个请求 500」,启动日志干净但目录其实是坏的。
- // Singleton + Configure 里强制解析一次 = 定义写错就起不来,见下方 Configure。
- services.AddSingleton<Admin.NET.Plugin.AiDOP.Service.S8.Rules.Definitions.IS8RuleDefinitionSource, Admin.NET.Plugin.AiDOP.Service.S8.Rules.Definitions.S8PurchaseDeliveryRuleDefinitions>();
- services.AddSingleton<Admin.NET.Plugin.AiDOP.Service.S8.Rules.Definitions.IS8RuleCatalog, Admin.NET.Plugin.AiDOP.Service.S8.Rules.Definitions.S8RuleCatalog>();
- // S8-RULE-GOVERNANCE-BATCH2:代码定义 → 每租户运行策略的供给服务。
- // ITransient 在本仓不做按接口自动注册,且它没有接口,故显式登记具体类型。
- services.AddTransient<Admin.NET.Plugin.AiDOP.Service.S8.S8RuleProvisioningService>();
- }
- /// <summary>
- /// 开发环境下为 Demo 表执行 CodeFirst(表已存在则跳过变更,行为取决于 SqlSugar 配置)。
- /// </summary>
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
- {
- // S8-RULE-GOVERNANCE-BATCH1:强制解析一次规则目录,把「定义写错」变成启动失败。
- // 刻意**不 try/catch**:重复 rule_code、rule_type 不在词表、TIMEOUT 却没给判定语义,
- // 都是发布事故。吞掉它只会让服务带着一个坏目录起来,然后在某条规则跑到那一 tick 时
- // 才在后台日志里报错——那时页面上一切正常,没人会看见。
- {
- var ruleCatalog = app.ApplicationServices
- .GetRequiredService<Admin.NET.Plugin.AiDOP.Service.S8.Rules.Definitions.IS8RuleCatalog>();
- Trace.TraceInformation($"Ai-DOP S8RuleCatalog: {ruleCatalog.Definitions.Count} rule definition(s) loaded");
- }
- try
- {
- AidopMenuLinkSync.EnsureLinked(app.ApplicationServices);
- }
- catch (Exception ex)
- {
- Trace.TraceWarning("Ai-DOP AidopMenuLinkSync: " + ex);
- }
- // S8-RULE-GOVERNANCE-BATCH2:启动期对账 —— 确保每个启用租户都拥有全部代码定义规则的运行策略行。
- //
- // 失败策略沿用本方法内既有的 sync 惯例(AidopMenuLinkSync 等):**记录后放行,不阻断启动**。
- // 理由是这段 Configure 与 S0–S7 共用同一个宿主,供给失败不该把整个平台拖下线;
- // 且新 provision 的规则一律 disabled,缺行的后果是"该租户暂时看不到这条规则",
- // 不是数据错误。异常对象完整落日志,不做静默吞掉 —— 「以为同步成功」比同步失败更危险。
- //
- // 与之对照:规则目录自身构造失败(重复定义 / 定义写错)**不在此列**,
- // 它在上方 GetRequiredService 处直接抛出、阻断启动,因为那是代码包本身有问题。
- try
- {
- using var provisioningScope = app.ApplicationServices.CreateScope();
- var provisioning = provisioningScope.ServiceProvider
- .GetRequiredService<Admin.NET.Plugin.AiDOP.Service.S8.S8RuleProvisioningService>();
- var summary = provisioning.SyncAsync().GetAwaiter().GetResult();
- Trace.TraceInformation(
- $"Ai-DOP S8RuleProvisioning: tenants={summary.TenantCount} definitions={summary.DefinitionCount} "
- + $"created={summary.CreatedCount} refreshed={summary.RefreshedCount} unchanged={summary.UnchangedCount} "
- + $"orphaned={summary.OrphanedCount} duplicate={summary.DuplicateCount}");
- }
- catch (Exception ex)
- {
- Trace.TraceError("Ai-DOP S8RuleProvisioning FAILED(本次启动未完成规则运行策略对账): " + ex);
- }
- try
- {
- using var scopeS4 = app.ApplicationServices.CreateScope();
- var dbS4 = scopeS4.ServiceProvider.GetRequiredService<ISqlSugarClient>();
- dbS4.CodeFirst.InitTables(
- typeof(AdoSmartOpsLayoutItem),
- typeof(AdoSmartOpsHomeModule),
- typeof(AdoSmartOpsKpiMaster),
- typeof(AdoSmartOpsDashboardPageConfig),
- typeof(AdoSmartOpsDashboardWidget),
- typeof(AdoSmartOpsImprovementPlan),
- typeof(AdoMdpHotWatch),
- typeof(Admin.NET.Plugin.AiDOP.Entity.SmartOps.AdoS1DashboardRebuildJob),
- typeof(Admin.NET.Plugin.AiDOP.Entity.SmartOps.AdoModuleDashboardRebuildJob),
- typeof(Admin.NET.Plugin.AiDOP.Entity.SmartOps.AdoMdpRebuildLock),
- typeof(AdoTenantMenuSuppression));
- AidopTenantMigration.MigrateOldTenantId(dbS4);
- AidopKpiMasterSeed.EnsureSeed(dbS4);
- AidopKpiMasterSeed.EnsureSeed(dbS4, 1300000000888);
- var kpiTenantIds = dbS4.Queryable<AdoSmartOpsKpiMaster>()
- .Select(x => x.TenantId)
- .Distinct()
- .ToList();
- foreach (var tenantId in kpiTenantIds.Where(x => x > 0))
- AidopKpiMasterSeed.EnsureSeed(dbS4, tenantId!.Value);
- AidopImprovementApprovalSeed.EnsureSeed(dbS4);
- Admin.NET.Plugin.AiDOP.Infrastructure.IqcInspBillFlowSeed.EnsureSeed(dbS4);
- Admin.NET.Plugin.AiDOP.Infrastructure.IpqcInspectionFlowSeed.EnsureSeed(dbS4);
- Admin.NET.Plugin.AiDOP.Infrastructure.S6ProcessInspectionFlowSeed.EnsureSeed(dbS4);
- Admin.NET.Plugin.AiDOP.Infrastructure.FqcInspBillFlowSeed.EnsureSeed(dbS4);
- }
- catch (Exception ex)
- {
- Trace.TraceWarning("Ai-DOP CodeFirst/S4 seed: " + ex);
- }
- if (!env.IsDevelopment())
- return;
- // 客户/物料/优先级等;远端若已是遗留表(已有主键),CodeFirst 会报 Multiple primary key,不可让启动失败。
- try
- {
- using var scope = app.ApplicationServices.CreateScope();
- var db = scope.ServiceProvider.GetRequiredService<ISqlSugarClient>();
- db.CodeFirst.InitTables(
- typeof(AdoS0CustMaster),
- typeof(AdoS0ItemMaster),
- typeof(AdoS0ItemSubstituteDetail),
- typeof(AdoS0PriorityCode),
- typeof(AdoS0ProductStructureOp),
- typeof(AdoS0StdOpMaster),
- typeof(AdoS0LineMaster),
- typeof(AdoS0MfgRoutingOpDetail),
- typeof(AdoS0MfgPersonSkillAssignment),
- typeof(AdoS0MfgWorkOrderControl),
- typeof(AdoS0MfgPersonSkill),
- typeof(AdoS0MfgLinePost),
- typeof(AdoS0MfgWorkCenter),
- typeof(AdoS0MfgLineMaterial),
- typeof(AdoS0ProcessFlowCard),
- typeof(AdoS0OrderScheduleCycle),
- typeof(AdoS0MfgElementParam),
- typeof(AdoS0MfgMaterialProcessElement),
- typeof(AdoS0MfgPreprocessElement),
- typeof(AdoS0MfgPreprocessElementParam),
- typeof(AdoS0ImageType),
- typeof(AdoS0QualitySegmentImage),
- typeof(AdoS0QmsQualityBaseType),
- typeof(AdoS0QmsFqcInspectionSpec),
- typeof(AdoS0QmsFqcInspectionSpecEntry),
- typeof(AdoS0QmsOqcInspectionSpec),
- typeof(AdoS0QmsOqcInspectionSpecEntry),
- typeof(AdoS0CategoryLeadTime),
- typeof(AdoS0MaterialPlanCycle),
- typeof(AdoS0SrmPurchase),
- typeof(AdoS8Exception),
- typeof(AdoS8ExceptionTimeline),
- typeof(AdoS8DecisionRecord),
- typeof(AdoS8Evidence),
- // S8-STANDARD-DATASET-HARD-CUTOVER-1:AdoS8AlertRule 已物理删除
- // (运行时权威一直是 AdoS8WatchRule;该表零 Runtime Consumer)。
- typeof(AdoS8SceneConfig),
- typeof(AdoS8NotificationLayer),
- typeof(AdoS8WatchRule),
- typeof(AdoS8RolePermissionConfig),
- typeof(AdoS8NotificationLog),
- typeof(AdoS8DashboardCellConfig),
- typeof(AdoS8ExceptionType),
- typeof(AdoS8DetectionLog),
- typeof(AdoS8RuleDetectionState),
- typeof(AdoS8Dimension),
- typeof(AdoS8DimensionNode),
- typeof(AdoS8ConfigDraft),
- typeof(AdoS8MonitorObject),
- typeof(AdoS8MonitorMetric),
- typeof(AdoS8OrderFlowOrder),
- typeof(AdoS8OrderFlowStage),
- typeof(AdoS8OrderFlowSnapshot),
- typeof(AdoS8OrderFlowSubstep),
- typeof(AdoS8OrderFlowSubstepUnit),
- typeof(AdoS8OrderFlowProcurementPivot),
- typeof(ContractReview),
- typeof(ContractReviewFlow),
- typeof(ProductDesign),
- typeof(ProductDesignBom),
- typeof(ProductDesignRouting)
- );
- AidopDimensionSeed.EnsureSeed(db);
- AidopMonitorDictionarySeed.EnsureSeed(db);
- }
- catch (Exception ex)
- {
- Trace.TraceWarning("Ai-DOP Development CodeFirst (S0/S8/Order demo tables): " + ex);
- }
- }
- }
|