Bladeren bron

迁移方案,忽略.pid和.log

YY968XX 1 week geleden
bovenliggende
commit
9403d12d8c
3 gewijzigde bestanden met toevoegingen van 1780 en 0 verwijderingen
  1. 3 0
      .gitignore
  2. 920 0
      doc/plan/S0/Batch2-产销建模迁移方案.md
  3. 857 0
      doc/plan/S0/S0迁移.md

+ 3 - 0
.gitignore

@@ -41,6 +41,9 @@ node_modules/
 .vs
 .idea
 .DS_Store
+
+# Local dev runtime (PID files, logs from restart scripts)
+/.run/
 /server/Admin.NET.Web.Entry/Admin.NET.db-journal
 /server/Admin.NET.sln.DotSettings.user
 server/Admin.NET.Application/Configuration/DeepSeek.json

+ 920 - 0
doc/plan/S0/Batch2-产销建模迁移方案.md

@@ -0,0 +1,920 @@
+# Batch 2:产销建模(Sales)迁移方案
+
+> 本文档是 [S0迁移.md](S0迁移.md) Batch 2 的**细化版**,可直接交由 Cursor / AI 编程助手逐步执行。
+
+---
+
+## 〇、与原计划的修正
+
+对比 `S0迁移.md` Batch 2 部分,本方案做了以下修正:
+
+| # | 原计划描述 | 实际情况 | 修正 |
+|---|-----------|---------|------|
+| 1 | Batch 2 含 Bom / BomItem(6 个实体) | `S0.Domain.Sales/` 只有 Customer、Material、OrderPriorityRule 3 个实体,BOM 不在 Sales 域 | 移除 Bom/BomItem,实体数改为 **3** |
+| 2 | Customer 仅列 8 个字段 | 实际含 NameEn、CustomerType、ForbidStatus、Currency、IsTaxIncluded、PrimarySales、BackupSales、CustomerLevel 等 **20 字段** | 补全 |
+| 3 | Material 仅列 10 个字段 | 实际含 30+ 字段(库存、采购、质量、批次管理等参数) | 补全 |
+| 4 | BOM 主从页面 | 不属于 Sales 域 | 移除 |
+| 5 | S0 菜单根目录 ID = `1321001000000L` | 代码实际生成 `1321000001000L`(`1321000000000 + 1*1000`) | 修正所有 S0DirId 引用 |
+| 6 | Batch 1 需新建 4 实体 + 4 页面 | S0 Platform 与 Admin.NET 的 SysOrg/SysDictType/SysDictData 同构 | 改为复用平台 + 灌入种子数据,无需新建页面 |
+
+---
+
+## 一、源文件清单(只读)
+
+### 1.1 后端
+
+```
+/home/yy968/work/s0/s0-operating-modeling/backend/src/
+├─ S0.Domain/
+│   ├─ Common/BaseEntity.cs                              ← 公共基类(Id, CompanyId, FactoryId, IsEnabled, IsDeleted, VersionNo, CreatedBy, CreatedAt, UpdatedBy, UpdatedAt)
+│   └─ Sales/
+│       ├─ Customer.cs                                    ← [Table("sales_customer")]
+│       ├─ Material.cs                                    ← [Table("sales_material")]
+│       └─ OrderPriorityRule.cs                           ← [Table("sales_order_priority_rule")]
+├─ S0.Application/Sales/
+│   ├─ Common/SalesPagedQuery.cs                          ← 分页基类(Keyword, IsEnabled, PageNo, PageSize)
+│   ├─ Customers/
+│   │   ├─ CustomerDtos.cs                                ← CustomerUpsertRequest + CustomerDto
+│   │   ├─ ICustomerService.cs
+│   │   └─ CustomerService.cs
+│   ├─ Materials/
+│   │   ├─ MaterialDtos.cs                                ← MaterialQuery + MaterialUpsertRequest + MaterialDto
+│   │   ├─ IMaterialService.cs
+│   │   └─ MaterialService.cs
+│   └─ OrderPriorityRules/
+│       ├─ OrderPriorityRuleDtos.cs                       ← OrderPriorityRuleQuery + UpsertRequest + Dto
+│       ├─ IOrderPriorityRuleService.cs
+│       └─ OrderPriorityRuleService.cs
+└─ S0.Api/Controllers/Sales/
+    ├─ CustomersController.cs                             ← api/sales/customers
+    ├─ MaterialsController.cs                             ← api/sales/materials
+    └─ OrderPriorityRulesController.cs                    ← api/sales/order-priority-rules
+```
+
+### 1.2 前端
+
+```
+/home/yy968/work/s0/s0-operating-modeling/src/
+├─ api/modules/sales.js                                   ← customersApi / materialsApi / orderPriorityRulesApi
+└─ views/
+    ├─ CustomerManagement.vue                             ← 路由 /sales-modeling/customers
+    ├─ MaterialManagement.vue                             ← 路由 /sales-modeling/materials
+    └─ OrderPriorityConfig.vue                            ← 路由 /sales-modeling/order-priority
+```
+
+---
+
+## 二、目标文件清单
+
+### 2.1 后端新建
+
+```
+server/Plugins/Admin.NET.Plugin.AiDOP/
+├─ Entity/S0/Sales/
+│   ├─ AdoS0Customer.cs               ← ado_s0_sales_customer
+│   ├─ AdoS0Material.cs               ← ado_s0_sales_material
+│   └─ AdoS0OrderPriorityRule.cs      ← ado_s0_sales_order_priority_rule
+├─ Dto/S0/Sales/
+│   └─ AdoS0SalesDtos.cs              ← 3 组 QueryDto + UpsertDto
+└─ Controllers/S0/Sales/
+    ├─ AdoS0CustomersController.cs
+    ├─ AdoS0MaterialsController.cs
+    └─ AdoS0OrderPriorityRulesController.cs
+```
+
+### 2.2 后端修改(追加,不替换)
+
+```
+server/Plugins/Admin.NET.Plugin.AiDOP/Startup.cs                   ← InitTables 追加 3 实体 + using
+server/Plugins/Admin.NET.Plugin.AiDOP/SeedData/SysMenuSeedData.cs  ← 追加 BuildS0SalesMenus()(注意是插件的 SeedData)
+```
+
+### 2.3 前端新建
+
+```
+Web/src/views/aidop/s0/
+├─ api/
+│   └─ s0SalesApi.ts                  ← 3 组 API 函数 + TS 接口
+└─ sales/
+    ├─ CustomerList.vue               ← name="aidopS0SalesCustomer"
+    ├─ MaterialList.vue               ← name="aidopS0SalesMaterial"
+    └─ OrderPriorityRuleList.vue      ← name="aidopS0SalesOrderPriorityRule"
+```
+
+---
+
+## 三、实体字段完整映射
+
+> 规则回顾:BaseEntity 字段内联(去掉 IsDeleted / VersionNo / CreatedBy / UpdatedBy);不继承基类;不迁移导航属性。
+
+### 3.1 AdoS0Customer → `ado_s0_sales_customer`
+
+```csharp
+namespace Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
+
+/// <summary>客户主数据(S0 Sales / sales_customer)</summary>
+[SugarTable("ado_s0_sales_customer", "S0 客户主数据")]
+public class AdoS0Customer
+{
+    [SugarColumn(ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    public long Id { get; set; }
+
+    [SugarColumn(ColumnDescription = "关联公司 ID", ColumnDataType = "bigint")]
+    public long CompanyRefId { get; set; }
+
+    [SugarColumn(ColumnDescription = "关联工厂 ID", ColumnDataType = "bigint")]
+    public long FactoryRefId { get; set; }
+
+    [SugarColumn(ColumnDescription = "客户编码", Length = 100)]
+    public string Code { get; set; } = string.Empty;
+
+    [SugarColumn(ColumnDescription = "客户名称", Length = 200)]
+    public string Name { get; set; } = string.Empty;
+
+    [SugarColumn(ColumnDescription = "英文名称", Length = 200, IsNullable = true)]
+    public string? NameEn { get; set; }
+
+    [SugarColumn(ColumnDescription = "客户类型", Length = 100, IsNullable = true)]
+    public string? CustomerType { get; set; }
+
+    [SugarColumn(ColumnDescription = "联系人", Length = 100, IsNullable = true)]
+    public string? ContactPerson { get; set; }
+
+    [SugarColumn(ColumnDescription = "联系电话", Length = 50, IsNullable = true)]
+    public string? ContactPhone { get; set; }
+
+    [SugarColumn(ColumnDescription = "地址", Length = 500, IsNullable = true)]
+    public string? Address { get; set; }
+
+    [SugarColumn(ColumnDescription = "禁用状态", Length = 20)]
+    public string ForbidStatus { get; set; } = "normal";
+
+    [SugarColumn(ColumnDescription = "币种", Length = 50, IsNullable = true)]
+    public string? Currency { get; set; }
+
+    [SugarColumn(ColumnDescription = "是否含税", ColumnDataType = "boolean")]
+    public bool IsTaxIncluded { get; set; } = true;
+
+    [SugarColumn(ColumnDescription = "主销售员", Length = 100, IsNullable = true)]
+    public string? PrimarySales { get; set; }
+
+    [SugarColumn(ColumnDescription = "备用销售员", Length = 100, IsNullable = true)]
+    public string? BackupSales { get; set; }
+
+    [SugarColumn(ColumnDescription = "客户等级")]
+    public int CustomerLevel { get; set; } = 1;
+
+    [SugarColumn(ColumnDescription = "备注", Length = 500, IsNullable = true)]
+    public string? Remark { get; set; }
+
+    [SugarColumn(ColumnDescription = "启用", ColumnDataType = "boolean")]
+    public bool IsEnabled { get; set; } = true;
+
+    [SugarColumn(ColumnDescription = "创建时间")]
+    public DateTime CreatedAt { get; set; } = DateTime.Now;
+
+    [SugarColumn(ColumnDescription = "更新时间", IsNullable = true)]
+    public DateTime? UpdatedAt { get; set; }
+}
+```
+
+**字段来源对照表**:
+
+| # | 目标字段 | C# 类型 | 长度/精度 | 默认值 | S0 原列名 | S0 来源 |
+|---|---------|---------|----------|--------|-----------|---------|
+| 1 | Id | long | — | PK 自增 | id | BaseEntity |
+| 2 | CompanyRefId | long | — | — | company_id | BaseEntity |
+| 3 | FactoryRefId | long | — | — | factory_id | BaseEntity |
+| 4 | Code | string | 100 | — | code | Customer |
+| 5 | Name | string | 200 | — | name | Customer |
+| 6 | NameEn | string? | 200 | — | name_en | Customer |
+| 7 | CustomerType | string? | 100 | — | customer_type | Customer |
+| 8 | ContactPerson | string? | 100 | — | contact_person | Customer |
+| 9 | ContactPhone | string? | 50 | — | contact_phone | Customer |
+| 10 | Address | string? | 500 | — | address | Customer |
+| 11 | ForbidStatus | string | 20 | "normal" | forbid_status | Customer |
+| 12 | Currency | string? | 50 | — | currency | Customer |
+| 13 | IsTaxIncluded | bool | — | true | is_tax_included | Customer |
+| 14 | PrimarySales | string? | 100 | — | primary_sales | Customer |
+| 15 | BackupSales | string? | 100 | — | backup_sales | Customer |
+| 16 | CustomerLevel | int | — | 1 | customer_level | Customer |
+| 17 | Remark | string? | 500 | — | remark | Customer |
+| 18 | IsEnabled | bool | — | true | is_enabled | BaseEntity |
+| 19 | CreatedAt | DateTime | — | Now | created_at | BaseEntity |
+| 20 | UpdatedAt | DateTime? | — | — | updated_at | BaseEntity |
+
+> **不迁移字段**:IsDeleted、VersionNo、CreatedBy、UpdatedBy(遵循总体原则)
+
+---
+
+### 3.2 AdoS0Material → `ado_s0_sales_material`
+
+```csharp
+namespace Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
+
+/// <summary>物料主数据(S0 Sales / sales_material)</summary>
+[SugarTable("ado_s0_sales_material", "S0 物料主数据")]
+public class AdoS0Material
+{
+    [SugarColumn(ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    public long Id { get; set; }
+
+    [SugarColumn(ColumnDescription = "关联公司 ID", ColumnDataType = "bigint")]
+    public long CompanyRefId { get; set; }
+
+    [SugarColumn(ColumnDescription = "关联工厂 ID", ColumnDataType = "bigint")]
+    public long FactoryRefId { get; set; }
+
+    // ── 基本信息 ──
+
+    [SugarColumn(ColumnDescription = "物料编码", Length = 100)]
+    public string Code { get; set; } = string.Empty;
+
+    [SugarColumn(ColumnDescription = "物料名称", Length = 200)]
+    public string Name { get; set; } = string.Empty;
+
+    [SugarColumn(ColumnDescription = "英文名称", Length = 200, IsNullable = true)]
+    public string? NameEn { get; set; }
+
+    [SugarColumn(ColumnDescription = "物料类型", Length = 100, IsNullable = true)]
+    public string? MaterialType { get; set; }
+
+    [SugarColumn(ColumnDescription = "单位", Length = 50, IsNullable = true)]
+    public string? Unit { get; set; }
+
+    [SugarColumn(ColumnDescription = "规格型号", Length = 200, IsNullable = true)]
+    public string? Spec { get; set; }
+
+    [SugarColumn(ColumnDescription = "计划类别", Length = 100, IsNullable = true)]
+    public string? PlCategory { get; set; }
+
+    [SugarColumn(ColumnDescription = "图纸编号", Length = 100, IsNullable = true)]
+    public string? DrawingNo { get; set; }
+
+    [SugarColumn(ColumnDescription = "语言版本", Length = 50, IsNullable = true)]
+    public string? Language { get; set; }
+
+    [SugarColumn(ColumnDescription = "业务版本", Length = 50, IsNullable = true)]
+    public string? BizVersion { get; set; }
+
+    [SugarColumn(ColumnDescription = "产品编码", Length = 100, IsNullable = true)]
+    public string? ProductCode { get; set; }
+
+    [SugarColumn(ColumnDescription = "物料属性", Length = 50, IsNullable = true)]
+    public string? MaterialAttribute { get; set; }
+
+    // ── 库存参数 ──
+
+    [SugarColumn(ColumnDescription = "默认库位 ID", ColumnDataType = "bigint", IsNullable = true)]
+    public long? DefaultLocationId { get; set; }
+
+    [SugarColumn(ColumnDescription = "默认货架 ID", ColumnDataType = "bigint", IsNullable = true)]
+    public long? DefaultRackId { get; set; }
+
+    [SugarColumn(ColumnDescription = "库存类型编码", Length = 50, IsNullable = true)]
+    public string? StockTypeCode { get; set; }
+
+    [SugarColumn(ColumnDescription = "安全库存", ColumnDataType = "decimal(18,5)", IsNullable = true)]
+    public decimal? SafetyStock { get; set; }
+
+    [SugarColumn(ColumnDescription = "保质期(天)", IsNullable = true)]
+    public int? ShelfLifeDays { get; set; }
+
+    [SugarColumn(ColumnDescription = "到期预警天数", IsNullable = true)]
+    public int? ExpireWarningDays { get; set; }
+
+    // ── 采购参数 ──
+
+    [SugarColumn(ColumnDescription = "采购提前期(天)", IsNullable = true)]
+    public int? PurchaseLeadDays { get; set; }
+
+    [SugarColumn(ColumnDescription = "最小订货量", ColumnDataType = "decimal(18,5)", IsNullable = true)]
+    public decimal? MinOrderQty { get; set; }
+
+    [SugarColumn(ColumnDescription = "最大订货量", ColumnDataType = "decimal(18,5)", IsNullable = true)]
+    public decimal? MaxOrderQty { get; set; }
+
+    [SugarColumn(ColumnDescription = "订货倍数", ColumnDataType = "decimal(18,5)", IsNullable = true)]
+    public decimal? OrderMultiple { get; set; }
+
+    [SugarColumn(ColumnDescription = "备料提前期(天)", IsNullable = true)]
+    public int? PreparationLeadDays { get; set; }
+
+    [SugarColumn(ColumnDescription = "按需采购", ColumnDataType = "boolean")]
+    public bool IsOnDemand { get; set; }
+
+    [SugarColumn(ColumnDescription = "特殊需求类型", Length = 50, IsNullable = true)]
+    public string? SpecialReqType { get; set; }
+
+    // ── 质量 / 管控 ──
+
+    [SugarColumn(ColumnDescription = "需检验", ColumnDataType = "boolean")]
+    public bool IsInspectionRequired { get; set; }
+
+    [SugarColumn(ColumnDescription = "检验天数", IsNullable = true)]
+    public int? InspectionDays { get; set; }
+
+    [SugarColumn(ColumnDescription = "关键物料", ColumnDataType = "boolean")]
+    public bool IsKeyMaterial { get; set; }
+
+    [SugarColumn(ColumnDescription = "主要物料", ColumnDataType = "boolean")]
+    public bool IsMainMaterial { get; set; }
+
+    [SugarColumn(ColumnDescription = "需预处理", ColumnDataType = "boolean")]
+    public bool IsPreprocess { get; set; }
+
+    [SugarColumn(ColumnDescription = "自动批次", ColumnDataType = "boolean")]
+    public bool IsAutoBatch { get; set; }
+
+    [SugarColumn(ColumnDescription = "需打标签", ColumnDataType = "boolean")]
+    public bool IsLabelRequired { get; set; }
+
+    // ── 批次管理 ──
+
+    [SugarColumn(ColumnDescription = "批次先进先出提醒", ColumnDataType = "boolean")]
+    public bool IsBatchFifoReminder { get; set; }
+
+    [SugarColumn(ColumnDescription = "批次先进先出严格", ColumnDataType = "boolean")]
+    public bool IsBatchFifoStrict { get; set; }
+
+    [SugarColumn(ColumnDescription = "库存周转率", ColumnDataType = "decimal(18,5)", IsNullable = true)]
+    public decimal? InventoryTurnoverRate { get; set; }
+
+    // ── 状态 ──
+
+    [SugarColumn(ColumnDescription = "禁用状态", Length = 20)]
+    public string ForbidStatus { get; set; } = "normal";
+
+    [SugarColumn(ColumnDescription = "备注", Length = 500, IsNullable = true)]
+    public string? Remark { get; set; }
+
+    [SugarColumn(ColumnDescription = "启用", ColumnDataType = "boolean")]
+    public bool IsEnabled { get; set; } = true;
+
+    [SugarColumn(ColumnDescription = "创建时间")]
+    public DateTime CreatedAt { get; set; } = DateTime.Now;
+
+    [SugarColumn(ColumnDescription = "更新时间", IsNullable = true)]
+    public DateTime? UpdatedAt { get; set; }
+}
+```
+
+**字段来源对照表**(共 39 字段):
+
+| # | 目标字段 | C# 类型 | S0 原列名 | S0 来源 |
+|---|---------|---------|-----------|---------|
+| 1 | Id | long | id | BaseEntity |
+| 2 | CompanyRefId | long | company_id | BaseEntity |
+| 3 | FactoryRefId | long | factory_id | BaseEntity |
+| 4 | Code | string(100) | code | Material |
+| 5 | Name | string(200) | name | Material |
+| 6 | NameEn | string?(200) | name_en | Material |
+| 7 | MaterialType | string?(100) | material_type | Material |
+| 8 | Unit | string?(50) | unit | Material |
+| 9 | Spec | string?(200) | spec | Material |
+| 10 | PlCategory | string?(100) | pl_category | Material |
+| 11 | DrawingNo | string?(100) | drawing_no | Material |
+| 12 | Language | string?(50) | language | Material |
+| 13 | BizVersion | string?(50) | biz_version | Material |
+| 14 | ProductCode | string?(100) | product_code | Material |
+| 15 | MaterialAttribute | string?(50) | material_attribute | Material |
+| 16 | DefaultLocationId | long? | default_location_id | Material |
+| 17 | DefaultRackId | long? | default_rack_id | Material |
+| 18 | StockTypeCode | string?(50) | stock_type_code | Material |
+| 19 | SafetyStock | decimal?(18,5) | safety_stock | Material |
+| 20 | ShelfLifeDays | int? | shelf_life_days | Material |
+| 21 | ExpireWarningDays | int? | expire_warning_days | Material |
+| 22 | PurchaseLeadDays | int? | purchase_lead_days | Material |
+| 23 | MinOrderQty | decimal?(18,5) | min_order_qty | Material |
+| 24 | MaxOrderQty | decimal?(18,5) | max_order_qty | Material |
+| 25 | OrderMultiple | decimal?(18,5) | order_multiple | Material |
+| 26 | PreparationLeadDays | int? | preparation_lead_days | Material |
+| 27 | IsOnDemand | bool | is_on_demand | Material |
+| 28 | SpecialReqType | string?(50) | special_req_type | Material |
+| 29 | IsInspectionRequired | bool | is_inspection_required | Material |
+| 30 | InspectionDays | int? | inspection_days | Material |
+| 31 | IsKeyMaterial | bool | is_key_material | Material |
+| 32 | IsMainMaterial | bool | is_main_material | Material |
+| 33 | IsPreprocess | bool | is_preprocess | Material |
+| 34 | IsAutoBatch | bool | is_auto_batch | Material |
+| 35 | IsLabelRequired | bool | is_label_required | Material |
+| 36 | IsBatchFifoReminder | bool | is_batch_fifo_reminder | Material |
+| 37 | IsBatchFifoStrict | bool | is_batch_fifo_strict | Material |
+| 38 | InventoryTurnoverRate | decimal?(18,5) | inventory_turnover_rate | Material |
+| 39 | ForbidStatus | string(20) | forbid_status | Material |
+| 40 | Remark | string?(500) | remark | Material |
+| 41 | IsEnabled | bool | is_enabled | BaseEntity |
+| 42 | CreatedAt | DateTime | created_at | BaseEntity |
+| 43 | UpdatedAt | DateTime? | updated_at | BaseEntity |
+
+> **跨 Batch 依赖**:DefaultLocationId / DefaultRackId 关联仓储 Location / Rack(Batch 4)。Batch 2 阶段前端用 `el-input-number` 输入 ID;Batch 4 完成后升级为仓储下拉。
+
+---
+
+### 3.3 AdoS0OrderPriorityRule → `ado_s0_sales_order_priority_rule`
+
+```csharp
+namespace Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
+
+/// <summary>订单优先规则(S0 Sales / sales_order_priority_rule)</summary>
+[SugarTable("ado_s0_sales_order_priority_rule", "S0 订单优先规则")]
+public class AdoS0OrderPriorityRule
+{
+    [SugarColumn(ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    public long Id { get; set; }
+
+    [SugarColumn(ColumnDescription = "关联公司 ID", ColumnDataType = "bigint")]
+    public long CompanyRefId { get; set; }
+
+    [SugarColumn(ColumnDescription = "关联工厂 ID", ColumnDataType = "bigint")]
+    public long FactoryRefId { get; set; }
+
+    [SugarColumn(ColumnDescription = "规则编码", Length = 100)]
+    public string Code { get; set; } = string.Empty;
+
+    [SugarColumn(ColumnDescription = "规则名称", Length = 200)]
+    public string Name { get; set; } = string.Empty;
+
+    [SugarColumn(ColumnDescription = "优先级别")]
+    public int PriorityLevel { get; set; }
+
+    [SugarColumn(ColumnDescription = "排序方向", Length = 10)]
+    public string SortDirection { get; set; } = "ASC";
+
+    [SugarColumn(ColumnDescription = "来源实体", Length = 200, IsNullable = true)]
+    public string? SourceEntity { get; set; }
+
+    [SugarColumn(ColumnDescription = "来源字段", Length = 200, IsNullable = true)]
+    public string? SourceField { get; set; }
+
+    [SugarColumn(ColumnDescription = "来源字段类型", Length = 100, IsNullable = true)]
+    public string? SourceFieldType { get; set; }
+
+    [SugarColumn(ColumnDescription = "来源关联字段", Length = 200, IsNullable = true)]
+    public string? SourceLinkField { get; set; }
+
+    [SugarColumn(ColumnDescription = "工单字段", Length = 200, IsNullable = true)]
+    public string? WorkOrderField { get; set; }
+
+    [SugarColumn(ColumnDescription = "工单字段类型", Length = 100, IsNullable = true)]
+    public string? WorkOrderFieldType { get; set; }
+
+    [SugarColumn(ColumnDescription = "工单关联字段", Length = 200, IsNullable = true)]
+    public string? WorkOrderLinkField { get; set; }
+
+    [SugarColumn(ColumnDescription = "规则表达式", Length = 1000, IsNullable = true)]
+    public string? RuleExpr { get; set; }
+
+    [SugarColumn(ColumnDescription = "备注", Length = 500, IsNullable = true)]
+    public string? Remark { get; set; }
+
+    [SugarColumn(ColumnDescription = "启用", ColumnDataType = "boolean")]
+    public bool IsEnabled { get; set; } = true;
+
+    [SugarColumn(ColumnDescription = "创建时间")]
+    public DateTime CreatedAt { get; set; } = DateTime.Now;
+
+    [SugarColumn(ColumnDescription = "更新时间", IsNullable = true)]
+    public DateTime? UpdatedAt { get; set; }
+}
+```
+
+**字段来源对照表**(共 19 字段):
+
+| # | 目标字段 | C# 类型 | S0 原列名 |
+|---|---------|---------|-----------|
+| 1 | Id | long | id |
+| 2 | CompanyRefId | long | company_id |
+| 3 | FactoryRefId | long | factory_id |
+| 4 | Code | string(100) | code |
+| 5 | Name | string(200) | name |
+| 6 | PriorityLevel | int | priority_level |
+| 7 | SortDirection | string(10) | sort_direction |
+| 8 | SourceEntity | string?(200) | source_entity |
+| 9 | SourceField | string?(200) | source_field |
+| 10 | SourceFieldType | string?(100) | source_field_type |
+| 11 | SourceLinkField | string?(200) | source_link_field |
+| 12 | WorkOrderField | string?(200) | work_order_field |
+| 13 | WorkOrderFieldType | string?(100) | work_order_field_type |
+| 14 | WorkOrderLinkField | string?(200) | work_order_link_field |
+| 15 | RuleExpr | string?(1000) | rule_expr |
+| 16 | Remark | string?(500) | remark |
+| 17 | IsEnabled | bool | is_enabled |
+| 18 | CreatedAt | DateTime | created_at |
+| 19 | UpdatedAt | DateTime? | updated_at |
+
+---
+
+## 四、DTO 设计
+
+文件:`server/Plugins/Admin.NET.Plugin.AiDOP/Dto/S0/Sales/AdoS0SalesDtos.cs`
+
+```csharp
+namespace Admin.NET.Plugin.AiDOP.Dto.S0.Sales;
+
+// ══════════════════════════════════════
+// Customer
+// ══════════════════════════════════════
+public class AdoS0CustomerQueryDto
+{
+    public string? Keyword { get; set; }       // 模糊搜索 Code / Name
+    public bool? IsEnabled { get; set; }
+    public int Page { get; set; } = 1;
+    public int PageSize { get; set; } = 20;
+}
+
+public class AdoS0CustomerUpsertDto
+{
+    public long CompanyRefId { get; set; }
+    public long FactoryRefId { get; set; }
+    public string Code { get; set; } = string.Empty;
+    public string Name { get; set; } = string.Empty;
+    public string? NameEn { get; set; }
+    public string? CustomerType { get; set; }
+    public string? ContactPerson { get; set; }
+    public string? ContactPhone { get; set; }
+    public string? Address { get; set; }
+    public string ForbidStatus { get; set; } = "normal";
+    public string? Currency { get; set; }
+    public bool IsTaxIncluded { get; set; } = true;
+    public string? PrimarySales { get; set; }
+    public string? BackupSales { get; set; }
+    public int CustomerLevel { get; set; } = 1;
+    public string? Remark { get; set; }
+    public bool IsEnabled { get; set; } = true;
+}
+
+// ══════════════════════════════════════
+// Material
+// ══════════════════════════════════════
+public class AdoS0MaterialQueryDto
+{
+    public string? Keyword { get; set; }       // 模糊搜索 Code / Name
+    public string? Code { get; set; }
+    public string? Name { get; set; }
+    public string? Spec { get; set; }
+    public string? DrawingNo { get; set; }
+    public string? PlCategory { get; set; }
+    public string? MaterialType { get; set; }
+    public string? Language { get; set; }
+    public bool? IsEnabled { get; set; }
+    public int Page { get; set; } = 1;
+    public int PageSize { get; set; } = 20;
+}
+
+public class AdoS0MaterialUpsertDto
+{
+    public long CompanyRefId { get; set; }
+    public long FactoryRefId { get; set; }
+    // 基本信息
+    public string Code { get; set; } = string.Empty;
+    public string Name { get; set; } = string.Empty;
+    public string? NameEn { get; set; }
+    public string? MaterialType { get; set; }
+    public string? Unit { get; set; }
+    public string? Spec { get; set; }
+    public string? PlCategory { get; set; }
+    public string? DrawingNo { get; set; }
+    public string? Language { get; set; }
+    public string? BizVersion { get; set; }
+    public string? ProductCode { get; set; }
+    public string? MaterialAttribute { get; set; }
+    // 库存参数
+    public long? DefaultLocationId { get; set; }
+    public long? DefaultRackId { get; set; }
+    public string? StockTypeCode { get; set; }
+    public decimal? SafetyStock { get; set; }
+    public int? ShelfLifeDays { get; set; }
+    public int? ExpireWarningDays { get; set; }
+    // 采购参数
+    public int? PurchaseLeadDays { get; set; }
+    public decimal? MinOrderQty { get; set; }
+    public decimal? MaxOrderQty { get; set; }
+    public decimal? OrderMultiple { get; set; }
+    public int? PreparationLeadDays { get; set; }
+    public bool IsOnDemand { get; set; }
+    public string? SpecialReqType { get; set; }
+    // 质量 / 管控
+    public bool IsInspectionRequired { get; set; }
+    public int? InspectionDays { get; set; }
+    public bool IsKeyMaterial { get; set; }
+    public bool IsMainMaterial { get; set; }
+    public bool IsPreprocess { get; set; }
+    public bool IsAutoBatch { get; set; }
+    public bool IsLabelRequired { get; set; }
+    // 批次管理
+    public bool IsBatchFifoReminder { get; set; }
+    public bool IsBatchFifoStrict { get; set; }
+    public decimal? InventoryTurnoverRate { get; set; }
+    // 状态
+    public string ForbidStatus { get; set; } = "normal";
+    public bool IsEnabled { get; set; } = true;
+    public string? Remark { get; set; }
+}
+
+// ══════════════════════════════════════
+// OrderPriorityRule
+// ══════════════════════════════════════
+public class AdoS0OrderPriorityRuleQueryDto
+{
+    public string? Keyword { get; set; }       // 模糊搜索 Code / Name
+    public string? SourceEntity { get; set; }
+    public bool? IsEnabled { get; set; }
+    public int Page { get; set; } = 1;
+    public int PageSize { get; set; } = 20;
+}
+
+public class AdoS0OrderPriorityRuleUpsertDto
+{
+    public long CompanyRefId { get; set; }
+    public long FactoryRefId { get; set; }
+    public string Code { get; set; } = string.Empty;
+    public string Name { get; set; } = string.Empty;
+    public int PriorityLevel { get; set; }
+    public string SortDirection { get; set; } = "ASC";
+    public string? SourceEntity { get; set; }
+    public string? SourceField { get; set; }
+    public string? SourceFieldType { get; set; }
+    public string? SourceLinkField { get; set; }
+    public string? WorkOrderField { get; set; }
+    public string? WorkOrderFieldType { get; set; }
+    public string? WorkOrderLinkField { get; set; }
+    public string? RuleExpr { get; set; }
+    public string? Remark { get; set; }
+    public bool IsEnabled { get; set; } = true;
+}
+```
+
+---
+
+## 五、控制器路由
+
+### 5.1 AdoS0CustomersController
+
+路由 `api/s0/sales/customers`,风格对标 `OrderController.cs`:
+
+```
+GET    /api/s0/sales/customers              ← 分页(keyword, isEnabled, page, pageSize)
+GET    /api/s0/sales/customers/{id:long}
+POST   /api/s0/sales/customers
+PUT    /api/s0/sales/customers/{id:long}
+DELETE /api/s0/sales/customers/{id:long}
+```
+
+查询过滤逻辑:
+
+```csharp
+.WhereIF(!string.IsNullOrWhiteSpace(q.Keyword),
+    x => x.Code.Contains(q.Keyword!) || x.Name.Contains(q.Keyword!))
+.WhereIF(q.IsEnabled.HasValue, x => x.IsEnabled == q.IsEnabled!.Value)
+```
+
+### 5.2 AdoS0MaterialsController
+
+路由 `api/s0/sales/materials`:
+
+```
+GET    /api/s0/sales/materials              ← 分页(多条件过滤)
+GET    /api/s0/sales/materials/{id:long}
+POST   /api/s0/sales/materials
+PUT    /api/s0/sales/materials/{id:long}
+DELETE /api/s0/sales/materials/{id:long}
+```
+
+查询过滤逻辑:
+
+```csharp
+.WhereIF(!string.IsNullOrWhiteSpace(q.Keyword),
+    x => x.Code.Contains(q.Keyword!) || x.Name.Contains(q.Keyword!))
+.WhereIF(!string.IsNullOrWhiteSpace(q.Code), x => x.Code.Contains(q.Code!))
+.WhereIF(!string.IsNullOrWhiteSpace(q.Name), x => x.Name.Contains(q.Name!))
+.WhereIF(!string.IsNullOrWhiteSpace(q.Spec), x => x.Spec!.Contains(q.Spec!))
+.WhereIF(!string.IsNullOrWhiteSpace(q.DrawingNo), x => x.DrawingNo!.Contains(q.DrawingNo!))
+.WhereIF(!string.IsNullOrWhiteSpace(q.PlCategory), x => x.PlCategory == q.PlCategory)
+.WhereIF(!string.IsNullOrWhiteSpace(q.MaterialType), x => x.MaterialType == q.MaterialType)
+.WhereIF(!string.IsNullOrWhiteSpace(q.Language), x => x.Language == q.Language)
+.WhereIF(q.IsEnabled.HasValue, x => x.IsEnabled == q.IsEnabled!.Value)
+```
+
+### 5.3 AdoS0OrderPriorityRulesController
+
+路由 `api/s0/sales/order-priority-rules`:
+
+```
+GET    /api/s0/sales/order-priority-rules   ← 分页(keyword, sourceEntity, isEnabled)
+GET    /api/s0/sales/order-priority-rules/{id:long}
+POST   /api/s0/sales/order-priority-rules
+PUT    /api/s0/sales/order-priority-rules/{id:long}
+DELETE /api/s0/sales/order-priority-rules/{id:long}
+```
+
+查询过滤逻辑:
+
+```csharp
+.WhereIF(!string.IsNullOrWhiteSpace(q.Keyword),
+    x => x.Code.Contains(q.Keyword!) || x.Name.Contains(q.Keyword!))
+.WhereIF(!string.IsNullOrWhiteSpace(q.SourceEntity), x => x.SourceEntity == q.SourceEntity)
+.WhereIF(q.IsEnabled.HasValue, x => x.IsEnabled == q.IsEnabled!.Value)
+```
+
+---
+
+## 六、Startup.cs 修改
+
+在 `server/Plugins/Admin.NET.Plugin.AiDOP/Startup.cs` 中:
+
+**顶部追加 using**:
+```csharp
+using Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
+```
+
+**InitTables 追加**:
+```csharp
+typeof(AdoS0Customer), typeof(AdoS0Material), typeof(AdoS0OrderPriorityRule)
+```
+
+---
+
+## 七、前端 API(s0SalesApi.ts)
+
+文件:`Web/src/views/aidop/s0/api/s0SalesApi.ts`
+
+**要点**:
+
+- `import service from '/@/utils/request'`
+- 3 组 API 对象:`s0CustomersApi`、`s0MaterialsApi`、`s0OrderPriorityRulesApi`
+- 每组提供 `list`、`get`、`create`、`update`、`delete` 方法
+- TS 接口:`S0CustomerRow` / `S0CustomerUpsert`、`S0MaterialRow` / `S0MaterialUpsert`、`S0OrderPriorityRuleRow` / `S0OrderPriorityRuleUpsert`
+- 公共分页接口 `Paged<T> { total, page, pageSize, list }`
+
+---
+
+## 八、前端页面设计
+
+### 8.1 CustomerList.vue(name="aidopS0SalesCustomer")
+
+- **搜索栏**:编码/名称关键字(el-input)
+- **表格列**:code、name、nameEn、customerType、contactPerson、contactPhone、forbidStatus(tag)、isEnabled(tag)
+- **操作**:编辑、删除
+- **弹窗表单**:
+  - companyRefId(下拉 → `SysOrgApi` 按 Type=201)、factoryRefId(下拉 → `SysOrgApi` 按 Type=501 + Pid 联动)
+  - code(必填)、name(必填)、nameEn
+  - customerType(下拉 → `SysDictDataApi` code=`s0_customer_type`)
+  - contactPerson、contactPhone、address
+  - forbidStatus(下拉 → `SysDictDataApi` code=`s0_forbid_status`)
+  - currency(下拉 → `SysDictDataApi` code=`s0_currency`)
+  - isTaxIncluded(switch)
+  - primarySales、backupSales
+  - customerLevel(el-input-number, min=1)
+  - isEnabled(switch)、remark(textarea)
+- **组件引用**:`import AidopDemoShell from '../../components/AidopDemoShell.vue'`
+
+### 8.2 MaterialList.vue(name="aidopS0SalesMaterial")
+
+- **搜索栏**:编码、名称、规格、图纸编号、计划类别(select)、物料类型(select)、语言(select)
+- **表格列**(精简展示):code、name、spec、unit、materialType、plCategory、isEnabled(tag)
+- **弹窗表单**(使用 `el-tabs` 分组):
+  - **Tab 0 归属**:companyRefId(下拉 → `SysOrgApi` Type=201)、factoryRefId(下拉 → `SysOrgApi` Type=501 联动)
+  - **Tab 1 基本信息**:code(必填)、name(必填)、nameEn、materialType(下拉 → `s0_material_type`)、unit、spec、plCategory(下拉 → `s0_pl_category`)、drawingNo、language、bizVersion、productCode、materialAttribute(下拉 → `s0_material_attribute`)
+  - **Tab 2 库存参数**:stockTypeCode(下拉 → `s0_stock_type`)、safetyStock、shelfLifeDays、expireWarningDays、defaultLocationId(输入框,Batch 4 后升级)、defaultRackId(输入框,Batch 4 后升级)
+  - **Tab 3 采购参数**:purchaseLeadDays、minOrderQty、maxOrderQty、orderMultiple、preparationLeadDays、isOnDemand(switch)、specialReqType(下拉 → `s0_special_req_type`)
+  - **Tab 4 质量/管控**:isInspectionRequired(switch)、inspectionDays、isKeyMaterial(switch)、isMainMaterial(switch)、isPreprocess(switch)、isAutoBatch(switch)、isLabelRequired(switch)
+  - **Tab 5 批次管理**:isBatchFifoReminder(switch)、isBatchFifoStrict(switch)、inventoryTurnoverRate
+  - **Tab 6 状态**:forbidStatus(下拉 → `s0_forbid_status`)、isEnabled(switch)、remark(textarea)
+
+> 下拉选项统一从平台字典 API 获取(`SysDictDataApi.apiSysDictDataDataListCodeGet`),
+> 不再硬编码。S0 原 `MaterialManagement.vue` 中的 fallback 选项仅作为种子数据参考。
+
+### 8.3 OrderPriorityRuleList.vue(name="aidopS0SalesOrderPriorityRule")
+
+- **搜索栏**:编码/名称关键字、sourceEntity(select/input)
+- **表格列**:code、name、priorityLevel、sortDirection、sourceEntity、sourceField、isEnabled(tag)
+- **弹窗表单**:
+  - code(必填)、name(必填)
+  - priorityLevel(el-input-number)
+  - sortDirection(select: ASC/DESC)
+  - sourceEntity、sourceField、sourceFieldType、sourceLinkField
+  - workOrderField、workOrderFieldType、workOrderLinkField
+  - ruleExpr(el-input type="textarea" :rows="4")
+  - isEnabled(switch)、remark(textarea)
+
+---
+
+## 九、菜单 SeedData 追加
+
+在 `server/Plugins/Admin.NET.Plugin.AiDOP/SeedData/SysMenuSeedData.cs` 的 `HasData()` 末尾(`BuildAidopSmartOpsSeedMenus` 之后)追加:
+
+```csharp
+foreach (var m in BuildS0SalesMenus(ct)) list.Add(m);
+```
+
+私有方法:
+
+```csharp
+private static IEnumerable<SysMenu> BuildS0SalesMenus(DateTime ct)
+{
+    const long S0DirId  = 1321000001000L;   // 已存在的 S0 根目录(ModuleDefinitions 第 1 项:1321000000000 + 1*1000)
+    const long SubDirId = 1329002000000L;   // 产销建模 子目录
+
+    yield return new SysMenu
+    {
+        Id = SubDirId, Pid = S0DirId,
+        Title = "产销建模", Path = "/aidop/s0/sales",
+        Name = "aidopS0Sales", Component = "Layout",
+        Icon = "ele-ShoppingCart", Type = MenuTypeEnum.Dir,
+        CreateTime = ct, OrderNo = 20
+    };
+
+    yield return new SysMenu
+    {
+        Id = SubDirId + 1, Pid = SubDirId,
+        Title = "客户管理", Path = "/aidop/s0/sales/customer",
+        Name = "aidopS0SalesCustomer",
+        Component = "/aidop/s0/sales/CustomerList",
+        Icon = "ele-User", Type = MenuTypeEnum.Menu,
+        CreateTime = ct, OrderNo = 1
+    };
+
+    yield return new SysMenu
+    {
+        Id = SubDirId + 2, Pid = SubDirId,
+        Title = "物料管理", Path = "/aidop/s0/sales/material",
+        Name = "aidopS0SalesMaterial",
+        Component = "/aidop/s0/sales/MaterialList",
+        Icon = "ele-Box", Type = MenuTypeEnum.Menu,
+        CreateTime = ct, OrderNo = 2
+    };
+
+    yield return new SysMenu
+    {
+        Id = SubDirId + 3, Pid = SubDirId,
+        Title = "订单优先规则", Path = "/aidop/s0/sales/order-priority-rule",
+        Name = "aidopS0SalesOrderPriorityRule",
+        Component = "/aidop/s0/sales/OrderPriorityRuleList",
+        Icon = "ele-Sort", Type = MenuTypeEnum.Menu,
+        CreateTime = ct, OrderNo = 3
+    };
+}
+```
+
+---
+
+## 十、执行步骤
+
+```
+前置   确认 Batch 1 种子已就绪                      →  S0DictSeedData.cs 已创建,平台字典和组织节点已入库
+Step 1   阅读本文档 + S0 源文件                    →  理解字段定义
+Step 2   新建 Entity/S0/Sales/ 下 3 个实体文件      →  [SugarTable] + [SugarColumn] 格式
+Step 3   新建 Dto/S0/Sales/AdoS0SalesDtos.cs       →  3 组 QueryDto + UpsertDto
+Step 4   新建 Controllers/S0/Sales/ 下 3 个控制器   →  [AllowAnonymous][NonUnify] 插件风格
+Step 5   修改 Startup.cs                           →  InitTables 追加 3 实体 + using
+Step 6   后端编译验证                               →  dotnet build 无报错
+Step 7   新建 Web/src/views/aidop/s0/api/s0SalesApi.ts
+Step 8   新建 Web/src/views/aidop/s0/sales/ 下 3 个 Vue 页面
+           (下拉调用 SysOrgApi + SysDictDataApi,不硬编码选项)
+Step 9   修改插件 SysMenuSeedData.cs               →  追加 BuildS0SalesMenus()(S0DirId=1321000001000L)
+Step 10  前端编译验证                               →  pnpm dev 无 TS 错误
+Step 11  集成验收                                   →  按第十一节标准逐项检查
+```
+
+---
+
+## 十一、验收标准
+
+- [ ] 后端 `dotnet build` 无报错
+- [ ] 开发环境启动后自动创建 3 张表:`ado_s0_sales_customer`、`ado_s0_sales_material`、`ado_s0_sales_order_priority_rule`
+- [ ] Swagger 中可见 `/api/s0/sales/*` 共 15 个接口(每实体 5 个 CRUD)
+- [ ] 前端 `pnpm dev` 无 TypeScript 类型错误
+- [ ] 侧栏菜单 "Ai-DOP > S0 运营建模 > 产销建模" 可见,含 3 个子菜单
+- [ ] 客户管理页:增删改查正常,表单含全部 17 个业务字段
+- [ ] 物料管理页:分 Tab 表单正常保存,全部 39 个字段入库
+- [ ] 订单优先规则页:增删改查正常,规则表达式可保存长文本
+
+---
+
+## 十二、前置依赖与后续关联
+
+### 前置依赖(Batch 1 已改为平台复用方案)
+
+Batch 1 不再新建独立实体/页面,改为向 `SysOrg` + `SysDictType`/`SysDictData` 灌入种子数据。
+产销建模页面中的下拉选择器直接调用平台标准 API:
+
+| 下拉项 | 数据源 API | 字典 Code / 过滤条件 |
+|--------|-----------|-------------------|
+| CompanyRefId(公司) | `SysOrgApi` 获取列表后按 `Type="201"` 过滤 | — |
+| FactoryRefId(工厂) | `SysOrgApi` 获取列表后按 `Type="501"` + `Pid=公司Id` 过滤 | — |
+| MaterialType(物料类型) | `SysDictDataApi.apiSysDictDataDataListCodeGet` | `s0_material_type` |
+| PlCategory(计划类别) | 同上 | `s0_pl_category` |
+| StockTypeCode(库存类型) | 同上 | `s0_stock_type` |
+| SpecialReqType(特殊需求) | 同上 | `s0_special_req_type` |
+| MaterialAttribute(物料属性) | 同上 | `s0_material_attribute` |
+| CustomerType(客户类型) | 同上 | `s0_customer_type` |
+| Currency(币种) | 同上 | `s0_currency` |
+| ForbidStatus(禁用状态) | 同上 | `s0_forbid_status` |
+
+### 后续关联
+
+| 后续 Batch | 关联点 | 动作 |
+|-----------|--------|------|
+| Batch 4(仓储) | Material.DefaultLocationId / DefaultRackId | 前端升级为仓储 Location/Rack 下拉 |
+| Batch 3(制造) | 制造实体引用 sales_material(物料 FK) | Material 表需先于制造迁移完成 |

