CLAUDE.md — issue.tlyq.ai 工单跟踪系统
项目概述
issue-ai 是基于 Next.js + SQLite 的工单跟踪管理系统,部署在腾讯云(txjp 服务器),域名 issue.tlyq.ai。与 assets.tlyq.ai 资产管理系统联动,获取设备信息、提供故障历史查询。
快速参考
| 属性 |
值 |
| 站点域名 |
issue.tlyq.ai |
| 服务器 |
txjp(IP: 43.133.38.210) |
| 代码路径 |
/root/docker/issue-ai/ |
| 本地端口 |
5176 |
| 容器名 |
issue-ai |
| 数据库 |
SQLite:data/issue.db |
| 报告存储 |
reports/ 目录(环境变量 REPORTS_DIR 可覆盖) |
| 默认账号 |
admin / admin123 |
常用命令
cd /Users/niuniu/programs/docker/issue-ai
npm run dev # 本地开发
npm run build # 生产构建
npm run db:init # 初始化数据库
npm run import # 导入工单
关键文件
| 文件 |
职责 |
next.config.ts |
output: 'standalone',outputFileTracingIncludes 防止 fs.readFileSync 依赖丢失 |
Dockerfile |
两阶段构建(alpine builder + debian slim runner),含 Chromium 系统依赖 |
docker-compose.yml |
使用 external webnet,挂载 .next 从宿主机 |
src/lib/db.ts |
SQLite 连接(单例,WAL 模式,外键开启) |
src/lib/db-schema.ts |
表初始化 + 默认数据(admin/operator/viewer) |
src/lib/auth.ts |
JWT 解析、session 验证 |
src/lib/permissions.ts |
权限检查(admin 全能,按角色 JSON 匹配) |
src/lib/sla.ts |
SLA 超时计算(Tier 1 = 1% 自然月秒数) |
src/lib/assets-client.ts |
调用 assets API 获取设备信息 |
src/lib/report-generator.ts |
报告数据组装 |
src/lib/docx-export.ts |
Word 文档生成 |
src/lib/excel.ts |
Excel 解析与导出 |
src/lib/pdf.ts |
PDF 生成(puppeteer) |
src/types/ticket.ts |
TicketCreateInput / TicketUpdateInput |
src/types/report.ts |
ReportType / ReportData |
数据库 Schema
表概览
| 表名 |
说明 |
users |
用户账号(username/password_hash/role) |
roles |
角色定义(name/display_name/permissions JSON) |
sessions |
会话(JWT id → user_id) |
tickets |
工单主体 |
ticket_steps |
工单时间线(关联 ticket_id) |
reports |
报告记录 |
audit_logs |
审计日志 |
tickets 表核心字段
| 字段 |
类型 |
说明 |
ticket_no |
TEXT UNIQUE |
工单编号 |
device_ip |
TEXT |
设备 IP(用于调 assets API 补全信息) |
ticket_type |
TEXT |
工单类型(OEM诊断/OEM维修) |
fault_category |
TEXT |
故障大类(硬件故障/网络故障/误判/其他/空) |
fault_subcategory |
TEXT |
故障子分类 |
current_status |
TEXT |
状态(open/in_progress/resolved/closed) |
availability |
REAL |
服务可用性(0-1) |
counted_in_sla |
INTEGER |
是否计入 SLA |
assign_time |
TEXT |
指派时间 |
close_time |
TEXT |
关闭时间 |
duration_minutes |
INTEGER |
处理时长(分钟) |
conclusion |
TEXT |
结论 |
parts_replaced |
TEXT |
更换配件 |
预置角色
| 角色 |
权限 |
admin |
["*"](全部权限) |
operator |
["tickets:read","tickets:write","reports:read"] |
viewer |
["tickets:read","reports:read"] |
API 路由
认证
| 方法 |
路径 |
说明 |
| POST |
/api/auth/login |
登录(username + password → JWT cookie) |
| POST |
/api/auth/logout |
登出 |
| GET |
/api/auth/me |
当前用户信息 |
工单
| 方法 |
路径 |
说明 |
| GET |
/api/tickets |
工单列表(分页/筛选/搜索) |
| POST |
/api/tickets |
创建工单(Cookie 认证) |
| POST |
/api/tickets/external |
外部系统创建工单(API Key 认证,幂等) |
| GET |
/api/tickets/[id] |
工单详情(自动调 assets API 补全设备名) |
| PUT |
/api/tickets/[id] |
更新工单(含时间线 steps) |
| DELETE |
/api/tickets/[id] |
删除工单 |
| POST |
/api/tickets/import |
Excel 批量导入 |
统计 / 报告 / 用户
| 方法 |
路径 |
说明 |
| GET |
/api/stats |
工单概览统计 |
| GET |
/api/stats/monthly |
月度趋势 |
| GET |
/api/stats/sla |
SLA 达标率 |
| GET/POST |
/api/reports |
报告列表 / 生成 |
| GET |
/api/reports/[id] |
报告详情/下载 |
| GET/POST |
/api/users |
用户列表/创建 |
| GET/PUT/DELETE |
/api/users/[id] |
单个用户操作 |
| GET/POST |
/api/roles |
角色列表/创建 |
| GET/PUT/DELETE |
/api/roles/[id] |
单个角色操作 |
环境配置
本地与云端差异
| 环境变量 |
本地开发 |
云服务器(txjp) |
说明 |
ASSETS_API_URL |
http://localhost:5177/api |
http://assets-ai:3000/api |
调用 assets API 地址 |
ASSETS_API_KEY |
本地 assets-ai 生成 |
云上 assets-ai 生成 |
每个环境独立,不可跨环境使用 |
NEXT_PUBLIC_ASSETS_URL |
http://localhost:5177 |
https://assets.tlyq.ai |
前端跳转链接(构建时内嵌) |
ALLOWED_API_KEYS |
本地 issue-ai 生成的 Key |
云上 issue-ai 生成的 Key |
允许外部系统调本系统的 Key,逗号分隔 |
JWT_SECRET |
dev-secret-key-local |
${ISSUE_JWT_SECRET} |
本地两系统需一致(同 localhost 域) |
DATABASE_PATH |
./data/issue.db |
/app/data/issue.db |
Docker volume 挂载 |
| Cookie 名 |
session_issue |
session_issue |
本地用不同名防 localhost 域冲突 |
.env.local 示例
DATABASE_PATH=./data/issue.db
JWT_SECRET=dev-secret-key-local
ADMIN_PASSWORD=admin123
NODE_ENV=development
ASSETS_API_URL=http://localhost:5177/api
ASSETS_API_KEY=ak_<32字节十六进制>
ALLOWED_API_KEYS=ak_<32字节十六进制>
NEXT_PUBLIC_ASSETS_URL=http://localhost:5177
与 assets.tlyq.ai 的联动
issue-ai ──→ GET {ASSETS_API_URL}/assets?search=IP&pageSize=50 (Authorization: Bearer {ASSETS_API_KEY})
assets-ai ──→ GET {ISSUE_API_URL}/tickets/by-asset?ip=xxx (Authorization: Bearer {ISSUE_API_KEY})
- issue → assets(
src/lib/assets-client.ts):根据 device_ip 模糊搜索,匹配 business_ip 或 hdm_ip
- assets → issue(
src/app/api/tickets/by-asset/route.ts):查询同 IP 历史工单,支持 Cookie 和 API Key 双认证
API Key 创建与配置
Key 格式:ak_<32位十六进制>,认证头:Authorization: Bearer <key>。每个环境独立创建,互不通用。
issue → assets 方向:在 assets-ai /settings/api-keys 创建 Key → 写入 issue-ai 的 ASSETS_API_KEY
assets → issue 方向:在 issue-ai /settings/api-keys 创建 Key → 写入 assets-ai 的 ISSUE_API_KEY + issue-ai 的 ALLOWED_API_KEYS
Docker 部署
txjp 服务器
├── issue-ai(容器) ← Next.js standalone,监听 3000
├── nginx-ai ← 反向代理 issue.tlyq.ai → issue-ai:3000
└── webnet(external) ← 共享网络
部署:bash deploy-ai.sh → 选择 4。源码打包上传 → 服务器 npm install + npm run build → .next 挂载进容器生效。--force 强制重建,--restart 仅重启。
关键:新增 npm 依赖后必须重建 Docker 镜像(deploy-ai.sh 只在宿主机 npm install,容器内 /app/node_modules/ 来自镜像构建时):
ssh txjp "cd /root/docker/issue-ai && docker compose build --no-cache && docker compose down && docker compose up -d"
生产环境变量
DATABASE_PATH=/app/data/issue.db
JWT_SECRET=<线上密钥>
ASSETS_API_URL=http://assets-ai:3000/api
ASSETS_API_KEY=<assets-ai 的 Key>
NODE_ENV=production
NEXT_PUBLIC_ASSETS_URL=https://assets.tlyq.ai
仪表盘统计规则
- 整体服务可用性:
AVG(tickets.availability) × 100(保留 2 位小数)
- 故障分类:硬件故障 / 网络故障 / 误判 / 其他(content 含 agent+上报) / 空值
- 批量更新:
UPDATE tickets SET fault_category='其他' WHERE (content LIKE '%agent%' OR content LIKE '%上报%') AND (fault_category IS NULL OR fault_category = '');
开发规范
- 新增 API:在
src/app/api/ 下创建路由 → 顶部调用 initDatabase() → getCurrentUser() 验证 → hasPermission() 校验
- 新增页面:在
src/app/(app)/ 下创建 → 布局由 (app)/layout.tsx 提供
- 权限格式:
resource:action,如 hasPermission(user, 'tickets:write')
- 日期处理:禁止使用
Date.toISOString() 格式化本地日期。toISOString() 返回 UTC 时间,在中国时区(UTC+8)下 new Date('2026-04-01T00:00:00').toISOString() 会返回 "2026-03-31T16:00:00.000Z",日期偏移一天。应使用本地时间方法拼接:${d.getFullYear()}-${String(d.getMonth()+1).padStart(2,'0')}-${String(d.getDate()).padStart(2,'0')}
故障排查
容器启动失败
ssh txjp "docker logs issue-ai"
月报/周报生成失败
先查日志:ssh txjp "docker logs issue-ai 2>&1 | grep -A5 'Report.*failed' | tail -30"
常见根因:
-
ENOENT: no such file or directory, open '/app/node_modules/xxx' — 新增了 npm 依赖但未重建镜像。修复:重建镜像(命令见上方 Docker 部署)。
-
error while loading shared libraries: libglib-2.0.so.0 — 缺少 Chromium 系统库。Dockerfile 已安装全套依赖(libglib2.0-0、libnss3、libnspr4、libatk1.0-0、libatk-bridge2.0-0、libcups2、libdrm2、libdbus-1-3、libxkbcommon0、libxcomposite1、libxdamage1、libxfixes3、libxrandr2、libgbm1、libasound2、libpango-1.0-0、libcairo2)。
-
fs.readFileSync 依赖在 standalone 中丢失 — @vercel/nft 不追踪动态读取。预防:next.config.ts 中 outputFileTracingIncludes 已添加 echarts。
其他
# 数据库初始化
ssh txjp "docker exec issue-ai node -e \"require('./scripts/init-db.js')\""
# 确认 assets API 连通性
ssh txjp "docker exec issue-ai sh -c 'wget -q -O- http://assets-ai:3000/api/auth/me'"
# 实时日志
ssh txjp "docker logs -f issue-ai"