issue-ai/CLAUDE.md

13 KiB
Raw Permalink Blame History

CLAUDE.md — issue.tlyq.ai 工单跟踪系统

项目概述

issue-ai 是基于 Next.js + SQLite 的工单跟踪管理系统部署在腾讯云txjp 服务器),域名 issue.tlyq.ai。与 assets.tlyq.ai 资产管理系统联动,获取设备信息、提供故障历史查询。


快速参考

属性
站点域名 issue.tlyq.ai
服务器 txjpIP: 43.133.38.210
代码路径 /root/docker/issue-ai/
本地端口 6176
容器名 issue-ai
数据库 SQLitedata/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 路由

认证

登录逻辑v2.1LDAP 优先 + 本地密码缓存回退 + localadmin 应急用户。 登录成功签发两个 cookiesession_issue(本地 JWT7 天)+ tlyq_session(共享 JWT7 天domain=.tlyq.ai。 中间件优先检查 tlyq_session,回退 session_issuegetCurrentUser() 每次验证时检查 LLDAP 用户是否存在(已删除则清除 cookie 踢出)。

方法 路径 说明
POST /api/auth/login 登录LDAP 优先 + 本地回退)
POST /api/auth/logout 登出(清除两个 cookie
GET /api/auth/me 当前用户信息
GET /api/internal/roles 内部 API返回角色列表x-internal-key 鉴权)

工单

方法 路径 说明
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] 单个角色操作

认证机制

  • Web UIv2.1middleware.ts 优先检查 tlyq_session(共享 JWTOA 统一签发)→ 回退 session_issue(本地 JWTgetCurrentUser() 每次请求时检查 LLDAP 用户是否存在,已删除则清除 cookie 踢出
  • localadmin:纯本地 BCrypt 认证,不依赖 LLDAP用于 LLDAP 故障时应急登录DB 预置admin 角色)
  • API Keyv2.2middleware.ts 检查 ALLOWED_API_KEYS 环境变量(逗号分隔明文 key无效 key 在中间件层直接返回 401。注意middleware 运行在 Edge Runtime不能使用 better-sqlite3DB 级别的 key 验证由 route handler 中的 auth.ts verifyApiKey() 进行(查 api_keys 表 SHA-256 hash。外部系统调用本系统 API 时key 必须注册在 ALLOWED_API_KEYSWeb UI 创建的 key 还需同步到环境变量,重启容器后生效)

环境配置

本地与云端差异

环境变量 本地开发 云服务器txjp 说明
ASSETS_API_URL http://localhost:6177/api http://assets-ai:3000/api 调用 assets API 地址
ASSETS_API_KEY 本地 assets-ai 生成 云上 assets-ai 生成 每个环境独立,不可跨环境使用
NEXT_PUBLIC_ASSETS_URL http://localhost:6177 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:6177/api
ASSETS_API_KEY=ak_<32字节十六进制>
ALLOWED_API_KEYS=ak_<32字节十六进制>
NEXT_PUBLIC_ASSETS_URL=http://localhost:6177

与 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 → assetssrc/lib/assets-client.ts):根据 device_ip 模糊搜索,匹配 business_ip 或 hdm_ip
  • assets → issuesrc/app/api/tickets/by-asset/route.ts):查询同 IP 历史工单,支持 Cookie 和 API Key 双认证

API Key 创建与配置

Key 格式:ak_<32位十六进制>,认证头:Authorization: Bearer <key>。每个环境独立创建,互不通用。

验证策略v2.2:两边中间件均采用 ALLOWED_API_KEYS 环境变量 → api_keys 数据库表两级验证。Key 注册在任意一处即可通过认证,推荐走 Web UI 创建(写入 api_keys 表,支持权限和追踪)。

issue → assets 方向:在 assets-ai /settings/api-keys 创建 Key → 写入 issue-ai 的 ASSETS_API_KEY.envdocker-compose.yml 环境变量)

assets → issue 方向:在 issue-ai /settings/api-keys 创建 Key → 写入 assets-ai 的 ISSUE_API_KEY + issue-ai 的 ALLOWED_API_KEYS

部署验证deploy-ai.sh 部署 issue 站点后会自动验证 issue→assets API 连通性(使用配置的 ASSETS_API_KEY 调 assets API若返回 401 则部署失败退出,防止 API Key 配置遗漏上线。


Docker 部署

txjp 服务器
├── issue-ai容器              ← Next.js standalone监听 3000
├── nginx-ai                      ← 反向代理 issue.tlyq.ai → issue-ai:3000
└── webnetexternal            ← 共享网络

部署: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')
  • 日期处理(时区规范):整个系统统一使用 UTC+8北京时间。两处必须遵守
    1. JavaScript/TypeScript:禁止使用 Date.toISOString() 格式化本地日期。toISOString() 返回 UTC 时间在中国时区UTC+8new 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')}
    2. SQLite:所有 datetime('now') 必须写成 datetime('now', '+8 hours'),包括 CREATE TABLE 的 DEFAULT 值、UPDATE/SET 语句、以及查询条件中的时间比较。禁止使用不含时区偏移的 datetime('now')

Git Tag 规范

使用日期版本号 vYYYY.MM.DD(如 v2026.05.18)。提交后打 tag 再推送:

git tag v$(date +%Y.%m.%d) && git push origin main && git push origin v$(date +%Y.%m.%d)

同一天多次提交只打一个 tag。详见根目录 CLAUDE.md


故障排查

容器启动失败

ssh txjp "docker logs issue-ai"

月报/周报生成失败

先查日志:ssh txjp "docker logs issue-ai 2>&1 | grep -A5 'Report.*failed' | tail -30"

常见根因:

  1. ENOENT: no such file or directory, open '/app/node_modules/xxx' — 新增了 npm 依赖但未重建镜像。修复:重建镜像(命令见上方 Docker 部署)。

  2. 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

  3. fs.readFileSync 依赖在 standalone 中丢失@vercel/nft 不追踪动态读取。预防:next.config.tsoutputFileTracingIncludes 已添加 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"