# IT 工单跟踪系统 基于 Next.js + SQLite 的 IT 基础设施工单跟踪管理系统,域名为 `issue.tlyq.ai`,用于记录和管理故障工单,支持与 [assets.tlyq.ai](https://assets.tlyq.ai) 资产管理系统联动。 ## 技术栈 | 分类 | 技术 | |------|------| | 框架 | Next.js 15 + TypeScript | | UI | React 19 + Tailwind CSS v4 + lucide-react | | 数据库 | SQLite(better-sqlite3,WAL 模式) | | 认证 | JWT(cookie 方式)+ 自定义 session | | 文档导出 | docx + ECharts + Puppeteer(图表渲染) | ## 功能 - 工单 CRUD(创建、编辑、删除、状态流转) - Excel 批量导入工单 - 仪表盘统计(整体可用性、故障分类、SLA 达标率) - 月报/周报生成(DOCX 格式,含封面、目录/图表(月报)、数据表格) - 与 assets-ai 资产管理系统双向联动 - 用户/角色权限管理 - API Key 管理(支持服务间调用认证) …… ## 月报设计规则 ### 概述 月报按自然月生成,统计当月已结单工单及跨月进行中工单,输出为 DOCX 文档。包含封面、目录及四个章节。 ### 报告结构 | 章节 | 内容 | |------|------| | 封面 | 标题、时间范围、公司名称、生成月份 | | 目录 | Word 目录域(打开后需更新域以刷新) | | 第一章 — 总体运营概况 | GPU 服务器 / 存储服务器每日在线节点数折线图 | | 第二章 — 运营数据总览 | 按日期 + 设备类型分组,自然语言描述故障及恢复情况 | | 第三章 — 运营故障概览 | 故障工单表格(GPU 故障 / 存储故障 / 其他工单三类) | | 第四章 — 服务可用性说明 | 每台设备服务可用性计算公式及百分比 | ### 数据采集规则 **设备清单**:从 assets-ai 拉取 `filter_status=腾讯使用` 的设备,按 `filter_device_type=GPU服务器` / `存储服务器` 分类,构建 business_ip → device_type 映射。 **工单筛选**:按 `close_time` 与 `assign_time` 双条件查询,同时覆盖当月结单工单和跨月进行中工单: ```sql SELECT * FROM tickets WHERE ( (close_time >= periodStart AND close_time <= periodEnd + ' 23:59:59') OR (assign_time <= periodEnd + ' 23:59:59' AND (close_time IS NULL OR close_time > periodEnd + ' 23:59:59')) ) ORDER BY assign_time ``` - 第一条件:当月内结单的工单(含上月派发本月恢复的跨月工单) - 第二条件:当月结束时仍未结单的工单(含当月派发和上月派发的进行中工单) - 使用 `close_time` 比较而非 `current_status`,保证事后补生成报告时判断不变 **工单分类**:按 device_ip 在设备清单中查找对应 device_type(gpu / storage / other)。 ### 第一章规则 - 遍历当月每一天,计算当日在线节点数 - **统计范围**:排除 `fault_category = '无故障'` 或 `fault_subcategory = '其他'` 的工单("其他"工单不计入节点在线数;跨月工单如 7/31 故障 8/3 恢复正常计入,8/1、8/2 各减 1 台) - 不在线判断:`assign_time日期 ≤ 当前日期 < close_time日期` - 当日发生故障次日恢复 → 发生日计入不在线,恢复日不计入 - 当日发生故障当日恢复 → 不计入不在线 - 在线 = 总节点数 - 不在线节点数 - 分别统计 GPU 和存储,生成两张 ECharts 折线图 - **Y 轴动态范围**:根据实际数据波动自动调整 min/max/interval,避免总节点数较大时微小变化无法分辨 - 无波动时 Y 轴范围 = total ± 2 - 有波动时根据实际最值加 buffer,确保 8~15 个刻度 - **报告预览"无故障天数"与 DOCX 图表计算方式不同**: - **预览**:按故障完整日期范围(`assign ~ close`,含 close 当天)计算,当日恢复也计入故障天数 - **DOCX 图表**:按 `assign ≤ date < close` 计算,当日恢复不计入离线节点 ### 第二章规则 - 仅统计 GPU / 存储工单,排除 `fault_category = '无故障'` 或 `fault_subcategory = '其他'` - 按 `device_type + assign_time日期` 分组 - 每条格式:`X月X日发生1次<故障子类>,故障节点为,<恢复描述>。` - 恢复描述: - 已结单:`assign_date` 与 `close_date` 天数差,0 → 当日,1 → 次日,≥2 → N日后 - 进行中:固定显示 `处理中。`(不含恢复描述) ### 第三章规则 **分流逻辑**: | 条件 | 归类 | |------|------| | `fault_subcategory = '其他'` | 其他工单 | | `fault_subcategory ≠ '其他'` 且 device_type 为 gpu | GPU 故障 | | `fault_subcategory ≠ '其他'` 且 device_type 为 storage | 存储故障 | | `fault_subcategory ≠ '其他'` 且 device_type 为 other | 其他工单 | **GPU/存储故障表**(7 列): | 列 | 来源 | 进行中工单 | |----|------|-----------| | 工单编号 | `ticket_id` | 正常显示 | | 故障节点 | `device_ip` | 正常显示 | | 故障日期 | `assign_time`(完整时间,精确到秒) | 正常显示 | | 故障问题 | `fault_subcategory` | 正常显示 | | 故障原因 | `parts_name` 有值 → `更换{parts_name}`,否则 → `-` | 正常显示 | | 处理时长(分钟) | `duration_minutes`(已结单) | 显示 `进行中` | | 是否计入 SLA | 见下方 SLA 规则 | 显示 `—` | **其他工单表**(7 列): | 列 | 来源 | 进行中工单 | |----|------|-----------| | 工单编号 | `ticket_id` | 正常显示 | | 设备 IP 地址 | `device_ip` | 正常显示 | | 工单日期 | `assign_time`(完整时间,精确到秒) | 正常显示 | | 工单内容 | `content` | 正常显示 | | 工单结论 | `conclusion` | 正常显示 | | 处理时长(分钟) | `duration_minutes`(已结单) | 显示 `进行中` | | 是否计入 SLA | 见下方 SLA 规则 | 显示 `—` | ### 第四章规则 - 排除 `fault_category = '无故障'` 的工单 - 按 device_ip 分组求和故障时长: - **已结单工单**:使用实际 `duration_minutes` - **进行中工单**:仅计算本月内部分,时长 = `(min(assign_date, 月初) → 月末最后一天) × 24 × 60` 分钟 - 公式:`可用性 = (monthDays × 24 × 60 - totalDurationMinutes) / (monthDays × 24 × 60) × 100` - monthDays 为当月实际天数(动态计算) - 百分比 **< 99%** 时,该值以黄底红字加粗标记 - 百分比 **≥ 99%** 时,正常样式 - 该 IP 存在进行中工单时,公式后追加橙色标注 `(故障处理中,仅计本月部分)` ### SLA 判定规则 `是否计入SLA` 字段判定逻辑: ``` IF availability IS NULL OR availability >= 0.99 → 否 IF availability < 0.99 AND conclusion 包含 "无异常" → 否 IF availability < 0.99 AND conclusion 不包含 "无异常" → 是 ``` ### 排版规范 | 元素 | 字体 | 字号 | 行距 | 其他 | |------|------|------|------|------| | 封面标题 | SimHei(黑体) | 22pt / 26pt | 1.5x | 居中 | | 章标题(Heading 1) | SimSun | 14pt | 1.5x | 加粗 | | 节标题(Heading 2) | SimSun | 12pt | 1.5x | 加粗 | | 正文 | SimSun | 11pt | 1.5x | 首行缩进 2 字符 | | 表格表头 | SimSun | 10pt | 1.5x | 蓝底白字加粗 | | 表格内容 | SimSun | 9pt | 1.15x | 垂直居中 | | 目录 TOC1/TOC2 | SimSun | 11pt | 1.5x | — | ## 周报设计规则 ### 概述 周报按自然周(周一至周日)生成,统计当周活跃工单(含处理中和已结单),输出为 DOCX 文档。包含封面及四个章节,不含目录和图表。 ### 报告结构 | 章节 | 内容 | |------|------| | 封面 | 标题、报告周期、生成时间、公司名称 | | 一、总体运营概况 | GPU/存储服务器总数 + 本周故障摘要 + 每日在线节点数表格 | | 二、GPU 服务器故障 | 故障概况表 + 已恢复工单的故障详情表 | | 三、存储服务器故障 | 故障概况表 + 已恢复工单的故障详情表 | | 四、其他工单 | 工单概况表 + 已恢复工单的工单详情表 | ### 数据采集规则 **设备清单**:与月报相同,从 assets-ai 拉取 GPU/存储设备,构建 IP → device_type 映射。 **工单筛选**:按周期内活跃度查询,含处理中工单(区别于月报仅统计已结单): ```sql -- 周期内结单的工单,或周期内仍在处理中的工单 (close_time >= periodStart AND close_time <= periodEnd + ' 23:59:59') OR (assign_time <= periodEnd + ' 23:59:59' AND (current_status NOT IN ('resolved','closed') OR close_time > periodEnd + ' 23:59:59')) ``` **工单分类(三路分流)**: | 条件 | 归类 | |------|------| | `ticket_type = 'OEM维修'` 且 `fault_category ≠ '无故障'` 且 device_type = gpu | GPU 服务器故障 | | `ticket_type = 'OEM维修'` 且 `fault_category ≠ '无故障'` 且 device_type = storage | 存储服务器故障 | | `ticket_type = 'OEM诊断'` 或(`ticket_type = 'OEM维修'` 且 `fault_category = '无故障'`) | 其他工单 | ### 第一章规则(总体运营概况) **1.1 本周概览**:展示 GPU/存储服务器总数,本周故障次数及恢复/处理中数量。 **1.2 / 1.3 每日运行状态表**:遍历周期内每一天,计算当日在线节点数。 - 不在线判断:`assign_time日期 ≤ 当前日期 < close_time日期`(与月报逻辑一致) - 当日有故障节点时:日期/在线数/故障数三列纵向合并,最后一列逐行列出故障 IP - 当日无故障时:单行显示,故障 IP 列填 `/` - 在线 = 总节点数 - 不在线节点数 ### 第二/三/四章规则(故障详情) **故障概况表**(5 列): | 列 | 来源 | 列宽 | |----|------|------| | 工单号 | `ticketNo` | 15%(2.41cm) | | 设备 IP | `deviceIp` | 15%(2.41cm) | | 工单内容 | `content` 或 `faultSubcategory` | 40%(6.29cm) | | 工单时间 | `assignTime` | 15%(2.41cm) | | 目前状态 | 已恢复 / 处理中 | 15%(2.41cm) | **故障详情表**(4 列,仅已恢复工单展示): - 基本信息区:工单号、设备 IP、工单内容(3 列合并)、派单/结单时间 - 工单流程区:第一列纵向合并为「工单流程」标签,后三列为时间节点、发现人/处理人、处理过程/处理结果 - 若 steps 中无「下发工单」步骤,自动补一行 - 若 steps 中无「结单」步骤,自动补一行 - 工单结论区:标签 + 结论文字(3 列合并,居中) ### 表格列宽实现 故障概况表采用 **DXA 绝对值 + 固定布局** 方式确保列宽精确: ```typescript // 15:15:40:15:15 比例换算 DXA(A4 内容宽度 9026 DXA) const colDxa = [1354, 1354, 3610, 1354, 1354] new Table({ width: { size: 9026, type: WidthType.DXA }, columnWidths: colDxa, layout: TableLayoutType.FIXED, // 关键:禁止 Word 自动调整列宽 rows: [...], }) ``` 每个单元格必须同时设置 `width: { size: colDxa[i], type: WidthType.DXA }`,确保 gridCol、表头 tcW、数据 tcW 三者值完全一致。 ### 与月报的关键差异 | 维度 | 月报 | 周报 | |------|------|------| | 统计范围 | 自然月,仅已结单 | 自然周(周一~周日),含处理中 | | 目录 | 有(TOC 域) | 无 | | 图表 | ECharts 折线图(第一章) | 无(纯表格) | | 服务可用性 | 第四章 SLA 计算 | 无 | | 故障详情展开 | 无(仅概况表) | 有(完整工单流程时间线) | | 表格布局 | 百分比自适应 | DXA 固定列宽 + Fixed Layout |