fix(backend): Resolve PgBoss infinite loop issue and cleanup unused files
Backend fixes: - Fix PgBoss task infinite loop on SAE (root cause: missing queue table constraints) - Add singletonKey to prevent duplicate job enqueueing - Add idempotency check in reviewWorker (skip completed tasks) - Add optimistic locking in reviewService (atomic status update) Frontend fixes: - Add isSubmitting state to prevent duplicate submissions in RVW Dashboard - Fix API baseURL in knowledgeBaseApi (relative path) Cleanup (removed): - Old frontend/ directory (migrated to frontend-v2) - python-microservice/ (unused, replaced by extraction_service) - Root package.json and node_modules (accidentally created) - redcap-docker-dev/ (external dependency) - Various temporary files and outdated docs in root New documentation: - docs/07-运维文档/01-PgBoss队列监控与维护.md - docs/07-运维文档/02-故障预防检查清单.md - docs/07-运维文档/03-数据库迁移注意事项.md Database fix applied to RDS: - Added PRIMARY KEY to platform_schema.queue - Added 3 missing foreign key constraints Tested: Local build passed, RDS constraints verified
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -210,4 +210,4 @@ rispy>=0.7.0
|
||||
|
||||
---
|
||||
|
||||
**维护人:** 技术架构师
|
||||
**维护人:** 技术架构师
|
||||
@@ -346,8 +346,4 @@ class KnowledgeBaseEngine {
|
||||
### 2025-11-06 初始版本
|
||||
|
||||
- 基于 Dify 实现
|
||||
- 仅 PKB 模块使用
|
||||
|
||||
---
|
||||
|
||||
**维护人:** 技术架构师
|
||||
- 仅 PKB 模块使用---**维护人:** 技术架构师
|
||||
@@ -307,10 +307,6 @@ Level 3: 兜底Prompt(缓存也失效)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1 +1,560 @@
|
||||
# ADMIN-运营管理端 - 模块当前状态与开发指南
|
||||
|
||||
> **最后更新:** 2026-01-25
|
||||
> **状态:** ✅ Phase 4.2 运营监控系统MVP完成!登录跳转逻辑优化完成!
|
||||
> **版本:** v0.6 (Alpha)
|
||||
|
||||
---
|
||||
|
||||
## 🎯 一句话总结
|
||||
|
||||
**运营管理端是AI临床研究平台的核心管理后台,提供多租户管理、Prompt工程化调试、用户权限配置等运营能力。**
|
||||
|
||||
---
|
||||
|
||||
## 📊 当前开发状态
|
||||
|
||||
### ✅ 已完成(2026-01-11)
|
||||
|
||||
**Phase 0-3:基础架构**
|
||||
- [x] 数据库 Schema 设计(platform_schema, capability_schema)
|
||||
- [x] JWT 认证系统(`backend/src/common/auth/`)
|
||||
- [x] 登录/登出功能(前后端完整实现)
|
||||
- [x] 认证中间件(Fastify)
|
||||
- [x] 前端认证对接(AuthContext, LoginPage)
|
||||
- [x] 测试用户创建(8个角色用户)
|
||||
|
||||
**Phase 3.5.1:Prompt 基础设施**
|
||||
- [x] 创建 capability_schema
|
||||
- [x] 添加 prompt_templates 和 prompt_versions 表
|
||||
- [x] 添加 prompt:* 权限(view/edit/debug/publish)
|
||||
- [x] 迁移 RVW Prompt 到数据库(2个:EDITORIAL, METHODOLOGY)
|
||||
|
||||
**Phase 3.5.2:PromptService 核心**
|
||||
- [x] 灰度预览逻辑(DRAFT/ACTIVE 分发)
|
||||
- [x] 模块级调试控制(setDebugMode)
|
||||
- [x] Handlebars 模板渲染
|
||||
- [x] 变量提取与校验(extractVariables, validateVariables)
|
||||
- [x] 三级容灾(数据库→缓存→兜底)
|
||||
- [x] 兜底 Prompt(hardcoded fallbacks)
|
||||
|
||||
**Phase 3.5.3:管理 API**
|
||||
- [x] 8个 RESTful 接口(列表、详情、保存、发布、回滚、调试、测试渲染、清缓存)
|
||||
- [x] 路由注册(`/api/admin/prompts`)
|
||||
|
||||
**Phase 3.5.4:前端管理界面**
|
||||
- [x] 管理端基础架构(AdminLayout, OrgLayout, 路由)
|
||||
- [x] 头像下拉菜单切换入口
|
||||
- [x] PromptListPage(筛选、搜索、调试开关)
|
||||
- [x] PromptEditor(CodeMirror 6 简化版,中文友好)
|
||||
- [x] PromptEditorPage(编辑、保存、发布、测试、版本历史)
|
||||
|
||||
**Phase 3.5.5:RVW 模块集成** ✅ 已完成(2026-01-12)
|
||||
- [x] RVW editorialService 集成 PromptService
|
||||
- [x] RVW methodologyService 集成 PromptService
|
||||
- [x] RVW reviewWorker 传递 userId
|
||||
- [x] 修复 ReviewTask 外键约束问题(跨 schema 外键)
|
||||
- [x] 全模块认证规范化(RVW, PKB, ASL, DC)
|
||||
|
||||
**Phase 4.0:租户与模块管理** ✅ 已完成(2026-01-12)
|
||||
- [x] 新增 modules 表(动态模块管理)
|
||||
- [x] ModuleService(多租户模块权限合并)
|
||||
- [x] requireModule 中间件(模块访问控制)
|
||||
- [x] 所有业务模块添加 requireModule 检查
|
||||
- [x] 租户管理后端 API(CRUD + 模块配置)
|
||||
- [x] 租户管理前端界面(列表、详情、编辑、模块配置)
|
||||
- [x] 前端模块权限动态过滤(首页 + 导航)
|
||||
- [x] Prompt 界面优化(模块中文显示、版本历史增强)
|
||||
|
||||
**Phase 4.1:用户管理** ✅ 已完成(2026-01-16)🎉
|
||||
- [x] 数据库:新增 user_modules 表(精细化模块权限)
|
||||
- [x] 权限系统:新增 4 个用户权限(view/create/edit/delete)
|
||||
- [x] UserService:CRUD + 租户隔离 + 模块权限管理(780 行)
|
||||
- [x] UserController + UserRoutes:13 个 API 端点(648 行)
|
||||
- [x] 批量导入:Excel 批量导入用户功能
|
||||
- [x] UserListPage:列表+筛选+搜索+分页(412 行)
|
||||
- [x] UserFormPage:创建/编辑+租户配置+模块权限(341 行)
|
||||
- [x] UserDetailPage:详情+租户管理+模块权限查看(393 行)
|
||||
- [x] 3 个弹窗组件:导入、分配租户、模块权限配置(592 行)
|
||||
- [x] **架构升级:** 模块权限系统改造(版本系统 → 模块代码系统)
|
||||
|
||||
**架构重大升级:模块权限系统** ✅ 已完成(2026-01-16)🏆
|
||||
- [x] 后端:登录API返回用户可访问模块列表(`modules: ['PKB', 'RVW']`)
|
||||
- [x] 后端:getUserModules() 方法(智能计算用户+租户模块权限)
|
||||
- [x] 前端:AuthContext 添加 hasModule() 方法
|
||||
- [x] 前端:导航栏基于模块权限过滤显示(只显示可访问模块)
|
||||
- [x] 前端:RouteGuard 基于 moduleCode 检查权限
|
||||
- [x] 前端:移除旧的 requiredVersion 系统
|
||||
- [x] 体验优化:登录跳转智能判断(避免普通用户跳转到管理端403)
|
||||
|
||||
**Phase 4.2:运营监控系统** ✅ 已完成(2026-01-25)🎉
|
||||
- [x] 数据库:新增 SimpleLog 运营日志表(admin_schema)
|
||||
- [x] 后端服务:ActivityService 火烧即忘埋点服务(带 try-catch 保护)
|
||||
- [x] 后端API:statsRoutes 统计接口(overview/live-feed/user-overview/cleanup)
|
||||
- [x] 模块埋点:7个模块埋点全部完成
|
||||
- SYSTEM(登录)
|
||||
- AIA(智能体对话)
|
||||
- PKB(知识库管理、RAG检索)
|
||||
- ASL(文献筛选)
|
||||
- DC(Tool B提取、Tool C代码处理)
|
||||
- RVW(稿件审查)
|
||||
- IIT(REDCap同步)
|
||||
- [x] 前端看板:Admin Dashboard 运营数据展示(DAU/DAT/模块统计/实时活动流)
|
||||
- [x] 权限控制:stats:view 权限检查
|
||||
|
||||
**Phase 4.3:登录体验优化** ✅ 已完成(2026-01-25)
|
||||
- [x] 修复:用户模块权限显示问题(userService.ts 逻辑修正)
|
||||
- [x] 修复:登录后默认进入AI问答页面(/ai-qa)而非首页
|
||||
- [x] 优化:顶部导航 LOGO 更换为品牌图标
|
||||
- [x] 修复:SUPER_ADMIN 用户模块权限返回完整列表
|
||||
- [x] 修复:LoginPage 路径映射与 moduleRegistry 一致
|
||||
|
||||
**Phase 4.4:PKB 布局修复** ✅ 已完成(2026-01-25)
|
||||
- [x] 修复:PKB 工作区问答页面布局问题(CSS类名冲突)
|
||||
- [x] 修复:Protocol Agent 模块 CSS 类名重命名(.pa-chat-container)
|
||||
|
||||
### ⏳ 待开发(按优先级)
|
||||
|
||||
**P2 - 用户管理增强(可选)**
|
||||
- [ ] 用户批量操作(批量禁用、批量分配租户)
|
||||
- [ ] 用户操作日志(audit_logs 集成)
|
||||
- [ ] 用户统计分析(活跃度、模块使用率)
|
||||
|
||||
**P2 - Prompt 管理优化**
|
||||
- [ ] Prompt 版本对比功能
|
||||
- [ ] Prompt 批量操作
|
||||
- [ ] Prompt 导入/导出
|
||||
|
||||
**P2 - 租户高级功能**
|
||||
- [ ] 品牌定制配置(logo、主题色)
|
||||
- [ ] 租户专属登录页
|
||||
- [ ] 配额管理界面
|
||||
|
||||
---
|
||||
|
||||
## 🗄️ 数据库状态
|
||||
|
||||
### 已有表(需要整合)
|
||||
|
||||
```sql
|
||||
-- 现有的用户表(需要统一)
|
||||
public.users -- 旧的用户表
|
||||
platform_schema.User -- 新的用户表(Prisma)
|
||||
|
||||
-- 现有的审计表
|
||||
public.AdminLog -- 旧的审计日志
|
||||
```
|
||||
|
||||
### ✅ 已创建的表(2026-01-12)
|
||||
|
||||
**platform_schema(平台基础)**
|
||||
- ✅ `users` - 用户表(含 phone, password, role, is_default_password)
|
||||
- ✅ `tenants` - 租户表(含 PUBLIC 类型)
|
||||
- ✅ `tenant_members` - 租户成员(支持用户加入多个租户)
|
||||
- ✅ `tenant_modules` - 租户订阅模块(控制租户可访问的功能)
|
||||
- ✅ `tenant_quotas` - 租户配额
|
||||
- ✅ `tenant_quota_allocations` - 配额分配
|
||||
- ✅ `departments` - 科室表
|
||||
- ✅ `permissions` - 权限表(含 prompt:*/tenant:* 权限)
|
||||
- ✅ `role_permissions` - 角色权限
|
||||
- ✅ `verification_codes` - 验证码表
|
||||
- ✅ `modules` - 系统模块表(动态管理可用模块)🆕 2026-01-12
|
||||
|
||||
**capability_schema(通用能力)** ✅ 新增
|
||||
- ✅ `prompt_templates` - Prompt模板
|
||||
- ✅ `prompt_versions` - Prompt版本
|
||||
|
||||
**admin_schema(运营管理)** ✅ 新增 2026-01-25
|
||||
- ✅ `simple_logs` - 极简运营日志表(MVP)🆕
|
||||
- `admin_operation_logs` - 运营操作日志(未来)
|
||||
|
||||
---
|
||||
|
||||
## 🏗️ 架构概览
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ 运营管理端(ADMIN Portal) │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ 🏢 租户管理 │ 👤 用户管理 │ 🎨 Prompt管理 │
|
||||
│ 📊 配额管理 │ 🔐 权限配置 │ 📋 审计日志 │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Platform Layer (平台层) │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ 认证中心 │ 权限中心 │ 存储服务 │ 通知服务 │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Capability Layer (能力层) │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ Prompt管理 │ LLM Gateway │ 文档引擎 │ RAG引擎 │
|
||||
└─────────────────────────────────────────────────┘
|
||||
↓
|
||||
┌─────────────────────────────────────────────────┐
|
||||
│ Business Modules (业务模块) │
|
||||
├─────────────────────────────────────────────────┤
|
||||
│ ASL │ DC │ IIT │ PKB │ AIA │ RVW │ SSA │ ST │
|
||||
└─────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔐 角色与权限矩阵
|
||||
|
||||
| 功能模块 | SUPER_ADMIN | PROMPT_ENGINEER | HOSPITAL_ADMIN | PHARMA_ADMIN | USER |
|
||||
|---------|-------------|-----------------|----------------|--------------|------|
|
||||
| 租户管理 | ✅ 全部 | ❌ | ❌ | ❌ | ❌ |
|
||||
| Prompt管理 | ✅ 全部 | ✅ 全部 | ❌ | ❌ | ❌ |
|
||||
| 用户管理(全局) | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| 用户管理(租户内) | ✅ | ❌ | ✅ | ✅ | ❌ |
|
||||
| 配额分配 | ✅ | ❌ | ✅ | ✅ | ❌ |
|
||||
| 审计日志(全局) | ✅ | ❌ | ❌ | ❌ | ❌ |
|
||||
| 审计日志(租户内) | ✅ | ❌ | ✅ | ✅ | ❌ |
|
||||
| 业务模块使用 | ✅ | ✅ | ✅ | ✅ | ✅ |
|
||||
|
||||
---
|
||||
|
||||
## 📁 代码结构
|
||||
|
||||
### ✅ 实际已完成的结构(2026-01-12)
|
||||
|
||||
**后端**
|
||||
```
|
||||
backend/src/
|
||||
├── common/
|
||||
│ ├── auth/ # ✅ 认证模块
|
||||
│ │ ├── jwt.service.ts # JWT Token管理
|
||||
│ │ ├── auth.service.ts # 业务逻辑
|
||||
│ │ ├── auth.middleware.ts # 认证中间件 + requireModule 🆕
|
||||
│ │ ├── module.service.ts # 🆕 模块权限服务(多租户合并)
|
||||
│ │ ├── auth.controller.ts # API控制器 + getUserModules 🆕
|
||||
│ │ ├── auth.routes.ts # 路由
|
||||
│ │ └── index.ts
|
||||
│ │
|
||||
│ └── prompt/ # ✅ Prompt管理
|
||||
│ ├── prompt.types.ts # 类型定义
|
||||
│ ├── prompt.service.ts # 核心服务
|
||||
│ ├── prompt.controller.ts # API控制器(增强版本返回)🆕
|
||||
│ ├── prompt.routes.ts # 路由
|
||||
│ ├── prompt.fallbacks.ts # 兜底Prompt
|
||||
│ └── index.ts
|
||||
│
|
||||
├── modules/
|
||||
│ ├── admin/ # 🆕 租户管理模块
|
||||
│ │ ├── types/
|
||||
│ │ │ └── tenant.types.ts # 租户类型定义
|
||||
│ │ ├── services/
|
||||
│ │ │ └── tenantService.ts # 租户业务逻辑
|
||||
│ │ ├── controllers/
|
||||
│ │ │ └── tenantController.ts # 租户控制器
|
||||
│ │ └── routes/
|
||||
│ │ └── tenantRoutes.ts # 租户路由
|
||||
│ │
|
||||
│ ├── rvw/ # ✅ RVW模块(已集成PromptService)
|
||||
│ ├── pkb/ # ✅ PKB模块(已添加认证)
|
||||
│ ├── asl/ # ✅ ASL模块(已添加认证)
|
||||
│ └── dc/ # ✅ DC模块(已添加认证)
|
||||
|
||||
backend/scripts/
|
||||
├── seed-modules.js # 🆕 模块数据初始化
|
||||
├── query-users.js # 查询用户和租户信息
|
||||
└── [其他脚本]
|
||||
```
|
||||
|
||||
**前端**
|
||||
```
|
||||
frontend-v2/src/
|
||||
├── framework/
|
||||
│ ├── auth/ # ✅ 认证框架
|
||||
│ │ ├── AuthContext.tsx # 认证上下文
|
||||
│ │ ├── api.ts # 认证API
|
||||
│ │ ├── moduleApi.ts # 🆕 用户模块权限API
|
||||
│ │ └── types.ts
|
||||
│ │
|
||||
│ ├── modules/ # ✅ 模块注册
|
||||
│ │ ├── moduleRegistry.ts # 模块注册(新增moduleCode)🆕
|
||||
│ │ └── types.ts # 模块类型定义
|
||||
│ │
|
||||
│ └── layout/ # ✅ 布局组件
|
||||
│ ├── MainLayout.tsx # 业务端布局
|
||||
│ ├── AdminLayout.tsx # 运营管理端布局
|
||||
│ ├── OrgLayout.tsx # 机构管理端布局
|
||||
│ └── TopNavigation.tsx # 顶部导航(模块权限过滤)🆕
|
||||
│
|
||||
├── pages/
|
||||
│ ├── HomePage.tsx # 首页(模块权限过滤)🆕
|
||||
│ ├── admin/ # ✅ 运营管理端页面
|
||||
│ │ ├── AdminDashboard.tsx # 概览页
|
||||
│ │ ├── PromptListPage.tsx # Prompt列表(模块中文显示)🆕
|
||||
│ │ ├── PromptEditorPage.tsx # Prompt编辑(版本历史增强)🆕
|
||||
│ │ ├── tenants/ # 🆕 租户管理页面
|
||||
│ │ │ ├── TenantListPage.tsx # 租户列表
|
||||
│ │ │ ├── TenantDetailPage.tsx # 租户详情/编辑/模块配置
|
||||
│ │ │ └── api/
|
||||
│ │ │ └── tenantApi.ts # 租户API调用
|
||||
│ │ ├── PromptListPage.tsx # ✅ Prompt列表(254行)
|
||||
│ │ ├── PromptEditorPage.tsx # ✅ Prompt编辑器(399行)
|
||||
│ │ ├── components/
|
||||
│ │ │ └── PromptEditor.tsx # ✅ CodeMirror 6(245行)
|
||||
│ │ └── api/
|
||||
│ │ └── promptApi.ts # ✅ API调用层(172行)
|
||||
│ │
|
||||
│ ├── org/ # ✅ 机构管理端页面
|
||||
│ │ └── OrgDashboard.tsx # 概览页
|
||||
│ │
|
||||
│ └── LoginPage.tsx # ✅ 通用登录页(368行)
|
||||
```
|
||||
|
||||
### 📋 原计划结构(待开发)
|
||||
|
||||
### 后端
|
||||
|
||||
```
|
||||
backend/src/
|
||||
├── modules/
|
||||
│ └── admin/ # 运营管理端模块
|
||||
│ ├── controllers/
|
||||
│ │ ├── tenant.controller.ts
|
||||
│ │ ├── user.controller.ts
|
||||
│ │ └── audit.controller.ts
|
||||
│ ├── services/
|
||||
│ │ ├── tenant.service.ts
|
||||
│ │ ├── user.service.ts
|
||||
│ │ └── audit.service.ts
|
||||
│ └── routes/
|
||||
│ └── admin.routes.ts
|
||||
│
|
||||
├── common/
|
||||
│ ├── capabilities/
|
||||
│ │ └── prompt/ # Prompt管理系统
|
||||
│ │ ├── prompt.service.ts # 核心逻辑
|
||||
│ │ ├── prompt.controller.ts
|
||||
│ │ └── prompt.routes.ts
|
||||
│ │
|
||||
│ └── middleware/
|
||||
│ ├── auth.middleware.ts # JWT认证
|
||||
│ ├── permission.middleware.ts # 权限检查
|
||||
│ └── tenant.middleware.ts # 租户隔离
|
||||
│
|
||||
└── platform/
|
||||
├── auth/
|
||||
│ ├── auth.service.ts
|
||||
│ └── jwt.service.ts
|
||||
└── permission/
|
||||
└── permission.service.ts
|
||||
```
|
||||
|
||||
### 前端
|
||||
|
||||
```
|
||||
frontend-v2/src/
|
||||
├── modules/
|
||||
│ └── admin/ # 运营管理端模块
|
||||
│ ├── pages/
|
||||
│ │ ├── TenantManagement/ # 租户管理
|
||||
│ │ ├── UserManagement/ # 用户管理
|
||||
│ │ ├── PromptManagement/ # Prompt管理
|
||||
│ │ └── AuditLog/ # 审计日志
|
||||
│ │
|
||||
│ ├── components/
|
||||
│ │ ├── TenantForm/
|
||||
│ │ ├── UserForm/
|
||||
│ │ ├── PromptEditor/
|
||||
│ │ └── PromptDebugSwitch/ # 全局调试开关
|
||||
│ │
|
||||
│ └── services/
|
||||
│ ├── tenant.service.ts
|
||||
│ ├── user.service.ts
|
||||
│ └── prompt.service.ts
|
||||
│
|
||||
└── framework/
|
||||
├── auth/
|
||||
│ └── AuthContext.tsx # 认证上下文
|
||||
└── permission/
|
||||
└── PermissionContext.tsx # 权限上下文
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🚀 快速开始开发
|
||||
|
||||
### 1. 环境准备
|
||||
|
||||
```bash
|
||||
# 后端
|
||||
cd backend
|
||||
npm install jsonwebtoken bcryptjs handlebars
|
||||
npm install -D @types/jsonwebtoken @types/bcryptjs
|
||||
|
||||
# 前端
|
||||
cd frontend-v2
|
||||
# 无需额外依赖,使用现有技术栈
|
||||
```
|
||||
|
||||
### 2. 数据库准备
|
||||
|
||||
```bash
|
||||
# 1. 备份现有数据
|
||||
cd backend
|
||||
npx prisma db pull --schema=./prisma/backup.prisma
|
||||
|
||||
# 2. 更新Schema
|
||||
# 编辑 prisma/schema.prisma,添加新表
|
||||
|
||||
# 3. 生成迁移
|
||||
npx prisma migrate dev --name add_admin_and_prompt_tables
|
||||
|
||||
# 4. 运行种子数据
|
||||
npx prisma db seed
|
||||
```
|
||||
|
||||
### 3. 开发优先级
|
||||
|
||||
**🔴 必须先做(Phase 0)**
|
||||
1. 数据库迁移(统一User表)
|
||||
2. 创建超级管理员账号
|
||||
3. JWT认证系统
|
||||
|
||||
**🟠 然后做(Phase 1)**
|
||||
1. PromptService实现
|
||||
2. Prompt管理API
|
||||
|
||||
**🟡 最后做(Phase 2)**
|
||||
1. 租户管理界面
|
||||
2. Prompt管理界面
|
||||
|
||||
---
|
||||
|
||||
## 📚 核心文档导航
|
||||
|
||||
### 必读文档(开发前)
|
||||
|
||||
1. **架构梳理**
|
||||
`00-系统设计/00-权限与角色体系梳理报告_v1.0.md`
|
||||
→ 了解整体架构、数据库设计、实施路线图
|
||||
|
||||
2. **需求文档**
|
||||
`01-需求分析/02-通用能力层_07-运营与机构管理端PRD_v2.1.md`
|
||||
→ 了解业务需求、用户故事、验收标准
|
||||
|
||||
3. **Prompt管理(核心)**
|
||||
`02-技术设计/03-Prompt管理系统快速参考.md`
|
||||
→ 了解Prompt管理的实现细节
|
||||
|
||||
### 开发中参考
|
||||
|
||||
1. **技术设计**
|
||||
`02-技术设计/02-通用能力层_03-Prompt管理系统与灰度预览设计方案.md`
|
||||
|
||||
2. **反馈建议**
|
||||
`00-系统设计/02-通用能力层_10-权限体系梳理反馈与修正建议.md`
|
||||
|
||||
---
|
||||
|
||||
## 🔧 技术要点
|
||||
|
||||
### 1. JWT认证
|
||||
|
||||
```typescript
|
||||
// 生成Token
|
||||
const token = jwt.sign(
|
||||
{
|
||||
userId: user.id,
|
||||
role: user.role,
|
||||
tenantId: user.tenantId // 多租户
|
||||
},
|
||||
process.env.JWT_SECRET,
|
||||
{ expiresIn: '7d' }
|
||||
);
|
||||
|
||||
// 验证Token
|
||||
const decoded = jwt.verify(token, process.env.JWT_SECRET);
|
||||
```
|
||||
|
||||
### 2. 多租户隔离
|
||||
|
||||
```typescript
|
||||
// 中间件:自动注入tenantId
|
||||
fastify.addHook('preHandler', async (request, reply) => {
|
||||
const user = request.user;
|
||||
request.tenantId = user.tenantId;
|
||||
});
|
||||
|
||||
// ORM查询:自动过滤
|
||||
const projects = await prisma.project.findMany({
|
||||
where: { tenantId: request.tenantId }
|
||||
});
|
||||
```
|
||||
|
||||
### 3. Prompt灰度预览
|
||||
|
||||
```typescript
|
||||
// 核心逻辑
|
||||
async get(code: string, variables: any, userId: string) {
|
||||
// 调试者看DRAFT版本
|
||||
if (this.debugUsers.has(userId)) {
|
||||
const draft = await this.getDraftVersion(code);
|
||||
if (draft) return this.render(draft.content, variables);
|
||||
}
|
||||
|
||||
// 普通用户看ACTIVE版本(带缓存)
|
||||
const active = await this.getActiveVersion(code);
|
||||
return this.render(active.content, variables);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## ⚠️ 常见问题
|
||||
|
||||
### Q1: 现有的`public.users`表怎么处理?
|
||||
|
||||
**A:** Phase 0会执行数据迁移:
|
||||
1. 将`public.users`数据迁移到`platform_schema.users`
|
||||
2. 重命名为`public.users_backup`保留1周
|
||||
3. 验证无误后删除
|
||||
|
||||
### Q2: Prompt管理会影响现有业务模块吗?
|
||||
|
||||
**A:** 不会。Prompt管理是增量功能:
|
||||
- 现有硬编码Prompt继续工作
|
||||
- 新开发模块调用`promptService.get()`
|
||||
- 老模块可逐步迁移
|
||||
|
||||
### Q3: 调试模式安全吗?
|
||||
|
||||
**A:** 是的,有多层保障:
|
||||
- 权限检查(`prompt:debug`)
|
||||
- 状态存储在内存(登出自动失效)
|
||||
- 审计日志记录所有操作
|
||||
|
||||
### Q4: 多租户隔离如何保证?
|
||||
|
||||
**A:** 三层防护:
|
||||
1. **API层**:中间件检查`tenantId`
|
||||
2. **Service层**:自动注入`tenantId`过滤
|
||||
3. **DB层**:(Phase 2)Prisma Extension强制隔离
|
||||
|
||||
---
|
||||
|
||||
## 📞 需要帮助?
|
||||
|
||||
1. **查看文档**:`README.md` 和各技术设计文档
|
||||
2. **查看代码**:参考DC/ASL等已有模块的实现
|
||||
3. **提问**:在开发记录中记录问题和解决方案
|
||||
|
||||
---
|
||||
|
||||
## 📅 下一步行动
|
||||
|
||||
- [ ] Review架构设计文档
|
||||
- [ ] 确认开发排期(建议4周)
|
||||
- [ ] 准备开发环境
|
||||
- [ ] **启动Phase 0**:数据库迁移
|
||||
|
||||
---
|
||||
|
||||
*祝开发顺利!🚀*
|
||||
|
||||
|
||||
735
docs/03-业务模块/ADMIN-运营管理端/04-开发计划/03-运营监控系统MVP开发计划.md
Normal file
735
docs/03-业务模块/ADMIN-运营管理端/04-开发计划/03-运营监控系统MVP开发计划.md
Normal file
@@ -0,0 +1,735 @@
|
||||
# 运营监控系统 MVP 开发计划
|
||||
|
||||
> **文档版本**:V3.1 (完整版)
|
||||
> **创建日期**:2026-01-25
|
||||
> **基于文档**:运营体系设计方案-MVP-V3.0.md
|
||||
> **预计工时**:4-5 小时
|
||||
|
||||
---
|
||||
|
||||
## 📋 修订说明
|
||||
|
||||
本计划基于 V3.0 方案进行审查修订,主要解决以下 **8 个问题**:
|
||||
|
||||
| # | 问题 | 严重程度 | 修订内容 |
|
||||
|---|------|---------|---------|
|
||||
| 1 | 模块覆盖不完整 | 🔴 严重 | 补充 RVW、IIT、Protocol Agent、SSA/ST 预留 |
|
||||
| 2 | 缺少 tenantName 字段 | 🔴 严重 | 添加冗余字段避免 JOIN |
|
||||
| 3 | RVW 埋点清单缺失 | 🔴 严重 | 新增 RVW 模块埋点清单 |
|
||||
| 4 | 用户360画像缺少 RVW | 🔴 严重 | 补充 RVW 资产统计 |
|
||||
| 5 | action 类型不够全面 | 🟡 中等 | 扩展 CREATE/DELETE 类型 |
|
||||
| 6 | 缺少 API 路由设计 | 🟡 中等 | 新增完整 API 端点设计 |
|
||||
| 7 | 数据保留策略缺失 | 🟡 中等 | 补充 180 天数据清理 |
|
||||
| 8 | 权限控制未说明 | 🟡 中等 | 明确角色权限矩阵 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 核心指标定义(保持 V3.0)
|
||||
|
||||
| 优先级 | 指标名称 | 定义 | 价值 |
|
||||
|--------|---------|------|-----|
|
||||
| **P0+** | 活跃医生数 (DAU) | 今日有行为的去重 user_id 数 | 真实价值线 |
|
||||
| **P0** | 活跃租户数 (DAT) | 今日有行为的去重 tenant_id 数 | 商务生死线 |
|
||||
| **P1** | 功能渗透率 | 各模块/功能使用次数分布 | 产品迭代指引 |
|
||||
| **P2** | 价值交付次数 | 导出/下载次数 | 北极星指标 |
|
||||
|
||||
---
|
||||
|
||||
## 2. 数据库设计(V3.1 修订版)
|
||||
|
||||
### 2.1 SimpleLog 表(admin_schema)
|
||||
|
||||
```prisma
|
||||
/// 运营日志表 (MVP V3.1)
|
||||
model SimpleLog {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
// === 租户和用户信息 ===
|
||||
tenantId String @map("tenant_id") @db.VarChar(50)
|
||||
tenantName String? @map("tenant_name") @db.VarChar(100) // 🆕 冗余字段,避免JOIN
|
||||
userId String @map("user_id") @db.Uuid
|
||||
userName String? @map("user_name") @db.VarChar(50)
|
||||
|
||||
// === 行为记录 ===
|
||||
module String @db.VarChar(20) // 模块代码
|
||||
feature String @db.VarChar(50) // 细分功能
|
||||
action String @db.VarChar(20) // 动作类型
|
||||
|
||||
// === 详情信息 ===
|
||||
info String? @db.Text // JSON或文本详情
|
||||
|
||||
// === 索引 ===
|
||||
@@index([createdAt])
|
||||
@@index([tenantId])
|
||||
@@index([userId])
|
||||
@@index([module, feature])
|
||||
@@index([action]) // 🆕 支持按动作筛选
|
||||
@@map("simple_logs")
|
||||
@@schema("admin_schema")
|
||||
}
|
||||
```
|
||||
|
||||
### 2.2 字段说明
|
||||
|
||||
#### module(模块代码)- 完整列表
|
||||
|
||||
```typescript
|
||||
type ModuleCode =
|
||||
| 'AIA' // AI智能问答 (12智能体 + Protocol Agent)
|
||||
| 'PKB' // 个人知识库
|
||||
| 'ASL' // AI智能文献
|
||||
| 'DC' // 数据清洗整理
|
||||
| 'RVW' // 稿件审查系统 🆕
|
||||
| 'IIT' // IIT Manager Agent 🆕
|
||||
| 'SSA' // 智能统计分析 (预留) 🆕
|
||||
| 'ST' // 统计分析工具 (预留) 🆕
|
||||
| 'SYSTEM'; // 系统级行为 (登录/登出)
|
||||
```
|
||||
|
||||
#### action(动作类型)
|
||||
|
||||
```typescript
|
||||
type ActionType =
|
||||
| 'LOGIN' // 登录系统
|
||||
| 'USE' // 使用功能
|
||||
| 'EXPORT' // 导出/下载
|
||||
| 'CREATE' // 创建资源 🆕
|
||||
| 'DELETE' // 删除资源 🆕
|
||||
| 'ERROR'; // 错误记录
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 完整埋点清单(按模块)
|
||||
|
||||
### 3.1 🤖 AIA 模块(AI智能问答)
|
||||
|
||||
#### 12 个智能体
|
||||
|
||||
| agentId | feature (中文) | 埋点位置 |
|
||||
|---------|---------------|---------|
|
||||
| topic-scoping | 科学问题梳理 | conversationService.complete() |
|
||||
| pico-analysis | PICO梳理 | conversationService.complete() |
|
||||
| topic-eval | 选题评价 | conversationService.complete() |
|
||||
| outcome-design | 观察指标设计 | conversationService.complete() |
|
||||
| crf-design | CRF设计 | conversationService.complete() |
|
||||
| sample-size | 样本量计算 | conversationService.complete() |
|
||||
| protocol-writing | 方案撰写 | conversationService.complete() |
|
||||
| methodology-review | 方法学评审 | conversationService.complete() |
|
||||
| paper-polish | 论文润色 | conversationService.complete() |
|
||||
| paper-translate | 论文翻译 | conversationService.complete() |
|
||||
| data-preprocess | 数据预处理 | 跳转DC,记录来源 |
|
||||
| stat-analysis | 统计分析 | 跳转DC,记录来源 |
|
||||
|
||||
#### Protocol Agent(🆕 2026-01-25 新功能)
|
||||
|
||||
| feature | action | 埋点位置 | info 示例 |
|
||||
|---------|--------|---------|----------|
|
||||
| Protocol要素收集 | USE | ProtocolOrchestrator.collectPhase() | "阶段1完成" |
|
||||
| Protocol方案生成 | USE | ProtocolOrchestrator.generateProtocol() | "生成12章节方案" |
|
||||
| Protocol Word导出 | EXPORT | ProtocolAgentController.exportWord() | "导出Word文档" |
|
||||
|
||||
**埋点代码位置**:
|
||||
- `backend/src/modules/agent/protocol/services/ProtocolOrchestrator.ts`
|
||||
- `backend/src/modules/agent/protocol/controllers/ProtocolAgentController.ts`
|
||||
|
||||
---
|
||||
|
||||
### 3.2 📚 PKB 模块(个人知识库)
|
||||
|
||||
| feature | action | 埋点位置 | info 示例 |
|
||||
|---------|--------|---------|----------|
|
||||
| 知识库创建 | CREATE | knowledgeBaseController.create() | "创建: 肺癌研究库" |
|
||||
| 文档上传 | USE | documentController.upload() | "上传: 5篇PDF" |
|
||||
| RAG问答 | USE | ragController.chat() | "提问: 入排标准是什么?" |
|
||||
| 批处理提取 | USE | batchController.process() | "批量提取: 10篇" |
|
||||
| 结果导出 | EXPORT | batchController.export() | "导出CSV" |
|
||||
|
||||
**埋点代码位置**:
|
||||
- `backend/src/modules/pkb/controllers/`
|
||||
|
||||
---
|
||||
|
||||
### 3.3 📖 ASL 模块(AI智能文献)
|
||||
|
||||
| feature | action | 埋点位置 | info 示例 |
|
||||
|---------|--------|---------|----------|
|
||||
| DeepSearch检索 | USE | researchController.stream() | "关键词: 肺癌治疗" |
|
||||
| 标题摘要筛选 | USE | screeningController.start() | "筛选: 500篇" |
|
||||
| 全文复筛 | USE | fullTextController.start() | "复筛: 100篇" |
|
||||
| 筛选结果导出 | EXPORT | screeningController.export() | "导出Excel" |
|
||||
|
||||
**埋点代码位置**:
|
||||
- `backend/src/modules/asl/controllers/`
|
||||
|
||||
---
|
||||
|
||||
### 3.4 🧹 DC 模块(数据清洗整理)
|
||||
|
||||
| feature | action | 埋点位置 | info 示例 |
|
||||
|---------|--------|---------|----------|
|
||||
| Tool B 健康检查 | USE | toolBController.healthCheck() | "检查: 1000行数据" |
|
||||
| Tool B 自动提取 | USE | toolBController.extract() | "提取任务: 50条" |
|
||||
| Tool C 数据清洗 | USE | toolCController.process() | "执行: 筛选操作" |
|
||||
| Tool C Pivot | USE | toolCController.pivot() | "Pivot转换" |
|
||||
| 结果导出 | EXPORT | toolCController.export() | "导出Excel" |
|
||||
|
||||
**埋点代码位置**:
|
||||
- `backend/src/modules/dc/controllers/`
|
||||
|
||||
---
|
||||
|
||||
### 3.5 📝 RVW 模块(稿件审查系统)🆕
|
||||
|
||||
| feature | action | 埋点位置 | info 示例 |
|
||||
|---------|--------|---------|----------|
|
||||
| 稿件上传 | USE | reviewController.upload() | "上传: xxx.pdf" |
|
||||
| 稿约规范性审查 | USE | reviewWorker (editorial) | "审查开始" |
|
||||
| 方法学审查 | USE | reviewWorker (methodology) | "方法学审查开始" |
|
||||
| 审查完成 | USE | reviewWorker.complete() | "评分: 规范85/方法78" |
|
||||
| 报告导出 | EXPORT | TaskDetail.exportWord() | "导出Word报告" |
|
||||
|
||||
**埋点代码位置**:
|
||||
- `backend/src/modules/rvw/services/reviewWorker.ts`
|
||||
- `backend/src/modules/rvw/controllers/reviewController.ts`
|
||||
|
||||
---
|
||||
|
||||
### 3.6 🏥 IIT 模块(IIT Manager Agent)🆕
|
||||
|
||||
| feature | action | 埋点位置 | info 示例 |
|
||||
|---------|--------|---------|----------|
|
||||
| REDCap数据同步 | USE | redcapAdapter.sync() | "同步: 10条记录" |
|
||||
| AI质控检查 | USE | qualityCheckService.check() | "检查患者ID 7" |
|
||||
| 企微通知推送 | USE | wechatService.notify() | "推送预警通知" |
|
||||
| 对话查询 | USE | chatService.query() | "查询患者统计" |
|
||||
| 人工确权 | USE | actionController.approve() | "确权: 排除患者" |
|
||||
|
||||
**埋点代码位置**:
|
||||
- `backend/src/modules/iit-manager/services/`
|
||||
- `backend/src/modules/iit-manager/controllers/`
|
||||
|
||||
---
|
||||
|
||||
### 3.7 🔐 SYSTEM(系统级)
|
||||
|
||||
| feature | action | 埋点位置 | info 示例 |
|
||||
|---------|--------|---------|----------|
|
||||
| 用户登录 | LOGIN | authController.login() | "密码登录" |
|
||||
| 用户登出 | USE | authController.logout() | - |
|
||||
|
||||
**埋点代码位置**:
|
||||
- `backend/src/common/auth/auth.controller.ts`
|
||||
|
||||
---
|
||||
|
||||
## 4. 后端 API 设计
|
||||
|
||||
### 4.1 运营统计 API
|
||||
|
||||
| 方法 | 路径 | 说明 | 权限 |
|
||||
|------|------|------|------|
|
||||
| GET | `/api/admin/stats/overview` | 今日大盘(DAU/DAT/导出数) | SUPER_ADMIN |
|
||||
| GET | `/api/admin/stats/live-feed` | 实时流水账(最近100条) | SUPER_ADMIN |
|
||||
| GET | `/api/admin/stats/module/:code` | 模块使用统计 | SUPER_ADMIN |
|
||||
| GET | `/api/admin/users/:id/overview` | 用户360画像 | SUPER_ADMIN |
|
||||
|
||||
### 4.2 API 响应示例
|
||||
|
||||
#### 今日大盘 `/api/admin/stats/overview`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"dau": 12, // 今日活跃医生数
|
||||
"dat": 3, // 今日活跃租户数
|
||||
"exportCount": 5, // 今日导出次数
|
||||
"moduleStats": {
|
||||
"AIA": 45,
|
||||
"PKB": 23,
|
||||
"DC": 12,
|
||||
"RVW": 8,
|
||||
"ASL": 5,
|
||||
"IIT": 3
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### 实时流水账 `/api/admin/stats/live-feed`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": [
|
||||
{
|
||||
"id": "uuid",
|
||||
"createdAt": "2026-01-25T10:05:00Z",
|
||||
"tenantName": "协和医院",
|
||||
"userName": "张主任",
|
||||
"module": "AIA",
|
||||
"feature": "选题评价",
|
||||
"action": "USE",
|
||||
"info": "评价得分: 85分"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
#### 用户360画像 `/api/admin/users/:id/overview`
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {
|
||||
"profile": {
|
||||
"id": "uuid",
|
||||
"name": "张主任",
|
||||
"phone": "138****1234",
|
||||
"tenantName": "协和医院"
|
||||
},
|
||||
"assets": {
|
||||
"aia": { "conversationCount": 158 },
|
||||
"pkb": { "kbCount": 3, "docCount": 450 },
|
||||
"dc": { "taskCount": 12 },
|
||||
"rvw": { "reviewTaskCount": 25, "completedCount": 20 } // 🆕
|
||||
},
|
||||
"activities": [
|
||||
{
|
||||
"createdAt": "2026-01-25T10:30:00Z",
|
||||
"module": "AIA",
|
||||
"feature": "选题评价",
|
||||
"action": "USE",
|
||||
"info": "生成结果: 85分"
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 后端服务实现
|
||||
|
||||
### 5.1 ActivityService(埋点服务)
|
||||
|
||||
**文件路径**:`backend/src/common/services/activity.service.ts`
|
||||
|
||||
```typescript
|
||||
import { prisma } from '../../config/database.js';
|
||||
import { logger } from '../logging/index.js';
|
||||
|
||||
type ModuleCode = 'AIA' | 'PKB' | 'ASL' | 'DC' | 'RVW' | 'IIT' | 'SSA' | 'ST' | 'SYSTEM';
|
||||
type ActionType = 'LOGIN' | 'USE' | 'EXPORT' | 'CREATE' | 'DELETE' | 'ERROR';
|
||||
|
||||
export const activityService = {
|
||||
/**
|
||||
* 核心埋点方法 (Fire-and-Forget 模式)
|
||||
* 异步执行,不阻塞主业务
|
||||
*/
|
||||
log(
|
||||
tenantId: string,
|
||||
tenantName: string, // 🆕 新增
|
||||
userId: string,
|
||||
userName: string,
|
||||
module: ModuleCode,
|
||||
feature: string,
|
||||
action: ActionType,
|
||||
info?: any
|
||||
) {
|
||||
// 异步执行,不要 await
|
||||
prisma.simpleLog.create({
|
||||
data: {
|
||||
tenantId,
|
||||
tenantName, // 🆕
|
||||
userId,
|
||||
userName,
|
||||
module,
|
||||
feature,
|
||||
action,
|
||||
info: typeof info === 'object' ? JSON.stringify(info) : String(info || ''),
|
||||
}
|
||||
}).catch(e => {
|
||||
logger.warn('埋点写入失败(可忽略)', { error: e.message });
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取今日核心大盘数据
|
||||
*/
|
||||
async getTodayOverview() {
|
||||
const todayStart = new Date();
|
||||
todayStart.setHours(0, 0, 0, 0);
|
||||
|
||||
const stats = await prisma.$queryRaw`
|
||||
SELECT
|
||||
COUNT(DISTINCT user_id) as dau,
|
||||
COUNT(DISTINCT tenant_id) as dat,
|
||||
COUNT(CASE WHEN action = 'EXPORT' THEN 1 END) as export_count
|
||||
FROM admin_schema.simple_logs
|
||||
WHERE created_at >= ${todayStart}
|
||||
` as any[];
|
||||
|
||||
// 模块使用统计
|
||||
const moduleStats = await prisma.$queryRaw`
|
||||
SELECT module, COUNT(*) as count
|
||||
FROM admin_schema.simple_logs
|
||||
WHERE created_at >= ${todayStart}
|
||||
GROUP BY module
|
||||
` as any[];
|
||||
|
||||
const moduleMap: Record<string, number> = {};
|
||||
moduleStats.forEach((m: any) => {
|
||||
moduleMap[m.module] = Number(m.count);
|
||||
});
|
||||
|
||||
return {
|
||||
dau: Number(stats[0]?.dau || 0),
|
||||
dat: Number(stats[0]?.dat || 0),
|
||||
exportCount: Number(stats[0]?.export_count || 0),
|
||||
moduleStats: moduleMap,
|
||||
};
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取实时流水账
|
||||
*/
|
||||
async getLiveFeed(limit = 100) {
|
||||
return prisma.simpleLog.findMany({
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: limit,
|
||||
select: {
|
||||
id: true,
|
||||
createdAt: true,
|
||||
tenantName: true,
|
||||
userName: true,
|
||||
module: true,
|
||||
feature: true,
|
||||
action: true,
|
||||
info: true,
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
/**
|
||||
* 获取用户360画像
|
||||
*/
|
||||
async getUserOverview(userId: string) {
|
||||
const [user, aiaStats, kbs, dcStats, rvwStats, logs] = await Promise.all([
|
||||
// 基础信息
|
||||
prisma.user.findUnique({
|
||||
where: { id: userId },
|
||||
include: { tenants: true }
|
||||
}),
|
||||
|
||||
// AIA 资产 (会话数)
|
||||
prisma.conversation.count({
|
||||
where: { userId, deletedAt: null }
|
||||
}),
|
||||
|
||||
// PKB 资产 (知识库数 + 文档数)
|
||||
prisma.knowledgeBase.findMany({
|
||||
where: { userId, deletedAt: null },
|
||||
include: { _count: { select: { documents: true } } }
|
||||
}),
|
||||
|
||||
// DC 资产 (任务数)
|
||||
prisma.extractionTask.count({ where: { userId } }),
|
||||
|
||||
// RVW 资产 (审稿任务数) 🆕
|
||||
prisma.reviewTask.groupBy({
|
||||
by: ['status'],
|
||||
where: { userId },
|
||||
_count: true,
|
||||
}),
|
||||
|
||||
// 最近行为 (从 SimpleLog 查最近 20 条)
|
||||
prisma.simpleLog.findMany({
|
||||
where: { userId },
|
||||
orderBy: { createdAt: 'desc' },
|
||||
take: 20,
|
||||
select: {
|
||||
createdAt: true,
|
||||
module: true,
|
||||
feature: true,
|
||||
action: true,
|
||||
info: true,
|
||||
}
|
||||
})
|
||||
]);
|
||||
|
||||
const totalDocs = kbs.reduce((sum, kb) => sum + kb._count.documents, 0);
|
||||
|
||||
// 计算 RVW 统计
|
||||
const rvwTotal = rvwStats.reduce((sum, s) => sum + s._count, 0);
|
||||
const rvwCompleted = rvwStats.find(s => s.status === 'completed')?._count || 0;
|
||||
|
||||
return {
|
||||
profile: user ? {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
phone: user.phone.replace(/(\d{3})\d{4}(\d{4})/, '$1****$2'),
|
||||
tenantName: user.tenants?.name,
|
||||
} : null,
|
||||
assets: {
|
||||
aia: { conversationCount: aiaStats },
|
||||
pkb: { kbCount: kbs.length, docCount: totalDocs },
|
||||
dc: { taskCount: dcStats },
|
||||
rvw: { reviewTaskCount: rvwTotal, completedCount: rvwCompleted }, // 🆕
|
||||
},
|
||||
activities: logs,
|
||||
};
|
||||
}
|
||||
};
|
||||
```
|
||||
|
||||
### 5.2 StatsController(统计控制器)
|
||||
|
||||
**文件路径**:`backend/src/modules/admin/controllers/statsController.ts`
|
||||
|
||||
```typescript
|
||||
import type { FastifyRequest, FastifyReply } from 'fastify';
|
||||
import { activityService } from '../../../common/services/activity.service.js';
|
||||
|
||||
/**
|
||||
* 获取今日大盘
|
||||
* GET /api/admin/stats/overview
|
||||
*/
|
||||
export async function getOverview(request: FastifyRequest, reply: FastifyReply) {
|
||||
const data = await activityService.getTodayOverview();
|
||||
return reply.send({ success: true, data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取实时流水账
|
||||
* GET /api/admin/stats/live-feed
|
||||
*/
|
||||
export async function getLiveFeed(
|
||||
request: FastifyRequest<{ Querystring: { limit?: number } }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
const limit = request.query.limit || 100;
|
||||
const data = await activityService.getLiveFeed(limit);
|
||||
return reply.send({ success: true, data });
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取用户360画像
|
||||
* GET /api/admin/users/:id/overview
|
||||
*/
|
||||
export async function getUserOverview(
|
||||
request: FastifyRequest<{ Params: { id: string } }>,
|
||||
reply: FastifyReply
|
||||
) {
|
||||
const { id } = request.params;
|
||||
const data = await activityService.getUserOverview(id);
|
||||
return reply.send({ success: true, data });
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 6. 前端页面设计
|
||||
|
||||
### 6.1 Admin 首页改造
|
||||
|
||||
**位置**:`frontend-v2/src/pages/admin/AdminDashboard.tsx`
|
||||
|
||||
#### 顶部卡片区域
|
||||
|
||||
```
|
||||
┌─────────────────┬─────────────────┬─────────────────┐
|
||||
│ 今日活跃医生 │ 今日活跃医院 │ 今日价值交付 │
|
||||
│ 12 👨⚕️ │ 3 🏥 │ 5 🟢 │
|
||||
│ (DAU) │ (DAT) │ (导出次数) │
|
||||
└─────────────────┴─────────────────┴─────────────────┘
|
||||
```
|
||||
|
||||
#### 实时流水账区域
|
||||
|
||||
```
|
||||
┌──────┬──────┬──────┬──────┬────────────┬──────┬────────────┐
|
||||
│ 时间 │ 医院 │ 医生 │ 模块 │ 具体功能 │ 动作 │ 详情 │
|
||||
├──────┼──────┼──────┼──────┼────────────┼──────┼────────────┤
|
||||
│10:05 │ 协和 │张主任│ AIA │ 选题评价 │🔵USE │评分: 85分 │
|
||||
│10:03 │ 协和 │张主任│ RVW │ 稿约规范 │🔵USE │审查开始 │
|
||||
│09:55 │ 华西 │李医生│ DC │ Tool C │🟢EXP │导出 Excel │
|
||||
└──────┴──────┴──────┴──────┴────────────┴──────┴────────────┘
|
||||
```
|
||||
|
||||
### 6.2 用户详情页增强
|
||||
|
||||
**位置**:`frontend-v2/src/modules/admin/pages/UserDetailPage.tsx`
|
||||
|
||||
#### 资产统计区域
|
||||
|
||||
```
|
||||
┌─────────────┬─────────────┬─────────────┬─────────────┬─────────────┐
|
||||
│ 💬 AIA对话 │ 📚 PKB知识库 │ 📄 上传文献 │ 🧹 DC清洗 │ 📝 RVW审稿 │
|
||||
│ 158 次 │ 3 个 │ 450 篇 │ 12 次 │ 25 篇 │
|
||||
└─────────────┴─────────────┴─────────────┴─────────────┴─────────────┘
|
||||
```
|
||||
|
||||
#### 行为时间轴
|
||||
|
||||
```
|
||||
• 10:30 [AIA] 使用了 "选题评价" (生成结果: 85分)
|
||||
• 10:15 [RVW] 完成了 "稿约规范性审查" (评分: 82分) 🆕
|
||||
• 09:50 [DC] 导出了 Tool C 清洗结果 (Excel)
|
||||
• 09:48 [SYSTEM] 登录系统
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 7. 权限控制
|
||||
|
||||
### 7.1 角色权限矩阵
|
||||
|
||||
| 功能 | SUPER_ADMIN | PROMPT_ENGINEER | HOSPITAL_ADMIN |
|
||||
|------|-------------|-----------------|----------------|
|
||||
| 查看全局大盘 | ✅ | ✅(只读) | ❌ |
|
||||
| 查看实时流水 | ✅ | ✅(只读) | ❌ |
|
||||
| 查看用户画像 | ✅ | ❌ | 本租户用户 |
|
||||
| 数据导出 | ✅ | ❌ | ❌ |
|
||||
|
||||
### 7.2 数据隔离规则
|
||||
|
||||
- **SUPER_ADMIN**:可查看全部租户数据
|
||||
- **HOSPITAL_ADMIN**:只能查看本租户的用户活动
|
||||
- **普通用户**:无运营数据访问权限
|
||||
|
||||
---
|
||||
|
||||
## 8. 数据保留策略
|
||||
|
||||
### 8.1 清理规则
|
||||
|
||||
- 保留期限:**180 天**
|
||||
- 清理方式:pg-boss 定时任务,每日 03:00 执行
|
||||
- 清理脚本:
|
||||
|
||||
```sql
|
||||
-- 清理180天前的日志
|
||||
DELETE FROM admin_schema.simple_logs
|
||||
WHERE created_at < NOW() - INTERVAL '180 days';
|
||||
```
|
||||
|
||||
### 8.2 定时任务配置
|
||||
|
||||
**文件路径**:`backend/src/common/jobs/cleanupWorker.ts`
|
||||
|
||||
```typescript
|
||||
import { jobQueue } from './jobQueue.js';
|
||||
import { prisma } from '../../config/database.js';
|
||||
import { logger } from '../logging/index.js';
|
||||
|
||||
// 注册清理任务
|
||||
export async function registerCleanupJobs() {
|
||||
await jobQueue.schedule('cleanup-simple-logs', '0 3 * * *', async () => {
|
||||
const result = await prisma.$executeRaw`
|
||||
DELETE FROM admin_schema.simple_logs
|
||||
WHERE created_at < NOW() - INTERVAL '180 days'
|
||||
`;
|
||||
logger.info('运营日志清理完成', { deletedCount: result });
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 9. 开发任务清单
|
||||
|
||||
### Phase 1: 数据库(15分钟)
|
||||
|
||||
- [ ] 更新 `prisma/schema.prisma`,添加 SimpleLog 模型
|
||||
- [ ] 执行 `npx prisma db push` 同步数据库
|
||||
- [ ] 验证表结构和索引
|
||||
|
||||
### Phase 2: 后端服务(60分钟)
|
||||
|
||||
- [ ] 创建 `common/services/activity.service.ts`
|
||||
- [ ] 创建 `modules/admin/controllers/statsController.ts`
|
||||
- [ ] 创建 `modules/admin/routes/statsRoutes.ts`
|
||||
- [ ] 在 `index.ts` 注册路由
|
||||
|
||||
### Phase 3: 埋点集成(90分钟)
|
||||
|
||||
#### 系统级
|
||||
- [ ] `auth.controller.ts` - 登录埋点
|
||||
|
||||
#### AIA 模块
|
||||
- [ ] `conversationService.ts` - 12个智能体埋点
|
||||
- [ ] `ProtocolOrchestrator.ts` - Protocol Agent 埋点
|
||||
|
||||
#### PKB 模块
|
||||
- [ ] `knowledgeBaseController.ts` - 知识库创建埋点
|
||||
- [ ] `documentController.ts` - 文档上传埋点
|
||||
- [ ] `ragController.ts` - RAG问答埋点
|
||||
|
||||
#### DC 模块
|
||||
- [ ] `toolBController.ts` - Tool B 埋点
|
||||
- [ ] `toolCController.ts` - Tool C 埋点
|
||||
|
||||
#### RVW 模块 🆕
|
||||
- [ ] `reviewController.ts` - 上传埋点
|
||||
- [ ] `reviewWorker.ts` - 审查完成埋点
|
||||
|
||||
#### IIT 模块 🆕
|
||||
- [ ] `chatService.ts` - 对话查询埋点
|
||||
- [ ] `redcapAdapter.ts` - 同步埋点
|
||||
|
||||
### Phase 4: 前端页面(90分钟)
|
||||
|
||||
- [ ] 改造 `AdminDashboard.tsx` - 添加大盘卡片和流水账
|
||||
- [ ] 改造 `UserDetailPage.tsx` - 添加资产统计和时间轴
|
||||
- [ ] 创建 `StatsCard.tsx` 组件
|
||||
- [ ] 创建 `LiveFeed.tsx` 组件
|
||||
- [ ] 创建 `ActivityTimeline.tsx` 组件
|
||||
|
||||
### Phase 5: 数据清理(15分钟)
|
||||
|
||||
- [ ] 创建 `cleanupWorker.ts`
|
||||
- [ ] 注册定时任务
|
||||
- [ ] 测试清理逻辑
|
||||
|
||||
---
|
||||
|
||||
## 10. 测试验证
|
||||
|
||||
### 10.1 单元测试
|
||||
|
||||
```bash
|
||||
# 埋点服务测试
|
||||
npm test -- --grep "ActivityService"
|
||||
|
||||
# API 测试
|
||||
npm test -- --grep "Stats API"
|
||||
```
|
||||
|
||||
### 10.2 端到端验证
|
||||
|
||||
1. 登录系统,检查是否记录 LOGIN
|
||||
2. 使用 AIA 智能体,检查是否记录 USE
|
||||
3. 导出文件,检查是否记录 EXPORT
|
||||
4. 访问 Admin 首页,验证大盘数据
|
||||
5. 访问用户详情,验证 360 画像
|
||||
|
||||
---
|
||||
|
||||
## 📊 总结
|
||||
|
||||
| 项目 | V3.0 原方案 | V3.1 修订版 |
|
||||
|------|------------|------------|
|
||||
| 模块覆盖 | 4 个 | **8 个** |
|
||||
| 字段设计 | 缺少 tenantName | ✅ 完整 |
|
||||
| API 设计 | 缺失 | ✅ 4 个端点 |
|
||||
| 数据保留 | 缺失 | ✅ 180 天 |
|
||||
| 权限控制 | 缺失 | ✅ 角色矩阵 |
|
||||
| 预计工时 | 3-4 小时 | **4-5 小时** |
|
||||
|
||||
---
|
||||
|
||||
**下一步**:按照任务清单执行开发,优先完成 Phase 1-2(基础设施),再逐步完成埋点集成和前端页面。
|
||||
|
||||
333
docs/03-业务模块/ADMIN-运营管理端/04-开发计划/04-运营监控系统MVP实施记录.md
Normal file
333
docs/03-业务模块/ADMIN-运营管理端/04-开发计划/04-运营监控系统MVP实施记录.md
Normal file
@@ -0,0 +1,333 @@
|
||||
# 运营监控系统 MVP 实施记录
|
||||
|
||||
> **文档版本**:V1.0
|
||||
> **实施日期**:2026-01-25
|
||||
> **基于文档**:03-运营监控系统MVP开发计划.md
|
||||
> **实施状态**:✅ **MVP 完成!**
|
||||
|
||||
---
|
||||
|
||||
## 📋 实施概要
|
||||
|
||||
### 完成状态
|
||||
|
||||
| 任务类型 | 计划 | 完成 | 完成率 |
|
||||
|---------|------|------|--------|
|
||||
| 数据库设计 | 1 | 1 | ✅ 100% |
|
||||
| 后端服务 | 3 | 3 | ✅ 100% |
|
||||
| 埋点集成 | 7模块 | 7模块 | ✅ 100% |
|
||||
| 前端看板 | 1 | 1 | ✅ 100% |
|
||||
| API测试 | 4端点 | 4端点 | ✅ 100% |
|
||||
|
||||
**总耗时**:约 6 小时(含调试和问题修复)
|
||||
|
||||
---
|
||||
|
||||
## 1. 数据库实施 ✅
|
||||
|
||||
### 1.1 SimpleLog 表创建
|
||||
|
||||
**Prisma Schema 位置**:`backend/prisma/schema.prisma`
|
||||
|
||||
```prisma
|
||||
/// 极简运营日志表 (MVP) - V3.1 修订版
|
||||
model SimpleLog {
|
||||
id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
|
||||
createdAt DateTime @default(now()) @map("created_at")
|
||||
|
||||
tenantId String @map("tenant_id") @db.VarChar(50)
|
||||
tenantName String? @map("tenant_name") @db.VarChar(100)
|
||||
userId String @map("user_id") @db.Uuid
|
||||
userName String? @map("user_name") @db.VarChar(50)
|
||||
|
||||
module String @db.VarChar(20)
|
||||
feature String @db.VarChar(50)
|
||||
action String @db.VarChar(20)
|
||||
|
||||
info String? @db.Text
|
||||
|
||||
@@index([createdAt])
|
||||
@@index([tenantId])
|
||||
@@index([userId])
|
||||
@@index([module, feature])
|
||||
@@index([action])
|
||||
@@map("simple_logs")
|
||||
@@schema("admin_schema")
|
||||
}
|
||||
```
|
||||
|
||||
**迁移命令**:
|
||||
|
||||
```bash
|
||||
# 由于存在跨 schema 外键约束问题,使用 db push 代替 migrate
|
||||
npx prisma db push
|
||||
```
|
||||
|
||||
**数据库备份**:
|
||||
- 备份文件:`ai_clinical_research_backup_20260125.sql`
|
||||
- 备份时间:2026-01-25 实施前
|
||||
- 备份命令:`pg_dump -h localhost -U postgres -F p ai_clinical_research > backup.sql`
|
||||
|
||||
---
|
||||
|
||||
## 2. 后端服务实施 ✅
|
||||
|
||||
### 2.1 ActivityService(埋点服务)
|
||||
|
||||
**文件位置**:`backend/src/common/services/activity.service.ts`
|
||||
|
||||
**核心特性**:
|
||||
- ✅ 火烧即忘模式(Fire-and-Forget)
|
||||
- ✅ 外层 try-catch 保护(永不抛出异常)
|
||||
- ✅ 自动填充 tenantName 和 userName
|
||||
- ✅ 静默失败,不影响业务逻辑
|
||||
|
||||
**使用示例**:
|
||||
|
||||
```typescript
|
||||
import { activityService } from '@/common/services/activity.service';
|
||||
|
||||
// 在业务逻辑中添加埋点(不阻塞主流程)
|
||||
await activityService.log({
|
||||
userId: user.id,
|
||||
tenantId: user.tenantId,
|
||||
module: 'AIA',
|
||||
feature: '智能体对话',
|
||||
action: 'MESSAGE_SENT',
|
||||
info: `对话完成,tokens: ${tokens}`,
|
||||
});
|
||||
```
|
||||
|
||||
### 2.2 StatsController(统计控制器)
|
||||
|
||||
**文件位置**:`backend/src/modules/admin/controllers/statsController.ts`
|
||||
|
||||
**API 端点**:
|
||||
|
||||
| 端点 | 方法 | 说明 |
|
||||
|------|------|------|
|
||||
| `/api/admin/stats/overview` | GET | 获取 DAU/DAT/模块统计 |
|
||||
| `/api/admin/stats/live-feed` | GET | 获取最近活动流 |
|
||||
| `/api/admin/users/:id/overview` | GET | 获取用户360画像 |
|
||||
| `/api/admin/stats/cleanup` | POST | 清理过期日志(180天) |
|
||||
|
||||
### 2.3 StatsRoutes(路由配置)
|
||||
|
||||
**文件位置**:`backend/src/modules/admin/routes/statsRoutes.ts`
|
||||
|
||||
**权限控制**:
|
||||
- 所有端点需要 `stats:view` 权限
|
||||
- 使用 `authenticate` + `requirePermission` 中间件
|
||||
|
||||
---
|
||||
|
||||
## 3. 埋点集成实施 ✅
|
||||
|
||||
### 3.1 各模块埋点清单
|
||||
|
||||
| 模块 | 埋点位置 | action 类型 | 状态 |
|
||||
|------|---------|------------|------|
|
||||
| **SYSTEM** | auth.controller.ts | LOGIN | ✅ |
|
||||
| **AIA** | conversationService.ts | MESSAGE_SENT | ✅ |
|
||||
| **PKB** | knowledgeBaseService.ts | KB_CREATED, KB_DELETED | ✅ |
|
||||
| **PKB** | ragService.ts | RAG_SEARCH | ✅ |
|
||||
| **ASL** | screeningWorker.ts | SCREENING_COMPLETED | ✅ |
|
||||
| **DC** | extractionWorker.ts (Tool B) | EXTRACTION_COMPLETED | ✅ |
|
||||
| **DC** | AICodeService.ts (Tool C) | CODE_GENERATE, CODE_EXECUTE | ✅ |
|
||||
| **RVW** | reviewWorker.ts | REVIEW_COMPLETED | ✅ |
|
||||
| **IIT** | SyncManager.ts | POLL_STARTED, SYNC_COMPLETED | ✅ |
|
||||
|
||||
### 3.2 埋点代码示例
|
||||
|
||||
**登录埋点**(auth.controller.ts):
|
||||
|
||||
```typescript
|
||||
// 登录成功后记录
|
||||
await activityService.log({
|
||||
userId: result.user.id,
|
||||
tenantId: result.user.tenantId,
|
||||
module: 'SYSTEM',
|
||||
feature: '用户登录',
|
||||
action: 'LOGIN',
|
||||
info: `用户 ${result.user.name} (${result.user.phone}) 登录成功`,
|
||||
});
|
||||
```
|
||||
|
||||
**AIA 对话埋点**(conversationService.ts):
|
||||
|
||||
```typescript
|
||||
// 在流式对话完成后记录
|
||||
await activityService.log({
|
||||
userId,
|
||||
tenantId: user.tenantId,
|
||||
module: 'AIA',
|
||||
feature: `智能体对话: ${conversation.agentId}`,
|
||||
action: 'MESSAGE_SENT',
|
||||
info: `对话 ${conversationId} 消息发送完成,tokens: ${aiMessage.tokens}`,
|
||||
});
|
||||
```
|
||||
|
||||
**PKB 知识库埋点**(knowledgeBaseService.ts):
|
||||
|
||||
```typescript
|
||||
// 创建知识库
|
||||
await activityService.log({
|
||||
userId,
|
||||
tenantId: user.tenantId,
|
||||
module: 'PKB',
|
||||
feature: '知识库管理',
|
||||
action: 'KB_CREATED',
|
||||
info: `创建知识库: ${name} (ID: ${knowledgeBase.id})`,
|
||||
});
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 4. 前端看板实施 ✅
|
||||
|
||||
### 4.1 Admin Dashboard 更新
|
||||
|
||||
**文件位置**:`frontend-v2/src/pages/admin/AdminDashboard.tsx`
|
||||
|
||||
**功能特性**:
|
||||
- ✅ DAU/DAT 实时统计卡片
|
||||
- ✅ 模块使用分布图表
|
||||
- ✅ 最近活动实时流
|
||||
- ✅ 自动刷新(基于 React Query)
|
||||
|
||||
### 4.2 API 调用层
|
||||
|
||||
**文件位置**:`frontend-v2/src/modules/admin/api/statsApi.ts`
|
||||
|
||||
**API 函数**:
|
||||
|
||||
```typescript
|
||||
// 获取运营概览
|
||||
export const getOverview = async (): Promise<StatsOverview> => {
|
||||
const response = await apiClient.get('/api/admin/stats/overview');
|
||||
return response.data.data;
|
||||
};
|
||||
|
||||
// 获取实时活动流
|
||||
export const getLiveFeed = async (limit?: number): Promise<LiveFeedItem[]> => {
|
||||
const response = await apiClient.get('/api/admin/stats/live-feed', {
|
||||
params: { limit },
|
||||
});
|
||||
return response.data.data;
|
||||
};
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. 测试验证 ✅
|
||||
|
||||
### 5.1 API 测试
|
||||
|
||||
**测试脚本位置**:`backend/src/modules/admin/__tests__/test-stats-api.ps1`
|
||||
|
||||
**测试结果**:
|
||||
|
||||
| 测试项 | 结果 |
|
||||
|--------|------|
|
||||
| 登录获取 Token | ✅ 通过 |
|
||||
| GET /overview | ✅ 返回 DAU/DAT |
|
||||
| GET /live-feed | ✅ 返回活动列表 |
|
||||
| 用户 360 画像 | ✅ 返回资产统计 |
|
||||
|
||||
### 5.2 前端测试
|
||||
|
||||
- ✅ 运营管理端 Dashboard 正常显示
|
||||
- ✅ 实时数据刷新正常
|
||||
- ✅ 权限控制正常(仅 SUPER_ADMIN 可访问)
|
||||
|
||||
---
|
||||
|
||||
## 6. 问题与解决
|
||||
|
||||
### 6.1 Prisma 迁移失败
|
||||
|
||||
**问题**:`prisma migrate dev` 报错 P3006(shadow database 问题)
|
||||
|
||||
**原因**:存在跨 schema 外键约束
|
||||
|
||||
**解决**:使用 `prisma db push` 直接推送 schema 变更(仅添加新表,安全)
|
||||
|
||||
### 6.2 TypeScript 编译错误
|
||||
|
||||
**问题**:添加埋点后多处 TypeScript 错误
|
||||
|
||||
**修复内容**:
|
||||
- 修正 Prisma 模型名称(如 `prisma.reviewTask` 代替 `prisma.review_tasks`)
|
||||
- 添加类型注解(`reduce` 函数参数)
|
||||
- 添加 `.js` 扩展名到 ESM 导入
|
||||
|
||||
### 6.3 DC Tool C 上传 401
|
||||
|
||||
**问题**:文件上传返回 401 Unauthorized
|
||||
|
||||
**原因**:后端服务未重启,新代码未生效
|
||||
|
||||
**解决**:重启后端服务后正常
|
||||
|
||||
---
|
||||
|
||||
## 7. 文件变更清单
|
||||
|
||||
### 新增文件
|
||||
|
||||
| 文件 | 说明 |
|
||||
|------|------|
|
||||
| `backend/src/common/services/activity.service.ts` | 埋点服务 |
|
||||
| `backend/src/modules/admin/controllers/statsController.ts` | 统计控制器 |
|
||||
| `backend/src/modules/admin/routes/statsRoutes.ts` | 统计路由 |
|
||||
| `frontend-v2/src/modules/admin/api/statsApi.ts` | 前端 API 层 |
|
||||
|
||||
### 修改文件
|
||||
|
||||
| 文件 | 修改内容 |
|
||||
|------|---------|
|
||||
| `backend/prisma/schema.prisma` | 添加 SimpleLog 模型 |
|
||||
| `backend/src/index.ts` | 注册 statsRoutes |
|
||||
| `backend/src/common/auth/auth.controller.ts` | 登录埋点 |
|
||||
| `backend/src/modules/aia/services/conversationService.ts` | AIA 对话埋点 |
|
||||
| `backend/src/modules/pkb/services/knowledgeBaseService.ts` | PKB 知识库埋点 |
|
||||
| `backend/src/modules/pkb/services/ragService.ts` | PKB RAG 埋点 |
|
||||
| `backend/src/modules/asl/services/screeningWorker.ts` | ASL 筛选埋点 |
|
||||
| `backend/src/modules/dc/tool-b/workers/extractionWorker.ts` | DC Tool B 埋点 |
|
||||
| `backend/src/modules/dc/tool-c/services/AICodeService.ts` | DC Tool C 埋点 |
|
||||
| `backend/src/modules/rvw/workers/reviewWorker.ts` | RVW 审查埋点 |
|
||||
| `backend/src/modules/iit-manager/services/SyncManager.ts` | IIT 同步埋点 |
|
||||
| `frontend-v2/src/pages/admin/AdminDashboard.tsx` | 运营看板 UI |
|
||||
|
||||
---
|
||||
|
||||
## 8. 后续优化建议
|
||||
|
||||
### P2 优先级
|
||||
|
||||
- [ ] 添加更多埋点:Protocol Agent 一键生成、Word 导出
|
||||
- [ ] 图表可视化:使用 ECharts 展示趋势图
|
||||
- [ ] 定时任务:每日 00:00 自动清理 180 天前日志
|
||||
|
||||
### P3 优先级
|
||||
|
||||
- [ ] 用户行为路径分析
|
||||
- [ ] 漏斗分析功能
|
||||
- [ ] 导出统计报表
|
||||
|
||||
---
|
||||
|
||||
## 9. 总结
|
||||
|
||||
✅ 运营监控系统 MVP 已完成核心功能:
|
||||
- **数据采集**:7 个模块埋点全部完成
|
||||
- **数据存储**:SimpleLog 表结构稳定
|
||||
- **数据展示**:Admin Dashboard 实时展示
|
||||
- **API 接口**:4 个核心端点全部可用
|
||||
|
||||
MVP 阶段目标达成,可支持基本的运营数据分析需求。
|
||||
|
||||
---
|
||||
|
||||
*文档完成时间:2026-01-25*
|
||||
|
||||
@@ -1360,25 +1360,13 @@ Body (raw JSON):
|
||||
**测试命令**:
|
||||
```bash
|
||||
curl http://localhost:3001/api/v1/asl/projects/55941145-bba0-4b15-bda4-f0a398d78208/statistics
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
**文档版本:** v3.0
|
||||
```---**文档版本:** v3.0
|
||||
**最后更新:** 2025-11-23(Day 5: 全文复筛API)
|
||||
**维护者:** AI智能文献开发团队
|
||||
|
||||
**本次更新**:
|
||||
**维护者:** AI智能文献开发团队**本次更新**:
|
||||
- ✅ 新增全文复筛管理API(5个核心接口)
|
||||
- ✅ 详细的12字段评估文档
|
||||
- ✅ 双模型对比和冲突检测说明
|
||||
- ✅ Excel导出格式规范
|
||||
- ✅ 完整的请求/响应示例
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [数据库设计文档](./01-数据库设计.md)
|
||||
- ✅ 完整的请求/响应示例---## 📚 相关文档- [数据库设计文档](./01-数据库设计.md)
|
||||
- [API测试报告](../../../backend/ASL-API-测试报告.md)
|
||||
- [Week 1完成报告](../05-开发记录/2025-11-18-Week1完成报告.md)
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
|
||||
1. **稿约规范性评估**(11项标准)
|
||||
- 文题字数、摘要结构、参考文献、图片DPI等
|
||||
- 基于《中华脑血管病杂志》稿约标准
|
||||
- 基于《中华医学超声杂志》稿约标准
|
||||
|
||||
2. **方法学评估**(3部分,20个检查点)
|
||||
- 科研设计评估(9个检查点)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# 🎯 2026年1月26日 部署状态真实记录
|
||||
# 🎯 2026年1月26-27日 部署状态真实记录
|
||||
|
||||
> **文档类型**:部署状态实时记录
|
||||
> **更新时间**:2026-01-26 16:30
|
||||
> **更新时间**:2026-01-27 15:30(数据库中文编码问题修复)
|
||||
> **部署人员**:开发团队
|
||||
> **文档目的**:记录本次部署的真实状态,供后续参考
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
| 3 | Node.js后端更新 | ✅ 已完成 | 2026-01-27 07:55 | v1.3 → v1.7,修复ES Module导入 |
|
||||
| 4 | 前端Nginx更新 | ✅ 已完成 | 2026-01-27 08:00 | v1.2 → v1.3,更新后端IP |
|
||||
| 5 | IIT回调地址配置 | ⏳ 待配置 | - | 外部平台配置 |
|
||||
| 6 | **数据库中文编码修复** | ✅ 已完成 | 2026-01-27 15:30 | PowerShell编码问题,重新导入数据 |
|
||||
|
||||
---
|
||||
|
||||
@@ -373,6 +374,17 @@ sha256:42bff8a3d65c0b11eafc09aae5aed9ef0ff6f550d60a3426c7f5d15c7433a973
|
||||
| 07:50 | 开始构建前端v1.3,修复Dockerfile跳过TypeScript检查 |
|
||||
| 08:00 | 前端v1.3部署成功,新IP(172.17.173.90) |
|
||||
| 08:05 | 更新前端环境变量,指向新后端IP |
|
||||
| 14:00 | 🔴 **发现数据库中文乱码问题**:用户名、租户名显示为 `????` |
|
||||
| 14:10 | 定位问题原因:PowerShell 编码破坏了 UTF-8 数据 |
|
||||
| 14:20 | 在 Docker 容器内重新导出本地数据库(正确的 UTF-8) |
|
||||
| 14:25 | 验证导出文件中文正确(王医生、示范医院等) |
|
||||
| 14:30 | 开启 RDS 外网访问,准备重新导入 |
|
||||
| 14:40 | 强制删除 RDS 测试数据库(`DROP DATABASE ... WITH (FORCE)`) |
|
||||
| 14:45 | 重新创建数据库,安装 pg_bigm、pgvector 插件 |
|
||||
| 14:50 | 在容器内直接导入数据(`psql -f /tmp/backup_utf8.sql`) |
|
||||
| 15:00 | ✅ 验证中文数据正确显示 |
|
||||
| 15:10 | 关闭 RDS 外网访问 |
|
||||
| 15:30 | 更新部署文档,记录问题和解决方案 |
|
||||
|
||||
---
|
||||
|
||||
@@ -405,20 +417,77 @@ sha256:42bff8a3d65c0b11eafc09aae5aed9ef0ff6f550d60a3426c7f5d15c7433a973
|
||||
5. ✅ **前后端TypeScript错误** - 跳过类型检查,部署优先
|
||||
6. ✅ **OSS环境配置** - 开发/生产Bucket分离
|
||||
7. ✅ **大量空文件恢复** - 从本地备份恢复
|
||||
8. ✅ **数据库中文乱码(????)** - PowerShell编码问题,在容器内直接导入修复
|
||||
|
||||
### 🎯 待完成任务
|
||||
|
||||
| 任务 | 优先级 | 说明 |
|
||||
|------|--------|------|
|
||||
| IIT回调地址配置 | 高 | 企业微信、微信服务号回调URL更新 |
|
||||
| TypeScript类型修复 | 中 | 改善代码质量,不影响运行 |
|
||||
| 功能全面测试 | 高 | 验证所有模块功能 |
|
||||
| 任务 | 优先级 | 状态 | 说明 |
|
||||
|------|--------|------|------|
|
||||
| 数据库中文编码修复 | 🔴 高 | ✅ **已完成** | PowerShell编码问题,已重新导入 |
|
||||
| IIT回调地址配置 | 🔴 高 | ⏳ 待完成 | 企业微信、微信服务号回调URL更新 |
|
||||
| 功能全面测试 | 🔴 高 | ⏳ 待完成 | 验证所有模块功能 |
|
||||
| TypeScript类型修复 | 🟡 中 | ⏳ 待完成 | 改善代码质量,不影响运行 |
|
||||
|
||||
---
|
||||
|
||||
> **🎉 部署状态**:核心服务全部完成!
|
||||
> **⏱️ 总耗时**:约17小时(跨2天)
|
||||
> **🎉 部署状态**:核心服务全部完成 + 中文编码问题已修复!
|
||||
> **⏱️ 总耗时**:约17小时(跨2天)+ 1.5小时(编码问题修复)
|
||||
> **🔜 下一步**:IIT回调地址配置 + 全面功能测试
|
||||
> **📝 最后更新**:2026-01-27 08:05
|
||||
> **📝 最后更新**:2026-01-27 15:30
|
||||
|
||||
---
|
||||
|
||||
## 🔴 2026-01-27 紧急修复:数据库中文编码问题
|
||||
|
||||
### 问题现象
|
||||
|
||||
部署完成后,前端显示用户名称、租户名称、联系人等信息全部为 `????` 或 `??`。
|
||||
|
||||
### 根因分析
|
||||
|
||||
数据迁移脚本 `database-migration-script.ps1` 使用 PowerShell 执行数据库导入:
|
||||
1. PowerShell 的 `>` 重定向默认使用 **UTF-16 LE** 编码
|
||||
2. `Get-Content` 默认使用 **GBK/CP936** 编码
|
||||
3. 虽然 `pg_dump` 指定了 `--encoding=UTF8`,但 PowerShell 管道破坏了编码
|
||||
|
||||
### 修复方案
|
||||
|
||||
**在 Docker 容器内直接操作,完全绕过 PowerShell:**
|
||||
|
||||
```bash
|
||||
# ❌ 错误做法(PowerShell编码问题)
|
||||
docker exec ... pg_dump ... > backup.sql
|
||||
Get-Content backup.sql | docker exec ... psql ...
|
||||
|
||||
# ✅ 正确做法(容器内直接操作)
|
||||
docker exec ... pg_dump ... -f /tmp/backup_utf8.sql
|
||||
docker exec ... psql ... -f /tmp/backup_utf8.sql
|
||||
```
|
||||
|
||||
### 修复步骤
|
||||
|
||||
1. ✅ 在容器内重新导出数据库(`pg_dump -f /tmp/backup_utf8.sql`)
|
||||
2. ✅ 验证导出文件中文正确(`王医生`、`示范医院` 等)
|
||||
3. ✅ 开启 RDS 外网访问
|
||||
4. ✅ 强制删除 RDS 测试数据库(`DROP DATABASE ... WITH (FORCE)`)
|
||||
5. ✅ 重新创建数据库(`CREATE DATABASE ... ENCODING='UTF8'`)
|
||||
6. ✅ 安装插件(pg_bigm、pgvector)
|
||||
7. ✅ 在容器内直接导入(`psql -f /tmp/backup_utf8.sql`)
|
||||
8. ✅ 验证中文数据正确显示
|
||||
9. ✅ 关闭 RDS 外网访问
|
||||
|
||||
### 验证结果
|
||||
|
||||
```
|
||||
用户名:王医生、李医生、测试用户、超级管理员、Prompt工程师 ✅
|
||||
租户名:示范医院、示范药企、壹证循科技、北京积水潭医院、武田制药 ✅
|
||||
联系人:张主任、李经理、张院长 ✅
|
||||
```
|
||||
|
||||
### 经验总结
|
||||
|
||||
- ⚠️ **Windows PowerShell 不适合处理 UTF-8 数据**:重定向和管道会破坏编码
|
||||
- ✅ **推荐做法**:数据库迁移时,在 Docker 容器内直接操作
|
||||
- ✅ **验证方法**:导入后立即查询中文字段,确认显示正确
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
# 🎉 2026年1月26-27日部署完成总结
|
||||
|
||||
> **部署日期**:2026-01-26 15:00 ~ 2026-01-27 08:05
|
||||
> **总耗时**:约17小时(跨2天)
|
||||
> **部署状态**:✅ 核心服务全部完成
|
||||
> **问题修复**:2026-01-27(数据库中文编码问题)
|
||||
> **总耗时**:约17小时(跨2天)+ 1小时(编码问题修复)
|
||||
> **部署状态**:✅ 核心服务全部完成 + ✅ 中文编码问题已修复
|
||||
> **文档日期**:2026-01-27
|
||||
|
||||
---
|
||||
@@ -70,6 +71,7 @@
|
||||
| 4 | **ES Module导入错误** | 🔴 严重 | 添加.js扩展名 |
|
||||
| 5 | **TypeScript类型错误** | 🟡 中等 | 跳过类型检查,部署优先 |
|
||||
| 6 | **网络构建失败** | 🟡 中等 | 重试构建,使用阿里云镜像源 |
|
||||
| 7 | **数据库中文乱码(????)** | 🔴 严重 | PowerShell编码问题,容器内直接导入修复 |
|
||||
|
||||
---
|
||||
|
||||
@@ -116,6 +118,55 @@
|
||||
| ✅ 部署优先策略 | 跳过类型检查,确保快速部署 |
|
||||
| ✅ 文档详细记录 | 问题排查和知识沉淀 |
|
||||
|
||||
### 4. 数据库迁移编码问题 🔴
|
||||
|
||||
| 问题 | 教训 | 改进措施 |
|
||||
|------|------|---------|
|
||||
| **PowerShell编码破坏** | PowerShell的 `>` 重定向默认使用UTF-16 LE,`Get-Content` 默认使用GBK,导致UTF-8数据被破坏 | **✅ 解决方案:在Docker容器内直接执行导入,完全绕过PowerShell** |
|
||||
| **中文显示为????** | 用户名称、租户名称、联系人等中文字段全部显示为乱码 | 使用 `docker exec ... psql -f` 直接在容器内导入,避免通过PowerShell管道 |
|
||||
|
||||
**问题详情(2026-01-27):**
|
||||
|
||||
**现象**:部署完成后,前端显示用户名称、租户名称、联系人等信息全部为 `????` 或 `??`。
|
||||
|
||||
**根因分析**:
|
||||
1. 数据迁移脚本 `database-migration-script.ps1` 使用 PowerShell 执行 `pg_dump` 和 `psql`
|
||||
2. PowerShell 的 `>` 重定向操作符默认使用 **UTF-16 LE** 编码,不是 UTF-8
|
||||
3. `Get-Content` 命令默认使用系统编码(Windows 通常是 **GBK/CP936**)
|
||||
4. 虽然 `pg_dump` 指定了 `--encoding=UTF8`,但 PowerShell 的管道破坏了编码
|
||||
|
||||
**修复方案**:
|
||||
```bash
|
||||
# ❌ 错误做法(PowerShell编码问题)
|
||||
docker exec ... pg_dump ... > backup.sql
|
||||
Get-Content backup.sql | docker exec ... psql ...
|
||||
|
||||
# ✅ 正确做法(容器内直接操作)
|
||||
docker exec ... pg_dump ... -f /tmp/backup_utf8.sql
|
||||
docker exec ... psql ... -f /tmp/backup_utf8.sql
|
||||
```
|
||||
|
||||
**修复步骤(2026-01-27):**
|
||||
1. ✅ 在容器内重新导出数据库(`pg_dump -f /tmp/backup_utf8.sql`)
|
||||
2. ✅ 验证导出文件中文正确(`王医生`、`示范医院` 等)
|
||||
3. ✅ 强制删除 RDS 测试数据库(`DROP DATABASE ... WITH (FORCE)`)
|
||||
4. ✅ 重新创建数据库(`CREATE DATABASE ... ENCODING='UTF8'`)
|
||||
5. ✅ 安装插件(pg_bigm、pgvector)
|
||||
6. ✅ 在容器内直接导入(`psql -f /tmp/backup_utf8.sql`)
|
||||
7. ✅ 验证中文数据正确显示
|
||||
|
||||
**验证结果**:
|
||||
```
|
||||
用户名:王医生、李医生、测试用户、超级管理员、Prompt工程师 ✅
|
||||
租户名:示范医院、示范药企、壹证循科技、北京积水潭医院、武田制药 ✅
|
||||
联系人:张主任、李经理、张院长 ✅
|
||||
```
|
||||
|
||||
**经验总结**:
|
||||
- ⚠️ **Windows PowerShell 不适合处理 UTF-8 数据**:重定向和管道会破坏编码
|
||||
- ✅ **推荐做法**:数据库迁移时,在 Docker 容器内直接操作,完全绕过 PowerShell
|
||||
- ✅ **验证方法**:导入后立即查询中文字段,确认显示正确
|
||||
|
||||
---
|
||||
|
||||
## 📋 当前系统配置速查
|
||||
@@ -154,13 +205,14 @@ OSS_ACCESS_KEY_ID=LTAI5tBHkL39GjdLfcr77Y3f
|
||||
|
||||
## 🔜 待完成任务
|
||||
|
||||
| 任务 | 优先级 | 预计时间 |
|
||||
|------|--------|---------|
|
||||
| **IIT回调地址配置** | 🔴 高 | 30分钟 |
|
||||
| **功能全面测试** | 🔴 高 | 2小时 |
|
||||
| **TypeScript类型修复** | 🟡 中 | 3小时 |
|
||||
| **性能测试** | 🟢 低 | 1小时 |
|
||||
| **监控告警配置** | 🟢 低 | 1小时 |
|
||||
| 任务 | 优先级 | 预计时间 | 状态 |
|
||||
|------|--------|---------|------|
|
||||
| **IIT回调地址配置** | 🔴 高 | 30分钟 | ⏳ 待完成 |
|
||||
| **功能全面测试** | 🔴 高 | 2小时 | ⏳ 待完成 |
|
||||
| **数据库中文编码修复** | 🔴 高 | 已完成 | ✅ **2026-01-27 完成** |
|
||||
| **TypeScript类型修复** | 🟡 中 | 3小时 | ⏳ 待完成 |
|
||||
| **性能测试** | 🟢 低 | 1小时 | ⏳ 待完成 |
|
||||
| **监控告警配置** | 🟢 低 | 1小时 | ⏳ 待完成 |
|
||||
|
||||
---
|
||||
|
||||
@@ -183,6 +235,7 @@ OSS_ACCESS_KEY_ID=LTAI5tBHkL39GjdLfcr77Y3f
|
||||
2. ⚠️ **环境变量同步**:确保本地.env、SAE环境变量一致
|
||||
3. ⚠️ **数据库备份**:任何Schema变更前必须备份
|
||||
4. ⚠️ **版本号管理**:按语义化版本递增
|
||||
5. ⚠️ **数据库迁移编码**:**Windows PowerShell 会破坏 UTF-8 编码**,必须使用容器内直接操作(`docker exec ... psql -f`)
|
||||
|
||||
---
|
||||
|
||||
@@ -192,7 +245,23 @@ OSS_ACCESS_KEY_ID=LTAI5tBHkL39GjdLfcr77Y3f
|
||||
|
||||
---
|
||||
|
||||
> **文档版本**:v1.0
|
||||
> **最后更新**:2026-01-27 08:05
|
||||
---
|
||||
|
||||
## 📝 后续问题修复记录
|
||||
|
||||
### 2026-01-27:数据库中文编码问题修复 ✅
|
||||
|
||||
**问题**:用户名称、租户名称、联系人等中文字段显示为 `????`
|
||||
|
||||
**原因**:PowerShell 编码问题导致数据迁移时 UTF-8 数据被破坏
|
||||
|
||||
**修复**:在 Docker 容器内直接执行导入,绕过 PowerShell 编码问题
|
||||
|
||||
**详情**:见"经验教训 - 数据库迁移编码问题"章节
|
||||
|
||||
---
|
||||
|
||||
> **文档版本**:v1.1
|
||||
> **最后更新**:2026-01-27(添加中文编码问题修复记录)
|
||||
> **维护人员**:开发团队
|
||||
|
||||
|
||||
148
docs/06-测试文档/故障分析报告 (1).md
Normal file
148
docs/06-测试文档/故障分析报告 (1).md
Normal file
@@ -0,0 +1,148 @@
|
||||
# **PG Boss 任务重复故障分析与修复方案**
|
||||
|
||||
## **1\. 故障核心分析 (Root Cause Analysis) \- 修正版**
|
||||
|
||||
针对 "同一 TaskID 被创建 7 次" 且 "创建时间在同一毫秒" 的现象,在确认 **仅有 1 个 SAE 实例** 运行的情况下,我们排除了多实例并发的可能性。
|
||||
|
||||
结合已清理的 "rvw\_review\_task 有 7 个重复条目" 这一关键证据,我们得出了确切的结论:
|
||||
|
||||
### **核心根因:持久化配置重复 (Persisted Configuration Duplication)**
|
||||
|
||||
**问题不在于有多少个实例在跑,而在于数据库里存了多少份重复的指令。**
|
||||
|
||||
#### **💡 深度解析:为什么会有 7 个?(7 个闹钟的比喻)**
|
||||
|
||||
你的疑问是:*"每次处理应该是生成不同的任务ID,不可能是重复的对吗?"*
|
||||
|
||||
**答案:是的,pg-boss 生成了 7 个完全不同的 Job ID,但它们都在做同一件事。**
|
||||
|
||||
这就好比你为了早上 7 点起床,设置了 **7 个闹钟**:
|
||||
|
||||
1. **指令 (Schedules/Definitions)**:数据库里那些被清理的 "7 个重复条目",就像是 7 个闹钟配置。它们都设定在同一个触发条件下(比如 Cron 表达式,或系统启动时)。
|
||||
2. **触发 (Trigger)**:当时间到了,或者系统启动扫描时,这 **7 个闹钟同时响了**。
|
||||
3. **执行 (Jobs)**:系统听到第 1 个闹钟,创建了 Job A;听到第 2 个闹钟,创建了 Job B... 直到 Job G。
|
||||
* **结果**:你在一毫秒内,被叫醒了 7 次。
|
||||
* **数据**:这 7 个 Job 都有**不同的 UUID**(符合数据库约束),但它们的\*\*内容(Payload)\*\*全是 "处理 Task bd19c3d3"。
|
||||
|
||||
这就是为什么你在数据库里看到 created\_on 完全一致,但 Job ID 不同。因为那 1 个 SAE 实例在极短的时间内,忠实地执行了数据库里残留的 7 条指令。
|
||||
|
||||
* **机制解析**:pg-boss 是一个基于数据库的任务队列。它的调度(Schedules)和某些队列配置是**持久化**在 PostgreSQL 数据库中的(通常在 pgboss.schedule 表中)。
|
||||
* **故障复盘**:
|
||||
1. **积累阶段**:在过去的历史部署或重启中,代码可能在启动时调用了 boss.schedule('queue', 'cron')。由于没有加去重逻辑,每次部署都在数据库里**新增**了一条调度记录,而不是更新旧的。日积月累,数据库里就有了 7 条完全一样的调度记录。
|
||||
2. **爆发阶段**:当你当前的 **1 个 SAE 实例** 运行时,pg-boss 内部的轮询器扫描数据库,读取到了这 7 条重复的记录。
|
||||
3. **瞬间执行**:当触发条件满足,这单个实例在极短的 CPU 周期内,为这 7 条记录分别生成了一个 Job。
|
||||
* **证据链闭环**:
|
||||
* **7 次重复** 对应 **7 个重复的 Schedule/配置记录**。
|
||||
* **同一毫秒创建** 对应 **单实例在一次事件循环中连续处理了这 7 条指令**。
|
||||
|
||||
**结论**:你执行的 "清理了 32 个重复的队列定义" 操作,实际上就是**关掉了多余的 6 个闹钟**,这已经移除了问题的根源。
|
||||
|
||||
### **为什么 SingletonKey 之前没生效?**
|
||||
|
||||
虽然这是单实例产生的重复,但如果代码使用的是 insert 或者是没有严格 unique constraint 保护的 send,在极快的循环中(Event Loop),数据库可能仍未完成第一条的提交,第二条就来了。
|
||||
|
||||
但最可能的原因是:**生成 Key 的逻辑有问题**,或者根本没有在产生任务的那段特定逻辑中加上 singletonKey。
|
||||
|
||||
## **2\. 解决方案:三层防御体系**
|
||||
|
||||
虽然根因(重复配置)已被你清理,但为了防止未来代码逻辑再次意外引入重复配置,或者防止前端意外的连击,我们依然强烈建议保留以下防御措施。
|
||||
|
||||
### **第一层:入队时防御 (生产者层面 \- 强制去重)**
|
||||
|
||||
这是最关键的一步。无论是因为配置重复导致被调用 7 次,还是前端点了 7 次,这里都能拦住。
|
||||
|
||||
**修改代码建议 (Producer/Service):**
|
||||
|
||||
// reviewTaskProducer.ts
|
||||
|
||||
import { PgBoss } from 'pg-boss';
|
||||
|
||||
// 假设这是你的入队逻辑
|
||||
export async function createReviewTask(boss: PgBoss, taskId: string, payload: any) {
|
||||
const queueName \= 'rvw\_review\_task';
|
||||
|
||||
// ✅ 核心修复:构造确定性的 singletonKey
|
||||
// 不要包含时间戳等变量,只包含业务唯一标识 (如 taskId)
|
||||
const singletonKey \= \`review\_task\_${taskId}\`;
|
||||
|
||||
// 发送任务
|
||||
const jobId \= await boss.send(queueName, payload, {
|
||||
// ✅ 启用单例模式
|
||||
singletonKey: singletonKey,
|
||||
// ✅ 节流/防抖:如果任务已存在且活跃,300秒内不再创建
|
||||
singletonSeconds: 300,
|
||||
// ✅ 即使旧任务完成了,保留Key一段时间以防重复触发
|
||||
singletonNextSlot: false
|
||||
});
|
||||
|
||||
if (\!jobId) {
|
||||
console.warn(\`\[Duplicate Prevented\] Task ${taskId} already exists in queue.\`);
|
||||
return null;
|
||||
}
|
||||
|
||||
return jobId;
|
||||
}
|
||||
|
||||
### **第二层:处理时防御 (Worker 层面 \- 幂等性检查)**
|
||||
|
||||
你已经添加了状态检查,这很好。为了处理潜在的竞争(虽然单实例下竞争少,但为了健壮性),建议保持乐观锁逻辑。
|
||||
|
||||
**修改代码建议 (Worker):**
|
||||
|
||||
// reviewWorker.ts
|
||||
|
||||
export async function processReviewTask(job: Job) {
|
||||
const { taskId } \= job.data;
|
||||
|
||||
// 1\. 业务状态检查 (你已经做了)
|
||||
const task \= await db.task.findUnique({ where: { id: taskId } });
|
||||
|
||||
// ✅ 状态检查:如果已经是处理中或完成,直接跳过
|
||||
if (task.status \=== 'COMPLETED' || task.status \=== 'PROCESSING') {
|
||||
console.log(\`\[Skipped\] Task ${taskId} is already ${task.status}\`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 2\. 乐观锁更新 (Database Atomic Update)
|
||||
const updateResult \= await db.task.updateMany({
|
||||
where: {
|
||||
id: taskId,
|
||||
status: 'PENDING' // 👈 关键:只有当前状态是 PENDING 时才更新
|
||||
},
|
||||
data: {
|
||||
status: 'PROCESSING',
|
||||
startedAt: new Date()
|
||||
}
|
||||
});
|
||||
|
||||
if (updateResult.count \=== 0\) {
|
||||
console.log(\`\[Concurrency Control\] Task ${taskId} claimed by another worker or logic.\`);
|
||||
return;
|
||||
}
|
||||
|
||||
// 3\. 执行逻辑
|
||||
try {
|
||||
await performReviewLogic(taskId);
|
||||
await db.task.update({ where: { id: taskId }, data: { status: 'COMPLETED' }});
|
||||
} catch (error) {
|
||||
await db.task.update({ where: { id: taskId }, data: { status: 'FAILED' }});
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
|
||||
### **第三层:初始化代码审查 (防止复发)**
|
||||
|
||||
针对 **"32 个重复队列定义"** 的来源,需要检查你的启动代码。
|
||||
|
||||
1. **检查 schedule 调用**:
|
||||
如果你在代码里使用了 boss.schedule('queue', cron, ...),请确保不要在每次应用启动时都无脑调用。
|
||||
* **错误做法**:在 main.ts 直接调用 boss.schedule(...)。每次部署都会尝试再加一个(取决于 pg-boss 版本行为)。
|
||||
* **正确做法**:通常 pg-boss 会处理去重,但如果参数稍有不同(比如 cron 表达式或数据),它可能会视为新 Schedules。建议检查 pg-boss 的 schedules 表,确保没有垃圾数据。
|
||||
2. **清理脚本**:
|
||||
保留一个数据库迁移脚本或运维 SQL,定期检查 pg-boss 的 job 表中是否有异常激增的 created 状态的任务。
|
||||
|
||||
## **3\. 总结**
|
||||
|
||||
* **问题原因**:数据库中残留的历史重复配置(7个重复条目)导致单实例在循环中瞬间创建了 7 个任务。
|
||||
* **当前状态**:你清理了重复条目,这已经解决了根源。
|
||||
* **未来保障**:部署带有 singletonKey 的代码,这将是永远的防线,即使数据库里有 100 个重复配置,pg-boss 也会拒绝创建第 2 到 第 100 个任务。
|
||||
347
docs/07-运维文档/01-PgBoss队列监控与维护.md
Normal file
347
docs/07-运维文档/01-PgBoss队列监控与维护.md
Normal file
@@ -0,0 +1,347 @@
|
||||
# PgBoss 队列监控与维护手册
|
||||
|
||||
> **文档版本**:v1.0
|
||||
> **创建日期**:2026-01-27
|
||||
> **基于故障**:2026-01-27 RVW 模块任务无限循环故障
|
||||
|
||||
---
|
||||
|
||||
## 📋 目录
|
||||
|
||||
1. [故障背景](#故障背景)
|
||||
2. [架构说明](#架构说明)
|
||||
3. [日常监控 SQL](#日常监控-sql)
|
||||
4. [故障排查指南](#故障排查指南)
|
||||
5. [清理操作](#清理操作)
|
||||
6. [预防措施](#预防措施)
|
||||
|
||||
---
|
||||
|
||||
## 故障背景
|
||||
|
||||
### 2026-01-27 故障复盘
|
||||
|
||||
**现象**:RVW 审稿模块任务完成后继续无限循环执行,前端不显示结果
|
||||
|
||||
**表层原因**:数据库中残留 7 个重复的队列定义,导致单实例在一次事件循环中为同一 taskId 创建了 7 个 Job
|
||||
|
||||
**根本原因**:**数据库迁移时 `platform_schema.queue` 表的主键约束丢失**
|
||||
|
||||
| 环境 | `queue` 表主键 | 结果 |
|
||||
|------|---------------|------|
|
||||
| **本地** | ✅ `queue_pkey` (name 唯一) | `createQueue()` 重复调用会报错被忽略 |
|
||||
| **RDS** | ❌ **无主键**(迁移丢失) | `createQueue()` 每次都插入新行 |
|
||||
|
||||
**证据**:
|
||||
```sql
|
||||
-- 同一 taskId 被处理 7 次
|
||||
task_id: bd19c3d3-80cc-42f7-85a4-d38b17319a1b
|
||||
created_on: 2026-01-27 16:06:07.446015+08 (全部相同!)
|
||||
job_count: 7
|
||||
```
|
||||
|
||||
**修复**:
|
||||
1. 清理 32 个重复队列定义
|
||||
2. 添加主键约束:`ALTER TABLE platform_schema.queue ADD PRIMARY KEY (name);`
|
||||
3. 代码四层防御(前端锁 + API幂等 + singletonKey + Worker检查)
|
||||
|
||||
详见:[03-数据库迁移注意事项](./03-数据库迁移注意事项.md)
|
||||
|
||||
---
|
||||
|
||||
## 架构说明
|
||||
|
||||
### PgBoss 表结构
|
||||
|
||||
| 表名 | 说明 | Schema |
|
||||
|------|------|--------|
|
||||
| `queue` | 队列定义(每种任务类型一条记录) | platform_schema |
|
||||
| `job` | 任务记录(旧版,可能未使用) | platform_schema |
|
||||
| `job_common` | 任务记录(当前使用) | platform_schema |
|
||||
| `schedule` | 定时任务配置 | platform_schema |
|
||||
| `subscription` | 订阅配置 | platform_schema |
|
||||
| `version` | pg-boss 版本信息 | platform_schema |
|
||||
|
||||
### 任务状态流转
|
||||
|
||||
```
|
||||
created → active → completed
|
||||
→ failed → retry → active
|
||||
→ expired
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 日常监控 SQL
|
||||
|
||||
### 1. 检查重复队列定义(🔴 每日必查)
|
||||
|
||||
```sql
|
||||
-- 如果返回结果,说明有重复队列定义需要清理
|
||||
SELECT name, COUNT(*) as cnt
|
||||
FROM platform_schema.queue
|
||||
GROUP BY name
|
||||
HAVING COUNT(*) > 1
|
||||
ORDER BY cnt DESC;
|
||||
```
|
||||
|
||||
**预期结果**:无返回(0 行)
|
||||
|
||||
**异常处理**:参考 [清理操作](#清理操作)
|
||||
|
||||
---
|
||||
|
||||
### 2. 检查任务状态分布
|
||||
|
||||
```sql
|
||||
-- 查看各队列的任务状态分布
|
||||
SELECT name, state, COUNT(*) as count
|
||||
FROM platform_schema.job_common
|
||||
GROUP BY name, state
|
||||
ORDER BY name, state;
|
||||
```
|
||||
|
||||
**关注点**:
|
||||
- `active` 状态任务不应该长期存在
|
||||
- `created` 状态任务堆积说明 Worker 未启动或有问题
|
||||
- `retry` 状态任务过多说明有系统性错误
|
||||
|
||||
---
|
||||
|
||||
### 3. 检查同一 taskId 重复处理
|
||||
|
||||
```sql
|
||||
-- 检查是否有任务被重复处理
|
||||
SELECT
|
||||
data->>'taskId' as task_id,
|
||||
COUNT(*) as job_count,
|
||||
MIN(created_on) as first_run,
|
||||
MAX(completed_on) as last_run
|
||||
FROM platform_schema.job_common
|
||||
WHERE name = 'rvw_review_task'
|
||||
AND created_on > NOW() - INTERVAL '24 hours'
|
||||
GROUP BY data->>'taskId'
|
||||
HAVING COUNT(*) > 1
|
||||
ORDER BY job_count DESC;
|
||||
```
|
||||
|
||||
**预期结果**:无返回(每个 taskId 只应处理一次)
|
||||
|
||||
---
|
||||
|
||||
### 4. 检查卡住的任务
|
||||
|
||||
```sql
|
||||
-- 查找运行超过 30 分钟的活跃任务
|
||||
SELECT
|
||||
id,
|
||||
name,
|
||||
state,
|
||||
data->>'taskId' as task_id,
|
||||
created_on,
|
||||
started_on,
|
||||
EXTRACT(EPOCH FROM (NOW() - started_on))/60 as running_minutes
|
||||
FROM platform_schema.job_common
|
||||
WHERE state = 'active'
|
||||
AND started_on < NOW() - INTERVAL '30 minutes'
|
||||
ORDER BY started_on;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5. 队列健康检查汇总
|
||||
|
||||
```sql
|
||||
-- 一键健康检查
|
||||
SELECT
|
||||
'queue_duplicates' as check_type,
|
||||
CASE WHEN COUNT(*) > 0 THEN '❌ 异常' ELSE '✅ 正常' END as status,
|
||||
COUNT(*) as count
|
||||
FROM (
|
||||
SELECT name FROM platform_schema.queue GROUP BY name HAVING COUNT(*) > 1
|
||||
) t
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'stuck_active_jobs',
|
||||
CASE WHEN COUNT(*) > 0 THEN '⚠️ 警告' ELSE '✅ 正常' END,
|
||||
COUNT(*)
|
||||
FROM platform_schema.job_common
|
||||
WHERE state = 'active' AND started_on < NOW() - INTERVAL '30 minutes'
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'duplicate_tasks_24h',
|
||||
CASE WHEN COUNT(*) > 0 THEN '❌ 异常' ELSE '✅ 正常' END,
|
||||
COUNT(*)
|
||||
FROM (
|
||||
SELECT data->>'taskId' FROM platform_schema.job_common
|
||||
WHERE created_on > NOW() - INTERVAL '24 hours'
|
||||
GROUP BY data->>'taskId' HAVING COUNT(*) > 1
|
||||
) t;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 故障排查指南
|
||||
|
||||
### 症状 1:任务无限循环
|
||||
|
||||
**现象**:同一任务反复执行,日志显示不断 "Processing job"
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
1. 检查重复队列定义
|
||||
```sql
|
||||
SELECT name, COUNT(*) FROM platform_schema.queue GROUP BY name HAVING COUNT(*) > 1;
|
||||
```
|
||||
|
||||
2. 检查同一 taskId 的 Job 数量
|
||||
```sql
|
||||
SELECT data->>'taskId', COUNT(*) FROM platform_schema.job_common
|
||||
WHERE name = 'rvw_review_task' GROUP BY data->>'taskId' HAVING COUNT(*) > 1;
|
||||
```
|
||||
|
||||
3. 检查任务状态
|
||||
```sql
|
||||
SELECT id, name, state, data->>'taskId', retry_count, created_on
|
||||
FROM platform_schema.job_common
|
||||
WHERE data->>'taskId' = '问题taskId' ORDER BY created_on;
|
||||
```
|
||||
|
||||
**修复**:清理重复队列定义 + 更新任务状态
|
||||
|
||||
---
|
||||
|
||||
### 症状 2:任务卡住不执行
|
||||
|
||||
**现象**:任务状态一直是 `created`,不变成 `active`
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
1. 检查 Worker 是否注册
|
||||
```bash
|
||||
# 查看 SAE 日志
|
||||
grep "Worker registered" /logs/app.log
|
||||
```
|
||||
|
||||
2. 检查 pg-boss 连接
|
||||
```sql
|
||||
SELECT * FROM pg_stat_activity WHERE application_name = 'aiclinical-queue';
|
||||
```
|
||||
|
||||
3. 重启后端服务
|
||||
|
||||
---
|
||||
|
||||
### 症状 3:任务状态不一致
|
||||
|
||||
**现象**:pg-boss 显示 completed,但业务表显示 pending
|
||||
|
||||
**排查步骤**:
|
||||
|
||||
1. 对比两边状态
|
||||
```sql
|
||||
-- pg-boss 状态
|
||||
SELECT id, state, data->>'taskId' as task_id, completed_on
|
||||
FROM platform_schema.job_common WHERE data->>'taskId' = 'xxx';
|
||||
|
||||
-- 业务表状态
|
||||
SELECT id, status, "completedAt" FROM rvw_schema."ReviewTask" WHERE id = 'xxx';
|
||||
```
|
||||
|
||||
2. 手动同步状态(如确认已完成)
|
||||
```sql
|
||||
UPDATE rvw_schema."ReviewTask"
|
||||
SET status = 'completed', "completedAt" = NOW()
|
||||
WHERE id = 'xxx';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 清理操作
|
||||
|
||||
### 清理重复队列定义
|
||||
|
||||
```sql
|
||||
-- 删除重复的队列定义,保留最新的一个
|
||||
DELETE FROM platform_schema.queue a
|
||||
USING platform_schema.queue b
|
||||
WHERE a.name = b.name
|
||||
AND a.created_on < b.created_on;
|
||||
|
||||
-- 验证
|
||||
SELECT name, COUNT(*) FROM platform_schema.queue GROUP BY name;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 清理卡住的任务
|
||||
|
||||
```sql
|
||||
-- 将卡住的 active 任务标记为 failed
|
||||
UPDATE platform_schema.job_common
|
||||
SET state = 'failed', completed_on = NOW()
|
||||
WHERE state = 'active'
|
||||
AND started_on < NOW() - INTERVAL '1 hour';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 清理历史完成任务(释放空间)
|
||||
|
||||
```sql
|
||||
-- 删除 7 天前的已完成任务(谨慎操作)
|
||||
DELETE FROM platform_schema.job_common
|
||||
WHERE state = 'completed'
|
||||
AND completed_on < NOW() - INTERVAL '7 days';
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 预防措施
|
||||
|
||||
### 代码层面(四层防御)
|
||||
|
||||
| 层级 | 措施 | 代码位置 |
|
||||
|------|------|---------|
|
||||
| 前端 | `isSubmitting` 防重复点击 | `Dashboard.tsx` |
|
||||
| API | `updateMany` 乐观锁 | `reviewService.ts` |
|
||||
| 队列 | `singletonKey` 去重 | `PgBossQueue.ts` |
|
||||
| Worker | 状态检查跳过已完成 | `reviewWorker.ts` |
|
||||
|
||||
### 运维层面
|
||||
|
||||
1. **每日巡检**:执行健康检查 SQL
|
||||
2. **部署后检查**:确认队列定义无重复
|
||||
3. **告警配置**:设置队列异常告警(待实现)
|
||||
|
||||
### 代码审查要点
|
||||
|
||||
- [ ] 新增队列时,确保使用 `singletonKey`
|
||||
- [ ] Worker 处理前检查业务状态
|
||||
- [ ] 避免在 `push()` 中调用 `createQueue()`
|
||||
- [ ] 前端异步操作添加 loading 状态
|
||||
|
||||
---
|
||||
|
||||
## 📝 连接信息
|
||||
|
||||
```bash
|
||||
# RDS 测试环境(仅供运维使用)
|
||||
# 外网访问需先开启白名单
|
||||
psql "postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5xo.pg.rds.aliyuncs.com:5432/ai_clinical_research_test"
|
||||
|
||||
# 通过本地 Docker 连接 RDS
|
||||
docker exec ai-clinical-postgres psql "postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5xo.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" -c "SQL语句"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [故障分析报告](../06-测试文档/故障分析报告%20(1).md)
|
||||
- [数据库运维手册](./03-数据库运维手册.md)
|
||||
- [日常更新快速操作手册](../05-部署文档/19-日常更新快速操作手册.md)
|
||||
200
docs/07-运维文档/02-故障预防检查清单.md
Normal file
200
docs/07-运维文档/02-故障预防检查清单.md
Normal file
@@ -0,0 +1,200 @@
|
||||
# 故障预防检查清单
|
||||
|
||||
> **文档版本**:v1.0
|
||||
> **创建日期**:2026-01-27
|
||||
> **适用场景**:部署前后检查、日常巡检、故障预防
|
||||
|
||||
---
|
||||
|
||||
## 📋 部署前检查
|
||||
|
||||
### 代码检查
|
||||
|
||||
- [ ] **前端防重复提交**
|
||||
- [ ] 异步操作添加 `isSubmitting` 或 `loading` 状态
|
||||
- [ ] 提交按钮在请求期间 disabled
|
||||
- [ ] 使用 `finally` 确保状态解锁
|
||||
|
||||
- [ ] **后端 API 幂等性**
|
||||
- [ ] 状态更新使用 `updateMany` + WHERE 条件
|
||||
- [ ] 检查更新数量,为 0 时返回错误
|
||||
- [ ] 避免 `update` 后再检查状态(非原子操作)
|
||||
|
||||
- [ ] **队列任务**
|
||||
- [ ] 使用 `singletonKey` 防止重复入队
|
||||
- [ ] Worker 处理前检查业务状态
|
||||
- [ ] 不在 `push()` 中调用 `createQueue()`
|
||||
|
||||
### 依赖检查
|
||||
|
||||
- [ ] 数据库连接正常
|
||||
- [ ] Redis 连接正常(如使用)
|
||||
- [ ] 第三方 API 可访问
|
||||
- [ ] 环境变量配置完整
|
||||
|
||||
---
|
||||
|
||||
## 📋 部署后检查
|
||||
|
||||
### 立即检查(部署后 5 分钟内)
|
||||
|
||||
```sql
|
||||
-- 0. 🔴 检查 pg-boss 4 个关键约束(数据库迁移后必查!)
|
||||
SELECT conname FROM pg_constraint
|
||||
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'platform_schema')
|
||||
AND conname IN ('queue_pkey', 'queue_dead_letter_fkey', 'schedule_name_fkey', 'subscription_name_fkey');
|
||||
-- 预期:4 行
|
||||
-- 如果少于 4 行,参考 03-数据库迁移注意事项.md 修复
|
||||
|
||||
-- 1. 检查重复队列定义
|
||||
SELECT name, COUNT(*) as cnt
|
||||
FROM platform_schema.queue
|
||||
GROUP BY name
|
||||
HAVING COUNT(*) > 1;
|
||||
-- 预期:无返回
|
||||
```
|
||||
|
||||
```sql
|
||||
-- 2. 检查 Worker 是否注册(查看日志)
|
||||
-- SAE 日志搜索关键字:
|
||||
-- "Worker registered" 或 "Handler registered"
|
||||
```
|
||||
|
||||
```bash
|
||||
# 3. 健康检查接口
|
||||
curl https://your-domain/api/health
|
||||
# 预期:{"status":"ok"}
|
||||
```
|
||||
|
||||
### 功能验证(部署后 30 分钟内)
|
||||
|
||||
- [ ] 用户登录正常
|
||||
- [ ] 核心功能可用
|
||||
- [ ] 异步任务执行正常
|
||||
- [ ] 文件上传/下载正常
|
||||
|
||||
---
|
||||
|
||||
## 📋 日常巡检清单
|
||||
|
||||
### 每日必查(09:00)
|
||||
|
||||
| 检查项 | SQL/命令 | 预期结果 |
|
||||
|--------|---------|---------|
|
||||
| 队列重复定义 | `SELECT name, COUNT(*) FROM platform_schema.queue GROUP BY name HAVING COUNT(*) > 1;` | 无返回 |
|
||||
| 卡住的任务 | `SELECT COUNT(*) FROM platform_schema.job_common WHERE state = 'active' AND started_on < NOW() - INTERVAL '30 minutes';` | 0 |
|
||||
| 数据库连接数 | `SELECT count(*) FROM pg_stat_activity;` | < 80 |
|
||||
|
||||
### 一键健康检查
|
||||
|
||||
```sql
|
||||
SELECT
|
||||
'queue_duplicates' as check_type,
|
||||
CASE WHEN COUNT(*) > 0 THEN '❌ 异常' ELSE '✅ 正常' END as status,
|
||||
COUNT(*) as count
|
||||
FROM (
|
||||
SELECT name FROM platform_schema.queue GROUP BY name HAVING COUNT(*) > 1
|
||||
) t
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'stuck_active_jobs',
|
||||
CASE WHEN COUNT(*) > 0 THEN '⚠️ 警告' ELSE '✅ 正常' END,
|
||||
COUNT(*)
|
||||
FROM platform_schema.job_common
|
||||
WHERE state = 'active' AND started_on < NOW() - INTERVAL '30 minutes'
|
||||
|
||||
UNION ALL
|
||||
|
||||
SELECT
|
||||
'db_connections',
|
||||
CASE WHEN COUNT(*) > 80 THEN '⚠️ 警告' ELSE '✅ 正常' END,
|
||||
COUNT(*)
|
||||
FROM pg_stat_activity;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 代码审查清单(PR Review)
|
||||
|
||||
### 前端
|
||||
|
||||
- [ ] 表单提交有 loading 状态
|
||||
- [ ] 按钮防抖/禁用处理
|
||||
- [ ] 错误处理完整(try-catch-finally)
|
||||
- [ ] 异步请求有超时设置
|
||||
|
||||
### 后端 API
|
||||
|
||||
- [ ] 使用事务或原子操作
|
||||
- [ ] 状态检查在更新之前
|
||||
- [ ] 错误信息明确
|
||||
- [ ] 日志记录关键步骤
|
||||
|
||||
### 队列任务
|
||||
|
||||
- [ ] 有 `singletonKey`
|
||||
- [ ] Worker 有幂等性检查
|
||||
- [ ] 超时和重试配置合理
|
||||
- [ ] 失败处理完整
|
||||
|
||||
---
|
||||
|
||||
## 📋 故障响应流程
|
||||
|
||||
### 1. 发现问题
|
||||
|
||||
- 用户反馈
|
||||
- 监控告警
|
||||
- 日志异常
|
||||
|
||||
### 2. 初步诊断(5 分钟内)
|
||||
|
||||
```sql
|
||||
-- 快速健康检查
|
||||
SELECT
|
||||
'queue_duplicates' as t, COUNT(*) FROM (SELECT name FROM platform_schema.queue GROUP BY name HAVING COUNT(*) > 1) x
|
||||
UNION ALL
|
||||
SELECT 'stuck_jobs', COUNT(*) FROM platform_schema.job_common WHERE state = 'active' AND started_on < NOW() - INTERVAL '30 minutes'
|
||||
UNION ALL
|
||||
SELECT 'db_connections', COUNT(*) FROM pg_stat_activity;
|
||||
```
|
||||
|
||||
### 3. 紧急修复
|
||||
|
||||
| 问题类型 | 紧急措施 |
|
||||
|---------|---------|
|
||||
| 任务无限循环 | 清理重复队列定义,重启服务 |
|
||||
| 数据库连接满 | 强制断开空闲连接,重启服务 |
|
||||
| 服务不可用 | 重启 SAE 应用 |
|
||||
|
||||
### 4. 根因分析
|
||||
|
||||
- 收集日志
|
||||
- 分析数据库状态
|
||||
- 复盘代码逻辑
|
||||
|
||||
### 5. 长期修复
|
||||
|
||||
- 提交代码修复 PR
|
||||
- 更新文档
|
||||
- 添加预防措施
|
||||
|
||||
---
|
||||
|
||||
## 📋 历史故障记录
|
||||
|
||||
| 日期 | 故障类型 | 影响范围 | 根因 | 修复措施 |
|
||||
|------|---------|---------|------|---------|
|
||||
| 2026-01-27 | 任务无限循环 | RVW 模块 | **数据库迁移丢失主键约束** → 队列定义重复 | 添加主键 + 四层防御 |
|
||||
| 2026-01-27 | 中文乱码 | 全系统 | PowerShell 编码问题 | Docker 内执行迁移 |
|
||||
| 2026-01-11 | 数据库事故 | 全系统 | 误操作 | 备份恢复流程 |
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [PgBoss 队列监控与维护](./01-PgBoss队列监控与维护.md)
|
||||
- [数据库运维手册](./03-数据库运维手册.md)
|
||||
- [故障分析报告](../06-测试文档/故障分析报告%20(1).md)
|
||||
379
docs/07-运维文档/03-数据库迁移注意事项.md
Normal file
379
docs/07-运维文档/03-数据库迁移注意事项.md
Normal file
@@ -0,0 +1,379 @@
|
||||
# 数据库迁移注意事项
|
||||
|
||||
> **文档版本**:v1.1
|
||||
> **创建日期**:2026-01-27
|
||||
> **最后更新**:2026-01-27
|
||||
> **基于故障**:2026-01-27 RVW 任务无限循环故障(根因:pg-boss 表约束丢失)
|
||||
|
||||
---
|
||||
|
||||
## 📋 背景
|
||||
|
||||
### 故障复盘
|
||||
|
||||
**现象**:RVW 审稿模块任务完成后继续无限循环执行,同一 taskId 被处理 7 次
|
||||
|
||||
**根因**:数据库从本地迁移到 RDS 时,`platform_schema` 中 **pg-boss 表的约束丢失**
|
||||
|
||||
| 丢失的约束 | 类型 | 影响 |
|
||||
|-----------|------|------|
|
||||
| `queue_pkey` | 主键 | 🔴 **导致队列定义重复** |
|
||||
| `queue_dead_letter_fkey` | 外键 | 死信队列引用完整性丢失 |
|
||||
| `schedule_name_fkey` | 外键 | 定时任务引用完整性丢失 |
|
||||
| `subscription_name_fkey` | 外键 | 订阅引用完整性丢失 |
|
||||
|
||||
**影响**:每次 SAE 重启都会创建新的队列定义,导致同一任务被入队多次
|
||||
|
||||
---
|
||||
|
||||
## 🔍 为什么只有 pg-boss 相关约束丢失?
|
||||
|
||||
### pg-boss 表的"双重管理者"问题
|
||||
|
||||
| 类别 | 表示例 | 管理方式 | 约束创建 |
|
||||
|------|--------|---------|---------|
|
||||
| **业务表** | `users`, `tenants`, `ReviewTask` | Prisma Migration | ✅ 迁移文件明确包含所有约束 |
|
||||
| **pg-boss 表** | `queue`, `job`, `schedule` | pg-boss 运行时自动创建 | ⚠️ 仅在首次启动时创建 |
|
||||
|
||||
**关键点**:
|
||||
1. **业务表由 Prisma 管理**:通过 `prisma migrate deploy` 创建,约束完整
|
||||
2. **pg-boss 表在 Prisma Schema 中定义**:但只是通过 `db pull` 拉取的,Prisma 不负责创建
|
||||
3. **pg-boss 表实际由 `boss.start()` 创建**:如果表已存在,pg-boss 跳过创建步骤
|
||||
|
||||
### 迁移时发生了什么?
|
||||
|
||||
```
|
||||
本地数据库 pg_dump RDS
|
||||
┌─────────────────┐ ────────► ┌─────────────────┐
|
||||
│ queue 表 │ │ queue 表 │
|
||||
│ ✅ queue_pkey │ │ ❌ queue_pkey │ ← 丢失!
|
||||
│ ✅ 3个外键约束 │ │ ❌ 3个外键约束 │ ← 丢失!
|
||||
└─────────────────┘ └─────────────────┘
|
||||
|
||||
原因:
|
||||
1. pg_dump 导出时外键延迟处理
|
||||
2. queue 表有自引用外键 (dead_letter → name)
|
||||
3. 导入时约束创建顺序问题导致失败
|
||||
4. pg-boss 检测到表已存在,跳过约束创建
|
||||
```
|
||||
|
||||
### 自引用外键是罪魁祸首
|
||||
|
||||
```sql
|
||||
-- queue 表的自引用外键(容易在迁移时丢失)
|
||||
FOREIGN KEY (dead_letter) REFERENCES platform_schema.queue(name)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 🔴 迁移后必须检查项
|
||||
|
||||
### 1. pg-boss 4 个关键约束检查(最重要!)
|
||||
|
||||
```sql
|
||||
-- 🔴 一键检查 pg-boss 关键约束(必须返回 4 行)
|
||||
SELECT conname, contype,
|
||||
CASE contype WHEN 'p' THEN '主键' WHEN 'f' THEN '外键' END as type_desc
|
||||
FROM pg_constraint
|
||||
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'platform_schema')
|
||||
AND conname IN (
|
||||
'queue_pkey', -- 主键(防止重复队列)
|
||||
'queue_dead_letter_fkey', -- 自引用外键
|
||||
'schedule_name_fkey', -- schedule → queue 外键
|
||||
'subscription_name_fkey' -- subscription → queue 外键
|
||||
)
|
||||
ORDER BY conname;
|
||||
|
||||
-- 预期结果(必须 4 行):
|
||||
-- conname | contype | type_desc
|
||||
-- -------------------------+---------+-----------
|
||||
-- queue_dead_letter_fkey | f | 外键
|
||||
-- queue_pkey | p | 主键
|
||||
-- schedule_name_fkey | f | 外键
|
||||
-- subscription_name_fkey | f | 外键
|
||||
```
|
||||
|
||||
**如果少于 4 行,立即修复**:
|
||||
|
||||
```sql
|
||||
-- 修复 1:添加 queue_pkey(如果缺失)
|
||||
-- 先清理重复数据
|
||||
DELETE FROM platform_schema.queue a
|
||||
USING platform_schema.queue b
|
||||
WHERE a.name = b.name
|
||||
AND a.created_on < b.created_on;
|
||||
-- 添加主键
|
||||
ALTER TABLE platform_schema.queue ADD PRIMARY KEY (name);
|
||||
|
||||
-- 修复 2:添加 queue_dead_letter_fkey(如果缺失)
|
||||
ALTER TABLE platform_schema.queue
|
||||
ADD CONSTRAINT queue_dead_letter_fkey
|
||||
FOREIGN KEY (dead_letter) REFERENCES platform_schema.queue(name);
|
||||
|
||||
-- 修复 3:添加 schedule_name_fkey(如果缺失)
|
||||
ALTER TABLE platform_schema.schedule
|
||||
ADD CONSTRAINT schedule_name_fkey
|
||||
FOREIGN KEY (name) REFERENCES platform_schema.queue(name) ON DELETE CASCADE;
|
||||
|
||||
-- 修复 4:添加 subscription_name_fkey(如果缺失)
|
||||
ALTER TABLE platform_schema.subscription
|
||||
ADD CONSTRAINT subscription_name_fkey
|
||||
FOREIGN KEY (name) REFERENCES platform_schema.queue(name) ON DELETE CASCADE;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 2. 检查重复队列定义
|
||||
|
||||
```sql
|
||||
-- 检查是否有重复队列定义
|
||||
SELECT name, COUNT(*) as cnt
|
||||
FROM platform_schema.queue
|
||||
GROUP BY name
|
||||
HAVING COUNT(*) > 1;
|
||||
|
||||
-- 预期结果:无返回(0 行)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3. 全局约束数量对比
|
||||
|
||||
```sql
|
||||
-- 检查各 schema 约束数量(与本地对比)
|
||||
SELECT n.nspname as schema, COUNT(*) as constraint_count
|
||||
FROM pg_constraint c
|
||||
JOIN pg_namespace n ON c.connamespace = n.oid
|
||||
WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')
|
||||
GROUP BY n.nspname
|
||||
ORDER BY n.nspname;
|
||||
|
||||
-- 🔴 platform_schema 约束数量必须是 33
|
||||
-- 如果少于 33,说明有约束丢失
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 4. 检查所有 pg-boss 表索引
|
||||
|
||||
```sql
|
||||
-- 列出 platform_schema 中的所有表和索引
|
||||
SELECT
|
||||
t.tablename,
|
||||
COUNT(i.indexname) as index_count
|
||||
FROM pg_tables t
|
||||
LEFT JOIN pg_indexes i ON t.tablename = i.tablename AND t.schemaname = i.schemaname
|
||||
WHERE t.schemaname = 'platform_schema'
|
||||
GROUP BY t.tablename
|
||||
ORDER BY t.tablename;
|
||||
|
||||
-- 关键表应该有索引:
|
||||
-- queue: 1 (queue_pkey)
|
||||
-- job_common: 多个索引
|
||||
-- schedule: 索引
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📋 数据库迁移完整检查清单
|
||||
|
||||
### 迁移前
|
||||
|
||||
- [ ] 备份源数据库
|
||||
- [ ] 记录源数据库的表结构和索引
|
||||
- [ ] 确认 pg_dump 参数包含约束和索引
|
||||
|
||||
### 迁移中
|
||||
|
||||
```bash
|
||||
# 推荐的 pg_dump 参数(包含完整结构)
|
||||
pg_dump -h localhost -U postgres -d ai_clinical_research \
|
||||
--no-owner \
|
||||
--no-acl \
|
||||
--format=plain \
|
||||
--encoding=UTF8 \
|
||||
> backup.sql
|
||||
|
||||
# 不要使用 --data-only(会丢失约束)
|
||||
```
|
||||
|
||||
### 迁移后(🔴 关键检查)
|
||||
|
||||
| 检查项 | SQL | 预期 |
|
||||
|--------|-----|------|
|
||||
| queue 主键 | `SELECT indexname FROM pg_indexes WHERE tablename='queue';` | `queue_pkey` |
|
||||
| 重复队列 | `SELECT name, COUNT(*) FROM platform_schema.queue GROUP BY name HAVING COUNT(*) > 1;` | 无返回 |
|
||||
| 表数量 | `SELECT COUNT(*) FROM pg_tables WHERE schemaname='platform_schema';` | 与源一致 |
|
||||
| 索引数量 | `SELECT COUNT(*) FROM pg_indexes WHERE schemaname='platform_schema';` | 与源一致 |
|
||||
|
||||
---
|
||||
|
||||
## 🛠️ 常见问题修复
|
||||
|
||||
### 问题 1:pg-boss 表约束丢失(2026-01-27 故障)
|
||||
|
||||
**症状**:
|
||||
- 每次应用启动都创建新的队列定义
|
||||
- 同一任务被重复处理多次
|
||||
- RVW 审稿任务无限循环
|
||||
|
||||
**丢失的约束(共 4 个)**:
|
||||
|
||||
| 约束 | 类型 | 影响 |
|
||||
|------|------|------|
|
||||
| `queue_pkey` | 主键 | 🔴 导致队列定义重复 |
|
||||
| `queue_dead_letter_fkey` | 外键 | 死信队列引用失效 |
|
||||
| `schedule_name_fkey` | 外键 | 定时任务引用失效 |
|
||||
| `subscription_name_fkey` | 外键 | 订阅引用失效 |
|
||||
|
||||
**一键修复脚本**:
|
||||
|
||||
```sql
|
||||
-- 🔴 执行前先检查哪些约束缺失
|
||||
SELECT conname FROM pg_constraint
|
||||
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'platform_schema')
|
||||
AND conname IN ('queue_pkey', 'queue_dead_letter_fkey', 'schedule_name_fkey', 'subscription_name_fkey');
|
||||
|
||||
-- 修复步骤:
|
||||
|
||||
-- 1. 清理重复队列定义(如果有)
|
||||
DELETE FROM platform_schema.queue a
|
||||
USING platform_schema.queue b
|
||||
WHERE a.name = b.name AND a.created_on < b.created_on;
|
||||
|
||||
-- 2. 添加主键(如果缺失)
|
||||
ALTER TABLE platform_schema.queue ADD PRIMARY KEY (name);
|
||||
|
||||
-- 3. 添加外键约束(如果缺失)
|
||||
ALTER TABLE platform_schema.queue
|
||||
ADD CONSTRAINT queue_dead_letter_fkey
|
||||
FOREIGN KEY (dead_letter) REFERENCES platform_schema.queue(name);
|
||||
|
||||
ALTER TABLE platform_schema.schedule
|
||||
ADD CONSTRAINT schedule_name_fkey
|
||||
FOREIGN KEY (name) REFERENCES platform_schema.queue(name) ON DELETE CASCADE;
|
||||
|
||||
ALTER TABLE platform_schema.subscription
|
||||
ADD CONSTRAINT subscription_name_fkey
|
||||
FOREIGN KEY (name) REFERENCES platform_schema.queue(name) ON DELETE CASCADE;
|
||||
|
||||
-- 4. 验证(必须返回 4 行)
|
||||
SELECT conname FROM pg_constraint
|
||||
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'platform_schema')
|
||||
AND conname IN ('queue_pkey', 'queue_dead_letter_fkey', 'schedule_name_fkey', 'subscription_name_fkey');
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 问题 2:迁移后中文乱码
|
||||
|
||||
**症状**:用户名、租户名显示为 `????`
|
||||
|
||||
**原因**:PowerShell 默认编码不是 UTF-8
|
||||
|
||||
**修复**:在 Docker 容器内执行导入导出
|
||||
|
||||
```bash
|
||||
# 在容器内导出(绕过 PowerShell 编码问题)
|
||||
docker exec -it postgres-container bash -c "pg_dump ... > /tmp/backup.sql"
|
||||
|
||||
# 在容器内导入
|
||||
docker exec -i postgres-container psql ... < backup.sql
|
||||
```
|
||||
|
||||
详见:[05-部署文档/0126部署/08-部署完成总结.md](../05-部署文档/0126部署/08-部署完成总结.md)
|
||||
|
||||
---
|
||||
|
||||
### 问题 3:迁移后外键约束失败
|
||||
|
||||
**症状**:`ERROR: insert or update violates foreign key constraint`
|
||||
|
||||
**原因**:表导入顺序错误
|
||||
|
||||
**修复**:使用事务或正确的依赖顺序
|
||||
|
||||
```sql
|
||||
-- 临时禁用外键检查(谨慎使用)
|
||||
SET session_replication_role = replica;
|
||||
|
||||
-- 导入数据...
|
||||
|
||||
-- 恢复外键检查
|
||||
SET session_replication_role = DEFAULT;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📊 迁移对比脚本
|
||||
|
||||
迁移后执行此脚本,对比源和目标数据库:
|
||||
|
||||
```sql
|
||||
-- 🔴 一键健康检查(迁移后必须执行)
|
||||
|
||||
-- 1. 全局统计(与本地对比)
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM pg_tables WHERE schemaname NOT IN ('pg_catalog', 'information_schema')) as tables,
|
||||
(SELECT COUNT(*) FROM pg_indexes WHERE schemaname NOT IN ('pg_catalog', 'information_schema')) as indexes,
|
||||
(SELECT COUNT(*) FROM pg_constraint c JOIN pg_namespace n ON c.connamespace = n.oid WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')) as constraints;
|
||||
|
||||
-- 预期值(2026-01-27):tables=64, indexes=236, constraints=108
|
||||
|
||||
-- 2. platform_schema 约束数量
|
||||
SELECT COUNT(*) as platform_constraints
|
||||
FROM pg_constraint c
|
||||
JOIN pg_namespace n ON c.connamespace = n.oid
|
||||
WHERE n.nspname = 'platform_schema';
|
||||
|
||||
-- 预期值:33
|
||||
|
||||
-- 3. 🔴 pg-boss 4 个关键约束
|
||||
SELECT conname FROM pg_constraint
|
||||
WHERE connamespace = (SELECT oid FROM pg_namespace WHERE nspname = 'platform_schema')
|
||||
AND conname IN ('queue_pkey', 'queue_dead_letter_fkey', 'schedule_name_fkey', 'subscription_name_fkey');
|
||||
|
||||
-- 预期:4 行
|
||||
|
||||
-- 4. 重复队列检查
|
||||
SELECT name, COUNT(*) FROM platform_schema.queue GROUP BY name HAVING COUNT(*) > 1;
|
||||
|
||||
-- 预期:0 行
|
||||
```
|
||||
|
||||
### 本地 vs RDS 快速对比命令
|
||||
|
||||
```bash
|
||||
# 本地
|
||||
docker exec ai-clinical-postgres psql "postgresql://postgres:postgres123@localhost:5432/ai_clinical_research" -c "
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM pg_tables WHERE schemaname NOT IN ('pg_catalog', 'information_schema')) as tables,
|
||||
(SELECT COUNT(*) FROM pg_indexes WHERE schemaname NOT IN ('pg_catalog', 'information_schema')) as indexes,
|
||||
(SELECT COUNT(*) FROM pg_constraint c JOIN pg_namespace n ON c.connamespace = n.oid WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')) as constraints;
|
||||
"
|
||||
|
||||
# RDS
|
||||
docker exec ai-clinical-postgres psql "postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5xo.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" -c "
|
||||
SELECT
|
||||
(SELECT COUNT(*) FROM pg_tables WHERE schemaname NOT IN ('pg_catalog', 'information_schema')) as tables,
|
||||
(SELECT COUNT(*) FROM pg_indexes WHERE schemaname NOT IN ('pg_catalog', 'information_schema')) as indexes,
|
||||
(SELECT COUNT(*) FROM pg_constraint c JOIN pg_namespace n ON c.connamespace = n.oid WHERE n.nspname NOT IN ('pg_catalog', 'information_schema')) as constraints;
|
||||
"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 📚 相关文档
|
||||
|
||||
- [01-PgBoss队列监控与维护](./01-PgBoss队列监控与维护.md) - 队列日常监控
|
||||
- [02-故障预防检查清单](./02-故障预防检查清单.md) - 部署检查清单
|
||||
- [故障分析报告](../06-测试文档/故障分析报告%20(1).md) - 原始故障分析
|
||||
|
||||
---
|
||||
|
||||
## 📝 变更记录
|
||||
|
||||
| 日期 | 版本 | 变更内容 |
|
||||
|------|------|---------|
|
||||
| 2026-01-27 | v1.1 | 补充 3 个外键约束丢失分析,添加"双重管理者"问题说明 |
|
||||
| 2026-01-27 | v1.0 | 初始版本,基于 RVW 任务无限循环故障 |
|
||||
@@ -1,50 +1,59 @@
|
||||
# 运维文档
|
||||
|
||||
> **文档定位:** 系统运维、监控、故障排查
|
||||
> **适用范围:** 运维团队、SRE团队
|
||||
> **文档目的**:记录系统运维相关的监控、故障排查、预防措施等
|
||||
> **创建日期**:2026-01-27
|
||||
> **维护者**:运维团队
|
||||
|
||||
---
|
||||
|
||||
## 📋 运维文档清单
|
||||
## 📚 文档索引
|
||||
|
||||
| 文档 | 说明 | 状态 |
|
||||
|------|------|------|
|
||||
| **01-环境配置指南.md** | 环境变量、数据库连接、API密钥配置 | ✅ 已完成 |
|
||||
| **02-环境变量配置模板.md** | .env配置模板,含CloseAI配置 ⭐ | ✅ 已完成 |
|
||||
| **03-监控告警.md** | 监控指标、告警规则 | ⏳ 待创建 |
|
||||
| **04-故障排查.md** | 常见问题排查手册 | ⏳ 待创建 |
|
||||
| **05-备份恢复.md** | 数据备份和恢复策略 | ⏳ 待创建 |
|
||||
| 文档 | 说明 | 优先级 |
|
||||
|------|------|--------|
|
||||
| [01-PgBoss队列监控与维护](./01-PgBoss队列监控与维护.md) | pg-boss 任务队列的监控、清理、故障排查 | 🔴 高 |
|
||||
| [02-故障预防检查清单](./02-故障预防检查清单.md) | 部署前/后的检查清单,预防常见故障 | 🔴 高 |
|
||||
| [03-数据库迁移注意事项](./03-数据库迁移注意事项.md) | 数据库迁移时的检查项,避免约束丢失 | 🔴 高 |
|
||||
|
||||
---
|
||||
|
||||
## 🎯 核心运维任务
|
||||
## 🔧 快速参考
|
||||
|
||||
### 1. 监控
|
||||
- 系统健康检查
|
||||
- 性能监控
|
||||
- 告警通知
|
||||
### 日常检查 SQL
|
||||
|
||||
### 2. 日志
|
||||
- 日志收集
|
||||
- 日志分析
|
||||
- 日志归档
|
||||
```sql
|
||||
-- 检查重复队列定义
|
||||
SELECT name, COUNT(*) as cnt
|
||||
FROM platform_schema.queue
|
||||
GROUP BY name
|
||||
HAVING COUNT(*) > 1;
|
||||
|
||||
### 3. 备份
|
||||
- 数据库备份
|
||||
- 文件备份
|
||||
- 恢复演练
|
||||
-- 检查任务状态分布
|
||||
SELECT name, state, COUNT(*)
|
||||
FROM platform_schema.job_common
|
||||
GROUP BY name, state
|
||||
ORDER BY name, state;
|
||||
```
|
||||
|
||||
### 4. 故障处理
|
||||
- 故障诊断
|
||||
- 应急预案
|
||||
- 事后总结
|
||||
### 紧急故障处理
|
||||
|
||||
1. **任务无限循环** → 参考 [01-PgBoss队列监控与维护](./01-PgBoss队列监控与维护.md)
|
||||
2. **数据库连接满** → 参考 [03-数据库运维手册](./03-数据库运维手册.md)
|
||||
3. **服务不可用** → 重启 SAE 应用,检查日志
|
||||
|
||||
---
|
||||
|
||||
**最后更新:** 2025-11-06
|
||||
**维护人:** 技术架构师
|
||||
|
||||
## 📈 监控告警
|
||||
|
||||
| 监控项 | 阈值 | 处理方式 |
|
||||
|--------|------|---------|
|
||||
| 队列重复定义 | > 1 | 清理重复条目 |
|
||||
| 活跃任务数 | > 100 | 检查是否有任务卡住 |
|
||||
| 数据库连接数 | > 80% | 检查连接泄漏 |
|
||||
|
||||
---
|
||||
|
||||
## 📝 相关文档
|
||||
|
||||
- [部署文档](../05-部署文档/README.md)
|
||||
- [测试文档](../06-测试文档/README.md)
|
||||
- [故障分析报告](../06-测试文档/故障分析报告%20(1).md)
|
||||
|
||||
@@ -190,7 +190,7 @@ DocumentUpload.tsx // 文件上传(拖拽支持)
|
||||
- ✅ **双维度评估**:
|
||||
- 稿约规范性评估(11项标准)
|
||||
- 方法学评估(3大部分)
|
||||
- ✅ **基于真实期刊标准**:《中华脑血管病杂志》稿约
|
||||
- ✅ **基于真实期刊标准**:《中华医学超声杂志》稿约
|
||||
- ✅ **智能分析**:使用LLM进行结构化评估
|
||||
- ✅ **完整报告**:JSON格式结果,支持导出PDF/复制文本
|
||||
- ✅ **模型选择**:DeepSeek-V3 / Qwen3-72B / Qwen-Long
|
||||
@@ -953,6 +953,8 @@ CREATE INDEX idx_rvw_tasks_created_at ON rvw_schema.review_tasks(created_at);
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user