فهرست منبع

feat(S0/S8): S0列名迁移+主键标准化,S0 Sales扩展,S8监控模块

S0 列名迁移与主键标准化:
- 新增 AidopS0ExcelColumnMigration,覆盖23张S0目标表
- 两段式迁移:先显式列重命名(旧列→Excel口径),再主键标准化修复
- SqlSugarSetup 增加 RunPreInitMigrations,确保在 CodeFirst InitTable 之前
  完成迁移,消除 shadow 列冲突(旧列/新列同时存在导致字段歧义)
- 修复 ic_substitute 主键修复条目 IsIdentity 误标为 true,避免 InitTable
  检测到 AUTO_INCREMENT 不一致后重建主键时报 Multiple primary key defined

S0 实体字段对齐:
- 23张表 Entity ColumnName 按 S0-field-diff-db-vs-excel.md Excel口径更新

S0 Sales 扩展:
- 新增 ContractReviewCycle / OrderReviewCycle / ProductDesignCycle
  完整 Entity + Controller + DTO + 前端列表页
- 扩展 CustomerList / MaterialList / SalesDtos

S8 监控模块:
- 新增 AdoS8MonitoringController / S8MonitoringService / S8SceneCode
- 新增 S8 监控前端:Overview / Production / Supply / Delivery 四页及组件
- AdoS8Exception 补字段,S8ExceptionService 补逻辑

菜单种子数据:补充 S0/S8 相关菜单及 AidopMenuLinkSync 同步逻辑
YY968XX 6 ماه پیش
والد
کامیت
325183ca5a
55فایلهای تغییر یافته به همراه3987 افزوده شده و 322 حذف شده
  1. 148 0
      Web/src/views/aidop/s0/api/s0SalesApi.ts
  2. 359 0
      Web/src/views/aidop/s0/sales/ContractReviewCycleList.vue
  3. 45 0
      Web/src/views/aidop/s0/sales/CustomerList.vue
  4. 26 0
      Web/src/views/aidop/s0/sales/MaterialList.vue
  5. 323 0
      Web/src/views/aidop/s0/sales/OrderReviewCycleList.vue
  6. 355 0
      Web/src/views/aidop/s0/sales/ProductDesignCycleList.vue
  7. 42 0
      Web/src/views/aidop/s8/api/s8MonitoringApi.ts
  8. 9 0
      Web/src/views/aidop/s8/monitoring/S8MonitoringDeliveryPage.vue
  9. 121 0
      Web/src/views/aidop/s8/monitoring/S8MonitoringOverviewPage.vue
  10. 9 0
      Web/src/views/aidop/s8/monitoring/S8MonitoringProductionPage.vue
  11. 9 0
      Web/src/views/aidop/s8/monitoring/S8MonitoringSupplyPage.vue
  12. 248 0
      Web/src/views/aidop/s8/monitoring/components/AnomalyDrillPanel.vue
  13. 198 0
      Web/src/views/aidop/s8/monitoring/components/AnomalyModuleTable.vue
  14. 152 0
      Web/src/views/aidop/s8/monitoring/components/AnomalySummaryBar.vue
  15. 18 0
      server/Admin.NET.Core/SqlSugar/SqlSugarSetup.cs
  16. 107 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/S0/Sales/AdoS0ContractReviewCyclesController.cs
  17. 10 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/S0/Sales/AdoS0CustomersController.cs
  18. 6 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/S0/Sales/AdoS0MaterialsController.cs
  19. 100 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/S0/Sales/AdoS0OrderReviewCyclesController.cs
  20. 107 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/S0/Sales/AdoS0ProductDesignCyclesController.cs
  21. 25 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/S8/AdoS8MonitoringController.cs
  22. 129 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Dto/S0/Sales/AdoS0SalesDtos.cs
  23. 41 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Dto/S8/AdoS8Dtos.cs
  24. 17 17
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0DevMonitor.cs
  25. 14 14
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0EmpSkills.cs
  26. 14 14
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0ItemOpCondition.cs
  27. 15 15
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0LineMaster.cs
  28. 1 1
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0LineSkillDetail.cs
  29. 9 9
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0LineSkillMaster.cs
  30. 19 19
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0MfgRoutingOpDetail.cs
  31. 18 18
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0ProdLineDetail.cs
  32. 12 12
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0ProductStructure.cs
  33. 10 10
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0WorkCtrMaster.cs
  34. 48 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/AdoS0ContractReviewCycle.cs
  35. 32 17
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/AdoS0CustMaster.cs
  36. 60 51
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/AdoS0ItemMaster.cs
  37. 11 11
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/AdoS0ItemSubstituteDetail.cs
  38. 41 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/AdoS0OrderReviewCycle.cs
  39. 16 16
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/AdoS0PriorityCode.cs
  40. 46 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/AdoS0ProductDesignCycle.cs
  41. 1 1
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/IcSubstitute.cs
  42. 2 2
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Supply/AdoS0SrmPurchase.cs
  43. 20 20
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Supply/AdoS0SuppMaster.cs
  44. 10 10
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Warehouse/AdoS0CostCtrMaster.cs
  45. 9 9
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Warehouse/AdoS0DepartmentMaster.cs
  46. 14 14
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Warehouse/AdoS0EmpWorkDutyMaster.cs
  47. 22 22
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Warehouse/AdoS0EmployeeMaster.cs
  48. 19 19
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Warehouse/AdoS0ItemPackMaster.cs
  49. 4 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S8/AdoS8Exception.cs
  50. 2 1
      server/Plugins/Admin.NET.Plugin.AiDOP/Infrastructure/AidopMenuLinkSync.cs
  51. 636 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Infrastructure/AidopS0ExcelColumnMigration.cs
  52. 89 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Infrastructure/S8/S8SceneCode.cs
  53. 119 0
      server/Plugins/Admin.NET.Plugin.AiDOP/SeedData/SysMenuSeedData.cs
  54. 1 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Service/S8/S8ExceptionService.cs
  55. 69 0
      server/Plugins/Admin.NET.Plugin.AiDOP/Service/S8/S8MonitoringService.cs

+ 148 - 0
Web/src/views/aidop/s0/api/s0SalesApi.ts

