/**
* Generate S2 制造协同模块蓝图设计方案.docx
* 严格按照 S1 蓝图模板格式:每个功能章节含 8 个子章节
* [目标/宗旨, 业务流程图, 业务流程说明, 业务流程规则, 变化和改进点, 权限管理需求, 系统接口集成, 报表需求]
*/
const fs = require('fs');
const sharp = require('sharp');
const {
Document, Packer, Paragraph, TextRun, Table, TableRow, TableCell,
HeadingLevel, AlignmentType, BorderStyle, WidthType, ShadingType,
convertInchesToTwip, PageBreak, ImageRun, TabStopType, TabStopPosition,
InternalHyperlink, TableOfContents
} = require('docx');
// ═══════════════════════════════════════════════════
// 通用样式与辅助函数
// ═══════════════════════════════════════════════════
const FONT = '微软雅黑';
const thinBorder = {
top: { style: BorderStyle.SINGLE, size: 1 },
bottom: { style: BorderStyle.SINGLE, size: 1 },
left: { style: BorderStyle.SINGLE, size: 1 },
right: { style: BorderStyle.SINGLE, size: 1 },
};
const headerShading = { fill: '4472C4', type: ShadingType.CLEAR };
function p(text, opts = {}) {
return new Paragraph({
children: [new TextRun({ text, size: 21, font: FONT, ...opts })],
spacing: { after: 120, line: 360 },
});
}
function bp(text) { return p(text, { bold: true }); }
function empty() { return new Paragraph({ spacing: { after: 80 }, children: [] }); }
function pageBreak() { return new Paragraph({ children: [new PageBreak()] }); }
function h1(text) {
return new Paragraph({
children: [new TextRun({ text, bold: true, size: 32, font: FONT })],
heading: HeadingLevel.HEADING_1,
spacing: { before: 400, after: 200 },
});
}
function h2(text) {
return new Paragraph({
children: [new TextRun({ text, bold: true, size: 28, font: FONT })],
heading: HeadingLevel.HEADING_2,
spacing: { before: 300, after: 150 },
});
}
function h3(text) {
return new Paragraph({
children: [new TextRun({ text, bold: true, size: 24, font: FONT })],
heading: HeadingLevel.HEADING_3,
spacing: { before: 200, after: 100 },
});
}
function bullet(text, indent = 0) {
return new Paragraph({
children: [new TextRun({ text: ` ${text}`, size: 21, font: FONT })],
spacing: { after: 60, line: 340 },
indent: { left: 300 + indent * 400 },
});
}
// Table helpers
function hdrCell(text, w = 1800) {
return new TableCell({
children: [new Paragraph({ children: [new TextRun({ text, bold: true, size: 20, font: FONT, color: 'FFFFFF' })], alignment: AlignmentType.CENTER })],
width: { size: w, type: WidthType.DXA }, borders: thinBorder, shading: headerShading,
});
}
function tc(text, w = 1800, opts = {}) {
return new TableCell({
children: [new Paragraph({ children: [new TextRun({ text: String(text ?? ''), size: 20, font: FONT })], alignment: opts.center ? AlignmentType.CENTER : AlignmentType.LEFT })],
width: { size: w, type: WidthType.DXA }, borders: thinBorder,
});
}
function table(headers, rows, widths) {
return new Table({
rows: [
new TableRow({ children: headers.map((h, i) => hdrCell(h, widths ? widths[i] : 1800)), tableHeader: true }),
...rows.map(r => new TableRow({ children: r.map((c, i) => tc(c, widths ? widths[i] : 1800)) })),
],
width: { size: 100, type: WidthType.PERCENTAGE },
});
}
// 活动流程表(6列)
function activityTable(rows) {
const w = [600, 1400, 1100, 3800, 1400, 1700];
return table(['编号', '活动名称', '执行角色', '活动描述', '输入', '输出'], rows, w);
}
// 业务规则表(3列)
function ruleTable(rows) {
return table(['序列号', '业务情形', '描述/方案'], rows, [900, 2800, 6300]);
}
// 接口表(4列)
function interfaceTable(rows) {
return table(['序号', '接口名称', '接口说明', '频次及触发方式'], rows, [600, 2400, 4200, 2800]);
}
// 报表表(4列)
function reportTable(rows) {
return table(['序号', '名称', '描述', '方案'], rows, [600, 2600, 3600, 3200]);
}
// 权限表(3列)
function permTable(rows) {
return table(['序号', '岗位名称', '对应系统权限'], rows, [600, 2000, 7400]);
}
// ── 目录条目(模板格式) ──
const TOC2_TAB = 2880;
const TOC3_TAB = 3600;
const TOC_FONT_SIZE = 20;
function tocChapter(num, title) {
return new Paragraph({
children: [
new TextRun({ text: String(num), size: TOC_FONT_SIZE, font: FONT }),
new TextRun({ text: '\t', size: TOC_FONT_SIZE, font: FONT }),
new TextRun({ text: title, bold: true, size: TOC_FONT_SIZE, font: FONT }),
new TextRun({ text: '\t', size: TOC_FONT_SIZE, font: FONT }),
new TextRun({ text: ' ', size: TOC_FONT_SIZE, font: FONT }),
],
tabStops: [
{ type: TabStopType.LEFT, position: TOC2_TAB },
{ type: TabStopType.RIGHT, position: TabStopPosition.MAX },
],
spacing: { before: 80, after: 40 },
});
}
function tocSub(num, title) {
return new Paragraph({
children: [
new TextRun({ text: String(num), size: TOC_FONT_SIZE, font: FONT }),
new TextRun({ text: '\t', size: TOC_FONT_SIZE, font: FONT }),
new TextRun({ text: title, size: TOC_FONT_SIZE, font: FONT }),
new TextRun({ text: '\t', size: TOC_FONT_SIZE, font: FONT }),
new TextRun({ text: ' ', size: TOC_FONT_SIZE, font: FONT }),
],
tabStops: [
{ type: TabStopType.LEFT, position: TOC3_TAB },
{ type: TabStopType.RIGHT, position: TabStopPosition.MAX },
],
spacing: { before: 30, after: 30 },
});
}
function buildTOC() {
const ch = [];
ch.push(new TableOfContents('目录', {
headingStyleRange: '1-2',
hyperlink: true,
}));
ch.push(new Paragraph({
children: [new TextRun({ text: '(如目录未显示,请在 Word 中右键此处 → 更新域)', size: 18, font: FONT, italics: true, color: '888888' })],
spacing: { before: 40, after: 200 },
}));
// 文档控制
ch.push(tocChapter('', '文档控制'));
ch.push(tocSub('', '更改记录'));
ch.push(tocSub('', '审核'));
ch.push(tocSub('', '发布'));
ch.push(tocChapter('', '目录'));
// 第1章
ch.push(tocChapter('1', '总体业务方案'));
ch.push(tocSub('1.1', '目标和宗旨'));
ch.push(tocSub('1.2', '总体业务流程图'));
ch.push(tocSub('1.3', '方案设计'));
// 第2~4章(8子章节)
const chSubs = [
'目标/宗旨', '业务流程图', '业务流程说明', '业务流程规则',
'变化和改进点', '权限管理需求', '系统接口集成', '报表需求',
];
const chTitles = ['生产排程', '作业计划', '制造协同看板与KPI'];
for (let i = 0; i < chTitles.length; i++) {
const n = i + 2;
ch.push(tocChapter(String(n), chTitles[i]));
for (let j = 0; j < chSubs.length; j++) {
ch.push(tocSub(`${n}.${j + 1}`, chSubs[j]));
}
}
// 第5章 数据中台
ch.push(tocChapter('5', '数据中台架构'));
['目标/宗旨', '分层设计', 'S2核心数据流', '对象映射总表', 'MDP作业调度'].forEach((title, i) => {
ch.push(tocSub(`5.${i + 1}`, title));
});
// 第6章 权限管理汇总
ch.push(tocChapter('6', '权限管理汇总'));
return ch;
}
// ═══════════════════════════════════════════════════
// 流程图生成
// ═══════════════════════════════════════════════════
const FLOW_W = 680, BOX_W = 220, BOX_H = 42, GAP_Y = 28;
const ROLE_X = 30, BOX_X = 130, MARGIN_Y = 40;
const flowImageCache = {};
function flowImageParagraph(pngBuffer) {
return new Paragraph({
children: [new ImageRun({ data: pngBuffer, transformation: { width: 540, height: 360 }, type: 'png' })],
alignment: AlignmentType.CENTER,
spacing: { before: 120, after: 120 },
});
}
async function generateFlowPNG(steps) {
const N = steps.length;
const svgH = MARGIN_Y * 2 + N * (BOX_H + GAP_Y) - GAP_Y;
const centerX = BOX_X + BOX_W / 2;
let svg = `';
return sharp(Buffer.from(svg)).png().toBuffer();
}
// 第1章专用:S2总体复合流程图
async function generateOverviewFlowPNG() {
const W = 780, H = 580;
let svg = `';
return sharp(Buffer.from(svg)).png().toBuffer();
}
// ═══════════════════════════════════════════════════
// 章节构建函数
// ═══════════════════════════════════════════════════
function buildChapter(num, title, cfg) {
const ch = [];
const pre = `${num}.`;
// §X.1 目标/宗旨
ch.push(h2(`${pre}1 目标/宗旨`));
(cfg.targets || []).forEach(t => ch.push(bullet(t)));
// §X.2 业务流程图
ch.push(h2(`${pre}2 业务流程图`));
const img = cfg.flowImage || (cfg.flowSteps ? flowImageCache[num] : null);
if (img) {
ch.push(flowImageParagraph(img));
} else {
ch.push(p('(见系统操作流程图 / 附件 Visio 流程图)'));
}
if (cfg.flowNote) ch.push(p(cfg.flowNote));
// §X.3 业务流程说明
ch.push(h2(`${pre}3 业务流程说明`));
if (cfg.activities && cfg.activities.length) {
ch.push(activityTable(cfg.activities));
} else {
ch.push(p('参照系统页面操作流程,主要操作步骤包含:'));
(cfg.activityDesc || []).forEach(a => ch.push(bullet(a)));
}
// §X.4 业务流程规则
ch.push(h2(`${pre}4 业务流程规则`));
if (cfg.rules && cfg.rules.length) {
ch.push(ruleTable(cfg.rules));
} else {
ch.push(p('无特殊业务规则'));
}
// §X.5 变化和改进点
ch.push(h2(`${pre}5 变化和改进点`));
(cfg.improvements || []).forEach(i => ch.push(bullet(i)));
// §X.6 权限管理需求
ch.push(h2(`${pre}6 权限管理需求`));
if (cfg.permissions && cfg.permissions.length) {
ch.push(permTable(cfg.permissions));
} else {
ch.push(p('参照第 6 章 权限管理汇总'));
}
// §X.7 系统接口集成
ch.push(h2(`${pre}7 系统接口集成`));
if (cfg.interfaces && cfg.interfaces.length) {
ch.push(interfaceTable(cfg.interfaces));
} else {
ch.push(p('无外部系统接口;页面数据通过 Admin.NET 内置 API 获取'));
}
// §X.8 报表需求
ch.push(h2(`${pre}8 报表需求`));
if (cfg.reports && cfg.reports.length) {
ch.push(reportTable(cfg.reports));
} else {
ch.push(p('无独立报表需求,相关数据通过看板和列表页面查看'));
}
return ch;
}
// ═══════════════════════════════════════════════════
// 章节数据配置
// ═══════════════════════════════════════════════════
// ── Chapter 2: 生产排程 ──
const _ch2cfg = {
targets: [
'实现生产工单的在线排程管理,将S1下达的工单按工序展开并确定执行优先级。',
'通过"生产排程"按钮统一触发工单排程计算,结合产线日历和资源产能约束生成可执行的工序任务。',
'支持工单优先级调整(加急/特急),快速响应生产异常和插单需求。',
'提供排产异常记录管理和工单关闭功能,形成完整的排程闭环。',
'工单工序排程中支持物料需求同步(MRP联动)、工艺路线同步和工序明细查看。',
],
flowNote: '流程路径:工单工序排程列表 → 筛选查询工单 → 生产排程(工具栏按钮) → 同步物料需求 → 调整优先级 → 排产数据维护异常记录 → 工单关闭。',
flowSteps: [
{ label: '工单工序排程列表查询', role: '计划员/调度' },
{ label: '生产排程(工具栏触发)', role: '计划员/调度' },
{ label: '同步物料需求(MRP)', role: '计划员/调度' },
{ label: '优先级调整(加急/特急)', role: '计划员/调度' },
{ label: '工单关闭 / 异常处理', role: '计划员/调度' },
],
activities: [
['10', '工单排程列表查询', '计划员/调度', '通过工单编号、生产批号、物料编码、开工日期、状态等条件筛选排程工单', '筛选条件', '排程工单列表'],
['20', '生产排程', '计划员/调度', '点击工具栏"生产排程"按钮,系统根据排程算法(考虑产线日历、工序、产能)生成排产结果', '工单列表+产线日历', '排产结果'],
['30', '同步物料需求', '计划员/调度', '点击"同步物料需求"按钮,同步生成物料需求计划(MRP),确保排产物料齐套', '工单列表', 'MRP需求'],
['40', '工单优先级别管理', '计划员/调度', '通过操作列"加急"或"特急"按钮调整工单优先级;弹出优先级调整表单,可选择原因说明', '生产异常/插单需求', '优先级更新'],
['50', '同步工艺路线', '计划员/调度', '点击"同步工艺路线"按钮,从产品设计中同步最新工艺路线到排程工单', '产品设计工艺', '工单工序明细'],
['60', '工单执行追踪', '计划员/调度', '点击"工单执行追踪"查看工单全流程执行进度', '工单编号', '执行进度'],
['70', '排产异常记录', '计划员/调度', '在排产异常记录页面新增/编辑/删除排产数据维护异常记录,记录工单编号、物料编码、操作类型和执行时间', '排产数据维护', '异常记录'],
['80', '工单关闭', '计划员/调度', '勾选多行工单后点击"工单关闭"按钮(批量操作),将工单状态置为已关闭', '选中的工单行', '已关闭工单'],
],
rules: [
['1', '排程数据来源', '工单排程页面数据来自WorkOrdMaster表,通过/api/WorkOrder/scheduling/list读取'],
['2', '工单状态流转', '待下达(S1)→已排产→生产中→已完成→已关闭;状态通过行内下拉框直接修改'],
['3', '优先级枚举', '优先级枚举值:1=普通,2=加急,3=特急;通过PriorityDialogForm选择调整'],
['4', '工单关闭', '支持批量勾选后一次性关闭,前端按钮在未选行时置灰;关闭操作不可逆(后端校验工单状态、数量约束)'],
['5', '排程算法', '生产排程按钮触发后端排程服务,综合考虑产线工作日历、工序顺序、资源产能约束'],
['6', '排产异常管理', '异常记录(ScheduleExceptionMaster表)支持独立CRUD,与排程工单通过工单编号关联;记录操作类型和执行时间'],
['7', '工单编码唯一性', '工单编号全局唯一,由上游S1系统生成后传入'],
['8', '防重复排程', '已排程且生产中的工单不可重复排程;排程前检查工单状态'],
],
improvements: [
'实现在线生产排程管理,替代线下Excel排程方式。',
'工单优先级在线调整(加急/特急),支持快速响应插单和异常。',
'排产异常记录可追溯,形成完整的排产数据维护日志。',
'工单关闭归口统一,与S1下达形成闭环联动。',
'工艺路线同步机制确保排程与设计数据一致。',
],
permissions: [
['1', '计划员/调度', '查看工单排程列表;执行生产排程;同步物料需求;调整优先级;工单关闭;管理排产异常记录'],
['2', '车间主管', '查看工单排程列表和排产异常记录'],
],
interfaces: [
['1', '工单排程列表API', 'GET /api/WorkOrder/scheduling/list 读取WorkOrdMaster表', '实时(页面加载)'],
['2', '生产排程API', 'POST /api/WorkOrder/scheduling/production-schedule 触发后端排程计算', '手动触发(工具栏)'],
['3', '同步物料需求API', 'POST /api/WorkOrder/scheduling/sync-material 触发MRP需求计算', '手动触发(工具栏)'],
['4', '工单关闭API', 'POST /api/WorkOrder/scheduling/batch-close 批量关闭工单', '手动触发(工具栏)'],
['5', '排产异常CRUD API', 'GET/POST/PUT/DELETE /api/ScheduleException/* 排产异常记录管理', '实时(CRUD)'],
['6', '同步工艺路线API', 'POST /api/WorkOrder/scheduling/sync-routing 从产品设计同步工艺到工单', '手动触发(行操作)'],
['7', '工单状态同步MES', '排程结果和工单状态变更同步至MES系统', '实时'],
],
reports: [
['1', '工单排程达成率', '统计生产排程与工单下达的时间差和排程覆盖率', '按周/按月'],
['2', '排产异常统计', '按异常类型、物料、产线统计排产异常次数和分布', '按日/周/月'],
],
};
const ch2 = () => buildChapter('2', '生产排程', _ch2cfg);
// ── Chapter 3: 作业计划 ──
const _ch3cfg = {
targets: [
'在工单排程完成后,生成面向车间产线的可执行日计划,将排产结果精确到每天、每道工序、每台设备。',
'管理产线工作日历(班次定义),为排程和日计划提供可用工时基础数据。',
'管理产线休息时间,定义每日固定停休时段,排程时自动避开。',
'管理产线节假日(休假/调班),确保排程不占用法定休息日。',
'管理产线加班时段(ResourceOccupancyTime),为临时加产提供延长工时支持。',
'通过日计划下达操作,将确认的日计划下发至S6 MES执行层。',
],
flowNote: '流程路径:可执行日计划列表 → 筛选(计划日期≥今天默认) → 勾选多行 → 点击下达 → 日计划状态更新。同时维护:产线工作日历 → 休息时间 → 节假日 → 加班(基础数据CRUD)。',
flowSteps: [
{ label: '可执行日计划筛选', role: '计划员/调度' },
{ label: '勾选待下达日计划行', role: '计划员/调度' },
{ label: '批量下达(工具栏)', role: '计划员/调度' },
{ label: '产线日历+休息+节假日维护', role: '基础数据管理员' },
{ label: '加班时段录入', role: '计划员/调度' },
],
activities: [
['10', '日计划列表查询', '计划员/调度', '通过生产指令、物料编码、生产批次、工单状态、日计划下达状态、工作中心、设备类型、工序、计划日期等条件筛选', '筛选条件', '日计划列表'],
['20', '日计划下达', '计划员/调度', '勾选多行可执行日计划后点击"下达"按钮(批量操作),将日计划下发至MES执行层', '选中的日计划行', '已下达日计划'],
['30', '产线工作日历管理', '基础数据管理员', '新增/编辑/删除产线工作日历记录,定义每条产线每周每天的班次开始时间和时长(班次1+班次2)', '产线信息', '工作日历记录'],
['40', '产线休息时间管理', '基础数据管理员', '新增/编辑/删除产线休息时间记录,定义每个产线的固定休息开始时间点和休息时长(分钟)', '产线信息', '休息时间记录'],
['50', '产线节假日管理', '基础数据管理员', '新增/编辑/删除节假日记录,类型可选"休假"或"调班";排程时自动避开休假日期', '节假日定义', '节假日记录'],
['60', '产线加班管理', '计划员/调度', '新增/编辑/删除产线加班时段记录,定义加班起止时间、有效工作时长和休息时长', '加班需求', '加班记录'],
],
rules: [
['1', '日计划默认筛选', '默认计划日期≥今天、工单未关闭;可通过"计划日期≥"条件调至更早日期查看历史数据'],
['2', '日计划下达为批量操作', '支持勾选多行后一次性下达;未选行时下达按钮置灰'],
['3', '工作中心下拉筛选', '支持从数据源远程搜索和筛选工作中心'],
['4', '产线工作日历字段', '星期(WeekDayName)、生产线(ProdLine)、描述、班次1开始/时长(小时)、班次2开始/时长(小时)'],
['5', '产线休息时间字段', '生产线(关联LineMaster)、产线描述、休息开始时间点、休息时长(分钟)、备注'],
['6', '节假日类型', '类型包含"休假"和"调班"两种,排程时自动过滤休假日期'],
['7', '加班记录字段', '产线(Resource)、开始/结束时间、有效工作时长(分钟)、休息时长(分钟)、类型、描述'],
['8', '基础数据关联', '产线工作日历和休息时间均关联LineMaster产线主数据;下拉选择时显示「Line|Describe」格式'],
],
improvements: [
'可执行日计划实现在线可视化,精确到每道工序和设备的日产能分配。',
'产线工作日历在线管理,替代线下纸质排班表。',
'休息时间、节假日、加班统一纳入系统,排程自动考虑产能约束。',
'日计划下达与MES联动,信息在线流转替代人工传递。',
],
permissions: [
['1', '计划员/调度', '查看可执行日计划列表;批量下达;查看产线日历、休息、节假日、加班信息'],
['2', '基础数据管理员', '新增/编辑/删除产线工作日历、休息时间、节假日、加班记录'],
['3', '车间主管', '查看可执行日计划列表和产线基础数据'],
],
interfaces: [
['1', '可执行日计划列表API', 'GET /api/ScheduleDailyPlan/list 读取日计划数据', '实时(页面加载)'],
['2', '日计划下达API', 'POST /api/ScheduleDailyPlan/release 批量下达日计划', '手动触发(工具栏)'],
['3', '产线工作日历API', 'GET/POST/PUT/DELETE /api/ShopCalendarWorkCtr/*', '实时(CRUD)'],
['4', '产线休息时间API', 'GET/POST/PUT/DELETE /api/QualityLineRestDetail/*', '实时(CRUD)'],
['5', '产线节假日API', 'GET/POST/PUT/DELETE /api/HolidayMaster/*', '实时(CRUD)'],
['6', '产线加班API', 'GET/POST/PUT/DELETE /api/ResourceOccupancyTime/*', '实时(CRUD)'],
['7', '产线下拉查询API', 'GET /api/LineMaster/list 获取产线列表(供工作日历等下拉选项使用)', '实时(页面加载/下拉触发)'],
],
reports: [
['1', '日计划执行率', '统计每日计划下达后的实际执行完成比率', '按日/周/月'],
['2', '产线利用率统计', '按产线统计总有效工时与计划工时的对比', '按周/按月'],
],
};
const ch3 = () => buildChapter('3', '作业计划', _ch3cfg);
// ── Chapter 4: 制造协同看板与KPI ──
const _ch4cfg = {
targets: [
'为管理层和业务人员提供S2模块的核心KPI可视化看板。',
'通过工单执行进度看板展示每张工单的全流程关键进度,包括工单数量、入库数量、未入库数量、领料状态和进度百分比。',
'通过指标看板(DynamicModuleDashboard, module-code="S2")展示L1层级核心指标。',
'将S2模块的排程满足率、排程周期、在制库存周转天数和排程人效纳入KPI考核体系。',
],
flowNote: '流程路径:工单执行进度看板 → 筛选查询 → 查看工单进度条和领料状态 → 定位于制造协同指标看板 → L1核心指标卡片 → 趋势分析。',
flowSteps: [
{ label: '进入工单执行进度看板', role: '管理层/计划员' },
{ label: '筛选查询工单', role: '计划员' },
{ label: '查看进度条和领料状态', role: '计划员' },
{ label: '进入KPI指标看板', role: '管理层' },
{ label: '分析L1核心指标', role: '管理层' },
],
activities: [
['10', '查看工单进度看板', '管理层/计划员', '进入工单执行进度看板页面,按工单编号、物料编码、计划开工日期筛选', '筛选条件', '工单进度列表'],
['20', '分析工单进度', '计划员', '查看进度百分条(完成数量/工单数量);关注领料状态和未入库数量', '工单进度数据', '进度分析'],
['30', '查看看板KPI', '管理层', '进入S2制造协同指标看板,查看L1核心KPI卡片', '—', 'L1指标展示'],
['40', '指标下钻分析', '管理层', '从L1指标下钻到L2分解指标,分析趋势变化', 'L1指标', '趋势图表'],
],
rules: [
['1', '进度看板数据来源', '工单进度数据来自WorkOrdMaster,关联工艺默认产线和MES领料状态'],
['2', '进度计算规则', '进度百分比 = 入库数量(QtyCompleted) / 工单数量(QtyOrded) × 100%'],
['3', '单据类型', '单据类型枚举:s=销售工单、p=计划工单;通过typed列区分'],
['4', '单据状态', '状态枚举:w=投产、r=下达、c=关闭;看板中为只读展示'],
['5', 'L1核心指标(S2)', '订单排程满足率(S2-L1-A)、订单排程周期(S2-L1-B)、在制库存周转天数(S2-L1-C)、订单排程人效(S2-L1-D)'],
['6', '看板组件架构', '使用DynamicModuleDashboard动态看板组件,module-code="S2"'],
['7', '指标存储', '指标定义写入ado_smart_ops_kpi_master;计算结果写入ado_s9_kpi_value_* 按天粒度存储'],
['8', '领料状态', '领料状态(materialSituation)关联MES/WMS领料数据,反映工单物料准备的实时状况'],
],
improvements: [
'工单进度看板实现在线可视化,替代人工跟单和纸质报表方式。',
'领料状态和进度条直观展示,管理层一目了然。',
'KPI自动化计算替代人工统计,排程满足率和周期指标实时更新。',
'看板与数据中台对接,确保数据口径一致。',
],
permissions: [
['1', '管理层', '查看S2看板全部指标和工单进度看板'],
['2', '计划员/调度', '查看工单进度看板;按条件筛选分析'],
],
interfaces: [
['1', '工单进度看板API', 'GET /api/WorkOrderProgressDashboard/list', '实时(页面加载)'],
['2', 'S2看板聚合API', 'GET /api/AidopKanban/module-detail?moduleCode=S2', '实时(页面加载)'],
['3', 'KPI数据源', 'ado_s9_kpi_value_l1~l4_day 按天粒度存储', 'MDP定时刷新'],
],
reports: [],
};
const ch4 = () => buildChapter('4', '制造协同看板与KPI', _ch4cfg);
// ── Chapter 5: 数据中台架构 ──
function buildCh5() {
const ch = [];
ch.push(h1('5 数据中台架构'));
ch.push(h2('5.1 目标/宗旨'));
ch.push(p('S2制造协同模块采用Ai-DOP统一数据中台分层架构,将工单排程数据、产线基础数据、MES执行数据经过贴源(STG)、标准化(STD)、宽表构建(DWD)、指标计算(KPI)四层处理,最终服务于前端排程列表、日计划、进度看板和KPI考核。'));
ch.push(h2('5.2 分层设计'));
ch.push(table(
['分层', '表前缀', '职责', '更新机制'],
[
['贴源层(STG)', 'mdp_stg_*', '保留源系统原始数据,含raw_data/source_table/sync_batch_id', 'MDP同步覆盖写入'],
['标准层(STD)', 'mdp_std_*', '统一字段口径,清洗脏数据,形成标准业务对象', 'MDP转换(取最新批次)'],
['宽表层(DWD)', 'dwd_*', '面向页面和KPI的主题事实表,关联多源数据', 'MDP转换(取最新批次)'],
['指标层(KPI)', 'ado_s9_kpi_value_*', '按天存储L1-L4指标值', 'MDP KPI计算(取最新批次)'],
],
[1500, 2000, 4200, 2300]
));
ch.push(h2('5.3 S2核心数据流'));
ch.push(bp('工单排程/日计划链路'));
ch.push(p('WorkOrdMaster / ScheduleDailyPlan / ScheduleExceptionMaster'));
ch.push(p(' → mdp_stg_production(贴源层)→ mdp_std_production(标准层)→ dwd_production_schedule(宽表)→ ado_s9_kpi_value_*(指标层)'));
ch.push(bp('产线基础数据链路'));
ch.push(p('ShopCalendarWorkCtr / QualityLineRestDetail / HolidayMaster / ResourceOccupancyTime'));
ch.push(p(' → mdp_stg_production(贴源层)→ mdp_std_production(标准层)→ dwd_line_calendar(宽表)→ 排程产能约束计算'));
ch.push(bp('MES执行反馈链路'));
ch.push(p('MES领料状态 / 完工入库数据 → mdp_stg_production → dwd_production_schedule(宽表)→ 工单进度看板'));
ch.push(h2('5.4 对象映射总表'));
ch.push(table(
['业务对象', '运行表', '贴源层', '标准层/DWD', '消费端'],
[
['工单排程', 'WorkOrdMaster', 'mdp_stg_production', 'mdp_std_production / dwd_production_schedule', '排程列表、日计划、KPI'],
['可执行日计划', 'ScheduleDailyPlan', 'mdp_stg_production', 'mdp_std_production / dwd_production_schedule', '日计划列表'],
['排产异常', 'ScheduleExceptionMaster', 'mdp_stg_production', 'mdp_std_production', '异常记录列表'],
['产线工作日历', 'ShopCalendarWorkCtr', 'mdp_stg_production', 'mdp_std_production / dwd_line_calendar', '日历管理、排程约束'],
['产线休息时间', 'QualityLineRestDetail', 'mdp_stg_production', 'mdp_std_production / dwd_line_calendar', '休息时间管理'],
['产线节假日', 'HolidayMaster', 'mdp_stg_production', 'mdp_std_production / dwd_line_calendar', '节假日管理'],
['产线加班', 'ResourceOccupancyTime', 'mdp_stg_production', 'mdp_std_production / dwd_line_calendar', '加班管理'],
['工单进度', 'WorkOrdMaster+领料状态', 'mdp_stg_production', 'dwd_production_schedule', '进度看板'],
],
[1100, 2000, 1800, 2600, 2500]
));
ch.push(h2('5.5 MDP作业调度'));
ch.push(table(
['配置项', '内容'],
[
['作业编码', 'S2_MDP_SYNC_TRANSFORM'],
['批次格式', 'S2_MDP_FULL_yyyyMMddHHmmss'],
['手动刷新入口', 'POST /api/Production/linkageplan/refresh'],
['监控入口', 'mdp_transform_run_log(作业日志)、mdp_sync_log(同步日志)、前端MDP监控页'],
['执行方式', '手动触发 + 未来扩展定时调度'],
],
[2200, 7800]
));
ch.push(h2('5.6 变化和改进点'));
ch.push(bullet('建立统一数据中台分层架构,S2排程和日计划数据从运行时表标准化到中台。'));
ch.push(bullet('产线基础数据(日历、休息、节假日、加班)纳入中台,确保排程约束数据唯一可信。'));
ch.push(bullet('MES执行反馈数据接入中台,驱动工单进度看板实时更新。'));
ch.push(bullet('MDP作业可观测,支持日志查询和异常追溯。'));
return ch;
}
// ── Chapter 6: 权限管理汇总 ──
function buildCh6() {
const ch = [];
ch.push(h1('6 权限管理汇总'));
ch.push(p('以下汇总S2制造协同模块各功能所涉及的角色和权限:'));
ch.push(table(
['序号', '角色', '权限范围', '涉及模块'],
[
['1', '计划员/调度', '查看工单排程列表;执行生产排程;同步物料需求;调整优先级(加急/特急);工单关闭;查看/下达可执行日计划;管理排产异常记录;查看工单进度看板', '生产排程、作业计划、制造协同看板'],
['2', '基础数据管理员', '新增/编辑/删除产线工作日历、休息时间、节假日、加班时段', '作业计划(基础数据)'],
['3', '车间主管', '查看工单排程列表、可执行日计划列表、产线基础数据和进度看板', '生产排程、作业计划、制造协同看板'],
['4', '管理层', '查看S2看板全部KPI指标和工单进度看板', '制造协同看板'],
['5', '系统管理员', '管理用户角色权限;监控MDP作业状态;数据修复操作', '系统配置'],
],
[600, 1800, 5000, 2600]
));
return ch;
}
// ═══════════════════════════════════════════════════
// 文档构建主流程
// ═══════════════════════════════════════════════════
async function buildDocument() {
// 预生成所有流程图 PNG
console.log('Generating flow chart images...');
flowImageCache['overview'] = await generateOverviewFlowPNG();
const allChapterConfigs = { '2': _ch2cfg, '3': _ch3cfg, '4': _ch4cfg };
for (const [num, cfg] of Object.entries(allChapterConfigs)) {
if (cfg.flowSteps) {
flowImageCache[num] = await generateFlowPNG(cfg.flowSteps);
}
}
console.log('Flow chart images generated.');
const sections = [];
// ── 封面 ──
sections.push(empty(), empty(), empty());
sections.push(new Paragraph({
children: [new TextRun({ text: 'S2 制造协同模块', bold: true, size: 52, font: FONT, color: '1F4E79' })],
alignment: AlignmentType.CENTER, spacing: { after: 100 },
}));
sections.push(new Paragraph({
children: [new TextRun({ text: '蓝图设计方案', bold: true, size: 52, font: FONT, color: '1F4E79' })],
alignment: AlignmentType.CENTER, spacing: { after: 600 },
}));
sections.push(new Paragraph({
children: [new TextRun({ text: 'Ai-DOP 智慧运营决策平台', size: 28, font: FONT, color: '4472C4' })],
alignment: AlignmentType.CENTER, spacing: { after: 120 },
}));
sections.push(new Paragraph({
children: [new TextRun({ text: '版本:V1.0 日期:2026年6月8日 作者:彭熙玉', size: 22, font: FONT, color: '808080' })],
alignment: AlignmentType.CENTER, spacing: { after: 2400 },
}));
sections.push(new Paragraph({
children: [new TextRun({ text: '【机密文档 · 仅限项目内部使用】', size: 21, font: FONT, color: 'C00000', italics: true })],
alignment: AlignmentType.CENTER,
}));
// ── 文档控制 ──
sections.push(pageBreak(), h1('文档控制'));
sections.push(h2('更改记录'));
sections.push(table(['日期', '姓名', '版本', '变更说明'],
[['2026/06/08', '彭熙玉', 'V1.0', '初版(基于DOP整体方案设计和S2模块实际实现)']],
[1800, 1200, 1000, 6000]));
sections.push(empty());
sections.push(h2('审核'));
sections.push(table(['姓名', '职位', '签字/日期'], [['', '', ''], ['', '', '']], [3000, 3000, 4000]));
sections.push(empty());
sections.push(h2('发布'));
sections.push(table(['编号', '名称', '地点'], [['', '', '']], [3000, 4000, 3000]));
// ── 目录 ──
sections.push(pageBreak(), h1('目录'));
sections.push(...buildTOC());
sections.push(pageBreak());
// ── 第1章: 总体业务方案 ──
sections.push(h1('1 总体业务方案'));
sections.push(h2('1.1 目标和宗旨'));
sections.push(bullet('S2制造协同模块是Ai-DOP平台"从排程到执行"的核心衔接层,承接S1下达的工单,完成生产排程、作业计划编制和制造进度监控。'));
sections.push(bullet('通过生产排程将工单按工序展开、确定优先级并计算产能约束,生成可执行的工序任务。'));
sections.push(bullet('通过作业计划将排程结果精细化到每日每道工序,并管理产线工作日历、休息时间、节假日和加班等基础产能数据。'));
sections.push(bullet('通过制造协同看板实时监控工单执行进度,驱动KPI考核和持续改善。'));
sections.push(h2('1.2 总体业务流程图'));
sections.push(p('S2制造协同模块承接S1下达的工单,完成生产排程和作业计划编制,并将结果下发至S6 MES执行层。下图展示了主线流程(排程到执行)、基础数据管理(支撑排程)和底层数据中台链路的整体关系:'));
sections.push(flowImageParagraph(flowImageCache['overview']));
sections.push(h2('1.3 方案设计'));
sections.push(p('S2模块作为Ai-DOP S0-S9模块化架构中的"制造协同"功能域,覆盖以下核心功能模块:'));
sections.push(bullet('生产排程:工单工序排程(优先级管理、物料同步、工艺同步、工单关闭)+ 排产异常记录管理'));
sections.push(bullet('作业计划:可执行日计划(批量下达)+ 产线工作日历/休息时间/节假日/加班(基础数据管理)'));
sections.push(bullet('制造协同看板:工单执行进度看板(进度条+领料状态)+ KPI指标看板(L1/L2层级)'));
sections.push(p('S2模块与S1(工单下达)、S6(生产执行/MES)紧密衔接:从S1接收已下达工单,排程后生成日计划下发给S6,并接收MES领料和入库反馈更新进度看板。基础数据(产线日历等)为排程算法提供可用产能约束,确保排程结果的可行性和准确性。'));
// ── 第2~4章: 各功能模块 ──
sections.push(pageBreak()); sections.push(h1('2 生产排程')); sections.push(...ch2());
sections.push(pageBreak()); sections.push(h1('3 作业计划')); sections.push(...ch3());
sections.push(pageBreak()); sections.push(h1('4 制造协同看板与KPI')); sections.push(...ch4());
// ── 第5~6章 ──
sections.push(pageBreak()); sections.push(...buildCh5());
sections.push(pageBreak()); sections.push(...buildCh6());
return sections;
}
// ═══════════════════════════════════════════════════
// 生成文档
// ═══════════════════════════════════════════════════
async function main() {
const doc = new Document({
styles: { default: { document: { run: { font: FONT, size: 21 } } } },
sections: [{
properties: { page: { margin: { top: convertInchesToTwip(0.8), bottom: convertInchesToTwip(0.8), left: convertInchesToTwip(1.0), right: convertInchesToTwip(1.0) } } },
children: await buildDocument(),
}],
});
const buffer = await Packer.toBuffer(doc);
const outPath = 'd:\\DEMONET\\doc\\S2制造协同模块蓝图设计方案.docx';
const fallbackPath = 'd:\\DEMONET\\doc\\S2制造协同模块蓝图设计方案_V2.docx';
const tempPath = 'd:\\DEMONET\\doc\\S2_blueprint_temp.docx';
fs.writeFileSync(tempPath, buffer);
try { fs.unlinkSync(outPath); } catch(e) { /* 被占用则跳过 */ }
try { fs.renameSync(tempPath, outPath); console.log(`Renamed to: ${outPath}`); }
catch(e) {
try { fs.unlinkSync(fallbackPath); } catch(_) {}
try { fs.renameSync(tempPath, fallbackPath); console.log(`Fallback to: ${fallbackPath}`); }
catch(e2) { console.log(`Generated: ${tempPath} (target locked)`); }
}
console.log(`Size: ${(buffer.length / 1024).toFixed(1)} KB`);
}
main().catch(err => { console.error('Error:', err); process.exit(1); });