Files
MES_Manage_View_V20/work/system-manual/build-system-issues.ps1

383 lines
24 KiB
PowerShell
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
$ErrorActionPreference = 'Stop'
$root = (Resolve-Path (Join-Path $PSScriptRoot '..\..')).Path
$prepared = Join-Path $PSScriptRoot 'prepared'
$docDir = Join-Path $root 'doc'
$desktop = [Environment]::GetFolderPath('Desktop')
$fileName = '大连元利流体技术有限公司MES系统_系统管理问题清单_2026-07-23'
$docxPath = Join-Path $docDir ($fileName + '.docx')
$pdfPath = Join-Path $docDir ($fileName + '.pdf')
$desktopDocx = Join-Path $desktop ($fileName + '.docx')
$desktopPdf = Join-Path $desktop ($fileName + '.pdf')
New-Item -ItemType Directory -Force -Path $docDir | Out-Null
$word = $null
$doc = $null
$buildError = $null
try {
$word = New-Object -ComObject Word.Application
$word.Visible = $false
$word.DisplayAlerts = 0
$doc = $word.Documents.Add()
$selection = $word.Selection
$doc.PageSetup.PageWidth = $word.CentimetersToPoints(21)
$doc.PageSetup.PageHeight = $word.CentimetersToPoints(29.7)
$doc.PageSetup.TopMargin = $word.CentimetersToPoints(2.2)
$doc.PageSetup.BottomMargin = $word.CentimetersToPoints(2.0)
$doc.PageSetup.LeftMargin = $word.CentimetersToPoints(2.1)
$doc.PageSetup.RightMargin = $word.CentimetersToPoints(2.1)
$normal = $doc.Styles.Item(-1)
$normal.Font.NameFarEast = '宋体'
$normal.Font.Name = 'Arial'
$normal.Font.Size = 10.5
$normal.ParagraphFormat.SpaceAfter = 6
function Move-End {
$end = $doc.Content.End - 1
$selection.SetRange($end, $end)
}
function Add-Text {
param(
[string]$Text,
[double]$Size = 10.5,
[bool]$Bold = $false,
[int]$Align = 0,
[int]$Color = 0,
[double]$Before = 0,
[double]$After = 6,
[double]$FirstIndentCm = 0
)
$selection.Style = $normal
$selection.Font.NameFarEast = '宋体'
$selection.Font.Name = 'Arial'
$selection.Font.Size = $Size
$selection.Font.Bold = [int]$Bold
$selection.Font.Color = $Color
$selection.ParagraphFormat.Alignment = $Align
$selection.ParagraphFormat.SpaceBefore = $Before
$selection.ParagraphFormat.SpaceAfter = $After
$selection.ParagraphFormat.FirstLineIndent = $word.CentimetersToPoints($FirstIndentCm)
$selection.TypeText($Text)
$selection.TypeParagraph()
}
function Add-Heading {
param([string]$Text, [int]$Level)
$styleId = switch ($Level) { 1 { -2 } 2 { -3 } default { -4 } }
$selection.Style = $doc.Styles.Item($styleId)
$selection.Font.NameFarEast = '微软雅黑'
$selection.Font.Name = 'Arial'
$selection.Font.Color = 0x8F4A31
$selection.Font.Bold = 1
$selection.ParagraphFormat.KeepWithNext = -1
$selection.TypeText($Text)
$selection.TypeParagraph()
$selection.Style = $normal
}
function Add-StepList {
param([string[]]$Items)
for ($i = 0; $i -lt $Items.Count; $i++) {
Add-Text (('{0}. {1}' -f ($i + 1), $Items[$i])) 10.5 $false 0 0 0 4 0
}
}
function Add-ItemList {
param([string[]]$Items)
for ($i = 0; $i -lt $Items.Count; $i++) {
Add-Text ('' + ($i + 1) + '' + $Items[$i]) 10.5 $false 0 0 0 4 0
}
}
function Add-Table {
param([object[]]$Rows, [double[]]$WidthsCm = @())
$table = $doc.Tables.Add($selection.Range, $Rows.Count, $Rows[0].Count)
$table.Borders.Enable = 1
$table.Range.Font.NameFarEast = '宋体'
$table.Range.Font.Name = 'Arial'
$table.Range.Font.Size = 8.8
$table.Range.ParagraphFormat.SpaceAfter = 0
for ($r = 0; $r -lt $Rows.Count; $r++) {
for ($c = 0; $c -lt $Rows[$r].Count; $c++) {
$table.Cell($r + 1, $c + 1).Range.Text = [string]$Rows[$r][$c]
$table.Cell($r + 1, $c + 1).VerticalAlignment = 1
if ($WidthsCm.Count -gt $c) {
$table.Cell($r + 1, $c + 1).Width = $word.CentimetersToPoints($WidthsCm[$c])
}
}
}
$table.Rows.Item(1).Range.Bold = 1
$table.Rows.Item(1).Shading.BackgroundPatternColor = 0xE6D8D0
$table.Rows.Item(1).Range.ParagraphFormat.Alignment = 1
$table.AllowAutoFit = -1
Move-End
$selection.TypeParagraph()
}
function Add-Note {
param([string]$Title, [string]$Text, [string]$Kind = 'info')
$table = $doc.Tables.Add($selection.Range, 1, 1)
$cell = $table.Cell(1, 1)
$table.Borders.Enable = 1
$cell.Range.Text = $Title + '' + $Text
$cell.Range.Font.NameFarEast = '微软雅黑'
$cell.Range.Font.Size = 9.5
$cell.Range.ParagraphFormat.SpaceAfter = 0
if ($Kind -eq 'danger') {
$cell.Shading.BackgroundPatternColor = 0xD9E2F3
} elseif ($Kind -eq 'warn') {
$cell.Shading.BackgroundPatternColor = 0xD9F2FF
} else {
$cell.Shading.BackgroundPatternColor = 0xF4F1EE
}
Move-End
$selection.TypeParagraph()
}
function Add-Figure {
param([string]$Path, [string]$Caption, [double]$MaxWidthCm = 16.4)
$selection.ParagraphFormat.Alignment = 1
$shape = $selection.InlineShapes.AddPicture($Path, $false, $true, $selection.Range)
$shape.LockAspectRatio = -1
$maxWidth = $word.CentimetersToPoints($MaxWidthCm)
if ($shape.Width -gt $maxWidth) { $shape.Width = $maxWidth }
$selection.SetRange($shape.Range.End, $shape.Range.End)
$selection.TypeParagraph()
Add-Text $Caption 9 $false 1 0x777777 0 8 0
}
function Add-Issue {
param(
[string]$Id,
[string]$Title,
[string]$Priority,
[string]$Status,
[string]$Description,
[string]$Evidence,
[string]$Impact,
[string]$Recommendation,
[string]$Verification
)
Add-Heading ($Id + ' ' + $Title) 2
Add-Table @(
@('优先级', '确认状态', '涉及模块'),
@($Priority, $Status, $Id.Split('-')[0])
) @(4, 5, 8)
Add-Text ('问题描述:' + $Description) 10.5 $false 0 0 0 5 0
Add-Text ('证据:' + $Evidence) 10 $false 0 0x555555 0 5 0
Add-Text ('影响:' + $Impact) 10.5 $false 0 0 0 5 0
Add-Text ('建议:' + $Recommendation) 10.5 $false 0 0 0 5 0
Add-Text ('验证建议:' + $Verification) 10 $false 0 0x555555 0 9 0
}
function Add-PageBreak { $selection.InsertBreak(7) }
Add-Text '' 12 $false 1 0 0 18 0
Add-Text '大连元利流体技术有限公司' 22 $true 1 0x8F4A31 0 12 0
Add-Text 'MES 系统管理问题清单' 30 $true 1 0x8F4A31 0 16 0
Add-Text '操作手册编制发现项(独立文档)' 19 $true 1 0x4D4D4D 0 28 0
Add-Text '版本V1.0' 12 $false 1 0 0 8 0
Add-Text '编制日期2026年7月23日' 12 $false 1 0 0 8 0
Add-Text '问题来源:前端代码、正式菜单、数据库对象定义及只读页面检查' 11 $false 1 0 0 24 0
Add-Note '安全声明' '本清单通过只读查询、源代码检查和页面查看形成。没有在正式环境执行新增、修改、删除、密码重置、权限分配或其他写操作。涉及写操作的问题未在正式环境提交复现。' 'warn'
Add-PageBreak
Add-Text '目 录' 20 $true 1 0x8F4A31 0 12 0
$toc = $doc.TablesOfContents.Add($selection.Range, $true, 1, 3, $true, '', $true, $true, '', $true, $true, $true)
Move-End
Add-PageBreak
Add-Heading '1 文档说明' 1
Add-Text '本文件只记录系统管理篇编制过程中发现的问题和风险,不与用户操作手册正文混排。问题按业务影响和修复紧迫程度分级,最终是否立项、修复方案和上线时间应由项目负责人、业务负责人及开发人员共同确认。' 10.5 $false 0 0 0 6 0.74
Add-Table @(
@('级别', '定义', '处理建议'),
@('P1 高', '可能造成账号安全、跨系统数据不一致、主数据不可恢复或权限风险', '优先评审,修复前设置管理措施'),
@('P2 中', '影响常用查询、维护准确性、审计完整性或容易导致误操作', '纳入近期版本,补充测试用例'),
@('P3 低', '文档、可维护性或未启用代码问题,当前业务影响较小', '随版本整理或技术债处理')
) @(2.5, 9, 5.5)
Add-Note '确认状态说明' '“已确认”表示代码、页面或数据库定义可直接证明;“待测试环境复现”表示风险已从实现中识别,但为避免正式环境产生数据,尚未提交写操作验证。' 'info'
Add-Heading '2 问题汇总' 1
Add-Table @(
@('编号', '优先级', '问题摘要', '确认状态'),
@('SEC-001', 'P1', '登录和人员接口存在明文密码暴露风险,初始密码固定', '已确认'),
@('MENU-001', 'P1', '菜单管理前端发送原始SQL文本接口授权边界风险', '已确认'),
@('WS-001', 'P1', '工位MES与SAP B1写入非原子失败无补偿', '待测试环境复现'),
@('WS-002', 'P1', '工位删除无占用校验且未同步删除B1资源', '已确认'),
@('WS-003', 'P2', '工位名称查询框不生效', '已确认'),
@('WS-004', 'P2', '工位名称/工位号校验不完整', '已确认'),
@('PER-001', 'P2', '人员“永久删除”提示与实际处理语义不一致', '已确认'),
@('LOG-001', 'P2', '报工记录精确匹配、最多100条且无分页', '已确认'),
@('MENU-002', 'P2', '菜单结构维护和按钮级权限界面未开放', '已确认'),
@('DOC-001', 'P3', '参考手册与当前正式菜单范围不一致', '已确认'),
@('WS-005', 'P3', '工位分页回调引用不存在的方法', '已确认')
) @(2.4, 2.2, 9, 3.4)
Add-PageBreak
Add-Heading '3 P1 高优先级问题' 1
Add-Issue 'SEC-001' '密码处理与默认密码存在安全风险' 'P1 高' '已确认' `
'人员查询响应包含密码字段前端虽然显示为星号仍把返回值保存到页面对象。登录页“记住账号”会把用户输入的密码写入浏览器Cookie并保留7天。新增和重置密码统一使用固定初始密码123456。' `
'src/views/SystemMaintenance/PersonnelManagement/index.vue:279、383、460src/views/login/index.vue:206、243-245数据库重置过程直接写入固定密码。' `
'拥有浏览器本机访问权限、调试权限或接口访问权限的人员可能取得密码;统一初始密码会扩大弱口令和批量撞库风险。' `
'后端仅保存加盐哈希人员查询接口不得返回密码移除密码Cookie记住登录应使用短期安全令牌新账号生成随机一次性密码并强制首次登录修改重置后立即失效其他会话。' `
'在隔离测试环境完成登录、记住账号、创建账号、重置密码和首次改密的安全测试并检查浏览器Cookie、网络响应和数据库字段。'
Add-Figure (Join-Path $prepared 'personnel-list.png') '图3-1 人员页面虽然遮蔽密码,但接口对象仍接收密码字段'
Add-Issue 'MENU-001' '菜单管理由浏览器发送原始SQL文本' 'P1 高' '已确认' `
'菜单管理的角色菜单查询、一级菜单初始化和二级菜单初始化由前端把SELECT语句放在请求name字段中发送给通用接口其中角色编号直接插入SQL字符串。' `
'src/api/BasicData/MenuManagement.js:20-59getModule、initFisrtLevel、initSecondLevel均使用type=3和原始SQL文本。' `
'即使正常页面只允许选择数字角色攻击者仍可绕过页面直接构造请求。若后端缺少严格白名单和参数化限制可能形成越权查询或SQL注入入口。' `
'改为固定后端接口或存储过程角色编号使用强类型参数服务器按当前用户权限校验目标角色关闭通用任意SQL执行能力增加安全审计和异常请求告警。' `
'由安全人员在测试环境检查通用接口是否允许修改SQL、跨角色查询和非SELECT语句禁止在正式环境进行攻击性验证。'
Add-Issue 'WS-001' '工位MES与SAP B1写入缺少一致性保障' 'P1 高' '待测试环境复现' `
'新增和编辑工位时先等待MES接口成功然后调用postB1或patchB1但B1调用没有await、失败处理、重试或回滚。页面在MES成功后即可提示成功并关闭弹窗。' `
'src/views/SystemMaintenance/WorkstationManagement/index.vue:193-205、228-249。' `
'B1超时、证书、登录态或业务校验失败时MES已经成功而B1可能失败形成同一工位号在两个系统中的名称或存在状态不一致。用户重复提交还可能产生重复资源。' `
'将跨系统写入放到后端编排记录同步状态和错误详情B1成功后再向页面返回整体成功失败时执行补偿或进入可重试队列按工位号实现幂等。' `
'测试环境模拟B1成功、超时、业务拒绝和重复编码四种情况核对MES、B1和页面提示是否一致。'
Add-Issue 'WS-002' '工位删除无占用校验且未同步B1' 'P1 高' '已确认' `
'工位删除存储过程中的菜单/人员占用校验代码已注释当前直接删除MES_资源主数据前端删除成功后没有调用SAP B1资源删除或停用接口。' `
'src/views/SystemMaintenance/WorkstationManagement/index.vue:257-280数据库过程“系统管理_工位管理_工位信息删除”直接DELETE依赖检查为注释。' `
'可能删除仍被排产、在制任务、人员绑定、设备或工时使用的工位同时B1保留资源造成主数据不一致和历史联查异常。' `
'恢复并扩展占用校验有历史记录时优先停用而不是物理删除同步B1停用状态增加删除审批、影响预览和可恢复机制。' `
'只在测试环境建立完整的排产、人员绑定和工时引用场景,验证阻止删除、停用和跨系统同步。'
Add-PageBreak
Add-Heading '4 P2 中优先级问题' 1
Add-Issue 'WS-003' '工位名称查询框不生效' 'P2 中' '已确认' `
'页面绑定了工位名称输入框但searchData构造的参数数组为空没有把RoleFunctioning传入查询接口。输入任意名称点击查询仍刷新全部工位。' `
'src/views/SystemMaintenance/WorkstationManagement/index.vue:4-10、122、163-169只读页面检查结果与代码一致。' `
'工位数量增多后无法快速定位,用户可能误以为没有匹配结果或误操作同名/相近工位。' `
'前端把工位名称作为查询参数传入参数化存储过程;后端按业务要求采用包含匹配;增加空条件、中文关键字、无结果和特殊字符测试。' `
'测试环境准备多个相近名称工位,验证空条件、部分名称和完整名称查询。'
Add-Figure (Join-Path $prepared 'workstation-list.png') '图4-1 工位页面显示查询框,但当前请求未使用输入值' 15
Add-Issue 'WS-004' '工位校验规则不完整' 'P2 中' '已确认' `
'新增只显式检查同名工位,没有在存储过程中预检查工位号重复。编辑的同名检查统计全部记录,包含当前工位本身,因此不修改名称直接保存时也会被判断为同名。' `
'数据库过程“系统管理_工位管理_工位信息增加”和“系统管理_工位管理_工位信息修改”编辑过程的同名条件未排除当前工位号。' `
'重复工位号可能依赖数据库异常兜底并产生不友好提示;无变化保存可能失败,增加用户困惑和重复操作。' `
'新增时同时校验工位号和名称唯一编辑时使用“Name=@名称 AND VisCode<>@当前工位号”;后端返回明确错误码和字段级提示。' `
'测试空值、重复名称、重复编号、原名称不变、名称仅大小写/空格变化等边界。'
Add-Issue 'PER-001' '人员删除提示与实际处理语义不一致' 'P2 中' '已确认' `
'页面确认文字为“当前人员将永久删除”成功提示为“人员离职成功”数据库过程没有物理删除人员主记录而是写Quit=0并删除人员角色关系。列表通过角色关系联查因此人员从列表消失。' `
'src/views/SystemMaintenance/PersonnelManagement/index.vue:345-366数据库过程“人员管理_人员信息修改_删除”。' `
'管理员难以判断人员是离职、停用还是永久删除;人员主记录仍在但无角色关系,后续重新启用和历史查询缺少明确流程。' `
'统一业务语义为“停用/离职”明确Quit字段状态定义保留角色历史或增加有效期页面展示在职/离职状态和恢复入口;禁止使用“永久删除”误导。' `
'测试环境验证离职后登录、列表、历史工时、报工记录和重新启用行为。'
Add-Issue 'LOG-001' '报工操作记录查询能力不足' 'P2 中' '已确认' `
'订单编号和操作人采用精确相等查询最多返回按记录编号倒序的前100条页面分页控件整体注释日期和操作类别等已存在的数据字段没有查询入口。' `
'src/views/SystemMaintenance/OperationLog/index.vue:63-75、87-93、108-125数据库过程“系统管理_报工操作记录_查询”使用TOP 100和等值条件。' `
'较早或同一订单超过100条的记录无法完整查看姓名/订单输入不完整时无结果;审计人员无法按时间范围和操作类别定位。' `
'增加服务端分页、总数、开始/结束日期和操作类别;订单支持精确优先并可选包含匹配;提供导出并限制最大范围;建立合适索引。' `
'使用脱敏测试数据验证跨页、超过100条、时间边界、空条件和导出结果完整性。'
Add-Figure (Join-Path $prepared 'operation-log.png') '图4-2 报工操作记录当前仅提供订单编号和操作人条件'
Add-Issue 'MENU-002' '菜单结构维护和按钮级权限界面未开放' 'P2 中' '已确认' `
'页面存在“新增一级模块”弹窗和相关方法,但模板没有可见入口;查询、新增、编辑、删除按钮级权限复选框被注释。当前实际功能只是把既有一级、二级菜单分配给角色。' `
'src/views/SystemMaintenance/MenuManagement/index.vue:82-89、152-170、367-393。' `
'旧手册描述新增、编辑、删除菜单和权限分配,容易让管理员寻找不存在的功能;页面级授权过粗,不能限制同页写操作。' `
'明确产品边界:如不开放结构维护,删除不可达代码并修订文档;如需按钮权限,完成前后端授权校验,不能只依赖前端隐藏按钮。' `
'测试不同角色的查询、新增、编辑、删除接口权限,确认直接请求也会被服务器拒绝。'
Add-Figure (Join-Path $prepared 'menu-permission.png') '图4-3 当前菜单页实际为角色菜单树分配'
Add-PageBreak
Add-Heading '5 P3 低优先级问题' 1
Add-Issue 'DOC-001' '参考手册与当前正式菜单不一致' 'P3 低' '已确认' `
'参考手册系统管理包含“节假日设定”但正式数据库菜单没有该页面正式菜单新增“报工操作记录”参考手册没有对应章节。项目中还存在OrgManagement页面但未配置为正式菜单。' `
'参考文档系统管理章节正式表“登录基础数据_二级菜单”src/views/SystemMaintenance目录。' `
'继续沿用旧手册会遗漏正式功能并培训不存在的入口,造成操作人员和实施人员理解不一致。' `
'以正式菜单版本为基线建立文档版本号和发布日期;每次菜单上线同步更新手册;未上线页面标注“开发中/停用”而不是混入正式手册。' `
'发布前由业务、实施和开发三方按角色逐项核对左侧菜单与手册目录。'
Add-Issue 'WS-005' '工位分页回调引用不存在的方法' 'P3 低' '已确认' `
'工位页面当前没有显示分页控件但handleSizeChange和handleCurrentChange均调用this.searchTable()组件实际查询方法名为searchData()。' `
'src/views/SystemMaintenance/WorkstationManagement/index.vue:163-175、288-297。' `
'当前未触发;如果后续恢复分页控件,切换页码将产生运行时错误,查询无法执行。' `
'把回调统一改为searchData补充组件测试删除未使用的分页状态或完整实现服务端分页。' `
'在测试环境启用分页后验证切换每页条数、页码和末页行为。'
Add-Heading '6 建议处理顺序' 1
Add-Table @(
@('阶段', '建议事项', '目标'),
@('立即管理措施', '禁用共享账号不勾选记住密码限制系统管理权限禁止直接删除工位工位变更人工核对MES/B1', '在代码修复前降低安全和数据风险'),
@('第一批修复', 'SEC-001、MENU-001、WS-001、WS-002', '处理账号安全、接口授权和跨系统一致性'),
@('第二批修复', 'WS-003、WS-004、PER-001、LOG-001、MENU-002', '改善维护准确性、审计完整性和权限粒度'),
@('文档与技术债', 'DOC-001、WS-005', '统一正式菜单与文档,清理潜在故障代码')
) @(3, 10, 4)
Add-Heading '7 验收要求' 1
Add-ItemList @(
'所有写操作问题必须在测试环境复现和回归,不得以正式业务数据作为测试数据。',
'涉及账号安全的修改需完成接口响应、Cookie、日志、数据库存储和会话失效检查。',
'涉及MES与SAP B1的修改需覆盖单边成功、单边失败、超时、重复请求和补偿重试。',
'删除/停用类功能需验证下游占用、历史查询、恢复和审计记录。',
'菜单权限必须同时验证页面可见性和后端接口授权,不能只检查按钮是否隐藏。',
'修复上线后同步更新操作手册、问题状态、版本号和发布日期。'
)
Add-Note '结论' '本清单共记录11项P1高优先级4项、P2中优先级5项、P3低优先级2项。问题清单与操作手册分开交付便于技术整改和业务培训分别管理。' 'danger'
foreach ($section in $doc.Sections) {
$header = $section.Headers.Item(1).Range
$header.Text = '大连元利流体技术有限公司 MES系统 | 系统管理问题清单'
$header.Font.NameFarEast = '微软雅黑'
$header.Font.Size = 8.5
$header.Font.Color = 0x777777
$header.ParagraphFormat.Alignment = 2
$footer = $section.Footers.Item(1).Range
$footer.Text = '内部整改使用 '
$footer.Font.NameFarEast = '宋体'
$footer.Font.Size = 8.5
$footer.Font.Color = 0x777777
$footer.ParagraphFormat.Alignment = 1
$footer.Collapse(0)
$footer.Fields.Add($footer, 33) | Out-Null
}
foreach ($item in $doc.TablesOfContents) { $item.Update() }
$doc.Fields.Update() | Out-Null
$doc.Repaginate()
if (Test-Path $docxPath) { Remove-Item -LiteralPath $docxPath -Force }
if (Test-Path $pdfPath) { Remove-Item -LiteralPath $pdfPath -Force }
$doc.SaveAs($docxPath, 12)
$doc.ExportAsFixedFormat($pdfPath, 17)
$pageCount = $doc.ComputeStatistics(2)
$tableCount = $doc.Tables.Count
$imageCount = $doc.InlineShapes.Count
$doc.Close(0)
$doc = $null
Copy-Item -LiteralPath $docxPath -Destination $desktopDocx -Force
Copy-Item -LiteralPath $pdfPath -Destination $desktopPdf -Force
Write-Output ('DOCX=' + $docxPath)
Write-Output ('PDF=' + $pdfPath)
Write-Output ('PAGES=' + $pageCount)
Write-Output ('TABLES=' + $tableCount)
Write-Output ('IMAGES=' + $imageCount)
} catch {
$buildError = $_
Write-Output ('BUILD_ERROR=' + $_.Exception.Message)
Write-Output ('BUILD_ERROR_LINE=' + $_.InvocationInfo.ScriptLineNumber)
} finally {
if ($doc -ne $null) { try { $doc.Close(0) } catch {} }
if ($word -ne $null) { try { $word.Quit() } catch {} }
[GC]::Collect()
[GC]::WaitForPendingFinalizers()
}
if ($buildError -ne $null) { throw $buildError }