@@ -39,6 +39,11 @@ export interface S0CustMasterRow {
 	taxClass?: string | null;
 	taxIn: boolean;
 	custClass?: string | null;
+	address?: string | null;
+	contact?: string | null;
+	position?: string | null;
+	contactInfo?: string | null;
+	creditRating?: string | null;
 	remarks?: string | null;
 	isActive: boolean;
 	isConfirm: boolean;
@@ -61,6 +66,11 @@ export interface S0CustMasterUpsert {
 	taxClass?: string;
 	taxIn: boolean;
 	custClass?: string;
+	address?: string;
+	contact?: string;
+	position?: string;
+	contactInfo?: string;
+	creditRating?: string;
 	remarks?: string;
 	isActive: boolean;
 	isConfirm: boolean;
@@ -121,6 +131,9 @@ export interface S0ItemMasterRow {
 	ownerApplication?: string | null;
 	bomDesign?: number | null;
 	routingDes?: number | null;
+	custSupplied: boolean;
+	drawingDesign?: number | null;
+	designOwner?: string | null;
 	isActive: boolean;
 	isConfirm: boolean;
 	createUser?: string | null;
@@ -179,6 +192,9 @@ export interface S0ItemMasterUpsert {
 	ownerApplication?: string;
 	bomDesign?: number | null;
 	routingDes?: number | null;
+	custSupplied: boolean;
+	drawingDesign?: number | null;
+	designOwner?: string;
 	isActive: boolean;
 	isConfirm: boolean;
 	createUser?: string;
@@ -349,6 +365,138 @@ export async function loadDictOptions(code: string): Promise<OptionItem[]> {
 	}
 }
 
+// ── B1: 合同评审周期标准 ──────────────────────────────────────────────
+
+export interface S0ContractReviewCycleRow {
+	id: number;
+	companyRefId: number;
+	factoryRefId: number;
+	domainCode?: string | null;
+	stageCode: string;
+	stageName: string;
+	stdHours: number;
+	orderNo: number;
+	isActive: boolean;
+	createUser?: string | null;
+	createTime?: string;
+	updateUser?: string | null;
+	updateTime?: string | null;
+}
+
+export interface S0ContractReviewCycleUpsert {
+	companyRefId: number;
+	factoryRefId: number;
+	domainCode?: string;
+	stageCode: string;
+	stageName: string;
+	stdHours: number;
+	orderNo: number;
+	isActive: boolean;
+	createUser?: string;
+	updateUser?: string;
+}
+
+export const s0ContractReviewCyclesApi = {
+	list: (params: Record<string, unknown>) =>
+		service.get<Paged<S0ContractReviewCycleRow>>('/api/s0/sales/contract-review-cycles', { params }).then(unwrap),
+	get: (id: number) =>
+		service.get<S0ContractReviewCycleRow>(`/api/s0/sales/contract-review-cycles/${id}`).then(unwrap),
+	create: (body: S0ContractReviewCycleUpsert) =>
+		service.post<S0ContractReviewCycleRow>('/api/s0/sales/contract-review-cycles', body).then(unwrap),
+	update: (id: number, body: S0ContractReviewCycleUpsert) =>
+		service.put<S0ContractReviewCycleRow>(`/api/s0/sales/contract-review-cycles/${id}`, body).then(unwrap),
+	delete: (id: number) =>
+		service.delete(`/api/s0/sales/contract-review-cycles/${id}`).then(unwrap),
+	toggleEnabled: (id: number, body: { isActive: boolean }) =>
+		service.patch<S0ContractReviewCycleRow>(`/api/s0/sales/contract-review-cycles/${id}/toggle-enabled`, body).then(unwrap),
+};
+
+// ── B2: 产品设计周期标准 ──────────────────────────────────────────────
+
+export interface S0ProductDesignCycleRow {
+	id: number;
+	companyRefId: number;
+	factoryRefId: number;
+	domainCode?: string | null;
+	itemType: string;
+	ownerApplication: string;
+	stdHours: number;
+	isActive: boolean;
+	createUser?: string | null;
+	createTime?: string;
+	updateUser?: string | null;
+	updateTime?: string | null;
+}
+
+export interface S0ProductDesignCycleUpsert {
+	companyRefId: number;
+	factoryRefId: number;
+	domainCode?: string;
+	itemType: string;
+	ownerApplication: string;
+	stdHours: number;
+	isActive: boolean;
+	createUser?: string;
+	updateUser?: string;
+}
+
+export const s0ProductDesignCyclesApi = {
+	list: (params: Record<string, unknown>) =>
+		service.get<Paged<S0ProductDesignCycleRow>>('/api/s0/sales/product-design-cycles', { params }).then(unwrap),
+	get: (id: number) =>
+		service.get<S0ProductDesignCycleRow>(`/api/s0/sales/product-design-cycles/${id}`).then(unwrap),
+	create: (body: S0ProductDesignCycleUpsert) =>
+		service.post<S0ProductDesignCycleRow>('/api/s0/sales/product-design-cycles', body).then(unwrap),
+	update: (id: number, body: S0ProductDesignCycleUpsert) =>
+		service.put<S0ProductDesignCycleRow>(`/api/s0/sales/product-design-cycles/${id}`, body).then(unwrap),
+	delete: (id: number) =>
+		service.delete(`/api/s0/sales/product-design-cycles/${id}`).then(unwrap),
+	toggleEnabled: (id: number, body: { isActive: boolean }) =>
+		service.patch<S0ProductDesignCycleRow>(`/api/s0/sales/product-design-cycles/${id}/toggle-enabled`, body).then(unwrap),
+};
+
+// ── B3: 订单评审周期标准 ──────────────────────────────────────────────
+
+export interface S0OrderReviewCycleRow {
+	id: number;
+	companyRefId: number;
+	factoryRefId: number;
+	domainCode?: string | null;
+	stdHours: number;
+	remarks?: string | null;
+	isActive: boolean;
+	createUser?: string | null;
+	createTime?: string;
+	updateUser?: string | null;
+	updateTime?: string | null;
+}
+
+export interface S0OrderReviewCycleUpsert {
+	companyRefId: number;
+	factoryRefId: number;
+	domainCode?: string;
+	stdHours: number;
+	remarks?: string;
+	isActive: boolean;
+	createUser?: string;
+	updateUser?: string;
+}
+
+export const s0OrderReviewCyclesApi = {
+	list: (params: Record<string, unknown>) =>
+		service.get<Paged<S0OrderReviewCycleRow>>('/api/s0/sales/order-review-cycles', { params }).then(unwrap),
+	get: (id: number) =>
+		service.get<S0OrderReviewCycleRow>(`/api/s0/sales/order-review-cycles/${id}`).then(unwrap),
+	create: (body: S0OrderReviewCycleUpsert) =>
+		service.post<S0OrderReviewCycleRow>('/api/s0/sales/order-review-cycles', body).then(unwrap),
+	update: (id: number, body: S0OrderReviewCycleUpsert) =>
+		service.put<S0OrderReviewCycleRow>(`/api/s0/sales/order-review-cycles/${id}`, body).then(unwrap),
+	delete: (id: number) =>
+		service.delete(`/api/s0/sales/order-review-cycles/${id}`).then(unwrap),
+	toggleEnabled: (id: number, body: { isActive: boolean }) =>
+		service.patch<S0OrderReviewCycleRow>(`/api/s0/sales/order-review-cycles/${id}/toggle-enabled`, body).then(unwrap),
+};
+
 export async function loadOrgList(type?: string): Promise<OrgOption[]> {
 	try {
 		const api = new SysOrgApi(undefined, undefined, service);

+ 359 - 0
Web/src/views/aidop/s0/sales/ContractReviewCycleList.vue

@@ -0,0 +1,359 @@
+<template>
+	<AidopDemoShell :title="pageTitle" subtitle="S0 / Sales / 合同评审周期">
+		<el-form :inline="true" :model="query" class="mb12" @submit.prevent>
+			<el-form-item label="公司">
+				<el-select v-model="query.companyRefId" clearable filterable placeholder="全部" style="width: 180px">
+					<el-option v-for="item in companyOptions" :key="item.id" :label="item.name || item.code || `${item.id}`" :value="item.id" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="工厂">
+				<el-select v-model="query.factoryRefId" clearable filterable placeholder="全部" style="width: 180px">
+					<el-option v-for="item in filteredFactoryOptions" :key="item.id" :label="item.name || item.code || `${item.id}`" :value="item.id" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="域编码">
+				<el-input v-model="query.domainCode" clearable style="width: 140px" />
+			</el-form-item>
+			<el-form-item label="启用">
+				<el-select v-model="query.isActive" clearable placeholder="全部" style="width: 120px">
+					<el-option label="是" :value="true" />
+					<el-option label="否" :value="false" />
+				</el-select>
+			</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="stageCode" label="阶段编码" width="160" show-overflow-tooltip />
+			<el-table-column prop="stageName" label="阶段名称" min-width="160" show-overflow-tooltip />
+			<el-table-column prop="stdHours" label="标准时长(h)" width="120" align="right" />
+			<el-table-column prop="orderNo" label="顺序" width="80" align="center" />
+			<el-table-column label="启用" width="80" align="center">
+				<template #default="{ row }">
+					<el-tag :type="row.isActive ? 'success' : 'info'" size="small">
+						{{ row.isActive ? '是' : '否' }}
+					</el-tag>
+				</template>
+			</el-table-column>
+			<el-table-column label="操作" width="200" fixed="right" align="center">
+				<template #default="{ row }">
+					<el-button link type="primary" @click="openEdit(row)">编辑</el-button>
+					<el-button link :type="row.isActive ? 'warning' : 'success'" @click="toggleActive(row)">
+						{{ row.isActive ? '禁用' : '启用' }}
+					</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="dialogTitle" width="640px" destroy-on-close @closed="resetForm">
+			<el-form ref="formRef" :model="form" :rules="rules" label-width="120px">
+				<el-row :gutter="16">
+					<el-col :span="12">
+						<el-form-item label="公司" prop="companyRefId">
+							<el-select v-model="form.companyRefId" filterable style="width: 100%">
+								<el-option v-for="item in companyOptions" :key="item.id" :label="item.name || item.code || `${item.id}`" :value="item.id" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="工厂" prop="factoryRefId">
+							<el-select v-model="form.factoryRefId" filterable style="width: 100%" @change="syncDomainCode">
+								<el-option v-for="item in formFactoryOptions" :key="item.id" :label="item.name || item.code || `${item.id}`" :value="item.id" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="阶段编码" prop="stageCode">
+							<el-select v-model="form.stageCode" filterable style="width: 100%" @change="onStageCodeChange">
+								<el-option label="意见评审" value="opinion_review" />
+								<el-option label="意见反馈" value="feedback" />
+								<el-option label="二次评审" value="second_review" />
+								<el-option label="领导意见" value="leader_opinion" />
+								<el-option label="合同盖章" value="sign" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="阶段名称" prop="stageName">
+							<el-input v-model="form.stageName" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="标准时长(h)" prop="stdHours">
+							<el-input-number v-model="form.stdHours" :min="0" style="width: 100%" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="顺序">
+							<el-input-number v-model="form.orderNo" :min="1" style="width: 100%" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="启用">
+							<el-switch v-model="form.isActive" />
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</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="aidopS0SalesContractReviewCycle">
+import { computed, onMounted, reactive, ref, watch } from 'vue';
+import { useRoute } from 'vue-router';
+import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus';
+import AidopDemoShell from '../../components/AidopDemoShell.vue';
+import {
+	loadOrgList,
+	s0ContractReviewCyclesApi,
+	type OrgOption,
+	type S0ContractReviewCycleRow,
+	type S0ContractReviewCycleUpsert,
+} from '../api/s0SalesApi';
+
+const route = useRoute();
+const pageTitle = computed(() => (route.meta?.title as string) || '合同评审周期');
+
+const STAGE_NAME_MAP: Record<string, string> = {
+	opinion_review: '意见评审',
+	feedback: '意见反馈',
+	second_review: '二次评审',
+	leader_opinion: '领导意见',
+	sign: '合同盖章',
+};
+
+const query = reactive({
+	domainCode: '',
+	companyRefId: undefined as number | undefined,
+	factoryRefId: undefined as number | undefined,
+	isActive: undefined as boolean | undefined,
+	page: 1,
+	pageSize: 20,
+});
+
+const loading = ref(false);
+const rows = ref<S0ContractReviewCycleRow[]>([]);
+const total = ref(0);
+const dialogVisible = ref(false);
+const dialogTitle = ref('新增');
+const editingId = ref<number | null>(null);
+const saving = ref(false);
+const formRef = ref<FormInstance>();
+
+const companyOptions = ref<OrgOption[]>([]);
+const factoryOptions = ref<OrgOption[]>([]);
+
+const filteredFactoryOptions = computed(() => {
+	if (!query.companyRefId) return factoryOptions.value;
+	return factoryOptions.value.filter((item) => item.pid === query.companyRefId);
+});
+
+const formFactoryOptions = computed(() => {
+	if (!form.companyRefId) return factoryOptions.value;
+	return factoryOptions.value.filter((item) => item.pid === form.companyRefId);
+});
+
+function emptyForm(): S0ContractReviewCycleUpsert {
+	return {
+		companyRefId: 0,
+		factoryRefId: 0,
+		domainCode: '',
+		stageCode: '',
+		stageName: '',
+		stdHours: 0,
+		orderNo: 1,
+		isActive: true,
+		createUser: '',
+		updateUser: '',
+	};
+}
+
+const form = reactive<S0ContractReviewCycleUpsert>(emptyForm());
+
+const rules: FormRules = {
+	companyRefId: [{ required: true, message: '请选择公司', trigger: 'change' }],
+	factoryRefId: [{ required: true, message: '请选择工厂', trigger: 'change' }],
+	stageCode: [{ required: true, message: '请选择阶段编码', trigger: 'change' }],
+	stageName: [{ required: true, message: '请填写阶段名称', trigger: 'blur' }],
+};
+
+function syncDomainCode() {
+	const f = factoryOptions.value.find((item) => item.id === form.factoryRefId);
+	form.domainCode = f?.code ?? '';
+}
+
+function onStageCodeChange(val: string) {
+	if (STAGE_NAME_MAP[val]) {
+		form.stageName = STAGE_NAME_MAP[val];
+	}
+}
+
+watch(
+	() => form.companyRefId,
+	() => {
+		if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) {
+			form.factoryRefId = 0;
+		}
+	}
+);
+
+watch(
+	() => query.companyRefId,
+	() => {
+		if (!filteredFactoryOptions.value.some((item) => item.id === query.factoryRefId)) {
+			query.factoryRefId = undefined;
+		}
+	}
+);
+
+async function loadOptions() {
+	const [companies, factories] = await Promise.all([loadOrgList('201'), loadOrgList('501')]);
+	companyOptions.value = companies;
+	factoryOptions.value = factories;
+}
+
+async function loadList() {
+	loading.value = true;
+	try {
+		const data = await s0ContractReviewCyclesApi.list({
+			page: query.page,
+			pageSize: query.pageSize,
+			domainCode: query.domainCode || undefined,
+			companyRefId: query.companyRefId || undefined,
+			factoryRefId: query.factoryRefId || undefined,
+			isActive: query.isActive,
+		});
+		rows.value = data.list;
+		total.value = data.total;
+	} catch {
+		rows.value = [];
+		total.value = 0;
+	} finally {
+		loading.value = false;
+	}
+}
+
+function resetQuery() {
+	Object.assign(query, {
+		domainCode: '',
+		companyRefId: undefined,
+		factoryRefId: undefined,
+		isActive: undefined,
+		page: 1,
+	});
+	void loadList();
+}
+
+function resetForm() {
+	editingId.value = null;
+	Object.assign(form, emptyForm());
+	formRef.value?.clearValidate();
+}
+
+function openCreate() {
+	resetForm();
+	dialogTitle.value = '新增合同评审周期';
+	dialogVisible.value = true;
+}
+
+function openEdit(row: S0ContractReviewCycleRow) {
+	resetForm();
+	editingId.value = row.id;
+	dialogTitle.value = `编辑:${row.stageName}`;
+	Object.assign(form, {
+		companyRefId: row.companyRefId,
+		factoryRefId: row.factoryRefId,
+		domainCode: row.domainCode ?? '',
+		stageCode: row.stageCode,
+		stageName: row.stageName,
+		stdHours: row.stdHours,
+		orderNo: row.orderNo,
+		isActive: row.isActive,
+		createUser: row.createUser ?? '',
+		updateUser: row.updateUser ?? '',
+	});
+	dialogVisible.value = true;
+}
+
+async function submitForm() {
+	await formRef.value?.validate();
+	saving.value = true;
+	try {
+		const payload: S0ContractReviewCycleUpsert = { ...form };
+		if (editingId.value) {
+			await s0ContractReviewCyclesApi.update(editingId.value, payload);
+			ElMessage.success('已保存');
+		} else {
+			await s0ContractReviewCyclesApi.create(payload);
+			ElMessage.success('已创建');
+		}
+		dialogVisible.value = false;
+		await loadList();
+	} finally {
+		saving.value = false;
+	}
+}
+
+function onDelete(row: S0ContractReviewCycleRow) {
+	ElMessageBox.confirm(`确定删除「${row.stageName}」?`, '确认', { type: 'warning' })
+		.then(async () => {
+			await s0ContractReviewCyclesApi.delete(row.id);
+			ElMessage.success('已删除');
+			await loadList();
+		})
+		.catch(() => {});
+}
+
+function toggleActive(row: S0ContractReviewCycleRow) {
+	const next = !row.isActive;
+	const actionText = next ? '启用' : '禁用';
+	ElMessageBox.confirm(`确定${actionText}「${row.stageName}」?`, '确认', { type: 'warning' })
+		.then(async () => {
+			await s0ContractReviewCyclesApi.toggleEnabled(row.id, { isActive: next });
+			ElMessage.success(`${actionText}成功`);
+			await loadList();
+		})
+		.catch(() => {});
+}
+
+onMounted(async () => {
+	await loadOptions();
+	await loadList();
+});
+</script>
+
+<style scoped lang="scss">
+@import '/@/views/aidop/styles/aidop-demo.scss';
+
+.mb12 {
+	margin-bottom: 12px;
+}
+
+.pager {
+	margin-top: 12px;
+	display: flex;
+	justify-content: flex-end;
+}
+</style>

+ 45 - 0
Web/src/views/aidop/s0/sales/CustomerList.vue

@@ -57,6 +57,11 @@
 					</el-tag>
 				</template>
 			</el-table-column>
+			<el-table-column prop="address" label="地址" min-width="160" show-overflow-tooltip />
+			<el-table-column prop="contact" label="联系人" width="100" show-overflow-tooltip />
+			<el-table-column prop="position" label="职务" width="100" show-overflow-tooltip />
+			<el-table-column prop="contactInfo" label="联系方式" width="140" show-overflow-tooltip />
+			<el-table-column prop="creditRating" label="信用评级" width="100" show-overflow-tooltip />
 			<el-table-column prop="remarks" label="备注" min-width="160" show-overflow-tooltip />
 			<el-table-column label="操作" width="240" fixed="right" align="center">
 				<template #default="{ row }">
@@ -145,6 +150,31 @@
 							<el-input v-model="form.custClass" placeholder="Class" />
 						</el-form-item>
 					</el-col>
+					<el-col :span="24">
+						<el-form-item label="地址">
+							<el-input v-model="form.address" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="联系人">
+							<el-input v-model="form.contact" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="职务">
+							<el-input v-model="form.position" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="联系方式">
+							<el-input v-model="form.contactInfo" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="信用评级">
+							<el-input v-model="form.creditRating" placeholder="如:A/B/C" />
+						</el-form-item>
+					</el-col>
 					<el-col :span="24">
 						<el-form-item label="备注">
 							<el-input v-model="form.remarks" type="textarea" rows="3" />
@@ -216,6 +246,11 @@ const form = reactive<S0CustMasterUpsert>({
 	taxClass: '',
 	taxIn: true,
 	custClass: '',
+	address: '',
+	contact: '',
+	position: '',
+	contactInfo: '',
+	creditRating: '',
 	remarks: '',
 	isActive: true,
 	isConfirm: false,
@@ -327,6 +362,11 @@ function resetForm() {
 		taxClass: '',
 		taxIn: true,
 		custClass: '',
+		address: '',
+		contact: '',
+		position: '',
+		contactInfo: '',
+		creditRating: '',
 		remarks: '',
 		isActive: true,
 		isConfirm: false,
@@ -359,6 +399,11 @@ function openEdit(row: S0CustMasterRow) {
 		taxClass: row.taxClass ?? '',
 		taxIn: row.taxIn,
 		custClass: row.custClass ?? '',
+		address: row.address ?? '',
+		contact: row.contact ?? '',
+		position: row.position ?? '',
+		contactInfo: row.contactInfo ?? '',
+		creditRating: row.creditRating ?? '',
 		remarks: row.remarks ?? '',
 		isActive: row.isActive,
 		isConfirm: row.isConfirm,

+ 26 - 0
Web/src/views/aidop/s0/sales/MaterialList.vue

@@ -71,6 +71,15 @@
 					<el-tag :type="row.isActive ? 'success' : 'info'" size="small">{{ row.isActive ? '是' : '否' }}</el-tag>
 				</template>
 			</el-table-column>
+			<el-table-column label="客供件" width="80" align="center">
+				<template #default="{ row }">
+					<el-tag :type="row.custSupplied ? 'warning' : 'info'" size="small">
+						{{ row.custSupplied ? '是' : '否' }}
+					</el-tag>
+				</template>
+			</el-table-column>
+			<el-table-column prop="drawingDesign" label="图纸设计周期(h)" width="140" show-overflow-tooltip />
+			<el-table-column prop="designOwner" label="设计负责人" width="120" show-overflow-tooltip />
 			<el-table-column prop="remark" label="备注" min-width="120" show-overflow-tooltip />
 			<el-table-column label="操作" width="220" fixed="right" align="center">
 				<template #default="{ row }">
@@ -181,11 +190,22 @@
 							<el-col :span="8"><el-form-item label="存货周转率"><el-input-number v-model="form.stockTurnOver" :min="0" :precision="5" style="width: 100%" controls-position="right" /></el-form-item></el-col>
 							<el-col :span="8"><el-form-item label="BOM设计(天)"><el-input-number v-model="form.bomDesign" :min="0" style="width: 100%" controls-position="right" /></el-form-item></el-col>
 							<el-col :span="8"><el-form-item label="工艺设计(天)"><el-input-number v-model="form.routingDes" :min="0" style="width: 100%" controls-position="right" /></el-form-item></el-col>
+							<el-col :span="8">
+								<el-form-item label="图纸设计周期(h)">
+									<el-input-number v-model="form.drawingDesign" :min="0" style="width: 100%" controls-position="right" />
+								</el-form-item>
+							</el-col>
+							<el-col :span="8">
+								<el-form-item label="设计负责人">
+									<el-input v-model="form.designOwner" />
+								</el-form-item>
+							</el-col>
 						</el-row>
 					</el-tab-pane>
 
 					<el-tab-pane label="开关" name="flags">
 						<el-row :gutter="16">
+							<el-col :span="8"><el-form-item label="客供件"><el-switch v-model="form.custSupplied" /></el-form-item></el-col>
 							<el-col :span="8"><el-form-item label="关键物料"><el-switch v-model="form.keyItem" /></el-form-item></el-col>
 							<el-col :span="8"><el-form-item label="检验"><el-switch v-model="form.inspect" /></el-form-item></el-col>
 							<el-col :span="8"><el-form-item label="是否前处理"><el-switch v-model="form.issuePolicy" /></el-form-item></el-col>
@@ -312,6 +332,9 @@ function emptyForm(): S0ItemMasterUpsert {
 		ownerApplication: '',
 		bomDesign: null,
 		routingDes: null,
+		custSupplied: false,
+		drawingDesign: null,
+		designOwner: '',
 		isActive: true,
 		isConfirm: false,
 		createUser: '',
@@ -486,6 +509,9 @@ function rowToForm(row: S0ItemMasterRow): void {
 		ownerApplication: row.ownerApplication ?? '',
 		bomDesign: row.bomDesign ?? null,
 		routingDes: row.routingDes ?? null,
+		custSupplied: row.custSupplied,
+		drawingDesign: row.drawingDesign ?? null,
+		designOwner: row.designOwner ?? '',
 		isActive: row.isActive,
 		isConfirm: row.isConfirm,
 		createUser: row.createUser ?? '',

+ 323 - 0
Web/src/views/aidop/s0/sales/OrderReviewCycleList.vue

@@ -0,0 +1,323 @@
+<template>
+	<AidopDemoShell :title="pageTitle" subtitle="S0 / Sales / 订单评审周期">
+		<el-form :inline="true" :model="query" class="mb12" @submit.prevent>
+			<el-form-item label="公司">
+				<el-select v-model="query.companyRefId" clearable filterable placeholder="全部" style="width: 180px">
+					<el-option v-for="item in companyOptions" :key="item.id" :label="item.name || item.code || `${item.id}`" :value="item.id" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="工厂">
+				<el-select v-model="query.factoryRefId" clearable filterable placeholder="全部" style="width: 180px">
+					<el-option v-for="item in filteredFactoryOptions" :key="item.id" :label="item.name || item.code || `${item.id}`" :value="item.id" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="域编码">
+				<el-input v-model="query.domainCode" clearable style="width: 140px" />
+			</el-form-item>
+			<el-form-item label="启用">
+				<el-select v-model="query.isActive" clearable placeholder="全部" style="width: 120px">
+					<el-option label="是" :value="true" />
+					<el-option label="否" :value="false" />
+				</el-select>
+			</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="domainCode" label="工厂域" width="120" show-overflow-tooltip />
+			<el-table-column prop="stdHours" label="标准评审时长(h)" width="150" align="right" />
+			<el-table-column prop="remarks" label="备注" min-width="160" show-overflow-tooltip />
+			<el-table-column label="启用" width="80" align="center">
+				<template #default="{ row }">
+					<el-tag :type="row.isActive ? 'success' : 'info'" size="small">
+						{{ row.isActive ? '是' : '否' }}
+					</el-tag>
+				</template>
+			</el-table-column>
+			<el-table-column label="操作" width="200" fixed="right" align="center">
+				<template #default="{ row }">
+					<el-button link type="primary" @click="openEdit(row)">编辑</el-button>
+					<el-button link :type="row.isActive ? 'warning' : 'success'" @click="toggleActive(row)">
+						{{ row.isActive ? '禁用' : '启用' }}
+					</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="dialogTitle" width="560px" destroy-on-close @closed="resetForm">
+			<el-form ref="formRef" :model="form" :rules="rules" label-width="130px">
+				<el-row :gutter="16">
+					<el-col :span="12">
+						<el-form-item label="公司" prop="companyRefId">
+							<el-select v-model="form.companyRefId" filterable style="width: 100%">
+								<el-option v-for="item in companyOptions" :key="item.id" :label="item.name || item.code || `${item.id}`" :value="item.id" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="工厂" prop="factoryRefId">
+							<el-select v-model="form.factoryRefId" filterable style="width: 100%" @change="syncDomainCode">
+								<el-option v-for="item in formFactoryOptions" :key="item.id" :label="item.name || item.code || `${item.id}`" :value="item.id" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="标准评审时长(h)" prop="stdHours">
+							<el-input-number v-model="form.stdHours" :min="0" style="width: 100%" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="启用">
+							<el-switch v-model="form.isActive" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="24">
+						<el-form-item label="备注">
+							<el-input v-model="form.remarks" type="textarea" rows="3" />
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</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="aidopS0SalesOrderReviewCycle">
+import { computed, onMounted, reactive, ref, watch } from 'vue';
+import { useRoute } from 'vue-router';
+import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus';
+import AidopDemoShell from '../../components/AidopDemoShell.vue';
+import {
+	loadOrgList,
+	s0OrderReviewCyclesApi,
+	type OrgOption,
+	type S0OrderReviewCycleRow,
+	type S0OrderReviewCycleUpsert,
+} from '../api/s0SalesApi';
+
+const route = useRoute();
+const pageTitle = computed(() => (route.meta?.title as string) || '订单评审周期');
+
+const query = reactive({
+	domainCode: '',
+	companyRefId: undefined as number | undefined,
+	factoryRefId: undefined as number | undefined,
+	isActive: undefined as boolean | undefined,
+	page: 1,
+	pageSize: 20,
+});
+
+const loading = ref(false);
+const rows = ref<S0OrderReviewCycleRow[]>([]);
+const total = ref(0);
+const dialogVisible = ref(false);
+const dialogTitle = ref('新增');
+const editingId = ref<number | null>(null);
+const saving = ref(false);
+const formRef = ref<FormInstance>();
+
+const companyOptions = ref<OrgOption[]>([]);
+const factoryOptions = ref<OrgOption[]>([]);
+
+const filteredFactoryOptions = computed(() => {
+	if (!query.companyRefId) return factoryOptions.value;
+	return factoryOptions.value.filter((item) => item.pid === query.companyRefId);
+});
+
+const formFactoryOptions = computed(() => {
+	if (!form.companyRefId) return factoryOptions.value;
+	return factoryOptions.value.filter((item) => item.pid === form.companyRefId);
+});
+
+function emptyForm(): S0OrderReviewCycleUpsert {
+	return {
+		companyRefId: 0,
+		factoryRefId: 0,
+		domainCode: '',
+		stdHours: 0,
+		remarks: '',
+		isActive: true,
+		createUser: '',
+		updateUser: '',
+	};
+}
+
+const form = reactive<S0OrderReviewCycleUpsert>(emptyForm());
+
+const rules: FormRules = {
+	companyRefId: [{ required: true, message: '请选择公司', trigger: 'change' }],
+	factoryRefId: [{ required: true, message: '请选择工厂', trigger: 'change' }],
+	stdHours: [{ required: true, message: '请填写标准评审时长', trigger: 'blur' }],
+};
+
+function syncDomainCode() {
+	const f = factoryOptions.value.find((item) => item.id === form.factoryRefId);
+	form.domainCode = f?.code ?? '';
+}
+
+watch(
+	() => form.companyRefId,
+	() => {
+		if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) {
+			form.factoryRefId = 0;
+		}
+	}
+);
+
+watch(
+	() => query.companyRefId,
+	() => {
+		if (!filteredFactoryOptions.value.some((item) => item.id === query.factoryRefId)) {
+			query.factoryRefId = undefined;
+		}
+	}
+);
+
+async function loadOptions() {
+	const [companies, factories] = await Promise.all([loadOrgList('201'), loadOrgList('501')]);
+	companyOptions.value = companies;
+	factoryOptions.value = factories;
+}
+
+async function loadList() {
+	loading.value = true;
+	try {
+		const data = await s0OrderReviewCyclesApi.list({
+			page: query.page,
+			pageSize: query.pageSize,
+			domainCode: query.domainCode || undefined,
+			companyRefId: query.companyRefId || undefined,
+			factoryRefId: query.factoryRefId || undefined,
+			isActive: query.isActive,
+		});
+		rows.value = data.list;
+		total.value = data.total;
+	} catch {
+		rows.value = [];
+		total.value = 0;
+	} finally {
+		loading.value = false;
+	}
+}
+
+function resetQuery() {
+	Object.assign(query, {
+		domainCode: '',
+		companyRefId: undefined,
+		factoryRefId: undefined,
+		isActive: undefined,
+		page: 1,
+	});
+	void loadList();
+}
+
+function resetForm() {
+	editingId.value = null;
+	Object.assign(form, emptyForm());
+	formRef.value?.clearValidate();
+}
+
+function openCreate() {
+	resetForm();
+	dialogTitle.value = '新增订单评审周期';
+	dialogVisible.value = true;
+}
+
+function openEdit(row: S0OrderReviewCycleRow) {
+	resetForm();
+	editingId.value = row.id;
+	dialogTitle.value = `编辑:${row.domainCode || row.factoryRefId}`;
+	Object.assign(form, {
+		companyRefId: row.companyRefId,
+		factoryRefId: row.factoryRefId,
+		domainCode: row.domainCode ?? '',
+		stdHours: row.stdHours,
+		remarks: row.remarks ?? '',
+		isActive: row.isActive,
+		createUser: row.createUser ?? '',
+		updateUser: row.updateUser ?? '',
+	});
+	dialogVisible.value = true;
+}
+
+async function submitForm() {
+	await formRef.value?.validate();
+	saving.value = true;
+	try {
+		const payload: S0OrderReviewCycleUpsert = { ...form };
+		if (editingId.value) {
+			await s0OrderReviewCyclesApi.update(editingId.value, payload);
+			ElMessage.success('已保存');
+		} else {
+			await s0OrderReviewCyclesApi.create(payload);
+			ElMessage.success('已创建');
+		}
+		dialogVisible.value = false;
+		await loadList();
+	} finally {
+		saving.value = false;
+	}
+}
+
+function onDelete(row: S0OrderReviewCycleRow) {
+	ElMessageBox.confirm(`确定删除该工厂的订单评审周期配置?`, '确认', { type: 'warning' })
+		.then(async () => {
+			await s0OrderReviewCyclesApi.delete(row.id);
+			ElMessage.success('已删除');
+			await loadList();
+		})
+		.catch(() => {});
+}
+
+function toggleActive(row: S0OrderReviewCycleRow) {
+	const next = !row.isActive;
+	const actionText = next ? '启用' : '禁用';
+	ElMessageBox.confirm(`确定${actionText}该工厂的订单评审周期配置?`, '确认', { type: 'warning' })
+		.then(async () => {
+			await s0OrderReviewCyclesApi.toggleEnabled(row.id, { isActive: next });
+			ElMessage.success(`${actionText}成功`);
+			await loadList();
+		})
+		.catch(() => {});
+}
+
+onMounted(async () => {
+	await loadOptions();
+	await loadList();
+});
+</script>
+
+<style scoped lang="scss">
+@import '/@/views/aidop/styles/aidop-demo.scss';
+
+.mb12 {
+	margin-bottom: 12px;
+}
+
+.pager {
+	margin-top: 12px;
+	display: flex;
+	justify-content: flex-end;
+}
+</style>

+ 355 - 0
Web/src/views/aidop/s0/sales/ProductDesignCycleList.vue

@@ -0,0 +1,355 @@
+<template>
+	<AidopDemoShell :title="pageTitle" subtitle="S0 / Sales / 产品设计周期">
+		<el-form :inline="true" :model="query" class="mb12" @submit.prevent>
+			<el-form-item label="公司">
+				<el-select v-model="query.companyRefId" clearable filterable placeholder="全部" style="width: 180px">
+					<el-option v-for="item in companyOptions" :key="item.id" :label="item.name || item.code || `${item.id}`" :value="item.id" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="工厂">
+				<el-select v-model="query.factoryRefId" clearable filterable placeholder="全部" style="width: 180px">
+					<el-option v-for="item in filteredFactoryOptions" :key="item.id" :label="item.name || item.code || `${item.id}`" :value="item.id" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="物料类型">
+				<el-select v-model="query.itemType" clearable filterable placeholder="全部" style="width: 150px">
+					<el-option v-for="item in materialTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="物料属性">
+				<el-select v-model="query.ownerApplication" clearable placeholder="全部" style="width: 140px">
+					<el-option label="常规产品" value="常规产品" />
+					<el-option label="非标产品" value="非标产品" />
+				</el-select>
+			</el-form-item>
+			<el-form-item label="启用">
+				<el-select v-model="query.isActive" clearable placeholder="全部" style="width: 120px">
+					<el-option label="是" :value="true" />
+					<el-option label="否" :value="false" />
+				</el-select>
+			</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="itemType" label="物料类型" width="160" show-overflow-tooltip />
+			<el-table-column prop="ownerApplication" label="物料属性" width="120" show-overflow-tooltip />
+			<el-table-column prop="stdHours" label="标准设计时长(h)" width="150" align="right" />
+			<el-table-column label="启用" width="80" align="center">
+				<template #default="{ row }">
+					<el-tag :type="row.isActive ? 'success' : 'info'" size="small">
+						{{ row.isActive ? '是' : '否' }}
+					</el-tag>
+				</template>
+			</el-table-column>
+			<el-table-column label="操作" width="200" fixed="right" align="center">
+				<template #default="{ row }">
+					<el-button link type="primary" @click="openEdit(row)">编辑</el-button>
+					<el-button link :type="row.isActive ? 'warning' : 'success'" @click="toggleActive(row)">
+						{{ row.isActive ? '禁用' : '启用' }}
+					</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="dialogTitle" width="600px" destroy-on-close @closed="resetForm">
+			<el-form ref="formRef" :model="form" :rules="rules" label-width="130px">
+				<el-row :gutter="16">
+					<el-col :span="12">
+						<el-form-item label="公司" prop="companyRefId">
+							<el-select v-model="form.companyRefId" filterable style="width: 100%">
+								<el-option v-for="item in companyOptions" :key="item.id" :label="item.name || item.code || `${item.id}`" :value="item.id" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="工厂" prop="factoryRefId">
+							<el-select v-model="form.factoryRefId" filterable style="width: 100%" @change="syncDomainCode">
+								<el-option v-for="item in formFactoryOptions" :key="item.id" :label="item.name || item.code || `${item.id}`" :value="item.id" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="物料类型" prop="itemType">
+							<el-select v-model="form.itemType" filterable style="width: 100%">
+								<el-option v-for="item in materialTypeOptions" :key="item.value" :label="item.label" :value="item.value" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="物料属性" prop="ownerApplication">
+							<el-select v-model="form.ownerApplication" style="width: 100%">
+								<el-option label="常规产品" value="常规产品" />
+								<el-option label="非标产品" value="非标产品" />
+							</el-select>
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="标准设计时长(h)" prop="stdHours">
+							<el-input-number v-model="form.stdHours" :min="0" style="width: 100%" />
+						</el-form-item>
+					</el-col>
+					<el-col :span="12">
+						<el-form-item label="启用">
+							<el-switch v-model="form.isActive" />
+						</el-form-item>
+					</el-col>
+				</el-row>
+			</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="aidopS0SalesProductDesignCycle">
+import { computed, onMounted, reactive, ref, watch } from 'vue';
+import { useRoute } from 'vue-router';
+import { ElMessage, ElMessageBox, type FormInstance, type FormRules } from 'element-plus';
+import AidopDemoShell from '../../components/AidopDemoShell.vue';
+import {
+	loadDictOptions,
+	loadOrgList,
+	s0ProductDesignCyclesApi,
+	type OrgOption,
+	type OptionItem,
+	type S0ProductDesignCycleRow,
+	type S0ProductDesignCycleUpsert,
+} from '../api/s0SalesApi';
+
+const route = useRoute();
+const pageTitle = computed(() => (route.meta?.title as string) || '产品设计周期');
+
+const query = reactive({
+	companyRefId: undefined as number | undefined,
+	factoryRefId: undefined as number | undefined,
+	itemType: '',
+	ownerApplication: '',
+	isActive: undefined as boolean | undefined,
+	page: 1,
+	pageSize: 20,
+});
+
+const loading = ref(false);
+const rows = ref<S0ProductDesignCycleRow[]>([]);
+const total = ref(0);
+const dialogVisible = ref(false);
+const dialogTitle = ref('新增');
+const editingId = ref<number | null>(null);
+const saving = ref(false);
+const formRef = ref<FormInstance>();
+
+const companyOptions = ref<OrgOption[]>([]);
+const factoryOptions = ref<OrgOption[]>([]);
+const materialTypeOptions = ref<OptionItem[]>([]);
+
+const filteredFactoryOptions = computed(() => {
+	if (!query.companyRefId) return factoryOptions.value;
+	return factoryOptions.value.filter((item) => item.pid === query.companyRefId);
+});
+
+const formFactoryOptions = computed(() => {
+	if (!form.companyRefId) return factoryOptions.value;
+	return factoryOptions.value.filter((item) => item.pid === form.companyRefId);
+});
+
+function emptyForm(): S0ProductDesignCycleUpsert {
+	return {
+		companyRefId: 0,
+		factoryRefId: 0,
+		domainCode: '',
+		itemType: '',
+		ownerApplication: '',
+		stdHours: 0,
+		isActive: true,
+		createUser: '',
+		updateUser: '',
+	};
+}
+
+const form = reactive<S0ProductDesignCycleUpsert>(emptyForm());
+
+const rules: FormRules = {
+	companyRefId: [{ required: true, message: '请选择公司', trigger: 'change' }],
+	factoryRefId: [{ required: true, message: '请选择工厂', trigger: 'change' }],
+	itemType: [{ required: true, message: '请选择物料类型', trigger: 'change' }],
+	ownerApplication: [{ required: true, message: '请选择物料属性', trigger: 'change' }],
+};
+
+function syncDomainCode() {
+	const f = factoryOptions.value.find((item) => item.id === form.factoryRefId);
+	form.domainCode = f?.code ?? '';
+}
+
+watch(
+	() => form.companyRefId,
+	() => {
+		if (!formFactoryOptions.value.some((item) => item.id === form.factoryRefId)) {
+			form.factoryRefId = 0;
+		}
+	}
+);
+
+watch(
+	() => query.companyRefId,
+	() => {
+		if (!filteredFactoryOptions.value.some((item) => item.id === query.factoryRefId)) {
+			query.factoryRefId = undefined;
+		}
+	}
+);
+
+async function loadOptions() {
+	const [companies, factories, materialTypes] = await Promise.all([
+		loadOrgList('201'),
+		loadOrgList('501'),
+		loadDictOptions('s0_item_type'),
+	]);
+	companyOptions.value = companies;
+	factoryOptions.value = factories;
+	materialTypeOptions.value = materialTypes;
+}
+
+async function loadList() {
+	loading.value = true;
+	try {
+		const data = await s0ProductDesignCyclesApi.list({
+			page: query.page,
+			pageSize: query.pageSize,
+			companyRefId: query.companyRefId || undefined,
+			factoryRefId: query.factoryRefId || undefined,
+			itemType: query.itemType || undefined,
+			ownerApplication: query.ownerApplication || undefined,
+			isActive: query.isActive,
+		});
+		rows.value = data.list;
+		total.value = data.total;
+	} catch {
+		rows.value = [];
+		total.value = 0;
+	} finally {
+		loading.value = false;
+	}
+}
+
+function resetQuery() {
+	Object.assign(query, {
+		companyRefId: undefined,
+		factoryRefId: undefined,
+		itemType: '',
+		ownerApplication: '',
+		isActive: undefined,
+		page: 1,
+	});
+	void loadList();
+}
+
+function resetForm() {
+	editingId.value = null;
+	Object.assign(form, emptyForm());
+	formRef.value?.clearValidate();
+}
+
+function openCreate() {
+	resetForm();
+	dialogTitle.value = '新增产品设计周期';
+	dialogVisible.value = true;
+}
+
+function openEdit(row: S0ProductDesignCycleRow) {
+	resetForm();
+	editingId.value = row.id;
+	dialogTitle.value = `编辑:${row.itemType} / ${row.ownerApplication}`;
+	Object.assign(form, {
+		companyRefId: row.companyRefId,
+		factoryRefId: row.factoryRefId,
+		domainCode: row.domainCode ?? '',
+		itemType: row.itemType,
+		ownerApplication: row.ownerApplication,
+		stdHours: row.stdHours,
+		isActive: row.isActive,
+		createUser: row.createUser ?? '',
+		updateUser: row.updateUser ?? '',
+	});
+	dialogVisible.value = true;
+}
+
+async function submitForm() {
+	await formRef.value?.validate();
+	saving.value = true;
+	try {
+		const payload: S0ProductDesignCycleUpsert = { ...form };
+		if (editingId.value) {
+			await s0ProductDesignCyclesApi.update(editingId.value, payload);
+			ElMessage.success('已保存');
+		} else {
+			await s0ProductDesignCyclesApi.create(payload);
+			ElMessage.success('已创建');
+		}
+		dialogVisible.value = false;
+		await loadList();
+	} finally {
+		saving.value = false;
+	}
+}
+
+function onDelete(row: S0ProductDesignCycleRow) {
+	ElMessageBox.confirm(`确定删除「${row.itemType} / ${row.ownerApplication}」?`, '确认', { type: 'warning' })
+		.then(async () => {
+			await s0ProductDesignCyclesApi.delete(row.id);
+			ElMessage.success('已删除');
+			await loadList();
+		})
+		.catch(() => {});
+}
+
+function toggleActive(row: S0ProductDesignCycleRow) {
+	const next = !row.isActive;
+	const actionText = next ? '启用' : '禁用';
+	ElMessageBox.confirm(`确定${actionText}「${row.itemType} / ${row.ownerApplication}」?`, '确认', { type: 'warning' })
+		.then(async () => {
+			await s0ProductDesignCyclesApi.toggleEnabled(row.id, { isActive: next });
+			ElMessage.success(`${actionText}成功`);
+			await loadList();
+		})
+		.catch(() => {});
+}
+
+onMounted(async () => {
+	await loadOptions();
+	await loadList();
+});
+</script>
+
+<style scoped lang="scss">
+@import '/@/views/aidop/styles/aidop-demo.scss';
+
+.mb12 {
+	margin-bottom: 12px;
+}
+
+.pager {
+	margin-top: 12px;
+	display: flex;
+	justify-content: flex-end;
+}
+</style>

+ 42 - 0
Web/src/views/aidop/s8/api/s8MonitoringApi.ts

@@ -0,0 +1,42 @@
+import service from '/@/utils/request';
+
+function unwrap<T>(res: { data: T }): T {
+	return res.data;
+}
+
+export interface S8ModuleSummaryItem {
+	moduleCode: string;
+	moduleLabel: string;
+	sceneCode: string;
+	sceneLabel: string;
+	total: number;
+	red: number;
+	yellow: number;
+	green: number;
+	timeout: number;
+}
+
+export interface S8MonitoringSummary {
+	total: number;
+	red: number;
+	yellow: number;
+	green: number;
+	timeout: number;
+	byModule: S8ModuleSummaryItem[];
+}
+
+export interface S8MonitoringSummaryQuery {
+	tenantId?: number;
+	factoryId?: number;
+	sceneCode?: string;
+	moduleCode?: string;
+	bizDateFrom?: string;
+	bizDateTo?: string;
+}
+
+export const s8MonitoringApi = {
+	summary: (params: S8MonitoringSummaryQuery = {}) =>
+		service
+			.get<S8MonitoringSummary>('/api/aidop/s8/monitoring/summary', { params })
+			.then(unwrap),
+};

+ 9 - 0
Web/src/views/aidop/s8/monitoring/S8MonitoringDeliveryPage.vue

@@ -0,0 +1,9 @@
+<template>
+	<AidopDemoShell title="交付异常大屏" subtitle="S8 · 异常监控 · 交付专题(页面占位,待接入 S1/S7 等交付域数据)">
+		<el-empty description="功能开发中" />
+	</AidopDemoShell>
+</template>
+
+<script setup lang="ts" name="aidopS8MonitoringDelivery">
+import AidopDemoShell from '../../components/AidopDemoShell.vue';
+</script>

+ 121 - 0
Web/src/views/aidop/s8/monitoring/S8MonitoringOverviewPage.vue

@@ -0,0 +1,121 @@
+<template>
+	<div class="s8-overview">
+		<div class="s8-overview__bg" aria-hidden="true" />
+
+		<div class="s8-overview__body">
+			<!-- 顶部汇总栏 -->
+			<AnomalySummaryBar
+				title="S8 · 异常监控 · 综合全景"
+				subtitle="实时监控 S1-S7 全链路异常,下钻查看订单明细"
+				:summary="summary"
+			/>
+
+			<!-- 模块汇总表 -->
+			<AnomalyModuleTable
+				:modules="summary.byModule"
+				:active-drill-code="activeDrillCode"
+				@drill="onDrill"
+			/>
+
+			<!-- 下钻面板 -->
+			<Transition name="adp-slide">
+				<AnomalyDrillPanel
+					v-if="activeDrillCode && activeDrillItem"
+					:key="activeDrillCode"
+					:module-code="activeDrillCode"
+					:module-label="activeDrillItem.moduleLabel"
+					:scene-code="activeDrillItem.sceneCode"
+					:tenant-id="1"
+					:factory-id="1"
+				/>
+			</Transition>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="aidopS8MonitoringOverview">
+import { computed, onMounted, reactive, ref } from 'vue';
+import { s8MonitoringApi, type S8MonitoringSummary } from '../api/s8MonitoringApi';
+import AnomalySummaryBar from './components/AnomalySummaryBar.vue';
+import AnomalyModuleTable from './components/AnomalyModuleTable.vue';
+import AnomalyDrillPanel from './components/AnomalyDrillPanel.vue';
+
+const summary = reactive<S8MonitoringSummary>({
+	total: 0,
+	red: 0,
+	yellow: 0,
+	green: 0,
+	timeout: 0,
+	byModule: [],
+});
+
+const activeDrillCode = ref<string | null>(null);
+
+const activeDrillItem = computed(() =>
+	summary.byModule.find((m) => m.moduleCode === activeDrillCode.value) ?? null
+);
+
+function onDrill(moduleCode: string) {
+	activeDrillCode.value = activeDrillCode.value === moduleCode ? null : moduleCode;
+}
+
+onMounted(async () => {
+	try {
+		const data = await s8MonitoringApi.summary({ tenantId: 1, factoryId: 1 });
+		Object.assign(summary, data);
+	} catch {
+		// 接口未就绪时页面静默降级
+	}
+});
+</script>
+
+<style scoped>
+.s8-overview {
+	position: relative;
+	min-height: calc(100vh - 72px);
+	padding: 16px 20px 28px;
+	color: #e2e8f0;
+	overflow-x: auto;
+	box-sizing: border-box;
+}
+
+.s8-overview__bg {
+	position: absolute;
+	inset: 0;
+	z-index: 0;
+	background:
+		radial-gradient(ellipse 120% 80% at 50% -20%, rgba(56, 189, 248, 0.12), transparent 50%),
+		linear-gradient(180deg, #0b1220 0%, #0f172a 45%, #0c1929 100%);
+	pointer-events: none;
+}
+
+.s8-overview__bg::after {
+	content: '';
+	position: absolute;
+	inset: 0;
+	opacity: 0.35;
+	background-image:
+		linear-gradient(rgba(56, 189, 248, 0.06) 1px, transparent 1px),
+		linear-gradient(90deg, rgba(56, 189, 248, 0.06) 1px, transparent 1px);
+	background-size: 28px 28px;
+}
+
+.s8-overview__body {
+	position: relative;
+	z-index: 1;
+	display: flex;
+	flex-direction: column;
+	gap: 16px;
+}
+
+/* 下钻展开动画 */
+.adp-slide-enter-active,
+.adp-slide-leave-active {
+	transition: opacity 0.2s ease, transform 0.2s ease;
+}
+.adp-slide-enter-from,
+.adp-slide-leave-to {
+	opacity: 0;
+	transform: translateY(-8px);
+}
+</style>

+ 9 - 0
Web/src/views/aidop/s8/monitoring/S8MonitoringProductionPage.vue

@@ -0,0 +1,9 @@
+<template>
+	<AidopDemoShell title="生产异常大屏" subtitle="S8 · 异常监控 · 生产专题(页面占位,待接入 S2/S6 等生产域数据)">
+		<el-empty description="功能开发中" />
+	</AidopDemoShell>
+</template>
+
+<script setup lang="ts" name="aidopS8MonitoringProduction">
+import AidopDemoShell from '../../components/AidopDemoShell.vue';
+</script>

+ 9 - 0
Web/src/views/aidop/s8/monitoring/S8MonitoringSupplyPage.vue

@@ -0,0 +1,9 @@
+<template>
+	<AidopDemoShell title="供应异常大屏" subtitle="S8 · 异常监控 · 供应专题(页面占位,待接入 S3–S5 等供应域数据)">
+		<el-empty description="功能开发中" />
+	</AidopDemoShell>
+</template>
+
+<script setup lang="ts" name="aidopS8MonitoringSupply">
+import AidopDemoShell from '../../components/AidopDemoShell.vue';
+</script>

+ 248 - 0
Web/src/views/aidop/s8/monitoring/components/AnomalyDrillPanel.vue

@@ -0,0 +1,248 @@
+<template>
+	<div class="adp">
+		<!-- 连接箭头 -->
+		<div class="adp__arrow-row">
+			<div class="adp__bidir-arrow" aria-hidden="true">
+				<span class="adp__tri adp__tri--up" />
+				<span class="adp__line" />
+				<span class="adp__tri adp__tri--down" />
+			</div>
+		</div>
+
+		<!-- 面板主体 -->
+		<div class="adp__panel">
+			<div class="adp__caption">
+				{{ moduleLabel }} — 异常明细
+			</div>
+
+			<!-- 筛选栏 -->
+			<el-form :inline="true" :model="query" class="adp__form" @submit.prevent>
+				<el-form-item label="状态">
+					<el-select v-model="query.status" clearable placeholder="全部" style="width: 120px" @change="loadList">
+						<el-option v-for="s in STATUS_OPTS" :key="s.value" :label="s.label" :value="s.value" />
+					</el-select>
+				</el-form-item>
+				<el-form-item label="严重度">
+					<el-select v-model="query.severity" clearable placeholder="全部" style="width: 110px" @change="loadList">
+						<el-option v-for="s in SEVERITY_OPTS" :key="s.value" :label="s.label" :value="s.value" />
+					</el-select>
+				</el-form-item>
+				<el-form-item>
+					<el-checkbox v-model="query.timeoutOnly" label="仅超时" @change="loadList" />
+				</el-form-item>
+				<el-form-item>
+					<el-button type="primary" size="small" @click="loadList">刷新</el-button>
+				</el-form-item>
+			</el-form>
+
+			<el-table
+				:data="rows"
+				v-loading="loading"
+				border
+				stripe
+				size="small"
+				class="adp__table"
+				@row-click="onRowClick"
+			>
+				<el-table-column prop="exceptionCode" label="编号" width="150" show-overflow-tooltip />
+				<el-table-column prop="title" label="标题" min-width="200" show-overflow-tooltip />
+				<el-table-column prop="severityLabel" label="严重度" width="80">
+					<template #default="{ row }">
+						<i
+							class="sd-dot"
+							:class="severityDot(row.severity)"
+							style="margin-right: 4px"
+							aria-hidden="true"
+						/>{{ row.severityLabel }}
+					</template>
+				</el-table-column>
+				<el-table-column prop="statusLabel" label="状态" width="90" />
+				<el-table-column prop="timeoutFlag" label="超时" width="64" align="center">
+					<template #default="{ row }">
+						<el-tag :type="row.timeoutFlag ? 'danger' : 'info'" size="small">
+							{{ row.timeoutFlag ? '是' : '否' }}
+						</el-tag>
+					</template>
+				</el-table-column>
+				<el-table-column prop="createdAt" label="创建时间" width="155" />
+				<el-table-column prop="assigneeName" label="负责人" width="90" show-overflow-tooltip />
+			</el-table>
+
+			<div class="adp__pager">
+				<el-pagination
+					v-model:current-page="query.page"
+					v-model:page-size="query.pageSize"
+					:total="total"
+					:page-sizes="[10, 20, 50]"
+					layout="total, sizes, prev, pager, next"
+					small
+					@current-change="loadList"
+					@size-change="loadList"
+				/>
+			</div>
+		</div>
+	</div>
+</template>
+
+<script setup lang="ts" name="AnomalyDrillPanel">
+import { reactive, ref, watch } from 'vue';
+import { useRouter } from 'vue-router';
+import { s8ExceptionApi, type S8ExceptionRow } from '../../api/s8ExceptionApi';
+
+const props = defineProps<{
+	moduleCode: string;
+	moduleLabel: string;
+	sceneCode?: string;
+	tenantId?: number;
+	factoryId?: number;
+}>();
+
+const router = useRouter();
+const loading = ref(false);
+const rows = ref<S8ExceptionRow[]>([]);
+const total = ref(0);
+
+const STATUS_OPTS = [
+	{ value: 'NEW', label: '新建' },
+	{ value: 'ASSIGNED', label: '已指派' },
+	{ value: 'IN_PROGRESS', label: '处理中' },
+	{ value: 'RESOLVED', label: '已处理' },
+	{ value: 'CLOSED', label: '已关闭' },
+];
+const SEVERITY_OPTS = [
+	{ value: 'CRITICAL', label: '紧急' },
+	{ value: 'HIGH', label: '高' },
+	{ value: 'MEDIUM', label: '中' },
+	{ value: 'LOW', label: '低' },
+];
+
+const query = reactive({
+	status: '',
+	severity: '',
+	timeoutOnly: false,
+	page: 1,
+	pageSize: 10,
+});
+
+async function loadList() {
+	loading.value = true;
+	try {
+		const res = await s8ExceptionApi.list({
+			tenantId: props.tenantId ?? 1,
+			factoryId: props.factoryId ?? 1,
+			moduleCode: props.moduleCode,
+			sceneCode: props.sceneCode || undefined,
+			status: query.status || undefined,
+			severity: query.severity || undefined,
+			timeoutFlag: query.timeoutOnly ? true : undefined,
+			page: query.page,
+			pageSize: query.pageSize,
+		});
+		rows.value = res.list;
+		total.value = res.total;
+	} finally {
+		loading.value = false;
+	}
+}
+
+function onRowClick(row: S8ExceptionRow) {
+	router.push(`/aidop/s8/exceptions/${row.id}`);
+}
+
+function severityDot(severity: string) {
+	if (severity === 'CRITICAL' || severity === 'HIGH') return 'sd-dot--red';
+	if (severity === 'MEDIUM') return 'sd-dot--yellow';
+	return 'sd-dot--green';
+}
+
+watch(() => props.moduleCode, () => {
+	query.page = 1;
+	void loadList();
+}, { immediate: true });
+</script>
+
+<style scoped>
+.adp {
+	margin-top: 4px;
+	position: relative;
+	z-index: 1;
+}
+
+.adp__arrow-row {
+	display: flex;
+	justify-content: center;
+	padding: 4px 0 2px;
+}
+
+.adp__bidir-arrow {
+	display: flex;
+	flex-direction: column;
+	align-items: center;
+	gap: 0;
+}
+.adp__line {
+	width: 2px;
+	height: 16px;
+	background: linear-gradient(180deg, #38bdf8, #0ea5e9);
+	border-radius: 1px;
+}
+.adp__tri--up {
+	width: 0; height: 0;
+	border-left: 6px solid transparent;
+	border-right: 6px solid transparent;
+	border-bottom: 8px solid #38bdf8;
+}
+.adp__tri--down {
+	width: 0; height: 0;
+	border-left: 6px solid transparent;
+	border-right: 6px solid transparent;
+	border-top: 8px solid #38bdf8;
+}
+
+.adp__panel {
+	border-top: 1px solid rgba(56, 189, 248, 0.18);
+	padding: 12px 16px 16px;
+	background: rgba(15, 23, 42, 0.35);
+	border-radius: 0 0 12px 12px;
+}
+
+.adp__caption {
+	font-size: 13px;
+	font-weight: 600;
+	color: #7dd3fc;
+	margin-bottom: 10px;
+	letter-spacing: 0.02em;
+}
+
+.adp__form {
+	margin-bottom: 10px;
+}
+
+.adp__table {
+	--el-table-bg-color: transparent;
+	--el-table-header-bg-color: rgba(14, 116, 144, 0.35);
+	--el-table-row-hover-bg-color: rgba(56, 189, 248, 0.08);
+	--el-table-border-color: rgba(100, 116, 139, 0.25);
+	--el-table-text-color: #e2e8f0;
+	--el-table-header-text-color: #e0f2fe;
+	cursor: pointer;
+}
+
+.adp__pager {
+	margin-top: 10px;
+	display: flex;
+	justify-content: flex-end;
+}
+
+.sd-dot {
+	display: inline-block;
+	width: 9px;
+	height: 9px;
+	border-radius: 50%;
+	flex-shrink: 0;
+	vertical-align: middle;
+}
+.sd-dot--green  { background: #22c55e; }
+.sd-dot--yellow { background: #eab308; }
+.sd-dot--red    { background: #ef4444; }
+</style>

+ 198 - 0
Web/src/views/aidop/s8/monitoring/components/AnomalyModuleTable.vue

@@ -0,0 +1,198 @@
+<template>
+	<div class="amt__wrap">
+		<table class="amt__table">
+			<colgroup>
+				<col class="amt__col-label" />
+				<col v-for="m in modules" :key="'cg-' + m.moduleCode" class="amt__col-module" />
+			</colgroup>
+			<thead>
+				<tr>
+					<th class="amt__th-corner">维度 / 模块</th>
+					<th v-for="m in modules" :key="'h-' + m.moduleCode" class="amt__th-module">
+						<div class="amt__module-head">
+							<i class="sd-dot" :class="severityDotClass(m)" aria-hidden="true" />
+							<span>{{ m.moduleLabel }}</span>
+						</div>
+						<div class="amt__scene-tag">{{ m.sceneLabel }}</div>
+					</th>
+				</tr>
+			</thead>
+			<tbody>
+				<tr>
+					<th scope="row">异常总数</th>
+					<td v-for="m in modules" :key="'t-' + m.moduleCode" class="amt__td-num">
+						{{ m.total }}
+					</td>
+				</tr>
+				<tr>
+					<th scope="row">紧急/高</th>
+					<td
+						v-for="m in modules"
+						:key="'r-' + m.moduleCode"
+						class="amt__td-num"
+						:class="{ 'amt__td--red': m.red > 0 }"
+					>
+						{{ m.red }}
+					</td>
+				</tr>
+				<tr>
+					<th scope="row">中</th>
+					<td
+						v-for="m in modules"
+						:key="'y-' + m.moduleCode"
+						class="amt__td-num"
+						:class="{ 'amt__td--yellow': m.yellow > 0 }"
+					>
+						{{ m.yellow }}
+					</td>
+				</tr>
+				<tr>
+					<th scope="row">低</th>
+					<td v-for="m in modules" :key="'g-' + m.moduleCode" class="amt__td-num">
+						{{ m.green }}
+					</td>
+				</tr>
+				<tr>
+					<th scope="row">超时未关</th>
+					<td
+						v-for="m in modules"
+						:key="'to-' + m.moduleCode"
+						class="amt__td-num"
+						:class="{ 'amt__td--timeout': m.timeout > 0 }"
+					>
+						{{ m.timeout }}
+					</td>
+				</tr>
+				<tr>
+					<th scope="row">操作</th>
+					<td v-for="m in modules" :key="'op-' + m.moduleCode" class="amt__td-op">
+						<el-button
+							type="primary"
+							link
+							size="small"
+							:class="{ 'amt__drill-btn--active': activeDrillCode === m.moduleCode }"
+							@click="emit('drill', m.moduleCode)"
+						>
+							{{ activeDrillCode === m.moduleCode ? '收起' : '下钻' }}
+						</el-button>
+					</td>
+				</tr>
+			</tbody>
+		</table>
+	</div>
+</template>
+
+<script setup lang="ts" name="AnomalyModuleTable">
+import type { S8ModuleSummaryItem } from '../../api/s8MonitoringApi';
+
+const props = defineProps<{
+	modules: S8ModuleSummaryItem[];
+	activeDrillCode?: string | null;
+}>();
+
+const emit = defineEmits<{
+	(e: 'drill', moduleCode: string): void;
+}>();
+
+function severityDotClass(m: S8ModuleSummaryItem) {
+	if (m.red > 0) return 'sd-dot--red';
+	if (m.yellow > 0) return 'sd-dot--yellow';
+	return 'sd-dot--green';
+}
+</script>
+
+<style scoped>
+.amt__wrap {
+	width: 100%;
+	overflow-x: auto;
+	border: 1px solid rgba(56, 189, 248, 0.2);
+	border-radius: 12px;
+	background: rgba(15, 23, 42, 0.45);
+	position: relative;
+	z-index: 1;
+}
+
+.amt__table {
+	width: 100%;
+	min-width: 560px;
+	table-layout: fixed;
+	border-collapse: collapse;
+	font-size: 12px;
+}
+
+.amt__table th,
+.amt__table td {
+	box-sizing: border-box;
+	padding: 11px 10px;
+	text-align: center;
+	border: 1px solid rgba(100, 116, 139, 0.25);
+}
+
+.amt__table thead th {
+	background: linear-gradient(180deg, rgba(14, 116, 144, 0.45), rgba(15, 23, 42, 0.9));
+	color: #e0f2fe;
+	font-weight: 600;
+}
+
+.amt__table tbody th[scope='row'] {
+	background: rgba(30, 41, 59, 0.85);
+	color: #94a3b8;
+	font-weight: 600;
+	text-align: left;
+	padding-left: 12px;
+	white-space: nowrap;
+	width: 88px;
+	min-width: 88px;
+	max-width: 88px;
+}
+
+.amt__table tbody td {
+	background: rgba(51, 65, 85, 0.35);
+	color: #e2e8f0;
+}
+
+.amt__col-label { width: 88px; }
+.amt__col-module { width: auto; }
+
+.amt__th-corner {
+	text-align: left;
+	padding-left: 12px;
+}
+
+.amt__th-module { vertical-align: middle; }
+
+.amt__module-head {
+	display: flex;
+	align-items: center;
+	justify-content: center;
+	gap: 6px;
+	font-size: 13px;
+	font-weight: 700;
+}
+
+.amt__scene-tag {
+	margin-top: 4px;
+	font-size: 10px;
+	color: #7dd3fc;
+	font-weight: 400;
+}
+
+.amt__td-num { font-size: 15px; font-weight: 700; }
+.amt__td--red    { color: #f87171; }
+.amt__td--yellow { color: #fbbf24; }
+.amt__td--timeout { color: #fb923c; }
+.amt__td-op { vertical-align: middle; }
+
+.amt__drill-btn--active { color: #7dd3fc !important; }
+
+.sd-dot {
+	display: inline-block;
+	width: 9px;
+	height: 9px;
+	border-radius: 50%;
+	flex-shrink: 0;
+}
+.sd-dot--green  { background: #22c55e; box-shadow: 0 0 6px rgba(34, 197, 94, 0.65); }
+.sd-dot--yellow { background: #eab308; box-shadow: 0 0 6px rgba(234, 179, 8, 0.55); }
+.sd-dot--red    { background: #ef4444; box-shadow: 0 0 6px rgba(239, 68, 68, 0.6); }
+</style>

+ 152 - 0
Web/src/views/aidop/s8/monitoring/components/AnomalySummaryBar.vue

@@ -0,0 +1,152 @@
+<template>
+	<header class="asb">
+		<div class="asb__left">
+			<div class="asb__banner">{{ title }}</div>
+			<p v-if="subtitle" class="asb__subtitle">{{ subtitle }}</p>
+		</div>
+
+		<div class="asb__center">
+			<div class="asb__badge asb__badge--total">
+				<span class="asb__badge-num">{{ summary.total }}</span>
+				<span class="asb__badge-label">全部</span>
+			</div>
+			<div class="asb__badge asb__badge--red">
+				<i class="sd-dot sd-dot--red" aria-hidden="true" />
+				<span class="asb__badge-num">{{ summary.red }}</span>
+				<span class="asb__badge-label">紧急/高</span>
+			</div>
+			<div class="asb__badge asb__badge--yellow">
+				<i class="sd-dot sd-dot--yellow" aria-hidden="true" />
+				<span class="asb__badge-num">{{ summary.yellow }}</span>
+				<span class="asb__badge-label">中</span>
+			</div>
+			<div class="asb__badge asb__badge--green">
+				<i class="sd-dot sd-dot--green" aria-hidden="true" />
+				<span class="asb__badge-num">{{ summary.green }}</span>
+				<span class="asb__badge-label">低</span>
+			</div>
+			<div class="asb__badge asb__badge--timeout">
+				<span class="asb__timeout-icon">⏱</span>
+				<span class="asb__badge-num">{{ summary.timeout }}</span>
+				<span class="asb__badge-label">超时未关</span>
+			</div>
+		</div>
+
+		<div class="asb__legend" role="list">
+			<span class="asb__legend-item" role="listitem">
+				<i class="sd-dot sd-dot--red" aria-hidden="true" />紧急/高
+			</span>
+			<span class="asb__legend-item" role="listitem">
+				<i class="sd-dot sd-dot--yellow" aria-hidden="true" />中
+			</span>
+			<span class="asb__legend-item" role="listitem">
+				<i class="sd-dot sd-dot--green" aria-hidden="true" />低
+			</span>
+		</div>
+	</header>
+</template>
+
+<script setup lang="ts" name="AnomalySummaryBar">
+import type { S8MonitoringSummary } from '../../api/s8MonitoringApi';
+
+defineProps<{
+	title: string;
+	subtitle?: string;
+	summary: Pick<S8MonitoringSummary, 'total' | 'red' | 'yellow' | 'green' | 'timeout'>;
+}>();
+</script>
+
+<style scoped>
+.asb {
+	display: flex;
+	flex-wrap: wrap;
+	align-items: flex-start;
+	justify-content: space-between;
+	gap: 16px;
+	margin-bottom: 20px;
+	position: relative;
+	z-index: 1;
+}
+
+.asb__banner {
+	display: inline-block;
+	padding: 10px 28px;
+	font-size: 18px;
+	font-weight: 700;
+	letter-spacing: 0.06em;
+	color: #f0f9ff;
+	background: linear-gradient(180deg, #1e3a5f 0%, #0f2942 100%);
+	border: 1px solid rgba(56, 189, 248, 0.35);
+	border-radius: 6px;
+	box-shadow: 0 4px 24px rgba(0, 0, 0, 0.35);
+}
+
+.asb__subtitle {
+	margin: 8px 0 0;
+	font-size: 12px;
+	color: #94a3b8;
+}
+
+.asb__center {
+	display: flex;
+	flex-wrap: wrap;
+	gap: 10px;
+	align-items: center;
+}
+
+.asb__badge {
+	display: flex;
+	align-items: center;
+	gap: 6px;
+	padding: 8px 16px;
+	border-radius: 8px;
+	background: rgba(15, 23, 42, 0.65);
+	border: 1px solid rgba(100, 116, 139, 0.25);
+	font-size: 12px;
+	color: #cbd5e1;
+}
+
+.asb__badge--red   { border-color: rgba(239, 68, 68, 0.4); }
+.asb__badge--yellow{ border-color: rgba(234, 179, 8, 0.4); }
+.asb__badge--green { border-color: rgba(34, 197, 94, 0.35); }
+.asb__badge--timeout { border-color: rgba(148, 163, 184, 0.3); }
+
+.asb__badge-num {
+	font-size: 20px;
+	font-weight: 700;
+	color: #f8fafc;
+	line-height: 1;
+}
+
+.asb__timeout-icon { font-size: 14px; }
+
+.asb__legend {
+	display: flex;
+	flex-wrap: wrap;
+	gap: 14px 20px;
+	padding: 10px 14px;
+	background: rgba(15, 23, 42, 0.65);
+	border: 1px solid rgba(148, 163, 184, 0.2);
+	border-radius: 8px;
+	font-size: 11px;
+	color: #cbd5e1;
+}
+
+.asb__legend-item {
+	display: inline-flex;
+	align-items: center;
+	gap: 6px;
+}
+
+/* RAG 点(与智慧诊断共享语义,此处本地定义以避免跨页样式耦合) */
+.sd-dot {
+	display: inline-block;
+	width: 10px;
+	height: 10px;
+	border-radius: 50%;
+	flex-shrink: 0;
+}
+.sd-dot--green  { background: #22c55e; box-shadow: 0 0 8px rgba(34, 197, 94, 0.65); }
+.sd-dot--yellow { background: #eab308; box-shadow: 0 0 8px rgba(234, 179, 8, 0.55); }
+.sd-dot--red    { background: #ef4444; box-shadow: 0 0 8px rgba(239, 68, 68, 0.6); }
+</style>

+ 18 - 0
server/Admin.NET.Core/SqlSugar/SqlSugarSetup.cs

@@ -445,6 +445,7 @@ public static class SqlSugarSetup
         {
             Log.Information($"初始化表结构 {config.DbType} - {config.ConfigId}");
             var entityTypes = GetEntityTypesForInit(config);
+            RunPreInitMigrations(dbProvider, entityTypes);
             InitializeTables(dbProvider, entityTypes, config);
         }
 
@@ -470,6 +471,23 @@ public static class SqlSugarSetup
             .ToList();
     }
 
+    /// <summary>
+    /// 在全局 InitTables 之前执行插件级预迁移,避免 CodeFirst 先补出新列。
+    /// </summary>
+    private static void RunPreInitMigrations(SqlSugarScopeProvider dbProvider, List<Type> entityTypes)
+    {
+        if (!entityTypes.Any(t => t.FullName?.Contains(".Entity.S0.") == true))
+            return;
+
+        var migrationType = App.EffectiveTypes.FirstOrDefault(t =>
+            t.FullName == "Admin.NET.Plugin.AiDOP.Infrastructure.AidopS0ExcelColumnMigration");
+        var migrateMethod = migrationType?.GetMethod("Migrate", BindingFlags.Public | BindingFlags.Static);
+        if (migrateMethod == null)
+            return;
+
+        migrateMethod.Invoke(null, [dbProvider]);
+    }
+
     /// <summary>
     /// 判断实体是否属于当前配置
     /// </summary>

+ 107 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/S0/Sales/AdoS0ContractReviewCyclesController.cs

@@ -0,0 +1,107 @@
+using Admin.NET.Plugin.AiDOP.Dto.S0.Sales;
+using Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
+using Admin.NET.Plugin.AiDOP.Infrastructure;
+
+namespace Admin.NET.Plugin.AiDOP.Controllers.S0.Sales;
+
+[ApiController]
+[Route("api/s0/sales/contract-review-cycles")]
+[AllowAnonymous]
+[NonUnify]
+public class AdoS0ContractReviewCyclesController : ControllerBase
+{
+    private readonly SqlSugarRepository<AdoS0ContractReviewCycle> _rep;
+
+    public AdoS0ContractReviewCyclesController(SqlSugarRepository<AdoS0ContractReviewCycle> rep)
+        => _rep = rep;
+
+    [HttpGet]
+    public async Task<IActionResult> GetPagedAsync([FromQuery] AdoS0ContractReviewCycleQueryDto q)
+    {
+        (q.Page, q.PageSize) = PagingGuard.Normalize(q.Page, q.PageSize);
+
+        var query = _rep.AsQueryable()
+            .WhereIF(q.CompanyRefId.HasValue, x => x.CompanyRefId == q.CompanyRefId!.Value)
+            .WhereIF(q.FactoryRefId.HasValue, x => x.FactoryRefId == q.FactoryRefId!.Value)
+            .WhereIF(!string.IsNullOrWhiteSpace(q.DomainCode), x => x.DomainCode == q.DomainCode)
+            .WhereIF(q.IsActive.HasValue, x => x.IsActive == q.IsActive!.Value);
+
+        var total = await query.CountAsync();
+        var list = await query.OrderBy(x => x.OrderNo).Skip((q.Page - 1) * q.PageSize).Take(q.PageSize).ToListAsync();
+        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 == null ? NotFound() : Ok(item);
+    }
+
+    [HttpPost]
+    public async Task<IActionResult> CreateAsync([FromBody] AdoS0ContractReviewCycleUpsertDto dto)
+    {
+        if (await _rep.IsAnyAsync(x => x.FactoryRefId == dto.FactoryRefId && x.StageCode == dto.StageCode))
+            return AdoS0ApiErrors.Conflict(AdoS0ErrorCodes.DuplicateCode, "该工厂下此阶段已存在");
+
+        var entity = new AdoS0ContractReviewCycle
+        {
+            CompanyRefId = dto.CompanyRefId,
+            FactoryRefId = dto.FactoryRefId,
+            DomainCode = dto.DomainCode,
+            StageCode = dto.StageCode,
+            StageName = dto.StageName,
+            StdHours = dto.StdHours,
+            OrderNo = dto.OrderNo,
+            IsActive = dto.IsActive,
+            CreateUser = dto.CreateUser,
+            CreateTime = DateTime.Now,
+        };
+        await _rep.AsInsertable(entity).ExecuteReturnEntityAsync();
+        return Ok(entity);
+    }
+
+    [HttpPut("{id:long}")]
+    public async Task<IActionResult> UpdateAsync(long id, [FromBody] AdoS0ContractReviewCycleUpsertDto dto)
+    {
+        var entity = await _rep.GetByIdAsync(id);
+        if (entity == null) return NotFound();
+
+        if (await _rep.IsAnyAsync(x => x.Id != id && x.FactoryRefId == dto.FactoryRefId && x.StageCode == dto.StageCode))
+            return AdoS0ApiErrors.Conflict(AdoS0ErrorCodes.DuplicateCode, "该工厂下此阶段已存在");
+
+        entity.CompanyRefId = dto.CompanyRefId;
+        entity.FactoryRefId = dto.FactoryRefId;
+        entity.DomainCode = dto.DomainCode;
+        entity.StageCode = dto.StageCode;
+        entity.StageName = dto.StageName;
+        entity.StdHours = dto.StdHours;
+        entity.OrderNo = dto.OrderNo;
+        entity.IsActive = dto.IsActive;
+        entity.UpdateUser = dto.UpdateUser;
+        entity.UpdateTime = DateTime.Now;
+
+        await _rep.AsUpdateable(entity).ExecuteCommandAsync();
+        return Ok(entity);
+    }
+
+    [HttpPatch("{id:long}/toggle-enabled")]
+    public async Task<IActionResult> ToggleActiveAsync(long id, [FromBody] AdoS0ContractReviewCycleToggleActiveDto dto)
+    {
+        var entity = await _rep.GetByIdAsync(id);
+        if (entity == null) return NotFound();
+        entity.IsActive = dto.IsActive;
+        entity.UpdateTime = DateTime.Now;
+        await _rep.AsUpdateable(entity).ExecuteCommandAsync();
+        return Ok(entity);
+    }
+
+    [HttpDelete("{id:long}")]
+    public async Task<IActionResult> DeleteAsync(long id)
+    {
+        var item = await _rep.GetByIdAsync(id);
+        if (item == null) return NotFound();
+        await _rep.DeleteAsync(item);
+        return Ok(new { message = "删除成功" });
+    }
+}

+ 10 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/S0/Sales/AdoS0CustomersController.cs

@@ -78,6 +78,11 @@ public class AdoS0CustomersController : ControllerBase
             TaxClass = dto.TaxClass,
             TaxIn = dto.TaxIn,
             CustClass = dto.CustClass,
+            Address = dto.Address,
+            Contact = dto.Contact,
+            Position = dto.Position,
+            ContactInfo = dto.ContactInfo,
+            CreditRating = dto.CreditRating,
             Remarks = dto.Remarks,
             IsActive = dto.IsActive,
             IsConfirm = dto.IsConfirm,
@@ -112,6 +117,11 @@ public class AdoS0CustomersController : ControllerBase
         entity.TaxClass = dto.TaxClass;
         entity.TaxIn = dto.TaxIn;
         entity.CustClass = dto.CustClass;
+        entity.Address = dto.Address;
+        entity.Contact = dto.Contact;
+        entity.Position = dto.Position;
+        entity.ContactInfo = dto.ContactInfo;
+        entity.CreditRating = dto.CreditRating;
         entity.Remarks = dto.Remarks;
         entity.IsActive = dto.IsActive;
         entity.IsConfirm = dto.IsConfirm;

+ 6 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/S0/Sales/AdoS0MaterialsController.cs

@@ -194,6 +194,9 @@ public class AdoS0MaterialsController : ControllerBase
         OwnerApplication = dto.OwnerApplication,
         BOMDesign = dto.BOMDesign,
         RoutingDes = dto.RoutingDes,
+        CustSupplied = dto.CustSupplied,
+        DrawingDesign = dto.DrawingDesign,
+        DesignOwner = dto.DesignOwner,
         IsActive = dto.IsActive,
         IsConfirm = dto.IsConfirm,
         CreateUser = dto.CreateUser,
@@ -252,6 +255,9 @@ public class AdoS0MaterialsController : ControllerBase
         entity.OwnerApplication = dto.OwnerApplication;
         entity.BOMDesign = dto.BOMDesign;
         entity.RoutingDes = dto.RoutingDes;
+        entity.CustSupplied = dto.CustSupplied;
+        entity.DrawingDesign = dto.DrawingDesign;
+        entity.DesignOwner = dto.DesignOwner;
         entity.IsActive = dto.IsActive;
         entity.IsConfirm = dto.IsConfirm;
         entity.UpdateUser = dto.UpdateUser;

+ 100 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/S0/Sales/AdoS0OrderReviewCyclesController.cs

@@ -0,0 +1,100 @@
+using Admin.NET.Plugin.AiDOP.Dto.S0.Sales;
+using Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
+using Admin.NET.Plugin.AiDOP.Infrastructure;
+
+namespace Admin.NET.Plugin.AiDOP.Controllers.S0.Sales;
+
+[ApiController]
+[Route("api/s0/sales/order-review-cycles")]
+[AllowAnonymous]
+[NonUnify]
+public class AdoS0OrderReviewCyclesController : ControllerBase
+{
+    private readonly SqlSugarRepository<AdoS0OrderReviewCycle> _rep;
+
+    public AdoS0OrderReviewCyclesController(SqlSugarRepository<AdoS0OrderReviewCycle> rep)
+        => _rep = rep;
+
+    [HttpGet]
+    public async Task<IActionResult> GetPagedAsync([FromQuery] AdoS0OrderReviewCycleQueryDto q)
+    {
+        (q.Page, q.PageSize) = PagingGuard.Normalize(q.Page, q.PageSize);
+
+        var query = _rep.AsQueryable()
+            .WhereIF(q.CompanyRefId.HasValue, x => x.CompanyRefId == q.CompanyRefId!.Value)
+            .WhereIF(q.FactoryRefId.HasValue, x => x.FactoryRefId == q.FactoryRefId!.Value)
+            .WhereIF(!string.IsNullOrWhiteSpace(q.DomainCode), x => x.DomainCode == q.DomainCode)
+            .WhereIF(q.IsActive.HasValue, x => x.IsActive == q.IsActive!.Value);
+
+        var total = await query.CountAsync();
+        var list = await query.OrderByDescending(x => x.CreateTime).Skip((q.Page - 1) * q.PageSize).Take(q.PageSize).ToListAsync();
+        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 == null ? NotFound() : Ok(item);
+    }
+
+    [HttpPost]
+    public async Task<IActionResult> CreateAsync([FromBody] AdoS0OrderReviewCycleUpsertDto dto)
+    {
+        if (await _rep.IsAnyAsync(x => x.FactoryRefId == dto.FactoryRefId))
+            return AdoS0ApiErrors.Conflict(AdoS0ErrorCodes.DuplicateCode, "该工厂已存在订单评审周期配置,请使用编辑功能");
+
+        var entity = new AdoS0OrderReviewCycle
+        {
+            CompanyRefId = dto.CompanyRefId,
+            FactoryRefId = dto.FactoryRefId,
+            DomainCode = dto.DomainCode,
+            StdHours = dto.StdHours,
+            Remarks = dto.Remarks,
+            IsActive = dto.IsActive,
+            CreateUser = dto.CreateUser,
+            CreateTime = DateTime.Now,
+        };
+        await _rep.AsInsertable(entity).ExecuteReturnEntityAsync();
+        return Ok(entity);
+    }
+
+    [HttpPut("{id:long}")]
+    public async Task<IActionResult> UpdateAsync(long id, [FromBody] AdoS0OrderReviewCycleUpsertDto dto)
+    {
+        var entity = await _rep.GetByIdAsync(id);
+        if (entity == null) return NotFound();
+
+        entity.CompanyRefId = dto.CompanyRefId;
+        entity.FactoryRefId = dto.FactoryRefId;
+        entity.DomainCode = dto.DomainCode;
+        entity.StdHours = dto.StdHours;
+        entity.Remarks = dto.Remarks;
+        entity.IsActive = dto.IsActive;
+        entity.UpdateUser = dto.UpdateUser;
+        entity.UpdateTime = DateTime.Now;
+
+        await _rep.AsUpdateable(entity).ExecuteCommandAsync();
+        return Ok(entity);
+    }
+
+    [HttpPatch("{id:long}/toggle-enabled")]
+    public async Task<IActionResult> ToggleActiveAsync(long id, [FromBody] AdoS0OrderReviewCycleToggleActiveDto dto)
+    {
+        var entity = await _rep.GetByIdAsync(id);
+        if (entity == null) return NotFound();
+        entity.IsActive = dto.IsActive;
+        entity.UpdateTime = DateTime.Now;
+        await _rep.AsUpdateable(entity).ExecuteCommandAsync();
+        return Ok(entity);
+    }
+
+    [HttpDelete("{id:long}")]
+    public async Task<IActionResult> DeleteAsync(long id)
+    {
+        var item = await _rep.GetByIdAsync(id);
+        if (item == null) return NotFound();
+        await _rep.DeleteAsync(item);
+        return Ok(new { message = "删除成功" });
+    }
+}

+ 107 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/S0/Sales/AdoS0ProductDesignCyclesController.cs

@@ -0,0 +1,107 @@
+using Admin.NET.Plugin.AiDOP.Dto.S0.Sales;
+using Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
+using Admin.NET.Plugin.AiDOP.Infrastructure;
+
+namespace Admin.NET.Plugin.AiDOP.Controllers.S0.Sales;
+
+[ApiController]
+[Route("api/s0/sales/product-design-cycles")]
+[AllowAnonymous]
+[NonUnify]
+public class AdoS0ProductDesignCyclesController : ControllerBase
+{
+    private readonly SqlSugarRepository<AdoS0ProductDesignCycle> _rep;
+
+    public AdoS0ProductDesignCyclesController(SqlSugarRepository<AdoS0ProductDesignCycle> rep)
+        => _rep = rep;
+
+    [HttpGet]
+    public async Task<IActionResult> GetPagedAsync([FromQuery] AdoS0ProductDesignCycleQueryDto q)
+    {
+        (q.Page, q.PageSize) = PagingGuard.Normalize(q.Page, q.PageSize);
+
+        var query = _rep.AsQueryable()
+            .WhereIF(q.CompanyRefId.HasValue, x => x.CompanyRefId == q.CompanyRefId!.Value)
+            .WhereIF(q.FactoryRefId.HasValue, x => x.FactoryRefId == q.FactoryRefId!.Value)
+            .WhereIF(!string.IsNullOrWhiteSpace(q.DomainCode), x => x.DomainCode == q.DomainCode)
+            .WhereIF(!string.IsNullOrWhiteSpace(q.ItemType), x => x.ItemType == q.ItemType)
+            .WhereIF(!string.IsNullOrWhiteSpace(q.OwnerApplication), x => x.OwnerApplication == q.OwnerApplication)
+            .WhereIF(q.IsActive.HasValue, x => x.IsActive == q.IsActive!.Value);
+
+        var total = await query.CountAsync();
+        var list = await query.OrderByDescending(x => x.CreateTime).Skip((q.Page - 1) * q.PageSize).Take(q.PageSize).ToListAsync();
+        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 == null ? NotFound() : Ok(item);
+    }
+
+    [HttpPost]
+    public async Task<IActionResult> CreateAsync([FromBody] AdoS0ProductDesignCycleUpsertDto dto)
+    {
+        if (await _rep.IsAnyAsync(x => x.FactoryRefId == dto.FactoryRefId && x.ItemType == dto.ItemType && x.OwnerApplication == dto.OwnerApplication))
+            return AdoS0ApiErrors.Conflict(AdoS0ErrorCodes.DuplicateCode, "该工厂下此物料类型+属性组合已存在");
+
+        var entity = new AdoS0ProductDesignCycle
+        {
+            CompanyRefId = dto.CompanyRefId,
+            FactoryRefId = dto.FactoryRefId,
+            DomainCode = dto.DomainCode,
+            ItemType = dto.ItemType,
+            OwnerApplication = dto.OwnerApplication,
+            StdHours = dto.StdHours,
+            IsActive = dto.IsActive,
+            CreateUser = dto.CreateUser,
+            CreateTime = DateTime.Now,
+        };
+        await _rep.AsInsertable(entity).ExecuteReturnEntityAsync();
+        return Ok(entity);
+    }
+
+    [HttpPut("{id:long}")]
+    public async Task<IActionResult> UpdateAsync(long id, [FromBody] AdoS0ProductDesignCycleUpsertDto dto)
+    {
+        var entity = await _rep.GetByIdAsync(id);
+        if (entity == null) return NotFound();
+
+        if (await _rep.IsAnyAsync(x => x.Id != id && x.FactoryRefId == dto.FactoryRefId && x.ItemType == dto.ItemType && x.OwnerApplication == dto.OwnerApplication))
+            return AdoS0ApiErrors.Conflict(AdoS0ErrorCodes.DuplicateCode, "该工厂下此物料类型+属性组合已存在");
+
+        entity.CompanyRefId = dto.CompanyRefId;
+        entity.FactoryRefId = dto.FactoryRefId;
+        entity.DomainCode = dto.DomainCode;
+        entity.ItemType = dto.ItemType;
+        entity.OwnerApplication = dto.OwnerApplication;
+        entity.StdHours = dto.StdHours;
+        entity.IsActive = dto.IsActive;
+        entity.UpdateUser = dto.UpdateUser;
+        entity.UpdateTime = DateTime.Now;
+
+        await _rep.AsUpdateable(entity).ExecuteCommandAsync();
+        return Ok(entity);
+    }
+
+    [HttpPatch("{id:long}/toggle-enabled")]
+    public async Task<IActionResult> ToggleActiveAsync(long id, [FromBody] AdoS0ProductDesignCycleToggleActiveDto dto)
+    {
+        var entity = await _rep.GetByIdAsync(id);
+        if (entity == null) return NotFound();
+        entity.IsActive = dto.IsActive;
+        entity.UpdateTime = DateTime.Now;
+        await _rep.AsUpdateable(entity).ExecuteCommandAsync();
+        return Ok(entity);
+    }
+
+    [HttpDelete("{id:long}")]
+    public async Task<IActionResult> DeleteAsync(long id)
+    {
+        var item = await _rep.GetByIdAsync(id);
+        if (item == null) return NotFound();
+        await _rep.DeleteAsync(item);
+        return Ok(new { message = "删除成功" });
+    }
+}

+ 25 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Controllers/S8/AdoS8MonitoringController.cs

@@ -0,0 +1,25 @@
+using Admin.NET.Plugin.AiDOP.Dto.S8;
+using Admin.NET.Plugin.AiDOP.Service.S8;
+
+namespace Admin.NET.Plugin.AiDOP.Controllers.S8;
+
+[ApiController]
+[Route("api/aidop/s8/monitoring")]
+[NonUnify]
+public class AdoS8MonitoringController : ControllerBase
+{
+    private readonly S8MonitoringService _svc;
+
+    public AdoS8MonitoringController(S8MonitoringService svc)
+    {
+        _svc = svc;
+    }
+
+    /// <summary>
+    /// 异常监控汇总——供 4 个监控页顶部徽标和模块汇总表使用。
+    /// sceneCode 不传时返回全部模块聚合(综合全景页)。
+    /// </summary>
+    [HttpGet("summary")]
+    public async Task<IActionResult> GetSummaryAsync([FromQuery] AdoS8MonitoringSummaryQueryDto q)
+        => Ok(await _svc.GetSummaryAsync(q));
+}

+ 129 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Dto/S0/Sales/AdoS0SalesDtos.cs

@@ -102,6 +102,11 @@ public class AdoS0CustMasterUpsertDto
     public string? TaxClass { get; set; }
     public bool TaxIn { get; set; } = true;
     public string? CustClass { get; set; }
+    public string? Address { get; set; }
+    public string? Contact { get; set; }
+    public string? Position { get; set; }
+    public string? ContactInfo { get; set; }
+    public string? CreditRating { get; set; }
     public string? Remarks { get; set; }
     public bool IsActive { get; set; } = true;
     public bool IsConfirm { get; set; }
@@ -195,6 +200,9 @@ public class AdoS0ItemMasterUpsertDto
     public string? OwnerApplication { get; set; }
     public int? BOMDesign { get; set; }
     public int? RoutingDes { get; set; }
+    public bool CustSupplied { get; set; }
+    public int? DrawingDesign { get; set; }
+    public string? DesignOwner { get; set; }
     public bool IsActive { get; set; } = true;
     public bool IsConfirm { get; set; }
     public string? CreateUser { get; set; }
@@ -251,3 +259,124 @@ public class AdoS0PriorityCodeUpsertDto
     public string? CreateUser { get; set; }
     public string? UpdateUser { get; set; }
 }
+
+// ── B1: 合同评审周期标准 ──────────────────────────────────────────────
+
+public class AdoS0ContractReviewCycleQueryDto
+{
+    public long? CompanyRefId { get; set; }
+    public long? FactoryRefId { get; set; }
+    public string? DomainCode { get; set; }
+    public bool? IsActive { get; set; }
+    public int Page { get; set; } = 1;
+    public int PageSize { get; set; } = 20;
+}
+
+public class AdoS0ContractReviewCycleUpsertDto
+{
+    [Range(1, long.MaxValue, ErrorMessage = "公司不能为空")]
+    public long CompanyRefId { get; set; }
+
+    [Range(1, long.MaxValue, ErrorMessage = "工厂不能为空")]
+    public long FactoryRefId { get; set; }
+
+    public string? DomainCode { get; set; }
+
+    [Required(ErrorMessage = "阶段编码不能为空")]
+    public string StageCode { get; set; } = string.Empty;
+
+    [Required(ErrorMessage = "阶段名称不能为空")]
+    public string StageName { get; set; } = string.Empty;
+
+    [Range(0, int.MaxValue, ErrorMessage = "标准时长不能为负")]
+    public int StdHours { get; set; }
+
+    public int OrderNo { get; set; }
+    public bool IsActive { get; set; } = true;
+    public string? CreateUser { get; set; }
+    public string? UpdateUser { get; set; }
+}
+
+public class AdoS0ContractReviewCycleToggleActiveDto
+{
+    public bool IsActive { get; set; }
+}
+
+// ── B2: 产品设计周期标准 ──────────────────────────────────────────────
+
+public class AdoS0ProductDesignCycleQueryDto
+{
+    public long? CompanyRefId { get; set; }
+    public long? FactoryRefId { get; set; }
+    public string? DomainCode { get; set; }
+    public string? ItemType { get; set; }
+    public string? OwnerApplication { get; set; }
+    public bool? IsActive { get; set; }
+    public int Page { get; set; } = 1;
+    public int PageSize { get; set; } = 20;
+}
+
+public class AdoS0ProductDesignCycleUpsertDto
+{
+    [Range(1, long.MaxValue, ErrorMessage = "公司不能为空")]
+    public long CompanyRefId { get; set; }
+
+    [Range(1, long.MaxValue, ErrorMessage = "工厂不能为空")]
+    public long FactoryRefId { get; set; }
+
+    public string? DomainCode { get; set; }
+
+    [Required(ErrorMessage = "物料类型不能为空")]
+    public string ItemType { get; set; } = string.Empty;
+
+    [Required(ErrorMessage = "物料属性不能为空")]
+    public string OwnerApplication { get; set; } = string.Empty;
+
+    [Range(0, int.MaxValue, ErrorMessage = "标准时长不能为负")]
+    public int StdHours { get; set; }
+
+    public bool IsActive { get; set; } = true;
+    public string? CreateUser { get; set; }
+    public string? UpdateUser { get; set; }
+}
+
+public class AdoS0ProductDesignCycleToggleActiveDto
+{
+    public bool IsActive { get; set; }
+}
+
+// ── B3: 订单评审周期标准 ──────────────────────────────────────────────
+
+public class AdoS0OrderReviewCycleQueryDto
+{
+    public long? CompanyRefId { get; set; }
+    public long? FactoryRefId { get; set; }
+    public string? DomainCode { get; set; }
+    public bool? IsActive { get; set; }
+    public int Page { get; set; } = 1;
+    public int PageSize { get; set; } = 20;
+}
+
+public class AdoS0OrderReviewCycleUpsertDto
+{
+    [Range(1, long.MaxValue, ErrorMessage = "公司不能为空")]
+    public long CompanyRefId { get; set; }
+
+    [Range(1, long.MaxValue, ErrorMessage = "工厂不能为空")]
+    public long FactoryRefId { get; set; }
+
+    public string? DomainCode { get; set; }
+
+    [Range(0, int.MaxValue, ErrorMessage = "标准时长不能为负")]
+    public int StdHours { get; set; }
+
+    public string? Remarks { get; set; }
+    public bool IsActive { get; set; } = true;
+    public string? CreateUser { get; set; }
+    public string? UpdateUser { get; set; }
+}
+
+public class AdoS0OrderReviewCycleToggleActiveDto
+{
+    public bool IsActive { get; set; }
+}

+ 41 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Dto/S8/AdoS8Dtos.cs

@@ -9,6 +9,8 @@ public class AdoS8ExceptionQueryDto
     public string? StatusBucket { get; set; }
     public string? Severity { get; set; }
     public string? SceneCode { get; set; }
+    /// <summary>模块码过滤(S1-S7),用于监控页下钻</summary>
+    public string? ModuleCode { get; set; }
     public long? DeptId { get; set; }
     public bool? TimeoutFlag { get; set; }
     public string? ProcessNodeCode { get; set; }
@@ -130,3 +132,42 @@ public class AdoS8AttachmentCreateDto
     public string? SourceSystem { get; set; }
     public long? UploadedBy { get; set; }
 }
+
+// ── 监控汇总 DTO ─────────────────────────────────────────────────────────────
+
+public class AdoS8MonitoringSummaryQueryDto
+{
+    public long TenantId { get; set; } = 1;
+    public long FactoryId { get; set; } = 1;
+    /// <summary>场景码过滤(不传 = 全部)</summary>
+    public string? SceneCode { get; set; }
+    /// <summary>模块码过滤(不传 = 全部)</summary>
+    public string? ModuleCode { get; set; }
+    public DateTime? BizDateFrom { get; set; }
+    public DateTime? BizDateTo { get; set; }
+}
+
+/// <summary>单个模块的异常汇总行</summary>
+public class AdoS8ModuleSummaryItem
+{
+    public string ModuleCode { get; set; } = string.Empty;
+    public string ModuleLabel { get; set; } = string.Empty;
+    public string SceneCode { get; set; } = string.Empty;
+    public string SceneLabel { get; set; } = string.Empty;
+    public int Total { get; set; }
+    public int Red { get; set; }
+    public int Yellow { get; set; }
+    public int Green { get; set; }
+    public int Timeout { get; set; }
+}
+
+/// <summary>监控汇总出参(综合全景页顶部 + 表格数据)</summary>
+public class AdoS8MonitoringSummaryDto
+{
+    public int Total { get; set; }
+    public int Red { get; set; }
+    public int Yellow { get; set; }
+    public int Green { get; set; }
+    public int Timeout { get; set; }
+    public List<AdoS8ModuleSummaryItem> ByModule { get; set; } = new();
+}

+ 17 - 17
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0DevMonitor.cs

@@ -6,54 +6,54 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Manufacturing;
 [SugarTable("DevMonitor", "生产要素参数表")]
 public class AdoS0DevMonitor
 {
-    [SugarColumn(ColumnName = "id", ColumnDescription = "生产要素参数主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "生产要素参数主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
-    [SugarColumn(ColumnName = "device_number", ColumnDescription = "要素编码", Length = 100)]
+    [SugarColumn(ColumnName = "DeviceNumber", ColumnDescription = "要素编码", Length = 100)]
     public string DeviceNumber { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "device_descr", ColumnDescription = "要素描述", Length = 255, IsNullable = true)]
+    [SugarColumn(ColumnName = "DeviceDescr", ColumnDescription = "要素描述", Length = 255, IsNullable = true)]
     public string? DeviceDescr { get; set; }
 
-    [SugarColumn(ColumnName = "message_code", ColumnDescription = "参数编码", Length = 100)]
+    [SugarColumn(ColumnName = "MessageCode", ColumnDescription = "参数编码", Length = 100)]
     public string MessageCode { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "code_descr", ColumnDescription = "参数描述", Length = 255, IsNullable = true)]
+    [SugarColumn(ColumnName = "CodeDescr", ColumnDescription = "参数描述", Length = 255, IsNullable = true)]
     public string? CodeDescr { get; set; }
 
-    [SugarColumn(ColumnName = "value_type", ColumnDescription = "值类型", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "ValueType", ColumnDescription = "值类型", Length = 50, IsNullable = true)]
     public string? ValueType { get; set; }
 
-    [SugarColumn(ColumnName = "std_value", ColumnDescription = "标准值", Length = 255, IsNullable = true)]
+    [SugarColumn(ColumnName = "StdValue", ColumnDescription = "标准值", Length = 255, IsNullable = true)]
     public string? StdValue { get; set; }
 
-    [SugarColumn(ColumnName = "top_limit", ColumnDescription = "上限值", DecimalDigits = 6, Length = 18, IsNullable = true)]
+    [SugarColumn(ColumnName = "TopLimit", ColumnDescription = "上限值", DecimalDigits = 6, Length = 18, IsNullable = true)]
     public decimal? TopLimit { get; set; }
 
-    [SugarColumn(ColumnName = "low_limit", ColumnDescription = "下限值", DecimalDigits = 6, Length = 18, IsNullable = true)]
+    [SugarColumn(ColumnName = "LowLimit", ColumnDescription = "下限值", DecimalDigits = 6, Length = 18, IsNullable = true)]
     public decimal? LowLimit { get; set; }
 
-    [SugarColumn(ColumnName = "ufld1", ColumnDescription = "值选择数据", Length = 1000, IsNullable = true)]
+    [SugarColumn(ColumnName = "Ufld1", ColumnDescription = "值选择数据", Length = 1000, IsNullable = true)]
     public string? Ufld1 { get; set; }
 
-    [SugarColumn(ColumnName = "um", ColumnDescription = "单位", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "UM", ColumnDescription = "单位", Length = 50, IsNullable = true)]
     public string? UM { get; set; }
 
-    [SugarColumn(ColumnName = "code_type", ColumnDescription = "参数分组类型", Length = 50)]
+    [SugarColumn(ColumnName = "CodeType", ColumnDescription = "参数分组类型", Length = 50)]
     public string CodeType { get; set; } = "Prod";
 
-    [SugarColumn(ColumnName = "domain", ColumnDescription = "工厂域编码", Length = 50)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50)]
     public string Domain { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 64, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间", IsNullable = true)]
     public DateTime? CreateTime { get; set; }
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 64, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 }

+ 14 - 14
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0EmpSkills.cs

@@ -6,49 +6,49 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Manufacturing;
 [SugarTable("EmpSkills", "人员技能关系表")]
 public class AdoS0EmpSkills
 {
-    [SugarColumn(ColumnDescription = "人员技能主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "人员技能主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
-    [SugarColumn(ColumnDescription = "人员编号", Length = 100)]
+    [SugarColumn(ColumnName = "Employee", ColumnDescription = "人员编号", Length = 100)]
     public string Employee { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnDescription = "人员内部 ID", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "EmployeeID", ColumnDescription = "人员内部 ID", Length = 100, IsNullable = true)]
     public string? EmployeeId { get; set; }
 
-    [SugarColumn(ColumnDescription = "人员名称", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "EmployeeName", ColumnDescription = "人员名称", Length = 100, IsNullable = true)]
     public string? EmployeeName { get; set; }
 
-    [SugarColumn(ColumnDescription = "工作组", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Site", ColumnDescription = "工作组", Length = 100, IsNullable = true)]
     public string? Site { get; set; }
 
-    [SugarColumn(ColumnDescription = "技能编码", Length = 100)]
+    [SugarColumn(ColumnName = "SkillNo", ColumnDescription = "技能编码", Length = 100)]
     public string SkillNo { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnDescription = "技能描述", Length = 255, IsNullable = true)]
+    [SugarColumn(ColumnName = "SkillDescription", ColumnDescription = "技能描述", Length = 255, IsNullable = true)]
     public string? SkillDescription { get; set; }
 
-    [SugarColumn(ColumnDescription = "技能等级", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "SkillLevel", ColumnDescription = "技能等级", Length = 50, IsNullable = true)]
     public string? SkillLevel { get; set; }
 
-    [SugarColumn(ColumnDescription = "生产效率", DecimalDigits = 6, Length = 18, IsNullable = true)]
+    [SugarColumn(ColumnName = "EfficiencyCoefficient", ColumnDescription = "生产效率", DecimalDigits = 6, Length = 18, IsNullable = true)]
     public decimal? EfficiencyCoefficient { get; set; }
 
     [SugarColumn(ColumnDescription = "技能日期", IsNullable = true)]
     public DateTime? DateSkill { get; set; }
 
-    [SugarColumn(ColumnDescription = "工厂域编码", Length = 50)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50)]
     public string Domain { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnDescription = "创建人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 64, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnDescription = "创建时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间", IsNullable = true)]
     public DateTime? CreateTime { get; set; }
 
-    [SugarColumn(ColumnDescription = "更新人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 64, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 }
 

+ 14 - 14
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0ItemOpCondition.cs

@@ -6,45 +6,45 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Manufacturing;
 [SugarTable("ItemOpCondition", "物料工序生产要素表")]
 public class AdoS0ItemOpCondition
 {
-    [SugarColumn(ColumnName = "id", ColumnDescription = "物料工序生产要素主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "物料工序生产要素主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
-    [SugarColumn(ColumnName = "item_num", ColumnDescription = "物料编码", Length = 100)]
+    [SugarColumn(ColumnName = "ItemNum", ColumnDescription = "物料编码", Length = 100)]
     public string ItemNum { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "op", ColumnDescription = "工序编码", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Op", ColumnDescription = "工序编码", Length = 100, IsNullable = true)]
     public string? Op { get; set; }
 
-    [SugarColumn(ColumnName = "line", ColumnDescription = "生产线编码", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Line", ColumnDescription = "生产线编码", Length = 100, IsNullable = true)]
     public string? Line { get; set; }
 
-    [SugarColumn(ColumnName = "typed", ColumnDescription = "类型编码", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Typed", ColumnDescription = "类型编码", Length = 100, IsNullable = true)]
     public string? Typed { get; set; }
 
-    [SugarColumn(ColumnName = "item_code", ColumnDescription = "要素编码", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "ItemCode", ColumnDescription = "要素编码", Length = 100, IsNullable = true)]
     public string? ItemCode { get; set; }
 
-    [SugarColumn(ColumnName = "descr", ColumnDescription = "描述", Length = 255, IsNullable = true)]
+    [SugarColumn(ColumnName = "Descr", ColumnDescription = "描述", Length = 255, IsNullable = true)]
     public string? Descr { get; set; }
 
-    [SugarColumn(ColumnName = "is_main", ColumnDescription = "是否关键", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsMain", ColumnDescription = "是否关键", ColumnDataType = "boolean")]
     public bool IsMain { get; set; }
 
-    [SugarColumn(ColumnName = "code_type", ColumnDescription = "参数分组类型", Length = 50)]
+    [SugarColumn(ColumnName = "CodeType", ColumnDescription = "参数分组类型", Length = 50)]
     public string CodeType { get; set; } = "Prod";
 
-    [SugarColumn(ColumnName = "domain", ColumnDescription = "工厂域编码", Length = 50)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50)]
     public string Domain { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 64, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间", IsNullable = true)]
     public DateTime? CreateTime { get; set; }
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 64, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 }

+ 15 - 15
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0LineMaster.cs

@@ -7,7 +7,7 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Manufacturing;
 [SugarIndex("uk_LineMaster_scope_line", nameof(CompanyRefId), OrderByType.Asc, nameof(FactoryRefId), OrderByType.Asc, nameof(Line), OrderByType.Asc, IsUnique = true)]
 public class AdoS0LineMaster
 {
-    [SugarColumn(ColumnName = "id", ColumnDescription = "生产线主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "生产线主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnName = "company_ref_id", ColumnDescription = "关联公司 ID", ColumnDataType = "bigint")]
@@ -16,28 +16,28 @@ public class AdoS0LineMaster
     [SugarColumn(ColumnName = "factory_ref_id", ColumnDescription = "关联工厂 ID", ColumnDataType = "bigint")]
     public long FactoryRefId { get; set; }
 
-    [SugarColumn(ColumnName = "domain", ColumnDescription = "工厂域编码", Length = 50)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50)]
     public string Domain { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "line", ColumnDescription = "生产线编码", Length = 100)]
+    [SugarColumn(ColumnName = "Line", ColumnDescription = "生产线编码", Length = 100)]
     public string Line { get; set; } = string.Empty;
 
     [SugarColumn(ColumnName = "line_describe", ColumnDescription = "生产线描述", Length = 255, IsNullable = true)]
     public string? Describe { get; set; }
 
-    [SugarColumn(ColumnName = "line_type", ColumnDescription = "产线类型", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "LineType", ColumnDescription = "产线类型", Length = 50, IsNullable = true)]
     public string? LineType { get; set; }
 
-    [SugarColumn(ColumnName = "line_category", ColumnDescription = "产线类别", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "LineCategory", ColumnDescription = "产线类别", Length = 50, IsNullable = true)]
     public string? LineCategory { get; set; }
 
-    [SugarColumn(ColumnName = "location", ColumnDescription = "主库位", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "Location", ColumnDescription = "主库位", Length = 50, IsNullable = true)]
     public string? Location { get; set; }
 
-    [SugarColumn(ColumnName = "workshop", ColumnDescription = "车间编码", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "Workshop", ColumnDescription = "车间编码", Length = 50, IsNullable = true)]
     public string? Workshop { get; set; }
 
-    [SugarColumn(ColumnName = "v_location", ColumnDescription = "虚拟库位", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "VLocation", ColumnDescription = "虚拟库位", Length = 50, IsNullable = true)]
     public string? VLocation { get; set; }
 
     [SugarColumn(ColumnName = "location2", ColumnDescription = "辅助库位2", Length = 50, IsNullable = true)]
@@ -46,24 +46,24 @@ public class AdoS0LineMaster
     [SugarColumn(ColumnName = "location3", ColumnDescription = "辅助库位3", Length = 50, IsNullable = true)]
     public string? Location3 { get; set; }
 
-    [SugarColumn(ColumnName = "picking_location", ColumnDescription = "拣料库位", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "PickingLocation", ColumnDescription = "拣料库位", Length = 50, IsNullable = true)]
     public string? PickingLocation { get; set; }
 
-    [SugarColumn(ColumnName = "mid_location", ColumnDescription = "中间库位", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "MidLocation", ColumnDescription = "中间库位", Length = 50, IsNullable = true)]
     public string? MidLocation { get; set; }
 
-    [SugarColumn(ColumnName = "is_active", ColumnDescription = "是否启用", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsActive", ColumnDescription = "是否启用", ColumnDataType = "boolean")]
     public bool IsActive { get; set; } = true;
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 64, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间", IsNullable = true)]
     public DateTime? CreateTime { get; set; }
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 64, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 }

+ 1 - 1
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0LineSkillDetail.cs

@@ -27,7 +27,7 @@ public class AdoS0LineSkillDetail
     [SugarColumn(ColumnName = "is_enabled", ColumnDescription = "启用", ColumnDataType = "boolean")]
     public bool IsEnabled { get; set; } = true;
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间")]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")]
     public DateTime CreateTime { get; set; } = DateTime.Now;
 }
 

+ 9 - 9
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0LineSkillMaster.cs

@@ -7,31 +7,31 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Manufacturing;
 [SugarIndex("uk_LineSkillMaster_domain_job", nameof(Domain), OrderByType.Asc, nameof(JOBNo), OrderByType.Asc, IsUnique = true)]
 public class AdoS0LineSkillMaster
 {
-    [SugarColumn(ColumnName = "id", ColumnDescription = "产线岗位主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "产线岗位主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
-    [SugarColumn(ColumnName = "domain", ColumnDescription = "工厂域编码", Length = 50)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50)]
     public string Domain { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "prod_line", ColumnDescription = "生产线编码", Length = 100)]
+    [SugarColumn(ColumnName = "ProdLine", ColumnDescription = "生产线编码", Length = 100)]
     public string ProdLine { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "job_no", ColumnDescription = "岗位编码", Length = 100)]
+    [SugarColumn(ColumnName = "JOBNo", ColumnDescription = "岗位编码", Length = 100)]
     public string JOBNo { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "job_descr", ColumnDescription = "岗位描述", Length = 255, IsNullable = true)]
+    [SugarColumn(ColumnName = "JOBDescr", ColumnDescription = "岗位描述", Length = 255, IsNullable = true)]
     public string? JOBDescr { get; set; }
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 64, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间", IsNullable = true)]
     public DateTime? CreateTime { get; set; }
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 64, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 }
 

+ 19 - 19
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0MfgRoutingOpDetail.cs

@@ -6,7 +6,7 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Manufacturing;
 [SugarTable("RoutingOpDetail", "S0 标准工艺路线明细")]
 public class AdoS0MfgRoutingOpDetail
 {
-    [SugarColumn(ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnDescription = "公司", ColumnDataType = "bigint")]
@@ -36,46 +36,46 @@ public class AdoS0MfgRoutingOpDetail
     [SugarColumn(ColumnDescription = "报工工序(MilestoneOp)", Length = 100, IsNullable = true)]
     public string? MilestoneOperation { get; set; }
 
-    [SugarColumn(ColumnDescription = "自定义字段Ufld1", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "Ufld1", ColumnDescription = "自定义字段Ufld1", Length = 200, IsNullable = true)]
     public string? Ufld1 { get; set; }
 
-    [SugarColumn(ColumnDescription = "活动一(Ufld2)", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "Ufld2", ColumnDescription = "活动一(Ufld2)", Length = 200, IsNullable = true)]
     public string? Ufld2 { get; set; }
 
-    [SugarColumn(ColumnDescription = "数量一(UDeci1)", DecimalDigits = 5, Length = 18, IsNullable = true)]
+    [SugarColumn(ColumnName = "UDeci1", ColumnDescription = "数量一(UDeci1)", DecimalDigits = 5, Length = 18, IsNullable = true)]
     public decimal? UDeci1 { get; set; }
 
-    [SugarColumn(ColumnDescription = "单位一(Ufld3)", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "Ufld3", ColumnDescription = "单位一(Ufld3)", Length = 50, IsNullable = true)]
     public string? Ufld3 { get; set; }
 
-    [SugarColumn(ColumnDescription = "自定义字段Ufld4", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "Ufld4", ColumnDescription = "自定义字段Ufld4", Length = 200, IsNullable = true)]
     public string? Ufld4 { get; set; }
 
-    [SugarColumn(ColumnDescription = "活动二(Ufld5)", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "Ufld5", ColumnDescription = "活动二(Ufld5)", Length = 200, IsNullable = true)]
     public string? Ufld5 { get; set; }
 
-    [SugarColumn(ColumnDescription = "数量二(UDeci2)", DecimalDigits = 5, Length = 18, IsNullable = true)]
+    [SugarColumn(ColumnName = "UDeci2", ColumnDescription = "数量二(UDeci2)", DecimalDigits = 5, Length = 18, IsNullable = true)]
     public decimal? UDeci2 { get; set; }
 
-    [SugarColumn(ColumnDescription = "单位二(Ufld6)", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "Ufld6", ColumnDescription = "单位二(Ufld6)", Length = 50, IsNullable = true)]
     public string? Ufld6 { get; set; }
 
-    [SugarColumn(ColumnDescription = "自定义字段Ufld7", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "Ufld7", ColumnDescription = "自定义字段Ufld7", Length = 200, IsNullable = true)]
     public string? Ufld7 { get; set; }
 
-    [SugarColumn(ColumnDescription = "活动三(Ufld8)", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "Ufld8", ColumnDescription = "活动三(Ufld8)", Length = 200, IsNullable = true)]
     public string? Ufld8 { get; set; }
 
-    [SugarColumn(ColumnDescription = "数量三(UDeci3)", DecimalDigits = 5, Length = 18, IsNullable = true)]
+    [SugarColumn(ColumnName = "UDeci3", ColumnDescription = "数量三(UDeci3)", DecimalDigits = 5, Length = 18, IsNullable = true)]
     public decimal? UDeci3 { get; set; }
 
-    [SugarColumn(ColumnDescription = "单位三(Ufld9)", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "Ufld9", ColumnDescription = "单位三(Ufld9)", Length = 50, IsNullable = true)]
     public string? Ufld9 { get; set; }
 
-    [SugarColumn(ColumnDescription = "基本批量(UDeci4)", DecimalDigits = 5, Length = 18, IsNullable = true)]
+    [SugarColumn(ColumnName = "UDeci4", ColumnDescription = "基本批量(UDeci4)", DecimalDigits = 5, Length = 18, IsNullable = true)]
     public decimal? UDeci4 { get; set; }
 
-    [SugarColumn(ColumnDescription = "工序委外(UDeci5)", DecimalDigits = 5, Length = 18, IsNullable = true)]
+    [SugarColumn(ColumnName = "UDeci5", ColumnDescription = "工序委外(UDeci5)", DecimalDigits = 5, Length = 18, IsNullable = true)]
     public decimal? UDeci5 { get; set; }
 
     [SugarColumn(ColumnDescription = "供应商代码(Supp)", Length = 100, IsNullable = true)]
@@ -90,16 +90,16 @@ public class AdoS0MfgRoutingOpDetail
     [SugarColumn(ColumnDescription = "启用(IsActive)", ColumnDataType = "boolean")]
     public bool IsEnabled { get; set; } = true;
 
-    [SugarColumn(ColumnDescription = "是否确认(IsConfirm)", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsConfirm", ColumnDescription = "是否确认(IsConfirm)", ColumnDataType = "boolean")]
     public bool IsConfirm { get; set; }
 
-    [SugarColumn(ColumnDescription = "批注索引(CommentIndex)", IsNullable = true)]
+    [SugarColumn(ColumnName = "CommentIndex", ColumnDescription = "批注索引(CommentIndex)", IsNullable = true)]
     public int? CommentIndex { get; set; }
 
-    [SugarColumn(ColumnDescription = "创建人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnDescription = "更新人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
     [SugarColumn(ColumnDescription = "创建时间")]

+ 18 - 18
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0ProdLineDetail.cs

@@ -6,61 +6,61 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Manufacturing;
 [SugarTable("ProdLineDetail", "生产线物料维护表")]
 public class AdoS0ProdLineDetail
 {
-    [SugarColumn(ColumnName = "id", ColumnDescription = "生产线物料主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "生产线物料主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
-    [SugarColumn(ColumnName = "part", ColumnDescription = "物料编码", Length = 100)]
+    [SugarColumn(ColumnName = "Part", ColumnDescription = "物料编码", Length = 100)]
     public string Part { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "op", ColumnDescription = "工序编码", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Op", ColumnDescription = "工序编码", Length = 100, IsNullable = true)]
     public string? Op { get; set; }
 
-    [SugarColumn(ColumnName = "site", ColumnDescription = "工作中心编码", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Site", ColumnDescription = "工作中心编码", Length = 100, IsNullable = true)]
     public string? Site { get; set; }
 
-    [SugarColumn(ColumnName = "line", ColumnDescription = "设备/生产线编码", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Line", ColumnDescription = "设备/生产线编码", Length = 100, IsNullable = true)]
     public string? Line { get; set; }
 
-    [SugarColumn(ColumnName = "rate", ColumnDescription = "单位标准产能(小时)", DecimalDigits = 6, Length = 18, IsNullable = true)]
+    [SugarColumn(ColumnName = "Rate", ColumnDescription = "单位标准产能(小时)", DecimalDigits = 6, Length = 18, IsNullable = true)]
     public decimal? Rate { get; set; }
 
-    [SugarColumn(ColumnName = "setup_time", ColumnDescription = "换模时间(小时)", DecimalDigits = 6, Length = 18, IsNullable = true)]
+    [SugarColumn(ColumnName = "SetupTime", ColumnDescription = "换模时间(小时)", DecimalDigits = 6, Length = 18, IsNullable = true)]
     public decimal? SetupTime { get; set; }
 
-    [SugarColumn(ColumnName = "skill_no", ColumnDescription = "员工技能编码", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "SkillNo", ColumnDescription = "员工技能编码", Length = 100, IsNullable = true)]
     public string? SkillNo { get; set; }
 
     [SugarColumn(ColumnName = "internal_equipment_type_code", ColumnDescription = "厂内设备类型编码", Length = 100, IsNullable = true)]
     public string? InternalEquipmentTypeCode { get; set; }
 
-    [SugarColumn(ColumnName = "mold_type_code", ColumnDescription = "模具类型编码", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "MoldTypeCode", ColumnDescription = "模具类型编码", Length = 100, IsNullable = true)]
     public string? MoldTypeCode { get; set; }
 
-    [SugarColumn(ColumnName = "standard_staff_count", ColumnDescription = "标准人数", IsNullable = true)]
+    [SugarColumn(ColumnName = "StandardStaffCount", ColumnDescription = "标准人数", IsNullable = true)]
     public int? StandardStaffCount { get; set; }
 
-    [SugarColumn(ColumnName = "start", ColumnDescription = "起始标记/顺序", IsNullable = true)]
+    [SugarColumn(ColumnName = "Start", ColumnDescription = "起始标记/顺序", IsNullable = true)]
     public int? Start { get; set; }
 
-    [SugarColumn(ColumnName = "is_active", ColumnDescription = "是否启用", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsActive", ColumnDescription = "是否启用", ColumnDataType = "boolean")]
     public bool IsActive { get; set; } = true;
 
-    [SugarColumn(ColumnName = "is_confirm", ColumnDescription = "是否确认", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsConfirm", ColumnDescription = "是否确认", ColumnDataType = "boolean")]
     public bool IsConfirm { get; set; }
 
-    [SugarColumn(ColumnName = "domain", ColumnDescription = "工厂域编码", Length = 50)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50)]
     public string Domain { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 64, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间", IsNullable = true)]
     public DateTime? CreateTime { get; set; }
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 64, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 }
 

+ 12 - 12
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0ProductStructure.cs

@@ -7,7 +7,7 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Manufacturing;
 [SugarIndex("ix_ProductStructureMaster_factory_parent", nameof(CompanyRefId), OrderByType.Asc, nameof(FactoryRefId), OrderByType.Asc, nameof(ParentMaterialId), OrderByType.Asc)]
 public class AdoS0ProductStructureMaster
 {
-    [SugarColumn(ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnDescription = "公司", ColumnDataType = "bigint")]
@@ -22,35 +22,35 @@ public class AdoS0ProductStructureMaster
     [SugarColumn(ColumnDescription = "子项物料", ColumnDataType = "bigint")]
     public long ComponentMaterialId { get; set; }
 
-    [SugarColumn(ColumnDescription = "标准用量", DecimalDigits = 5, Length = 18)]
+    [SugarColumn(ColumnName = "Qty", ColumnDescription = "标准用量", DecimalDigits = 5, Length = 18)]
     public decimal Qty { get; set; } = 1m;
 
-    [SugarColumn(ColumnDescription = "单位", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "UM", ColumnDescription = "单位", Length = 50, IsNullable = true)]
     public string? UM { get; set; }
 
-    [SugarColumn(ColumnDescription = "损耗率", DecimalDigits = 5, Length = 18)]
+    [SugarColumn(ColumnName = "Scrap", ColumnDescription = "损耗率", DecimalDigits = 5, Length = 18)]
     public decimal Scrap { get; set; }
 
-    [SugarColumn(ColumnDescription = "固定损耗量", DecimalDigits = 5, Length = 18)]
+    [SugarColumn(ColumnName = "QtyConsumed", ColumnDescription = "固定损耗量", DecimalDigits = 5, Length = 18)]
     public decimal QtyConsumed { get; set; }
 
     /// <summary>行上工序号(原平台 pm.Op),与 ProductStructureOp 多工序并存。</summary>
     [SugarColumn(ColumnDescription = "行工序号", IsNullable = true)]
     public int? LineOp { get; set; }
 
-    [SugarColumn(ColumnDescription = "参考", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "Refs", ColumnDescription = "参考", Length = 200, IsNullable = true)]
     public string? Refs { get; set; }
 
-    [SugarColumn(ColumnDescription = "备注", Length = 500, IsNullable = true)]
+    [SugarColumn(ColumnName = "Remark", ColumnDescription = "备注", Length = 500, IsNullable = true)]
     public string? Remark { get; set; }
 
-    [SugarColumn(ColumnDescription = "生效开始", IsNullable = true)]
+    [SugarColumn(ColumnName = "StartEff", ColumnDescription = "生效开始", IsNullable = true)]
     public DateTime? StartEff { get; set; }
 
-    [SugarColumn(ColumnDescription = "生效结束", IsNullable = true)]
+    [SugarColumn(ColumnName = "EndEff", ColumnDescription = "生效结束", IsNullable = true)]
     public DateTime? EndEff { get; set; }
 
-    [SugarColumn(ColumnDescription = "结构类型", Length = 10, IsNullable = true)]
+    [SugarColumn(ColumnName = "StructureType", ColumnDescription = "结构类型", Length = 10, IsNullable = true)]
     public string? StructureType { get; set; }
 
     [SugarColumn(ColumnDescription = "启用", ColumnDataType = "boolean")]
@@ -70,7 +70,7 @@ public class AdoS0ProductStructureMaster
 [SugarIndex("ix_ProductStructureOp_factory_pc", nameof(CompanyRefId), OrderByType.Asc, nameof(FactoryRefId), OrderByType.Asc, nameof(ParentMaterialId), OrderByType.Asc, nameof(ComponentMaterialId), OrderByType.Asc)]
 public class AdoS0ProductStructureOp
 {
-    [SugarColumn(ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnDescription = "公司", ColumnDataType = "bigint")]
@@ -88,7 +88,7 @@ public class AdoS0ProductStructureOp
     [SugarColumn(ColumnDescription = "子项物料", ColumnDataType = "bigint")]
     public long ComponentMaterialId { get; set; }
 
-    [SugarColumn(ColumnDescription = "工序号")]
+    [SugarColumn(ColumnName = "Op", ColumnDescription = "工序号")]
     public int Op { get; set; }
 
     [SugarColumn(ColumnDescription = "排序")]

+ 10 - 10
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Manufacturing/AdoS0WorkCtrMaster.cs

@@ -7,34 +7,34 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Manufacturing;
 [SugarIndex("uk_WorkCtrMaster_domain_ctr", nameof(Domain), OrderByType.Asc, nameof(WorkCtr), OrderByType.Asc, IsUnique = true)]
 public class AdoS0WorkCtrMaster
 {
-    [SugarColumn(ColumnName = "id", ColumnDescription = "工作中心主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "工作中心主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
-    [SugarColumn(ColumnName = "work_ctr", ColumnDescription = "工作中心编码", Length = 100)]
+    [SugarColumn(ColumnName = "WorkCtr", ColumnDescription = "工作中心编码", Length = 100)]
     public string WorkCtr { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "descr", ColumnDescription = "工作中心描述", Length = 255, IsNullable = true)]
+    [SugarColumn(ColumnName = "Descr", ColumnDescription = "工作中心描述", Length = 255, IsNullable = true)]
     public string? Descr { get; set; }
 
-    [SugarColumn(ColumnName = "department", ColumnDescription = "所属部门编码", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Department", ColumnDescription = "所属部门编码", Length = 100, IsNullable = true)]
     public string? Department { get; set; }
 
-    [SugarColumn(ColumnName = "is_active", ColumnDescription = "状态", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsActive", ColumnDescription = "状态", ColumnDataType = "boolean")]
     public bool IsActive { get; set; } = true;
 
-    [SugarColumn(ColumnName = "domain", ColumnDescription = "工厂域编码", Length = 50)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50)]
     public string Domain { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 64, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间", IsNullable = true)]
     public DateTime? CreateTime { get; set; }
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 64, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 }
 

+ 48 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/AdoS0ContractReviewCycle.cs

@@ -0,0 +1,48 @@
+namespace Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
+
+[SugarTable("S0ContractReviewCycle", "合同评审周期标准")]
+[SugarIndex("uk_S0ContractReviewCycle_factory_stage",
+    nameof(FactoryRefId), OrderByType.Asc,
+    nameof(StageCode), OrderByType.Asc,
+    IsUnique = true)]
+public class AdoS0ContractReviewCycle
+{
+    [SugarColumn(ColumnName = "rec_id", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    public long Id { get; set; }
+
+    [SugarColumn(ColumnName = "company_ref_id", ColumnDataType = "bigint")]
+    public long CompanyRefId { get; set; }
+
+    [SugarColumn(ColumnName = "factory_ref_id", ColumnDataType = "bigint")]
+    public long FactoryRefId { get; set; }
+
+    [SugarColumn(ColumnName = "domain_code", Length = 50, IsNullable = true)]
+    public string? DomainCode { get; set; }
+
+    [SugarColumn(ColumnName = "stage_code", Length = 50)]
+    public string StageCode { get; set; } = string.Empty;
+
+    [SugarColumn(ColumnName = "stage_name", Length = 200)]
+    public string StageName { get; set; } = string.Empty;
+
+    [SugarColumn(ColumnName = "std_hours")]
+    public int StdHours { get; set; }
+
+    [SugarColumn(ColumnName = "order_no")]
+    public int OrderNo { get; set; }
+
+    [SugarColumn(ColumnName = "is_active", ColumnDataType = "boolean")]
+    public bool IsActive { get; set; } = true;
+
+    [SugarColumn(ColumnName = "create_user", Length = 100, IsNullable = true)]
+    public string? CreateUser { get; set; }
+
+    [SugarColumn(ColumnName = "create_time")]
+    public DateTime CreateTime { get; set; } = DateTime.Now;
+
+    [SugarColumn(ColumnName = "update_user", Length = 100, IsNullable = true)]
+    public string? UpdateUser { get; set; }
+
+    [SugarColumn(ColumnName = "update_time", IsNullable = true)]
+    public DateTime? UpdateTime { get; set; }
+}

+ 32 - 17
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/AdoS0CustMaster.cs

@@ -7,7 +7,7 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
 [SugarIndex("uk_CustMaster_factory_cust", nameof(FactoryRefId), OrderByType.Asc, nameof(Cust), OrderByType.Asc, IsUnique = true)]
 public class AdoS0CustMaster
 {
-    [SugarColumn(ColumnName = "rec_id", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnName = "company_ref_id", ColumnDescription = "关联公司 ID", ColumnDataType = "bigint")]
@@ -16,54 +16,69 @@ public class AdoS0CustMaster
     [SugarColumn(ColumnName = "factory_ref_id", ColumnDescription = "关联工厂 ID", ColumnDataType = "bigint")]
     public long FactoryRefId { get; set; }
 
-    [SugarColumn(ColumnName = "domain_code", ColumnDescription = "工厂域编码", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50, IsNullable = true)]
     public string? DomainCode { get; set; }
 
-    [SugarColumn(ColumnName = "cust", ColumnDescription = "客户编码", Length = 100)]
+    [SugarColumn(ColumnName = "Cust", ColumnDescription = "客户编码", Length = 100)]
     public string Cust { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "sort_name", ColumnDescription = "客户简称", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "SortName", ColumnDescription = "客户简称", Length = 200, IsNullable = true)]
     public string? SortName { get; set; }
 
-    [SugarColumn(ColumnName = "curr", ColumnDescription = "币种", Length = 20, IsNullable = true)]
+    [SugarColumn(ColumnName = "Curr", ColumnDescription = "币种", Length = 20, IsNullable = true)]
     public string? Curr { get; set; }
 
-    [SugarColumn(ColumnName = "slspsn1", ColumnDescription = "主销售员", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Slspsn1", ColumnDescription = "主销售员", Length = 100, IsNullable = true)]
     public string? Slspsn1 { get; set; }
 
-    [SugarColumn(ColumnName = "slspsn2", ColumnDescription = "备用销售员", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Slspsn2", ColumnDescription = "备用销售员", Length = 100, IsNullable = true)]
     public string? Slspsn2 { get; set; }
 
-    [SugarColumn(ColumnName = "ship_to", ColumnDescription = "发至", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "ShipTo", ColumnDescription = "发至", Length = 200, IsNullable = true)]
     public string? ShipTo { get; set; }
 
-    [SugarColumn(ColumnName = "tax_class", ColumnDescription = "税类型", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "TaxClass", ColumnDescription = "税类型", Length = 50, IsNullable = true)]
     public string? TaxClass { get; set; }
 
-    [SugarColumn(ColumnName = "tax_in", ColumnDescription = "是否含税", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "TaxIn", ColumnDescription = "是否含税", ColumnDataType = "boolean")]
     public bool TaxIn { get; set; } = true;
 
     [SugarColumn(ColumnName = "cust_class", ColumnDescription = "客户级别", Length = 50, IsNullable = true)]
     public string? CustClass { get; set; }
 
-    [SugarColumn(ColumnName = "remarks", ColumnDescription = "备注", Length = 1000, IsNullable = true)]
+    [SugarColumn(ColumnName = "address", ColumnDescription = "地址", Length = 500, IsNullable = true)]
+    public string? Address { get; set; }
+
+    [SugarColumn(ColumnName = "contact", ColumnDescription = "联系人", Length = 200, IsNullable = true)]
+    public string? Contact { get; set; }
+
+    [SugarColumn(ColumnName = "position", ColumnDescription = "职务", Length = 200, IsNullable = true)]
+    public string? Position { get; set; }
+
+    [SugarColumn(ColumnName = "contact_info", ColumnDescription = "联系方式", Length = 200, IsNullable = true)]
+    public string? ContactInfo { get; set; }
+
+    [SugarColumn(ColumnName = "CreditRating", ColumnDescription = "信用评级", Length = 50, IsNullable = true)]
+    public string? CreditRating { get; set; }
+
+    [SugarColumn(ColumnName = "Remarks", ColumnDescription = "备注", Length = 1000, IsNullable = true)]
     public string? Remarks { get; set; }
 
-    [SugarColumn(ColumnName = "is_active", ColumnDescription = "是否启用", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsActive", ColumnDescription = "是否启用", ColumnDataType = "boolean")]
     public bool IsActive { get; set; } = true;
 
-    [SugarColumn(ColumnName = "is_confirm", ColumnDescription = "是否确认", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsConfirm", ColumnDescription = "是否确认", ColumnDataType = "boolean")]
     public bool IsConfirm { get; set; }
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间")]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")]
     public DateTime CreateTime { get; set; } = DateTime.Now;
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 }

+ 60 - 51
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/AdoS0ItemMaster.cs

@@ -7,7 +7,7 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
 [SugarIndex("uk_ItemMaster_factory_item", nameof(FactoryRefId), OrderByType.Asc, nameof(ItemNum), OrderByType.Asc, IsUnique = true)]
 public class AdoS0ItemMaster
 {
-    [SugarColumn(ColumnName = "rec_id", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnName = "company_ref_id", ColumnDescription = "关联公司 ID", ColumnDataType = "bigint")]
@@ -16,139 +16,139 @@ public class AdoS0ItemMaster
     [SugarColumn(ColumnName = "factory_ref_id", ColumnDescription = "关联工厂 ID", ColumnDataType = "bigint")]
     public long FactoryRefId { get; set; }
 
-    [SugarColumn(ColumnName = "domain_code", ColumnDescription = "工厂域编码", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50, IsNullable = true)]
     public string? DomainCode { get; set; }
 
-    [SugarColumn(ColumnName = "item_num", ColumnDescription = "物料编码", Length = 100)]
+    [SugarColumn(ColumnName = "ItemNum", ColumnDescription = "物料编码", Length = 100)]
     public string ItemNum { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "descr", ColumnDescription = "物料名称", Length = 200)]
+    [SugarColumn(ColumnName = "Descr", ColumnDescription = "物料名称", Length = 200)]
     public string Descr { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "descr1", ColumnDescription = "规格型号", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "Descr1", ColumnDescription = "规格型号", Length = 200, IsNullable = true)]
     public string? Descr1 { get; set; }
 
-    [SugarColumn(ColumnName = "pkg_code", ColumnDescription = "语种", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "PkgCode", ColumnDescription = "语种", Length = 50, IsNullable = true)]
     public string? PkgCode { get; set; }
 
-    [SugarColumn(ColumnName = "um", ColumnDescription = "单位", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "UM", ColumnDescription = "单位", Length = 50, IsNullable = true)]
     public string? UM { get; set; }
 
-    [SugarColumn(ColumnName = "pur_mfg", ColumnDescription = "P/L件", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "PurMfg", ColumnDescription = "P/L件", Length = 100, IsNullable = true)]
     public string? PurMfg { get; set; }
 
-    [SugarColumn(ColumnName = "drawing", ColumnDescription = "图号", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Drawing", ColumnDescription = "图号", Length = 100, IsNullable = true)]
     public string? Drawing { get; set; }
 
-    [SugarColumn(ColumnName = "item_type", ColumnDescription = "物料类型", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "ItemType", ColumnDescription = "物料类型", Length = 100, IsNullable = true)]
     public string? ItemType { get; set; }
 
-    [SugarColumn(ColumnName = "location", ColumnDescription = "默认库位", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Location", ColumnDescription = "默认库位", Length = 100, IsNullable = true)]
     public string? Location { get; set; }
 
-    [SugarColumn(ColumnName = "default_shelf", ColumnDescription = "默认货架", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "DefaultShelf", ColumnDescription = "默认货架", Length = 100, IsNullable = true)]
     public string? DefaultShelf { get; set; }
 
-    [SugarColumn(ColumnName = "key_item", ColumnDescription = "关键物料", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "KeyItem", ColumnDescription = "关键物料", ColumnDataType = "boolean")]
     public bool KeyItem { get; set; }
 
-    [SugarColumn(ColumnName = "net_weight", ColumnDescription = "净重", ColumnDataType = "decimal(18,5)", IsNullable = true)]
+    [SugarColumn(ColumnName = "NetWeight", ColumnDescription = "净重", ColumnDataType = "decimal(18,5)", IsNullable = true)]
     public decimal? NetWeight { get; set; }
 
-    [SugarColumn(ColumnName = "net_weight_um", ColumnDescription = "净重单位", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "NetWeightUM", ColumnDescription = "净重单位", Length = 50, IsNullable = true)]
     public string? NetWeightUM { get; set; }
 
-    [SugarColumn(ColumnName = "inspect", ColumnDescription = "检验", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "Inspect", ColumnDescription = "检验", ColumnDataType = "boolean")]
     public bool Inspect { get; set; }
 
-    [SugarColumn(ColumnName = "pur_lt", ColumnDescription = "供应提前期", IsNullable = true)]
+    [SugarColumn(ColumnName = "PurLT", ColumnDescription = "供应提前期", IsNullable = true)]
     public int? PurLT { get; set; }
 
-    [SugarColumn(ColumnName = "ins_lt", ColumnDescription = "检验天数", IsNullable = true)]
+    [SugarColumn(ColumnName = "InsLT", ColumnDescription = "检验天数", IsNullable = true)]
     public int? InsLT { get; set; }
 
-    [SugarColumn(ColumnName = "mfg_lt", ColumnDescription = "备料提前期", IsNullable = true)]
+    [SugarColumn(ColumnName = "MfgLT", ColumnDescription = "备料提前期", IsNullable = true)]
     public int? MfgLT { get; set; }
 
-    [SugarColumn(ColumnName = "length", ColumnDescription = "长度", ColumnDataType = "decimal(18,5)", IsNullable = true)]
+    [SugarColumn(ColumnName = "Length", ColumnDescription = "长度", ColumnDataType = "decimal(18,5)", IsNullable = true)]
     public decimal? Length { get; set; }
 
-    [SugarColumn(ColumnName = "size", ColumnDescription = "尺寸", ColumnDataType = "decimal(18,5)", IsNullable = true)]
+    [SugarColumn(ColumnName = "Size", ColumnDescription = "尺寸", ColumnDataType = "decimal(18,5)", IsNullable = true)]
     public decimal? Size { get; set; }
 
-    [SugarColumn(ColumnName = "size_um", ColumnDescription = "尺寸单位", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "SizeUM", ColumnDescription = "尺寸单位", Length = 50, IsNullable = true)]
     public string? SizeUM { get; set; }
 
-    [SugarColumn(ColumnName = "issue_policy", ColumnDescription = "是否前处理", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IssuePolicy", ColumnDescription = "是否前处理", ColumnDataType = "boolean")]
     public bool IssuePolicy { get; set; }
 
-    [SugarColumn(ColumnName = "mfgmttr", ColumnDescription = "备料提前期(天)", IsNullable = true)]
+    [SugarColumn(ColumnName = "MFGMTTR", ColumnDescription = "备料提前期(天)", IsNullable = true)]
     public int? MfgMttr { get; set; }
 
-    [SugarColumn(ColumnName = "location_type", ColumnDescription = "库存类型", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "LocationType", ColumnDescription = "库存类型", Length = 50, IsNullable = true)]
     public string? LocationType { get; set; }
 
-    [SugarColumn(ColumnName = "commodity_code", ColumnDescription = "商品代码", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "CommodityCode", ColumnDescription = "商品代码", Length = 100, IsNullable = true)]
     public string? CommodityCode { get; set; }
 
-    [SugarColumn(ColumnName = "rev", ColumnDescription = "版本", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "Rev", ColumnDescription = "版本", Length = 50, IsNullable = true)]
     public string? Rev { get; set; }
 
-    [SugarColumn(ColumnName = "max_ord", ColumnDescription = "最大订单", ColumnDataType = "decimal(18,5)", IsNullable = true)]
+    [SugarColumn(ColumnName = "MaxOrd", ColumnDescription = "最大订单", ColumnDataType = "decimal(18,5)", IsNullable = true)]
     public decimal? MaxOrd { get; set; }
 
-    [SugarColumn(ColumnName = "min_ord", ColumnDescription = "最小订单", ColumnDataType = "decimal(18,5)", IsNullable = true)]
+    [SugarColumn(ColumnName = "MinOrd", ColumnDescription = "最小订单", ColumnDataType = "decimal(18,5)", IsNullable = true)]
     public decimal? MinOrd { get; set; }
 
-    [SugarColumn(ColumnName = "ord_mult", ColumnDescription = "订单倍数", ColumnDataType = "decimal(18,5)", IsNullable = true)]
+    [SugarColumn(ColumnName = "OrdMult", ColumnDescription = "订单倍数", ColumnDataType = "decimal(18,5)", IsNullable = true)]
     public decimal? OrdMult { get; set; }
 
-    [SugarColumn(ColumnName = "min_ord_sales", ColumnDescription = "起订量", ColumnDataType = "decimal(18,5)", IsNullable = true)]
+    [SugarColumn(ColumnName = "MinOrdSales", ColumnDescription = "起订量", ColumnDataType = "decimal(18,5)", IsNullable = true)]
     public decimal? MinOrdSales { get; set; }
 
-    [SugarColumn(ColumnName = "auto_lot_nums", ColumnDescription = "自动批次号", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "AutoLotNums", ColumnDescription = "自动批次号", ColumnDataType = "boolean")]
     public bool AutoLotNums { get; set; }
 
-    [SugarColumn(ColumnName = "install", ColumnDescription = "是否打标", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "Install", ColumnDescription = "是否打标", ColumnDataType = "boolean")]
     public bool Install { get; set; }
 
-    [SugarColumn(ColumnName = "safety_stk", ColumnDescription = "安全库存", ColumnDataType = "decimal(18,5)", IsNullable = true)]
+    [SugarColumn(ColumnName = "SafetyStk", ColumnDescription = "安全库存", ColumnDataType = "decimal(18,5)", IsNullable = true)]
     public decimal? SafetyStk { get; set; }
 
-    [SugarColumn(ColumnName = "days_between_pm", ColumnDescription = "保质期(天)", IsNullable = true)]
+    [SugarColumn(ColumnName = "DaysBetweenPM", ColumnDescription = "保质期(天)", IsNullable = true)]
     public int? DaysBetweenPM { get; set; }
 
     [SugarColumn(ColumnName = "expire_alarm_day", ColumnDescription = "过期预警天数", IsNullable = true)]
     public int? ExpireAlarmDay { get; set; }
 
-    [SugarColumn(ColumnName = "stock_turn_over", ColumnDescription = "存货周转率", ColumnDataType = "decimal(18,5)", IsNullable = true)]
+    [SugarColumn(ColumnName = "StockTurnOver", ColumnDescription = "存货周转率", ColumnDataType = "decimal(18,5)", IsNullable = true)]
     public decimal? StockTurnOver { get; set; }
 
-    [SugarColumn(ColumnName = "lot_serial_control", ColumnDescription = "批次先进先出提醒", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "LotSerialControl", ColumnDescription = "批次先进先出提醒", ColumnDataType = "boolean")]
     public bool LotSerialControl { get; set; }
 
-    [SugarColumn(ColumnName = "allocate_single_lot", ColumnDescription = "批次先进先出强控", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "AllocateSingleLot", ColumnDescription = "批次先进先出强控", ColumnDataType = "boolean")]
     public bool AllocateSingleLot { get; set; }
 
-    [SugarColumn(ColumnName = "status", ColumnDescription = "状态", Length = 50)]
+    [SugarColumn(ColumnName = "Status", ColumnDescription = "状态", Length = 50)]
     public string Status { get; set; } = "normal";
 
-    [SugarColumn(ColumnName = "planner", ColumnDescription = "计划员", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Planner", ColumnDescription = "计划员", Length = 100, IsNullable = true)]
     public string? Planner { get; set; }
 
-    [SugarColumn(ColumnName = "trace_detail", ColumnDescription = "按需发料", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "TraceDetail", ColumnDescription = "按需发料", ColumnDataType = "boolean")]
     public bool TraceDetail { get; set; }
 
-    [SugarColumn(ColumnName = "is_main_mas", ColumnDescription = "是否主材", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsMainMas", ColumnDescription = "是否主材", ColumnDataType = "boolean")]
     public bool IsMainMas { get; set; }
 
-    [SugarColumn(ColumnName = "remark", ColumnDescription = "备注", Length = 1000, IsNullable = true)]
+    [SugarColumn(ColumnName = "Remark", ColumnDescription = "备注", Length = 1000, IsNullable = true)]
     public string? Remark { get; set; }
 
-    [SugarColumn(ColumnName = "emt_type", ColumnDescription = "特殊采购类型", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "EMTType", ColumnDescription = "特殊采购类型", Length = 50, IsNullable = true)]
     public string? EMTType { get; set; }
 
-    [SugarColumn(ColumnName = "owner_application", ColumnDescription = "物料属性", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "OwnerApplication", ColumnDescription = "物料属性", Length = 100, IsNullable = true)]
     public string? OwnerApplication { get; set; }
 
     [SugarColumn(ColumnName = "bom_design", ColumnDescription = "BOM设计周期(天)", IsNullable = true)]
@@ -157,22 +157,31 @@ public class AdoS0ItemMaster
     [SugarColumn(ColumnName = "routing_des", ColumnDescription = "工艺设计周期(天)", IsNullable = true)]
     public int? RoutingDes { get; set; }
 
-    [SugarColumn(ColumnName = "is_active", ColumnDescription = "是否生效", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "cust_supplied", ColumnDescription = "客供件", ColumnDataType = "boolean")]
+    public bool CustSupplied { get; set; }
+
+    [SugarColumn(ColumnName = "drawing_design", ColumnDescription = "图纸设计周期(小时)", IsNullable = true)]
+    public int? DrawingDesign { get; set; }
+
+    [SugarColumn(ColumnName = "design_owner", ColumnDescription = "设计负责人", Length = 100, IsNullable = true)]
+    public string? DesignOwner { get; set; }
+
+    [SugarColumn(ColumnName = "IsActive", ColumnDescription = "是否生效", ColumnDataType = "boolean")]
     public bool IsActive { get; set; } = true;
 
-    [SugarColumn(ColumnName = "is_confirm", ColumnDescription = "是否确认", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsConfirm", ColumnDescription = "是否确认", ColumnDataType = "boolean")]
     public bool IsConfirm { get; set; }
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间")]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")]
     public DateTime CreateTime { get; set; } = DateTime.Now;
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 
     /// <summary>列表展示:库位+库位说明拼接(不落库;无 LocationMaster 时仅库位)</summary>

+ 11 - 11
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/AdoS0ItemSubstituteDetail.cs

@@ -6,37 +6,37 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
 [SugarTable("ItemSubstituteDetail", "物料替代关系表")]
 public class AdoS0ItemSubstituteDetail
 {
-    [SugarColumn(ColumnDescription = "物料替代关系主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "物料替代关系主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
-    [SugarColumn(ColumnDescription = "父级物料编码", Length = 100)]
+    [SugarColumn(ColumnName = "ParentItem", ColumnDescription = "父级物料编码", Length = 100)]
     public string ParentItem { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnDescription = "物料编码", Length = 100)]
+    [SugarColumn(ColumnName = "ItemNum", ColumnDescription = "物料编码", Length = 100)]
     public string ItemNum { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnDescription = "替代物料编码", Length = 100)]
+    [SugarColumn(ColumnName = "SubstituteItem", ColumnDescription = "替代物料编码", Length = 100)]
     public string SubstituteItem { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnDescription = "替代数量", DecimalDigits = 6, Length = 18, IsNullable = true)]
+    [SugarColumn(ColumnName = "SubstituteQty", ColumnDescription = "替代数量", DecimalDigits = 6, Length = 18, IsNullable = true)]
     public decimal? SubstituteQty { get; set; }
 
-    [SugarColumn(ColumnDescription = "备注", Length = 255, IsNullable = true)]
+    [SugarColumn(ColumnName = "Remark", ColumnDescription = "备注", Length = 255, IsNullable = true)]
     public string? Remark { get; set; }
 
-    [SugarColumn(ColumnDescription = "工厂域编码", Length = 50)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50)]
     public string Domain { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnDescription = "创建人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 64, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnDescription = "创建时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间", IsNullable = true)]
     public DateTime? CreateTime { get; set; }
 
-    [SugarColumn(ColumnDescription = "更新人", Length = 64, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 64, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 }
 

+ 41 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/AdoS0OrderReviewCycle.cs

@@ -0,0 +1,41 @@
+namespace Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
+
+[SugarTable("S0OrderReviewCycle", "订单评审周期标准")]
+[SugarIndex("uk_S0OrderReviewCycle_factory",
+    nameof(FactoryRefId), OrderByType.Asc,
+    IsUnique = true)]
+public class AdoS0OrderReviewCycle
+{
+    [SugarColumn(ColumnName = "rec_id", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    public long Id { get; set; }
+
+    [SugarColumn(ColumnName = "company_ref_id", ColumnDataType = "bigint")]
+    public long CompanyRefId { get; set; }
+
+    [SugarColumn(ColumnName = "factory_ref_id", ColumnDataType = "bigint")]
+    public long FactoryRefId { get; set; }
+
+    [SugarColumn(ColumnName = "domain_code", Length = 50, IsNullable = true)]
+    public string? DomainCode { get; set; }
+
+    [SugarColumn(ColumnName = "std_hours")]
+    public int StdHours { get; set; }
+
+    [SugarColumn(ColumnName = "remarks", Length = 500, IsNullable = true)]
+    public string? Remarks { get; set; }
+
+    [SugarColumn(ColumnName = "is_active", ColumnDataType = "boolean")]
+    public bool IsActive { get; set; } = true;
+
+    [SugarColumn(ColumnName = "create_user", Length = 100, IsNullable = true)]
+    public string? CreateUser { get; set; }
+
+    [SugarColumn(ColumnName = "create_time")]
+    public DateTime CreateTime { get; set; } = DateTime.Now;
+
+    [SugarColumn(ColumnName = "update_user", Length = 100, IsNullable = true)]
+    public string? UpdateUser { get; set; }
+
+    [SugarColumn(ColumnName = "update_time", IsNullable = true)]
+    public DateTime? UpdateTime { get; set; }
+}

+ 16 - 16
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/AdoS0PriorityCode.cs

@@ -6,7 +6,7 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
 [SugarTable("PriorityCode", "订单优先级配置(复刻 PriorityCode)")]
 public class AdoS0PriorityCode
 {
-    [SugarColumn(ColumnName = "rec_id", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnName = "company_ref_id", ColumnDescription = "关联公司 ID", ColumnDataType = "bigint")]
@@ -15,53 +15,53 @@ public class AdoS0PriorityCode
     [SugarColumn(ColumnName = "factory_ref_id", ColumnDescription = "关联工厂 ID", ColumnDataType = "bigint")]
     public long FactoryRefId { get; set; }
 
-    [SugarColumn(ColumnName = "domain_code", ColumnDescription = "工厂域编码", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50, IsNullable = true)]
     public string? DomainCode { get; set; }
 
-    [SugarColumn(ColumnName = "descr", ColumnDescription = "名称", Length = 200)]
+    [SugarColumn(ColumnName = "Descr", ColumnDescription = "名称", Length = 200)]
     public string Descr { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "value", ColumnDescription = "工单表字段", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "Value", ColumnDescription = "工单表字段", Length = 200, IsNullable = true)]
     public string? Value { get; set; }
 
-    [SugarColumn(ColumnName = "priority", ColumnDescription = "优先级")]
+    [SugarColumn(ColumnName = "Priority", ColumnDescription = "优先级")]
     public int Priority { get; set; }
 
     /// <summary>源系统 OrderBy:0=倒序,非0=正序</summary>
     [SugarColumn(ColumnName = "order_by_code", ColumnDescription = "排序方式编码")]
     public int OrderByCode { get; set; } = 1;
 
-    [SugarColumn(ColumnName = "source_table", ColumnDescription = "来源表", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "SourceTable", ColumnDescription = "来源表", Length = 200, IsNullable = true)]
     public string? SourceTable { get; set; }
 
-    [SugarColumn(ColumnName = "source_column", ColumnDescription = "来源表字段", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "SourceColumn", ColumnDescription = "来源表字段", Length = 200, IsNullable = true)]
     public string? SourceColumn { get; set; }
 
-    [SugarColumn(ColumnName = "source_type", ColumnDescription = "来源表字段类型", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "SourceType", ColumnDescription = "来源表字段类型", Length = 100, IsNullable = true)]
     public string? SourceType { get; set; }
 
-    [SugarColumn(ColumnName = "source_id", ColumnDescription = "来源表关联字段", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "SourceID", ColumnDescription = "来源表关联字段", Length = 200, IsNullable = true)]
     public string? SourceId { get; set; }
 
-    [SugarColumn(ColumnName = "value_type", ColumnDescription = "工单表字段类型", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "ValueType", ColumnDescription = "工单表字段类型", Length = 100, IsNullable = true)]
     public string? ValueType { get; set; }
 
-    [SugarColumn(ColumnName = "value_id", ColumnDescription = "工单表关联字段", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "ValueID", ColumnDescription = "工单表关联字段", Length = 200, IsNullable = true)]
     public string? ValueId { get; set; }
 
-    [SugarColumn(ColumnName = "is_active", ColumnDescription = "是否生效", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsActive", ColumnDescription = "是否生效", ColumnDataType = "boolean")]
     public bool IsActive { get; set; } = true;
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间")]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")]
     public DateTime CreateTime { get; set; } = DateTime.Now;
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 
     /// <summary>列表展示:正序/倒序(不落库)</summary>

+ 46 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/AdoS0ProductDesignCycle.cs

@@ -0,0 +1,46 @@
+namespace Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
+
+[SugarTable("S0ProductDesignCycle", "产品设计周期标准")]
+[SugarIndex("uk_S0ProductDesignCycle_factory_type_attr",
+    nameof(FactoryRefId), OrderByType.Asc,
+    nameof(ItemType), OrderByType.Asc,
+    nameof(OwnerApplication), OrderByType.Asc,
+    IsUnique = true)]
+public class AdoS0ProductDesignCycle
+{
+    [SugarColumn(ColumnName = "rec_id", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    public long Id { get; set; }
+
+    [SugarColumn(ColumnName = "company_ref_id", ColumnDataType = "bigint")]
+    public long CompanyRefId { get; set; }
+
+    [SugarColumn(ColumnName = "factory_ref_id", ColumnDataType = "bigint")]
+    public long FactoryRefId { get; set; }
+
+    [SugarColumn(ColumnName = "domain_code", Length = 50, IsNullable = true)]
+    public string? DomainCode { get; set; }
+
+    [SugarColumn(ColumnName = "item_type", Length = 100)]
+    public string ItemType { get; set; } = string.Empty;
+
+    [SugarColumn(ColumnName = "owner_application", Length = 100)]
+    public string OwnerApplication { get; set; } = string.Empty;
+
+    [SugarColumn(ColumnName = "std_hours")]
+    public int StdHours { get; set; }
+
+    [SugarColumn(ColumnName = "is_active", ColumnDataType = "boolean")]
+    public bool IsActive { get; set; } = true;
+
+    [SugarColumn(ColumnName = "create_user", Length = 100, IsNullable = true)]
+    public string? CreateUser { get; set; }
+
+    [SugarColumn(ColumnName = "create_time")]
+    public DateTime CreateTime { get; set; } = DateTime.Now;
+
+    [SugarColumn(ColumnName = "update_user", Length = 100, IsNullable = true)]
+    public string? UpdateUser { get; set; }
+
+    [SugarColumn(ColumnName = "update_time", IsNullable = true)]
+    public DateTime? UpdateTime { get; set; }
+}

+ 1 - 1
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Sales/IcSubstitute.cs

@@ -7,7 +7,7 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Sales;
 [SugarTable("ic_substitute", "替代方案(源平台表映射)")]
 public class IcSubstitute
 {
-    [SugarColumn(ColumnName = "id", ColumnDescription = "主键", IsPrimaryKey = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "Id", ColumnDescription = "主键", IsPrimaryKey = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnName = "substitute_code", ColumnDescription = "替代方案编码", IsNullable = true, Length = 100)]

+ 2 - 2
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Supply/AdoS0SrmPurchase.cs

@@ -6,7 +6,7 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Supply;
 [SugarTable("srm_purchase", "货源清单(srm_purchase)")]
 public class AdoS0SrmPurchase
 {
-    [SugarColumn(ColumnName = "id", ColumnDescription = "货源清单主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "Id", ColumnDescription = "货源清单主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnName = "company_ref_id", ColumnDescription = "公司 ID", ColumnDataType = "bigint")]
@@ -81,7 +81,7 @@ public class AdoS0SrmPurchase
     [SugarColumn(ColumnName = "order_rector_num", ColumnDescription = "下单负责人编号", Length = 100, IsNullable = true)]
     public string? OrderRectorNum { get; set; }
 
-    [SugarColumn(ColumnName = "is_require_goods", ColumnDescription = "采购类型(需到货)", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsRequireGoods", ColumnDescription = "采购类型(需到货)", ColumnDataType = "boolean")]
     public bool IsRequireGoods { get; set; }
 
     [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 100, IsNullable = true)]

+ 20 - 20
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Supply/AdoS0SuppMaster.cs

@@ -7,7 +7,7 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Supply;
 [SugarIndex("uk_SuppMaster_factory_supp", nameof(FactoryRefId), OrderByType.Asc, nameof(Supp), OrderByType.Asc, IsUnique = true)]
 public class AdoS0SuppMaster
 {
-    [SugarColumn(ColumnName = "rec_id", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnName = "company_ref_id", ColumnDescription = "关联公司 ID", ColumnDataType = "bigint")]
@@ -16,62 +16,62 @@ public class AdoS0SuppMaster
     [SugarColumn(ColumnName = "factory_ref_id", ColumnDescription = "关联工厂 ID", ColumnDataType = "bigint")]
     public long FactoryRefId { get; set; }
 
-    [SugarColumn(ColumnName = "domain_code", ColumnDescription = "工厂域编码", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50, IsNullable = true)]
     public string? DomainCode { get; set; }
 
-    [SugarColumn(ColumnName = "supp", ColumnDescription = "供应商编码", Length = 100)]
+    [SugarColumn(ColumnName = "Supp", ColumnDescription = "供应商编码", Length = 100)]
     public string Supp { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "sort_name", ColumnDescription = "简称", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "SortName", ColumnDescription = "简称", Length = 200, IsNullable = true)]
     public string? SortName { get; set; }
 
     /// <summary>供应商库位(原 PurContact)</summary>
-    [SugarColumn(ColumnName = "pur_contact", ColumnDescription = "供应商库位", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "PurContact", ColumnDescription = "供应商库位", Length = 100, IsNullable = true)]
     public string? PurContact { get; set; }
 
-    [SugarColumn(ColumnName = "cr_terms", ColumnDescription = "支付方式", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "CrTerms", ColumnDescription = "支付方式", Length = 50, IsNullable = true)]
     public string? CrTerms { get; set; }
 
-    [SugarColumn(ColumnName = "curr", ColumnDescription = "币别", Length = 20, IsNullable = true)]
+    [SugarColumn(ColumnName = "Curr", ColumnDescription = "币别", Length = 20, IsNullable = true)]
     public string? Curr { get; set; }
 
-    [SugarColumn(ColumnName = "type", ColumnDescription = "供应商类型", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "Type", ColumnDescription = "供应商类型", Length = 50, IsNullable = true)]
     public string? Type { get; set; }
 
-    [SugarColumn(ColumnName = "tax_in", ColumnDescription = "含税", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "TaxIn", ColumnDescription = "含税", ColumnDataType = "boolean")]
     public bool TaxIn { get; set; } = true;
 
-    [SugarColumn(ColumnName = "tax_class", ColumnDescription = "税类型", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "TaxClass", ColumnDescription = "税类型", Length = 50, IsNullable = true)]
     public string? TaxClass { get; set; }
 
-    [SugarColumn(ColumnName = "ap_start_day1", ColumnDescription = "对账周期1开始", IsNullable = true)]
+    [SugarColumn(ColumnName = "APStartDay1", ColumnDescription = "对账周期1开始", IsNullable = true)]
     public int? APStartDay1 { get; set; }
 
-    [SugarColumn(ColumnName = "ap_end_day1", ColumnDescription = "对账周期1结束", IsNullable = true)]
+    [SugarColumn(ColumnName = "APEndDay1", ColumnDescription = "对账周期1结束", IsNullable = true)]
     public int? APEndDay1 { get; set; }
 
-    [SugarColumn(ColumnName = "ap_start_day2", ColumnDescription = "对账周期2开始", IsNullable = true)]
+    [SugarColumn(ColumnName = "APStartDay2", ColumnDescription = "对账周期2开始", IsNullable = true)]
     public int? APStartDay2 { get; set; }
 
-    [SugarColumn(ColumnName = "ap_end_day2", ColumnDescription = "对账周期2结束", IsNullable = true)]
+    [SugarColumn(ColumnName = "APEndDay2", ColumnDescription = "对账周期2结束", IsNullable = true)]
     public int? APEndDay2 { get; set; }
 
-    [SugarColumn(ColumnName = "remark", ColumnDescription = "备注", Length = 1000, IsNullable = true)]
+    [SugarColumn(ColumnName = "Remark", ColumnDescription = "备注", Length = 1000, IsNullable = true)]
     public string? Remark { get; set; }
 
-    [SugarColumn(ColumnName = "is_active", ColumnDescription = "是否启用", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsActive", ColumnDescription = "是否启用", ColumnDataType = "boolean")]
     public bool IsActive { get; set; } = true;
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间")]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")]
     public DateTime CreateTime { get; set; } = DateTime.Now;
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 
     /// <summary>列表展示:库位+库位说明拼接(展示字段,不落库)</summary>

+ 10 - 10
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Warehouse/AdoS0CostCtrMaster.cs

@@ -7,7 +7,7 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Warehouse;
 [SugarIndex("uk_CostCtrMaster_domain_code", nameof(DomainCode), OrderByType.Asc, nameof(CostCtr), OrderByType.Asc, IsUnique = true)]
 public class AdoS0CostCtrMaster
 {
-    [SugarColumn(ColumnName = "rec_id", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnName = "company_ref_id", ColumnDescription = "关联公司 ID", ColumnDataType = "bigint")]
@@ -16,30 +16,30 @@ public class AdoS0CostCtrMaster
     [SugarColumn(ColumnName = "factory_ref_id", ColumnDescription = "关联工厂 ID", ColumnDataType = "bigint")]
     public long FactoryRefId { get; set; }
 
-    [SugarColumn(ColumnName = "domain_code", ColumnDescription = "工厂域编码", Length = 50)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50)]
     public string DomainCode { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "cost_ctr", ColumnDescription = "成本中心编码", Length = 100)]
+    [SugarColumn(ColumnName = "CostCtr", ColumnDescription = "成本中心编码", Length = 100)]
     public string CostCtr { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "descr", ColumnDescription = "说明", Length = 255, IsNullable = true)]
+    [SugarColumn(ColumnName = "Descr", ColumnDescription = "说明", Length = 255, IsNullable = true)]
     public string? Descr { get; set; }
 
-    [SugarColumn(ColumnName = "ufld1", ColumnDescription = "自定义字段1", Length = 255, IsNullable = true)]
+    [SugarColumn(ColumnName = "Ufld1", ColumnDescription = "自定义字段1", Length = 255, IsNullable = true)]
     public string? Ufld1 { get; set; }
 
-    [SugarColumn(ColumnName = "eff_time", ColumnDescription = "生效时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "EffTime", ColumnDescription = "生效时间", IsNullable = true)]
     public DateTime? EffTime { get; set; }
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间")]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")]
     public DateTime CreateTime { get; set; } = DateTime.Now;
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 }

+ 9 - 9
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Warehouse/AdoS0DepartmentMaster.cs

@@ -7,7 +7,7 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Warehouse;
 [SugarIndex("uk_DepartmentMaster_domain_dept", nameof(DomainCode), OrderByType.Asc, nameof(Department), OrderByType.Asc, IsUnique = true)]
 public class AdoS0DepartmentMaster
 {
-    [SugarColumn(ColumnName = "rec_id", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnName = "company_ref_id", ColumnDescription = "关联公司 ID", ColumnDataType = "bigint")]
@@ -16,27 +16,27 @@ public class AdoS0DepartmentMaster
     [SugarColumn(ColumnName = "factory_ref_id", ColumnDescription = "关联工厂 ID", ColumnDataType = "bigint")]
     public long FactoryRefId { get; set; }
 
-    [SugarColumn(ColumnName = "domain_code", ColumnDescription = "工厂域编码", Length = 50)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50)]
     public string DomainCode { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "department", ColumnDescription = "部门编码", Length = 100)]
+    [SugarColumn(ColumnName = "Department", ColumnDescription = "部门编码", Length = 100)]
     public string Department { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "descr", ColumnDescription = "部门说明", Length = 255, IsNullable = true)]
+    [SugarColumn(ColumnName = "Descr", ColumnDescription = "部门说明", Length = 255, IsNullable = true)]
     public string? Descr { get; set; }
 
-    [SugarColumn(ColumnName = "is_active", ColumnDescription = "是否启用", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsActive", ColumnDescription = "是否启用", ColumnDataType = "boolean")]
     public bool IsActive { get; set; } = true;
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间")]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")]
     public DateTime CreateTime { get; set; } = DateTime.Now;
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 }

+ 14 - 14
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Warehouse/AdoS0EmpWorkDutyMaster.cs

@@ -6,7 +6,7 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Warehouse;
 [SugarTable("EmpWorkDutyMaster", "物料职责主数据(复刻 EmpWorkDutyMaster)")]
 public class AdoS0EmpWorkDutyMaster
 {
-    [SugarColumn(ColumnName = "rec_id", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnName = "company_ref_id", ColumnDescription = "关联公司 ID", ColumnDataType = "bigint")]
@@ -15,43 +15,43 @@ public class AdoS0EmpWorkDutyMaster
     [SugarColumn(ColumnName = "factory_ref_id", ColumnDescription = "关联工厂 ID", ColumnDataType = "bigint")]
     public long FactoryRefId { get; set; }
 
-    [SugarColumn(ColumnName = "domain_code", ColumnDescription = "工厂域编码", Length = 50)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50)]
     public string DomainCode { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "employee", ColumnDescription = "员工编码", Length = 100)]
+    [SugarColumn(ColumnName = "Employee", ColumnDescription = "员工编码", Length = 100)]
     public string Employee { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "item_num1", ColumnDescription = "物料编码起", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "ItemNum1", ColumnDescription = "物料编码起", Length = 100, IsNullable = true)]
     public string? ItemNum1 { get; set; }
 
-    [SugarColumn(ColumnName = "item_num2", ColumnDescription = "物料编码止", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "ItemNum2", ColumnDescription = "物料编码止", Length = 100, IsNullable = true)]
     public string? ItemNum2 { get; set; }
 
-    [SugarColumn(ColumnName = "location", ColumnDescription = "库位编码", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Location", ColumnDescription = "库位编码", Length = 100, IsNullable = true)]
     public string? Location { get; set; }
 
-    [SugarColumn(ColumnName = "prod_line", ColumnDescription = "生产线编码", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "ProdLine", ColumnDescription = "生产线编码", Length = 100, IsNullable = true)]
     public string? ProdLine { get; set; }
 
-    [SugarColumn(ColumnName = "duty", ColumnDescription = "职责编码(原始值)", Length = 500, IsNullable = true)]
+    [SugarColumn(ColumnName = "Duty", ColumnDescription = "职责编码(原始值)", Length = 500, IsNullable = true)]
     public string? Duty { get; set; }
 
-    [SugarColumn(ColumnName = "emp_type", ColumnDescription = "员工类型", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "EmpType", ColumnDescription = "员工类型", Length = 50, IsNullable = true)]
     public string? EmpType { get; set; }
 
-    [SugarColumn(ColumnName = "ufld2", ColumnDescription = "自定义字段2", Length = 255, IsNullable = true)]
+    [SugarColumn(ColumnName = "Ufld2", ColumnDescription = "自定义字段2", Length = 255, IsNullable = true)]
     public string? Ufld2 { get; set; }
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间")]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")]
     public DateTime CreateTime { get; set; } = DateTime.Now;
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 
     /// <summary>列表展示:员工姓名(不落库)</summary>

+ 22 - 22
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Warehouse/AdoS0EmployeeMaster.cs

@@ -7,7 +7,7 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Warehouse;
 [SugarIndex("uk_EmployeeMaster_domain_employee", nameof(DomainCode), OrderByType.Asc, nameof(Employee), OrderByType.Asc, IsUnique = true)]
 public class AdoS0EmployeeMaster
 {
-    [SugarColumn(ColumnName = "rec_id", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnName = "company_ref_id", ColumnDescription = "关联公司 ID", ColumnDataType = "bigint")]
@@ -16,67 +16,67 @@ public class AdoS0EmployeeMaster
     [SugarColumn(ColumnName = "factory_ref_id", ColumnDescription = "关联工厂 ID", ColumnDataType = "bigint")]
     public long FactoryRefId { get; set; }
 
-    [SugarColumn(ColumnName = "domain_code", ColumnDescription = "工厂域编码", Length = 50)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50)]
     public string DomainCode { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "employee", ColumnDescription = "员工编码", Length = 100)]
+    [SugarColumn(ColumnName = "Employee", ColumnDescription = "员工编码", Length = 100)]
     public string Employee { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "name", ColumnDescription = "姓名", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "Name", ColumnDescription = "姓名", Length = 200, IsNullable = true)]
     public string? Name { get; set; }
 
-    [SugarColumn(ColumnName = "sex", ColumnDescription = "性别", Length = 10, IsNullable = true)]
+    [SugarColumn(ColumnName = "Sex", ColumnDescription = "性别", Length = 10, IsNullable = true)]
     public string? Sex { get; set; }
 
-    [SugarColumn(ColumnName = "phone", ColumnDescription = "电话", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "Phone", ColumnDescription = "电话", Length = 50, IsNullable = true)]
     public string? Phone { get; set; }
 
-    [SugarColumn(ColumnName = "email", ColumnDescription = "邮箱", Length = 200, IsNullable = true)]
+    [SugarColumn(ColumnName = "Email", ColumnDescription = "邮箱", Length = 200, IsNullable = true)]
     public string? Email { get; set; }
 
-    [SugarColumn(ColumnName = "birth_date", ColumnDescription = "出生日期", IsNullable = true)]
+    [SugarColumn(ColumnName = "BirthDate", ColumnDescription = "出生日期", IsNullable = true)]
     public DateTime? BirthDate { get; set; }
 
-    [SugarColumn(ColumnName = "department", ColumnDescription = "部门编码", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "Department", ColumnDescription = "部门编码", Length = 100, IsNullable = true)]
     public string? Department { get; set; }
 
-    [SugarColumn(ColumnName = "date_employed", ColumnDescription = "入职日期", IsNullable = true)]
+    [SugarColumn(ColumnName = "DateEmployed", ColumnDescription = "入职日期", IsNullable = true)]
     public DateTime? DateEmployed { get; set; }
 
-    [SugarColumn(ColumnName = "employment_status", ColumnDescription = "在职状态", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "EmploymentStatus", ColumnDescription = "在职状态", Length = 50, IsNullable = true)]
     public string? EmploymentStatus { get; set; }
 
-    [SugarColumn(ColumnName = "marital_status", ColumnDescription = "婚姻状况", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "MaritalStatus", ColumnDescription = "婚姻状况", Length = 50, IsNullable = true)]
     public string? MaritalStatus { get; set; }
 
-    [SugarColumn(ColumnName = "job_title", ColumnDescription = "岗位", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "JobTitle", ColumnDescription = "岗位", Length = 100, IsNullable = true)]
     public string? JobTitle { get; set; }
 
-    [SugarColumn(ColumnName = "date_terminated", ColumnDescription = "离职日期", IsNullable = true)]
+    [SugarColumn(ColumnName = "DateTerminated", ColumnDescription = "离职日期", IsNullable = true)]
     public DateTime? DateTerminated { get; set; }
 
-    [SugarColumn(ColumnName = "work_ctr", ColumnDescription = "工作中心", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "WorkCtr", ColumnDescription = "工作中心", Length = 100, IsNullable = true)]
     public string? WorkCtr { get; set; }
 
-    [SugarColumn(ColumnName = "car_id", ColumnDescription = "工号/卡号", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "CarId", ColumnDescription = "工号/卡号", Length = 100, IsNullable = true)]
     public string? CarId { get; set; }
 
-    [SugarColumn(ColumnName = "default_work_location", ColumnDescription = "默认工作地点", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "DefaultWorkLocation", ColumnDescription = "默认工作地点", Length = 100, IsNullable = true)]
     public string? DefaultWorkLocation { get; set; }
 
-    [SugarColumn(ColumnName = "is_active", ColumnDescription = "是否启用", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsActive", ColumnDescription = "是否启用", ColumnDataType = "boolean")]
     public bool IsActive { get; set; } = true;
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间")]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")]
     public DateTime CreateTime { get; set; } = DateTime.Now;
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 
     /// <summary>列表展示:部门说明(不落库,来自 DepartmentMaster 关联)</summary>

+ 19 - 19
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S0/Warehouse/AdoS0ItemPackMaster.cs

@@ -7,7 +7,7 @@ namespace Admin.NET.Plugin.AiDOP.Entity.S0.Warehouse;
 [SugarIndex("uk_ItemPackMaster_domain_itemnum", nameof(DomainCode), OrderByType.Asc, nameof(ItemNum), OrderByType.Asc, IsUnique = true)]
 public class AdoS0ItemPackMaster
 {
-    [SugarColumn(ColumnName = "rec_id", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
+    [SugarColumn(ColumnName = "RecID", ColumnDescription = "主键", IsPrimaryKey = true, IsIdentity = true, ColumnDataType = "bigint")]
     public long Id { get; set; }
 
     [SugarColumn(ColumnName = "company_ref_id", ColumnDescription = "关联公司 ID", ColumnDataType = "bigint")]
@@ -16,58 +16,58 @@ public class AdoS0ItemPackMaster
     [SugarColumn(ColumnName = "factory_ref_id", ColumnDescription = "关联工厂 ID", ColumnDataType = "bigint")]
     public long FactoryRefId { get; set; }
 
-    [SugarColumn(ColumnName = "domain_code", ColumnDescription = "工厂域编码", Length = 50)]
+    [SugarColumn(ColumnName = "Domain", ColumnDescription = "工厂域编码", Length = 50)]
     public string DomainCode { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "item_num", ColumnDescription = "物料编码", Length = 100)]
+    [SugarColumn(ColumnName = "ItemNum", ColumnDescription = "物料编码", Length = 100)]
     public string ItemNum { get; set; } = string.Empty;
 
-    [SugarColumn(ColumnName = "packing_qty", ColumnDescription = "包装数量", IsNullable = true)]
+    [SugarColumn(ColumnName = "PackingQty", ColumnDescription = "包装数量", IsNullable = true)]
     public decimal? PackingQty { get; set; }
 
-    [SugarColumn(ColumnName = "small_packing_qty", ColumnDescription = "小包装数量", IsNullable = true)]
+    [SugarColumn(ColumnName = "SmallPackingQty", ColumnDescription = "小包装数量", IsNullable = true)]
     public decimal? SmallPackingQty { get; set; }
 
-    [SugarColumn(ColumnName = "packing_type", ColumnDescription = "包装类型", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "PackingType", ColumnDescription = "包装类型", Length = 50, IsNullable = true)]
     public string? PackingType { get; set; }
 
-    [SugarColumn(ColumnName = "net_weight", ColumnDescription = "净重", IsNullable = true)]
+    [SugarColumn(ColumnName = "NetWeight", ColumnDescription = "净重", IsNullable = true)]
     public decimal? NetWeight { get; set; }
 
-    [SugarColumn(ColumnName = "weight_um", ColumnDescription = "重量单位", Length = 20, IsNullable = true)]
+    [SugarColumn(ColumnName = "WeightUM", ColumnDescription = "重量单位", Length = 20, IsNullable = true)]
     public string? WeightUM { get; set; }
 
-    [SugarColumn(ColumnName = "length", ColumnDescription = "长", IsNullable = true)]
+    [SugarColumn(ColumnName = "Length", ColumnDescription = "长", IsNullable = true)]
     public decimal? Length { get; set; }
 
-    [SugarColumn(ColumnName = "width", ColumnDescription = "宽", IsNullable = true)]
+    [SugarColumn(ColumnName = "Width", ColumnDescription = "宽", IsNullable = true)]
     public decimal? Width { get; set; }
 
-    [SugarColumn(ColumnName = "high", ColumnDescription = "高", IsNullable = true)]
+    [SugarColumn(ColumnName = "High", ColumnDescription = "高", IsNullable = true)]
     public decimal? High { get; set; }
 
-    [SugarColumn(ColumnName = "iss_specific", ColumnDescription = "发料特性", Length = 50, IsNullable = true)]
+    [SugarColumn(ColumnName = "IssSpecific", ColumnDescription = "发料特性", Length = 50, IsNullable = true)]
     public string? IssSpecific { get; set; }
 
-    [SugarColumn(ColumnName = "cust_item", ColumnDescription = "客户物料号", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "CustItem", ColumnDescription = "客户物料号", Length = 100, IsNullable = true)]
     public string? CustItem { get; set; }
 
-    [SugarColumn(ColumnName = "remark", ColumnDescription = "备注", Length = 500, IsNullable = true)]
+    [SugarColumn(ColumnName = "Remark", ColumnDescription = "备注", Length = 500, IsNullable = true)]
     public string? Remark { get; set; }
 
-    [SugarColumn(ColumnName = "is_active", ColumnDescription = "是否启用", ColumnDataType = "boolean")]
+    [SugarColumn(ColumnName = "IsActive", ColumnDescription = "是否启用", ColumnDataType = "boolean")]
     public bool IsActive { get; set; } = true;
 
-    [SugarColumn(ColumnName = "create_user", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "CreateUser", ColumnDescription = "创建人", Length = 100, IsNullable = true)]
     public string? CreateUser { get; set; }
 
-    [SugarColumn(ColumnName = "create_time", ColumnDescription = "创建时间")]
+    [SugarColumn(ColumnName = "CreateTime", ColumnDescription = "创建时间")]
     public DateTime CreateTime { get; set; } = DateTime.Now;
 
-    [SugarColumn(ColumnName = "update_user", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateUser", ColumnDescription = "更新人", Length = 100, IsNullable = true)]
     public string? UpdateUser { get; set; }
 
-    [SugarColumn(ColumnName = "update_time", ColumnDescription = "更新时间", IsNullable = true)]
+    [SugarColumn(ColumnName = "UpdateTime", ColumnDescription = "更新时间", IsNullable = true)]
     public DateTime? UpdateTime { get; set; }
 
     /// <summary>列表展示:物料名称(不落库,来自 ItemMaster.Descr)</summary>

+ 4 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Entity/S8/AdoS8Exception.cs

@@ -76,6 +76,10 @@ public class AdoS8Exception
     [SugarColumn(ColumnName = "closed_at", IsNullable = true)]
     public DateTime? ClosedAt { get; set; }
 
+    /// <summary>所属模块码(S1-S7),标识异常产生于哪个业务环节</summary>
+    [SugarColumn(ColumnName = "module_code", Length = 16, IsNullable = true)]
+    public string? ModuleCode { get; set; }
+
     [SugarColumn(ColumnName = "process_node_code", Length = 64, IsNullable = true)]
     public string? ProcessNodeCode { get; set; }
 

+ 2 - 1
server/Plugins/Admin.NET.Plugin.AiDOP/Infrastructure/AidopMenuLinkSync.cs

@@ -162,7 +162,8 @@ public static class AidopMenuLinkSync
             1329008000013L,
             1329008000014L,
             1329008000015L,
-            1329008000016L
+            1329008000016L,
+            1329008000020L
         };
 
         db.Updateable<SysMenu>()

+ 636 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Infrastructure/AidopS0ExcelColumnMigration.cs

@@ -0,0 +1,636 @@
+using System.Diagnostics;
+using System.Text;
+using System.Data;
+using SqlSugar;
+
+namespace Admin.NET.Plugin.AiDOP.Infrastructure;
+
+/// <summary>
+/// S0 Excel 字段口径迁移:将现有旧列名重命名为 Excel 字段名。
+/// 仅处理文档中已确认映射的字段,不自动补 Excel 独有字段。
+/// </summary>
+public static class AidopS0ExcelColumnMigration
+{
+    private sealed record ColumnRename(string TableName, string OldColumnName, string NewColumnName);
+    private sealed record PrimaryKeyRepair(string TableName, string PkColumnName, bool IsIdentity, string PkComment, params string[] ShadowColumns);
+    private sealed record MySqlColumnMeta(
+        string ColumnName,
+        string ColumnType,
+        string IsNullable,
+        string? ColumnDefault,
+        string? Extra,
+        string? ColumnComment,
+        string? CollationName,
+        string? CharacterSetName);
+
+    private static readonly ColumnRename[] Renames =
+    [
+        new("CostCtrMaster", "cost_ctr", "CostCtr"),
+        new("CostCtrMaster", "create_time", "CreateTime"),
+        new("CostCtrMaster", "create_user", "CreateUser"),
+        new("CostCtrMaster", "descr", "Descr"),
+        new("CostCtrMaster", "domain_code", "Domain"),
+        new("CostCtrMaster", "eff_time", "EffTime"),
+        new("CostCtrMaster", "rec_id", "RecID"),
+        new("CostCtrMaster", "ufld1", "Ufld1"),
+        new("CostCtrMaster", "update_time", "UpdateTime"),
+        new("CostCtrMaster", "update_user", "UpdateUser"),
+        new("CustMaster", "create_time", "CreateTime"),
+        new("CustMaster", "create_user", "CreateUser"),
+        new("CustMaster", "credit_rating", "CreditRating"),
+        new("CustMaster", "curr", "Curr"),
+        new("CustMaster", "cust", "Cust"),
+        new("CustMaster", "domain_code", "Domain"),
+        new("CustMaster", "is_active", "IsActive"),
+        new("CustMaster", "is_confirm", "IsConfirm"),
+        new("CustMaster", "rec_id", "RecID"),
+        new("CustMaster", "remarks", "Remarks"),
+        new("CustMaster", "ship_to", "ShipTo"),
+        new("CustMaster", "slspsn1", "Slspsn1"),
+        new("CustMaster", "slspsn2", "Slspsn2"),
+        new("CustMaster", "sort_name", "SortName"),
+        new("CustMaster", "tax_class", "TaxClass"),
+        new("CustMaster", "tax_in", "TaxIn"),
+        new("CustMaster", "update_time", "UpdateTime"),
+        new("CustMaster", "update_user", "UpdateUser"),
+        new("DepartmentMaster", "create_time", "CreateTime"),
+        new("DepartmentMaster", "create_user", "CreateUser"),
+        new("DepartmentMaster", "department", "Department"),
+        new("DepartmentMaster", "descr", "Descr"),
+        new("DepartmentMaster", "domain_code", "Domain"),
+        new("DepartmentMaster", "is_active", "IsActive"),
+        new("DepartmentMaster", "rec_id", "RecID"),
+        new("DepartmentMaster", "update_time", "UpdateTime"),
+        new("DepartmentMaster", "update_user", "UpdateUser"),
+        new("DevMonitor", "code_descr", "CodeDescr"),
+        new("DevMonitor", "code_type", "CodeType"),
+        new("DevMonitor", "create_time", "CreateTime"),
+        new("DevMonitor", "create_user", "CreateUser"),
+        new("DevMonitor", "device_descr", "DeviceDescr"),
+        new("DevMonitor", "device_number", "DeviceNumber"),
+        new("DevMonitor", "domain", "Domain"),
+        new("DevMonitor", "id", "RecID"),
+        new("DevMonitor", "low_limit", "LowLimit"),
+        new("DevMonitor", "message_code", "MessageCode"),
+        new("DevMonitor", "std_value", "StdValue"),
+        new("DevMonitor", "top_limit", "TopLimit"),
+        new("DevMonitor", "ufld1", "Ufld1"),
+        new("DevMonitor", "um", "UM"),
+        new("DevMonitor", "update_time", "UpdateTime"),
+        new("DevMonitor", "update_user", "UpdateUser"),
+        new("DevMonitor", "value_type", "ValueType"),
+        new("EmpSkills", "create_time", "CreateTime"),
+        new("EmpSkills", "create_user", "CreateUser"),
+        new("EmpSkills", "domain", "Domain"),
+        new("EmpSkills", "efficiency_coefficient", "EfficiencyCoefficient"),
+        new("EmpSkills", "employee", "Employee"),
+        new("EmpSkills", "employee_id", "EmployeeID"),
+        new("EmpSkills", "employee_name", "EmployeeName"),
+        new("EmpSkills", "id", "RecID"),
+        new("EmpSkills", "site", "Site"),
+        new("EmpSkills", "skill_description", "SkillDescription"),
+        new("EmpSkills", "skill_level", "SkillLevel"),
+        new("EmpSkills", "skill_no", "SkillNo"),
+        new("EmpSkills", "update_time", "UpdateTime"),
+        new("EmpSkills", "update_user", "UpdateUser"),
+        new("EmpWorkDutyMaster", "create_time", "CreateTime"),
+        new("EmpWorkDutyMaster", "create_user", "CreateUser"),
+        new("EmpWorkDutyMaster", "domain_code", "Domain"),
+        new("EmpWorkDutyMaster", "duty", "Duty"),
+        new("EmpWorkDutyMaster", "emp_type", "EmpType"),
+        new("EmpWorkDutyMaster", "employee", "Employee"),
+        new("EmpWorkDutyMaster", "item_num1", "ItemNum1"),
+        new("EmpWorkDutyMaster", "item_num2", "ItemNum2"),
+        new("EmpWorkDutyMaster", "location", "Location"),
+        new("EmpWorkDutyMaster", "prod_line", "ProdLine"),
+        new("EmpWorkDutyMaster", "rec_id", "RecID"),
+        new("EmpWorkDutyMaster", "ufld2", "Ufld2"),
+        new("EmpWorkDutyMaster", "update_time", "UpdateTime"),
+        new("EmpWorkDutyMaster", "update_user", "UpdateUser"),
+        new("EmployeeMaster", "birth_date", "BirthDate"),
+        new("EmployeeMaster", "car_id", "CarId"),
+        new("EmployeeMaster", "create_time", "CreateTime"),
+        new("EmployeeMaster", "create_user", "CreateUser"),
+        new("EmployeeMaster", "date_employed", "DateEmployed"),
+        new("EmployeeMaster", "date_terminated", "DateTerminated"),
+        new("EmployeeMaster", "default_work_location", "DefaultWorkLocation"),
+        new("EmployeeMaster", "department", "Department"),
+        new("EmployeeMaster", "domain_code", "Domain"),
+        new("EmployeeMaster", "email", "Email"),
+        new("EmployeeMaster", "employee", "Employee"),
+        new("EmployeeMaster", "employment_status", "EmploymentStatus"),
+        new("EmployeeMaster", "is_active", "IsActive"),
+        new("EmployeeMaster", "job_title", "JobTitle"),
+        new("EmployeeMaster", "marital_status", "MaritalStatus"),
+        new("EmployeeMaster", "name", "Name"),
+        new("EmployeeMaster", "phone", "Phone"),
+        new("EmployeeMaster", "rec_id", "RecID"),
+        new("EmployeeMaster", "sex", "Sex"),
+        new("EmployeeMaster", "update_time", "UpdateTime"),
+        new("EmployeeMaster", "update_user", "UpdateUser"),
+        new("EmployeeMaster", "work_ctr", "WorkCtr"),
+        new("ItemMaster", "allocate_single_lot", "AllocateSingleLot"),
+        new("ItemMaster", "auto_lot_nums", "AutoLotNums"),
+        new("ItemMaster", "commodity_code", "CommodityCode"),
+        new("ItemMaster", "create_time", "CreateTime"),
+        new("ItemMaster", "create_user", "CreateUser"),
+        new("ItemMaster", "days_between_pm", "DaysBetweenPM"),
+        new("ItemMaster", "default_shelf", "DefaultShelf"),
+        new("ItemMaster", "descr", "Descr"),
+        new("ItemMaster", "descr1", "Descr1"),
+        new("ItemMaster", "domain_code", "Domain"),
+        new("ItemMaster", "drawing", "Drawing"),
+        new("ItemMaster", "emt_type", "EMTType"),
+        new("ItemMaster", "ins_lt", "InsLT"),
+        new("ItemMaster", "inspect", "Inspect"),
+        new("ItemMaster", "install", "Install"),
+        new("ItemMaster", "is_active", "IsActive"),
+        new("ItemMaster", "is_confirm", "IsConfirm"),
+        new("ItemMaster", "is_main_mas", "IsMainMas"),
+        new("ItemMaster", "issue_policy", "IssuePolicy"),
+        new("ItemMaster", "item_num", "ItemNum"),
+        new("ItemMaster", "item_type", "ItemType"),
+        new("ItemMaster", "key_item", "KeyItem"),
+        new("ItemMaster", "length", "Length"),
+        new("ItemMaster", "location", "Location"),
+        new("ItemMaster", "location_type", "LocationType"),
+        new("ItemMaster", "lot_serial_control", "LotSerialControl"),
+        new("ItemMaster", "max_ord", "MaxOrd"),
+        new("ItemMaster", "mfg_lt", "MfgLT"),
+        new("ItemMaster", "mfgmttr", "MFGMTTR"),
+        new("ItemMaster", "min_ord", "MinOrd"),
+        new("ItemMaster", "min_ord_sales", "MinOrdSales"),
+        new("ItemMaster", "net_weight", "NetWeight"),
+        new("ItemMaster", "net_weight_um", "NetWeightUM"),
+        new("ItemMaster", "ord_mult", "OrdMult"),
+        new("ItemMaster", "owner_application", "OwnerApplication"),
+        new("ItemMaster", "pkg_code", "PkgCode"),
+        new("ItemMaster", "planner", "Planner"),
+        new("ItemMaster", "pur_lt", "PurLT"),
+        new("ItemMaster", "pur_mfg", "PurMfg"),
+        new("ItemMaster", "rec_id", "RecID"),
+        new("ItemMaster", "remark", "Remark"),
+        new("ItemMaster", "rev", "Rev"),
+        new("ItemMaster", "safety_stk", "SafetyStk"),
+        new("ItemMaster", "size", "Size"),
+        new("ItemMaster", "size_um", "SizeUM"),
+        new("ItemMaster", "status", "Status"),
+        new("ItemMaster", "stock_turn_over", "StockTurnOver"),
+        new("ItemMaster", "trace_detail", "TraceDetail"),
+        new("ItemMaster", "um", "UM"),
+        new("ItemMaster", "update_time", "UpdateTime"),
+        new("ItemMaster", "update_user", "UpdateUser"),
+        new("ItemOpCondition", "code_type", "CodeType"),
+        new("ItemOpCondition", "create_time", "CreateTime"),
+        new("ItemOpCondition", "create_user", "CreateUser"),
+        new("ItemOpCondition", "descr", "Descr"),
+        new("ItemOpCondition", "domain", "Domain"),
+        new("ItemOpCondition", "id", "RecID"),
+        new("ItemOpCondition", "is_main", "IsMain"),
+        new("ItemOpCondition", "item_code", "ItemCode"),
+        new("ItemOpCondition", "item_num", "ItemNum"),
+        new("ItemOpCondition", "line", "Line"),
+        new("ItemOpCondition", "op", "Op"),
+        new("ItemOpCondition", "typed", "Typed"),
+        new("ItemOpCondition", "update_time", "UpdateTime"),
+        new("ItemOpCondition", "update_user", "UpdateUser"),
+        new("ItemPackMaster", "create_time", "CreateTime"),
+        new("ItemPackMaster", "create_user", "CreateUser"),
+        new("ItemPackMaster", "cust_item", "CustItem"),
+        new("ItemPackMaster", "domain_code", "Domain"),
+        new("ItemPackMaster", "high", "High"),
+        new("ItemPackMaster", "is_active", "IsActive"),
+        new("ItemPackMaster", "iss_specific", "IssSpecific"),
+        new("ItemPackMaster", "item_num", "ItemNum"),
+        new("ItemPackMaster", "length", "Length"),
+        new("ItemPackMaster", "net_weight", "NetWeight"),
+        new("ItemPackMaster", "packing_qty", "PackingQty"),
+        new("ItemPackMaster", "packing_type", "PackingType"),
+        new("ItemPackMaster", "rec_id", "RecID"),
+        new("ItemPackMaster", "remark", "Remark"),
+        new("ItemPackMaster", "small_packing_qty", "SmallPackingQty"),
+        new("ItemPackMaster", "update_time", "UpdateTime"),
+        new("ItemPackMaster", "update_user", "UpdateUser"),
+        new("ItemPackMaster", "weight_um", "WeightUM"),
+        new("ItemPackMaster", "width", "Width"),
+        new("ItemSubstituteDetail", "create_time", "CreateTime"),
+        new("ItemSubstituteDetail", "create_user", "CreateUser"),
+        new("ItemSubstituteDetail", "domain", "Domain"),
+        new("ItemSubstituteDetail", "id", "RecID"),
+        new("ItemSubstituteDetail", "item_num", "ItemNum"),
+        new("ItemSubstituteDetail", "parent_item", "ParentItem"),
+        new("ItemSubstituteDetail", "remark", "Remark"),
+        new("ItemSubstituteDetail", "substitute_item", "SubstituteItem"),
+        new("ItemSubstituteDetail", "substitute_qty", "SubstituteQty"),
+        new("ItemSubstituteDetail", "update_time", "UpdateTime"),
+        new("ItemSubstituteDetail", "update_user", "UpdateUser"),
+        new("LineMaster", "create_time", "CreateTime"),
+        new("LineMaster", "create_user", "CreateUser"),
+        new("LineMaster", "domain", "Domain"),
+        new("LineMaster", "id", "RecID"),
+        new("LineMaster", "is_active", "IsActive"),
+        new("LineMaster", "line", "Line"),
+        new("LineMaster", "line_category", "LineCategory"),
+        new("LineMaster", "line_type", "LineType"),
+        new("LineMaster", "location", "Location"),
+        new("LineMaster", "mid_location", "MidLocation"),
+        new("LineMaster", "picking_location", "PickingLocation"),
+        new("LineMaster", "update_time", "UpdateTime"),
+        new("LineMaster", "update_user", "UpdateUser"),
+        new("LineMaster", "v_location", "VLocation"),
+        new("LineMaster", "workshop", "Workshop"),
+        new("LineSkillDetail", "create_time", "CreateTime"),
+        new("LineSkillMaster", "create_time", "CreateTime"),
+        new("LineSkillMaster", "create_user", "CreateUser"),
+        new("LineSkillMaster", "domain", "Domain"),
+        new("LineSkillMaster", "id", "RecID"),
+        new("LineSkillMaster", "job_descr", "JOBDescr"),
+        new("LineSkillMaster", "job_no", "JOBNo"),
+        new("LineSkillMaster", "prod_line", "ProdLine"),
+        new("LineSkillMaster", "update_time", "UpdateTime"),
+        new("LineSkillMaster", "update_user", "UpdateUser"),
+        new("PriorityCode", "create_time", "CreateTime"),
+        new("PriorityCode", "create_user", "CreateUser"),
+        new("PriorityCode", "descr", "Descr"),
+        new("PriorityCode", "domain_code", "Domain"),
+        new("PriorityCode", "is_active", "IsActive"),
+        new("PriorityCode", "priority", "Priority"),
+        new("PriorityCode", "rec_id", "RecID"),
+        new("PriorityCode", "source_column", "SourceColumn"),
+        new("PriorityCode", "source_id", "SourceID"),
+        new("PriorityCode", "source_table", "SourceTable"),
+        new("PriorityCode", "source_type", "SourceType"),
+        new("PriorityCode", "update_time", "UpdateTime"),
+        new("PriorityCode", "update_user", "UpdateUser"),
+        new("PriorityCode", "value", "Value"),
+        new("PriorityCode", "value_id", "ValueID"),
+        new("PriorityCode", "value_type", "ValueType"),
+        new("ProdLineDetail", "create_time", "CreateTime"),
+        new("ProdLineDetail", "create_user", "CreateUser"),
+        new("ProdLineDetail", "domain", "Domain"),
+        new("ProdLineDetail", "id", "RecID"),
+        new("ProdLineDetail", "is_active", "IsActive"),
+        new("ProdLineDetail", "is_confirm", "IsConfirm"),
+        new("ProdLineDetail", "line", "Line"),
+        new("ProdLineDetail", "mold_type_code", "MoldTypeCode"),
+        new("ProdLineDetail", "op", "Op"),
+        new("ProdLineDetail", "part", "Part"),
+        new("ProdLineDetail", "rate", "Rate"),
+        new("ProdLineDetail", "setup_time", "SetupTime"),
+        new("ProdLineDetail", "site", "Site"),
+        new("ProdLineDetail", "skill_no", "SkillNo"),
+        new("ProdLineDetail", "standard_staff_count", "StandardStaffCount"),
+        new("ProdLineDetail", "start", "Start"),
+        new("ProdLineDetail", "update_time", "UpdateTime"),
+        new("ProdLineDetail", "update_user", "UpdateUser"),
+        new("ProductStructureMaster", "end_eff", "EndEff"),
+        new("ProductStructureMaster", "id", "RecID"),
+        new("ProductStructureMaster", "qty", "Qty"),
+        new("ProductStructureMaster", "qty_consumed", "QtyConsumed"),
+        new("ProductStructureMaster", "refs", "Refs"),
+        new("ProductStructureMaster", "remark", "Remark"),
+        new("ProductStructureMaster", "scrap", "Scrap"),
+        new("ProductStructureMaster", "start_eff", "StartEff"),
+        new("ProductStructureMaster", "structure_type", "StructureType"),
+        new("ProductStructureMaster", "um", "UM"),
+        new("ProductStructureOp", "id", "RecID"),
+        new("ProductStructureOp", "op", "Op"),
+        new("RoutingOpDetail", "comment_index", "CommentIndex"),
+        new("RoutingOpDetail", "create_user", "CreateUser"),
+        new("RoutingOpDetail", "id", "RecID"),
+        new("RoutingOpDetail", "is_confirm", "IsConfirm"),
+        new("RoutingOpDetail", "u_deci1", "UDeci1"),
+        new("RoutingOpDetail", "u_deci2", "UDeci2"),
+        new("RoutingOpDetail", "u_deci3", "UDeci3"),
+        new("RoutingOpDetail", "u_deci4", "UDeci4"),
+        new("RoutingOpDetail", "u_deci5", "UDeci5"),
+        new("RoutingOpDetail", "ufld1", "Ufld1"),
+        new("RoutingOpDetail", "ufld2", "Ufld2"),
+        new("RoutingOpDetail", "ufld3", "Ufld3"),
+        new("RoutingOpDetail", "ufld4", "Ufld4"),
+        new("RoutingOpDetail", "ufld5", "Ufld5"),
+        new("RoutingOpDetail", "ufld6", "Ufld6"),
+        new("RoutingOpDetail", "ufld7", "Ufld7"),
+        new("RoutingOpDetail", "ufld8", "Ufld8"),
+        new("RoutingOpDetail", "ufld9", "Ufld9"),
+        new("RoutingOpDetail", "update_user", "UpdateUser"),
+        new("SuppMaster", "ap_end_day1", "APEndDay1"),
+        new("SuppMaster", "ap_end_day2", "APEndDay2"),
+        new("SuppMaster", "ap_start_day1", "APStartDay1"),
+        new("SuppMaster", "ap_start_day2", "APStartDay2"),
+        new("SuppMaster", "cr_terms", "CrTerms"),
+        new("SuppMaster", "create_time", "CreateTime"),
+        new("SuppMaster", "create_user", "CreateUser"),
+        new("SuppMaster", "curr", "Curr"),
+        new("SuppMaster", "domain_code", "Domain"),
+        new("SuppMaster", "is_active", "IsActive"),
+        new("SuppMaster", "pur_contact", "PurContact"),
+        new("SuppMaster", "rec_id", "RecID"),
+        new("SuppMaster", "remark", "Remark"),
+        new("SuppMaster", "sort_name", "SortName"),
+        new("SuppMaster", "supp", "Supp"),
+        new("SuppMaster", "tax_class", "TaxClass"),
+        new("SuppMaster", "tax_in", "TaxIn"),
+        new("SuppMaster", "type", "Type"),
+        new("SuppMaster", "update_time", "UpdateTime"),
+        new("SuppMaster", "update_user", "UpdateUser"),
+        new("WorkCtrMaster", "create_time", "CreateTime"),
+        new("WorkCtrMaster", "create_user", "CreateUser"),
+        new("WorkCtrMaster", "department", "Department"),
+        new("WorkCtrMaster", "descr", "Descr"),
+        new("WorkCtrMaster", "domain", "Domain"),
+        new("WorkCtrMaster", "id", "RecID"),
+        new("WorkCtrMaster", "is_active", "IsActive"),
+        new("WorkCtrMaster", "update_time", "UpdateTime"),
+        new("WorkCtrMaster", "update_user", "UpdateUser"),
+        new("WorkCtrMaster", "work_ctr", "WorkCtr"),
+        new("ic_substitute", "id", "Id"),
+        new("ic_substitute", "is_deleted", "IsDeleted"),
+        new("srm_purchase", "id", "Id"),
+        new("srm_purchase", "is_require_goods", "IsRequireGoods"),
+    ];
+
+    private static readonly PrimaryKeyRepair[] PrimaryKeyRepairs =
+    [
+        new("ItemSubstituteDetail", "RecID", true, "物料替代关系主键", "Id"),
+        new("CostCtrMaster", "RecID", true, "主键"),
+        new("CustMaster", "RecID", true, "主键"),
+        new("DepartmentMaster", "RecID", true, "主键"),
+        new("DevMonitor", "RecID", true, "主键", "Id"),
+        new("EmpSkills", "RecID", true, "主键", "Id"),
+        new("EmpWorkDutyMaster", "RecID", true, "主键"),
+        new("EmployeeMaster", "RecID", true, "主键"),
+        new("ItemMaster", "RecID", true, "主键"),
+        new("ItemOpCondition", "RecID", true, "物料工序生产要素主键", "Id"),
+        new("ItemPackMaster", "RecID", true, "主键"),
+        new("LineMaster", "RecID", true, "生产线主键", "Id"),
+        new("LineSkillMaster", "RecID", true, "产线岗位主键", "Id"),
+        new("PriorityCode", "RecID", true, "主键"),
+        new("ProdLineDetail", "RecID", true, "生产线物料主键"),
+        new("ProductStructureMaster", "RecID", true, "主键", "Id"),
+        new("ProductStructureOp", "RecID", true, "主键", "Id"),
+        new("RoutingOpDetail", "RecID", true, "主键", "Id"),
+        new("SuppMaster", "RecID", true, "主键"),
+        new("WorkCtrMaster", "RecID", true, "工作中心主键"),
+        new("ic_substitute", "Id", false, "主键"),
+        new("srm_purchase", "Id", true, "货源清单主键")
+    ];
+
+    public static void Migrate(ISqlSugarClient db)
+    {
+        var databaseName = db.Ado.SqlQuerySingle<string>("SELECT DATABASE();");
+        foreach (var group in Renames.GroupBy(x => x.TableName))
+        {
+            var tableName = group.Key;
+            var tables = db.DbMaintenance.GetTableInfoList(false);
+            if (!tables.Any(t => t.Name.Equals(tableName, StringComparison.OrdinalIgnoreCase)))
+                continue;
+
+            try
+            {
+                var columns = db.DbMaintenance.GetColumnInfosByTableName(tableName, false) ?? [];
+                foreach (var rename in group)
+                {
+                    var oldColumn = columns.FirstOrDefault(c => c.DbColumnName.Equals(rename.OldColumnName, StringComparison.Ordinal));
+                    var newColumn = columns.FirstOrDefault(c => c.DbColumnName.Equals(rename.NewColumnName, StringComparison.Ordinal));
+                    if (oldColumn == null)
+                        continue;
+
+                    if (newColumn != null)
+                    {
+                        if (oldColumn.IsPrimarykey)
+                        {
+                            db.Ado.ExecuteCommand($"ALTER TABLE `{tableName}` DROP COLUMN `{rename.NewColumnName}`");
+                            Trace.TraceInformation($"Ai-DOP S0 drop duplicate new PK shadow column: {tableName}.{rename.NewColumnName}");
+
+                            RenameColumnMySql(db, databaseName, tableName, rename.OldColumnName, rename.NewColumnName);
+                            Trace.TraceInformation($"Ai-DOP S0 rename PK column: {tableName}.{rename.OldColumnName} -> {rename.NewColumnName}");
+                        }
+                        else
+                        {
+                            db.Ado.ExecuteCommand($"ALTER TABLE `{tableName}` DROP COLUMN `{rename.OldColumnName}`");
+                            Trace.TraceInformation($"Ai-DOP S0 drop duplicate old column: {tableName}.{rename.OldColumnName}");
+                        }
+
+                        columns = db.DbMaintenance.GetColumnInfosByTableName(tableName, false) ?? [];
+                        continue;
+                    }
+
+                    RenameColumnMySql(db, databaseName, tableName, rename.OldColumnName, rename.NewColumnName);
+                    Trace.TraceInformation($"Ai-DOP S0 rename column: {tableName}.{rename.OldColumnName} -> {rename.NewColumnName}");
+
+                    columns = db.DbMaintenance.GetColumnInfosByTableName(tableName, false) ?? [];
+                }
+            }
+            catch (Exception ex)
+            {
+                Trace.TraceError($"Ai-DOP S0 rename columns failed for {tableName}: {ex}");
+                throw;
+            }
+        }
+
+        RepairPrimaryKeys(db, databaseName);
+    }
+
+    private static void RenameColumnMySql(ISqlSugarClient db, string databaseName, string tableName, string oldColumnName, string newColumnName)
+    {
+        var meta = GetMySqlColumnMeta(db, databaseName, tableName, oldColumnName);
+        if (meta == null)
+            return;
+
+        var ddl = $"ALTER TABLE `{tableName}` CHANGE COLUMN `{oldColumnName}` `{newColumnName}` {BuildColumnDefinition(meta)}";
+        db.Ado.ExecuteCommand(ddl);
+    }
+
+    private static void RepairPrimaryKeys(ISqlSugarClient db, string databaseName)
+    {
+        var tableList = db.DbMaintenance.GetTableInfoList(false);
+        foreach (var repair in PrimaryKeyRepairs)
+        {
+            var tableName = tableList.FirstOrDefault(t => t.Name.Equals(repair.TableName, StringComparison.OrdinalIgnoreCase))?.Name;
+            if (string.IsNullOrEmpty(tableName))
+                continue;
+
+            var tableColumns = db.DbMaintenance.GetColumnInfosByTableName(tableName, false) ?? [];
+            if (tableColumns.Count == 0)
+                continue;
+
+            foreach (var shadowColumn in repair.ShadowColumns)
+            {
+                if (tableColumns.Any(c => c.DbColumnName.Equals(shadowColumn, StringComparison.Ordinal)))
+                {
+                    db.Ado.ExecuteCommand($"ALTER TABLE `{tableName}` DROP COLUMN `{shadowColumn}`");
+                }
+            }
+
+            SugarParameter[] pkParameters =
+            [
+                new SugarParameter("@db", databaseName),
+                new SugarParameter("@table", tableName)
+            ];
+            var pkTable = db.Ado.GetDataTable(
+                """
+                SELECT COLUMN_NAME
+                FROM information_schema.KEY_COLUMN_USAGE
+                WHERE TABLE_SCHEMA = @db
+                  AND TABLE_NAME = @table
+                  AND CONSTRAINT_NAME = 'PRIMARY'
+                ORDER BY ORDINAL_POSITION;
+                """,
+                pkParameters);
+            var primaryKeyColumns = pkTable.Rows.Cast<DataRow>()
+                .Select(r => r["COLUMN_NAME"]?.ToString())
+                .Where(x => !string.IsNullOrWhiteSpace(x))
+                .ToList();
+
+            if (primaryKeyColumns.Count == 0)
+            {
+                primaryKeyColumns = tableColumns
+                    .Where(c => c.IsPrimarykey)
+                    .OrderBy(c => c.DbColumnName)
+                    .Select(c => c.DbColumnName)
+                    .Where(x => !string.IsNullOrWhiteSpace(x))
+                    .Select(x => (string?)x)
+                    .ToList();
+            }
+
+            var pkMatches = primaryKeyColumns.Count == 1
+                && primaryKeyColumns[0]!.Equals(repair.PkColumnName, StringComparison.OrdinalIgnoreCase);
+
+            if (!pkMatches)
+            {
+                if (primaryKeyColumns.Count > 0)
+                    db.Ado.ExecuteCommand($"ALTER TABLE `{tableName}` DROP PRIMARY KEY");
+
+                db.Ado.ExecuteCommand($"ALTER TABLE `{tableName}` ADD PRIMARY KEY(`{repair.PkColumnName}`)");
+            }
+
+            if (!repair.IsIdentity)
+                continue;
+
+            var meta = GetMySqlColumnMeta(db, databaseName, tableName, repair.PkColumnName)
+                ?? GetMySqlColumnMeta(db, databaseName, tableName, primaryKeyColumns.FirstOrDefault() ?? repair.PkColumnName);
+            if (meta == null || ContainsToken(meta.Extra, "auto_increment"))
+                continue;
+
+            var ddl = $"ALTER TABLE `{tableName}` MODIFY COLUMN `{meta.ColumnName}` {BuildColumnDefinition(meta, forceNotNull: true, forceAutoIncrement: true, skipDefault: true, commentOverride: repair.PkComment)}";
+            db.Ado.ExecuteCommand(ddl);
+        }
+    }
+
+    private static MySqlColumnMeta? GetMySqlColumnMeta(ISqlSugarClient db, string databaseName, string tableName, string columnName)
+    {
+        var sql = """
+                  SELECT
+                      COLUMN_NAME AS ColumnName,
+                      COLUMN_TYPE AS ColumnType,
+                      IS_NULLABLE AS IsNullable,
+                      COLUMN_DEFAULT AS ColumnDefault,
+                      EXTRA AS Extra,
+                      COLUMN_COMMENT AS ColumnComment,
+                      COLLATION_NAME AS CollationName,
+                      CHARACTER_SET_NAME AS CharacterSetName
+                  FROM information_schema.COLUMNS
+                  WHERE TABLE_SCHEMA = @db
+                    AND TABLE_NAME = @table
+                    AND BINARY COLUMN_NAME = @column
+                  LIMIT 1;
+                  """;
+        SugarParameter[] parameters =
+        [
+            new SugarParameter("@db", databaseName),
+            new SugarParameter("@table", tableName),
+            new SugarParameter("@column", columnName)
+        ];
+        var table = db.Ado.GetDataTable(sql, parameters);
+        if (table.Rows.Count == 0)
+            return null;
+
+        var row = table.Rows[0];
+        return new MySqlColumnMeta(
+            row["ColumnName"]?.ToString() ?? columnName,
+            row["ColumnType"]?.ToString() ?? throw new InvalidOperationException($"Missing ColumnType for {tableName}.{columnName}"),
+            row["IsNullable"]?.ToString() ?? "YES",
+            row["ColumnDefault"] == DBNull.Value ? null : row["ColumnDefault"]?.ToString(),
+            row["Extra"] == DBNull.Value ? null : row["Extra"]?.ToString(),
+            row["ColumnComment"] == DBNull.Value ? null : row["ColumnComment"]?.ToString(),
+            row["CollationName"] == DBNull.Value ? null : row["CollationName"]?.ToString(),
+            row["CharacterSetName"] == DBNull.Value ? null : row["CharacterSetName"]?.ToString());
+    }
+
+    private static string BuildColumnDefinition(MySqlColumnMeta meta, bool forceNotNull = false, bool forceAutoIncrement = false, bool skipDefault = false, string? commentOverride = null)
+    {
+        var builder = new StringBuilder();
+        builder.Append(meta.ColumnType);
+
+        if (!string.IsNullOrWhiteSpace(meta.CharacterSetName))
+            builder.Append($" CHARACTER SET {meta.CharacterSetName}");
+
+        if (!string.IsNullOrWhiteSpace(meta.CollationName))
+            builder.Append($" COLLATE {meta.CollationName}");
+
+        var isNullable = !forceNotNull && meta.IsNullable.Equals("YES", StringComparison.OrdinalIgnoreCase);
+        builder.Append(isNullable ? " NULL" : " NOT NULL");
+
+        if (!skipDefault && meta.ColumnDefault != null)
+        {
+            builder.Append(" DEFAULT ");
+            builder.Append(FormatDefaultValue(meta.ColumnType, meta.ColumnDefault));
+        }
+        else if (!skipDefault && isNullable &&
+                 !ContainsToken(meta.Extra, "auto_increment") &&
+                 !ContainsToken(meta.Extra, "DEFAULT_GENERATED"))
+        {
+            builder.Append(" DEFAULT NULL");
+        }
+
+        if (forceAutoIncrement || ContainsToken(meta.Extra, "auto_increment"))
+            builder.Append(" AUTO_INCREMENT");
+
+        if (ContainsToken(meta.Extra, "on update CURRENT_TIMESTAMP"))
+            builder.Append(" ON UPDATE CURRENT_TIMESTAMP");
+
+        var comment = commentOverride ?? meta.ColumnComment;
+        if (!string.IsNullOrWhiteSpace(comment))
+            builder.Append($" COMMENT '{EscapeSqlLiteral(comment)}'");
+
+        return builder.ToString();
+    }
+
+    private static string FormatDefaultValue(string columnType, string columnDefault)
+    {
+        if (columnDefault.Equals("NULL", StringComparison.OrdinalIgnoreCase))
+            return "NULL";
+
+        if (columnDefault.StartsWith("CURRENT_TIMESTAMP", StringComparison.OrdinalIgnoreCase))
+            return columnDefault;
+
+        return IsNumericLikeType(columnType)
+            ? columnDefault
+            : $"'{EscapeSqlLiteral(columnDefault)}'";
+    }
+
+    private static bool IsNumericLikeType(string columnType)
+    {
+        var normalized = columnType.ToLowerInvariant();
+        return normalized.StartsWith("tinyint") ||
+               normalized.StartsWith("smallint") ||
+               normalized.StartsWith("mediumint") ||
+               normalized.StartsWith("int") ||
+               normalized.StartsWith("bigint") ||
+               normalized.StartsWith("decimal") ||
+               normalized.StartsWith("numeric") ||
+               normalized.StartsWith("float") ||
+               normalized.StartsWith("double") ||
+               normalized.StartsWith("real") ||
+               normalized.StartsWith("bit") ||
+               normalized.StartsWith("bool") ||
+               normalized.StartsWith("boolean");
+    }
+
+    private static bool ContainsToken(string? value, string token)
+        => !string.IsNullOrWhiteSpace(value) && value.Contains(token, StringComparison.OrdinalIgnoreCase);
+
+    private static string EscapeSqlLiteral(string value)
+        => value.Replace("\\", "\\\\").Replace("'", "\\'");
+}

+ 89 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Infrastructure/S8/S8SceneCode.cs

@@ -0,0 +1,89 @@
+namespace Admin.NET.Plugin.AiDOP.Infrastructure.S8;
+
+/// <summary>
+/// S8 异常场景码常量。
+/// scene_code 格式:S{模块组}_场景域,方便前端按监控页分类。
+/// </summary>
+public static class S8SceneCode
+{
+    /// <summary>交付异常(S1 订单评审变更 + S7 成品出库交期)</summary>
+    public const string S1S7Delivery = "S1S7_DELIVERY";
+
+    /// <summary>生产异常(S2 工单开工 + S6 完工/质量)</summary>
+    public const string S2S6Production = "S2S6_PRODUCTION";
+
+    /// <summary>供应异常(S3 MRP套料 + S5 IQC/库存)</summary>
+    public const string S3S5Supply = "S3S5_SUPPLY";
+
+    /// <summary>采购执行异常(S4 PO跟单/收货/退货)</summary>
+    public const string S4Purchase = "S4_PURCHASE";
+
+    /// <summary>scene_code → 中文名称</summary>
+    public static string Label(string sceneCode) => sceneCode switch
+    {
+        S1S7Delivery   => "交付异常",
+        S2S6Production => "生产异常",
+        S3S5Supply     => "供应异常",
+        S4Purchase     => "采购执行异常",
+        _              => sceneCode
+    };
+
+    /// <summary>scene_code → 所属模块列表(用于页面展示列头)</summary>
+    public static string[] Modules(string sceneCode) => sceneCode switch
+    {
+        S1S7Delivery   => new[] { "S1", "S7" },
+        S2S6Production => new[] { "S2", "S6" },
+        S3S5Supply     => new[] { "S3", "S5" },
+        S4Purchase     => new[] { "S4" },
+        _              => Array.Empty<string>()
+    };
+
+    /// <summary>所有 scene_code 有序列表(综合全景页列顺序)</summary>
+    public static readonly string[] All =
+    {
+        S1S7Delivery,
+        S2S6Production,
+        S3S5Supply,
+        S4Purchase
+    };
+}
+
+/// <summary>
+/// S8 模块码常量(对应 S1-S7 业务模块)。
+/// </summary>
+public static class S8ModuleCode
+{
+    public const string S1 = "S1";
+    public const string S2 = "S2";
+    public const string S3 = "S3";
+    public const string S4 = "S4";
+    public const string S5 = "S5";
+    public const string S6 = "S6";
+    public const string S7 = "S7";
+
+    /// <summary>module_code → 中文名称</summary>
+    public static string Label(string moduleCode) => moduleCode switch
+    {
+        S1 => "S1·销售评审",
+        S2 => "S2·计划排产",
+        S3 => "S3·物料套料",
+        S4 => "S4·采购执行",
+        S5 => "S5·IQC入库",
+        S6 => "S6·生产完工",
+        S7 => "S7·成品出库",
+        _  => moduleCode
+    };
+
+    /// <summary>module_code → 所属 scene_code</summary>
+    public static string SceneOf(string moduleCode) => moduleCode switch
+    {
+        S1 or S7 => S8SceneCode.S1S7Delivery,
+        S2 or S6 => S8SceneCode.S2S6Production,
+        S3 or S5 => S8SceneCode.S3S5Supply,
+        S4       => S8SceneCode.S4Purchase,
+        _        => string.Empty
+    };
+
+    /// <summary>所有模块码有序列表</summary>
+    public static readonly string[] All = { S1, S2, S3, S4, S5, S6, S7 };
+}

+ 119 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/SeedData/SysMenuSeedData.cs

@@ -299,6 +299,51 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
             Remark = "S0 订单优先规则"
         };
 
+        yield return new SysMenu
+        {
+            Id = subDirId + 4,
+            Pid = subDirId,
+            Title = "合同评审周期",
+            Path = "/aidop/s0/sales/contract-review-cycle",
+            Name = "aidopS0SalesContractReviewCycle",
+            Component = "/aidop/s0/sales/ContractReviewCycleList",
+            Icon = "ele-Clock",
+            Type = MenuTypeEnum.Menu,
+            CreateTime = ct,
+            OrderNo = 4,
+            Remark = "S0 合同评审周期标准配置"
+        };
+
+        yield return new SysMenu
+        {
+            Id = subDirId + 5,
+            Pid = subDirId,
+            Title = "产品设计周期",
+            Path = "/aidop/s0/sales/product-design-cycle",
+            Name = "aidopS0SalesProductDesignCycle",
+            Component = "/aidop/s0/sales/ProductDesignCycleList",
+            Icon = "ele-Timer",
+            Type = MenuTypeEnum.Menu,
+            CreateTime = ct,
+            OrderNo = 5,
+            Remark = "S0 产品设计周期标准配置"
+        };
+
+        yield return new SysMenu
+        {
+            Id = subDirId + 6,
+            Pid = subDirId,
+            Title = "订单评审周期",
+            Path = "/aidop/s0/sales/order-review-cycle",
+            Name = "aidopS0SalesOrderReviewCycle",
+            Component = "/aidop/s0/sales/OrderReviewCycleList",
+            Icon = "ele-AlarmClock",
+            Type = MenuTypeEnum.Menu,
+            CreateTime = ct,
+            OrderNo = 6,
+            Remark = "S0 订单评审周期标准配置"
+        };
+
     }
 
     private static IEnumerable<SysMenu> BuildS0ManufacturingMenus(DateTime ct)
@@ -489,6 +534,80 @@ public class SysMenuSeedData : ISqlSugarEntitySeedData<SysMenu>
     {
         const long s8DirId = 1321000009000L;
         const long baseId = 1329008000000L;
+        const long s8MonitoringDirId = 1329008000020L;
+
+        // 异常监控子模块(专题大屏占位,后续接业务数据)
+        yield return new SysMenu
+        {
+            Id = s8MonitoringDirId,
+            Pid = s8DirId,
+            Title = "异常监控",
+            Path = "/aidop/s8/monitoring",
+            Name = "aidopS8MonitoringDir",
+            Component = "Layout",
+            Redirect = "/aidop/s8/monitoring/overview",
+            Icon = "ele-Monitor",
+            Type = MenuTypeEnum.Dir,
+            CreateTime = ct,
+            OrderNo = 8,
+            Remark = "S8 异常监控子模块"
+        };
+        yield return new SysMenu
+        {
+            Id = s8MonitoringDirId + 1,
+            Pid = s8MonitoringDirId,
+            Title = "异常监控大屏",
+            Path = "/aidop/s8/monitoring/overview",
+            Name = "aidopS8MonitoringOverview",
+            Component = "/aidop/s8/monitoring/S8MonitoringOverviewPage",
+            Icon = "ele-DataBoard",
+            Type = MenuTypeEnum.Menu,
+            CreateTime = ct,
+            OrderNo = 10,
+            Remark = "S8 异常监控大屏(占位)"
+        };
+        yield return new SysMenu
+        {
+            Id = s8MonitoringDirId + 2,
+            Pid = s8MonitoringDirId,
+            Title = "交付异常大屏",
+            Path = "/aidop/s8/monitoring/delivery",
+            Name = "aidopS8MonitoringDelivery",
+            Component = "/aidop/s8/monitoring/S8MonitoringDeliveryPage",
+            Icon = "ele-Position",
+            Type = MenuTypeEnum.Menu,
+            CreateTime = ct,
+            OrderNo = 11,
+            Remark = "S8 交付异常大屏(占位)"
+        };
+        yield return new SysMenu
+        {
+            Id = s8MonitoringDirId + 3,
+            Pid = s8MonitoringDirId,
+            Title = "生产异常大屏",
+            Path = "/aidop/s8/monitoring/production",
+            Name = "aidopS8MonitoringProduction",
+            Component = "/aidop/s8/monitoring/S8MonitoringProductionPage",
+            Icon = "ele-Cpu",
+            Type = MenuTypeEnum.Menu,
+            CreateTime = ct,
+            OrderNo = 12,
+            Remark = "S8 生产异常大屏(占位)"
+        };
+        yield return new SysMenu
+        {
+            Id = s8MonitoringDirId + 4,
+            Pid = s8MonitoringDirId,
+            Title = "供应异常大屏",
+            Path = "/aidop/s8/monitoring/supply",
+            Name = "aidopS8MonitoringSupply",
+            Component = "/aidop/s8/monitoring/S8MonitoringSupplyPage",
+            Icon = "ele-Box",
+            Type = MenuTypeEnum.Menu,
+            CreateTime = ct,
+            OrderNo = 13,
+            Remark = "S8 供应异常大屏(占位)"
+        };
 
         // 可见业务页(「异常监控看板」复用自动生成的 1322000000028 菜单位)
         yield return new SysMenu

+ 1 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Service/S8/S8ExceptionService.cs

@@ -37,6 +37,7 @@ public class S8ExceptionService : ITransient
                 (e, sc) => pendingStatuses.Contains(e.Status))
             .WhereIF(!string.IsNullOrWhiteSpace(q.Severity), (e, sc) => e.Severity == q.Severity)
             .WhereIF(!string.IsNullOrWhiteSpace(q.SceneCode), (e, sc) => e.SceneCode == q.SceneCode)
+            .WhereIF(!string.IsNullOrWhiteSpace(q.ModuleCode), (e, sc) => e.ModuleCode == q.ModuleCode)
             .WhereIF(q.DeptId.HasValue, (e, sc) => e.ResponsibleDeptId == q.DeptId!.Value || e.OccurrenceDeptId == q.DeptId!.Value)
             .WhereIF(q.TimeoutFlag.HasValue, (e, sc) => e.TimeoutFlag == q.TimeoutFlag!.Value)
             .WhereIF(q.BeginTime.HasValue, (e, sc) => e.CreatedAt >= q.BeginTime!.Value)

+ 69 - 0
server/Plugins/Admin.NET.Plugin.AiDOP/Service/S8/S8MonitoringService.cs

@@ -0,0 +1,69 @@
+using Admin.NET.Plugin.AiDOP.Dto.S8;
+using Admin.NET.Plugin.AiDOP.Entity.S8;
+using Admin.NET.Plugin.AiDOP.Infrastructure.S8;
+
+namespace Admin.NET.Plugin.AiDOP.Service.S8;
+
+public class S8MonitoringService : ITransient
+{
+    private readonly SqlSugarRepository<AdoS8Exception> _rep;
+
+    public S8MonitoringService(SqlSugarRepository<AdoS8Exception> rep)
+    {
+        _rep = rep;
+    }
+
+    /// <summary>
+    /// 异常监控汇总:按 module_code 分组统计红/黄/绿/超时数。
+    /// 供综合全景页顶部徽标和模块汇总表使用。
+    /// </summary>
+    public async Task<AdoS8MonitoringSummaryDto> GetSummaryAsync(AdoS8MonitoringSummaryQueryDto q)
+    {
+        var query = _rep.AsQueryable()
+            .Where(e => e.TenantId == q.TenantId && e.FactoryId == q.FactoryId && !e.IsDeleted)
+            .WhereIF(!string.IsNullOrWhiteSpace(q.SceneCode), e => e.SceneCode == q.SceneCode)
+            .WhereIF(!string.IsNullOrWhiteSpace(q.ModuleCode), e => e.ModuleCode == q.ModuleCode)
+            .WhereIF(q.BizDateFrom.HasValue, e => e.CreatedAt >= q.BizDateFrom!.Value)
+            .WhereIF(q.BizDateTo.HasValue, e => e.CreatedAt <= q.BizDateTo!.Value);
+
+        // 聚合到内存(数据量在可控范围内,避免复杂 GROUP BY 兼容性问题)
+        var raw = await query
+            .Select(e => new
+            {
+                e.ModuleCode,
+                e.SceneCode,
+                e.Severity,
+                e.TimeoutFlag,
+                e.Status
+            })
+            .ToListAsync();
+
+        var byModule = raw
+            .GroupBy(e => new { mc = e.ModuleCode ?? string.Empty, sc = e.SceneCode ?? string.Empty })
+            .Select(g => new AdoS8ModuleSummaryItem
+            {
+                ModuleCode  = g.Key.mc,
+                ModuleLabel = S8ModuleCode.Label(g.Key.mc),
+                SceneCode   = g.Key.sc,
+                SceneLabel  = S8SceneCode.Label(g.Key.sc),
+                Total   = g.Count(),
+                Red     = g.Count(e => e.Severity == "CRITICAL" || e.Severity == "HIGH"),
+                Yellow  = g.Count(e => e.Severity == "MEDIUM"),
+                Green   = g.Count(e => e.Severity == "LOW"),
+                Timeout = g.Count(e => e.TimeoutFlag && e.Status != "CLOSED")
+            })
+            // 按 S8ModuleCode.All 顺序排列
+            .OrderBy(r => Array.IndexOf(S8ModuleCode.All, r.ModuleCode))
+            .ToList();
+
+        return new AdoS8MonitoringSummaryDto
+        {
+            Total   = raw.Count,
+            Red     = raw.Count(e => e.Severity == "CRITICAL" || e.Severity == "HIGH"),
+            Yellow  = raw.Count(e => e.Severity == "MEDIUM"),
+            Green   = raw.Count(e => e.Severity == "LOW"),
+            Timeout = raw.Count(e => e.TimeoutFlag && e.Status != "CLOSED"),
+            ByModule = byModule
+        };
+    }
+}