S0迁移.md 32 KB

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.jsuseCrudPage.js
  5. 不动现有文件:只新增文件;若需修改 Startup.csSysMenuSeedData.csaidopMenuDisplay.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)

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)

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 模板

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)

// 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)

<!-- 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 = AidopRootIdName = "aidopDirS0"
  • S0 子模块目录 ID 范围:1329_001_000_000L1329_099_000_000L(步长 1000)
  • S0 子菜单 ID 范围:1329_001_000_001L1329_099_000_999L

每批追加菜单时,在 SysMenuSeedData.csHasData() 末尾 foreach 之后,新增一个私有方法 BuildS0XxxMenus() 并在 HasDataforeach 后用 foreach (var m in BuildS0XxxMenus(ct)) list.Add(m); 拼入。


四、Startup.cs 修改规则

每批新增实体后,在 Startup.csdb.CodeFirst.InitTables(...) 调用中追加新实体类型

// 现有
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/Salesapi/modules/sales.jsviews/CustomerManagement.vue 3 3 1329002000000L
3 Manufacturing 制造建模 Domain/Manufacturingapi/modules/manufacturing.jsviews/manufacturing/ 12 18 1329003000000L
4 Warehouse 仓储建模 Domain/Warehouseapi/modules/warehouse.jsviews/warehouse/ 12 12 1329004000000L
5 Quality 质量建模 Domain/Qualityapi/modules/quality.jsviews/quality/ 16 16 1329005000000L
6 Supply 供应建模 Domain/Supplyapi/modules/supply.jsviews/supply/ 4 3 1329006000000L
7 Business 业务流程 Domain/Businessapi/modules/business.jsviews/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.csSysDictType/SysDictData/SysOrg 已由框架 CodeFirst 管理)。

1.5 前端改动

无新建页面。使用已有页面:

  • 公司/工厂维护 → "系统管理 > 机构管理"(views/system/org/index.vue
  • 字典维护 → "系统管理 > 字典管理"(views/system/dict/index.vue

1.6 后续 Batch 调用方式

产销建模等页面中的下拉选择器调用平台标准 API:

// 获取公司列表(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

源文件参考路径(只读):

/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_customerado_s0_sales_materialado_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.csAppDbContext.csConfigureManufacturing 段读取全部字段定义,逐一转为 [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)。