+ 857 - 0
doc/plan/S0/S0迁移.md

@@ -0,0 +1,857 @@
+# S0 → AiDOPWarehouse 迁移计划
+
+> 面向 Cursor / AI 编程助手执行。每次只做一个 Batch,完成验收后再继续下一个。
+
+---
+
+## 一、总体原则
+
+1. **分批迁移**:共 7 批,按依赖关系排序,每批独立可运行。
+2. **遵守目标项目架构**:Admin.NET + SqlSugar + 动态菜单,严禁引入 EF Core。
+3. **架构折叠**:S0 的 Domain / Application / Infrastructure 四层 → 插件单层(Entity + Dto + Controller)。
+4. **前端风格对齐**:`<script setup lang="ts">` + Admin.NET `service` 实例,禁用 S0 的 `http.js` 和 `useCrudPage.js`。
+5. **不动现有文件**:只新增文件;若需修改 `Startup.cs`、`SysMenuSeedData.cs`、`aidopMenuDisplay.ts`,追加内容,不替换已有内容。
+
+---
+
+## 二、架构映射
+
+| 维度 | S0 独立项目 | → AiDOPWarehouse 目标位置 |
+|------|------------|--------------------------|
+| 实体 | `S0.Domain.*.XxxEntity` (EF Core `BaseEntity`) | `server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/[模块]/AdoS0Xxx.cs` |
+| DTO | `S0.Application.*.XxxDto` / `XxxRequest` | `server/Plugins/Admin.NET.Plugin.AiDOP/Dto/S0/[模块]/AdoS0XxxDtos.cs` |
+| 控制器 | `S0.Api.Controllers.*.*Controller` (EF Core Service) | `server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/S0/[模块]/AdoS0XxxController.cs` |
+| 表名 | `plat_company` 等 | `ado_s0_platform_company` 等(加 `ado_s0_` 前缀) |
+| 前端 API | `src/api/modules/xxx.js` (S0 http.js) | `Web/src/views/aidop/s0/api/s0XxxApi.ts` |
+| 前端页面 | `src/views/xxx/XxxList.vue` (JS) | `Web/src/views/aidop/s0/[模块]/XxxList.vue` (TS) |
+
+---
+
+## 三、命名与编码规范
+
+### 3.1 后端
+
+```
+实体类名   : AdoS0<PascalName>        示例: AdoS0Company
+表名       : ado_s0_<module>_<name>   示例: ado_s0_platform_company
+控制器路由 : api/s0/<module>/<name>   示例: api/s0/platform/companies
+命名空间   : Admin.NET.Plugin.AiDOP.Entity.S0.<Module>
+           : Admin.NET.Plugin.AiDOP.Dto.S0.<Module>
+           : Admin.NET.Plugin.AiDOP.Controllers.S0.<Module>
+```
+
+### 3.2 实体模板(SqlSugar,对标 AdoOrder.cs)
+
+```csharp
+namespace Admin.NET.Plugin.AiDOP.Entity.S0.Platform;
+
+/// <summary>公司主数据(S0 Platform / plat_company)</summary>
+[SugarTable("ado_s0_platform_company", "S0 公司主数据")]
+public class AdoS0Company
+{
+    [SugarColumn(ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    public long Id { get; set; }
+
+    [SugarColumn(ColumnDescription = "公司编码", Length = 50)]
+    public string Code { get; set; } = string.Empty;
+
+    [SugarColumn(ColumnDescription = "公司名称", Length = 200)]
+    public string Name { get; set; } = string.Empty;
+
+    [SugarColumn(ColumnDescription = "简称", Length = 100, IsNullable = true)]
+    public string? ShortName { get; set; }
+
+    [SugarColumn(ColumnDescription = "地址", Length = 500, IsNullable = true)]
+    public string? Address { get; set; }
+
+    [SugarColumn(ColumnDescription = "备注", Length = 500, IsNullable = true)]
+    public string? Remark { get; set; }
+
+    [SugarColumn(ColumnDescription = "启用", ColumnDataType = "boolean")]
+    public bool IsEnabled { get; set; } = true;
+
+    [SugarColumn(ColumnDescription = "创建时间")]
+    public DateTime CreatedAt { get; set; } = DateTime.Now;
+
+    [SugarColumn(ColumnDescription = "更新时间", IsNullable = true)]
+    public DateTime? UpdatedAt { get; set; }
+}
+```
+
+> **注意**:
+> - 不继承任何基类(S0 的 `BaseEntity` 字段逐一内联为 `[SugarColumn]` 列)。
+> - S0 中的导航属性(如 `public Company? Company { get; set; }`)**不迁移**。
+> - `IsDeleted` 软删除字段**不迁移**(后端直接物理删除,前端通过空列表反映结果)。
+> - `VersionNo` 并发版本字段**不迁移**(插件层不做并发冲突处理)。
+> - `CompanyRefId` / `FactoryRefId` 等关联 ID 以 `long` 外键字段保留,命名保持与 S0 一致。
+
+### 3.3 控制器模板(对标 OrderController.cs)
+
+```csharp
+using Admin.NET.Plugin.AiDOP.Dto.S0.Platform;
+using Admin.NET.Plugin.AiDOP.Entity.S0.Platform;
+
+namespace Admin.NET.Plugin.AiDOP.Controllers.S0.Platform;
+
+/// <summary>S0 公司主数据</summary>
+[ApiController]
+[Route("api/s0/platform/companies")]
+[AllowAnonymous]
+[NonUnify]
+public class AdoS0CompaniesController : ControllerBase
+{
+    private readonly SqlSugarRepository<AdoS0Company> _rep;
+    public AdoS0CompaniesController(SqlSugarRepository<AdoS0Company> rep) => _rep = rep;
+
+    [HttpGet]
+    public async Task<IActionResult> GetPagedAsync([FromQuery] AdoS0CompanyQueryDto q)
+    {
+        var total = 0;
+        var list = await _rep.AsQueryable()
+            .WhereIF(!string.IsNullOrWhiteSpace(q.Code), x => x.Code.Contains(q.Code!))
+            .WhereIF(!string.IsNullOrWhiteSpace(q.Name), x => x.Name.Contains(q.Name!))
+            .WhereIF(q.IsEnabled.HasValue, x => x.IsEnabled == q.IsEnabled!.Value)
+            .ToPageListAsync(q.Page, q.PageSize, ref total);
+        return Ok(new { total, page = q.Page, pageSize = q.PageSize, list });
+    }
+
+    [HttpGet("{id:long}")]
+    public async Task<IActionResult> GetAsync(long id)
+    {
+        var item = await _rep.GetByIdAsync(id);
+        return item is null ? NotFound() : Ok(item);
+    }
+
+    [HttpPost]
+    public async Task<IActionResult> CreateAsync([FromBody] AdoS0CompanyUpsertDto dto)
+    {
+        var entity = new AdoS0Company
+        {
+            Code = dto.Code,
+            Name = dto.Name,
+            ShortName = dto.ShortName,
+            Address = dto.Address,
+            Remark = dto.Remark,
+            IsEnabled = dto.IsEnabled,
+            CreatedAt = DateTime.Now
+        };
+        await _rep.InsertAsync(entity);
+        return Ok(entity);
+    }
+
+    [HttpPut("{id:long}")]
+    public async Task<IActionResult> UpdateAsync(long id, [FromBody] AdoS0CompanyUpsertDto dto)
+    {
+        var entity = await _rep.GetByIdAsync(id);
+        if (entity is null) return NotFound();
+        entity.Code = dto.Code;
+        entity.Name = dto.Name;
+        entity.ShortName = dto.ShortName;
+        entity.Address = dto.Address;
+        entity.Remark = dto.Remark;
+        entity.IsEnabled = dto.IsEnabled;
+        entity.UpdatedAt = DateTime.Now;
+        await _rep.UpdateAsync(entity);
+        return Ok(entity);
+    }
+
+    [HttpDelete("{id:long}")]
+    public async Task<IActionResult> DeleteAsync(long id)
+    {
+        await _rep.DeleteByIdAsync(id);
+        return Ok();
+    }
+}
+```
+
+### 3.4 DTO 模板
+
+```csharp
+namespace Admin.NET.Plugin.AiDOP.Dto.S0.Platform;
+
+public class AdoS0CompanyQueryDto
+{
+    public string? Code { get; set; }
+    public string? Name { get; set; }
+    public bool? IsEnabled { get; set; }
+    public int Page { get; set; } = 1;
+    public int PageSize { get; set; } = 20;
+}
+
+public class AdoS0CompanyUpsertDto
+{
+    public string Code { get; set; } = string.Empty;
+    public string Name { get; set; } = string.Empty;
+    public string? ShortName { get; set; }
+    public string? Address { get; set; }
+    public string? Remark { get; set; }
+    public bool IsEnabled { get; set; } = true;
+}
+```
+
+### 3.5 前端 API 模板(对标 legacyAidop.ts)
+
+```typescript
+// Web/src/views/aidop/s0/api/s0PlatformApi.ts
+import service from '/@/utils/request';
+
+function unwrap<T>(res: { data: T }): T { return res.data; }
+
+export interface Paged<T> { total: number; page: number; pageSize: number; list: T[]; }
+
+export interface S0CompanyRow {
+  id: number; code: string; name: string;
+  shortName?: string | null; address?: string | null;
+  remark?: string | null; isEnabled: boolean;
+  createdAt?: string; updatedAt?: string | null;
+}
+export interface S0CompanyUpsert {
+  code: string; name: string;
+  shortName?: string; address?: string;
+  remark?: string; isEnabled: boolean;
+}
+
+export const s0CompaniesApi = {
+  list: (params: Record<string, unknown>) =>
+    service.get<Paged<S0CompanyRow>>('/api/s0/platform/companies', { params }).then(unwrap),
+  get: (id: number) =>
+    service.get<S0CompanyRow>(`/api/s0/platform/companies/${id}`).then(unwrap),
+  create: (body: S0CompanyUpsert) =>
+    service.post<S0CompanyRow>('/api/s0/platform/companies', body).then(unwrap),
+  update: (id: number, body: S0CompanyUpsert) =>
+    service.put<S0CompanyRow>(`/api/s0/platform/companies/${id}`, body).then(unwrap),
+  delete: (id: number) =>
+    service.delete(`/api/s0/platform/companies/${id}`).then(unwrap),
+};
+```
+
+### 3.6 前端页面模板(对标 orderList.vue)
+
+```vue
+<!-- Web/src/views/aidop/s0/platform/CompanyManagement.vue -->
+<template>
+  <AidopDemoShell :title="pageTitle" subtitle="S0 / Platform / 公司主数据">
+    <!-- 查询栏 -->
+    <el-form :inline="true" :model="query" class="mb12" @submit.prevent>
+      <el-form-item label="编码">
+        <el-input v-model="query.code" clearable style="width:140px" />
+      </el-form-item>
+      <el-form-item label="名称">
+        <el-input v-model="query.name" clearable style="width:160px" />
+      </el-form-item>
+      <el-form-item>
+        <el-button type="primary" @click="loadList">查询</el-button>
+        <el-button @click="resetQuery">重置</el-button>
+        <el-button type="success" @click="openCreate">新增</el-button>
+      </el-form-item>
+    </el-form>
+
+    <!-- 表格 -->
+    <el-table :data="rows" v-loading="loading" border stripe style="width:100%">
+      <el-table-column prop="code"    label="编码"   width="120" />
+      <el-table-column prop="name"    label="名称"   min-width="160" />
+      <el-table-column prop="shortName" label="简称" width="120" />
+      <el-table-column prop="isEnabled" label="启用" width="80" align="center">
+        <template #default="{ row }">
+          <el-tag :type="row.isEnabled ? 'success' : 'info'" size="small">
+            {{ row.isEnabled ? '是' : '否' }}
+          </el-tag>
+        </template>
+      </el-table-column>
+      <el-table-column label="操作" width="160" fixed="right" align="center">
+        <template #default="{ row }">
+          <el-button link type="primary" @click="openEdit(row)">编辑</el-button>
+          <el-button link type="danger"  @click="onDelete(row)">删除</el-button>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <div class="pager">
+      <el-pagination
+        v-model:current-page="query.page"
+        v-model:page-size="query.pageSize"
+        :total="total"
+        :page-sizes="[20, 50, 100]"
+        layout="total, sizes, prev, pager, next"
+        @current-change="loadList"
+        @size-change="loadList"
+      />
+    </div>
+
+    <!-- 弹窗 -->
+    <el-dialog
+      v-model="dialogVisible"
+      :title="editingId ? '编辑公司' : '新增公司'"
+      width="480px"
+      destroy-on-close
+    >
+      <el-form ref="formRef" :model="form" :rules="rules" label-width="80px">
+        <el-form-item label="编码" prop="code">
+          <el-input v-model="form.code" />
+        </el-form-item>
+        <el-form-item label="名称" prop="name">
+          <el-input v-model="form.name" />
+        </el-form-item>
+        <el-form-item label="简称">
+          <el-input v-model="form.shortName" />
+        </el-form-item>
+        <el-form-item label="地址">
+          <el-input v-model="form.address" />
+        </el-form-item>
+        <el-form-item label="启用">
+          <el-switch v-model="form.isEnabled" />
+        </el-form-item>
+      </el-form>
+      <template #footer>
+        <el-button @click="dialogVisible = false">取消</el-button>
+        <el-button type="primary" :loading="saving" @click="submitForm">保存</el-button>
+      </template>
+    </el-dialog>
+  </AidopDemoShell>
+</template>
+
+<script setup lang="ts" name="aidopS0PlatformCompany">
+import { reactive, ref, onMounted } from 'vue';
+import { useRoute } from 'vue-router';
+import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus';
+import AidopDemoShell from '../../components/AidopDemoShell.vue';
+import { s0CompaniesApi, type S0CompanyRow, type S0CompanyUpsert } from '../api/s0PlatformApi';
+
+const route   = useRoute();
+const pageTitle = (route.meta?.title as string) || '公司管理';
+
+const rows    = ref<S0CompanyRow[]>([]);
+const total   = ref(0);
+const loading = ref(false);
+const query   = reactive({ code: '', name: '', page: 1, pageSize: 20 });
+
+const dialogVisible = ref(false);
+const saving   = ref(false);
+const editingId = ref<number | null>(null);
+const formRef  = ref<FormInstance>();
+const form     = reactive<S0CompanyUpsert>({ code: '', name: '', shortName: '', address: '', isEnabled: true });
+
+const rules: FormRules = {
+  code: [{ required: true, message: '请填写编码', trigger: 'blur' }],
+  name: [{ required: true, message: '请填写名称', trigger: 'blur' }],
+};
+
+async function loadList() {
+  loading.value = true;
+  try {
+    const r = await s0CompaniesApi.list({ ...query });
+    rows.value  = r.list;
+    total.value = r.total;
+  } finally {
+    loading.value = false;
+  }
+}
+
+function resetQuery() {
+  Object.assign(query, { code: '', name: '', page: 1 });
+  loadList();
+}
+
+function openCreate() {
+  editingId.value = null;
+  Object.assign(form, { code: '', name: '', shortName: '', address: '', isEnabled: true });
+  dialogVisible.value = true;
+}
+
+function openEdit(row: S0CompanyRow) {
+  editingId.value = row.id;
+  Object.assign(form, { code: row.code, name: row.name, shortName: row.shortName ?? '', address: row.address ?? '', isEnabled: row.isEnabled });
+  dialogVisible.value = true;
+}
+
+async function submitForm() {
+  await formRef.value?.validate();
+  saving.value = true;
+  try {
+    if (editingId.value) {
+      await s0CompaniesApi.update(editingId.value, { ...form });
+    } else {
+      await s0CompaniesApi.create({ ...form });
+    }
+    ElMessage.success('保存成功');
+    dialogVisible.value = false;
+    await loadList();
+  } finally {
+    saving.value = false;
+  }
+}
+
+function onDelete(row: S0CompanyRow) {
+  ElMessageBox.confirm(`确定删除「${row.name}」?`, '确认', { type: 'warning' })
+    .then(async () => {
+      await s0CompaniesApi.delete(row.id);
+      ElMessage.success('已删除');
+      await loadList();
+    });
+}
+
+onMounted(loadList);
+</script>
+```
+
+### 3.7 菜单 SeedData 追加规则
+
+- S0 根目录(已存在):`Id = 1321000001000L`(由 `ModuleDefinitions` 第 1 项自动生成:`1321000000000 + 1*1000`),`Pid = AidopRootId`,`Name = "aidopDirS0"`
+- S0 子模块目录 ID 范围:`1329_001_000_000L` ~ `1329_099_000_000L`(步长 1000)
+- S0 子菜单 ID 范围:`1329_001_000_001L` ~ `1329_099_000_999L`
+
+> **每批追加菜单时,在 `SysMenuSeedData.cs` 中 `HasData()` 末尾 `foreach` 之后,新增一个私有方法 `BuildS0XxxMenus()` 并在 `HasData` 的 `foreach` 后用 `foreach (var m in BuildS0XxxMenus(ct)) list.Add(m);` 拼入。**
+
+---
+
+## 四、Startup.cs 修改规则
+
+每批新增实体后,在 `Startup.cs` 的 `db.CodeFirst.InitTables(...)` 调用中**追加新实体类型**:
+
+```csharp
+// 现有
+db.CodeFirst.InitTables(typeof(AdoOrder), typeof(AdoPlan), typeof(AdoWorkOrder));
+
+// Batch 1 后改为:
+db.CodeFirst.InitTables(
+    typeof(AdoOrder), typeof(AdoPlan), typeof(AdoWorkOrder),
+    // S0 Platform
+    typeof(AdoS0Company), typeof(AdoS0Factory),
+    typeof(AdoS0DictType), typeof(AdoS0DictItem)
+);
+```
+
+---
+
+## 五、分批计划
+
+| Batch | 模块 | S0 源目录 | 实体数 | 页面数 | 菜单子目录 ID |
+|-------|------|-----------|--------|--------|--------------|
+| 1 | Platform 基础平台(**复用平台**) | `Domain/Platform` | 0(复用 SysOrg + SysDictType/Data) | 0(复用已有页面) | 无需新建 |
+| 2 | Sales 产销建模 | `Domain/Sales`、`api/modules/sales.js`、`views/CustomerManagement.vue` 等 | 3 | 3 | `1329002000000L` |
+| 3 | Manufacturing 制造建模 | `Domain/Manufacturing`、`api/modules/manufacturing.js`、`views/manufacturing/` | 12 | 18 | `1329003000000L` |
+| 4 | Warehouse 仓储建模 | `Domain/Warehouse`、`api/modules/warehouse.js`、`views/warehouse/` | 12 | 12 | `1329004000000L` |
+| 5 | Quality 质量建模 | `Domain/Quality`、`api/modules/quality.js`、`views/quality/` | 16 | 16 | `1329005000000L` |
+| 6 | Supply 供应建模 | `Domain/Supply`、`api/modules/supply.js`、`views/supply/` | 4 | 3 | `1329006000000L` |
+| 7 | Business 业务流程 | `Domain/Business`、`api/modules/business.js`、`views/business/` | 4 | 2 | `1329007000000L` |
+
+---
+
+## 六、逐批详细内容
+
+---
+
+### Batch 1:Platform 基础平台(复用平台能力)
+
+> **方案变更**:S0 的 Platform 模块(Company / Factory / DictType / DictItem)与 Admin.NET 已有的
+> `SysOrg`(组织机构)和 `SysDictType` + `SysDictData`(字典)高度同构,不再新建独立实体/页面,
+> 改为**复用平台表 + 灌入业务种子数据**。
+
+#### 1.1 映射关系
+
+| S0 Platform 实体 | 本质 | 目标平台表 | 复用策略 |
+|-----------------|------|-----------|---------|
+| `plat_company` (Code/Name/ShortName/Address) | 公司主数据 | `SysOrg` (Type="201"=公司) | 通过"系统管理 > 机构管理"维护,或 SeedData 灌入 |
+| `plat_factory` (Code/Name/CompanyRefId) | 工厂主数据 | `SysOrg` (Type="501"=组,Pid=公司节点) | 同上,用 Pid 父子关系表达归属 |
+| `plat_dict_type` (TypeCode/TypeName) | 字典分类 | `SysDictType` (Code/Name) | 通过"系统管理 > 字典管理"维护,或 SeedData 灌入 |
+| `plat_dict_item` (TypeCode/ItemCode/ItemName/SortNo) | 字典值 | `SysDictData` (DictTypeId/Value/Label/OrderNo) | 同上 |
+
+#### 1.2 SysOrg 树形结构
+
+S0 的 Company → Factory 父子关系映射到 `SysOrg` 树:
+
+```
+系统默认(集团 Type=101, Id=SqlSugarConst.DefaultTenantId)
+  ├─ 公司A(公司 Type=201, Code="C001", Name="XX有限公司")    ← S0 Company
+  │   ├─ 工厂A1(组 Type=501, Code="F001", Name="一工厂")    ← S0 Factory
+  │   └─ 工厂A2(组 Type=501, Code="F002", Name="二工厂")
+  └─ 公司B(公司 Type=201, Code="C002")
+      └─ 工厂B1
+```
+
+> `SysOrg.Remark` 可存放 S0 Company 的 Address 信息。SpareText1~3 在 S0 中未实际使用,不迁移。
+
+#### 1.3 S0 业务字典灌入
+
+在 AiDOP 插件中新增种子文件 `server/Plugins/Admin.NET.Plugin.AiDOP/SeedData/S0DictSeedData.cs`,
+向 `SysDictType` + `SysDictData` 灌入 S0 各模块所需的业务字典(编码加 `s0_` 前缀避免与系统字典冲突):
+
+| 字典 Code | 字典名称 | 值示例 | 消费方 |
+|-----------|---------|--------|--------|
+| `s0_material_type` | 物料类型 | raw/semi/finished/aux/fixture/packaging | Batch 2 物料管理 |
+| `s0_pl_category` | 计划类别 | purchase/manufacture/outsource | Batch 2 物料管理 |
+| `s0_stock_type` | 库存类型 | normal/inspection/consigned/safety | Batch 2 物料管理 |
+| `s0_special_req_type` | 特殊需求类型 | common/urgent/export/custom | Batch 2 物料管理 |
+| `s0_material_attribute` | 物料属性 | standard/purchase/manufacture/outsource | Batch 2 物料管理 |
+| `s0_customer_type` | 客户类型 | domestic/overseas/dealer | Batch 2 客户管理 |
+| `s0_currency` | 币种 | CNY/USD/EUR/JPY/HKD | Batch 2 客户管理 |
+| `s0_forbid_status` | 禁用状态 | normal/forbidden | 通用 |
+
+> 后续 Batch 3~7 的字典也在此文件统一追加。
+
+#### 1.4 后端改动
+
+**新建文件(1 个):**
+```
+server/Plugins/Admin.NET.Plugin.AiDOP/SeedData/S0DictSeedData.cs
+```
+
+**不新建实体、不新建控制器、不修改 Startup.cs**(`SysDictType`/`SysDictData`/`SysOrg` 已由框架 CodeFirst 管理)。
+
+#### 1.5 前端改动
+
+**无新建页面**。使用已有页面:
+- 公司/工厂维护 → "系统管理 > 机构管理"(`views/system/org/index.vue`)
+- 字典维护 → "系统管理 > 字典管理"(`views/system/dict/index.vue`)
+
+#### 1.6 后续 Batch 调用方式
+
+产销建模等页面中的下拉选择器调用平台标准 API:
+
+```typescript
+// 获取公司列表(Type=201 为公司节点)
+import { SysOrgApi } from '/@/api-services/apis/sys-org-api';
+// 调用后在前端按 Type 过滤,或后端提供按 Type 过滤的接口
+
+// 获取字典值列表(按字典编码)
+import { SysDictDataApi } from '/@/api-services/apis/sys-dict-data-api';
+SysDictDataApi.apiSysDictDataDataListCodeGet('s0_material_type')
+```
+
+#### 1.7 Batch 1 验收标准
+
+- [ ] `S0DictSeedData.cs` 编译通过
+- [ ] 启动后 `sys_dict_type` 中可见 `s0_*` 前缀的字典类型
+- [ ] 启动后 `sys_dict_data` 中可见对应字典值
+- [ ] 前端"字典管理"页面可查看和编辑 S0 业务字典
+- [ ] 前端"机构管理"页面可创建公司/工厂组织节点
+
+---
+
+### Batch 2:Sales 产销建模
+
+> **详细方案见** [Batch2-产销建模迁移方案.md](Batch2-产销建模迁移方案.md)
+
+**源文件参考路径(只读):**
+```
+/home/yy968/work/s0/s0-operating-modeling/backend/src/
+  S0.Domain/Sales/Customer.cs          ← [Table("sales_customer")]
+  S0.Domain/Sales/Material.cs          ← [Table("sales_material")](30+ 字段)
+  S0.Domain/Sales/OrderPriorityRule.cs ← [Table("sales_order_priority_rule")]
+  S0.Api/Controllers/Sales/CustomersController.cs
+  S0.Api/Controllers/Sales/MaterialsController.cs
+  S0.Api/Controllers/Sales/OrderPriorityRulesController.cs
+
+/home/yy968/work/s0/s0-operating-modeling/src/
+  api/modules/sales.js
+  views/CustomerManagement.vue         ← 路由 /sales-modeling/customers
+  views/MaterialManagement.vue         ← 路由 /sales-modeling/materials
+  views/OrderPriorityConfig.vue        ← 路由 /sales-modeling/order-priority
+```
+
+> **修正说明**:S0.Domain.Sales 实际只有 3 个实体(Customer / Material / OrderPriorityRule),
+> 不含 BOM(原计划误列)。Material 实际含 30+ 字段(库存、采购、质量、批次管理等参数),
+> 远超原计划列出的 10 个字段。
+
+**后端 — 新建文件:**
+
+```
+Entity/S0/Sales/
+  AdoS0Customer.cs              ← sales_customer → ado_s0_sales_customer(20 字段)
+  AdoS0Material.cs              ← sales_material → ado_s0_sales_material(43 字段)
+  AdoS0OrderPriorityRule.cs     ← sales_order_priority_rule → ado_s0_sales_order_priority_rule(19 字段)
+Dto/S0/Sales/
+  AdoS0SalesDtos.cs             ← 3 组 QueryDto + UpsertDto
+Controllers/S0/Sales/
+  AdoS0CustomersController.cs
+  AdoS0MaterialsController.cs
+  AdoS0OrderPriorityRulesController.cs
+```
+
+**前端 — 新建文件:**
+
+```
+Web/src/views/aidop/s0/
+  api/s0SalesApi.ts
+  sales/
+    CustomerList.vue              ← name="aidopS0SalesCustomer"
+    MaterialList.vue              ← name="aidopS0SalesMaterial"(el-tabs 分 6 组表单)
+    OrderPriorityRuleList.vue     ← name="aidopS0SalesOrderPriorityRule"
+```
+
+**下拉数据源(来自 Batch 1 灌入的平台数据):**
+- CompanyRefId / FactoryRefId → `SysOrgApi`(按 Type 过滤公司/工厂)
+- MaterialType / PlCategory 等 → `SysDictDataApi.apiSysDictDataDataListCodeGet('s0_material_type')` 等
+
+**菜单 SeedData 追加**(方法名 `BuildS0SalesMenus`,S0DirId = `1321000001000L`,SubDirId = `1329002000000L`)
+
+**Batch 2 验收标准:**
+- [ ] 3 张表自动创建(`ado_s0_sales_customer`、`ado_s0_sales_material`、`ado_s0_sales_order_priority_rule`)
+- [ ] Swagger 可见 `/api/s0/sales/*` 共 15 个接口
+- [ ] 3 个页面可增删改查
+- [ ] 物料管理分 Tab 表单正常保存,全部 39 个业务字段入库
+- [ ] 下拉选择器正确从平台字典/组织 API 拉取数据
+
+---
+
+### Batch 3:Manufacturing 制造建模
+
+**源文件参考路径(只读):**
+```
+/home/yy968/work/s0/s0-operating-modeling/backend/src/
+  S0.Api/Controllers/Manufacturing/ManufacturingControllers.cs  ← 多个 Controller 合并在一文件
+  S0.Application/Manufacturing/
+
+/home/yy968/work/s0/s0-operating-modeling/src/
+  api/modules/manufacturing.js
+  views/manufacturing/
+    RoutingList.vue
+    ProductionLineList.vue
+    StandardProcessList.vue
+    StandardBomManagement.vue
+    WorkOrderControlParams.vue
+    MaterialSubstitutionList.vue
+    PersonnelSkillList.vue
+    PersonSkillAssignmentList.vue
+    LineMaterialList.vue
+    LinePostList.vue
+    WorkCenterList.vue
+    MaterialProcessElementList.vue
+    ProductionElementParamList.vue
+    PreprocessElementList.vue
+    PreprocessElementParamList.vue
+    SopFileTypeList.vue
+    SopMaintenanceList.vue
+    ManufacturingCrudPage.vue   ← 含多个子路由入口
+    ManufacturingAggregatePage.vue
+    ManufacturingTodoPage.vue
+```
+
+**后端 — 新建实体(约 12 个):**
+
+```
+Entity/S0/Manufacturing/
+  AdoS0StandardOperation.cs    → ado_s0_mfg_standard_operation
+  AdoS0ProductionLine.cs       → ado_s0_mfg_production_line
+  AdoS0Routing.cs              → ado_s0_mfg_routing
+  AdoS0RoutingOperation.cs     → ado_s0_mfg_routing_operation
+  AdoS0MaterialSubstitution.cs → ado_s0_mfg_material_substitution
+  AdoS0WorkOrderControl.cs     → ado_s0_mfg_work_order_control
+  AdoS0PersonSkill.cs          → ado_s0_mfg_person_skill
+  AdoS0WorkCenter.cs           → ado_s0_mfg_work_center
+  AdoS0LineMaterial.cs         → ado_s0_mfg_line_material
+  AdoS0LinePost.cs             → ado_s0_mfg_line_post
+  AdoS0SopFileType.cs          → ado_s0_mfg_sop_file_type
+  AdoS0MaterialProcessElement.cs → ado_s0_mfg_material_process_element
+```
+
+> **实体字段**:从 `ManufacturingControllers.cs` 和 `AppDbContext.cs` 的 `ConfigureManufacturing` 段读取全部字段定义,逐一转为 `[SugarColumn]` 格式。
+
+**前端 — 新建文件:**
+
+```
+Web/src/views/aidop/s0/
+  api/s0ManufacturingApi.ts
+  manufacturing/
+    RoutingList.vue              ← name="aidopS0MfgRouting"
+    ProductionLineList.vue       ← name="aidopS0MfgProductionLine"
+    StandardProcessList.vue      ← name="aidopS0MfgStandardProcess"
+    WorkOrderControlParams.vue   ← name="aidopS0MfgWorkOrderControl"
+    MaterialSubstitutionList.vue ← name="aidopS0MfgMaterialSubstitution"
+    PersonnelSkillList.vue       ← name="aidopS0MfgPersonSkill"
+    WorkCenterList.vue           ← name="aidopS0MfgWorkCenter"
+    LineMaterialList.vue         ← name="aidopS0MfgLineMaterial"
+    LinePostList.vue             ← name="aidopS0MfgLinePost"
+    SopFileTypeList.vue          ← name="aidopS0MfgSopFileType"
+    MaterialProcessElementList.vue ← name="aidopS0MfgMaterialProcessElement"
+```
+
+> `ManufacturingAggregatePage.vue` / `ManufacturingTodoPage.vue` 是 S0 的聚合展示页,暂不迁移(标注为后续补充)。
+
+**菜单 SeedData 追加(方法名 `BuildS0ManufacturingMenus`,SubDirId = 1329003000000L)**
+
+---
+
+### Batch 4:Warehouse 仓储建模
+
+**源文件参考路径(只读):**
+```
+/home/yy968/work/s0/s0-operating-modeling/src/
+  api/modules/warehouse.js
+  views/warehouse/
+    LocationMaintenanceList.vue
+    DepartmentMaintenanceList.vue
+    EmployeeList.vue
+    RackList.vue
+    BarcodeRuleList.vue
+    LabelFormatList.vue
+    CostCenterList.vue
+    MaterialDutyList.vue
+    MaterialStatusTaskList.vue
+    OrderRuleList.vue
+    OrderTypeList.vue
+    PackagingSpecList.vue
+    WarehouseCrudPage.vue
+    WarehouseAggregatePage.vue
+```
+
+**后端 — 新建实体(约 12 个):**
+
+```
+Entity/S0/Warehouse/
+  AdoS0Location.cs          → ado_s0_wh_location
+  AdoS0Department.cs        → ado_s0_wh_department
+  AdoS0Employee.cs          → ado_s0_wh_employee
+  AdoS0Rack.cs              → ado_s0_wh_rack
+  AdoS0BarcodeRule.cs       → ado_s0_wh_barcode_rule
+  AdoS0LabelFormat.cs       → ado_s0_wh_label_format
+  AdoS0CostCenter.cs        → ado_s0_wh_cost_center
+  AdoS0MaterialDuty.cs      → ado_s0_wh_material_duty
+  AdoS0MaterialStatusTask.cs→ ado_s0_wh_material_status_task
+  AdoS0OrderRule.cs         → ado_s0_wh_order_rule
+  AdoS0OrderType.cs         → ado_s0_wh_order_type
+  AdoS0PackagingSpec.cs     → ado_s0_wh_packaging_spec
+```
+
+**菜单 SeedData 追加(方法名 `BuildS0WarehouseMenus`,SubDirId = 1329004000000L)**
+
+---
+
+### Batch 5:Quality 质量建模
+
+**源文件参考路径(只读):**
+```
+/home/yy968/work/s0/s0-operating-modeling/src/
+  api/modules/quality.js
+  views/quality/
+    AqlLevelList.vue
+    AqlValueList.vue
+    InspectionInstrumentList.vue
+    SamplingPlanList.vue
+    InspectionSchemeList.vue
+    DocumentTypeList.vue
+    BusinessTypeList.vue
+    InspectionMethodList.vue
+    QualityProjectTemplateList.vue
+    ComplaintCategoryList.vue
+    EightDTemplateList.vue
+    InspectionItemList.vue
+    InspectionPriorityList.vue
+    InspectionStandardList.vue
+    InspectionStatusList.vue
+    InspectionFrequencyList.vue
+    InspectionBasisList.vue
+    NcrDispositionTypeList.vue
+    NonconformanceContactTemplateList.vue
+    CapaTypeList.vue
+    RawMaterialSkipList.vue
+```
+
+**后端 — 新建实体(约 16 个):**
+
+```
+Entity/S0/Quality/
+  AdoS0AqlLevel.cs          → ado_s0_qly_aql_level
+  AdoS0AqlValue.cs          → ado_s0_qly_aql_value
+  AdoS0SamplingPlan.cs      → ado_s0_qly_sampling_plan
+  AdoS0InspectionScheme.cs  → ado_s0_qly_inspection_scheme
+  AdoS0InspectionInstrument.cs → ado_s0_qly_inspection_instrument
+  AdoS0InspectionMethod.cs  → ado_s0_qly_inspection_method
+  AdoS0InspectionItem.cs    → ado_s0_qly_inspection_item
+  AdoS0InspectionStandard.cs→ ado_s0_qly_inspection_standard
+  AdoS0DocumentType.cs      → ado_s0_qly_document_type
+  AdoS0BusinessType.cs      → ado_s0_qly_business_type
+  AdoS0ComplaintCategory.cs → ado_s0_qly_complaint_category
+  AdoS0NcrDispositionType.cs→ ado_s0_qly_ncr_disposition_type
+  AdoS0CapaType.cs          → ado_s0_qly_capa_type
+  AdoS0InspectionPriority.cs→ ado_s0_qly_inspection_priority
+  AdoS0QualityProjectTemplate.cs → ado_s0_qly_project_template
+  AdoS0EightDTemplate.cs    → ado_s0_qly_eight_d_template
+```
+
+**菜单 SeedData 追加(方法名 `BuildS0QualityMenus`,SubDirId = 1329005000000L)**
+
+---
+
+### Batch 6:Supply 供应建模
+
+**源文件参考路径(只读):**
+```
+/home/yy968/work/s0/s0-operating-modeling/src/
+  api/modules/supply.js
+  views/supply/
+    SupplierList.vue
+    SourcingList.vue
+```
+
+**后端 — 新建实体(约 4 个):**
+
+```
+Entity/S0/Supply/
+  AdoS0Supplier.cs          → ado_s0_supply_supplier
+  AdoS0Sourcing.cs          → ado_s0_supply_sourcing
+  AdoS0SourcingItem.cs      → ado_s0_supply_sourcing_item
+  AdoS0SupplierMaterial.cs  → ado_s0_supply_supplier_material
+```
+
+**菜单 SeedData 追加(方法名 `BuildS0SupplyMenus`,SubDirId = 1329006000000L)**
+
+---
+
+### Batch 7:Business 业务流程
+
+**源文件参考路径(只读):**
+```
+/home/yy968/work/s0/s0-operating-modeling/src/
+  api/modules/business.js
+  views/business/
+    ProcessManagementList.vue
+    FormManagementList.vue
+```
+
+**后端 — 新建实体(约 4 个):**
+
+```
+Entity/S0/Business/
+  AdoS0ProcessDef.cs        → ado_s0_biz_process_def
+  AdoS0ProcessNode.cs       → ado_s0_biz_process_node
+  AdoS0FormTemplate.cs      → ado_s0_biz_form_template
+  AdoS0FormField.cs         → ado_s0_biz_form_field
+```
+
+**菜单 SeedData 追加(方法名 `BuildS0BusinessMenus`,SubDirId = 1329007000000L)**
+
+---
+
+## 七、通用执行步骤(每批重复)
+
+每个 Batch 按如下顺序执行,**未通过验收不得进入下一步**:
+
+```
+Step 1  读取 S0 源文件                 →  了解字段定义
+Step 2  新建 Entity/*.cs               →  [SugarTable] + [SugarColumn] 格式
+Step 3  新建 Dto/*.cs                  →  Query/Upsert DTO
+Step 4  新建 Controllers/*.cs          →  [AllowAnonymous][NonUnify] 插件风格
+Step 5  修改 Startup.cs                →  InitTables 追加新实体类型
+Step 6  新建 Web/src/views/aidop/s0/   →  api/*.ts + views/*.vue (TS script setup)
+Step 7  修改 SysMenuSeedData.cs        →  追加 BuildS0XxxMenus() 私有方法
+Step 8  执行验收                       →  后端编译 + 表创建 + Swagger + 前端菜单 + CRUD
+```
+
+---
+
+## 八、注意事项
+
+1. **[AllowAnonymous] 上线前须收紧**:所有 S0 Controller 目前都加 `[AllowAnonymous]`,上线前统一换成 Admin.NET 的 `[Authorize]` 并配置角色权限。
+
+2. **Startup.cs using 语句**:每批新增实体后,在 `Startup.cs` 顶部追加对应命名空间 using。
+
+3. **SeedData ID 唯一性**:追加菜单时,每个 `Id` 在整个 `SysMenuSeedData` 范围内必须唯一,严格使用本文档的 ID 分配方案(1329xxx000000L 起)。
+
+4. **前端 AidopDemoShell 路径**:页面在 `s0/[module]/` 子目录下,相对路径为 `../../components/AidopDemoShell.vue`。
+
+5. **跨实体下拉联动**:如 Factory 依赖 Company、BomItem 依赖 Material,在对应页面中通过 `api.list({pageSize:1000})` 拉取选项列表,赋值给 `ref([])` 作为 `el-select` 的 options。
+
+6. **S0 源项目只读**:迁移时只读取源代码作为参考,不修改 `/home/yy968/work/s0/` 下任何文件。
+
+7. **表名前缀**:所有 S0 新增表统一用 `ado_s0_` 前缀,子模块缩写参照第五节分批计划表(`platform` / `sales` / `mfg` / `wh` / `qly` / `supply` / `biz`)。