diff --git a/AI Clinical Research PRD.txt b/AI Clinical Research PRD.txt deleted file mode 100644 index 8662d4f2..00000000 --- a/AI Clinical Research PRD.txt +++ /dev/null @@ -1,90 +0,0 @@ -AI科研助手产品需求文档 (PRD) - - 1. 产品概述 -1.1 产品简介 -AI科研助手是一款专注于赋能临床及科研人员的智能化平台。它通过集成一系列基于大语言模型的AI智能体,为科研工作者在从课题选择、方案设计、数据收集、论文撰写到最终发表的全流程中提供精准、高效、专业的AI辅助,旨在解决科研过程中耗时耗力、信息壁垒、方法学门槛高等痛点,全面提升科研工作的效率与质量。 -1.2 目标用户 -本平台的核心用户群体包括: -(1)临床医生/科研人员: 需要在繁忙的临床工作之余,高效开展高质量科学研究的核心人群。 -(2)在读硕/博士研究生: 在科研学习阶段,需要专业指导和高效工具来完成研究课题与毕业论文的学生。 -(3)医药企业研发人员(R&D): 从事新药研发、临床试验等工作,需要快速获取文献信息、设计研究方案的专业人士。 -1.3 产品目标 - -(1)打造一套覆盖临床研究核心流程的AI智能体工具集,解决用户在选题、研究设计及论文写作阶段的关键需求。 -(2)优化人机交互体验,确保用户可以低门槛、便捷地使用各项功能。 - (3)发展成为科研领域内领先的一站式AI解决方案平台。 -(4)后期考虑,增加基于大规模(1000篇以内)文献的读取、识别、内容提取的工作 - - - -2. 功能需求详述 (Functional Requirements) -2.1 用户端功能 - -2.1.1 核心功能:项目/课题管理 (Context Persistence) -为解决用户需要反复输入背景信息的问题,引入项目制管理,确保AI对话的上下文连贯性。 -(1)项目创建与管理: 用户可以创建、编辑和删除不同的“项目”或“课题”。 -(2)项目背景信息预设: 在每个项目中,用户可以预先填入该项目的核心背景信息。 -(3)上下文自动关联: 在特定项目内发起的所有AI智能体对话,都将自动继承该项目的背景信息。 -● 动态背景信息管理: -(1)宏观编辑: 在项目主页,用户可通过点击编辑按钮,在弹出的模态框中对项目背景信息进行全面的、多行的编辑与修改。 -(2)微观追加: 在项目内的对话中,用户可将AI生成的关键回答(如PICOS框架、核心结论等)通过“固定”操作,一键追加到当前项目的背景信息中,实现项目信息的动态演进。 - - -2.1.2 核心功能:AI智能体 (Agent) -AI智能体是产品的核心,用户可以通过以下两种主要路径与智能体进行交互: -1. 全局快速问答 (Global Quick Chat): 用户可直接在主界面访问所有智能体,用于处理与特定项目无关的一次性问题。 -2. 项目内深度研究 (In-Project Deep Research): 用户进入一个具体的“项目/课题”空间后调用智能体,所有对话将自动继承该项目的背景信息。 - -(重点)智能体功能列表: - - (1)选题评价 智能体:从创新性、临床价值、科学性和可行性等维度,对用户提出的临床问题进行全面评价,并提供初步的SWOT分析。 用户输入的临床问题描述(自由文本),可关联项目背景。 结构化的评价报告,包含创新性、临床价值、科学性、可行性分析及综合建议。 - (2)科学问题梳理智能体: 辅助用户将一个模糊或宽泛的研究想法,提炼成一个清晰、具体、可验证的科学问题。 一个初步的研究想法,如“研究肠道菌群与抑郁症的关系”。 多个精炼后的科学问题选项,供用户选择或参考。 - (3)PICOS构建智能体: 引导用户按照PICOS原则来结构化地定义临床研究的核心要素。 引导式表单或自由文本描述的研究问题。 一个标准化的PICOS表格,清晰列出各项具体内容。 - (4)观察指标设计智能体: 基于研究设计和因果关系,为用户的研究推荐合适的主要、次要及安全性观察指标集。 已确定的PICOS框架或研究问题。 包含具体指标、定义、测量方法建议的清单。 - (5)CRF制定智能体: 基于研究方案和观察指标,自动生成一份结构化、符合规范的病例报告表(CRF)框架。 研究方案、观察指标清单。 一份可下载的CRF框架草稿(如Word/Excel格式)。 - (6)样本量计算智能体: 根据研究类型、检验水准、把握度、效应量等核心参数,提供科学合理的样本量估算结果。 引导式表单输入研究设计、α、β、效应量等。 详细的样本量计算结果,包含公式、参数说明和最终建议样本量。 - (7)临床研究方案撰写智能体: 基于科学问题、PICOS等信息,自动生成一份初步但结构完整的临床研究设计方案。 已确定的科学问题、PICOS、样本量等。 一份包含研究背景、目的、设计、流程等章节的Word文档初稿。 - (8)论文润色智能体: 针对用户上传的稿件,提供专业级的语言润色,修正语法、拼写和表达方式。 上传的论文稿件(Word/Text),目标期刊名称(可选)。 一份带修订标记的润色后文稿及修改摘要说明。 - (9)论文翻译智能体: 提供专业、精准的中英互译服务,特别针对医学科研领域的术语进行优化。 上传的论文稿件(Word/Text),选择源语言和目标语言。 翻译完成的稿件,保留原格式。 - (10)方法学评审智能体: 从研究问题、方案设计、实施可行性和临床意义等维度,对研究方案或论文进行全面的方法学评审。 上传的研究方案或论文稿件。 一份评审报告,分点列出方法学上的优点、潜在缺陷及改进建议。 - (11)期刊方法学评审智能体: 模拟期刊审稿人视角,针对投稿文章中可能存在的统计学、方法学问题进行提问和挑战。 上传的论文稿件,目标期刊。 一份模拟的“审稿意见”,列出可能被挑战的问题点。 - (12)期刊稿约评审智能体: 依据目标期刊的投稿要求,自动检查文章在格式、字数、参考文献规范等方面是否符合标准。 上传的论文稿件,目标期刊的投稿指南。 一份格式审查报告,清晰列出所有不符合项和修改建议。 - -在智能体对话中的重点功能: - (1)对话交互: 模型即时切换 在对话界面,用户可随时通过下拉菜单切换当前对话所使用的大语言模型(如Gemini, DeepSeek等),以应对不同任务。用户选择模型,后续对话由新选定的模型驱动。 - (2)临时附件上传: 在对话输入框旁提供上传按钮,支持用户上传临时文件(如PDF, Word)供AI进行一次性分析、总结或评审。 用户上传的单个文件。 AI基于该附件内容进行回答。 - (3)知识库融合对话: 在任何对话窗口中,用户可随时通过“@”操作调用个人知识库中的分类,让AI优先基于所选内容回答。 @骨质疏松专题 融合了私有知识和通用知识的回答。 - (4)答案溯源与引用: 当AI回答引用自个人知识库时,必须生成清晰、可点击的引用标记。 - 回答中包含指向源文档具体位置的引用。 - (5)对话内追加背景: 在项目对话中,提供将AI回答“固定”到项目背景的功能。 用户点击“固定”按钮。 该条回答被追加到项目描述中。 - (6)历史对话管理: 自动保存所有对话记录,支持用户在独立的“历史记录”页面中进行查看、搜索和筛选。: - -2.1.3 核心功能:个人知识库 (Personal Knowledge Base) -个人知识库是用户专属的、私密的知识管理中心。 -(1)层级化管理: 采用两级结构进行管理。第一级为知识库列表,用户可在此创建、编辑知识库分类;第二级为知识库详情页,用户点击某个知识库后,可查看和管理该库内的所有文件。 -(2)文档上传与处理: 支持在知识库详情页上传多种格式的文档。 -(3)文档处理状态透明化: 每份上传的文档都应有明确的状态标识:“处理中”、“已就绪”、“处理失败”。 -(4)失败原因提示: 若文档处理失败,系统需向用户提供清晰的原因说明。 -用户在智能体问答过程中,可以基于创建的个人知识库来进行问答 - -2.1.4 核心功能:历史记录 (History) -提供一个独立的顶级功能入口,用于管理用户的所有对话。 -(1)统一列表: 按时间倒序展示用户所有的对话记录。 -(2)来源标识: 每条记录都清晰标识其来源是“全局快速问答”还是具体的某个“项目”。 -(3)搜索与筛选: 提供强大的搜索框,并支持按项目、智能体、日期范围进行筛选,帮助用户快速定位历史对话。 -2.1.5 辅助功能 (Support) -(1)快速上手: 在侧边栏提供独立入口,点击后展示产品核心功能引导和使用教程。 -(2)帮助中心: 在侧边栏提供独立入口,点击后展示FAQ和详细的用户手册。 - -2.2 运营端/后台功能 - -(1)仪表盘: 核心数据概览 以图表形式展示关键运营指标,如:今日活跃用户、7日活跃用户、总用户数、今日对话数等。 方便运营人员快速了解产品状态。 -(2)智能体管理: 智能体列表 以列表形式展示所有前端可用的AI智能体,支持上下线操作。 -(3)智能体配置: 管理员可新增、编辑智能体,配置其名称、图标、描述、所属分类等。 -(4)Prompt管理 核心功能。管理员可为每个智能体精细化地调试和优化其背后的系统级提示词(System Prompt),并进行版本管理。 这是保障智能体专业性的关键。 -(5)模型管理 模型接入与配置 管理后台接入的各类大语言模型(如Gemini, DeepSeek),并可配置其API密钥、调用参数、启用/禁用状态。 -(6)用户管理 用户列表与检索 查看所有用户信息(ID, 手机号/邮箱, 注册时间, 状态),并支持多条件搜索。 -(7)账户详情与操作 管理员可查看单个用户的详情,并进行操作,如: - 设置/延长试用期。 - 启用或禁用账户。 - 查看操作日志。 -(8)数据统计 用户行为统计 查看用户的登录历史记录(登录时间),以及各个核心功能(如不同智能体)的使用频次统计。 帮助分析用户偏好和功能热度。 -(9)对话管理 :用户历史对话查看, 需建立严格的权限分级。只有在处理用户申诉或技术排障时,由授权的高级管理员在获得审批后,才能查看经过脱敏和屏蔽隐私信息的对话数据。 安全与隐私强化。所有查看行为必须记录在案,以备审计。 - - diff --git a/Dify完整部署方案.md b/Dify完整部署方案.md deleted file mode 100644 index c788e1ec..00000000 --- a/Dify完整部署方案.md +++ /dev/null @@ -1,300 +0,0 @@ -# Dify 完整部署方案 - 网络优化版 - -**日期**: 2025-10-10 -**状态**: 正在部署 - ---- - -## 🎯 当前情况 - -✅ **好消息**: -- Dify代码已完整下载 -- Docker镜像**已开始下载** -- 部分镜像层已成功下载(如:54e822d8ee0c Already exists) - -❌ **问题**: -- 网络不稳定,出现EOF错误 -- 需要优化网络配置 - ---- - -## 🚀 推荐方案:配置镜像加速 + 持续重试 - -### 第一步:立即配置Docker镜像加速器 ⚡ - -**这是关键步骤,可以大幅提升成功率!** - -#### 操作步骤: - -1. **右键点击**系统托盘的Docker Desktop图标(🐋) -2. 选择 **Settings** (设置) -3. 左侧菜单选择 **Docker Engine** -4. 在JSON配置编辑器中,找到现有配置 -5. 添加或修改 `registry-mirrors` 配置: - -```json -{ - "builder": { - "gc": { - "defaultKeepStorage": "20GB", - "enabled": true - } - }, - "experimental": false, - "registry-mirrors": [ - "https://docker.mirrors.ustc.edu.cn", - "https://hub-mirror.c.163.com", - "https://registry.docker-cn.com", - "https://dockerproxy.com", - "https://mirror.baidubce.com" - ] -} -``` - -6. 点击 **Apply & restart** (应用并重启) -7. 等待Docker重启完成(约30秒) - ---- - -### 第二步:重新拉取镜像 - -Docker重启后,打开PowerShell并运行: - -```powershell -# 进入Dify目录 -cd D:\MyCursor\dify\docker - -# 重新拉取镜像(这次应该会快很多) -docker compose pull -``` - -**预计时间**: 10-20分钟(取决于网络速度) - ---- - -### 第三步:启动Dify服务 - -镜像拉取完成后: - -```powershell -# 启动所有服务 -docker compose up -d - -# 等待服务启动(约2-3分钟) -Start-Sleep -Seconds 120 - -# 查看服务状态 -docker compose ps -``` - ---- - -### 第四步:验证部署 - -1. **检查容器状态**: -```powershell -docker ps | findstr dify -``` - -应该看到以下容器运行中: -- docker-api-1 -- docker-worker-1 -- docker-web-1 -- docker-nginx-1 -- docker-weaviate-1 -- docker-redis-1 -- docker-db-1 - -2. **访问Dify界面**: -- 打开浏览器 -- 访问:http://localhost -- 应该看到Dify欢迎页面 - -3. **完成初始化**: -- 创建管理员账号 -- 设置管理员密码 -- 获取API Key - ---- - -## 🔄 备选方案:如果镜像加速器仍然失败 - -### 方案1:逐个拉取镜像 - -```powershell -cd D:\MyCursor\dify\docker - -# 一次拉取一个服务的镜像 -docker compose pull redis -docker compose pull db -docker compose pull nginx -docker compose pull weaviate -docker compose pull api -docker compose pull worker -docker compose pull web -docker compose pull sandbox - -# 全部拉取完成后启动 -docker compose up -d -``` - -### 方案2:使用国内Dify镜像(如果有) - -检查Dify是否提供国内镜像源。 - -### 方案3:暂时跳过Dify部署 - -**如果网络问题持续无法解决**,可以: -1. 先继续Day 6的前端开发 -2. 使用大模型API直接实现对话功能 -3. 等网络稳定或有VPN时再部署Dify -4. 或者考虑使用其他RAG方案 - ---- - -## 📊 Dify完整架构说明 - -当Dify成功部署后,它包含以下组件: - -| 服务 | 端口 | 说明 | -|------|------|------| -| nginx | 80 | 反向代理,统一入口 | -| api | 5001 | Dify API服务 | -| web | 3000 | Dify前端界面 | -| worker | - | 后台任务处理 | -| worker_beat | - | 定时任务调度 | -| db (PostgreSQL) | 5432 | Dify数据库 | -| redis | 6379 | 缓存服务 | -| weaviate | 8080 | 向量数据库(RAG核心) | -| sandbox | 8194 | 代码执行沙箱 | - ---- - -## 🎯 快速命令参考 - -### 查看部署进度 -```powershell -cd D:\MyCursor\dify\docker -docker compose ps -``` - -### 查看容器日志 -```powershell -# 查看所有日志 -docker compose logs - -# 查看特定服务日志 -docker compose logs api -docker compose logs web -``` - -### 重启服务 -```powershell -docker compose restart -``` - -### 停止服务 -```powershell -docker compose down -``` - -### 完全清理并重新部署 -```powershell -docker compose down -v # -v 会删除数据卷 -docker compose pull -docker compose up -d -``` - ---- - -## ✅ 部署成功检查清单 - -- [ ] Docker镜像加速器已配置 -- [ ] 所有镜像拉取完成(无报错) -- [ ] `docker compose ps` 显示所有容器 "Up" -- [ ] 访问 http://localhost 可以打开Dify界面 -- [ ] 完成初始化,创建管理员账号 -- [ ] 获取Dify API Key -- [ ] 测试创建知识库功能 - ---- - -## 🔧 常见问题排查 - -### 问题1:端口冲突 - -**症状**:提示端口80或5432已被占用 - -**解决**: -```powershell -# 查看端口占用 -netstat -ano | findstr :80 -netstat -ano | findstr :5432 - -# 停止AI Clinical的PostgreSQL(如果冲突) -cd D:\MyCursor\AIclinicalresearch -docker-compose down -``` - -### 问题2:内存不足 - -**症状**:容器启动失败,提示OOM - -**解决**: -- Docker Desktop → Settings → Resources -- 增加Memory限制到至少8GB - -### 问题3:磁盘空间不足 - -**症状**:镜像拉取失败,提示no space left - -**解决**: -```powershell -# 清理未使用的镜像 -docker system prune -a - -# 查看磁盘使用 -docker system df -``` - ---- - -## 🎉 部署成功后的下一步 - -1. **获取API Key** - - 登录Dify控制台 - - 进入设置 → API Keys - - 创建新的API Key - - 复制并保存到 `backend/.env` 的 `DIFY_API_KEY` - -2. **创建测试知识库** - - 创建一个知识库 - - 上传1-2个测试PDF文件 - - 记录知识库ID - -3. **更新开发里程碑** - - 标记Day 4的Dify部署任务为完成 - - 更新文档 - -4. **继续Day 6开发** - - 开始前端基础架构搭建 - - 集成Dify API - ---- - -## 📞 需要帮助? - -如果部署过程中遇到问题: - -1. 查看本文档的"常见问题排查"部分 -2. 查看容器日志:`docker compose logs` -3. 查看Dify官方文档:https://docs.dify.ai -4. 告诉我具体的错误信息 - ---- - -**💪 加油!配置镜像加速器后成功率会大大提升!** - - - - diff --git a/Dify部署监控.bat b/Dify部署监控.bat deleted file mode 100644 index dd3d3fb8..00000000 --- a/Dify部署监控.bat +++ /dev/null @@ -1,23 +0,0 @@ -@echo off -chcp 65001 >nul -echo ==================================== -echo Dify 部署状态监控 -echo ==================================== -echo. -echo 正在检查Docker容器状态... -echo. -docker ps -a --format "table {{.Names}}\t{{.Image}}\t{{.Status}}" -echo. -echo ==================================== -echo. -echo 检查Dify相关容器: -docker ps -a | findstr /i "dify weaviate sandbox nginx redis" -echo. -echo ==================================== -echo. -echo 按任意键退出... -pause >nul - - - - diff --git a/Git提交准备清单.md b/Git提交准备清单.md deleted file mode 100644 index 0f6fb7b4..00000000 --- a/Git提交准备清单.md +++ /dev/null @@ -1,463 +0,0 @@ -# Git 提交准备清单 - -> **创建时间:** 2025-11-15 -> **目的:** 整理未被 Git 管理的文件,准备提交到远程仓库 - ---- - -## 📊 未追踪文件统计 - -| 类别 | 数量 | 操作建议 | -|------|------|----------| -| **✅ 应该提交的文件** | ~350 个 | 需要添加 | -| **⚠️ 需要确认的文件** | ~30 个 | 人工判断 | -| **❌ 不应提交的文件** | ~20 个 | 忽略或删除 | - ---- - -## ✅ 第一类:应该提交的文件(高优先级) - -### 1. 配置文件(2 个)⭐ -``` -.editorconfig ← 编辑器统一配置(UTF-8) -.gitattributes ← Git 行尾配置 -``` -**理由**:防止中文乱码,统一团队开发规范 - -### 2. 后端核心代码(~60 个文件)⭐⭐⭐ -``` -backend/src/common/ ← 通用能力层(新架构) -├── document/ExtractionClient.ts -├── llm/adapters/ -│ ├── DeepSeekAdapter.ts -│ ├── QwenAdapter.ts -│ ├── LLMFactory.ts -│ └── types.ts -├── middleware/validateProject.ts -├── rag/DifyClient.ts -└── utils/jsonParser.ts - -backend/src/legacy/ ← 业务代码(新架构) -├── controllers/ (8 个文件) -├── routes/ (7 个文件) -├── services/ (8 个文件) -└── templates/ (1 个文件) -``` -**理由**:Task 19 后端架构重构的核心成果 - -### 3. 前端新架构(frontend-v2,~30 个文件)⭐⭐⭐ -``` -frontend-v2/ ← 新前端架构(React 19) -├── package.json -├── src/framework/ ← 模块注册、权限、路由 -│ ├── modules/ ← 模块注册机制 -│ ├── permission/ ← 权限管理 -│ └── router/ ← 路由守卫 -├── src/modules/ ← 6 个业务模块占位符 -│ ├── aia/ -│ ├── asl/ -│ ├── pkb/ -│ ├── dc/ -│ ├── ssa/ -│ └── st/ -└── src/pages/HomePage.tsx -``` -**理由**:Task 17 前端模块注册机制的成果 - -### 4. 前端扩展功能(~40 个文件)⭐⭐ -``` -frontend/src/api/ -├── batchApi.ts ← 批量处理 API -└── reviewApi.ts ← 稿件审查 API - -frontend/src/components/chat/ -├── BatchMode.tsx ← 批量处理模式 -├── DeepReadMode.tsx ← 深度阅读模式 -├── FullTextMode.tsx ← 全文模式 -├── DocumentSelector.tsx ← 文档选择器 -└── ...(共 20+ 个组件) - -frontend/src/components/review/ -├── EditorialReview.tsx ← 编辑规范评估 -├── MethodologyReview.tsx ← 方法学评估 -└── ScoreCard.tsx - -frontend/src/pages/ -├── ReviewPage.tsx ← 稿件审查页面 -├── ChatPage.backup.tsx ← 备份文件(可删除) -└── ChatPage.new.tsx ← 备份文件(可删除) -``` -**理由**:Phase 2-3 的重要功能扩展 - -### 5. 文档体系(~250 个文件)⭐⭐⭐ -``` -docs/00-系统总体设计/ ← 系统架构文档(12 个文件) -docs/00-项目概述/ ← PRD 和白皮书(6 个文件) -docs/01-平台基础层/ ← 平台层文档(10 个文件) -docs/02-通用能力层/ ← 通用能力文档(9 个文件) -docs/03-业务模块/ ← 业务模块文档(~150 个文件) -├── ASL-AI智能文献/ ← 新增的 ASL 模块文档 ⭐ -│ ├── 01-需求分析/ -│ ├── 02-技术设计/ -│ │ ├── 06-质量保障与可追溯策略.md -│ │ └── 07-文献处理技术选型.md -│ ├── 03-UI设计/ -│ ├── 04-开发计划/ -│ └── 05-测试文档/ -│ └── 03-测试数据/ -│ ├── README.md -│ ├── Test Cases.xlsx -│ └── 测试案例的PICOS、纳入标准、排除标准.txt -docs/04-开发规范/ ← 开发规范(6 个文件) -docs/05-部署文档/ ← 部署文档(2 个文件) -docs/06-测试文档/ -docs/07-运维文档/ ← 运维文档(3 个文件) -docs/08-项目管理/ ← 项目管理文档(14 个文件) -docs/09-架构实施/ ← 架构实施文档(21 个文件) -docs/_templates/ ← 文档模板(5 个文件) -``` -**理由**:完整的文档体系,团队协作必需 - -### 6. 后端配置和脚本(~15 个文件) -``` -backend/CLOSEAI-CONFIG.md -backend/check-api-config.js -backend/database-validation-simple.sql -backend/database-validation.sql -backend/prisma/migrations/20251012124747_add_batch_processing_module/ -backend/prisma/migrations/20251014120128_add_review_tasks/ -backend/prisma/seed.ts -backend/prompts/ -├── review_editorial_system.txt -└── review_methodology_system.txt -backend/update-env-closeai.ps1 -backend/初始化测试用户.bat -backend/测试用户说明.md -``` -**理由**:数据库迁移、配置脚本、提示词模板 - -### 7. 项目根目录文档(~15 个文件) -``` -START-HERE-FOR-AI.md ← AI 上手指南 -PHASE1-测试指南.md -Phase2-UI优化总结.md -Phase2-全文阅读模式-真实实现.md -Phase2-快速测试清单.md -Phase2-测试指南.md -Phase3-快速参考.md -Phase3-最终收尾-测试指南.md -README-Phase2测试.md -README-里程碑1.5完成.md -如何测试Phase2.md -智能引用功能-测试指南.md -检查测试环境.bat -稿件审查功能-最终完成报告.md -测试记录/ -``` -**理由**:阶段性成果总结和测试指南 - ---- - -## ⚠️ 第二类:需要确认的文件 - -### 1. 测试脚本(可选) -``` -backend/test-batch-api.js ← 批量处理测试 -backend/test-review-api.js ← 审查 API 测试 -backend/temp-migration/ ← 临时迁移脚本 -``` -**建议**:如果还需要,可以提交;如果是一次性测试,可以删除 - -### 2. 前端备份文件 -``` -frontend/src/pages/ChatPage.backup.tsx -frontend/src/pages/ChatPage.new.tsx -``` -**建议**:**删除**(已有正式的 `ChatPage.tsx`) - -### 3. Phase 测试文档(根目录) -``` -PHASE1-测试指南.md -Phase2-*.md -Phase3-*.md -README-Phase2测试.md -``` -**建议**:考虑是否移到 `docs/06-测试文档/` 或 `docs/08-项目管理/`,然后提交 - ---- - -## ❌ 第三类:不应提交的文件(应该被忽略) - -### 1. 乱码文件夹(手动删除)❌ -``` -docs/03-涓氬姟妯″潡/ ← 已确认是 "03-业务模块" 的乱码版本 -``` -**操作**:**手动删除**(您已确认) - -### 2. 停止服务脚本(根目录) -``` -stop-all-services.bat ← 重复文件(已有正式版本) -``` -**建议**:检查是否与其他启动脚本重复,如重复则删除 - ---- - -## 🎯 提交策略建议 - -### 方案 A:一次性全部提交(快速但风险较高) - -```bash -# 添加所有应该提交的文件 -git add .editorconfig .gitattributes -git add backend/src/common/ backend/src/legacy/ -git add backend/prisma/migrations/ backend/prisma/seed.ts -git add backend/prompts/ backend/CLOSEAI-CONFIG.md -git add frontend-v2/ -git add frontend/src/api/batchApi.ts frontend/src/api/reviewApi.ts -git add frontend/src/components/chat/ frontend/src/components/review/ -git add frontend/src/pages/ReviewPage.tsx -git add "docs/00-系统总体设计/" "docs/03-业务模块/" -git add START-HERE-FOR-AI.md - -# 一次性提交 -git commit -m "feat: 提交 Week 2 完整成果 - -- ✅ 后端架构重构(legacy + common 分层) -- ✅ 前端模块注册机制(frontend-v2) -- ✅ 批量处理和稿件审查功能 -- ✅ 完整文档体系(250+ 文档) -- ✅ ASL 模块设计文档 -- ✅ 编码规范配置(.editorconfig, .gitattributes) -" -``` - -### 方案 B:分批提交(推荐,更清晰)⭐ - -#### 批次 1:配置文件和编码规范 -```bash -git add .editorconfig .gitattributes -git commit -m "chore: 添加编码规范配置,防止中文乱码" -``` - -#### 批次 2:后端架构重构 -```bash -git add backend/src/common/ backend/src/legacy/ -git add backend/prisma/schema.prisma backend/src/index.ts -git commit -m "refactor(backend): 后端架构增量演进(Task 19) - -- 新增 common/ 通用能力层 -- 新增 legacy/ 业务代码层 -- 更新 index.ts 路由注册 -- 保持系统正常运行" -``` - -#### 批次 3:数据库迁移和配置 -```bash -git add backend/prisma/migrations/ -git add backend/prisma/seed.ts -git add backend/prompts/ -git add backend/CLOSEAI-CONFIG.md -git commit -m "feat(backend): 添加批量处理和稿件审查模块 - -- Prisma 迁移:批量处理表 -- Prisma 迁移:稿件审查表 -- 新增提示词模板 -- CloseAI 配置文档" -``` - -#### 批次 4:前端新架构(frontend-v2) -```bash -git add frontend-v2/ -git commit -m "feat(frontend): 新增 frontend-v2 模块化架构(Task 17) - -- React 19 + TypeScript + Vite -- 模块注册机制 -- 权限管理系统 -- 路由守卫 -- 6 个业务模块占位符(AIA/ASL/PKB/DC/SSA/ST)" -``` - -#### 批次 5:前端功能扩展 -```bash -git add frontend/src/api/batchApi.ts -git add frontend/src/api/reviewApi.ts -git add frontend/src/components/chat/ -git add frontend/src/components/review/ -git add frontend/src/pages/ReviewPage.tsx -git add frontend/src/hooks/ -git add frontend/src/types/chat.ts -git commit -m "feat(frontend): 添加批量处理和稿件审查功能 - -- 批量处理模式(BatchMode) -- 深度阅读模式(DeepReadMode) -- 全文模式(FullTextMode) -- 稿件审查页面(ReviewPage) -- 文档选择器组件" -``` - -#### 批次 6:文档体系 -```bash -git add "docs/00-系统总体设计/" -git add "docs/00-项目概述/" -git add "docs/01-平台基础层/" -git add "docs/02-通用能力层/" -git add "docs/03-业务模块/" -git add "docs/04-开发规范/" -git add "docs/05-部署文档/" -git add "docs/06-测试文档/" -git add "docs/07-运维文档/" -git add "docs/08-项目管理/" -git add "docs/09-架构实施/" -git add "docs/_templates/" -git commit -m "docs: 完善文档体系(250+ 文档) - -- 系统总体设计(12 个文档) -- 业务模块文档(ASL/AIA/PKB/RVW 等) -- ASL 模块完整设计(质量保障策略、技术选型) -- 架构实施报告 -- 项目管理文档 -- 开发规范和模板" -``` - -#### 批次 7:测试和指南文档 -```bash -git add START-HERE-FOR-AI.md -git add PHASE1-测试指南.md -git add Phase2*.md Phase3*.md -git add README-*.md -git add 如何测试Phase2.md -git add 智能引用功能-测试指南.md -git add 检查测试环境.bat -git add 稿件审查功能-最终完成报告.md -git add 测试记录/ -git commit -m "docs: 添加测试指南和阶段性总结文档 - -- AI 快速上手指南 -- Phase 1-3 测试指南和总结 -- 功能测试文档 -- 检查脚本" -``` - -#### 批次 8:清理和最终检查 -```bash -# 删除备份文件 -git rm frontend/src/pages/ChatPage.backup.tsx -git rm frontend/src/pages/ChatPage.new.tsx - -# 删除临时脚本(如果不需要) -git rm backend/test-batch-api.js -git rm backend/test-review-api.js -git rm -r backend/temp-migration/ - -git commit -m "chore: 清理备份文件和临时测试脚本" -``` - ---- - -## 📋 执行前检查清单 - -在提交前,请确认: - -### ✅ 必做项 -- [ ] **删除乱码文件夹**:`docs/03-涓氬姟妯″潡/` -- [ ] **确认 .env 文件未被追踪**(包含敏感信息) -- [ ] **确认 node_modules/ 未被追踪** -- [ ] **确认 venv/ 未被追踪** -- [ ] **后端服务正常运行** -- [ ] **前端服务正常运行** -- [ ] **数据库连接正常** - -### ⚠️ 建议项 -- [ ] 删除前端备份文件:`ChatPage.backup.tsx`, `ChatPage.new.tsx` -- [ ] 整理根目录测试文档:考虑移到 `docs/` 目录 -- [ ] 检查是否有个人配置文件(如 `.vscode/settings.json`) -- [ ] 运行一次完整测试 - ---- - -## 🚀 推荐执行步骤 - -### 步骤 1:清理不需要的文件 -```bash -# 1. 手动删除乱码文件夹 -删除:D:\MyCursor\AIclinicalresearch\docs\03-涓氬姟妯″潡 - -# 2. 删除前端备份文件(可选) -cd D:\MyCursor\AIclinicalresearch\frontend\src\pages -del ChatPage.backup.tsx -del ChatPage.new.tsx - -# 3. 删除临时测试脚本(可选) -cd D:\MyCursor\AIclinicalresearch\backend -del test-batch-api.js -del test-review-api.js -``` - -### 步骤 2:验证系统正常运行 -```bash -# 启动后端 -cd backend -npm run dev - -# 启动前端(新窗口) -cd frontend-v2 -npm run dev - -# 测试几个核心功能 -``` - -### 步骤 3:分批提交(按方案 B 执行) -```bash -# 按照上面"方案 B"的 7 个批次依次执行 -# 每个批次提交后,检查 git log 确认无误 -``` - -### 步骤 4:推送到远程仓库 -```bash -# 查看所有提交 -git log --oneline -10 - -# 推送到远程 -git push origin master -``` - ---- - -## 📊 预期结果 - -提交完成后,远程仓库将包含: - -| 项目 | 数量 | 状态 | -|------|------|------| -| **后端代码** | ~60 个文件 | ✅ 新架构 | -| **前端代码(v2)** | ~30 个文件 | ✅ 模块化 | -| **前端扩展功能** | ~40 个文件 | ✅ 批量/审查 | -| **文档** | ~250 个文件 | ✅ 完整体系 | -| **配置文件** | 2 个 | ✅ 编码规范 | -| **数据库迁移** | 2 个 | ✅ 新表结构 | -| **测试数据** | 2 个 | ✅ ASL 测试 | - -**总计:约 400 个文件** - ---- - -## ⚠️ 注意事项 - -1. **不要一次性 `git add .`** - - 可能会误提交 `.env`、`node_modules/` 等敏感/大文件 - -2. **分批提交的好处** - - ✅ 提交历史清晰 - - ✅ 易于回滚 - - ✅ 代码审查方便 - - ✅ 便于追踪问题 - -3. **推送前确认** - - 检查是否有敏感信息(API Key、密码) - - 确认 `.gitignore` 已生效 - - 运行一次完整测试 - ---- - -**准备好后,请告诉我您想使用哪种方案(A 或 B),我来帮您执行!** 🚀 - diff --git a/PHASE1-测试指南.md b/PHASE1-测试指南.md deleted file mode 100644 index 3f5e05c9..00000000 --- a/PHASE1-测试指南.md +++ /dev/null @@ -1,148 +0,0 @@ -# 🧪 Phase 1 测试指南 - 快速参考 - -**状态**: ✅ 代码优化完成,等待测试 -**耗时**: 约20分钟 -**目标**: 验证RAG优化效果,决定是否需要Phase 2 - ---- - -## 📊 优化成果 - -| 参数 | 优化前 | 优化后 | 提升 | -|------|--------|--------|------| -| top_k | 3 | 15 | **5倍** | -| chunk_size | 500 | 1500 | **3倍** | -| 总覆盖 | 1页 | 15-20页 | **15倍** | - ---- - -## 🚀 立即开始测试(3步) - -### Step 1: 重启后端(1分钟) - -```bash -# 在后端目录停止服务(Ctrl+C),然后: -cd D:\MyCursor\AIclinicalresearch\backend -npm run dev -``` - -等待看到: -``` -✓ Server listening on http://0.0.0.0:3001 -``` - ---- - -### Step 2: 重新上传文档(10-15分钟) - -1. **访问**: http://localhost:3000/knowledge - -2. **删除旧知识库** - - 找到"阿尔兹海默知识库" - - 点击删除 → 确认 - -3. **创建新知识库** - - 点击"创建知识库" - - 名称:`阿尔兹海默知识库(优化版)` - - 点击确定 - -4. **上传文档** - - 进入新知识库 - - 上传7个PDF文件 - - **等待处理完成**(每个文件1-2分钟) - - ✅ 确认所有文档显示"已就绪" - ---- - -### Step 3: 测试效果(5分钟) - -访问"智能问答":http://localhost:3000/chat - -#### 测试1:元数据查询 -``` -@阿尔兹海默知识库(优化版) -这个知识库有几个文件?请列出文件名。 -``` - -**预期**:能识别出更多文件(接近7个) - ---- - -#### 测试2:总结问题 -``` -@阿尔兹海默知识库(优化版) -请总结这7篇文献的核心观点和主要发现。 -``` - -**预期**:总结更全面,覆盖更多文献 - ---- - -#### 测试3:对比问题 -``` -@阿尔兹海默知识库(优化版) -这些文献在研究方法上有什么异同?请对比分析。 -``` - -**预期**:对比更深入,分析更准确 - ---- - -## 📝 记录测试结果 - -### 问题1结果 -- 识别出文件数:____/7 -- 回答质量:⭐⭐⭐⭐⭐ (打分) - -### 问题2结果 -- 总结完整度:⭐⭐⭐⭐⭐ -- 覆盖文献数:____/7 - -### 问题3结果 -- 对比深度:⭐⭐⭐⭐⭐ -- 分析维度数:____ - ---- - -## 🎯 决策标准 - -### ✅ **满意** → 停止Phase 2 -- AI能识别大部分文件 -- 总结覆盖70%+文献 -- 对比分析有深度 -- **行动**:进入里程碑2(开发其他智能体) - -### ⚠️ **不满意** → 进入Phase 2 -- AI仍遗漏重要信息 -- 总结不够全面 -- 对比有明显缺失 -- **行动**:开发全文精读模式(5-7天) - ---- - -## 💡 提示 - -- 每个问题都要@知识库 -- 可以选择qwen-long模型测试(更强大) -- 记录详细的测试结果 -- 截图保存关键回答 - ---- - -## 📞 遇到问题? - -**文档上传失败**: -- 检查后端服务是否运行 -- 检查Dify服务是否正常 -- 查看后端控制台错误日志 - -**AI回答仍不理想**: -- 确认使用的是新上传的知识库 -- 确认文档已完成处理(✅ 已就绪) -- 可以尝试增加top_k到20测试 - ---- - -**准备好了吗?立即开始测试!** 🚀 - - diff --git a/Phase2-UI优化总结.md b/Phase2-UI优化总结.md deleted file mode 100644 index fd7a0b22..00000000 --- a/Phase2-UI优化总结.md +++ /dev/null @@ -1,334 +0,0 @@ -# Phase 2 - 全文阅读模式UI优化总结 - -**优化时间**:2025-10-13 -**优化人员**:AI助手 -**触发原因**:用户反馈 - ---- - -## 📋 优化概览 - -| 项目 | 内容 | -|------|------| -| **优化目标** | 最大化聊天区域,优化用户体验 | -| **问题来源** | 用户测试反馈 | -| **严重等级** | 🟡 中等(用户体验问题) | -| **完成状态** | ✅ 已完成 | -| **修改文件** | 5个文件(2新建 + 3修改) | -| **预期效果** | 聊天区域扩大2-3倍 | - ---- - -## 🎯 用户反馈 - -### 原始问题 -在全文阅读模式下: -- 容量使用情况和已加载文献显示在聊天框上方 -- 占据了大量屏幕空间(约50-60%的高度) -- 导致聊天框太小,影响对话体验 - -### 用户建议 -> 在最上边AI模型选择旁边,加一个按钮"用量说明",点击按钮后弹框显示,如果不去点击就不显示。默认显示聊天框,让聊天框的面积尽可能的大。 - ---- - -## 🔧 优化方案 - -### 方案一:原设计(问题5的修复) -**修改**:限制Header最大高度为40vh,超出滚动 -**问题**: -- 仍然占据固定空间 -- 用户每次都看到,无法隐藏 -- 对于文献多的知识库,40vh仍然很大 - -### 方案二:用户建议方案(采纳)✅ -**修改**:完全移除Header,添加"用量说明"按钮+模态框 -**优势**: -- ✅ 聊天区域最大化 -- ✅ 信息按需查看 -- ✅ 符合用户心理模型 -- ✅ 更现代化的UI设计 - ---- - -## 📝 实现细节 - -### 1. 移除FullTextMode的顶部Header - -**修改文件**:`frontend/src/components/chat/FullTextMode.tsx` - -**修改内容**: -- 移除`FullTextModeHeader`组件导入和使用 -- 保留纯净的聊天界面(MessageList + MessageInput) -- 在空状态添加提示:"💡 点击右上角'用量说明'按钮查看详细信息" - -**代码对比**: -```typescript -// 修改前 -
- // 占据大量空间 -
...
-
...
-
- -// 修改后 -
-
...
// 直接从顶部开始 -
...
-
-``` - ---- - -### 2. 创建UsageInfoModal模态框组件 - -**新建文件**: -- `frontend/src/components/chat/UsageInfoModal.tsx` -- `frontend/src/components/chat/UsageInfoModal.css` - -**组件功能**: -```typescript -interface UsageInfoModalProps { - visible: boolean - onClose: () => void - state: FullTextModeState - kbName: string -} -``` - -**内容结构**: -1. **基本信息** - - 知识库名称 - - 已加载文献数量 - - 加载原因(全部加载/文件数限制/Token限制) - -2. **容量指示器** - - 文件数使用情况(进度条) - - Token使用情况(进度条) - - 三级警告系统(绿色/黄色/红色) - -3. **已加载文献列表** - - 序号 + 文件名 + Token数 - - 最大高度300px,超出滚动 - - 悬停高亮效果 - -4. **使用提示** - - 全文阅读模式适用场景 - - 使用建议 - - 模式切换提醒 - -**样式特点**: -- 宽度:700px -- 圆角、阴影、现代化设计 -- 信息分块,层次清晰 -- 自定义滚动条样式 - ---- - -### 3. 在顶部工具栏添加"用量说明"按钮 - -**修改文件**:`frontend/src/pages/ChatPage.tsx` - -**添加位置**: -```typescript -
- {/* 用量说明按钮(仅在全文阅读模式显示) */} - {modeState.baseMode === 'knowledge_base' && - modeState.kbMode === 'full_text' && - modeState.fullTextState && ( - - - - )} - - {/* 模型选择器 */} - -
-``` - -**按钮特性**: -- ✅ 仅在全文阅读模式下显示 -- ✅ 有清晰的图标(InfoCircleOutlined) -- ✅ 有Tooltip提示 -- ✅ 位置:模型选择器左侧 - ---- - -## 📊 优化效果对比 - -### 空间利用对比 - -| 区域 | 优化前 | 优化后 | 改善 | -|------|--------|--------|------| -| Header区域 | ~50-60vh | 0 | 节省50-60vh | -| 聊天消息区域 | ~25-35vh | ~80-85vh | **扩大2.5倍** | -| 输入框区域 | ~10vh | ~10vh | 不变 | - -### 用户体验提升 - -#### ✅ 视觉焦点明确 -- **优化前**:进入页面先看到容量指示器和文献列表 -- **优化后**:直接看到聊天对话,焦点在核心功能上 - -#### ✅ 信息层级清晰 -- **常用功能(对话)**:默认显示,占据主要空间 -- **辅助信息(用量)**:按需查看,不占据空间 - -#### ✅ 交互更流畅 -- **优化前**:滚动查看对话,受限于Header高度 -- **优化后**:大屏幕空间,一次性看到更多对话历史 - -#### ✅ 专业感提升 -- 模态框设计更精致 -- 信息展示更完整 -- 符合现代SaaS产品UI标准 - ---- - -## 🎨 UI/UX设计原则 - -本次优化遵循以下设计原则: - -### 1. **Progressive Disclosure(渐进式揭示)** -不是一次性展示所有信息,而是根据用户需求逐步展示。 -- 主界面:核心功能(对话) -- 次级界面:辅助信息(用量说明) - -### 2. **Focus on User Goals(聚焦用户目标)** -用户来到全文阅读模式的主要目标是: -1. ✅ 与AI对话分析文献 -2. ⬇️ 查看已加载哪些文献 - -优先级明确,界面布局响应用户目标。 - -### 3. **Space Economy(空间经济学)** -屏幕空间是宝贵资源: -- 高频操作:占据固定空间(聊天框) -- 低频操作:按需展示(用量信息) - -### 4. **Visual Hierarchy(视觉层次)** -清晰的信息层级: -- 第一层:聊天对话 -- 第二层:工具栏(模型选择、用量说明) -- 第三层:模态框(详细信息) - ---- - -## ✅ 验证清单 - -### 功能验证 -- [ ] 重启Frontend服务 -- [ ] 进入全文阅读模式 -- [ ] 检查聊天框是否占据大部分空间 -- [ ] 检查顶部是否有"用量说明"按钮 -- [ ] 点击按钮,检查模态框是否正常弹出 -- [ ] 检查模态框内容是否完整(容量指示器、文献列表、使用提示) -- [ ] 检查文献列表是否可以滚动 -- [ ] 关闭模态框,检查是否正常关闭 -- [ ] 发送消息,检查对话功能是否正常 - -### 视觉验证 -- [ ] 聊天区域是否足够大 -- [ ] 按钮位置是否合理 -- [ ] 模态框设计是否美观 -- [ ] 文献列表是否易读 -- [ ] 滚动条样式是否美观 - -### 交互验证 -- [ ] 按钮点击是否响应 -- [ ] 模态框动画是否流畅 -- [ ] 关闭模态框的方式是否多样(点击遮罩、关闭按钮) -- [ ] Tooltip是否正常显示 - ---- - -## 📂 修改文件清单 - -### 新建文件(2个) -1. ✅ `frontend/src/components/chat/UsageInfoModal.tsx` - 模态框组件 -2. ✅ `frontend/src/components/chat/UsageInfoModal.css` - 模态框样式 - -### 修改文件(3个) -3. ✅ `frontend/src/components/chat/FullTextMode.tsx` - 移除Header -4. ✅ `frontend/src/components/chat/FullTextMode.css` - 添加empty-hint样式 -5. ✅ `frontend/src/pages/ChatPage.tsx` - 添加按钮和模态框逻辑 - -### 保留文件(不删除) -- ⚠️ `frontend/src/components/chat/FullTextModeHeader.tsx` - 保留,可能用于其他地方 -- ⚠️ `frontend/src/components/chat/FullTextModeHeader.css` - 保留,避免引用错误 - ---- - -## 💡 后续优化建议 - -### 短期优化 -1. **添加快捷键**:`Ctrl + I` 快速打开用量说明 -2. **记忆用户偏好**:第一次使用时自动弹出提示 -3. **添加关闭提示**:在模态框中添加"不再显示"选项 - -### 中期优化 -1. **逐篇精读模式同步**:考虑为逐篇精读模式也添加类似设计 -2. **容量预警**:当接近限制时,主动提示用户 -3. **文献筛选**:在模态框中添加"移除此文献"功能 - -### 长期优化 -1. **智能推荐**:基于Token使用情况,推荐最优文献组合 -2. **用量历史**:展示历史对话的Token消耗 -3. **自定义容量**:允许用户自定义Token和文件数限制 - ---- - -## 🎉 总结 - -### 核心改进 -✅ **聊天区域扩大2.5倍**,从~30vh → ~85vh -✅ **信息层级更清晰**,核心功能优先 -✅ **用户体验提升**,符合现代UI标准 -✅ **代码结构优化**,组件职责单一 - -### 用户价值 -👍 **更专注的对话体验** -👍 **更高效的空间利用** -👍 **更专业的视觉呈现** -👍 **更灵活的信息获取** - -### 技术价值 -⭐ **组件复用性好**:UsageInfoModal可以被其他模式复用 -⭐ **易于维护**:FullTextMode组件更简洁 -⭐ **扩展性强**:模态框可以添加更多功能 - ---- - -**优化完成时间**:2025-10-13 -**状态**:✅ 已完成,等待用户验证 - ---- - -## 🚀 下一步 - -**用户行动**: -1. 重启Frontend服务 -2. 测试全文阅读模式的新UI -3. 提供反馈 - -**如果用户满意**: -- 继续进行其他功能测试 -- 考虑将类似设计应用到逐篇精读模式 - -**如果需要调整**: -- 根据反馈快速迭代 -- 记录用户新的需求和建议 - - - - - - - - - diff --git a/Phase2-全文阅读模式-真实实现.md b/Phase2-全文阅读模式-真实实现.md deleted file mode 100644 index 6f35b12f..00000000 --- a/Phase2-全文阅读模式-真实实现.md +++ /dev/null @@ -1,510 +0,0 @@ -# Phase 2 全文阅读模式 - 真实实现说明 - -**实现时间**:2025-10-13 -**触发原因**:用户测试发现实现偏差 -**实现人员**:AI助手 - ---- - -## 🎯 问题发现 - -### 用户反馈 -> "我感觉在全文阅读模式下,好像也是Dify下的知识库RAG,而不是全部7篇文献的全部文本。" - -### 问题验证 - -**✅ 用户的感觉完全正确!** - -通过代码审查发现: - -| 项目 | Phase 2 设计意图 | 之前的实际实现 | 偏差程度 | -|------|----------------|-------------|---------| -| **数据来源** | 全文(Full Text) | Dify RAG检索片段 | 🔴 严重 | -| **传输内容** | 所有选中文献的完整文本(~750K tokens) | 15个检索结果片段(几千tokens) | 🔴 严重 | -| **工作方式** | 广度优先,全局视野 | RAG检索,局部片段 | 🔴 严重 | -| **核心价值** | 解决"中间文本不敏感"问题 | 问题依然存在 | 🔴 失效 | - ---- - -## 🔧 解决方案(方案B) - -### 设计原则 - -1. ✅ **符合原始设计**:真正实现"全文阅读" -2. ✅ **充分利用已有功能**:文档提取、Token计数、智能选择 -3. ✅ **Qwen-Long作为默认模型**:支持1M上下文 -4. ✅ **文件名标记来源**:通过【文献N:文件名】区分 -5. ✅ **全文组装**:将所有选中文献的extractedText拼接 - ---- - -## 📝 实现细节 - -### 1. 后端修改(`chatController.ts`) - -#### 1.1 添加fullTextDocumentIds参数 - -```typescript -interface SendChatMessageBody { - content: string; - modelType: ModelType; - knowledgeBaseIds?: string[]; - documentIds?: string[]; // 逐篇精读 - RAG检索过滤 - fullTextDocumentIds?: string[]; // 全文阅读 - 传递完整全文 ✅ 新增 - conversationId?: string; -} -``` - -#### 1.2 全文加载逻辑 - -```typescript -// Phase 2: 全文阅读模式 - 传递完整文献全文 -if (fullTextDocumentIds && fullTextDocumentIds.length > 0) { - console.log('📚 [ChatController] 全文阅读模式 - 加载文献全文'); - - // 1. 获取所有选中文档的全文 - const documents = await prisma.document.findMany({ - where: { id: { in: fullTextDocumentIds } }, - select: { - id: true, - filename: true, - extractedText: true, // ✅ 关键:使用提取的全文 - tokensCount: true, - }, - orderBy: { filename: 'asc' }, - }); - - // 2. 组装全文上下文 - const fullTextParts: string[] = []; - - for (let i = 0; i < documents.length; i++) { - const doc = documents[i]; - const docNumber = i + 1; - - // 为每篇文献添加引用信息 - allCitations.push({ - id: docNumber, - fileName: doc.filename, // ✅ 文件名标记 - position: 0, - score: 1.0, // 全文模式相关度100% - content: doc.extractedText?.substring(0, 200) || '', - }); - - // 格式:【文献N:文件名】\n全文内容 - fullTextParts.push( - `【文献${docNumber}:${doc.filename}】\n\n${doc.extractedText || '(该文献无可用文本)'}` - ); - } - - knowledgeBaseContext = fullTextParts.join('\n\n---\n\n'); - - console.log(`📚 [ChatController] 全文上下文已组装`, { - totalDocuments: documents.length, - totalCharacters: knowledgeBaseContext.length, - totalTokens: documents.reduce((sum, doc) => sum + (doc.tokensCount || 0), 0), - }); -} -// RAG检索模式(逐篇精读或通用对话) -else if (knowledgeBaseIds && knowledgeBaseIds.length > 0) { - // 原有的RAG检索逻辑 - ... -} -``` - -#### 1.3 优化系统提示词 - -```typescript -// 全文阅读模式的系统提示 -if (fullTextDocumentIds && fullTextDocumentIds.length > 0) { - systemPrompt = '你是一个专业的学术文献分析助手。用户会提供多篇文献的完整全文,每篇文献用【文献N:文件名】标记。请认真阅读所有文献,进行深入的综合分析。在回答时请引用具体文献,使用【文献N】格式。你的优势是能够看到所有文献的全貌,进行跨文献的比较、归纳和总结。'; -} -``` - -#### 1.4 优化用户消息提示 - -```typescript -// 全文阅读模式的提示 -if (fullTextDocumentIds && fullTextDocumentIds.length > 0) { - userContent = `${content}\n\n## 参考资料(文献全文)\n\n**重要提示**:下面提供的是完整的文献全文。每篇文献用【文献N:文件名】标记。请在回答时引用文献,格式如"根据【文献1】..."或"研究表明【文献2】【文献3】..."。你可以综合分析所有文献,进行跨文献的比较和总结。\n\n${knowledgeBaseContext}`; -} -``` - ---- - -### 2. 前端修改 - -#### 2.1 API接口更新(`chatApi.ts`) - -```typescript -export interface SendChatMessageData { - content: string - modelType: string - knowledgeBaseIds?: string[] - documentIds?: string[] // 逐篇精读 - RAG检索 - fullTextDocumentIds?: string[] // 全文阅读 - 完整全文 ✅ 新增 - conversationId?: string -} -``` - -#### 2.2 自动切换模型(`ChatPage.tsx`) - -```typescript -// 监听模式变化,自动切换默认模型 -useEffect(() => { - // 全文阅读模式默认使用Qwen-Long(需要1M上下文) - if (modeState.baseMode === 'knowledge_base' && modeState.kbMode === 'full_text') { - if (selectedModel !== 'qwen-long') { - setSelectedModel('qwen-long') - antdMessage.info('已自动切换到Qwen-Long模型(支持1M上下文)', 3) - } - } -}, [modeState.baseMode, modeState.kbMode, selectedModel]) -``` - -#### 2.3 传递全文文档ID(`ChatPage.tsx`) - -```typescript -// 判断是否是全文阅读模式 -const isFullTextMode = modeState.baseMode === 'knowledge_base' && modeState.kbMode === 'full_text' -const fullTextDocIds = isFullTextMode && modeState.fullTextState?.loadedDocs - ? modeState.fullTextState.loadedDocs.map(doc => doc.id) - : undefined - -console.log('📤 [ChatPage] 发送消息', { - mode: isFullTextMode ? '全文阅读' : '通用/RAG', - fullTextDocCount: fullTextDocIds?.length || 0, -}) - -await chatApi.sendMessageStream({ - content, - modelType: selectedModel, - knowledgeBaseIds, - fullTextDocumentIds: fullTextDocIds, // ✅ 传递文档ID列表 - conversationId: currentConversationId, -}, ...) -``` - ---- - -## 🎯 三个关键要求的实现 - -### 要求1:默认选择Qwen-Long模型 ✅ - -**实现**: -- 使用`useEffect`监听模式变化 -- 当进入全文阅读模式时自动切换到`qwen-long` -- 显示提示信息:"已自动切换到Qwen-Long模型(支持1M上下文)" - -**原因**: -- Qwen-Long支持1M上下文 -- 全文模式需要传输~750K tokens -- 需要充足的对话空间(~250K) - ---- - -### 要求2:组装全文时包含文件名 ✅ - -**实现**: -```typescript -// 格式:【文献N:文件名】\n全文内容 -fullTextParts.push( - `【文献${docNumber}:${doc.filename}】\n\n${doc.extractedText}` -); -``` - -**示例输出**: -``` -【文献1:糖尿病治疗研究.pdf】 - -[文献1的完整文本内容...] - ---- - -【文献2:胰岛素疗法综述.pdf】 - -[文献2的完整文本内容...] - ---- - -【文献3:血糖监测方法.pdf】 - -[文献3的完整文本内容...] -``` - -**优势**: -- 文件名清晰标记每篇文献 -- AI可以自然引用:「根据【文献1:糖尿病治疗研究.pdf】...」 -- 用户可以快速识别来源 - ---- - -### 要求3:文献来源通过文件名标记 ✅ - -**实现**: -```typescript -// 为每篇文献添加引用信息 -allCitations.push({ - id: docNumber, - fileName: doc.filename, // ✅ 文件名作为来源标识 - position: 0, // 全文没有段落位置 - score: 1.0, // 全文模式相关度100% - content: doc.extractedText?.substring(0, 200) || '', -}); -``` - -**效果**: -- 文献来源列表显示: - ``` - 📚 参考文献 - - 【文献1】📄 糖尿病治疗研究.pdf - 全文 (相关度100%) - "糖尿病是一种代谢性疾病,主要特征是血糖升高..." - - 【文献2】📄 胰岛素疗法综述.pdf - 全文 (相关度100%) - "胰岛素治疗是1型糖尿病的主要治疗方法..." - - 【文献3】📄 血糖监测方法.pdf - 全文 (相关度100%) - "持续血糖监测(CGM)是一种新型的血糖监测技术..." - ``` - ---- - -## 📊 实现效果对比 - -### 之前(RAG模式) - -| 维度 | 值 | -|------|-----| -| 数据来源 | Dify RAG检索 | -| 传输内容 | 15个片段 | -| Token使用 | ~5-10K | -| 覆盖率 | 局部片段 | -| 准确性 | 中等(可能遗漏) | -| 适用场景 | 快速查找 | - -### 现在(全文模式) - -| 维度 | 值 | -|------|-----| -| 数据来源 | 数据库extractedText字段 | -| 传输内容 | 35-50篇文献完整全文 | -| Token使用 | ~750K(真实全文) | -| 覆盖率 | 100%文献内容 | -| 准确性 | 高(无遗漏) | -| 适用场景 | 文献综述、深度分析 | - ---- - -## 🔬 技术细节 - -### Token使用计算 - -**假设场景**:知识库有10篇文献 - -``` -文献1: 15,000 tokens -文献2: 23,000 tokens -文献3: 18,000 tokens -文献4: 32,000 tokens -文献5: 21,000 tokens -文献6: 19,000 tokens -文献7: 28,000 tokens -文献8: 16,000 tokens -文献9: 25,000 tokens -文献10: 20,000 tokens - -总计: 217,000 tokens(文献内容) -+ 系统提示词: ~200 tokens -+ 用户消息: ~100 tokens -+ 引用清单: ~1,000 tokens ------------------------------------ -上下文总计: ~218,300 tokens - -对话空间剩余: 1,000,000 - 218,300 = ~781,700 tokens -``` - -**✅ 充足的对话空间!** - ---- - -### 文献来源标记格式 - -**传递给AI的格式**: -``` -【文献1:filename.pdf】 - -完整的文献内容... - ---- - -【文献2:another.pdf】 - -完整的文献内容... -``` - -**AI自然引用示例**: -``` -根据【文献1】的研究结果,糖尿病患者的血糖控制... -研究表明【文献2】【文献3】都使用了相似的实验方法... -``` - -**引用清单格式**(自动添加): -``` -📚 参考文献 - -【文献1】📄 filename.pdf - 全文 (相关度100%) - "(前200字符预览)" - -【文献2】📄 another.pdf - 全文 (相关度100%) - "(前200字符预览)" -``` - ---- - -## 🚀 使用流程 - -### 1. 进入全文阅读模式 -- 智能问答 → 知识库模式 → 选择知识库 -- 选择"全文阅读"模式 -- 系统自动加载选中的文献 -- **自动切换到Qwen-Long模型** ⭐ - -### 2. 后台处理 -- Frontend传递`fullTextDocumentIds`数组 -- Backend查询数据库获取`extractedText` -- 组装格式:【文献N:文件名】\n全文内容 -- 传递给Qwen-Long(上下文:~750K + 对话空间:~250K) - -### 3. AI分析 -- AI看到所有文献的完整内容 -- 可以进行跨文献的综合分析 -- 自动使用【文献N】格式引用 -- 不会遗漏任何重要信息 - -### 4. 显示结果 -- AI回答包含【文献N】引用 -- 底部自动显示引用清单 -- 每个引用显示文件名和预览 - ---- - -## 🎯 核心优势 - -### 1. 真正的全局视野 -- ✅ AI能看到所有文献的完整内容 -- ✅ 不受RAG检索算法限制 -- ✅ 不会遗漏重要信息 - -### 2. 深度综合分析 -- ✅ 跨文献比较 -- ✅ 趋势总结 -- ✅ 研究方法归纳 -- ✅ 发现文献之间的关联 - -### 3. 准确的引用 -- ✅ 基于文件名的明确引用 -- ✅ 100%相关度(全文) -- ✅ 用户易于理解和验证 - -### 4. 充足的对话空间 -- ✅ Qwen-Long 1M上下文 -- ✅ ~250K tokens对话空间 -- ✅ 支持多轮深入对话 - ---- - -## ⚠️ 注意事项 - -### 1. 模型选择 -- **必须使用Qwen-Long** -- DeepSeek-V3只支持64K上下文(不够用) -- 其他模型也不支持1M上下文 - -### 2. 成本考虑 -- 全文模式使用~750K tokens(输入) -- Qwen-Long定价:¥0.0005/千tokens(输入) -- 每次对话成本:~¥0.375 -- 比RAG模式贵但价值更高 - -### 3. 响应时间 -- 全文传输需要更长时间 -- 首次响应可能需要5-10秒 -- 但分析质量显著提升 - -### 4. 文档质量 -- 依赖extractedText的质量 -- 确保文档提取服务正常工作 -- 检查tokensCount准确性 - ---- - -## 📋 验证清单 - -### Backend验证 -- [ ] fullTextDocumentIds参数正确接收 -- [ ] document表中extractedText字段有数据 -- [ ] 全文组装格式正确(【文献N:文件名】) -- [ ] 引用清单生成正确 -- [ ] 日志输出完整 - -### Frontend验证 -- [ ] 进入全文阅读模式时自动切换到qwen-long -- [ ] fullTextDocumentIds正确传递 -- [ ] loadedDocs数据正确 -- [ ] 控制台日志显示"全文阅读"模式 - -### 功能验证 -- [ ] AI回答基于完整文献 -- [ ] 引用使用【文献N】格式 -- [ ] 文献来源列表显示文件名 -- [ ] 可以进行跨文献综合分析 -- [ ] 不会遗漏重要信息 - ---- - -## 🎉 总结 - -**现在的全文阅读模式是真正的"全文阅读":** - -1. ✅ 传递完整的文献全文(~750K tokens) -2. ✅ 使用Qwen-Long 1M上下文模型 -3. ✅ 文件名清晰标记每篇文献 -4. ✅ AI可以综合分析所有文献 -5. ✅ 充足的对话空间(~250K) -6. ✅ 准确的文献引用和来源标记 - -**与设计意图完全一致,真正解决了"大模型中间文本不敏感"问题!** 🚀 - ---- - -**实现完成时间**:2025-10-13 -**状态**:✅ 已完成,等待测试验证 - ---- - -## 📞 测试建议 - -1. **重启Backend和Frontend服务** -2. **进入全文阅读模式** -3. **验证Qwen-Long自动选择** -4. **提问测试**: - - "这些文献的主要研究方向是什么?" - - "比较这些文献的研究方法" - - "总结所有文献的主要结论" -5. **检查引用格式**:是否使用【文献N:文件名】 -6. **验证全文分析**:AI是否能够看到并分析所有文献内容 - -预期结果: -- ✅ AI回答更全面、准确 -- ✅ 能够进行真正的跨文献综合分析 -- ✅ 不会遗漏重要信息 -- ✅ 引用清晰、易于验证 - - - - - - - - - diff --git a/Phase2-快速测试清单.md b/Phase2-快速测试清单.md deleted file mode 100644 index 534eabea..00000000 --- a/Phase2-快速测试清单.md +++ /dev/null @@ -1,238 +0,0 @@ -# ⚡ Phase 2 快速测试清单(30分钟版) - -**目标**:快速验证Phase 2核心功能是否正常工作 -**适用场景**:开发后的快速验收 -**详细版本**:[Phase2-测试指南.md](./Phase2-测试指南.md) - ---- - -## 🚀 立即开始(3步) - -### Step 1: 启动服务(2分钟) - -```bash -cd D:\MyCursor\AIclinicalresearch -.\一键启动.bat -``` - -**等待**:30秒,看到3个窗口打开 -**验证**:自动打开 http://localhost:3000 - ---- - -### Step 2: 准备测试数据(10分钟) - -1. **访问知识库**:http://localhost:3000/knowledge - -2. **创建知识库** - - 名称:`快速测试库` - - 点击确定 - -3. **上传5个文档** - - 2个PDF(1个中文、1个英文) - - 1个Docx - - 2个Txt - -4. **等待处理**:每个约30秒 - - ✅ 确认全部显示"已就绪" - ---- - -### Step 3: 核心功能测试(15分钟) - -#### ✅ 测试A:全文阅读模式(5分钟) - -1. 访问:http://localhost:3000/chat -2. 选择:`知识库模式` → `快速测试库` → `全文阅读` -3. **验证**:看到容量指示器(5/50篇,XXK/980K) -4. **提问**:"请总结这些文献的主要内容" -5. **验证**: - - [ ] AI能提到多篇文献 - - [ ] 有引用标记`[来源1]`等 - - [ ] 点击引用能跳转 - ---- - -#### ✅ 测试B:逐篇精读模式(10分钟) - -1. 切换模式:`逐篇精读` -2. **验证**:弹出文献选择器 -3. 选择3篇文献 → 确认 -4. **验证**:看到文献切换器(3个标签) - -**精读测试**: -``` -问题1: 这篇文献的主要内容是什么? -问题2(追问): 研究方法是什么? -``` -- [ ] 回答详细准确 -- [ ] 记住上下文 - -**切换测试**: -- 切换到第2篇文献 -- [ ] 对话历史清空(新文献) -- 再切回第1篇 -- [ ] 能看到之前的对话历史 - ---- - -## ✅ 快速验收清单 - -### 核心功能(10项) - -- [ ] 1. Python微服务正常启动(8000端口) -- [ ] 2. Backend正常启动(3001端口) -- [ ] 3. Frontend正常访问(3000端口) -- [ ] 4. 文档上传成功 -- [ ] 5. 文档处理完成(显示已就绪) -- [ ] 6. 容量指示器显示正确 -- [ ] 7. 全文阅读模式可用 -- [ ] 8. 文献选择器弹出 -- [ ] 9. 逐篇精读模式可用 -- [ ] 10. 文献切换正常 - -### 关键指标(5项) - -- [ ] AI回答包含多篇文献(全文模式) -- [ ] 有清晰的引用标记 -- [ ] 逐篇精读回答详细 -- [ ] 对话历史正确保存 -- [ ] 文献切换流畅(<1秒) - ---- - -## 🐛 快速问题排查 - -### 问题1:服务启动失败 - -**检查**: -```bash -# 检查Docker -docker ps - -# 检查端口占用 -netstat -ano | findstr "3000" -netstat -ano | findstr "3001" -netstat -ano | findstr "8000" -``` - -**解决**: -- 重启Docker Desktop -- 关闭占用端口的进程 -- 手动启动各服务 - ---- - -### 问题2:文档一直"处理中" - -**检查**: -```bash -# 检查Python微服务 -http://localhost:8000/api/health - -# 应该返回: -{ - "status": "healthy", - "nougat_available": true, - "pymupdf_version": "1.23.8" -} -``` - -**解决**: -- 重启Python微服务 -- 检查extraction_service日志 -- 删除文档重新上传 - ---- - -### 问题3:容量显示不对 - -**解决**: -- 刷新页面 -- 检查Backend API: - ```bash - http://localhost:3001/api/v1/knowledge-bases/{kbId}/document-selection - ``` -- 查看返回数据是否正确 - ---- - -### 问题4:文献选择器空白 - -**检查**: -- F12打开Console,查看错误 -- 确认知识库中有"已就绪"的文档 -- 刷新页面重试 - ---- - -### 问题5:文献切换历史丢失 - -**临时解决**: -- 刷新页面 -- 重新进入精读模式 - -**永久解决**: -- 查看Console错误 -- 检查React状态管理 - ---- - -## 📊 测试结果记录 - -### 服务状态 -- Python微服务:✅ 正常 / ❌ 异常 -- Backend:✅ 正常 / ❌ 异常 -- Frontend:✅ 正常 / ❌ 异常 - -### 功能测试 -- 全文阅读模式:✅ 通过 / ⚠️ 有问题 / ❌ 失败 -- 逐篇精读模式:✅ 通过 / ⚠️ 有问题 / ❌ 失败 - -### 发现的问题 -1. _____________________________________________ -2. _____________________________________________ -3. _____________________________________________ - -### 最终结论 -- [ ] ✅ **验收通过** - 可以进入下一阶段 -- [ ] ⚠️ **有条件通过** - 修复小问题后可继续 -- [ ] ❌ **需要返工** - 有重大问题需要修复 - ---- - -## 🎯 下一步 - -### 如果验收通过 ✅ -1. **选择开发方向**: - - 方向A:Phase 3(批处理模式,1-2天) - - 方向B:里程碑2(11个智能体,2-3周) - -2. **准备工作**: - - 备份当前代码 - - 创建新分支 - - 更新任务清单 - -### 如果有问题需要修复 ⚠️ -1. 记录所有问题到 `Phase2-问题清单.md` -2. 按优先级排序 -3. 逐个修复并重新测试 - ---- - -**测试时间**:______ -**测试人员**:______ -**测试结果**:✅ / ⚠️ / ❌ - ---- - -**需要详细测试?** 查看 [Phase2-测试指南.md](./Phase2-测试指南.md)(90分钟完整版) - - - - - - - - - diff --git a/Phase2-测试指南.md b/Phase2-测试指南.md deleted file mode 100644 index e9e95ec7..00000000 --- a/Phase2-测试指南.md +++ /dev/null @@ -1,834 +0,0 @@ -# 🧪 Phase 2 完整测试指南 - 双模式智能问答系统 - -**创建时间**:2025-10-13 -**测试目标**:验证全文阅读模式和逐篇精读模式的完整功能 -**预计耗时**:60-90分钟 -**测试人员**:____ - ---- - -## 📋 测试清单总览 - -``` -☑️ 环境准备 (10分钟) -☑️ 服务启动验证 (5分钟) -☑️ 知识库准备 (15-20分钟) -☑️ 全文阅读模式测试 (15分钟) -☑️ 逐篇精读模式测试 (20分钟) -☑️ 端到端场景测试 (15分钟) -☑️ 问题记录与汇总 (5分钟) -``` - ---- - -## 🔧 Part 1: 环境准备(10分钟) - -### 1.1 检查系统要求 - -**必需服务**: -- [ ] Docker Desktop 已启动 -- [ ] Python 3.11+ 已安装(虚拟环境) -- [ ] Node.js 18+ 已安装 -- [ ] PostgreSQL容器运行中 -- [ ] Redis容器运行中 - -**检查命令**: -```bash -docker ps -# 应该看到:postgres、redis容器 - -python --version -# 应该看到:Python 3.11.x 或更高 - -node --version -# 应该看到:v18.x.x 或更高 -``` - ---- - -### 1.2 检查代码同步 - -**Phase 2关键文件**: -``` -✅ extraction_service/ # Python微服务 -✅ backend/src/clients/ExtractionClient.ts -✅ backend/src/services/tokenService.ts -✅ frontend/src/components/chat/FullTextMode.tsx -✅ frontend/src/components/chat/DeepReadMode.tsx -✅ frontend/src/components/chat/DocumentSelector.tsx -``` - -**验证方法**: -```bash -cd D:\MyCursor\AIclinicalresearch - -# 检查Python微服务 -dir extraction_service\main.py - -# 检查Backend文件 -dir backend\src\clients\ExtractionClient.ts - -# 检查Frontend组件 -dir frontend\src\components\chat\FullTextMode.tsx -``` - ---- - -## 🚀 Part 2: 服务启动验证(5分钟) - -### 2.1 一键启动(推荐) - -```bash -cd D:\MyCursor\AIclinicalresearch -.\一键启动.bat -``` - -**等待时间**:约30秒 - -**预期结果**: -- 窗口1:Python微服务(端口8000) -- 窗口2:Backend服务(端口3001) -- 窗口3:Frontend服务(端口3000) -- 自动打开浏览器:http://localhost:3000 - ---- - -### 2.2 手动启动(备选) - -**Terminal 1: Python微服务** -```bash -cd D:\MyCursor\AIclinicalresearch\extraction_service -.\start.bat -``` -✅ 看到:`Uvicorn running on http://0.0.0.0:8000` - -**Terminal 2: Backend** -```bash -cd D:\MyCursor\AIclinicalresearch\backend -npm run dev -``` -✅ 看到:`Server listening on http://0.0.0.0:3001` - -**Terminal 3: Frontend** -```bash -cd D:\MyCursor\AIclinicalresearch\frontend -npm run dev -``` -✅ 看到:`Local: http://localhost:3000/` - ---- - -### 2.3 服务健康检查 - -**Python微服务健康检查**: -```bash -# 浏览器访问 -http://localhost:8000/api/health - -# 预期返回 -{ - "status": "healthy", - "nougat_available": true, - "pymupdf_version": "1.23.8" -} -``` - -**Backend健康检查**: -```bash -http://localhost:3001/health - -# 预期返回 -{ - "status": "ok", - "timestamp": "2025-10-13T..." -} -``` - -**Frontend访问**: -```bash -http://localhost:3000 - -# 预期:能看到登录页面或主页 -``` - ---- - -## 📚 Part 3: 知识库准备(15-20分钟) - -### 3.1 准备测试文档 - -**推荐文档类型**: -1. **PDF文档**(3-5个) - - 英文学术论文(测试Nougat) - - 中文学术论文(测试PyMuPDF) - - 混合语言文档 - -2. **Docx文档**(1-2个) - - Word格式的研究报告 - -3. **Txt文档**(1个) - - 纯文本文献摘要 - -**文档大小建议**: -- PDF:每个5-20页(1-5MB) -- Docx:每个5-10页(<2MB) -- Txt:每个<1MB - ---- - -### 3.2 创建测试知识库 - -**步骤**: - -1. **访问知识库管理** - ``` - http://localhost:3000/knowledge - ``` - -2. **创建新知识库** - - 点击"创建知识库" - - 名称:`Phase2测试知识库` - - 描述:`用于测试双模式功能` - - 点击"确定" - -3. **上传文档** - - 进入新知识库 - - 点击"上传文档" - - 选择准备好的7-10个文档 - - **等待处理完成** - -**预期处理时间**: -| 文档类型 | 页数 | 预计时间 | -|---------|-----|---------| -| 中文PDF | 20页 | 30-60秒 | -| 英文PDF | 20页 | 60-90秒 | -| Docx | 10页 | 10-20秒 | -| Txt | 1MB | 5-10秒 | - ---- - -### 3.3 验证文档处理状态 - -**检查清单**: -- [ ] 所有文档显示状态:`✅ 已就绪` -- [ ] 每个文档有Token数显示 -- [ ] 每个文档有提取方法显示(pymupdf/nougat/mammoth) -- [ ] 没有文档显示"❌ 处理失败" - -**如果有失败文档**: -``` -查看文档详情 → 查看失败原因 → 根据错误重新上传或删除 -``` - -**验证文档信息**(点击文档查看详情): -``` -✅ extractedText: 有内容(不为空) -✅ charCount: > 0 -✅ tokensCount: > 0 -✅ extractionMethod: "pymupdf" | "nougat" | "mammoth" | "direct_read" -✅ language: "chinese" | "english" -``` - ---- - -## 🌍 Part 4: 全文阅读模式测试(15分钟) - -### 4.1 进入全文阅读模式 - -**步骤**: -1. 访问:`http://localhost:3000/chat` -2. 左侧栏选择:`⚫ 知识库模式` -3. 选择知识库:`Phase2测试知识库` -4. 选择工作模式:`🌍 全文阅读` - -**预期界面**: -``` -┌─────────────────────────────────────────────┐ -│ 🌍 全文阅读模式 │ -│ │ -│ 📊 容量使用情况 │ -│ ┌─────────────────────────────────────┐ │ -│ │ 文件数: 7 / 50 篇 │ │ -│ │ [████░░░░░░░░] 14% │ │ -│ │ │ │ -│ │ Token容量: 150K / 980K │ │ -│ │ [███░░░░░░░░░] 15% │ │ -│ └─────────────────────────────────────┘ │ -│ │ -│ 已加载文档(7篇): │ -│ • paper1.pdf (28K tokens) │ -│ • paper2.pdf (25K tokens) │ -│ • ... │ -└─────────────────────────────────────────────┘ -``` - ---- - -### 4.2 测试用例1:文献综述问题 - -**测试目标**:验证AI能否综合所有文献回答 - -**测试问题**: -``` -请总结这些文献的主要研究方向和核心观点。 -``` - -**预期结果**: -- ✅ AI能提到多篇文献(至少3篇以上) -- ✅ 回答包含不同文献的不同观点 -- ✅ 有明确的引用标记:`[来源1]`、`[来源2]`等 -- ✅ 点击引用标记能跳转到引用清单 - -**评分标准**(5分制): -- 5分:涵盖所有文献,综述全面 -- 4分:涵盖大部分文献(70%+) -- 3分:涵盖一半文献(50%+) -- 2分:只涵盖少数文献(<50%) -- 1分:基本没有综合 - -**记录结果**: -- 实际涵盖文献数:____/7 -- 评分:⭐⭐⭐⭐⭐ - ---- - -### 4.3 测试用例2:趋势分析 - -**测试问题**: -``` -从这些文献中,你能看出该领域的研究趋势是什么? -``` - -**预期结果**: -- ✅ 能识别跨文献的共同趋势 -- ✅ 能对比不同时期的研究重点 -- ✅ 分析有深度 - -**记录结果**: -- 趋势识别准确度:⭐⭐⭐⭐⭐ -- 分析深度:⭐⭐⭐⭐⭐ - ---- - -### 4.4 测试用例3:快速查找 - -**测试问题**: -``` -哪些文献提到了[某个关键词]?请列出文献名称和具体内容。 -``` - -**预期结果**: -- ✅ 能准确找到包含关键词的文献 -- ✅ 提供文献名称 -- ✅ 引用具体内容片段 - -**记录结果**: -- 查找准确率:⭐⭐⭐⭐⭐ - ---- - -### 4.5 容量显示测试 - -**测试目标**:验证容量指示器的准确性 - -**检查项**: -- [ ] 文件数显示正确(与实际上传数一致) -- [ ] Token数显示合理(与文档大小匹配) -- [ ] 进度条百分比正确 -- [ ] 颜色标识合理(<60%绿色,60-80%黄色,>80%红色) - -**记录**: -- 显示文件数:____ / 50 -- 显示Token数:____K / 980K -- 进度条颜色:🟢绿色 / 🟡黄色 / 🔴红色 - ---- - -## 🔍 Part 5: 逐篇精读模式测试(20分钟) - -### 5.1 进入逐篇精读模式 - -**步骤**: -1. 在智能问答页面 -2. 切换工作模式:`🔍 逐篇精读` -3. **预期**:弹出文献选择器 - ---- - -### 5.2 测试用例4:文献选择器 - -**功能检查**: - -**a) 显示所有可选文献** -- [ ] 能看到知识库中的所有文档 -- [ ] 每个文档显示:文件名、大小、Token数 - -**b) 多选功能** -- [ ] 可以勾选文献 -- [ ] 最多只能选择5篇 -- [ ] 选满5篇后其他变灰不可选 - -**c) 统计信息** -- [ ] 底部显示:`已选X篇` -- [ ] 底部显示:`共XXK tokens` -- [ ] 统计实时更新 - -**d) 确认选择** -- [ ] "确认选择"按钮可点击 -- [ ] 点击后弹窗关闭 -- [ ] 进入精读模式 - -**测试步骤**: -1. 选择3篇文献(不同类型) - - paper1.pdf(英文,Nougat提取) - - paper2.pdf(中文,PyMuPDF提取) - - report.docx(Word文档) - -2. 确认选择 - -**记录结果**: -- 选择器UI体验:⭐⭐⭐⭐⭐ -- 功能完整性:✅ 完整 / ⚠️ 有缺陷 / ❌ 不工作 - ---- - -### 5.3 测试用例5:逐篇精读界面 - -**预期界面**: -``` -┌─────────────────────────────────────────────┐ -│ 🔍 逐篇精读模式 │ -│ │ -│ 📄 文献切换器 │ -│ ┌───────┬───────┬───────┐ │ -│ │paper1 │paper2 │report │ (3篇) │ -│ │ 活跃 │ │ │ │ -│ └───────┴───────┴───────┘ │ -│ │ -│ 当前文献: paper1.pdf (28K tokens) │ -│ │ -│ [对话区域...] │ -└─────────────────────────────────────────────┘ -``` - -**检查项**: -- [ ] 显示所有选中的文献(3个标签) -- [ ] 当前文献高亮显示 -- [ ] 显示当前文献的Token数 -- [ ] 输入框提示包含当前文献名 - ---- - -### 5.4 测试用例6:精读对话 - -**测试目标**:验证AI专注于当前文献 - -**测试问题1**(针对paper1.pdf): -``` -这篇文献的研究方法是什么? -``` - -**预期结果**: -- ✅ AI回答专注于paper1.pdf -- ✅ 回答详细(有充足对话空间) -- ✅ 可以进行多轮追问 - -**测试问题2**(多轮对话): -``` -继续问:样本量是多少? -继续问:统计分析方法是什么? -继续问:研究的局限性有哪些? -``` - -**预期结果**: -- ✅ 每个问题都能得到详细回答 -- ✅ AI记得之前的对话上下文 -- ✅ 回答准确度高 - -**记录结果**: -- 回答准确度:⭐⭐⭐⭐⭐ -- 对话深度:⭐⭐⭐⭐⭐ -- 上下文记忆:✅ 正常 / ❌ 有问题 - ---- - -### 5.5 测试用例7:文献切换 - -**测试目标**:验证文献切换和对话历史独立性 - -**操作步骤**: - -1. **当前在paper1.pdf** - - 提问:"这篇文献的结论是什么?" - - 记录AI回答 - -2. **切换到paper2.pdf** - - 点击"paper2"标签 - - **验证**:对话区域清空,显示paper2的历史(为空) - - 提问:"这篇文献的研究对象是什么?" - - 记录AI回答 - -3. **切换回paper1.pdf** - - 点击"paper1"标签 - - **验证**:能看到之前的对话历史 - - 继续提问:"请继续详细说明。" - - **验证**:AI记得之前的上下文 - -**检查清单**: -- [ ] 文献切换无延迟 -- [ ] 对话历史正确保存 -- [ ] 对话历史正确加载 -- [ ] 上下文不混淆(不会把paper2的问题混入paper1) - -**记录结果**: -- 切换流畅度:⭐⭐⭐⭐⭐ -- 历史保存:✅ 正常 / ❌ 有问题 -- 上下文独立性:✅ 正常 / ❌ 混淆 - ---- - -### 5.6 测试用例8:对话空间验证 - -**测试目标**:验证逐篇模式有充足的对话空间 - -**操作**: -1. 选择1篇最大的文献(如30K tokens) -2. 进行10轮以上的深度对话 -3. 观察AI回答质量是否下降 - -**预期**: -- ✅ AI回答始终详细完整 -- ✅ 不会出现"上下文太长"错误 -- ✅ 对话空间充裕(800K+ tokens) - -**记录结果**: -- 对话轮数:____轮 -- 回答质量:⭐⭐⭐⭐⭐ -- 是否出错:✅ 无错误 / ❌ 出错 - ---- - -## 🎯 Part 6: 端到端场景测试(15分钟) - -### 6.1 场景1:文献综述(全文模式) - -**用户角色**:研究生,需要写文献综述 - -**操作流程**: -1. 上传10篇相关文献到知识库 -2. 选择"全文阅读模式" -3. 提问:"请总结这些文献的研究现状" -4. 提问:"有哪些研究空白?" -5. 提问:"未来研究方向是什么?" - -**验收标准**: -- ✅ 3个问题都能得到综合性回答 -- ✅ 回答涵盖多篇文献 -- ✅ 分析有深度 - -**记录**: -- 场景完成度:✅ 完成 / ⚠️ 部分完成 / ❌ 未完成 -- 用户体验:⭐⭐⭐⭐⭐ - ---- - -### 6.2 场景2:核心论文精读(精读模式) - -**用户角色**:科研人员,需要深度分析3篇核心论文 - -**操作流程**: -1. 选择"逐篇精读模式" -2. 选择3篇核心论文 -3. 对paper1进行5轮深度提问(方法、数据、结论、局限性、创新点) -4. 切换到paper2,继续深度分析 -5. 切换到paper3,继续深度分析 - -**验收标准**: -- ✅ 每篇论文都能进行多轮深度对话 -- ✅ 文献切换流畅 -- ✅ 对话历史正确保存 -- ✅ AI回答准确详细 - -**记录**: -- 场景完成度:✅ 完成 / ⚠️ 部分完成 / ❌ 未完成 -- 用户体验:⭐⭐⭐⭐⭐ - ---- - -### 6.3 场景3:模式切换 - -**测试目标**:验证两种模式可以自由切换 - -**操作流程**: -1. 全文模式 → 提问1个综合问题 -2. 切换到精读模式 → 选择2篇文献 -3. 精读模式 → 深度分析paper1 -4. 切换回全文模式 → 再问1个综合问题 -5. 验证历史记录是否独立 - -**验收标准**: -- ✅ 模式切换无错误 -- ✅ 两个模式的对话历史独立 -- ✅ 无数据丢失 - -**记录**: -- 切换成功率:____/4次 -- 数据一致性:✅ 正常 / ❌ 有问题 - ---- - -## 📝 Part 7: 问题记录与汇总(5分钟) - -### 7.1 功能完整性检查 - -| 功能模块 | 状态 | 备注 | -|---------|------|------| -| Python微服务启动 | ✅ / ❌ | | -| Backend服务启动 | ✅ / ❌ | | -| Frontend页面加载 | ✅ / ❌ | | -| 文档上传 | ✅ / ❌ | | -| 文档处理(PDF) | ✅ / ❌ | | -| 文档处理(Docx) | ✅ / ❌ | | -| 文档处理(Txt) | ✅ / ❌ | | -| Token计数 | ✅ / ❌ | | -| 容量指示器 | ✅ / ❌ | | -| 全文阅读模式 | ✅ / ❌ | | -| 文献选择器 | ✅ / ❌ | | -| 逐篇精读模式 | ✅ / ❌ | | -| 文献切换 | ✅ / ❌ | | -| 对话历史保存 | ✅ / ❌ | | - ---- - -### 7.2 发现的问题清单 - -**严重问题(🔴 阻断性)**: -1. _______________________________________________ -2. _______________________________________________ - -**中等问题(🟡 影响使用)**: -1. _______________________________________________ -2. _______________________________________________ - -**轻微问题(🟢 不影响主流程)**: -1. _______________________________________________ -2. _______________________________________________ - ---- - -### 7.3 性能测试记录 - -| 指标 | 预期 | 实际 | 是否达标 | -|------|------|------|---------| -| 页面加载时间 | <2秒 | ___秒 | ✅ / ❌ | -| 文献选择器打开 | <1秒 | ___秒 | ✅ / ❌ | -| 文献切换响应 | <0.5秒 | ___秒 | ✅ / ❌ | -| AI首字响应 | <3秒 | ___秒 | ✅ / ❌ | -| 文档上传(PDF 20页) | <60秒 | ___秒 | ✅ / ❌ | - ---- - -### 7.4 用户体验评分 - -**整体体验**:⭐⭐⭐⭐⭐ - -**分项评分**: -- 界面美观度:⭐⭐⭐⭐⭐ -- 操作流畅度:⭐⭐⭐⭐⭐ -- 功能易用性:⭐⭐⭐⭐⭐ -- 错误提示清晰度:⭐⭐⭐⭐⭐ - ---- - -### 7.5 最终结论 - -**Phase 2验收结果**: - -✅ **通过验收** - 可以进入下一阶段 -- 核心功能完整 -- 性能达标 -- 无阻断性问题 - -⚠️ **有条件通过** - 需要修复中等问题后再进入下一阶段 -- 核心功能基本完整 -- 有影响使用的问题 -- 需要优先修复 - -❌ **未通过验收** - 需要继续完善 -- 核心功能有缺失 -- 有阻断性问题 -- 无法正常使用 - -**测试人员签字**:________ -**测试日期**:________ -**建议**:________________________________________________ - ---- - -## 🔧 附录A:常见问题排查 - -### A.1 Python微服务无法启动 - -**症状**:start.bat运行后报错 - -**排查步骤**: -1. 检查虚拟环境 - ```bash - cd extraction_service - dir venv - ``` - -2. 重新安装依赖 - ```bash - cd extraction_service - .\install.bat - ``` - -3. 手动启动测试 - ```bash - cd extraction_service - venv\Scripts\activate - python main.py - ``` - ---- - -### A.2 文档上传后一直"处理中" - -**症状**:文档上传后长时间不变为"已就绪" - -**排查步骤**: -1. 检查Python微服务是否运行 - ```bash - http://localhost:8000/api/health - ``` - -2. 查看Backend日志 - - 找到Backend的Terminal窗口 - - 查看是否有错误日志 - -3. 查看Python微服务日志 - - 找到Python微服务的Terminal窗口 - - 查看处理进度 - -4. 手动测试提取 - ```bash - http://localhost:8000/docs - # 使用Swagger UI测试 /api/extract 接口 - ``` - ---- - -### A.3 容量显示不准确 - -**症状**:容量指示器数字不对 - -**排查步骤**: -1. 刷新页面 -2. 检查Backend API - ```bash - http://localhost:3001/api/v1/knowledge-bases/{kbId}/document-selection - ``` -3. 检查数据库数据 - - 使用Prisma Studio查看documents表 - - 验证tokensCount字段 - ---- - -### A.4 文献选择器空白 - -**症状**:点击"逐篇精读"后弹窗空白 - -**排查步骤**: -1. 打开浏览器Console(F12) -2. 查看是否有JavaScript错误 -3. 检查Backend API - ```bash - http://localhost:3001/api/v1/knowledge-bases/{kbId}/documents - ``` -4. 确认知识库中有已完成的文档 - ---- - -### A.5 文献切换后对话历史丢失 - -**症状**:切换文献后看不到之前的对话 - -**排查步骤**: -1. 打开浏览器Console -2. 查看是否有React状态错误 -3. 检查LocalStorage/SessionStorage -4. 尝试刷新页面重新进入 - ---- - -## 🔧 附录B:测试数据准备建议 - -### B.1 推荐测试文档 - -**英文PDF(Nougat测试)**: -- 学术论文,包含表格和公式 -- 大小:5-20页 -- 示例:Nature、Science期刊论文 - -**中文PDF(PyMuPDF测试)**: -- 中文学术论文 -- 大小:10-30页 -- 示例:中文核心期刊论文 - -**Word文档(Mammoth测试)**: -- 研究报告或综述 -- 大小:5-10页 -- 包含段落、列表、表格 - -**文本文件(编码测试)**: -- 纯文本文献摘要 -- 大小:<1MB -- 测试UTF-8、GBK编码 - ---- - -### B.2 测试场景示例 - -**场景:阿尔兹海默病研究** -- 上传7-10篇相关论文 -- 测试问题: - - "这些文献的主要研究方向是什么?" - - "有哪些治疗方法被提到?" - - "样本量分布是怎样的?" - -**场景:COVID-19研究** -- 上传8-12篇相关论文 -- 测试问题: - - "这些文献发表在哪些期刊?" - - "研究方法有哪些?" - - "主要发现是什么?" - ---- - -## 📞 联系与反馈 - -**测试问题反馈**: -- 记录在本文档的"问题清单"部分 -- 截图关键错误信息 -- 记录复现步骤 - -**测试完成后**: -- 保存此文档 -- 整理测试结果 -- 准备与开发团队Review - ---- - -**祝测试顺利!** 🎉 - -如有任何问题,随时记录在"问题清单"中。 - - - - - - - - - diff --git a/Phase2-问题9-Token限制与超时修复.md b/Phase2-问题9-Token限制与超时修复.md deleted file mode 100644 index 3bd523a1..00000000 --- a/Phase2-问题9-Token限制与超时修复.md +++ /dev/null @@ -1,433 +0,0 @@ -# Phase 2 问题9 - Token限制与超时修复 - -**发现时间**:2025-10-13 -**严重等级**:🔴 极严重(导致功能完全无法使用) -**状态**:✅ 已修复 - ---- - -## 🔍 问题现象 - -### 用户报告 - -**场景**:智能问答-知识库模式-全文阅读,选中7篇文献 - -**症状**: -1. AI回答输出了一部分内容后**卡在中间不动** -2. 前端控制台报错: - ``` - [vite] http proxy error: /api/v1/chat/stream - Error: read ECONNRESET - ``` - -**初步分析**:看起来像超时问题 - ---- - -## 💡 用户的关键质疑 - -> "你确定解决超时问题就能解决卡死的问题吗?这是本质问题吗?**会不会Token超出大模型上下文了?**" - -**这个质疑非常关键!** 用户一针见血地指出了问题的本质。 - ---- - -## 🎯 问题根源(深度分析) - -### 问题1:输出Token限制过小 🔴 - -**代码中的致命缺陷**: -```typescript -for await (const chunk of adapter.chatStream(messages, { - temperature: 0.7, - maxTokens: 2000, // ❌ 只允许输出2000个tokens! -})) { -``` - -**为什么这是问题**: -- 全文阅读模式需要对**多篇文献进行综合分析** -- 典型的回答需要: - - 引言和概述:~200 tokens - - 每篇文献分析:~300-500 tokens × 7篇 = 2100-3500 tokens - - 综合对比和总结:~500-1000 tokens - - 引用清单:~200-500 tokens - - **总计:3000-5000+ tokens** - -**实际效果**: -- AI正在生成内容 -- 达到2000 tokens时被**强制截断** -- 用户看到回答"卡在中间" -- 可能触发连接重置(ECONNRESET) - ---- - -### 问题2:未检查输入Token总数 🔴 - -**缺失的保护逻辑**: -- 7篇文献的总Token数可能达到**几十万甚至上百万** -- 没有检查是否超出Qwen-Long的1M输入限制 -- 如果超限: - - API调用失败 - - 模型无法处理 - - 连接被异常终止 - -**Qwen-Long的限制**: -- **输入上下文**:1,000,000 tokens(1M) -- **输出tokens**:通常6000-8000 tokens -- **总计**:~1,006,000 tokens - -**风险场景**: -``` -文献1: 150,000 tokens -文献2: 120,000 tokens -文献3: 180,000 tokens -文献4: 140,000 tokens -文献5: 160,000 tokens -文献6: 130,000 tokens -文献7: 150,000 tokens -------------------------------- -总计: 1,030,000 tokens ❌ 超出限制! - -+ 系统提示: ~500 tokens -+ 用户消息: ~200 tokens -+ 格式标记: ~5,000 tokens -------------------------------- -实际输入: 1,035,700 tokens ❌❌ 严重超限! -``` - ---- - -### 问题3:超时配置不足 🟡 - -**次要问题**(但也需要修复): -- Qwen-Long处理大量输入需要更长时间 -- 默认60秒超时对于全文模式不够 -- 需要增加到300秒(5分钟) - ---- - -## 🔧 修复方案 - -### 修复1:增加输出Token限制 ✅ - -**文件**:`backend/src/controllers/chatController.ts`(第422-436行) - -```typescript -// Phase 2: 全文阅读模式需要更大的输出空间(用于综合分析、引用等) -const maxOutputTokens = fullTextDocumentIds && fullTextDocumentIds.length > 0 - ? 6000 // 全文模式:需要更长的回答空间 ✅ - : 2000; // 其他模式:常规长度 - -console.log(`🤖 [ChatController] 开始调用LLM`, { - model: modelType, - maxOutputTokens, - mode: fullTextDocumentIds && fullTextDocumentIds.length > 0 ? '全文阅读' : '其他', -}); - -for await (const chunk of adapter.chatStream(messages, { - temperature: 0.7, - maxTokens: maxOutputTokens, // ✅ 动态设置 -})) { -``` - -**效果**: -- 全文模式:6000 tokens输出空间 -- 足够进行深入的综合分析 -- 不会被强制截断 - ---- - -### 修复2:添加输入Token检查 ✅ - -**文件**:`backend/src/controllers/chatController.ts`(第214-236行) - -```typescript -// ⚠️ 检查Token限制(Qwen-Long输入限制:1M tokens) -const QWEN_LONG_INPUT_LIMIT = 1000000; -const SYSTEM_OVERHEAD = 10000; // 系统提示、格式等开销 -const SAFE_INPUT_LIMIT = QWEN_LONG_INPUT_LIMIT - SYSTEM_OVERHEAD; - -if (totalTokens > SAFE_INPUT_LIMIT) { - const errorMsg = `输入Token数量 (${totalTokens}) 超出Qwen-Long模型限制 (${SAFE_INPUT_LIMIT})。请减少文献数量后重试。`; - console.error(`❌ [ChatController] ${errorMsg}`); - - // 返回错误信息给前端 ✅ - reply.raw.write(`data: ${JSON.stringify({ - content: `\n\n⚠️ **Token数量超限**\n\n${errorMsg}\n\n**建议**:\n- 当前选中 ${validDocuments.length} 篇文献,共 ${totalTokens.toLocaleString()} tokens\n- 请减少到 ${Math.floor(validDocuments.length * SAFE_INPUT_LIMIT / totalTokens)} 篇以内\n- 或使用"逐篇精读"模式深入分析单篇文献`, - role: 'assistant', - error: true, - })}\n\n`); - reply.raw.write('data: [DONE]\n\n'); - return reply.raw.end(); -} - -// 警告:如果接近限制 -if (totalTokens > SAFE_INPUT_LIMIT * 0.8) { - console.warn(`⚠️ [ChatController] Token数量接近限制 (${totalTokens}/${SAFE_INPUT_LIMIT}), 建议减少文献数量`); -} -``` - -**保护机制**: -- 超出990K tokens(安全限制):**拒绝请求**,返回友好错误提示 -- 超过792K tokens(80%):**警告日志**,但允许继续 -- 提供具体建议:减少到多少篇文献 - ---- - -### 修复3:过滤无效文档 ✅ - -**文件**:`backend/src/controllers/chatController.ts`(第172-179行) - -```typescript -// 过滤掉没有extractedText的文档 -const validDocuments = documents.filter(doc => doc.extractedText && doc.extractedText.trim().length > 0); - -if (validDocuments.length === 0) { - console.warn('⚠️ [ChatController] 所有文档都没有提取文本,无法使用全文模式'); -} else if (validDocuments.length < documents.length) { - console.warn(`⚠️ [ChatController] ${documents.length - validDocuments.length} 篇文档没有提取文本,已跳过`); -} -``` - -**保护**: -- 防止空文档导致的异常 -- 提供明确的日志信息 - ---- - -### 修复4:增加超时配置 ✅ - -**文件1**:`backend/src/adapters/QwenAdapter.ts`(第77-84行) - -```typescript -// Qwen-Long需要更长的超时时间(全文模式可能传输~750K tokens) -const timeout = this.modelName === 'qwen-long' ? 300000 : 60000; // 5分钟 vs 1分钟 - -console.log(`[QwenAdapter] 开始流式调用`, { - model: this.modelName, - timeout: `${timeout / 1000}秒`, - messagesCount: messages.length, -}); -``` - -**文件2**:`frontend/vite.config.ts`(第19-21行) - -```typescript -proxy: { - '/api': { - target: 'http://localhost:3001', - changeOrigin: true, - // Phase 2: 全文阅读模式需要更长的超时时间 - timeout: 300000, // 5分钟 - proxyTimeout: 300000, // 5分钟 - }, -}, -``` - -**效果**: -- Qwen-Long调用:5分钟超时 -- 其他模型:1分钟超时(足够) -- Vite代理:5分钟超时 - ---- - -## 📊 修复前后对比 - -### 之前(有问题) - -| 项目 | 值 | 结果 | -|------|-----|------| -| 输出Token限制 | 2000 | ❌ AI回答被截断,看起来"卡死" | -| 输入Token检查 | 无 | ❌ 超限时API失败,无提示 | -| 空文档过滤 | 无 | ❌ 可能导致异常 | -| 超时配置 | 60秒 | ❌ 大文本处理超时 | -| 错误提示 | 无 | ❌ 用户不知道原因 | - -### 现在(已修复) - -| 项目 | 值 | 结果 | -|------|-----|------| -| 输出Token限制 | 6000(全文模式) | ✅ 足够完整回答 | -| 输入Token检查 | 990K限制 | ✅ 超限前拦截 | -| 空文档过滤 | 已实现 | ✅ 跳过无效文档 | -| 超时配置 | 300秒(Qwen-Long) | ✅ 足够处理时间 | -| 错误提示 | 友好提示+建议 | ✅ 用户体验好 | - ---- - -## 🎯 Token使用建议 - -### 推荐配置 - -| 文献数量 | 平均Token/篇 | 总Input Token | 状态 | 建议 | -|---------|------------|--------------|------|------| -| 1-5篇 | ~100K | ~500K | ✅ 安全 | 理想范围 | -| 6-8篇 | ~100K | ~700K | 🟡 可用 | 接近上限 | -| 9-10篇 | ~100K | ~900K | ⚠️ 危险 | 容易超限 | -| 11+篇 | ~100K | ~1.1M+ | ❌ 超限 | 必须减少 | - -### 实际案例 - -**用户的7篇文献**: -- 如果每篇平均150K tokens → 总计1.05M → ❌ 超限 -- 如果每篇平均120K tokens → 总计840K → ✅ 可用 -- 如果每篇平均100K tokens → 总计700K → ✅ 安全 - -**建议**: -1. 优先选择较短的文献(<100K tokens) -2. 全文模式建议5-7篇为宜 -3. 如果需要更多文献,使用逐篇精读模式 -4. 可以分批次进行综合分析 - ---- - -## 🚀 验证步骤 - -### 1. 重启服务 - -```bash -# Backend -cd AIclinicalresearch/backend -npm run dev - -# Frontend -cd AIclinicalresearch/frontend -npm run dev -``` - -### 2. 测试场景1:正常情况(<800K tokens) - -- 选择5-7篇较短文献 -- 进入全文阅读模式 -- 提问:"这些文献的主要研究方向是什么?" -- **预期**:完整回答,不卡死,~3000-5000 tokens输出 - -### 3. 测试场景2:超限情况(>990K tokens) - -- 选择10篇大文献 -- 进入全文阅读模式 -- **预期**:立即收到友好错误提示,建议减少文献数量 - -### 4. 测试场景3:接近限制(800-900K tokens) - -- 选择8-9篇文献 -- 进入全文阅读模式 -- 检查Backend日志 -- **预期**:警告日志,但正常运行 - ---- - -## 📋 检查清单 - -- [x] 修复输出Token限制(2000 → 6000) -- [x] 添加输入Token检查(990K限制) -- [x] 过滤无效文档(空extractedText) -- [x] 增加Qwen-Long超时(60s → 300s) -- [x] 增加Vite代理超时(默认 → 300s) -- [x] 添加友好错误提示 -- [x] 添加详细日志 -- [ ] 重启服务验证 -- [ ] 测试正常情况 -- [ ] 测试超限情况 -- [ ] 更新测试记录 - ---- - -## 💡 经验教训 - -### 1. Token管理是核心问题 - -在处理大模型应用时: -- **不能只关注超时**,Token限制才是根本 -- 必须同时考虑**输入和输出**的Token限制 -- 需要提前检查并**友好提示**用户 - -### 2. 用户的直觉很重要 - -用户的质疑:"会不会Token超出大模型上下文了?" -- ✅ 完全正确! -- 技术人员容易先入为主(认为是超时) -- 用户的实际体验往往能揭示本质问题 - -### 3. 防御性编程 - -- 过滤空数据(validDocuments) -- 检查限制(SAFE_INPUT_LIMIT) -- 提供降级方案(建议逐篇精读) -- 友好错误提示(而不是连接重置) - -### 4. 配置要动态 - -```typescript -// ✅ 根据模式动态调整 -const maxOutputTokens = isFullTextMode ? 6000 : 2000; -const timeout = this.modelName === 'qwen-long' ? 300000 : 60000; -``` - -而不是硬编码固定值。 - ---- - -## 🔗 相关文档 - -- ✅ `Phase2-全文阅读模式-真实实现.md` - 核心实现 -- ✅ `backend/src/controllers/chatController.ts` - Token检查逻辑 -- ✅ `backend/src/adapters/QwenAdapter.ts` - 超时配置 -- ✅ `frontend/vite.config.ts` - 代理超时 - ---- - -## 📝 后续建议 - -### 短期(立即) -1. ✅ 验证修复效果 -2. 记录实际Token使用情况 -3. 更新用户文档(说明Token限制) - -### 中期(1-2周) -1. 添加前端Token预估功能 -2. 文献选择器显示Token警告 -3. 智能文档选择算法优化 - -### 长期(Phase 3) -1. 实现文档分段处理(如果单个超大文档) -2. Token使用统计和可视化 -3. 成本估算功能 - ---- - -**修复完成时间**:2025-10-13 -**修复人员**:AI助手 -**感谢**:用户的敏锐洞察! - ---- - -## 🎉 总结 - -**真正的问题**: -1. 🔴 输出Token限制太小(2000) → AI回答被截断 -2. 🔴 未检查输入Token数 → 超限时失败无提示 -3. 🟡 超时配置不足 → 辅助问题 - -**根本教训**: -- 处理大模型应用,Token管理是**第一优先级** -- 超时只是**表象**,Token限制才是**本质** -- 用户的直觉和质疑往往最接近真相 - -**现在的状态**: -- ✅ 输入Token有保护(990K限制) -- ✅ 输出Token足够(6000) -- ✅ 超时配置合理(300秒) -- ✅ 错误提示友好 -- ✅ 日志详细完整 - -**可以正常使用全文阅读模式了!** 🚀 - - - - - - - - - diff --git a/Phase2-问题9-快速验证.md b/Phase2-问题9-快速验证.md deleted file mode 100644 index d6ff9cb7..00000000 --- a/Phase2-问题9-快速验证.md +++ /dev/null @@ -1,227 +0,0 @@ -# Phase 2 问题9 - 快速验证指南 - -**修复内容**:Token限制与超时问题 -**修复时间**:2025-10-13 - ---- - -## 🚀 重启服务(必需) - -### 1. 重启Backend - -```bash -cd AIclinicalresearch/backend -# 如果正在运行,按Ctrl+C停止 -npm run dev -``` - -**等待输出**: -``` -✅ Backend服务已启动: http://localhost:3001 -``` - -### 2. 重启Frontend - -```bash -cd AIclinicalresearch/frontend -# 如果正在运行,按Ctrl+C停止 -npm run dev -``` - -**等待输出**: -``` -✅ Frontend服务已启动: http://localhost:3000 -``` - ---- - -## ✅ 验证步骤 - -### 测试1:正常使用(推荐5-7篇文献) - -1. **进入全文阅读模式** - - 智能问答 → 知识库模式 - - 选择知识库 - - 选择"全文阅读"模式 - -2. **检查自动切换模型** - - 应该看到提示:"已自动切换到Qwen-Long模型(支持1M上下文)" - -3. **点击"用量说明"按钮** - - 查看已加载文献数量 - - 查看Token使用情况 - -4. **提问测试** - ``` - 这些文献的主要研究方向是什么? - ``` - -5. **预期结果**: - - ✅ AI开始流式输出回答 - - ✅ 回答完整,不会卡在中间 - - ✅ 底部显示引用清单(【文献N:文件名】格式) - - ✅ Backend日志显示Token使用情况 - ---- - -### 测试2:Token接近限制(8-9篇文献) - -1. 选择8-9篇文献 -2. 进入全文阅读模式 -3. 查看Backend日志 - -**预期**: -- 🟡 警告日志:`Token数量接近限制 (XXX/990000)` -- ✅ 但仍能正常运行 - ---- - -### 测试3:Token超限(10+篇或超大文献) - -1. 尝试选择10篇以上文献 -2. 进入全文阅读模式 -3. 尝试发送消息 - -**预期(如果超限)**: -- ❌ 立即收到友好错误提示: - ``` - ⚠️ Token数量超限 - - 输入Token数量 (XXXXX) 超出Qwen-Long模型限制 (990000)。 - 请减少文献数量后重试。 - - 建议: - - 当前选中 X 篇文献,共 XXXXX tokens - - 请减少到 Y 篇以内 - - 或使用"逐篇精读"模式深入分析单篇文献 - ``` - ---- - -## 📊 Backend日志检查 - -启动对话后,Backend应该输出: - -``` -📚 [ChatController] 全文阅读模式 - 加载文献全文 { documentCount: 7 } -📄 [ChatController] 加载了 7 篇文献全文 -📚 [ChatController] 全文上下文已组装 { - totalDocuments: 7, - totalCharacters: 345678, - totalTokens: 723456, - estimatedTokens: 138271 -} -🤖 [ChatController] 开始调用LLM { - model: 'qwen-long', - maxOutputTokens: 6000, - mode: '全文阅读' -} -[QwenAdapter] 开始流式调用 { - model: 'qwen-long', - timeout: '300秒', - messagesCount: 2 -} -``` - -**关键指标**: -- ✅ `totalTokens` 应该 < 990,000 -- ✅ `maxOutputTokens` 应该是 6000(不是2000) -- ✅ `timeout` 应该是 300秒 -- ✅ `mode` 应该是"全文阅读" - ---- - -## ❓ 常见问题 - -### Q1: 还是卡死怎么办? - -**检查**: -1. Backend日志中的`totalTokens`是多少? - - 如果 > 990,000:说明文献太多,减少到5-7篇 - - 如果 < 800,000:可能是其他问题 - -2. 是否看到`maxOutputTokens: 6000`? - - 如果是2000:说明代码未更新,重启Backend - -3. Qwen API Key是否配置正确? - - 检查`.env`文件中的`DASHSCOPE_API_KEY` - -### Q2: 如何知道文献的Token数? - -在"用量说明"弹窗中可以看到: -- 容量使用情况:X / 980,000 tokens -- 已加载文献列表(每篇的Token数) - -### Q3: 多少篇文献最合适? - -**推荐**: -- **5-7篇**:理想范围(~500-700K tokens) -- 8-9篇:可用但接近上限 -- 10+篇:很可能超限 - -**原则**: -- 平均每篇100K tokens以内最安全 -- 总Token数建议 < 800K - ---- - -## 📝 验证结果记录 - -### 正常使用测试 - -- [ ] 自动切换到Qwen-Long模型 -- [ ] Token数量显示正确 -- [ ] AI回答完整,无卡顿 -- [ ] 引用格式正确(【文献N:文件名】) -- [ ] Backend日志正常 - -### 超限测试 - -- [ ] 超限时收到友好错误提示 -- [ ] 提示包含具体Token数和建议 -- [ ] 不会导致连接重置或白屏 - ---- - -## 🎯 成功标志 - -如果以下全部成立,说明修复成功: - -1. ✅ 全文阅读模式能正常进行对话 -2. ✅ AI回答不会"卡在中间" -3. ✅ 回答长度合理(3000-5000 tokens) -4. ✅ Token超限时有友好提示 -5. ✅ Backend日志清晰详细 -6. ✅ 无 ECONNRESET 错误 - ---- - -## 📞 如果还有问题 - -请提供以下信息: - -1. **Backend日志**(完整的控制台输出) -2. **文献数量和Token数**(从"用量说明"中获取) -3. **Frontend控制台错误**(如果有) -4. **具体卡在哪里**(输出了多少内容) - -**关键数据**: -- `totalTokens: ?` -- `maxOutputTokens: ?` -- `timeout: ?` - -这些信息能帮助快速定位问题! - ---- - -**验证文档版本**:1.0 -**最后更新**:2025-10-13 - - - - - - - - - diff --git a/Phase2-首次测试-修复总结.md b/Phase2-首次测试-修复总结.md deleted file mode 100644 index 972c6b48..00000000 --- a/Phase2-首次测试-修复总结.md +++ /dev/null @@ -1,310 +0,0 @@ -# Phase 2 首次测试 - 问题修复总结 - -**修复时间**:2025-10-13 -**修复人员**:AI助手 -**测试人员**:用户 - ---- - -## 📊 修复概览 - -| 项目 | 数量 | -|------|------| -| **发现问题总数** | 3个 | -| **严重问题** | 3个(阻断性) | -| **已修复** | 3个 | -| **修复率** | 100% | -| **修复用时** | 约30分钟 | - ---- - -## 🔧 修复详情 - -### 问题 #1:全文阅读模式-API数据结构错误 ✅ - -**问题类型**:🔴 严重(阻断核心功能) -**影响范围**:全文阅读模式无法加载 - -**修复文件**: -- `frontend/src/api/knowledgeBaseApi.ts` - -**修复内容**: -```typescript -// 第208行 -// 修复前 -return response.data; - -// 修复后 -return response.data.data; // 返回内层的data对象 -``` - -**修复原因**: -- Backend返回:`{ success: true, data: { limits, selection, selectedDocuments } }` -- 前端期望:`{ limits, selection, selectedDocuments }` -- API客户端返回了多余的一层包装 - -**验证方法**: -1. 重启Frontend服务 -2. 选择知识库 → 全文阅读模式 -3. 应能正常显示容量指示器和文献列表 - ---- - -### 问题 #2:全文阅读模式-找不到对话输入框 ✅ - -**问题类型**:🔴 严重(阻断核心功能) -**影响范围**:全文阅读模式无法对话 - -**修复文件**: -- `frontend/src/pages/ChatPage.tsx` - -**修复内容**: -```typescript -// 第294-305行 -// 修复前:直接返回FullTextMode组件 -return ( - -) - -// 修复后:添加外层flex容器 -return ( -
- -
-) -``` - -同时对逐篇精读模式应用相同修复(第318-336行)。 - -**修复原因**: -- 页面主布局使用flex,子元素需要设置`flex: 1`占据剩余空间 -- FullTextMode缺少外层容器导致高度塌缩 -- 输入框被挤出可视区域 - -**验证方法**: -1. 重启Frontend服务 -2. 全文阅读模式和逐篇精读模式 -3. 应能在页面底部看到对话输入框 - ---- - -### 问题 #3:逐篇精读模式-React Hooks调用错误 ✅ - -**问题类型**:🔴 严重(阻断核心功能) -**影响范围**:逐篇精读模式无法选择文献 - -**修复文件**: -- `frontend/src/pages/ChatPage.tsx` - -**修复内容**: - -**1. 第43-46行:在组件顶层调用Hook** -```typescript -// 修复前 -const [deepReadState, setDeepReadState] = useState | null>(null) - -// 修复后 -const deepReadHook = useDeepReadState([]) // ✅ 在组件顶层调用 -``` - -**2. 第104-118行:使用Hook返回的方法** -```typescript -// 修复前 -const handleConfirmDocSelection = (selectedDocs: Document[]) => { - const deepRead = useDeepReadState(selectedDocs) // ❌ 错误:在事件处理器中调用Hook - setDeepReadState(deepRead as any) - ... -} - -// 修复后 -const handleConfirmDocSelection = (selectedDocs: Document[]) => { - deepReadHook.updateSelectedDocs(selectedDocs) // ✅ 调用Hook返回的方法 - ... -} -``` - -**3. 全文替换:`deepReadState` → `deepReadHook`** -- 第184-235行:`handleSendDeepReadMessage`函数 -- 第310-336行:渲染逐篇精读模式 - -**修复原因**: -- 违反React Hooks规则:Hook只能在组件顶层调用 -- 不能在事件处理器、条件语句、循环中调用Hook -- 原代码在事件处理器中直接调用了`useDeepReadState` - -**验证方法**: -1. 重启Frontend服务 -2. 选择知识库 → 逐篇精读模式 -3. 选择文献 → 点击确认 -4. 应能正常进入精读模式,不再报Hooks错误 - ---- - -## 📈 修复影响 - -### 直接影响 -✅ **全文阅读模式** -- 能正常加载文档选择结果 -- 能显示容量指示器 -- 能看到对话输入框 -- 能进行文献综述对话 - -✅ **逐篇精读模式** -- 能正常选择文献 -- 能看到对话输入框 -- 能进行深度对话 -- 能切换文献 - -### 潜在影响 -⚠️ **需要验证的地方** -1. 其他API是否有类似的数据结构访问问题 -2. 文献切换时的对话历史保持是否正常 -3. Token容量显示的准确性 -4. 其他组件的布局是否受影响 - ---- - -## 🎓 经验教训 - -### 1. API数据结构一致性 -**教训**:Backend和Frontend对API返回数据结构的理解要一致 -**改进建议**: -- 为所有API返回值定义TypeScript类型 -- 添加API响应数据的运行时验证 -- 统一Backend所有API的返回格式处理方式 - -### 2. React Hooks规则 -**教训**:严格遵守Hooks只能在组件顶层调用的规则 -**改进建议**: -- 添加ESLint规则检查Hooks调用位置 -- 代码审查时重点检查Hook使用 -- 团队培训React基础规则 - -### 3. 布局设计完整性 -**教训**:Flex布局中必须明确设置子元素的flex属性 -**改进建议**: -- 建立组件布局最佳实践文档 -- 使用CSS-in-JS时注意容器层级 -- 测试不同内容长度下的布局表现 - -### 4. 测试驱动修复 -**教训**:快速测试能有效发现问题,Console错误能准确定位 -**改进建议**: -- 保持测试清单更新 -- 重视浏览器Console信息 -- 建立问题记录和修复流程 - ---- - -## ✅ 验证清单 - -### 用户需要验证的项目 - -**环境准备** -- [ ] 重启Frontend服务(`Ctrl+C` → `npm run dev`) -- [ ] 确认服务启动成功 -- [ ] 浏览器清除缓存(推荐) - -**问题1验证** -- [ ] 选择知识库 → 全文阅读模式 -- [ ] 不再报错"加载文档选择失败" -- [ ] 能看到容量指示器 -- [ ] 能看到已加载文献列表 - -**问题2验证** -- [ ] 全文阅读模式:页面底部能看到对话输入框 -- [ ] 输入框有提示文字 -- [ ] 能输入文字 -- [ ] 能看到发送按钮 - -**问题3验证** -- [ ] 选择知识库 → 逐篇精读模式 -- [ ] 文献选择器弹出 -- [ ] 选择1-5篇文献 → 点击确认 -- [ ] 不再报React Hooks错误 -- [ ] 进入精读模式,能看到输入框 - -**功能验证** -- [ ] 全文阅读模式能正常对话 -- [ ] 逐篇精读模式能正常对话 -- [ ] 逐篇精读模式能切换文献 -- [ ] 切换文献时对话历史独立 - ---- - -## 📝 后续计划 - -### 继续测试(用户执行) -1. **全文阅读模式完整测试** - - Token容量显示准确性 - - 文献综述质量 - - 引用标记功能 - - 多轮对话连贯性 - -2. **逐篇精读模式完整测试** - - 文献切换功能 - - 对话历史独立性 - - 深度分析能力 - - Token使用情况 - -3. **端到端场景测试** - - 文献综述 → 发现核心论文 → 切换到精读 - - 精读多篇文献 → 对比分析 - - 模式切换时的状态保持 - -### 代码质量提升(开发团队) -1. 添加TypeScript类型定义 -2. 添加ESLint规则 -3. 完善单元测试 -4. 建立布局最佳实践文档 - ---- - -## 📞 联系方式 - -如果验证过程中发现新问题,请: -1. 记录详细的错误信息(截图 + Console日志) -2. 记录复现步骤 -3. 更新 `测试记录/问题清单.md` -4. 通知开发团队 - ---- - -**修复完成时间**:2025-10-13 -**状态**:✅ 所有已知问题已修复,等待用户验证 - ---- - -## 🎉 总结 - -本次测试虽然发现了3个严重问题,但都是典型的前端开发常见问题: -1. API数据结构理解偏差 -2. React基础规则违反 -3. 布局容器设置遗漏 - -所有问题均已快速定位并修复,修复方案简洁明确。这些问题的发现和解决过程也为团队积累了宝贵经验。 - -**Phase 2核心功能已具备运行基础,可以继续深入测试!** 🚀 - - - - - - - - - diff --git a/Phase3-Day1-后端完成总结.md b/Phase3-Day1-后端完成总结.md deleted file mode 100644 index 31ba2155..00000000 --- a/Phase3-Day1-后端完成总结.md +++ /dev/null @@ -1,379 +0,0 @@ -# Phase 3 - Day 1 后端开发完成总结 - -**完成时间**:2025-10-13 -**开发用时**:约2小时 -**完成度**:后端100% | 整体50% - ---- - -## 🎉 已完成功能 - -### 1. 数据库设计 ✅ - -**新增表**: -- `batch_tasks` - 批处理任务 -- `batch_results` - 批处理结果(每篇文献一条) -- `task_templates` - 任务模板(预留) - -**关键字段**: -- 任务状态追踪(processing/completed/failed) -- 进度统计(totalDocuments/completedCount/failedCount) -- 性能指标(durationSeconds/processingTimeMs/tokensUsed) -- 固定3并发(concurrency=3) - -**迁移状态**:✅ 已应用(20251012124747_add_batch_processing_module) - ---- - -### 2. 核心业务逻辑 ✅ - -**文件**:`backend/src/services/batchService.ts` - -**核心功能**: -- ✅ `executeBatchTask()` - 主执行函数 -- ✅ `processDocument()` - 处理单个文档 -- ✅ `retryFailedDocuments()` - 重试失败项 -- ✅ 并发控制(p-queue,固定3并发) -- ✅ 进度计算和推送 -- ✅ 错误处理(不影响其他文档) - -**关键参数**: -- 文献数量:3-50篇 ✅ -- 并发数:固定3 ✅ -- 超时处理:失败标记,可重试 ✅ - ---- - -### 3. 预设模板系统 ✅ - -**文件**:`backend/src/templates/clinicalResearch.ts` - -**临床研究信息提取模板**(8字段): -1. ✅ 研究目的 -2. ✅ 研究设计 -3. ✅ 研究对象 -4. ✅ 样本量(text类型,保留原文) -5. ✅ 干预组 -6. ✅ 对照组 -7. ✅ 结果及数据 -8. ✅ 牛津评级(详细标准说明) - -**特点**: -- 提供完整的系统提示词 -- 提供详细的用户提示词模板 -- 包含牛津评级标准(1a-5) -- 支持扩展更多预设模板 - ---- - -### 4. JSON解析工具 ✅ - -**文件**:`backend/src/utils/jsonParser.ts` - -**功能**: -- ✅ 提取JSON块(支持多种格式) -- ✅ 解析和验证JSON -- ✅ 处理AI输出的额外文字 -- ✅ 自动填充缺失字段 - -**支持格式**: -``` -1. 纯JSON:{ "key": "value" } -2. 带前言:这是结果:\n{ "key": "value" } -3. 带后缀:{ "key": "value" }\n\n以上... -4. 代码块:```json\n{ "key": "value" }\n``` -``` - ---- - -### 5. API控制器 ✅ - -**文件**:`backend/src/controllers/batchController.ts` - -**API端点**: -- ✅ `POST /api/v1/batch/execute` - 执行批处理 -- ✅ `GET /api/v1/batch/tasks/:taskId` - 获取任务状态 -- ✅ `GET /api/v1/batch/tasks/:taskId/results` - 获取任务结果 -- ✅ `POST /api/v1/batch/tasks/:taskId/retry-failed` - 重试失败项 -- ✅ `GET /api/v1/batch/templates` - 获取所有模板 - -**参数验证**: -- ✅ 文献数量检查(3-50篇) -- ✅ 模板类型验证 -- ✅ 知识库存在性检查 -- ✅ 文档所属检查 - ---- - -### 6. 路由注册 ✅ - -**文件**: -- `backend/src/routes/batchRoutes.ts` - 路由定义 -- `backend/src/index.ts` - 路由注册 - -**集成状态**:✅ 已注册到Fastify - ---- - -## 📋 文件清单 - -### 新建文件(6个) -1. ✅ `backend/src/templates/clinicalResearch.ts` (169行) -2. ✅ `backend/src/utils/jsonParser.ts` (100行) -3. ✅ `backend/src/services/batchService.ts` (283行) -4. ✅ `backend/src/controllers/batchController.ts` (373行) -5. ✅ `backend/src/routes/batchRoutes.ts` (26行) -6. ✅ `backend/prisma/migrations/20251012124747_add_batch_processing_module/` (迁移) - -### 修改文件(2个) -1. ✅ `backend/prisma/schema.prisma` - 添加3个表 -2. ✅ `backend/src/index.ts` - 注册批处理路由 - -### 安装依赖(1个) -1. ✅ `p-queue` - 并发队列控制 - ---- - -## 🎯 核心特性确认 - -| 特性 | 设计要求 | 实际实现 | 状态 | -|------|---------|---------|------| -| 文献数量 | 3-50篇 | 3-50篇 | ✅ | -| 并发数 | 固定3 | 固定3 | ✅ | -| 样本量字段 | text类型 | text类型 | ✅ | -| 牛津评级 | 详细标准 | 8级标准 | ✅ | -| 失败重试 | 支持 | 支持 | ✅ | -| 进度推送 | WebSocket | 准备就绪 | ⏳ | -| 预设模板 | 1个 | 1个(临床研究)| ✅ | -| 自定义模板 | 文本块 | 文本块 | ✅ | - ---- - -## 🔧 技术实现亮点 - -### 1. 并发控制(p-queue) -```typescript -const queue = new PQueue({ concurrency: 3 }); -const promises = documentIds.map(docId => - queue.add(async () => { - // 处理文档 - }) -); -await Promise.allSettled(promises); -``` - -**优势**: -- 固定3并发,避免API限流 -- Promise.allSettled保证所有任务执行 -- 单个失败不影响其他文档 - ---- - -### 2. 灵活的JSON解析 -```typescript -function extractJSON(text: string): string | null { - // 尝试多种格式 - const jsonPattern = /(\{[\s\S]*\}|\[[\s\S]*\])/; - const match = text.match(jsonPattern); - // ... -} -``` - -**优势**: -- 容错性强,AI输出不稳定也能处理 -- 支持代码块、前言后缀等多种格式 -- 自动填充缺失字段 - ---- - -### 3. 进度追踪 -```typescript -// 实时更新进度 -if (onProgress) { - onProgress({ - taskId, - completed, - total, - failed, - estimatedSeconds: calculateEstimatedTime(...), - }); -} -``` - -**优势**: -- 实时反馈 -- 预估剩余时间 -- WebSocket推送准备(待集成) - ---- - -## ⚠️ 待完成功能 - -### 1. WebSocket集成 ⏳ - -**当前状态**: -- 批处理服务已支持`onProgress`回调 -- 控制器中已准备WebSocket代码 -- 需要在index.ts中集成Socket.IO - -**优先级**:中(可用HTTP轮询替代) - ---- - -### 2. 前端开发 ⏳ - -**待开发**: -- API封装(`frontend/src/api/batchApi.ts`) -- UI组件(`BatchMode`、`TaskDefinition`、`DocumentSelection`等) -- 状态管理(`useBatchTask` hook) -- 结果表格(预设8列 vs 自定义3列) -- 导出Excel功能 - -**预计用时**:4-5小时 - ---- - -### 3. 测试验证 ⏳ - -**测试内容**: -- [ ] 预设模板测试(10篇文献) -- [ ] 自定义模板测试(5篇文献) -- [ ] 失败重试测试 -- [ ] 边界测试(3篇、50篇) -- [ ] 并发控制验证 -- [ ] JSON解析容错测试 - ---- - -## 🚀 后端API测试指南 - -### 使用方法 - -**1. 启动Backend**: -```bash -cd AIclinicalresearch/backend -npm run dev -``` - -**2. 测试API**(使用Postman或curl): - -#### 执行批处理任务 -```bash -POST http://localhost:3001/api/v1/batch/execute -Content-Type: application/json - -{ - "kb_id": "知识库ID", - "document_ids": ["文档ID1", "文档ID2", ...], - "template_type": "preset", - "template_id": "clinical_research", - "model_type": "deepseek-v3", - "task_name": "测试任务" -} -``` - -#### 获取任务状态 -```bash -GET http://localhost:3001/api/v1/batch/tasks/{taskId} -``` - -#### 获取任务结果 -```bash -GET http://localhost:3001/api/v1/batch/tasks/{taskId}/results -``` - -#### 获取所有模板 -```bash -GET http://localhost:3001/api/v1/batch/templates -``` - ---- - -## 📊 开发进度 - -``` -Phase 3: 批处理模式 -├── 后端开发 ████████████████████ 100% ✅ -│ ├── 数据库设计 ████████████████████ 100% ✅ -│ ├── 业务逻辑 ████████████████████ 100% ✅ -│ ├── API控制器 ████████████████████ 100% ✅ -│ └── 路由集成 ████████████████████ 100% ✅ -│ -├── WebSocket ░░░░░░░░░░░░░░░░░░░░ 0% ⏳ -│ -├── 前端开发 ░░░░░░░░░░░░░░░░░░░░ 0% ⏳ -│ ├── API封装 ░░░░░░░░░░░░░░░░░░░░ 0% -│ ├── UI组件 ░░░░░░░░░░░░░░░░░░░░ 0% -│ └── 状态管理 ░░░░░░░░░░░░░░░░░░░░ 0% -│ -└── 测试验证 ░░░░░░░░░░░░░░░░░░░░ 0% ⏳ - -整体进度: ██████████░░░░░░░░░░ 50% -``` - ---- - -## 🎯 下一步计划 - -### 选项A:先测试后端API -1. 使用Postman测试各个API端点 -2. 验证批处理逻辑正确性 -3. 确认JSON解析和错误处理 -4. 然后开发前端 - -**优势**:确保后端稳定,避免前端开发时发现后端问题 - ---- - -### 选项B:直接开发前端 -1. 创建批处理API封装 -2. 开发UI组件(三栏布局) -3. 集成WebSocket进度推送 -4. 端到端测试 - -**优势**:快速看到完整效果,体验更直观 - ---- - -## 📝 技术文档 - -- ✅ `docs/05-每日进度/Phase3-批处理模式-完整设计.md` - 设计方案 -- ✅ `Phase3-Day1-后端完成总结.md` - 本文档 - ---- - -## 💡 建议 - -**我的建议**:选择**选项A**(先测试后端) - -**理由**: -1. 批处理逻辑较复杂,需要验证 -2. JSON解析可能有edge case -3. 并发控制需要确认 -4. 确保后端稳定后,前端开发更顺畅 - -**测试重点**: -- 3篇、10篇、50篇文献的不同场景 -- JSON解析的各种格式 -- 失败重试机制 -- Token使用统计 - ---- - -**后端开发完成!** 🎉 -**准备开始下一阶段** 🚀 - ---- - -**文档创建**:2025-10-13 -**最后更新**:2025-10-13 -**状态**:后端完成,等待继续开发或测试 - - - - - - - - - diff --git a/Phase3-快速参考.md b/Phase3-快速参考.md deleted file mode 100644 index a801a438..00000000 --- a/Phase3-快速参考.md +++ /dev/null @@ -1,314 +0,0 @@ -# Phase 3 批处理模式 - 快速参考 - -**完成时间**:2025-10-12 -**开发用时**:6小时(开发)+ 2小时(测试修复) -**状态**:✅ 全部完成,测试通过 - ---- - -## 🎯 核心概念 - -**批处理模式 ≠ 对话** -**批处理模式 = 任务执行器 + 数据提取器** - -### 与其他模式的区别 - -| 模式 | 交互方式 | 输出 | 适用场景 | -|------|---------|------|---------| -| 全文阅读 | 对话式 | 自然语言 | 文献综述 | -| 逐篇精读 | 对话式 | 自然语言 | 深度分析 | -| **批处理** | **任务式** | **结构化表格** | **批量提取** | - ---- - -## ⚙️ 核心参数 - -| 参数 | 值 | 说明 | -|------|-----|------| -| 文献数量 | **3-50篇** | 最少3篇,最多50篇 | -| 并发数 | **固定3** | 避免API限流 | -| 预设模板 | **1个** | 临床研究信息提取(8字段) | -| 自定义模板 | **支持** | 文本块显示(3列) | -| 失败重试 | **支持** | 单独重试失败项 | -| 导出格式 | **Excel** | 双Sheet设计 | - ---- - -## 📋 预设模板:临床研究信息提取 - -### 8个提取字段 - -1. **研究目的** - 研究问题或假设 -2. **研究设计** - RCT、队列研究等 -3. **研究对象** - 纳入/排除标准 -4. **样本量** - text类型,保留原文(如"干预组156人,对照组152人") -5. **干预组** - 实验组干预措施 -6. **对照组** - 对照组情况 -7. **结果及数据** - 主要结局指标数据 -8. **牛津评级** - 证据等级(1a/1b/2a/2b/3a/3b/4/5) - -### 牛津评级标准 - -- **1a**:系统评价/Meta分析 -- **1b**:单个RCT -- **2a**:设计良好的对照研究(无随机化) -- **2b**:准实验研究(队列、病例对照) -- **3a**:描述性研究(横断面、病例系列) -- **3b**:个案报告 -- **4**:专家意见 -- **5**:基础研究(动物、体外) - ---- - -## 🗂️ 文件结构 - -### 后端文件(6个新建) - -``` -backend/src/ -├── templates/ -│ └── clinicalResearch.ts # 预设模板配置 -├── utils/ -│ └── jsonParser.ts # JSON解析工具 -├── services/ -│ └── batchService.ts # 批处理核心逻辑 -├── controllers/ -│ └── batchController.ts # API控制器 -└── routes/ - └── batchRoutes.ts # 路由定义 -``` - -### 前端文件(13个新建) - -``` -frontend/src/ -├── api/ -│ └── batchApi.ts # API封装 -├── hooks/ -│ └── useBatchTask.ts # 状态管理 -└── components/chat/ - ├── TaskDefinition.tsx/.css # 任务定义 - ├── DocumentSelection.tsx/.css # 文献选择 - ├── BatchProgress.tsx/.css # 执行进度 - ├── PresetTable.tsx # 预设表格(8列) - ├── CustomTable.tsx # 自定义表格(3列) - ├── BatchResults.tsx/.css # 结果展示 - └── BatchMode.tsx/.css # 批处理主组件 -``` - ---- - -## 🔌 API端点 - -### 后端API - -``` -GET /api/v1/batch/templates # 获取模板列表 -POST /api/v1/batch/execute # 执行批处理 -GET /api/v1/batch/tasks/:taskId # 获取任务状态 -GET /api/v1/batch/tasks/:taskId/results # 获取任务结果 -POST /api/v1/batch/tasks/:taskId/retry-failed # 重试失败项 -``` - -**测试状态**:✅ 模板API已验证通过 - ---- - -## 🎨 UI设计 - -### 三栏布局 - -``` -┌────────┬───────────────┬───────────────┐ -│ 模式 │ 任务定义 │ 进度/结果 │ -│ 选择 │ + │ │ -│ (120px)│ 文献选择 │ │ -│ │ + │ │ -│ ○ 全文│ 开始按钮 │ │ -│ ○ 精读│ │ │ -│ ● 批处理│ │ │ -└────────┴───────────────┴───────────────┘ -``` - -### 步骤流程 - -``` -Step 1: 定义任务 - ↓ -Step 2: 选择文献(3-50篇) - ↓ -Step 3: 开始批处理 - ↓ -Step 4: 查看结果,导出Excel -``` - ---- - -## 📊 结果展示 - -### 预设模板(8列表格) - -``` -┌──┬────────┬──────┬──────┬──────┬────┬────┬────┬──────┬────┐ -│#│文献名称 │研究 │研究 │研究 │样本│干预│对照│结果 │牛津│ -│ │ │目的 │设计 │对象 │量 │组 │组 │数据 │评级│ -└──┴────────┴──────┴──────┴──────┴────┴────┴────┴──────┴────┘ -``` - -### 自定义模板(3列表格) - -``` -┌──┬──────────┬──────────────────────┐ -│#│文献名称 │提取结果(文本块) │ -└──┴──────────┴──────────────────────┘ -``` - ---- - -## 📥 Excel导出 - -### 文件结构 - -**Sheet 1: 提取结果** -- 预设模板:8列数据 -- 自定义模板:3列数据 - -**Sheet 2: 任务信息** -- 任务名称、模板类型 -- 执行时间、完成时间 -- 文献统计(总数、成功、失败) -- 总用时 - -**文件名格式**: -- 预设:`批处理结果_临床研究信息提取_2025-10-13.xlsx` -- 自定义:`批处理结果_自定义任务_2025-10-13.xlsx` - ---- - -## ⚡ 性能指标 - -| 指标 | 预期值 | -|------|--------| -| 单文档处理 | 15-25秒 | -| 10篇总时长 | 5-8分钟 | -| 50篇总时长 | 25-40分钟 | -| JSON解析成功率 | >90% | - -**效率提升**: -- 手动:10篇 × 10分钟 = 100分钟 -- 批处理:10篇 × 20秒 = ~7分钟 -- **提升约14倍** 🚀 - ---- - -## ✅ 已完成的收尾工作 - -### 测试与修复(已完成) - -1. ✅ **类型定义** - 已更新 -2. ✅ **依赖安装** - xlsx已安装 -3. ✅ **Frontend启动** - 正常运行 -4. ✅ **端到端测试** - 全流程测试通过 -5. ✅ **问题修复** - 5个问题已全部修复 - -### 修复的问题 -1. ✅ API导入路径错误 -2. ✅ 数据库用户数据缺失 -3. ✅ 批处理页面空白问题 -4. ✅ 结果页面布局问题(改为垂直布局) -5. ✅ 表格显示优化(列宽、换行、滚动条) - -**详细修复记录**:见 `docs/05-每日进度/Phase3-最终测试-修复总结.md` - ---- - -## ✅ 验收清单(已完成) - -### 功能测试 - -- [x] 进入批处理模式(界面正常显示)✅ -- [x] 模板选择(预设/自定义切换)✅ -- [x] 文献选择(3-50篇限制生效)✅ -- [x] 执行任务(进度显示正常)✅ -- [x] 查看结果(表格显示正确)✅ -- [x] 导出Excel(文件格式正确)✅ -- [x] 重试失败项(如有失败)✅ - -### 边界测试 - -- [x] 选择<3篇文献(应提示错误)✅ -- [x] 选择>50篇文献(应提示错误)✅ -- [x] 自定义模板空提示词(应提示错误)✅ -- [x] JSON解析失败(应标记失败,不影响其他)✅ - -### UI优化测试 - -- [x] 垂直布局显示正常 ✅ -- [x] 表格列宽合理 ✅ -- [x] 单元格内容换行显示 ✅ -- [x] 横向滚动条可用 ✅ -- [x] 牛津评级列固定右侧 ✅ - ---- - -## 📚 相关文档 - -### 设计文档 -- `docs/05-每日进度/Phase3-批处理模式-完整设计.md` (设计方案) - -### 完成报告 -- `docs/05-每日进度/Phase3-完成总结.md` (完成总结) -- `Phase3-Day1-后端完成总结.md` (后端详情) - -### 开发计划 -- `docs/04-开发计划/开发里程碑.md` (整体进度) - -### 测试工具 -- `backend/test-batch-api.js` (API测试脚本) - ---- - -## 💡 使用提示 - -### 最佳实践 - -**文献数量**: -- 首次测试:5-10篇 -- 常规使用:10-20篇 -- 大规模:30-50篇 - -**模板选择**: -- 标准临床研究 → 使用预设模板 -- 特殊需求 → 使用自定义模板 - -**性能优化**: -- 避免超大文献(>200K tokens) -- 分批处理(如需处理100+篇) -- 优先使用DeepSeek-V3(快且便宜) - ---- - -## 🎉 总结 - -**Phase 3 批处理模式:** -- ✅ 任务式交互(非对话) -- ✅ 双模板系统(预设8字段 + 自定义文本) -- ✅ 固定3并发(稳定可靠) -- ✅ 完整的Excel导出 -- ✅ 友好的UI体验 -- ✅ ~4,000行代码(6小时完成) - -**核心价值**: -- 批量信息提取 -- 结构化数据输出 -- 效率提升14倍 -- 可追溯、可归档 - -**已完成**:开发 + 测试 + 修复 + 优化 ✅ - ---- - -**快速参考版本**:2.0 -**最后更新**:2025-10-12 -**状态**:✅ 全部完成,已通过最终测试 - diff --git a/Phase3-最终收尾-测试指南.md b/Phase3-最终收尾-测试指南.md deleted file mode 100644 index 64faac6a..00000000 --- a/Phase3-最终收尾-测试指南.md +++ /dev/null @@ -1,543 +0,0 @@ -# Phase 3 批处理模式 - 最终收尾与测试指南 - -**完成时间**:2025-10-13 -**状态**:✅ 开发100%完成,等待测试验证 - ---- - -## ✅ 已完成检查清单 - -### 开发完成度:100% - -- [x] **后端开发**:100% - - [x] 数据库Schema(3个表) - - [x] 数据库迁移 - - [x] 预设模板(临床研究,8字段) - - [x] JSON解析工具 - - [x] 批处理服务(p-queue,固定3并发) - - [x] API控制器(5个端点) - - [x] 路由注册 - - [x] p-queue依赖安装 - -- [x] **前端开发**:100% - - [x] API封装(batchApi.ts) - - [x] 状态管理(useBatchTask.ts) - - [x] UI组件(7个组件,14个文件) - - [x] 集成到ChatPage - - [x] 更新ModeSelector - - [x] xlsx依赖安装 ✅ - -- [x] **类型定义**:100% - - [x] KnowledgeBaseMode包含'batch' ✅ - - [x] 批处理相关类型定义完整 - -- [x] **基础测试**:100% - - [x] 后端API测试(模板API验证通过) - ---- - -## 🧪 端到端测试指南 - -### 前提条件 - -**确保服务已启动**: -1. ✅ Backend服务运行中(http://localhost:3001) -2. ⏳ Frontend服务需要启动(http://localhost:3000) -3. ✅ PostgreSQL数据库运行中 -4. ⏳ 知识库中有测试文献(至少3篇,已提取extractedText) - ---- - -### 测试步骤 - -#### **测试1:预设模板测试(推荐)** - -**操作流程**: - -1. **启动Frontend** - ```bash - cd D:\MyCursor\AIclinicalresearch\frontend - npm run dev - ``` - -2. **进入批处理模式** - - 打开浏览器:http://localhost:3000 - - 智能问答 → 知识库模式 - - 选择知识库 - - 点击"📦 批处理"按钮 - -3. **定义任务(第1步)** - - 默认选中"● 临床研究信息提取" - - 查看8个提取字段预览 - - 确认字段正确 - -4. **选择文献(第2步)** - - 滚动查看文献列表 - - 勾选3-10篇文献(测试建议5-7篇) - - 查看"已选择:X/50篇"统计 - - 查看预计处理时间 - -5. **开始批处理(第3步)** - - 点击"🚀 开始批处理"按钮 - - 等待任务开始 - -6. **观察进度** - - 查看进度条(百分比) - - 查看统计卡片(成功/失败/处理中/等待) - - 查看时间信息(已用时、平均速度) - - 等待任务完成(约5-10分钟) - -7. **查看结果** - - 任务完成后自动切换到结果页 - - 查看8列表格 - - 检查每列数据是否正确 - - 查看牛津评级标签颜色 - -8. **导出Excel** - - 点击"📥 导出Excel"按钮 - - 文件自动下载 - - 打开文件验证: - - Sheet1:提取结果(8列) - - Sheet2:任务信息 - -**预期结果**: -- ✅ UI流程流畅 -- ✅ 进度实时更新 -- ✅ 结果表格正确(8列) -- ✅ Excel文件格式正确 -- ✅ 数据提取准确 - ---- - -#### **测试2:自定义模板测试** - -**操作流程**: - -1. **选择自定义模板** - - 点击"○ 自定义任务"单选按钮 - -2. **输入自定义提示词** - ``` - 请总结这篇文献的研究方法、主要发现和局限性,用3-5段话描述。 - ``` - -3. **选择文献** - - 选择3-5篇文献(测试用较少文献) - -4. **执行任务** - - 点击"🚀 开始批处理" - - 等待完成 - -5. **查看结果** - - 结果表格应为3列(序号、文献名、提取结果) - - 文本块显示AI的回答 - - 点击"查看完整内容"链接 - -6. **导出Excel** - - 验证3列格式 - -**预期结果**: -- ✅ 文本块完整显示 -- ✅ 格式为3列(不是8列) -- ✅ Excel导出正确 - ---- - -#### **测试3:边界测试** - -**场景1:文献数量不足** -- 只选择1-2篇文献 -- 点击"开始批处理" -- **预期**:提示"请至少选择3篇文献" - -**场景2:文献数量过多** -- 选择51篇或更多 -- **预期**:提示"最多只能选择50篇文献" - -**场景3:自定义模板空提示词** -- 选择"自定义任务" -- 不输入提示词(留空) -- 选择文献后点击开始 -- **预期**:提示"请输入自定义提示词" - -**场景4:失败重试** -- 如果有文档处理失败 -- 点击"重试失败项"按钮 -- **预期**:重新处理失败的文档 - ---- - -## 🔍 检查要点 - -### Frontend控制台 - -**应该看到**: -- 组件正常渲染 -- API调用成功 -- 进度更新日志 - -**不应该看到**: -- TypeScript类型错误 -- 组件渲染错误 -- API调用失败(404/500) - ---- - -### Backend控制台 - -**应该看到**: -``` -📦 [BatchController] 收到批处理请求 -✅ [BatchController] 创建任务: -📦 [BatchService] 开始执行批处理任务 -✅ [BatchService] 使用已存在的任务: -🔄 [BatchService] 处理文档 1/10: -✅ [BatchService] 文档处理成功: (2300ms) -... -🎉 [BatchService] 批处理任务完成 -``` - -**不应该看到**: -- 数据库错误 -- LLM API错误(除非真的失败) -- JSON解析错误(除非AI输出异常) - ---- - -## 📋 功能验收清单 - -### 核心功能 - -- [ ] **模板选择**:预设/自定义切换正常 -- [ ] **字段预览**:预设模板显示8个字段 -- [ ] **文献选择**:3-50篇限制生效 -- [ ] **批量操作**:全选/清空/反选正常 -- [ ] **搜索过滤**:文献搜索正常 -- [ ] **开始执行**:任务正常启动 -- [ ] **进度显示**:进度条和统计实时更新 -- [ ] **结果切换**:执行完成后自动显示结果 -- [ ] **表格展示**:预设8列/自定义3列正确 -- [ ] **Excel导出**:文件下载并格式正确 -- [ ] **失败重试**:重试按钮正常工作 -- [ ] **新建任务**:重置状态正常 - -### 用户体验 - -- [ ] UI布局清晰(三栏) -- [ ] 操作流程直观 -- [ ] 提示信息友好 -- [ ] 错误处理完善 -- [ ] 响应速度快 -- [ ] 无明显bug - ---- - -## 🐛 可能遇到的问题 - -### 问题1:Frontend启动报类型错误 - -**原因**:某些导入路径或类型不匹配 - -**解决**: -- 检查控制台错误信息 -- 调整导入路径或类型定义 -- 重新启动Frontend - ---- - -### 问题2:批处理模式不显示 - -**原因**:路由或条件判断问题 - -**检查**: -- 是否选择了知识库 -- 是否点击了"批处理"按钮 -- ChatPage.tsx中的条件判断 - ---- - -### 问题3:执行任务后无进度 - -**原因**:API调用失败或轮询问题 - -**检查**: -- Backend控制台是否有错误 -- Frontend控制台Network标签 -- task_id是否正确返回 - ---- - -### 问题4:结果表格为空 - -**原因**:JSON解析失败或数据格式问题 - -**检查**: -- Backend日志中的JSON解析结果 -- 原始AI输出(raw_output字段) -- 是否有extractedText - ---- - -### 问题5:Excel导出失败 - -**原因**:xlsx库未正确导入或数据格式问题 - -**解决**: -- 确认xlsx已安装(✅已完成) -- 检查浏览器控制台错误 -- 验证results数据结构 - ---- - -## 📊 测试数据建议 - -### 理想测试场景 - -**知识库**:临床研究文献库 - -**文献要求**: -- 至少5-10篇临床研究文献 -- 已上传并提取extractedText -- 包含完整的研究信息(目的、设计、结果等) - -**文献类型**: -- 建议:RCT研究(容易提取PICO) -- 样本量信息明确 -- 结果数据完整 - ---- - -## 🎯 验收标准 - -### 必须通过(核心功能) - -✅ **预设模板流程**: -1. 选择临床研究信息提取模板 -2. 选择5-10篇文献 -3. 执行任务,进度正常显示 -4. 结果显示为8列表格 -5. 数据提取基本准确 -6. Excel导出成功(双Sheet) - -✅ **自定义模板流程**: -1. 选择自定义任务 -2. 输入提示词 -3. 执行任务 -4. 结果显示为3列(文本块) -5. Excel导出成功 - -### 建议通过(用户体验) - -- [ ] UI美观、响应流畅 -- [ ] 错误提示清晰 -- [ ] 无明显卡顿 -- [ ] 边界测试通过 - ---- - -## 🚀 启动测试 - -### 命令 - -```bash -# Backend(应该已经运行中) -cd D:\MyCursor\AIclinicalresearch\backend -npm run dev - -# Frontend(需要启动) -cd D:\MyCursor\AIclinicalresearch\frontend -npm run dev -``` - -### 访问地址 - -- Frontend: http://localhost:3000 -- Backend: http://localhost:3001 - ---- - -## 📝 测试记录模板 - -### 测试执行记录 - -**测试时间**:_____________ -**测试人员**:_____________ - -#### 预设模板测试 - -| 检查项 | 状态 | 备注 | -|--------|------|------| -| 模板选择 | ⬜ 通过 / ⬜ 失败 | | -| 文献选择 | ⬜ 通过 / ⬜ 失败 | | -| 任务执行 | ⬜ 通过 / ⬜ 失败 | | -| 进度显示 | ⬜ 通过 / ⬜ 失败 | | -| 结果表格 | ⬜ 通过 / ⬜ 失败 | | -| Excel导出 | ⬜ 通过 / ⬜ 失败 | | - -#### 自定义模板测试 - -| 检查项 | 状态 | 备注 | -|--------|------|------| -| 提示词输入 | ⬜ 通过 / ⬜ 失败 | | -| 任务执行 | ⬜ 通过 / ⬜ 失败 | | -| 文本块显示 | ⬜ 通过 / ⬜ 失败 | | -| Excel导出 | ⬜ 通过 / ⬜ 失败 | | - -#### 边界测试 - -| 检查项 | 状态 | 备注 | -|--------|------|------| -| <3篇提示 | ⬜ 通过 / ⬜ 失败 | | -| >50篇提示 | ⬜ 通过 / ⬜ 失败 | | -| 空提示词拦截 | ⬜ 通过 / ⬜ 失败 | | -| 失败重试 | ⬜ 通过 / ⬜ 失败 | | - -### 发现的问题 - -(记录测试中发现的任何问题) - -1. -2. -3. - ---- - -## 🎉 Phase 3 开发总结 - -### 开发成果 - -**代码统计**: -- 后端:~1,700行(6个新文件 + 2个修改) -- 前端:~2,300行(14个新文件 + 2个修改) -- **总计:~4,000行代码** - -**开发时间**: -- 设计:1小时 -- 后端:2小时 -- 前端:4小时 -- 测试:待验证 -- **总计:~8小时(1个工作日)** - ---- - -### 技术亮点 - -1. **双模板系统** - - 预设模板:8字段结构化 - - 自定义模板:文本块灵活 - -2. **并发控制** - - p-queue固定3并发 - - Promise.allSettled容错 - -3. **容错JSON解析** - - 支持多种AI输出格式 - - 自动填充缺失字段 - -4. **完整Excel导出** - - 双Sheet设计 - - 数据可追溯 - -5. **三栏布局** - - 流程清晰 - - 步骤引导 - ---- - -### 核心价值 - -**效率提升**: -- 手动处理:10篇 × 10分钟 = 100分钟 -- 批处理:10篇 × 20秒 = ~7分钟 -- **提升约14倍** 🚀 - -**适用场景**: -- 系统性文献综述 -- 批量信息提取 -- 多文献对比研究 -- 结构化数据归档 - ---- - -## 📄 相关文档 - -### 设计与开发 -- `docs/05-每日进度/Phase3-批处理模式-完整设计.md` - 完整设计方案 -- `docs/05-每日进度/Phase3-完成总结.md` - 完成报告 -- `Phase3-Day1-后端完成总结.md` - 后端详情 -- `Phase3-快速参考.md` - 快速参考 -- `docs/04-开发计划/开发里程碑.md` - 整体进度 - -### 测试工具 -- `backend/test-batch-api.js` - 后端API测试脚本 - ---- - -## 🎯 下一步 - -### 立即执行(必需) - -1. **启动Frontend测试** - ```bash - cd frontend - npm run dev - ``` - -2. **端到端测试**(15-30分钟) - - 预设模板测试 - - 自定义模板测试 - - 边界测试 - -3. **问题修复**(如有) - - 记录问题 - - 修复bug - - 重新测试 - -4. **验收决策** - - 通过 → Phase 3完成 🎉 - - 有问题 → 继续修复 - ---- - -### 可选优化(Phase 3.1) - -- WebSocket进度推送(替代HTTP轮询) -- 任务历史记录 -- 更多预设模板 -- 结果可视化 -- 智能文献选择 - ---- - -## ✅ 完成标志 - -**Phase 3批处理模式验收通过标准**: - -1. ✅ 能进入批处理模式 -2. ✅ 能选择模板和文献 -3. ✅ 能执行任务(3-50篇) -4. ✅ 进度显示正常 -5. ✅ 结果表格正确(预设8列/自定义3列) -6. ✅ Excel导出成功 -7. ✅ 无阻塞性bug - -**如果以上全部通过 → Phase 3 正式完成!** 🎉 - ---- - -**测试指南版本**:1.0 -**创建时间**:2025-10-13 -**状态**:✅ 准备就绪,等待测试 - ---- - -**祝测试顺利!** 🚀 - - - - - - - - - diff --git a/README-Phase2测试.md b/README-Phase2测试.md deleted file mode 100644 index 1d1e1fe3..00000000 --- a/README-Phase2测试.md +++ /dev/null @@ -1,399 +0,0 @@ -# 📦 Phase 2 测试工具包 - 快速索引 - -**创建日期**:2025-10-13 -**Phase 2完成**:双模式智能问答系统(全文阅读 + 逐篇精读) - ---- - -## 🎯 测试目标 - -验证以下核心功能: -- ✅ Python文档提取微服务(PDF/Docx/Txt) -- ✅ Token精确计数和智能管理 -- ✅ 全文阅读模式(35-50篇综合分析) -- ✅ 逐篇精读模式(1-5篇深度分析) -- ✅ 文献切换和对话历史独立性 - ---- - -## 📚 测试工具(共5个) - -### 1️⃣ 【入门必读】如何测试Phase2.md ⭐ -**用途**:测试工具使用指南 -**内容**: -- 测试策略建议 -- 工具选择指南 -- 常见场景处理 -- 问题记录管理 - -**适合**:第一次测试前阅读 - ---- - -### 2️⃣ 【环境检查】检查测试环境.bat -**用途**:自动检查测试环境是否就绪 -**耗时**:1分钟 - -**使用**: -```bash -# 双击运行 -检查测试环境.bat -``` - -**输出**: -- 🟢 测试环境完全就绪 -- 🟡 基本就绪,有小问题 -- 🔴 有严重问题需要修复 - ---- - -### 3️⃣ 【快速测试】Phase2-快速测试清单.md ⭐⭐⭐ -**用途**:30分钟快速验证核心功能 -**耗时**:30分钟 - -**适合场景**: -- ✅ 日常开发测试 -- ✅ 代码更新后验证 -- ✅ 问题修复后回归测试 - -**测试内容**: -- Step 1: 启动服务(2分钟) -- Step 2: 准备数据(10分钟) -- Step 3: 核心功能测试(15分钟) - - 全文阅读模式(5分钟) - - 逐篇精读模式(10分钟) -- Step 4: 快速验收(3分钟) - ---- - -### 4️⃣ 【完整测试】Phase2-测试指南.md -**用途**:90分钟完整功能验证 -**耗时**:60-90分钟 - -**适合场景**: -- ✅ Phase 2正式验收 -- ✅ 上线前的完整测试 -- ✅ 详细的功能测试 - -**测试内容**: -- Part 1: 环境准备(10分钟) -- Part 2: 服务启动验证(5分钟) -- Part 3: 知识库准备(15-20分钟) -- Part 4: 全文阅读模式测试(15分钟) - - 8个详细测试用例 -- Part 5: 逐篇精读模式测试(20分钟) - - 8个详细测试用例 -- Part 6: 端到端场景测试(15分钟) - - 3个真实用户场景 -- Part 7: 问题记录与汇总(5分钟) -- 附录: 常见问题排查 - ---- - -### 5️⃣ 【服务启动】一键启动.bat -**用途**:自动启动所有服务 -**耗时**:30秒启动 + 10秒等待 - -**启动内容**: -- PostgreSQL容器 -- Redis容器 -- Python微服务(8000端口) -- Backend服务(3001端口) -- Frontend服务(3000端口) - -**使用**: -```bash -# 双击运行 -一键启动.bat -``` - ---- - -## 🚀 快速开始(3步) - -### 新手路线 🔰 - -``` -第1步: 阅读入门必读 - ↓ -第2步: 运行环境检查 - ↓ -第3步: 打开快速测试清单 - ↓ -开始测试! -``` - -**推荐文件顺序**: -1. `如何测试Phase2.md` - 先看使用说明 -2. `检查测试环境.bat` - 检查环境 -3. `Phase2-快速测试清单.md` - 开始快速测试 - ---- - -### 老手路线 ⚡ - -``` -第1步: 运行环境检查 - ↓ -第2步: 启动服务 - ↓ -第3步: 直接开始测试 -``` - -**文件顺序**: -1. `检查测试环境.bat` -2. `一键启动.bat` -3. `Phase2-快速测试清单.md` 或 `Phase2-测试指南.md` - ---- - -## 📊 测试方案选择 - -### 情况1:第一次测试(首次验收) - -**推荐**:完整测试指南(90分钟) - -**原因**: -- 全面了解系统功能 -- 发现所有潜在问题 -- 为后续测试建立基准 - -**流程**: -``` -环境检查 → 完整测试 → 记录问题 → 修复 → 快速测试验证 -``` - ---- - -### 情况2:日常开发测试 - -**推荐**:快速测试清单(30分钟) - -**原因**: -- 快速验证核心功能 -- 节省时间 -- 适合频繁测试 - -**流程**: -``` -环境检查 → 快速测试 → 发现问题即停止修复 -``` - ---- - -### 情况3:问题修复后验证 - -**推荐**:快速测试清单(30分钟) - -**原因**: -- 针对性验证修复效果 -- 回归测试核心功能 -- 快速确认问题解决 - -**流程**: -``` -环境检查 → 针对问题点测试 → 快速回归测试 -``` - ---- - -### 情况4:正式验收/上线前 - -**推荐**:完整测试指南(90分钟) - -**原因**: -- 最终质量保证 -- 全面功能验证 -- 性能指标确认 - -**流程**: -``` -环境检查 → 完整测试 → 端到端场景 → 性能验证 → 最终决策 -``` - ---- - -## 🎯 测试检查清单 - -### 测试前准备 -- [ ] 阅读测试使用说明 -- [ ] 运行环境检查脚本 -- [ ] 确保环境显示🟢就绪 -- [ ] 准备测试文档(7-10个) -- [ ] 预留足够测试时间 - -### 测试执行 -- [ ] 启动所有服务 -- [ ] 验证服务健康状态 -- [ ] 准备知识库和文档 -- [ ] 执行测试用例 -- [ ] 记录测试结果 - -### 测试后处理 -- [ ] 汇总测试结果 -- [ ] 整理问题清单 -- [ ] 按优先级排序问题 -- [ ] 制定修复计划 -- [ ] 做出验收决策 - ---- - -## 📝 测试结果记录 - -### 建议创建测试记录文件夹 - -``` -AIclinicalresearch/ -├── 测试记录/ -│ ├── 2025-10-13-首次测试.md -│ ├── 2025-10-14-问题修复验证.md -│ ├── 2025-10-15-正式验收.md -│ └── 问题清单.md -``` - -### 测试记录模板 - -```markdown -# Phase 2 测试记录 - [日期] - -## 测试信息 -- 测试人员:______ -- 测试时间:______ -- 测试类型:快速测试 / 完整测试 -- 测试耗时:______分钟 - -## 测试结果 -- 全文阅读模式:✅ / ⚠️ / ❌ -- 逐篇精读模式:✅ / ⚠️ / ❌ -- 文献切换:✅ / ⚠️ / ❌ -- 对话历史:✅ / ⚠️ / ❌ - -## 发现的问题 -### 严重问题(🔴) -1. [描述] - -### 中等问题(🟡) -1. [描述] - -### 轻微问题(🟢) -1. [描述] - -## 最终结论 -✅ 通过 / ⚠️ 有条件通过 / ❌ 需返工 -``` - ---- - -## 🔧 常见问题快速索引 - -### Q1: 环境检查失败怎么办? -**A**: 查看 `如何测试Phase2.md` → "场景1:环境检查失败" - -### Q2: 服务启动后报错? -**A**: 查看 `Phase2-测试指南.md` → "附录A:常见问题排查" - -### Q3: 文档一直"处理中"? -**A**: 查看 `Phase2-测试指南.md` → "附录A.2" - -### Q4: 容量显示不准确? -**A**: 查看 `Phase2-测试指南.md` → "附录A.3" - -### Q5: 文献选择器空白? -**A**: 查看 `Phase2-测试指南.md` → "附录A.4" - -### Q6: 文献切换历史丢失? -**A**: 查看 `Phase2-测试指南.md` → "附录A.5" - ---- - -## 📞 需要帮助? - -### 测试过程中遇到问题 -1. 先查看测试指南的"常见问题排查" -2. 查看浏览器Console错误(F12) -3. 记录详细问题和截图 -4. 整理问题清单 - -### 不确定测试结果 -**参考标准**: -- ✅ 功能正常,无错误 → 通过 -- ⚠️ 基本正常,有小问题 → 有条件通过 -- ❌ 无法工作,严重错误 → 不通过 - ---- - -## 🎊 测试完成后 - -### ✅ 如果验收通过 - -**恭喜!Phase 2验收通过!** 🎉 - -**下一步选择**: - -#### 选项A:开发Phase 3(批处理模式)⭐ 推荐 -- 时间:1-2天 -- 内容:批量文献处理、结果汇总、导出 - -#### 选项B:开发里程碑2(11个智能体) -- 时间:2-3周 -- 内容:开发其余11个专业智能体 - ---- - -### ⚠️ 如果有问题需要修复 - -**处理流程**: -1. 问题分类(严重/中等/轻微) -2. 优先级排序 -3. 制定修复计划 -4. 修复后用快速测试验证 -5. 重新验收 - ---- - -## 📚 相关文档 - -### Phase 2开发文档 -- [Phase2-最终技术方案.md](./docs/05-每日进度/Phase2-最终技术方案.md) -- [Phase2-完成报告.md](./docs/05-每日进度/Phase2-完成报告.md) -- [Phase2-最终总结.md](./docs/05-每日进度/Phase2-最终总结.md) - -### 开发计划 -- [开发里程碑.md](./docs/04-开发计划/开发里程碑.md) - ---- - -## 🎯 工具包总结 - -| 文件 | 用途 | 耗时 | 优先级 | -|------|------|------|--------| -| 如何测试Phase2.md | 使用指南 | 5分钟阅读 | ⭐⭐⭐ | -| 检查测试环境.bat | 环境检查 | 1分钟 | ⭐⭐⭐ | -| Phase2-快速测试清单.md | 快速测试 | 30分钟 | ⭐⭐⭐ | -| Phase2-测试指南.md | 完整测试 | 90分钟 | ⭐⭐ | -| 一键启动.bat | 启动服务 | 1分钟 | ⭐⭐⭐ | - ---- - -**准备好开始测试了吗?** 🚀 - -**推荐第一步**: -1. 打开 `如何测试Phase2.md` 了解全貌(5分钟) -2. 运行 `检查测试环境.bat` 验证环境(1分钟) -3. 运行 `一键启动.bat` 启动服务(1分钟) -4. 打开 `Phase2-快速测试清单.md` 开始测试(30分钟) - -**总耗时**:约40分钟完成首次快速测试 - ---- - -**祝测试顺利!** 🎉 - - - - - - - - - diff --git a/README-里程碑1.5完成.md b/README-里程碑1.5完成.md deleted file mode 100644 index 859a48c5..00000000 --- a/README-里程碑1.5完成.md +++ /dev/null @@ -1,143 +0,0 @@ -# 🎉 里程碑1.5 全部完成! - -**完成时间**: 2025-10-12 (Day 26-27) -**开发时长**: 2天 -**状态**: ✅ 全部功能已完成并测试通过 - ---- - -## 📊 完成成果 - -### **RAG优化** -- ✅ 检索数量:3 → 15(**5倍**) -- ✅ Chunk大小:500 → 1500 tokens(**3倍**) -- ✅ 总覆盖:1,500 → 22,500 tokens(**15倍**) -- ✅ 覆盖率:~5% → ~40-50%(**8-10倍**) - -### **智能引用系统** -- ✅ 后端自动收集引用信息 -- ✅ AI使用 `[来源N]` 标准编号 -- ✅ 自动追加引用清单(文件名+段落+相关度+100字预览) -- ✅ 前端蓝色高亮badge显示 -- ✅ 点击跳转功能 -- ✅ 鼠标悬停效果 -- ✅ 跳转后高亮闪烁 -- ✅ **100%覆盖所有位置**(段落/列表/表格/任何地方) - ---- - -## 🎯 用户体验提升 - -| 指标 | 优化前 | 优化后 | 提升 | -|------|--------|--------|------| -| 引用可追溯性 | ❌ 无 | ✅ 100%准确 | ∞ | -| 视觉区分度 | ⭐ | ⭐⭐⭐⭐⭐ | 5倍 | -| 交互便捷性 | ❌ 无 | ✅ 一键跳转 | ∞ | -| 检索覆盖率 | ~5% | ~40-50% | 8-10倍 | - ---- - -## 📝 创建的文档 - -### **进度文档**(4个) -1. `docs/05-每日进度/Day26-RAG参数优化完成.md` -2. `docs/05-每日进度/Day27-智能引用功能开发完成.md` -3. `docs/05-每日进度/Day27B-引用标记高亮显示.md` -4. `docs/05-每日进度/Day27C-里程碑1.5全部完成.md` - -### **测试指南**(2个) -1. `PHASE1-测试指南.md` -2. `智能引用功能-测试指南.md` - -### **技术文档**(1个) -1. `docs/05-每日进度/里程碑1.5-RAG优化与全文精读计划.md` - ---- - -## 🛠️ 修改的文件 - -### **后端**(5个文件) -1. `backend/src/services/conversationService.ts` - 引用收集、格式化、追加 -2. `backend/src/controllers/chatController.ts` - 智能问答引用支持 -3. `backend/src/services/knowledgeBaseService.ts` - top_k参数 -4. `backend/src/controllers/knowledgeBaseController.ts` - top_k参数 -5. `backend/src/clients/DifyClient.ts` - chunk大小 - -### **前端**(1个文件) -1. `frontend/src/components/chat/MessageList.tsx` - 引用高亮、跳转、交互 - -### **代码统计** -- **后端**: ~300行 -- **前端**: ~150行 -- **文档**: ~2000行 -- **总计**: ~2450行 - ---- - -## 🚀 下一步计划 - -### **里程碑2:扩展智能体** - -**开始时间**: Day 28起 -**目标**: 开发其他11个智能体 -**预计时间**: 2-3周 -**工作量**: 每个智能体约1天 - -**智能体列表**(还需开发11个): -1. ✅ 选题评价智能体(已完成) -2. ⏳ 科学问题梳理 -3. ⏳ PICOS构建 -4. ⏳ 研究设计 -5. ⏳ 文献综述 -6. ⏳ 统计分析规划 -7. ⏳ 方案撰写 -8. ⏳ 伦理申报 -9. ⏳ 论文撰写 -10. ⏳ 投稿准备 -11. ⏳ 数据分析 -12. ⏳ 图表制作 - ---- - -## 📄 文档位置 - -- **开发里程碑**: `docs/04-开发计划/开发里程碑.md` -- **进度文档**: `docs/05-每日进度/` -- **测试指南**: 根目录 - ---- - -## 💡 技术亮点 - -1. **RAG优化**: 15倍覆盖率提升,成本仅增加3倍 -2. **100%准确**: 引用信息从检索结果直接生成,不依赖AI -3. **智能截断**: 上下文预览支持中英文句子边界识别 -4. **事件委托**: 高性能的交互处理方式 -5. **文本预处理**: 简单高效的全覆盖解决方案 -6. **向后兼容**: 不影响不使用知识库的场景 - ---- - -## 🎊 成就解锁 - -- 🏆 **RAG检索大师**: 15倍覆盖率提升 -- 📚 **引用追踪专家**: 100%准确的引用系统 -- 🎨 **UX优化达人**: 完美的视觉和交互体验 -- 🐛 **Bug终结者**: 连续解决3个技术难题 -- 📖 **文档狂魔**: 13篇详细文档 -- ⚡ **效率达人**: 2天完成预计2周的工作 - ---- - -## 🙏 感谢 - -感谢用户的耐心测试和反馈! -感谢DeepSeek-V3、Qwen、Dify等技术支持! - ---- - -**准备好开始里程碑2了吗?** 🚀 - -开发其他11个智能体,让系统功能更完整! - - diff --git a/README.md b/README.md deleted file mode 100644 index 22071b6c..00000000 --- a/README.md +++ /dev/null @@ -1,136 +0,0 @@ -# AI科研助手 - -> 专注于赋能临床及科研人员的智能化平台 - -## 📚 项目文档 - -### 📖 文档导航中心 -- **[📚 完整文档导航](./docs/README.md)** ⭐ 查看所有设计文档和开发规范 - -### 🔗 快速链接 -- [产品需求文档(PRD)](./docs/00-项目概述/产品需求文档(PRD).md) - 了解产品需求 -- [技术架构总览](./docs/00-项目概述/技术架构总览.md) - 了解技术方案 -- [数据库设计文档](./docs/01-设计文档/数据库设计文档.md) - 理解数据结构 -- [API设计规范](./docs/01-设计文档/API设计规范.md) - 掌握接口定义 -- [开发里程碑](./docs/04-开发计划/开发里程碑.md) - 查看开发进度 - -### 🛠️ 子项目文档 -- **[⚙️ 后端开发指南](./backend/README.md)** - Node.js + Fastify + Prisma -- **[🎨 前端开发指南](./frontend/README.md)** - React + TypeScript + Ant Design - -## 🏗️ 技术栈 - -### 前端 -- React 18 + TypeScript -- Vite -- TailwindCSS -- Zustand -- LobeChat组件 - -### 后端 -- Node.js + Fastify + TypeScript -- Prisma ORM -- PostgreSQL -- Redis - -### 第三方服务 -- Dify(RAG知识库) -- DeepSeek-V3(主力LLM) -- Qwen3(备用LLM) - -## ✨ 核心功能 - -### 1. 智能问答系统 -- 基于项目背景的上下文对话 -- 支持@知识库引用 -- 3种对话模式:RAG快速检索、全文阅读、批处理 - -### 2. 知识库管理 -- 文档上传与管理(支持PDF/Word/Txt) -- 智能文本提取(Python微服务) -- RAG检索优化(top_k=15, chunk_size=1500) - -### 3. 批处理模式 -- 批量提取结构化信息(3-50个文档) -- 预设模板+自定义Prompt -- Excel导出 - -### 4. 稿件审查功能 ⭐ 新增 -- **双维度智能评估**: - - 稿约规范性评估(11个标准) - - 方法学评估(3个部分,20个检查点) -- **完整工作流程**: - - Word文档上传(.doc/.docx) - - 实时进度展示 - - 详细评估报告 - - PDF导出+文本复制 -- **多模型支持**:DeepSeek-V3 / Qwen3-72B / Qwen-Long -- **独立导航入口**:左侧菜单"稿件审查" - -### 5. 12个智能体(规划中) -- ✅ 选题评价智能体(已完成) -- ⏳ 其他11个智能体(计划中) - -## 🚀 快速开始 - -### 1. 启动基础服务 - -```bash -# 启动PostgreSQL和Redis -docker-compose up -d -``` - -### 2. 后端开发 - -```bash -cd backend -npm install -npm run dev -``` - -### 3. 前端开发 - -```bash -cd frontend -npm install -npm run dev -``` - -## 📦 目录结构 - -``` -AIclinicalresearch/ -├── frontend/ # 前端项目 -├── backend/ # 后端项目 -├── docs/ # 项目文档 -├── docker-compose.yml # Docker配置 -└── README.md # 本文件 -``` - -## 🔑 环境变量 - -请参考 `.env.example` 文件配置环境变量。 - -## 📖 开发指南 - -请查看 [开发里程碑](./docs/04-开发计划/开发里程碑.md) 了解详细的开发计划。 - -## 📄 License - -MIT - ---- - -## 🔗 相关链接 - -- [📚 文档中心](./docs/README.md) - 完整的项目文档导航 -- [⚙️ 后端项目](./backend/README.md) - 后端开发指南 -- [🎨 前端项目](./frontend/README.md) - 前端开发指南 -- [🚀 快速启动指南](./启动指南.md) - 一步步启动项目 -- [🐳 Dify部署方案](./Dify完整部署方案.md) - Dify部署指南 - ---- - -**当前开发阶段:** 里程碑1 - Day 6(前端基础架构) -**开发进度:** 50% - 前后端基础架构已完成 - diff --git a/Redcap 问题原因排查.md b/Redcap 问题原因排查.md deleted file mode 100644 index 4ffdf173..00000000 --- a/Redcap 问题原因排查.md +++ /dev/null @@ -1,59 +0,0 @@ -# **REDCap ERR\_CONTENT\_DECODING\_FAILED 错误排查与修复指南** - -如果在浏览器访问 REDCap 时遇到 net::ERR\_CONTENT\_DECODING\_FAILED 200 (OK),说明数据包在传输过程中被“弄脏”了。以下是按优先级排序的 4 种解决方案: - -## **1\. 禁用 PHP 的 zlib 输出压缩 (最常见原因)** - -REDCap 内部有时会尝试自己控制输出压缩,如果你的 php.ini 里也开启了 zlib.output\_compression,两者冲突就会导致解码失败。 - -**操作步骤:** - -1. 找到你挂载到 Docker 容器里的 php.ini 文件。 -2. 搜索 zlib.output\_compression。 -3. 将其设置为 Off: - zlib.output\_compression \= Off - -4. **重启 Docker 容器**:docker-compose restart redcap-web。 - -## **2\. 检查并清理 PHP 文件的 BOM 头** - -如果你编辑过 database.php 或 config.php,并使用了 Windows 记事本,可能会在文件开头插入一个不可见的 **UTF-8 BOM** 字符。这个字符会抢在 Gzip 报文头之前发送,导致浏览器解压失败。 - -**操作步骤:** - -1. 使用 VS Code 或 Notepad++ 打开 database.php。 -2. 检查右下角的编码格式。如果是 UTF-8 with BOM,请将其更改为 **UTF-8** (无 BOM)。 -3. 确保 \ **欢迎新的AI对话!** 👋 -> **本文档是您的快速入口,3分钟了解项目全貌** - ---- - -## 📍 从这里开始 - -### 第一步:阅读核心对接文档 ⭐⭐⭐ - -**必读文档(5-10分钟):** - -📄 **`docs/[AI对接] 项目状态与下一步指南.md`** - -这个文档包含: -- ✅ 项目概况和技术栈 -- ✅ 当前架构状态(前后端、数据库) -- ✅ 已完成的工作 -- ✅ 下一步明确任务 -- ✅ 必读文档清单 -- ✅ 关键技术信息 -- ✅ 常见问题速查 - ---- - -## 🚀 快速上手路径 - -### 如果是全新AI对话 - -``` -阅读顺序: -1️⃣ 本文档(3分钟) -2️⃣ docs/[AI对接] 项目状态与下一步指南.md(5-10分钟) -3️⃣ docs/08-项目管理/下一阶段行动计划-V2.2-完整版.md(15分钟) -4️⃣ docs/00-系统总体设计/前后端模块化架构设计-V2.md(10分钟) -``` - -**总计:** 30-40分钟全面了解项目 - ---- - -### 如果是继续某个具体任务 - -**直接跳转到:** -- **开发ASL模块?** → 读 ASL需求分析文档 + CloseAI集成指南 -- **前端开发?** → 读 前后端模块化架构设计-V2.md(前端章节) -- **后端开发?** → 读 前后端模块化架构设计-V2.md(后端章节) -- **数据库设计?** → 读 Schema隔离架构设计文档 - ---- - -## 🎯 当前状态速览 - -| 项目 | 状态 | 说明 | -|------|------|------| -| **数据库** | ✅ 已完成 | 10个Schema隔离,11个表迁移完成 | -| **后端** | ✅ 功能正常 | Prisma配置完成,API正常工作 | -| **前端** | ✅ 框架完成 | Frontend-v2创建,6个模块占位 | -| **下一步** | 🎯 进行中 | Week 2 Day 7 - 模块注册完善 | -| **重点任务** | 📋 Week 3-4 | ASL模块开发(4个LLM文献筛选) | - ---- - -## 📞 获取帮助 - -**遇到问题时:** - -1. **技术问题** → 查阅 `docs/07-运维文档/` -2. **架构疑问** → 查阅 `docs/00-系统总体设计/` -3. **任务不明** → 查阅 `docs/08-项目管理/下一阶段行动计划-V2.2-完整版.md` -4. **代码规范** → 查阅 `docs/00-系统总体设计/前后端模块化架构设计-V2.md` - ---- - -## 🌟 核心文档清单 - -### 必读文档(⭐⭐⭐) -1. `docs/[AI对接] 项目状态与下一步指南.md` - **从这里开始!** -2. `docs/08-项目管理/下一阶段行动计划-V2.2-完整版.md` - 详细计划 -3. `docs/00-系统总体设计/前后端模块化架构设计-V2.md` - 架构设计 - -### 重要文档(⭐⭐) -4. `docs/09-架构实施/01-Schema隔离架构设计(10个).md` - 数据库设计 -5. `docs/02-通用能力层/01-LLM大模型网关/03-CloseAI集成指南.md` - LLM配置 - -### 参考文档(⭐) -6. `docs/03-业务模块/AIA-AI智能问答/02-技术设计/01-数据库设计.md` -7. `docs/03-业务模块/PKB-个人知识库/02-技术设计/01-数据库设计.md` -8. `docs/01-平台基础层/06-前端架构/01-前端总体架构设计.md` - ---- - -## ⚡ 一句话总结 - -**我们正在构建一个模块化的AI临床研究平台,已完成数据库Schema隔离和前端框架搭建,下一步是完善前端模块机制,然后在Week 3-4开发AI智能文献模块(支持4个LLM的文献筛选)。** - ---- - -**文档路径:** `AIclinicalresearch/START-HERE-FOR-AI.md` -**最后更新:** 2025-11-12 18:00 - -**🤖 祝新的AI对话顺利!** 🚀 - - - - - - - - - - - - - - - - diff --git a/START-HERE-FOR-NEW-AI.md b/START-HERE-FOR-NEW-AI.md deleted file mode 100644 index 6275746b..00000000 --- a/START-HERE-FOR-NEW-AI.md +++ /dev/null @@ -1,246 +0,0 @@ -# 🚀 新AI启动指令(2分钟快速上手) - -> **日期:** 2025-11-18 -> **状态:** 平台基础设施已完成,立即开始ASL模块开发 -> **阅读时间:** 2分钟 - ---- - -## 📋 项目现状(一句话) - -**医学科研AI平台,基础设施已完成(8个核心模块+5个LLM模型),现在开发ASL文献筛选模块的第一个功能:标题摘要初筛。所有依赖就绪,可立即开始!** - ---- - -## ✅ 已完成的工作(你的优势) - -| 完成时间 | 工作内容 | 状态 | -|---------|---------|------| -| 2025-11-17 | **平台基础设施**(8个模块) | ✅ 100% | -| | - 存储服务(本地/OSS切换) | ✅ | -| | - 日志系统(结构化JSON) | ✅ | -| | - 缓存服务(Memory/Redis) | ✅ | -| | - 异步任务队列 | ✅ | -| | - 健康检查+监控 | ✅ | -| | - 数据库连接池+环境配置 | ✅ | -| 2025-11-18 | **CloseAI集成**(GPT-4o+Claude) | ✅ 100% | -| | - GPT-4o: 1.5秒响应 ⭐ | ✅ | -| | - Claude-4.5: 2.8秒响应 | ✅ | -| | - 双模型筛选: 4.8秒 | ✅ | -| Week 1-2 | 前后端架构+文档 | ✅ 100% | - ---- - -## 🎯 你的任务(ASL模块开发) - -### 第一步:定义数据库Schema(2小时) - -**文件位置:** `backend/prisma/schema.prisma` - -**需要添加4个模型:** -1. `AslScreeningProject` - 筛选项目 -2. `AslLiterature` - 文献信息 -3. `AslScreeningResult` - 筛选结果 -4. `AslScreeningTask` - 筛选任务 - -**⚠️ 关键要求:** -- 每个模型必须添加 `@@schema("asl_schema")` -- 参考 `docs/03-业务模块/ASL-AI智能文献/04-开发计划/02-标题摘要初筛开发计划.md` Week 1 Day 1(第299-402行有完整代码) - -**执行命令:** -```bash -cd backend -npx prisma migrate dev --name add_asl_screening_tables -npx prisma generate -``` - ---- - -## 📚 必读文档(3个核心文档) - -### 1️⃣ 系统全貌(20分钟)⭐⭐⭐ -**`docs/00-系统总体设计/00-系统当前状态与开发指南.md`** - -**为什么必读:** -- 包含平台基础设施使用方法(storage/logger/cache/jobQueue) -- 包含5个LLM模型的调用方式 -- 包含云原生开发规范(必须遵守) -- 包含禁止的操作清单 - -**重点章节:** -- Part 1.3:后端架构 - 平台基础设施(必读) -- Part 2.3:云原生开发规范(必须遵守) -- Part 3:重要原则与禁忌(必须遵守) - ---- - -### 2️⃣ ASL开发计划(20分钟)⭐⭐⭐ -**`docs/03-业务模块/ASL-AI智能文献/04-开发计划/02-标题摘要初筛开发计划.md`** - -**为什么必读:** -- Week 1 Day 1 包含完整的Prisma Schema代码(可直接复制) -- 每一天的开发任务详细说明 -- 包含LLM筛选服务代码示例 - -**重点章节:** -- Week 1 Day 1:数据库Schema设计(第299-402行) -- Week 2 Day 1:LLM筛选核心实现(第403-530行) -- 云原生开发注意事项(第77-162行) - ---- - -### 3️⃣ 任务分解清单(15分钟)⭐⭐ -**`docs/03-业务模块/ASL-AI智能文献/04-开发计划/03-任务分解.md`** - -**为什么必读:** -- 80+个详细任务,每个有ID、耗时、验收标准 -- 按天组织,清晰明确 -- 包含云原生开发要求 - -**重点章节:** -- T1.1.1 - T1.1.5:数据库Schema设计任务 -- 云原生开发要求(第61-143行) - ---- - -## ⭐ 核心代码示例(直接使用) - -### 1. 使用平台基础设施 - -```typescript -// ✅ 必须使用平台服务 -import { storage, logger, cache, jobQueue } from '@/common' -import { prisma } from '@/config/database' - -// 文件上传 -await storage.upload('literature/123.pdf', buffer) - -// 日志记录 -logger.info('Screening started', { projectId, count }) - -// 缓存LLM响应 -await cache.set('llm:key', response, 3600) - -// 异步任务 -const job = await jobQueue.push('asl:screening', data) - -// 数据库操作 -await prisma.aslProject.create({ data: {...} }) -``` - -### 2. 调用LLM(双模型筛选) - -```typescript -import { LLMFactory } from '@/common/llm/adapters' - -// 并行调用两个模型(4.8秒完成) -const [deepseekResult, gpt4oResult] = await Promise.all([ - LLMFactory.getAdapter('deepseek-v3').chat(messages), - LLMFactory.getAdapter('gpt-5').chat(messages) // 实际使用 gpt-4o -]) - -// 判断一致性 -if (deepseekResult.decision === gpt4oResult.decision) { - // 共识度高,直接采纳 -} else { - // 不一致,标记为需要人工复核 -} -``` - -### 3. Excel内存解析(云原生) - -```typescript -import xlsx from 'xlsx' - -// ✅ 正确:内存解析 -const workbook = xlsx.read(buffer, { type: 'buffer' }) - -// ❌ 错误:不要保存到磁盘 -// fs.writeFileSync('./temp.xlsx', buffer) // 禁止! -``` - ---- - -## ⚠️ 必须遵守的规范 - -### 禁止的操作(会被拒绝) - -1. ❌ `fs.writeFileSync()` - 使用 `storage.upload()` -2. ❌ `new PrismaClient()` - 使用全局 `prisma` -3. ❌ 同步处理LLM批量任务 - 使用 `jobQueue` -4. ❌ Excel保存到磁盘 - 内存解析 -5. ❌ 重复实现存储/日志/缓存 - 使用平台服务 -6. ❌ 频繁Git提交 - 一天工作结束后统一提交 -7. ❌ 提交未测试的代码 - 必须测试通过 - -### 必须遵守的原则 - -1. ✅ 使用平台基础设施(storage/logger/cache/jobQueue) -2. ✅ Schema隔离(所有表必须 `@@schema("asl_schema")`) -3. ✅ Excel内存解析(不落盘) -4. ✅ 异步处理LLM任务(避免超时) -5. ✅ 使用全局Prisma实例 - ---- - -## 🚀 立即开始的3个步骤 - -```bash -# Step 1: 阅读核心文档(35分钟) -1. docs/00-系统总体设计/00-系统当前状态与开发指南.md(20分钟) -2. docs/03-业务模块/ASL-AI智能文献/04-开发计划/02-标题摘要初筛开发计划.md(15分钟) - -# Step 2: 定义数据库Schema(2小时) -1. 打开 backend/prisma/schema.prisma -2. 复制开发计划文档中的Prisma代码(Week 1 Day 1) -3. 运行迁移:npx prisma migrate dev --name add_asl_screening_tables - -# Step 3: 创建后端目录结构(10分钟) -mkdir -p backend/src/modules/asl/{routes,controllers,services,schemas,types,utils} -``` - ---- - -## 📞 遇到问题? - -| 问题类型 | 查看文档 | -|---------|---------| -| 不了解架构 | `00-系统当前状态与开发指南.md` | -| 不知道怎么用平台服务 | `00-系统当前状态与开发指南.md` Part 1.3 | -| 不知道怎么调用LLM | `02-通用能力层/01-LLM大模型网关/03-CloseAI集成指南.md` | -| 不知道做什么任务 | `03-业务模块/ASL-AI智能文献/04-开发计划/03-任务分解.md` | -| 不知道怎么写代码 | `03-业务模块/ASL-AI智能文献/04-开发计划/02-标题摘要初筛开发计划.md` | - ---- - -## 🎉 准备好了吗? - -**检查清单:** -- [ ] 已阅读本文档(2分钟)✅ -- [ ] 已阅读 `00-系统当前状态与开发指南.md` Part 1.3 和 Part 2.3(10分钟) -- [ ] 已查看 `02-标题摘要初筛开发计划.md` Week 1 Day 1(5分钟) -- [ ] 理解了平台基础设施的使用方式(storage/logger/cache/jobQueue) -- [ ] 理解了5个LLM模型的调用方式 -- [ ] 知道了第一个任务:定义数据库Schema - -**开始开发吧!** 🚀 - ---- - -**详细上下文:** 如需更多信息,查看 `docs/03-业务模块/ASL-AI智能文献/[AI对接] ASL模块快速上下文.md` - -**系统全貌:** `docs/00-系统总体设计/00-系统当前状态与开发指南.md` - -**最后更新:** 2025-11-18 -**更新内容:** 添加平台基础设施和CloseAI集成信息 - - - - - - - - - - - diff --git a/START-开始使用.md b/START-开始使用.md deleted file mode 100644 index 3e83b0a4..00000000 --- a/START-开始使用.md +++ /dev/null @@ -1,174 +0,0 @@ -# 🚀 快速开始 - -## ✅ 问题已全部解决! - -### 修复内容 -1. ✅ 前端端口:**http://localhost:3000/** (已正确配置) -2. ✅ 获取项目列表失败:**已创建模拟用户** -3. ✅ 创建项目500错误:**外键约束已解决** - ---- - -## 🎯 快速启动(3步) - -### 方法1:一键启动 ⭐ 推荐 -```powershell -双击运行:一键启动.bat -``` -会自动: -- 启动Docker容器(PostgreSQL + Redis) -- 启动后端服务(新窗口) -- 启动前端服务(新窗口) -- 10秒后自动打开浏览器 - ---- - -### 方法2:手动启动 - -**终端1 - 后端:** -```powershell -cd d:\MyCursor\AIclinicalresearch\backend -npm run dev -``` - -**终端2 - 前端:** -```powershell -cd d:\MyCursor\AIclinicalresearch\frontend -npm run dev -``` - -**浏览器:** -``` -访问 http://localhost:3000/ -``` - ---- - -## 🧪 测试流程 - -### 1. 创建项目 -1. 打开 http://localhost:3000/ -2. 点击右上角"+ 新建项目" -3. 填写: - - 项目名称:心血管疾病研究 - - 研究类型:观察性研究 - - 项目背景:研究心血管疾病与生活方式的关系... -4. 点击"创建" ✅ **现在应该成功了!** - -### 2. 选择智能体 -1. 在首页点击"选题评价智能体"卡片 -2. 进入对话界面 - -### 3. 开始对话 -1. 在输入框输入: - ``` - 请评价这个研究选题: - 高血压患者服药依从性的影响因素研究 - ``` -2. 按Enter发送 -3. 观察AI实时流式回复 🔥 -4. 尝试切换模型(右上角): - - DeepSeek-V3(推荐) - - Qwen3-72B - -### 4. 测试Markdown渲染 -让AI生成包含代码的回复: -``` -请用Python写一个计算BMI的函数 -``` - ---- - -## 🔧 如果仍有问题 - -### 运行诊断工具 -```powershell -双击运行:诊断问题.bat -``` - -会检查: -- Docker运行状态 -- PostgreSQL容器 -- Redis容器 -- 后端服务 -- 前端服务 -- 环境变量配置 -- 数据库迁移 - ---- - -## 📋 系统状态确认 - -当前系统状态(应该全部OK): - -- [x] ✅ Docker运行中 -- [x] ✅ PostgreSQL容器运行中 -- [x] ✅ Redis容器运行中 -- [x] ✅ 后端健康检查通过(http://localhost:3001/health) -- [x] ✅ 模拟用户已创建(user-mock-001) -- [x] ✅ 前端代理配置正确(/api -> 3001) -- [x] ✅ 测试项目已创建 - ---- - -## 💡 重要说明 - -### 当前登录用户 -- **用户ID:** user-mock-001 -- **用户名:** 演示用户 -- **邮箱:** demo@example.com - -> 📌 注意:这是模拟用户,用于开发测试。 -> 后续版本会集成真实的用户认证系统。 - -### API端口 -- **后端:** http://localhost:3001 -- **前端:** http://localhost:3000 -- **前端API代理:** `/api` -> `http://localhost:3001` - ---- - -## 🎉 系统功能 - -### 已实现 ✅ -- [x] 项目管理(CRUD) -- [x] 智能体配置(12个) -- [x] AI对话(流式输出) -- [x] Markdown渲染 -- [x] 代码高亮 -- [x] 模型切换 -- [x] @知识库UI - -### 待开发 ⏳ -- [ ] 知识库系统(Dify RAG) -- [ ] 历史对话管理 -- [ ] 其他11个智能体 -- [ ] 用户认证集成 -- [ ] 运营管理后台 - ---- - -## 📖 更多文档 - -- **完整测试指南:** `测试和启动.md` -- **开发文档:** `docs/README.md` -- **API文档:** `docs/02-技术设计/API设计文档.md` -- **数据库文档:** `docs/02-技术设计/数据库设计文档.md` - ---- - -## 🆘 需要帮助? - -如果遇到任何问题,请提供: -1. 后端终端的日志截图 -2. 浏览器控制台的错误信息 -3. `诊断问题.bat` 的输出结果 - ---- - -**🎊 现在可以愉快地使用系统了!** -**祝您测试顺利!** 🚀 - - - - diff --git a/aiclinicalresearch.code-workspace b/aiclinicalresearch.code-workspace new file mode 100644 index 00000000..876a1499 --- /dev/null +++ b/aiclinicalresearch.code-workspace @@ -0,0 +1,8 @@ +{ + "folders": [ + { + "path": "." + } + ], + "settings": {} +} \ No newline at end of file diff --git a/backend/AIclinicalresearch/backend/backup_20260118_172651.sql b/backend/AIclinicalresearch/backend/backup_20260118_172651.sql deleted file mode 100644 index 4721dc1d..00000000 Binary files a/backend/AIclinicalresearch/backend/backup_20260118_172651.sql and /dev/null differ diff --git a/backend/src/common/jobs/PgBossQueue.ts b/backend/src/common/jobs/PgBossQueue.ts index 14d38f10..1cc94e86 100644 --- a/backend/src/common/jobs/PgBossQueue.ts +++ b/backend/src/common/jobs/PgBossQueue.ts @@ -50,7 +50,7 @@ export class PgBossQueue implements JobQueue { this.boss = new PgBoss({ connectionString, schema, // 使用platform_schema - max: 4, // 🛡️ 限制连接数,避免挤占 Prisma 连接配额(RDS 限制 100) + max: 5, // 🛡️ 限制连接数,避免挤占 Prisma 连接配额(RDS 限制 100) application_name: 'aiclinical-queue', // 调度配置 @@ -143,21 +143,13 @@ export class PgBossQueue implements JobQueue { // 存储元数据到缓存 this.jobs.set(jobId, job) - // 确保队列存在(幂等操作) - try { - await this.boss.createQueue(type, { - retryLimit: 3, - retryDelay: 60, - expireInSeconds: 6 * 60 * 60 // 6小时 - }); - } catch (error: any) { - // 队列已存在时会报错,忽略 - if (!error.message?.includes('already exists')) { - throw error; - } - } + // 📝 注意:队列已在 registerBossHandler() 中创建,这里不再重复创建 + // 避免每次 push 都尝试 createQueue 导致重复定义 // 发送任务到pg-boss + // ✅ 使用 singletonKey 防止同一任务被重复入队 + const singletonKey = (data as any).taskId || jobId + const bossJobId = await this.boss.send(type, { ...data, __jobId: jobId, // 嵌入我们的jobId @@ -165,7 +157,9 @@ export class PgBossQueue implements JobQueue { }, { retryLimit: 3, retryDelay: 60, - expireInSeconds: 6 * 60 * 60 // 6小时过期(更适合长批次任务) + expireInSeconds: 6 * 60 * 60, // 6小时过期(更适合长批次任务) + singletonKey, // ✅ 防止同一任务重复入队 + singletonSeconds: 3600, // 1小时内不允许重复 }) console.log(`[PgBossQueue] Job pushed: ${jobId} -> pg-boss:${bossJobId} (type: ${type})`) @@ -222,7 +216,7 @@ export class PgBossQueue implements JobQueue { await this.boss.work>(type, { batchSize: 1, // 每次处理1个任务 - pollingIntervalSeconds: 1 // 每秒轮询一次 + pollingIntervalSeconds: 2 // 每2秒轮询一次(降低频率避免竞态) }, async (bossJobs) => { // pg-boss的work handler接收的是Job数组 const bossJob = bossJobs[0] @@ -230,6 +224,7 @@ export class PgBossQueue implements JobQueue { const { __jobId, __createdAt, ...data } = bossJob.data const jobId = __jobId || randomUUID() + const bossJobId = bossJob.id // pg-boss 自己的 job ID // 获取或创建Job对象 let job = this.jobs.get(jobId) @@ -251,18 +246,33 @@ export class PgBossQueue implements JobQueue { job.updatedAt = new Date() } - console.log(`[PgBossQueue] Processing job: ${jobId} (type: ${type})`) + // ✅ 检查:是否已经处理过这个 job(防止重复处理) + const existingJob = this.jobs.get(jobId) + if (existingJob && existingJob.status === 'completed') { + console.warn(`[PgBossQueue] ⚠️ Job already completed, skipping: ${jobId} (pg-boss: ${bossJobId})`) + return // 跳过已完成的任务 + } + + console.log(`[PgBossQueue] Processing job: ${jobId} (pg-boss: ${bossJobId}, type: ${type})`) try { // 执行用户提供的处理函数 const result = await handler(job) - // 标记为完成 + // 标记为完成(更新内存缓存) await this.completeJob(jobId, result) - return result + // ✅ 重要:pg-boss 12.x work handler 返回 void + // pg-boss 会在 handler 成功返回后自动将任务标记为 completed + console.log(`[PgBossQueue] ✅ Job handler finished successfully: ${jobId} (pg-boss: ${bossJobId})`) + + // 打印最终状态 + const finalJob = this.jobs.get(jobId) + console.log(`[PgBossQueue] Job final status: ${finalJob?.status || 'unknown'}`) } catch (error: any) { - // 标记为失败 + console.error(`[PgBossQueue] ❌ Job handler failed: ${jobId} (pg-boss: ${bossJobId})`, error.message) + + // 标记为失败(更新内存缓存) await this.failJob(jobId, error.message || String(error)) // 抛出错误让pg-boss处理重试 diff --git a/backend/src/common/storage/index.ts b/backend/src/common/storage/index.ts index 41aa1bc7..9f70e322 100644 --- a/backend/src/common/storage/index.ts +++ b/backend/src/common/storage/index.ts @@ -62,4 +62,4 @@ export const storage = StorageFactory.getInstance() * - 系统Logo * - RAG引用的图片 */ -export const staticStorage = StorageFactory.getStaticInstance() +export const staticStorage = StorageFactory.getStaticInstance() \ No newline at end of file diff --git a/backend/src/modules/rvw/services/reviewService.ts b/backend/src/modules/rvw/services/reviewService.ts index 148f5c8b..5f04f5d1 100644 --- a/backend/src/modules/rvw/services/reviewService.ts +++ b/backend/src/modules/rvw/services/reviewService.ts @@ -156,9 +156,14 @@ export async function runReview(params: RunReviewParams): Promise<{ jobId: strin throw new Error('文档尚未提取完成,请稍后再试'); } - // 更新任务状态为reviewing - await prisma.reviewTask.update({ - where: { id: taskId }, + // 🔒 使用原子操作实现幂等性(防止并发请求重复入队) + // updateMany 只会更新匹配条件的记录,返回更新数量 + const updateResult = await prisma.reviewTask.updateMany({ + where: { + id: taskId, + // 只有当状态是 pending/completed/failed 时才允许启动 + status: { in: ['pending', 'completed', 'failed'] } + }, data: { status: 'reviewing', selectedAgents: agents, @@ -171,6 +176,14 @@ export async function runReview(params: RunReviewParams): Promise<{ jobId: strin }, }); + // 🔒 幂等性检查:如果没有更新任何记录,说明任务已经在运行中 + if (updateResult.count === 0) { + logger.warn('[RVW] ⚠️ 任务已在运行中,拒绝重复启动', { taskId, currentStatus: task.status }); + throw new Error('任务已在运行中,请勿重复提交'); + } + + logger.info('[RVW] ✅ 任务状态已原子更新为 reviewing', { taskId }); + // ✅ 推送任务到 pg-boss 队列(Platform-Only架构) const job = await jobQueue.push('rvw_review_task', { taskId, diff --git a/backend/src/modules/rvw/workers/reviewWorker.ts b/backend/src/modules/rvw/workers/reviewWorker.ts index ea310881..80bb98cd 100644 --- a/backend/src/modules/rvw/workers/reviewWorker.ts +++ b/backend/src/modules/rvw/workers/reviewWorker.ts @@ -63,6 +63,29 @@ export function registerReviewWorker() { console.log(` 文本长度: ${extractedText.length} 字符`); try { + // ✅ 检查任务是否已经完成(防止重复处理) + const existingTask = await prisma.reviewTask.findUnique({ + where: { id: taskId }, + select: { status: true, completedAt: true, overallScore: true }, + }); + + if (existingTask?.status === 'completed' && existingTask.completedAt) { + logger.warn('[reviewWorker] ⚠️ Task already completed, skipping', { + jobId: job.id, + taskId, + completedAt: existingTask.completedAt, + overallScore: existingTask.overallScore, + }); + console.log(`\n⚠️ 任务已完成,跳过重复处理`); + console.log(` Task ID: ${taskId}`); + console.log(` 完成时间: ${existingTask.completedAt}`); + console.log(` 得分: ${existingTask.overallScore}`); + return { + taskId, + skipped: true, + reason: 'Task already completed', + }; + } // ======================================== // 1. 运行选中的智能体 // ======================================== diff --git a/check-git-status.ps1 b/check-git-status.ps1 deleted file mode 100644 index 260d6ab1..00000000 --- a/check-git-status.ps1 +++ /dev/null @@ -1,78 +0,0 @@ -# ======================================== -# Git Status Check Script -# 用于关闭 Cursor 前检查是否有未提交的代码 -# ======================================== - -Write-Host "" -Write-Host "========================================" -ForegroundColor Cyan -Write-Host " Git Status Check" -ForegroundColor Cyan -Write-Host "========================================" -ForegroundColor Cyan -Write-Host "" - -# 切换到项目根目录 -$projectRoot = "D:\MyCursor\AIclinicalresearch" -if (Test-Path $projectRoot) { - Set-Location $projectRoot -} else { - Write-Host "❌ Project directory not found: $projectRoot" -ForegroundColor Red - exit 1 -} - -# 检查Git状态 -$status = git status --short - -if ($status) { - Write-Host "⚠️ WARNING: You have uncommitted changes!" -ForegroundColor Red - Write-Host "" - Write-Host "Uncommitted files:" -ForegroundColor Yellow - Write-Host $status - Write-Host "" - Write-Host "┌─────────────────────────────────────────────────────────┐" -ForegroundColor Red - Write-Host "│ ⚠️ Please commit your changes before closing Cursor! │" -ForegroundColor Red - Write-Host "│ │" -ForegroundColor Red - Write-Host "│ 🔴 Uncommitted code may be lost permanently! │" -ForegroundColor Red - Write-Host "└─────────────────────────────────────────────────────────┘" -ForegroundColor Red - Write-Host "" - Write-Host "Quick commit command:" -ForegroundColor Yellow - Write-Host "git add . && git commit -m 'feat: Complete today development' && git push origin master" -ForegroundColor Cyan - Write-Host "" - exit 1 -} else { - Write-Host "✅ All changes committed!" -ForegroundColor Green - Write-Host "" -} - -# 检查是否有未推送的提交 -$unpushed = git log origin/master..HEAD --oneline 2>$null - -if ($unpushed) { - Write-Host "⚠️ WARNING: You have unpushed commits!" -ForegroundColor Yellow - Write-Host "" - Write-Host "Unpushed commits:" -ForegroundColor Yellow - Write-Host $unpushed - Write-Host "" - Write-Host "Push command:" -ForegroundColor Yellow - Write-Host "git push origin master" -ForegroundColor Cyan - Write-Host "" -} else { - Write-Host "✅ All commits pushed to remote!" -ForegroundColor Green - Write-Host "" -} - -# 显示最后一次提交 -Write-Host "Last commit:" -ForegroundColor Cyan -$lastCommit = git log -1 --pretty=format:"%C(yellow)%h%C(reset) - %C(green)%cr%C(reset) - %s" -Write-Host $lastCommit -Write-Host "" - -# 显示当前分支 -$currentBranch = git branch --show-current -Write-Host "Current branch:" -ForegroundColor Cyan -Write-Host $currentBranch -ForegroundColor White -Write-Host "" - -Write-Host "========================================" -ForegroundColor Cyan -Write-Host " Check Complete!" -ForegroundColor Cyan -Write-Host "========================================" -ForegroundColor Cyan -Write-Host "" - diff --git a/docs/00-系统总体设计/00-系统当前状态与开发指南.md b/docs/00-系统总体设计/00-系统当前状态与开发指南.md index 3e1fe2c4..201484fe 100644 --- a/docs/00-系统总体设计/00-系统当前状态与开发指南.md +++ b/docs/00-系统总体设计/00-系统当前状态与开发指南.md @@ -1,9 +1,23 @@ # AIclinicalresearch 系统当前状态与开发指南 -> **文档版本:** v1.0 +> **文档版本:** v4.3 > **创建日期:** 2025-11-28 > **维护者:** 开发团队 -> **最后更新:** 2025-11-28 +> **最后更新:** 2026-01-25 +> **🎉 重大里程碑:** +> - **2026-01-25:Protocol Agent MVP完整交付!** 一键生成研究方案+Word导出 +> - **2026-01-24:Protocol Agent 框架完成!** 可复用Agent框架+5阶段对话流程 +> - **2026-01-22:OSS 存储集成完成!** 阿里云 OSS 正式接入平台基础层 +> - **2026-01-21:成功替换 Dify!** PKB 模块完全使用自研 pgvector RAG 引擎 +> +> **最新进展(Protocol Agent MVP 完整交付 2026-01-25):** +> - ✅ **一键生成研究方案**:流式输出+A4预览+12章节结构 +> - ✅ **Word文档导出**:Pandoc转换,格式完美 +> - ✅ **动态双面板布局**:可拖拽调整,收集65:35/生成35:65 +> - ✅ **用户体验优化**:折叠展开、延迟创建、滚动跟随 +> - ✅ **代码总量**:~8,500行(前端3,300+后端4,700+Python500) +> +> **部署状态:** ✅ 生产环境运行中 | 公网地址:http://8.140.53.236/ > **文档目的:** 快速了解系统当前状态,为新AI助手提供上下文 --- @@ -32,17 +46,19 @@ --- -## 📊 业务模块概览(7大核心功能) +## 📊 业务模块概览(8大核心功能) | 模块代号 | 模块名称 | 核心功能 | 商业价值 | 当前状态 | 优先级 | |---------|---------|---------|---------|---------|--------| -| **AIA** | AI智能问答 | 10+专业智能体(选题评价、PICO梳理等) | ⭐⭐⭐⭐ | ✅ 已完成 | P1 | -| **PKB** | 个人知识库 | RAG问答、私人文献库 | ⭐⭐⭐ | ✅ 已完成 | P1 | -| **ASL** | AI智能文献 | 文献筛选、Meta分析、证据图谱 | ⭐⭐⭐⭐⭐ | 🚧 **正在开发** | **P0** | -| **DC** | 数据清洗整理 | ETL + 医学NER(百万行级数据) | ⭐⭐⭐⭐⭐ | 🚧 **正在开发** | **P0** | +| **AIA** | AI智能问答 | 12个智能体 + Protocol Agent(全流程方案) | ⭐⭐⭐⭐⭐ | 🎉 **V3.1 MVP完整交付(90%)** - 一键生成+Word导出 | **P0** | +| **PKB** | 个人知识库 | RAG问答、私人文献库 | ⭐⭐⭐ | 🎉 **Dify已替换!自研RAG上线(95%)** | P1 | +| **ASL** | AI智能文献 | 文献筛选、Meta分析、证据图谱 | ⭐⭐⭐⭐⭐ | 🎉 **智能检索MVP完成(60%)** - DeepSearch集成 | **P0** | +| **DC** | 数据清洗整理 | ETL + 医学NER(百万行级数据) | ⭐⭐⭐⭐⭐ | ✅ **Tool B完成 + Tool C 99%(异步架构+性能优化-99%+多指标转换+7大功能)** | **P0** | +| **IIT** | IIT Manager Agent | AI驱动IIT研究助手 - 智能质控+REDCap集成 | ⭐⭐⭐⭐⭐ | 🎉 **Phase 1.5完成(60%)- AI对话+REDCap数据集成** | **P0** | | **SSA** | 智能统计分析 | 队列/预测模型/RCT分析 | ⭐⭐⭐⭐⭐ | 📋 规划中 | P2 | | **ST** | 统计分析工具 | 100+轻量化统计工具 | ⭐⭐⭐⭐ | 📋 规划中 | P2 | -| **RVW** | 稿件审查系统 | 方法学评估、审稿流程 | ⭐⭐⭐⭐ | 📋 规划中 | P3 | +| **RVW** | 稿件审查系统 | 方法学评估、审稿流程、Word导出 | ⭐⭐⭐⭐ | ✅ **开发完成(95%)** | P3 | +| **ADMIN** | 运营管理端 | Prompt管理、租户管理、用户管理、运营监控 | ⭐⭐⭐⭐⭐ | 🎉 **Phase 4.2完成(80%)** - 运营监控MVP+登录优化 | **P0** | --- @@ -52,19 +68,35 @@ ``` ┌─────────────────────────────────────────────────────────┐ │ 业务模块层 (Product Layer) │ -│ AIA | PKB | ASL | DC | SSA | ST | RVW │ -│ ✅ ✅ 🚧 🚧 📋 📋 📋 │ +│ AIA | PKB | ASL | DC | IIT | SSA | ST | RVW │ +│ ✅ ✅ 🚧 🚧 🚀 📋 📋 📋 │ └─────────────────────────────────────────────────────────┘ ↓ 依赖 ┌─────────────────────────────────────────────────────────┐ │ 通用能力层 (Capability Layer) │ -│ LLM网关 | 文档处理 | RAG引擎 | ETL引擎 | 医学NLP │ -│ ✅ ✅ ✅ 🚧 📋 │ +│ 后端:LLM网关 | 流式响应服务🆕 | 文档处理 | 🆕RAG引擎 | Prompt管理│ +│ ✅ ✅ OpenAI Compatible ✅ 🎉pgvector✅ ✅ │ +│ • EmbeddingService (text-embedding-v4) │ +│ • VectorSearchService (多查询+Rerank) │ +│ • QueryRewriter (DeepSeek V3 查询理解) │ +│ 前端:Chat组件V2(Ant Design X)🆕 ✅ │ +│ AIStreamChat | ThinkingBlock | useAIStream Hook │ └─────────────────────────────────────────────────────────┘ ↓ 依赖 ┌─────────────────────────────────────────────────────────┐ │ 平台基础层 (Platform Layer) │ -│ 存储 | 日志 | 缓存 | 任务 | 健康检查 | 监控 | 数据库连接池 │ +│ 🏆 **Postgres-Only架构**(新) │ +│ ├── 统一缓存:platform_schema.app_cache ✅ │ +│ ├── 统一队列:platform_schema.job (pg-boss) ✅ │ +│ ├── 任务管理:job.data 统一存储 ✅ │ +│ └── 断点续传:CheckpointService 通用化 ✅ │ +│ │ +│ 🆕 **OSS 存储服务**(2026-01-22) │ +│ ├── 阿里云 OSS:4 Bucket(生产/开发 × 数据/静态) │ +│ ├── StorageAdapter:OSSAdapter + LocalAdapter │ +│ └── 私有化部署:STORAGE_TYPE=local 支持本地存储 │ +│ │ +│ 存储 | 日志 | 缓存 | 任务 | 健康检查 | 监控 | 连接池 │ │ ✅ ✅ ✅ ✅ ✅ ✅ ✅ │ └─────────────────────────────────────────────────────────┘ ``` @@ -72,9 +104,9 @@ ### 技术栈 **前端**: -- React 19 + TypeScript 5 + Vite 6 -- Ant Design 5 + TailwindCSS 3 -- React Query v5 + React Router DOM v6 +- React 19 + TypeScript 5 + Vite 7 +- **Ant Design 6.0** + **Ant Design X 2.1** ✨ 新增! +- TailwindCSS 3 + React Query v5 + React Router DOM v7 - 架构:frontend-v2(模块化,顶部导航) **后端**: @@ -84,50 +116,564 @@ - 架构:增量演进(legacy + common + modules) **数据库**: -- PostgreSQL 16 -- 10个Schema隔离(platform/aia/pkb/asl/dc/ssa/st/rvw/admin/common) +- PostgreSQL 15 (Docker: pgvector/pgvector:pg15) +- **pgvector 0.8.1** ✅ 2026-01-19 新增(向量数据库扩展,支持 RAG) +- **pg_bigm 1.2** ✅ 2026-01-24 新增(中日韩全文搜索,10-100倍性能提升) +- **13个Schema隔离**(platform/aia/pkb/asl/dc/iit/ssa/st/rvw/admin/common/capability/ekb ✅ 2026-01-21新增) **云原生部署**: - 阿里云 SAE (Serverless 应用引擎) -- RDS (PostgreSQL) + OSS (对象存储) + Redis (可选) + - ✅ Python微服务(v1.0)- 内网:172.17.173.66:8000 + - ✅ Node.js后端(v1.3)- 内网:172.17.173.73:3001 + - ✅ 前端Nginx(v1.0)- 内网:172.17.173.72:80 + - ✅ CLB负载均衡 - 公网:http://8.140.53.236/ +- RDS PostgreSQL 15(生产环境运行中) +- OSS对象存储(✅ 2026-01-22 已集成,4个Bucket) +- ACR容器镜像仓库(已推送3个镜像) +- 阿里云 ACR (容器镜像服务) ✅ 已推送3个镜像(Frontend、Backend、Python) +- 阿里云 RDS (PostgreSQL 15) ✅ 已迁移数据 +- RDS PostgreSQL 15 + OSS (对象存储) + NAT网关 +- ACR (容器镜像服务 - 个人版免费) +- **部署状态**:🚀 **进行中**(PostgreSQL✅、Python微服务✅、前端镜像✅、Node.js后端⏳) --- -## 🚀 当前开发状态(2025-11-28) +## 🚀 当前开发状态(2026-01-25) + +### 🎉 最新进展:Protocol Agent MVP 完整交付(2026-01-25) + +#### ✅ 一键生成研究方案 + Word 导出 + +**重大里程碑**: +- 🎉 **MVP 完整可用**:从关键要素收集到完整研究方案输出的全流程 +- 🎉 **Word 导出**:Pandoc 转换,格式完美,支持自定义模板 +- 🎉 **代码量**:累计 ~8,500 行(前端3,300+后端4,700+Python500) + +**核心功能**: +| 功能 | 实现 | 状态 | +|------|------|------| +| 一键生成研究方案 | 流式输出+A4预览+12章节 | ✅ | +| Word文档导出 | pypandoc + Pandoc | ✅ | +| 动态双面板布局 | ResizableSplitPane | ✅ | +| 研究摘要展示 | CollapsibleContent 折叠/展开 | ✅ | +| 延迟创建对话 | 类ChatGPT体验 | ✅ | +| 对话历史管理 | 保存+加载+标题更新 | ✅ | + +**技术亮点**: +- **无编辑器方案**:对话生成→Markdown预览→Pandoc导出,开发快速 +- **Prompt工程**:阶段约束+数据凝练放宽,避免模型混乱 +- **流式渲染**:SSE + 自定义Markdown组件,打字机效果 + +**相关文档**: +- 开发记录:`docs/03-业务模块/AIA-AI智能问答/06-开发记录/2026-01-25-Protocol_Agent_MVP完整交付.md` +- 开发计划V2:`docs/03-业务模块/AIA-AI智能问答/04-开发计划/06-一键生成研究方案开发计划V2.md` + +--- + +### 🆕 运营监控系统 MVP 完成(2026-01-25) + +#### ✅ 全模块埋点 + 运营看板 + +**功能完成**: +- 🎉 **数据采集**:7个业务模块埋点全部完成 +- 🎉 **运营看板**:DAU/DAT/模块统计/实时活动流 +- 🎉 **用户画像**:360度用户资产统计(知识库、审查任务等) + +**埋点模块覆盖**: + +| 模块 | 埋点功能 | 状态 | +|------|---------|------| +| SYSTEM | 用户登录 | ✅ | +| AIA | 智能体对话完成 | ✅ | +| PKB | 知识库创建/删除、RAG检索 | ✅ | +| ASL | 文献筛选完成 | ✅ | +| DC | Tool B提取、Tool C代码处理 | ✅ | +| RVW | 稿件审查完成 | ✅ | +| IIT | REDCap数据同步 | ✅ | + +**技术实现**: +- ActivityService:火烧即忘模式,带 try-catch 保护 +- SimpleLog 表:admin_schema,5个索引优化查询 +- Stats API:overview/live-feed/user-overview/cleanup + +**相关文档**: +- 开发计划:`docs/03-业务模块/ADMIN-运营管理端/04-开发计划/03-运营监控系统MVP开发计划.md` +- 实施记录:`docs/03-业务模块/ADMIN-运营管理端/04-开发计划/04-运营监控系统MVP实施记录.md` + +--- + +### 🆕 登录体验优化(2026-01-25) + +#### ✅ 默认跳转 AI 问答 + 模块权限修复 + +**优化内容**: +- ✅ 用户登录后默认进入 `/ai-qa`(AI问答模块)而非首页 +- ✅ 修复用户模块权限显示逻辑(有自定义配置时正确显示) +- ✅ SUPER_ADMIN 用户返回完整模块权限列表 +- ✅ 顶部导航 LOGO 更换为品牌图标(52px高度) +- ✅ LoginPage 路径映射与 moduleRegistry.ts 保持一致 + +**修复文件**: +- `backend/src/modules/admin/services/userService.ts` - 模块权限显示逻辑 +- `backend/src/common/auth/auth.service.ts` - getUserModules SUPER_ADMIN处理 +- `frontend-v2/src/pages/LoginPage.tsx` - 路径映射修正 +- `frontend-v2/src/framework/layout/TopNavigation.tsx` - LOGO更换 + +--- + +### 🆕 PKB 布局修复(2026-01-25) + +#### ✅ 解决 CSS 类名冲突 + +**问题**:PKB 工作区问答页面只显示部分内容 + +**原因**:Protocol Agent 的 `.chat-container` 样式覆盖了共享组件的同名样式 + +**解决**:将 Protocol Agent 模块的 CSS 类名重命名为 `.pa-chat-container` + +**修复文件**: +- `frontend-v2/src/modules/aia/protocol-agent/components/ChatArea.tsx` +- `frontend-v2/src/modules/aia/protocol-agent/styles/protocol-agent.css` + +--- + +### 🆕 OSS 存储集成完成(2026-01-22) + +#### ✅ 阿里云 OSS 正式接入平台基础层 + +**重大里程碑**: +- 🎉 **存储服务上线**:文件持久化从本地存储升级到云端 OSS +- 🎉 **双模式架构**:支持 SaaS 云端部署和医疗机构私有化部署 +- 🎉 **PKB 首个集成**:个人知识库文档已对接 OSS 存储 + +**核心组件**: +| 组件 | 说明 | 状态 | +|------|------|------| +| OSSAdapter | 阿里云 OSS 存储实现 | ✅ | +| LocalAdapter | 本地文件系统实现(私有化部署) | ✅ | +| StorageFactory | 根据环境变量自动选择适配器 | ✅ | +| 签名URL | 支持原始文件名下载 | ✅ | + +**Bucket 配置**: +| Bucket | 用途 | 权限 | +|--------|------|------| +| ai-clinical-data | 生产数据 | 私有 + SSE-OSS 加密 | +| ai-clinical-data-dev | 开发数据 | 私有 | +| ai-clinical-static | 生产静态资源 | 公共读 | +| ai-clinical-static-dev | 开发静态资源 | 公共读 | + +**目录结构规范**: +``` +tenants/{tenantId}/users/{userId}/pkb/{kbId}/{uuid}.{ext} +``` + +**相关文档**: +- 实施方案:`docs/01-平台基础层/02-存储服务/OSS存储实施方案-MVP版.md` +- 开发规范:`docs/04-开发规范/11-OSS存储开发规范.md` +- 开发记录:`docs/01-平台基础层/02-存储服务/OSS集成开发记录-2026-01-22.md` + +**数据库变更**: +- `difyDocumentId` 字段重命名为 `storageKey`(存储 OSS 路径) + +--- + +### 🏆 里程碑:成功替换 Dify!PKB 完全使用自研 RAG 引擎(2026-01-21) + +#### ✅ Dify 已完全移除,pgvector RAG 引擎生产可用 + +**重大里程碑**: +- 🎉 **彻底移除 Dify 依赖**:PKB 模块不再依赖任何外部 RAG 服务 +- 🎉 **自研引擎上线**:完全使用 PostgreSQL + pgvector 的本地 RAG 方案 +- 🎉 **Postgres-Only 架构完成**:所有核心功能都在 PostgreSQL 内实现 + +**核心技术栈**: +| 组件 | 技术 | 状态 | +|------|------|------| +| 数据库 | PostgreSQL 15 + pgvector 0.8.1 | ✅ | +| 文档处理 | Python pymupdf4llm | ✅ | +| 向量化 | 阿里云 text-embedding-v4 (1024维) | ✅ | +| 查询理解 | DeepSeek V3 | ✅ | +| 重排序 | 阿里云 qwen3-rerank | ✅ | + +**本次完成工作(2026-01-21)**: +- ✅ **移除 Dify 代码**: + - 重构 `ragService.ts` - 移除双轨模式,只保留 pgvector + - 重构 `knowledgeBaseService.ts` - 移除 Dify 创建逻辑 + - 重构 `documentService.ts` - 移除 Dify 上传/轮询逻辑 + - 删除 `DifyClient.ts` - 改为废弃桩文件(兼容 Legacy 代码) + - 移除 `env.ts` 中的 Dify 配置项 +- ✅ **端到端测试通过**:创建知识库 → 上传文档 → 向量检索 全流程验证 + +**架构亮点**: +``` +Brain-Hand 模型: + 业务层 (Brain) → DeepSeek V3 查询理解 → 生成检索词 + 引擎层 (Hand) → 向量+关键词 → RRF → Rerank → 结果 + +完整链路: + PDF → Markdown → 分块 → 向量化 → 存储(pgvector) + 用户查询 → DeepSeek翻译 → 向量检索 → Rerank → Top K +``` + +**性能指标**: +- 单次检索:2.5秒 +- 单次成本:¥0.0025 +- 跨语言准确率提升:+20.5% + +**遗留问题**: +- ✅ OSS 存储集成已完成(2026-01-22) +- ✅ pg_bigm 扩展已安装(2026-01-24) +- 🔧 Legacy 代码保留 Dify 桩文件(兼容性考虑) + +**使用文档**: +- 📖 [RAG 引擎使用指南](../02-通用能力层/03-RAG引擎/05-RAG引擎使用指南.md) +- 📖 [数据模型设计](../02-通用能力层/03-RAG引擎/04-数据模型设计.md) +- 📖 [pgvector替换Dify计划](../02-通用能力层/03-RAG引擎/02-pgvector替换Dify计划.md) + +--- + +### 🏆 历史进展:pgvector 向量数据库集成(2026-01-19) + +#### ✅ pgvector 0.8.1 安装成功 + +**背景**: +- PKB 模块需要实现 RAG 检索功能 +- 原计划依赖 Dify 知识库,现改为 PostgreSQL 原生 pgvector 方案 +- 与 Postgres-Only 架构理念一致,减少外部依赖 + +**完成工作**: +- ✅ Docker 镜像迁移:`postgres:15-alpine` → `pgvector/pgvector:pg15` +- ✅ pgvector 扩展安装:版本 0.8.1 +- ✅ 数据安全:执行双重备份(SQL dump + Volume tarball) +- ✅ 功能验证:前后端服务重启后功能全部正常 +- ✅ 数据完整性:用户数据、pg-boss 队列函数全部正常 + +**技术细节**: +| 项目 | 说明 | +|------|------| +| Docker 镜像 | `pgvector/pgvector:pg15` | +| pgvector 版本 | 0.8.1 | +| 支持索引类型 | HNSW、IVFFlat | +| 最大向量维度 | 16000 维 | +| 阿里云 RDS 兼容性 | ✅ 完全兼容(RDS 为 0.8.0) | + +**版本兼容性说明**: +- 开发环境:pgvector 0.8.1 +- 生产环境(阿里云 RDS):pgvector 0.8.0 +- 兼容性:0.8.x 系列向后兼容,数据格式和 API 一致 + +**文件变更**: +- `docker-compose.yml`:更新 postgres 服务镜像 +- `Dockerfile.postgres-pgvector`:自定义构建文件(备用) +- `backup_before_pgvector_20260119.sql`:迁移前备份 +- `postgres_volume_backup_20260119.tar`:Volume 备份 + +**下一步**: +- ✅ 已完成:RAG 引擎完整实现(2026-01-21) +- 🔜 Phase 2: 安装 pg_bigm 扩展(关键词检索增强) +- 🔜 PKB 模块切换到 pgvector 后端(替换 Dify) + +--- + +### 🏆 历史进展:AIA V2.1 Prompt管理集成(2026-01-18) + +#### ✅ AIA 模块 Prompt 管理系统集成 + +**功能:** +- ✅ 10 个智能体 Prompt 迁移到数据库(`capability_schema.prompt_templates`) +- ✅ 管理端可在线编辑和调试提示词 +- ✅ 灰度预览(调试者看 DRAFT,普通用户看 ACTIVE) +- ✅ 三级容灾(数据库 → 缓存 → 兜底) +- ✅ 版本管理和回滚 + +**Prompt Code 列表:** + +| Prompt Code | 智能体 | +|-------------|--------| +| `AIA_SCIENTIFIC_QUESTION` | 科学问题梳理 | +| `AIA_PICO_ANALYSIS` | PICO 梳理 | +| `AIA_TOPIC_EVALUATION` | 选题评价 | +| `AIA_OUTCOME_DESIGN` | 观察指标设计 | +| `AIA_CRF_DESIGN` | 病例报告表设计 | +| `AIA_SAMPLE_SIZE` | 样本量计算 | +| `AIA_PROTOCOL_WRITING` | 临床研究方案撰写 | +| `AIA_METHODOLOGY_REVIEW` | 方法学评审智能体 | +| `AIA_PAPER_POLISH` | 论文润色 | +| `AIA_PAPER_TRANSLATE` | 论文翻译 | + +**修改文件:** +- `backend/scripts/migrate-aia-prompts.ts` - 迁移脚本 +- `backend/src/common/prompt/prompt.fallbacks.ts` - 兜底 Prompt +- `backend/src/modules/aia/services/agentService.ts` - 集成 PromptService +- `backend/src/modules/aia/services/conversationService.ts` - 传递 userId + +--- + +### 🆕 ASL 智能文献检索 DeepSearch MVP(2026-01-18) + +#### ✅ 功能完成 + +**核心功能:** +- ✅ 集成 unifuncs DeepSearch API(OpenAI 兼容协议) +- ✅ 自然语言输入研究问题,AI 自动生成 PubMed 检索策略 +- ✅ SSE 实时流式显示 AI 思考过程 +- ✅ 提取并展示 PubMed 文献链接 +- ✅ 数据库存储任务记录 + +**技术实现:** +- 后端:`researchService.ts` + `researchController.ts`(SSE 流式接口) +- 前端:`ResearchSearch.tsx`(统一内容流展示) +- 数据库:`asl_schema.asl_research_tasks` + +**API 端点:** +| 方法 | 路径 | 说明 | +|------|------|------| +| POST | `/api/v1/asl/research/stream` | SSE 流式检索 | +| POST | `/api/v1/asl/research/tasks` | 异步任务(备用) | +| GET | `/api/v1/asl/research/tasks/:taskId/status` | 任务状态 | + +**前端入口:** +- 路由:`/literature/research/search` +- 菜单:AI智能文献 → 2. 智能文献检索 + +**已知限制:** +- ⚠️ SSE 模式,离开页面任务中断 +- ⚠️ 每次检索成本约 0.3 元(unifuncs API) + +--- + +### 🏆 历史进展:通用能力层重大升级 + AIA V2.0(2026-01-14) + +#### ✅ Phase 1: 通用流式响应服务(OpenAI Compatible) + +**后端能力:** +- ✅ 创建 `common/streaming/` 模块(4个文件,~400行) +- ✅ `OpenAIStreamAdapter` - SSE适配器 +- ✅ `StreamingService` - 流式响应服务 +- ✅ 支持 `content` 和 `reasoning_content` 双流 +- ✅ 深度思考标签处理(`...`) +- ✅ Token统计和错误处理 + +**输出格式:** +``` +data: {"id":"chatcmpl-xxx","choices":[{"delta":{"content":"你好"}}]}\n\n +data: {"id":"chatcmpl-xxx","choices":[{"delta":{"reasoning_content":"思考..."}}]}\n\n +data: [DONE]\n\n +``` + +#### ✅ Phase 2: Chat通用组件V2(Ant Design X深度集成) + +**前端能力:** +- ✅ 升级 `shared/components/Chat/`(12个文件,~2000行) +- ✅ `AIStreamChat` - 流式对话组件(现代感设计) +- ✅ `ThinkingBlock` - 深度思考展示组件 +- ✅ `ConversationList` - 会话列表组件 +- ✅ `useAIStream` Hook - 流式响应处理 +- ✅ `useConversations` Hook - 会话管理 +- ✅ 现代感样式(Ultramodern风格) + +**核心特性:** +- 逐字流式显示(打字机效果) +- 深度思考可折叠展示 +- 会话列表分组(今天/昨天/更早) +- 欢迎页配置 +- 快捷提示 +- 附件上传(UI完成,后端待实现) + +#### ✅ Phase 3: AIA模块V2.0完整实现 + +**前端开发:** +- ✅ `AgentHub` - 智能体大厅(100%还原原型图V11) + - 12个智能体卡片 + - 时间轴设计(5个阶段) + - 主题色区分(蓝/黄/青/紫) + - 序号水印 + - 悬停动画效果 +- ✅ `ChatWorkspace` - 对话工作台 + - 全屏沉浸式体验 + - 左侧会话列表(256px) + - 欢迎语(左上角单行) + - 流式响应集成 + - 深度思考展示 + - 自动创建对话 + +**后端开发:** +- ✅ `agentService` - 12个智能体配置 +- ✅ `conversationService` - 重构使用 StreamingService +- ✅ `attachmentService` - 附件处理骨架 +- ✅ API端点(12个) +- ✅ 认证授权(符合规范) +- ✅ 流式响应测试通过 + +**代码统计:** +- 前端业务:~1,500行(10个文件) +- 后端业务:~900行(9个文件) +- 通用能力(前端):~2,000行(12个文件) +- 通用能力(后端):~400行(4个文件) +- **总计:~4,800行** + +**测试结果:** +- ✅ 智能体大厅展示正常 +- ✅ 卡片点击进入对话 +- ✅ 自动创建对话 +- ✅ 流式响应测试通过(HTTP 200) +- ✅ 深度思考展示正常 +- ✅ 认证授权正常 + +**待完成功能:** +- 🔜 附件上传API实现 +- 🔜 历史消息加载 +- 🔜 知识库集成(RAG) +- 🔜 Prompt管理系统对接 + +**技术创新:** +- 🏆 **OpenAI Compatible标准化** - 业界主流格式 +- 🏆 **通用能力抽象** - 前后端Chat能力可复用 +- 🏆 **现代感设计** - Ant Design X Ultramodern风格 + +--- + +### 🎉 历史进展:ADMIN 运营管理端(2026-01-11) + +#### ✅ Phase 3.5.1-3.5.4 已完成(83%) + +**Phase 3.5.1: 基础设施搭建** +- ✅ 创建 `capability_schema` +- ✅ 添加 `prompt_templates` 和 `prompt_versions` 表 +- ✅ 添加 `prompt:view/edit/debug/publish` 权限 +- ✅ 迁移 RVW Prompt 到数据库(2个:RVW_EDITORIAL, RVW_METHODOLOGY) + +**Phase 3.5.2: PromptService 核心服务** +- ✅ 灰度预览逻辑(调试者看 DRAFT,用户看 ACTIVE) +- ✅ 模块级调试控制(`setDebugMode(userId, ['RVW'], true)`) +- ✅ Handlebars 模板渲染 +- ✅ 变量提取与校验(自动从 `{{xxx}}` 提取) +- ✅ 三级容灾(数据库→缓存→兜底 hardcoded) + +**Phase 3.5.3: 管理 API** +- ✅ 8个 RESTful 接口(`/api/admin/prompts/*`) +- ✅ 权限控制(PROMPT_ENGINEER 只能编辑,SUPER_ADMIN 才能发布) + +**Phase 3.5.4: 前端管理界面** +- ✅ 管理端基础架构(AdminLayout, OrgLayout) +- ✅ 路由系统(`/admin/*`, `/org/*`) +- ✅ 头像下拉菜单切换入口 +- ✅ PromptListPage(筛选、搜索、调试开关) +- ✅ PromptEditor(CodeMirror 6 简化版,中文友好,15px字体) +- ✅ PromptEditorPage(编辑、保存、发布、测试、版本历史) + +**⏳ Phase 3.5.5 待完成** +- [ ] 改造 RVW 服务使用 `promptService.get()`(替代文件读取) +- [ ] 端到端测试 + +**📄 相关文档** +- 详细计划:`docs/03-业务模块/ADMIN-运营管理端/04-开发计划/02-Prompt管理系统开发计划.md` +- TODO清单:`docs/03-业务模块/ADMIN-运营管理端/04-开发计划/01-TODO清单(可追踪).md` + +--- ### ✅ 已完成模块 -#### 1. 平台基础层(2025-11-17完成) +#### 1. 平台基础层 🏆 **Postgres-Only 架构完成!**(2025-12-13) + +**核心架构:Platform-Only 模式** +- ✅ **统一缓存**:`PostgresCacheAdapter` → `platform_schema.app_cache` +- ✅ **统一队列**:`PgBossQueue` → `platform_schema.job` (pg-boss) +- ✅ **任务管理**:所有任务信息存储在 `job.data` (JSONB) +- ✅ **断点续传**:`CheckpointService` 通用化(操作 job.data) +- ✅ **智能阈值**:小任务直接处理,大任务队列处理(THRESHOLD=50) + +**原有能力:** - ✅ 存储服务(LocalAdapter ↔ OSSAdapter) - ✅ 日志系统(Winston + 结构化JSON) -- ✅ 缓存服务(Memory ↔ Redis) -- ✅ 异步任务(MemoryQueue ↔ DatabaseQueue) - ✅ 健康检查(Liveness + Readiness) - ✅ 监控指标(数据库连接/内存/API) - ✅ 数据库连接池(Serverless优化) -- ✅ **100%测试通过** -#### 2. AIA模块 - AI智能问答(已完成) -- ✅ 10个专业智能体 -- ✅ 流式对话 + 非流式对话 -- ✅ 知识库模式(RAG检索) -- ✅ 批处理模式 -- **状态**:生产就绪 +**测试覆盖:** +- ✅ 单元测试:8个全部通过 +- ✅ 集成测试:2个全部通过 +- ✅ 架构验证:Platform-Only 验证通过 -#### 3. PKB模块 - 个人知识库(已完成) -- ✅ 知识库CRUD -- ✅ 文档上传(PDF/Word/TXT/MD) -- ✅ RAG问答 -- ✅ 批处理任务 -- **状态**:生产就绪 +**技术债务:** +- ⚠️ Phase 8 全面测试(断点续传压力测试、1000篇文献完整流程) +- ⚠️ Phase 9 SAE 部署验证 + +#### 2. AIA模块 - AI智能问答 🎉 **V2.0 重构完成!**(2026-01-14) + +**重大升级:** +- 🆕 **通用能力层架构**:StreamingService + Chat组件V2 +- 🆕 **OpenAI Compatible**:标准流式格式 + 深度思考支持 +- 🎨 **现代感UI**:100%还原原型图V11 +- ✨ **12个智能体**:覆盖选题→方案→评审→统计→写作全流程 + +**5个阶段,12个智能体:** +1. **选题优化**(3个):科学问题梳理、PICO梳理、选题评价 +2. **方案设计**(4个):观察指标、CRF设计、样本量计算、方案撰写 +3. **方案预评审**(1个):方法学评审 +4. **数据与统计**(2个):数据预处理、统计分析(工具类,跳转DC) +5. **写作助手**(2个):论文润色、论文翻译 + +**技术栈:** +- 前端:React 19 + Ant Design X 2.1 + Lucide Icons +- 后端:Fastify + Prisma + OpenAI Compatible API +- 通用能力:StreamingService + AIStreamChat + ThinkingBlock + +**当前状态:** +- ✅ 前端AgentHub(100%还原原型图) +- ✅ 前端ChatWorkspace(流式对话 + 深度思考) +- ✅ 后端API(12个端点) +- ✅ 流式响应测试通过 +- 🔜 附件上传(待完成) +- 🔜 历史消息加载(待完成) + +**完成度:85%** - 核心功能完成,附件和历史功能待开发 + +**详细文档:** [AIA模块状态与开发指南](../../03-业务模块/AIA-AI智能问答/00-模块当前状态与开发指南.md) + +#### 3. PKB模块 - 个人知识库 🎉 **成功替换 Dify!自研 RAG 引擎上线!** + +**开发进度**: +- ✅ **后端API**:100%完成(v1 + v2双路由运行) +- ✅ **前端Dashboard**:95%完成(基于知识库仪表盘V5原型) +- ✅ **前端Workspace**:95%完成(基于工作台V3原型) +- ✅ **全文阅读模式**:95%完成(Chat组件集成) +- ✅ **逐篇精读模式**:95%完成(文档选择+对话) +- ✅ **批处理模式**:95%完成(完整流程+结果导出) +- ✅ **RAG检索模式**:100%完成(🎉 **2026-01-21 替换 Dify 完成!**) + +**核心功能**: +- 知识库CRUD + 文档管理 +- 4种工作模式(全文阅读、逐篇精读、批处理、RAG检索) +- 自研 pgvector RAG 引擎(替代 Dify) +- Ant Design X Chat组件集成 +- 响应式全屏布局 + +**技术亮点**: +- ✅ 模块化架构迁移(/modules/pkb) +- ✅ Zustand状态管理 +- ✅ 复用shared/components/Chat通用组件 +- ✅ 单层Header + 紧凑工作模式栏设计 +- ✅ **pgvector 0.8.1 已集成**(2026-01-19) +- ✅ **自研 RAG 引擎上线,Dify 已移除**(2026-01-21) +- ✅ **跨语言检索**:DeepSeek V3 查询理解 + 中英双语 +- ✅ **OSS 存储集成**(2026-01-22):文档存储云端化 + +**待解决问题**: +- 🔧 OSS 存储集成待完善 +- 🔧 pg_bigm 扩展待安装(优化中文关键词检索) + +**详细文档**:[PKB模块当前状态](../03-业务模块/PKB-个人知识库/00-模块当前状态与开发指南.md) ### 🚧 正在开发模块 -#### 4. ASL模块 - AI智能文献(正在开发) +#### 4. ASL模块 - AI智能文献 🏆 **Postgres-Only 架构改造完成!** + **开发进度**: - ✅ **标题摘要初筛MVP**:完整流程(设置→启动→审核→结果→导出) -- ✅ **全文复筛后端**:LLM服务、数据库、批处理、API(Day 2-5完成) -- 🚧 **全文复筛前端UI**:4个核心页面(Day 6-8,预计2.5天) +- ✅ **全文复筛后端**:LLM服务、数据库、批处理、API +- ✅ **🏆 Postgres-Only 架构改造**:智能阈值、任务拆分、断点续传(Phase 6完成) +- 🚧 **全文复筛前端UI**:4个核心页面(待开发) **核心功能**: - 双模型并行筛选(DeepSeek-V3 + Qwen-Max) @@ -136,33 +682,374 @@ - 医学逻辑验证 + 证据链验证 - Excel批量导出 -**技术亮点**: -- Nougat优先 + PyMuPDF降级(PDF提取) -- 3层JSON解析(容错机制) -- 冲突检测与人工复核 -- 云原生存储(零文件落盘) +**🚀 Postgres-Only 架构亮点**: +- ✅ **智能双模式**:<50篇直接处理,≥50篇队列处理 +- ✅ **任务拆分**:1000篇 → 20个批次,每批50篇 +- ✅ **断点续传**:支持2-24小时长任务,实例重启可恢复 +- ✅ **Platform层统一**:任务管理信息存储在 `job.data`,不在业务表中 +- ✅ **零额外成本**:使用 pg-boss,无需 Redis +- ✅ **高可靠性**:自动重试3次,6小时过期保护 + +**技术实现**: +- `screeningService.ts`:智能阈值判断,推送批次任务 +- `screeningWorker.ts`:批次处理,断点续传 +- `CheckpointService`:操作 job.data,所有模块通用 **详细文档**:[ASL模块当前状态](../03-业务模块/ASL-AI智能文献/00-模块当前状态与开发指南.md) -#### 5. DC模块 - 数据清洗整理(正在开发) -**开发进度**: -- ✅ **Day 2完成**:健康检查 + 模板管理基础 -- ✅ **Day 3完成**:HealthCheckService + TemplateService完整实现 -- 🚧 **Day 4-5**:工具B核心功能开发 +#### 5. DC模块 - 数据清洗整理 🏆 **Tool C MVP + Postgres-Only 架构改造完成!** -**核心功能**: -- **工具A**:医疗数据超级合并器(多表JOIN) -- **工具B**:病历结构化机器人(医学NER)🚧 **当前开发中** -- **工具C**:科研数据编辑器 -- **Portal**:智能数据清洗工作台 +**开发进度**: +- ✅ **Tool B后端**:100%完成(1,658行代码) + - 4个核心服务(HealthCheck、Template、DualModel、Conflict) + - 1个控制器(6个API端点) + - 路由集成(/api/v1/dc/tool-b) + - Prisma Schema(4个表) + - 100%云原生(复用平台能力) + - ✅ **🏆 Postgres-Only 架构改造**:智能阈值、任务拆分、断点续传(Phase 7完成) +- ❌ **Tool B前端**:0%(有V4原型设计,未实现) + +- ✅ **Tool C(数据编辑器)**:**MVP + NA处理 + Pivot优化 + UX重大改进完成** ✅ + - ✅ Python微服务(~1800行)- Day 1 + NA处理优化 + 全量数据处理 + - ✅ Node.js后端(~3500行)- Day 2-3, Day 5-8增强 + 全量返回 + - ✅ 前端界面(~4000行)- Day 4-8, 筛选/行号/滚动条/全量加载 + - ✅ 通用 Chat 组件(~968行)- Day 5 + - ✅ 7个功能按钮(筛选、映射、分箱、条件、删NA、计算、Pivot) + - ✅ NA处理优化(4个功能支持空值处理) + - ✅ Pivot优化(保留未选列+原始列顺序) + - ✅ 计算列方案B(安全列名映射,支持特殊字符 + 全角字符转换) + - ✅ **UX重大改进**(Day 8): + - 列头筛选(Excel风格,Community版本,中文本地化) + - 行号列(固定左侧,灰色背景) + - 滚动条修复(修改MainLayout,整个页面无滚动条) + - 全量数据(不再限制50行,筛选精确) + - 删除预览提示条 + - **总计:~13068行** | **完成度:98%** + +- ❌ **Tool A**:未开发 +- ✅ **Portal**:已完成(Tool B + Tool C 入口) + +**核心功能(Tool C,2025-12-10最新)**: +- 7个功能按钮:高级筛选、数值映射、生成分类变量、条件生成列、删除缺失值、计算列、Pivot转换 +- NA处理支持:数值映射(保持/映射/删除)、分箱(保持/标记/分配)、条件(为空/不为空) +- Pivot优化:保留未选择的列、保持原始列顺序 +- 计算列方案B:安全列名映射,支持中文括号等特殊字符 + 全角字符自动转换 +- **UX重大改进**: + - 列头筛选(Excel风格,Community版本,中文本地化,显示值计数) + - 行号列(固定左侧,灰色背景,#列头) + - 滚动条修复(修改MainLayout,整个页面无滚动条,只有表格内部滚动) + - 全量数据(不再限制50行,筛选精确,所有操作全量返回) + +**核心功能(Tool B)**: +- 双模型并发提取(DeepSeek-V3 + Qwen-Max) +- 自动冲突检测(字段级对比) +- Excel健康检查(空值率、Token估算、拦截策略) +- 预设模板系统(肺癌、糖尿病、高血压) + +**🚀 Postgres-Only 架构亮点**: +- ✅ **智能双模式**:<50条直接处理,≥50条队列处理 +- ✅ **任务拆分**:1000条 → 20个批次,每批50条 +- ✅ **断点续传**:支持长时间提取任务,实例重启可恢复 +- ✅ **Platform层统一**:与 ASL 共用 CheckpointService +- ✅ **零额外成本**:使用 pg-boss,无需 Redis + +**技术实现**: +- `ExtractionController.ts`:智能阈值判断,推送批次任务 +- `extractionWorker.ts`:批次处理,断点续传 +- `CheckpointService`:操作 job.data,所有模块通用 **技术亮点**: -- Excel内存处理(百万行级) -- LLM驱动的医学实体识别 -- 健康检查机制(拦截不合格数据) -- 模板系统(30+预设模板) +- ✅ Excel内存处理(零落盘,云原生) +- ✅ 双模型交叉验证(减少AI幻觉) +- ✅ 3层JSON解析(容错机制) +- ✅ 复用LLMFactory、storage、cache、jobQueue +- ✅ 预写Python函数架构(稳定、安全、高性能) +- ✅ 安全列名映射(支持特殊字符列名) -**详细文档**:[DC模块README](../03-业务模块/DC-数据清洗整理/README.md) +**待开发功能**: +- ⏳ 缺失值填补(均值/中位数/众数/固定值) +- ⏳ 多重插补(MICE)- 高优先级 + +**详细文档**:[DC模块当前状态](../03-业务模块/DC-数据清洗整理/00-模块当前状态与开发指南.md) + +--- + +### 🚀 IIT Manager Agent(代号:IIT,2025-12-31启动) + +**定位**:AI驱动的IIT(研究者发起的临床研究)智能助手 + +**核心价值**: +- 🎯 **主动工作的AI Agent** - 不是被动工具,而是24/7主动监控的智能助手 +- 🎯 **REDCap深度集成** - 与医院现有EDC系统无缝对接 +- 🎯 **影子状态机制** - AI建议+人类确权,符合医疗合规要求(FDA 21 CFR Part 11) +- 🎯 **企业微信实时通知** - 质控预警秒级推送,移动端查看 + +**MVP目标**(2周冲刺): +- ✅ 打通 REDCap → AI质控 → 企微通知 完整闭环 +- ✅ 实现智能数据质控(基于Protocol的入排标准检查) +- ✅ 支持历史数据全量扫描 +- ✅ PC Workbench复核界面 + +**Day 1 完成情况**(2025-12-31):✅ **100%** +- ✅ **数据库Schema**:5个表(IitProject, IitPendingAction, IitTaskRun, IitUserMapping, IitAuditLog) +- ✅ **模块结构**:controllers/services/agents/adapters/routes/types/workers +- ✅ **类型系统**:223行完整TypeScript类型定义 +- ✅ **系统集成**:健康检查端点正常(`/api/v1/iit/health`) +- ✅ **企业微信配置**:Access Token获取成功(核心验证通过) +- ✅ **企业微信可信域名**:iit.xunzhengyixue.com(网页授权+JS-SDK授权) +- ✅ **Prisma Schema**:含V1.1新增字段(cachedRules, lastSyncAt, miniProgramOpenId) + +**REDCap环境就绪**(2026-01-02):✅ **100%** +- ✅ **REDCap本地部署**:15.8.0版本,Docker Compose(3容器架构) +- ✅ **测试项目创建**:test0102 (PID 16),已录入测试数据 +- ✅ **DET功能验证**:Data Entry Trigger真实存在(源码验证通过) +- ✅ **技术调研完成**:源码分析 + External Module文档研究 +- ✅ **对接方案确定**:DET(实时触发) + REST API(数据读写) +- ✅ **技术方案文档**:《REDCap对接技术方案与实施指南》(1070行完整文档) +- ✅ **代码设计完成**:RedcapAdapter、WebhookController、SyncManager +- ✅ **REDCap文档体系**:部署手册、问题排查、API对接指南 + +**Day 1 技术验证**: +```bash +# 数据库CRUD测试 - 全部通过 ✅ +✅ IIT项目创建成功 +✅ 影子状态记录创建成功 +✅ 任务运行记录创建成功 +✅ 用户映射创建成功 +✅ 审计日志创建成功 +✅ 关联查询成功 + +# 企业微信API测试 - Access Token获取成功 ✅ +✅ CorpID: ww6ab493470ab4f377 +✅ AgentID: 1000002 +✅ Access Token获取成功(核心验证通过) +``` + +**技术架构**(REDCap对接方案V1.0): +- ✅ **DET实时触发**:Data Entry Trigger(REDCap原生,0秒延迟) +- ✅ **REST API集成**:exportRecords(数据拉取)+ importRecords(数据回写) +- ✅ **双保险机制**:Webhook(主,95%) + 定时轮询(补充,30分钟) +- ✅ **Postgres-Only架构**:复用平台缓存(app_cache)和队列(pg-boss) +- ✅ **Dify RAG集成**:Protocol知识检索 + 规则预缓存(性能优化) +- ✅ **影子状态机制**:PROPOSED → APPROVED → EXECUTED 状态流转 +- ✅ **前端技术栈**:Taro 4.x(React语法,支持小程序+H5双端) + +**核心创新**: +- 🔥 **DET实时触发**:CRC保存数据→5秒内收到企微质控通知(实时性100%) +- 🔥 **零侵入性**:只用REDCap原生API和DET,无需修改源码(维护成本<10%) +- 🔥 **双保险机制**:Webhook幂等性 + 轮询补充,数据不丢失(可靠性99.9%) +- 🔥 **历史数据扫描**:BulkScanService支持存量数据质控(智能阈值+断点续传) +- 🔥 **规则预缓存**:Protocol上传时提取关键规则,简单检查<100ms + +**开发进度**: +- Day 1/14:✅ 基础架构就位(数据库、模块结构、企微配置) +- REDCap准备:✅ 本地环境部署 + 对接方案确定 + 技术方案文档 +- Day 2:✅ REDCap API Adapter + WebhookController + SyncManager完成 +- Day 3:✅ 企微推送 + 端到端测试通过(REDCap → Node.js → 企微) +- **Phase 1.5:✅ AI对话集成完成(2026-01-03)** + - ✅ ChatService + SessionMemory + REDCap数据查询 + - ✅ 意图识别 + 数据注入LLM + 解决LLM幻觉 + - ✅ 测试通过(查询ID 7,10条记录统计) +- Phase 2:待开始 - Function Calling + Dify知识库 +- Day 6-9:影子状态管理 + 历史数据扫描 +- Day 10-14:PC Workbench前端 + 端到端测试 + Demo录制 + +**已创建文件**(Day 1 + REDCap准备): +``` +backend/prisma/schema.prisma # 新增iit_schema(5个表) +backend/src/modules/iit-manager/ # 模块目录结构 +├── types/index.ts # 223行类型定义 +├── routes/index.ts # 路由骨架 +├── test-iit-database.ts # 数据库测试(通过) +└── test-wechat-push.ts # 企微测试(Access Token成功) +backend/src/config/env.ts # 新增企微配置 +backend/src/index.ts # IIT模块集成 + +redcap-docker-dev/ # REDCap Docker环境(新增) +├── docker-compose.yml # 开发环境配置 +├── docker-compose.prod.yml # 生产环境配置 +├── Dockerfile.redcap # REDCap镜像 +├── docker-entrypoint.sh # 容器启动脚本 +├── config/ +│ ├── apache/redcap.conf # Apache配置 +│ ├── php/php.ini # PHP配置 +│ └── database.php # REDCap数据库配置 +└── scripts/ # 管理脚本(setup/start/stop/logs/clean) + +docs/03-业务模块/IIT Manager Agent/ # 完整文档架构 +├── 00-系统设计/ # 技术架构白皮书、实施战略 +├── 02-技术设计/ # 完整技术开发方案(V1.1,2170行) +├── 04-开发计划/ +│ ├── MVP开发任务清单.md # 开发任务清单 +│ ├── 企业微信注册指南.md # 企微配置指南 +│ └── REDCap对接技术方案与实施指南.md # ⭐ 1070行完整方案(新增) +└── 06-开发记录/ # V1.1更新完成报告 + +docs/03-业务模块/Redcap/ # REDCap文档体系(新增) +├── 00-模块概览/ # REDCap文档导航 +├── 01-部署与配置/ # Docker部署手册、问题排查 +└── 03-API对接与开发/ # 二次开发指南、API对接 +``` + +**下一步**(Day 2): +- 🔄 **RedcapAdapter开发**(exportRecords/exportMetadata/importRecords) +- 🔄 **WebhookController开发**(DET接收器、<100ms响应、异步处理) +- 🔄 **SyncManager开发**(定时轮询、增量同步、幂等性保护) +- 🔄 **集成测试**(DET配置、API测试、端到端验证) + +**详细文档**: +- ⭐ [REDCap对接技术方案与实施指南](../03-业务模块/IIT%20Manager%20Agent/04-开发计划/REDCap对接技术方案与实施指南.md) - **Day 2核心参考** +- [IIT Manager Agent 完整技术开发方案 (V1.1)](../03-业务模块/IIT%20Manager%20Agent/02-技术设计/IIT%20Manager%20Agent%20完整技术开发方案%20(V1.1).md) +- [IIT Manager Agent 模块当前状态与开发指南](../03-业务模块/IIT%20Manager%20Agent/00-模块当前状态与开发指南.md) +- [MVP开发任务清单](../03-业务模块/IIT%20Manager%20Agent/04-开发计划/MVP开发任务清单.md) +- [企业微信注册指南](../03-业务模块/IIT%20Manager%20Agent/04-开发计划/企业微信注册指南.md) +- [REDCap Docker部署操作手册](../03-业务模块/Redcap/01-部署与配置/10-REDCap_Docker部署操作手册.md) +- [REDCap二次开发深度指南](../03-业务模块/Redcap/03-API对接与开发/33-REDCap二次开发深度指南.md) + +--- + +## 🚀 阿里云生产环境部署状态(2025-12-24) + +### ✅ 已完成部署 + +#### 1. 基础设施层 +- ✅ **VPC网络**:`vpc-2ze055cptkew9c38w4r06`(172.17.0.0/16) +- ✅ **NAT网关**:`ngw-2zeec9ulzgw7ywvx1pst6`(公网IP: 182.92.176.14) +- ✅ **安全组**:`sg-2zedk6fi8sgmmcwdu7tu` +- ✅ **交换机**:2个(可用区F + 可用区A) +- ✅ **SAE命名空间**:`cn-beijing:test-airesearch` + +#### 2. 数据存储层 +- ✅ **RDS PostgreSQL 15** + - 实例ID: `pgm-2zex1m2y3r23hdn5` + - 规格: 2核4GB + - 内网地址: `pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432` + - 数据库: `ai_clinical_research` + - **数据迁移**: ✅ 完成(90MB SQL文件,约12秒导入) + - **Schema验证**: ✅ 11个Schema全部迁移成功 + - **数据验证**: ✅ 用户3条、项目2条、文献1204条 + - **部署时间**: 2025-12-24 + +- ✅ **OSS对象存储** + - Bucket: `ai-clinical-research` + - 存储类型: 标准存储(同城冗余) + - 内网域名: `ai-clinical-research.oss-cn-beijing-internal.aliyuncs.com` + - RAM用户: `oss-bucket-put-object@1991407246109125.onaliyun.com` + - AccessKey: 已配置(不公开) + +#### 3. 容器镜像服务(ACR) +- ✅ **命名空间**: `ai-clinical` +- ✅ **Registry**: `crpi-cd5ij4pjt65mweeo.cn-beijing.personal.cr.aliyuncs.com` +- ✅ **已推送镜像**: + - **前端Nginx**: `ai-clinical_frontend-nginx:v1.0`(约50MB) + - 构建时间: 2025-12-24 + - 基础镜像: `nginx:alpine` + - 功能: React SPA + Nginx反向代理 + 动态环境变量 + - 配置文件: `frontend-v2/Dockerfile`, `nginx.conf`, `.dockerignore` + + - **Python微服务**: `python-extraction:v1.0`(1.12GB) + - 构建时间: 2025-12-24 + - 基础镜像: `python:3-slim` + - 功能: PDF提取(PyMuPDF)+ 数据清洗(pandas/numpy/polars) + - 特性: 移除Nougat(减小1.5GB)、使用阿里云Debian源 + - 配置文件: `extraction_service/Dockerfile`, `requirements-prod.txt`, `.dockerignore` + + - **Node.js后端**: `backend-service:v1.0`(838MB,压缩后~186MB)✨ 新增! + - 构建时间: 2025-12-24(约5分钟) + - 基础镜像: `node:alpine` + - 构建策略: 改进版方案B(本地编译+Docker打包) + - 技术突破: 修复200+TypeScript错误、手动补全30+Prisma关系字段 + - 架构特性: Postgres-Only(pg-boss队列+PostgreSQL缓存) + - 配置文件: `backend/Dockerfile`, `backend/.dockerignore`, `backend/prisma/schema.prisma` + +### 🚧 进行中 + +#### 4. SAE应用部署 +- ✅ **Python微服务**: 已成功部署到SAE轻量版 + - 应用名称: `python-extraction-test` + - 规格: 1核2GB + - 内网地址: `http://172.17.173.66:8000` + - 状态: 运行中 ✅ + +- ⏳ **Node.js后端**: 镜像已推送,待部署到SAE + - 目标规格: 1核2GB(测试环境) + - 端口: 8000 + - 健康检查: `/api/health` + +- ⏳ **Node.js后端**: Docker镜像待构建 + - 目标规格: 2核4GB + - 端口: 3001 + - 依赖: RDS PostgreSQL + +- ⏳ **前端Nginx**: 镜像已推送,待部署到SAE + - 目标规格: 1核2GB + - 端口: 80 + - 需配置: 后端API内网地址 + +### 📋 待完成 + +- [ ] Python微服务部署到SAE +- [ ] Node.js后端Docker镜像构建 +- [ ] Node.js后端部署到SAE +- [ ] 前端Nginx部署到SAE +- [ ] 配置服务间内网通信 +- [ ] 全链路验证测试 +- [ ] Dify AI服务部署(可选) + +### 📊 部署文档 + +**部署进度总览**: +- [00-部署进度总览.md](../05-部署文档/00-部署进度总览.md) - 🎯 **一站式部署状态查看** + +**操作手册**: +- [07-前端Nginx-SAE部署操作手册.md](../05-部署文档/07-前端Nginx-SAE部署操作手册.md) +- [08-PostgreSQL数据库部署操作手册.md](../05-部署文档/08-PostgreSQL数据库部署操作手册.md) +- Python微服务SAE部署操作手册(待创建) + +**技术指南**: +- [01-快速部署SOP-零基础版.md](../05-部署文档/01-快速部署SOP-零基础版.md) - 完整部署流程 +- [04-Python微服务-SAE容器部署指南.md](../05-部署文档/04-Python微服务-SAE容器部署指南.md) +- [06-前端Nginx-SAE容器部署指南.md](../05-部署文档/06-前端Nginx-SAE容器部署指南.md) + +### 🎯 部署关键成就 + +1. **PostgreSQL数据迁移** ✅ + - 采用`pg_dump`全量导出/导入方案 + - 11个Schema完整迁移 + - 数据一致性验证通过 + - 安全加固(外网访问已关闭) + +2. **前端Nginx镜像优化** ✅ + - 解决Docker Hub网络问题(使用通用标签) + - 修复30个TypeScript编译错误 + - 多阶段构建优化 + - 健康检查通过 + +3. **Python微服务镜像优化** ✅ + - 移除Nougat OCR(减小1.5GB) + - 使用阿里云Debian镜像源(解决apt-get网络问题) + - 保留数据清洗功能(pandas/numpy/polars) + - 运行时依赖优化(libgl1、libglib2.0) + +4. **镜像配置文件Git管理** ✅ + - Dockerfile: ✅ 已提交Git(构建蓝图) + - .dockerignore: ✅ 已提交Git(优化构建) + - 依赖文件: ✅ 已提交Git(可复现) + - 敏感信息: ❌ 禁止提交(.env等) + +### 💰 当前运行成本估算 + +| 服务 | 规格 | 月成本 | 状态 | +|------|------|-------|------| +| RDS PostgreSQL | 2核4GB | ¥260 | ✅ 运行中 | +| OSS存储 | 10GB | ¥2 | ✅ 运行中 | +| NAT网关 | 小型 | ¥60 | ✅ 运行中 | +| EIP流量 | 5Mbps | ¥40 | ✅ 运行中 | +| ACR镜像仓库 | 个人版 | ¥0(免费) | ✅ 运行中 | +| SAE - Python | 1核2GB×1 | ¥60 | ⏳ 待部署 | +| SAE - Node.js | 2核4GB×1 | ¥120 | ⏳ 待部署 | +| SAE - Frontend | 1核2GB×1 | ¥60 | ⏳ 待部署 | +| **总计** | - | **¥602/月** | 部署中 | --- @@ -175,11 +1062,17 @@ AIclinicalresearch/ │ ├── framework/ # 框架层(布局、路由、权限) │ ├── modules/ # 业务模块 │ │ ├── asl/ # ✅ AI智能文献 -│ │ ├── aia/ # ✅ AI智能问答 +│ │ ├── aia/ # 🎉 AI智能问答 V2.0(12个智能体) │ │ ├── pkb/ # ✅ 个人知识库 -│ │ ├── dc/ # 🚧 数据清洗(开发中) +│ │ ├── dc/ # ✅ 数据清洗(Tool C 完成) │ │ └── ... │ └── shared/ # 共享组件和工具 +│ └── components/ # ✨ 通用能力层 +│ └── Chat/ # ✅ Chat 通用组件 V2(Ant Design X) +│ ├── AIStreamChat.tsx # 🆕 流式对话(推荐) +│ ├── ThinkingBlock.tsx # 🆕 深度思考展示 +│ ├── ConversationList.tsx # 🆕 会话列表 +│ └── hooks/useAIStream.ts # 🆕 流式响应Hook │ ├── backend/ # ⚙️ 后端(Fastify + Prisma) │ └── src/ @@ -188,6 +1081,11 @@ AIclinicalresearch/ │ │ ├── logging/ # 日志系统 │ │ ├── cache/ # 缓存服务 │ │ ├── jobs/ # 异步任务 +│ │ ├── llm/ # LLM 适配器层(5个模型) +│ │ ├── streaming/ # 🆕 流式响应服务(OpenAI Compatible) +│ │ ├── rag/ # RAG 引擎(Dify集成) +│ │ ├── document/ # 文档处理引擎 +│ │ ├── prompt/ # Prompt 管理系统 │ │ └── ... │ ├── legacy/ # 🔸 现有业务代码(稳定) │ └── modules/ # 🌟 新架构模块 @@ -246,33 +1144,87 @@ AIclinicalresearch/ | **2025-11-18~21** | Week 3~4 | ✅ ASL标题摘要初筛MVP | | **2025-11-22~23** | ASL Day 2-5 | ✅ ASL全文复筛后端完成 | | **2025-11-26~27** | DC Day 2-3 | ✅ DC工具B健康检查+模板管理 | -| **2025-11-28** | 当前 | 🚧 ASL全文复筛前端 + DC工具B开发 | +| **2025-11-28** | DC Day 4-8 | ✅ DC Tool C MVP + UX重大改进完成 | +| **2025-12-13** | 架构优化 | ✅ Postgres-Only架构改造完成 | +| **2025-12-24 上午** | **部署启动** 🚀 | ✅ PostgreSQL数据迁移 + 前端/Python镜像推送ACR | +| **2025-12-24 下午** | **后端镜像构建** 🎉 | ✅ Node.js后端镜像构建成功(修复200+TS错误) | +| **2025-12-31** | **IIT Agent启动** 🎯 | ✅ Day 1完成(数据库+企微配置+模块骨架) | +| **2026-01-01** | **企微可信域名** 🌐 | ✅ iit.xunzhengyixue.com域名验证完成 | +| **2026-01-02** | **REDCap对接方案** 🏆 | ✅ REDCap环境部署 + DET+REST API方案确定 | +| **2026-01-07 上午** | **PKB前端V3** 🎉 | ✅ PKB模块前端V3设计实现完成(Dashboard+Workspace+3种工作模式) | +| **2026-01-07 下午** | **PKB批处理完善** 🏆 | ✅ 批处理完整流程调试通过(执行+进度+结果导出)+ 文档上传功能 + UI优化 | +| **2026-01-19** | **pgvector集成** 🎉 | ✅ pgvector 0.8.1 安装成功,PKB RAG基础设施就绪 | +| **2026-01-21** | **🎉 Dify替换完成** | ✅ PKB 成功替换 Dify,完全使用自研 pgvector RAG 引擎 | +| **2026-01-22** | **🆕 OSS存储集成** | ✅ 阿里云OSS接入,PKB文档存储云端化,建立存储开发规范 | +| **当前** | **PKB模块生产可用** | ✅ 核心功能全部实现(95%),自研RAG+OSS存储上线 | +| **2026-01-07 晚** | **RVW模块开发完成** 🎉 | ✅ Phase 1-3完成(后端迁移+数据库扩展+前端重构) | --- ## 🎯 下一步计划 -### 短期(1-2周) -1. **ASL全文复筛前端**(Day 6-8) +### 🔥 最高优先级(当前)- 部署到生产环境 +1. ✅ **Python微服务部署到SAE** - 已完成 + - ✅ 创建SAE应用(轻量版) + - ✅ 配置环境变量(OSS、数据库) + - ✅ 健康检查验证 + - ✅ 获取内网地址:`http://172.17.173.66:8000` + +2. ✅ **Node.js后端Docker镜像构建** - 已完成 + - ✅ Prisma反向同步(32个模型) + - ✅ 手动补全Prisma关系字段(30+个) + - ✅ 修复TypeScript编译错误(200+ → 0) + - ✅ 创建Dockerfile(改进版方案B) + - ✅ 构建并推送到ACR(838MB镜像) + +3. ⏳ **Node.js后端部署到SAE** - 待执行 + - [ ] 创建SAE应用(1核2GB,测试环境) + - [ ] 配置ACR镜像拉取认证 + - [ ] 配置环境变量(数据库、Python服务、OSS、LLM API) + - [ ] 健康检查验证 + - [ ] 获取内网地址 + +3. **Node.js后端部署到SAE** + - 创建SAE应用(2核4GB) + - 配置环境变量(DATABASE_URL、OSS等) + - 配置Python微服务内网地址 + - 健康检查验证 + +4. **前端Nginx部署到SAE** + - 创建SAE应用(1核2GB) + - 配置后端API内网地址 + - 公网域名绑定 + - SSL证书配置 + +5. **全链路验证测试** + - 前端→Node.js后端→Python微服务→RDS + - ASL文献筛选完整流程 + - DC数据清洗完整流程 + - 性能和稳定性测试 + +### 短期(1-2周)- 功能完善 +6. **ASL全文复筛前端**(Day 6-8) - 4个核心页面:设置、进度、工作台、结果 - PDF上传和预览功能 - 双模型判断对比UI - 实时进度监控 -2. **DC工具B完成**(Day 4-7) - - ExtractionService实现 - - 批量提取API - - 前端集成和测试 +7. **DC工具B前端开发** + - 健康检查界面 + - 模板管理界面 + - 批量提取界面 + - 冲突解决界面 -### 中期(1-2月) -3. DC模块完整实现(工具A、工具C、Portal) -4. ASL模块优化(Prompt优化、并发处理) -5. LLM网关统一抽取 +### 中期(1-2月)- 模块完善 +8. DC模块完整实现(工具A、Portal优化) +9. ASL模块优化(Prompt优化、并发处理) +10. 监控和告警系统配置 +11. CI/CD流程建立 -### 长期(3月+) -6. SSA模块(智能统计分析) -7. ST模块(统计分析工具) -8. RVW模块(稿件审查系统) +### 长期(3月+)- 新模块开发 +12. SSA模块(智能统计分析) +13. ST模块(统计分析工具) +14. RVW模块(稿件审查系统) --- @@ -282,14 +1234,21 @@ AIclinicalresearch/ 1. ⭐⭐⭐ **本文档** - 系统当前状态 2. ⭐⭐⭐ [前后端模块化架构设计-V2.md](./前后端模块化架构设计-V2.md) - 架构总纲 3. ⭐⭐⭐ [云原生开发规范.md](../04-开发规范/08-云原生开发规范.md) - 开发规范(必读) -4. ⭐⭐ [01-系统架构分层设计.md](./01-系统架构分层设计.md) - 三层架构详解 -5. ⭐⭐ [09-总体需求文档(PRD).md](./09-总体需求文档\(PRD\).md) - 产品需求 +4. 🔴⭐⭐⭐ [数据库开发规范.md](../04-开发规范/09-数据库开发规范.md) - **数据库操作安全(必读!)** +5. ⭐⭐ [01-系统架构分层设计.md](./01-系统架构分层设计.md) - 三层架构详解 +6. ⭐⭐ [09-总体需求文档(PRD).md](./09-总体需求文档\(PRD\).md) - 产品需求 ### 🚀 当前开发相关 - [ASL模块当前状态](../03-业务模块/ASL-AI智能文献/00-模块当前状态与开发指南.md) - [DC模块README](../03-业务模块/DC-数据清洗整理/README.md) - [DC Day3完成总结](../03-业务模块/DC-数据清洗整理/06-开发记录/Day3完成总结.md) +### 🚀 部署文档(新增) +- ⭐ [00-部署进度总览.md](../05-部署文档/00-部署进度总览.md) - **一站式部署状态查看** +- [01-快速部署SOP-零基础版.md](../05-部署文档/01-快速部署SOP-零基础版.md) - 完整部署流程 +- [07-前端Nginx-SAE部署操作手册.md](../05-部署文档/07-前端Nginx-SAE部署操作手册.md) +- [08-PostgreSQL数据库部署操作手册.md](../05-部署文档/08-PostgreSQL数据库部署操作手册.md) + ### 🏗️ 架构设计 - [平台基础设施规划](../09-架构实施/04-平台基础设施规划.md) - [云原生部署架构指南](../09-架构实施/03-云原生部署架构指南.md) @@ -302,7 +1261,8 @@ AIclinicalresearch/ ### 环境要求 ``` Node.js: v22.18.0+ -PostgreSQL: 16+ +PostgreSQL: 15+(当前使用15.14,Docker镜像: pgvector/pgvector:pg15) +pgvector: 0.8.1(向量数据库扩展) npm: 10+ ``` @@ -342,6 +1302,28 @@ npm run dev # http://localhost:3000 3. ❌ **不要依赖本地文件系统**:使用OSS或内存处理 4. ❌ **不要创建新的Prisma实例**:使用全局 `prisma` 实例 +### 🔴 数据库操作安全(2026-01-11 事故教训) + +> ⚠️ **严重警告**:2026-01-11 因误用 `prisma db push --force-reset` 导致数据库事故,详见 [事故总结报告](../08-项目管理/2026-01-11-数据库事故总结.md) + +**禁止使用的危险命令:** +| 命令 | 危险等级 | 说明 | +|------|----------|------| +| `prisma db push --force-reset` | 🔴 **极高** | 会删除所有数据和非Prisma管理的对象 | +| `prisma migrate reset` | 🔴 **极高** | 重置整个数据库 | + +**必须遵守的规范:** +1. ✅ **操作前必须备份**:`docker exec ai-clinical-postgres pg_dump -U postgres -d ai_clinical_research > backup.sql` +2. ✅ **使用安全命令**:`prisma migrate dev`(开发)或 `prisma migrate deploy`(生产) +3. ✅ **了解 Prisma 管理边界**:pg-boss 的 `job_common` 表和函数不由 Prisma 管理 + +**Prisma 不管理的对象(需手动恢复):** +- `platform_schema.job_common` 表 → 恢复脚本:`restore_job_common.sql` +- `platform_schema.create_queue()` 函数 → 恢复脚本:`restore_pgboss_functions.sql` +- `platform_schema.delete_queue()` 函数 + +📚 **完整规范**:[数据库开发规范](../04-开发规范/09-数据库开发规范.md) + --- ## 📊 项目统计 @@ -353,27 +1335,145 @@ npm run dev # http://localhost:3000 - **总计**:约 85,000 行 ### 模块完成度 -- ✅ **已完成**:AIA(100%)、PKB(100%)、平台基础层(100%) -- 🚧 **开发中**:ASL(80%)、DC(30%) -- 📋 **未开始**:SSA、ST、RVW +- ✅ **已完成**:AIA V2.0(85%,核心功能完成)、平台基础层(100%)、RVW(95%)、通用能力层升级(100%)、**PKB(95%,Dify已替换)** 🎉 +- 🚧 **开发中**:ASL(80%)、DC(Tool C 98%,Tool B后端100%,Tool B前端0%)、IIT(60%,Phase 1.5完成) +- 📋 **未开始**:SSA、ST + +### 部署完成度 +- ✅ **基础设施**:VPC(100%)、NAT网关(100%)、安全组(100%) +- ✅ **数据存储**:RDS PostgreSQL(100%)、OSS(100%) +- ✅ **容器镜像**:前端Nginx(100%)、Python微服务(100%)、Node.js后端(100%)✨ 新增! +- ✅ **SAE应用**:Python微服务(100%,已运行)、Node.js后端(镜像已推送,待部署)、前端Nginx(待部署) +- 📋 **验证测试**:全链路测试(待进行) ### 测试覆盖率 - **平台基础层**:100%(8/8模块全部通过) -- **AIA模块**:手动测试通过 +- **通用能力层**:100%(StreamingService + Chat组件V2) +- **AIA模块 V2.0**:流式响应测试通过 ✅ - **PKB模块**:手动测试通过 - **ASL模块**:部分自动化测试(31个REST Client测试用例) - **DC模块**:开发中 --- +## 🏆 Postgres-Only 架构(2025-12-13 重大创新) + +### 核心理念 + +**Platform-Only 模式**:所有平台级功能(缓存、队列、任务管理)统一在 Platform 层实现,业务层只关注业务逻辑。 + +### 架构演进 + +``` +改造前: + 业务层 (分散) + ├── ASL: 任务管理字段 (6个) + └── DC: 任务管理字段 (6个) + ❌ 代码重复 + ❌ 维护困难 + +改造后(Platform-Only): + 平台层 (统一) + ├── platform_schema.job.data (pg-boss) + │ └── 所有任务管理信息 + └── CheckpointService (通用) + └── 操作 job.data,所有模块复用 + + 业务层 (简洁) + ├── ASL: 只存储业务信息 + └── DC: 只存储业务信息 + ✅ 无重复 + ✅ 易维护 + ✅ 符合3层架构 +``` + +### 核心组件 + +| 组件 | 位置 | 功能 | 通用性 | +|------|------|------|--------| +| **PostgresCacheAdapter** | `common/cache/` | Postgres 缓存 | ✅ 所有模块 | +| **PgBossQueue** | `common/jobs/` | pg-boss 队列封装 | ✅ 所有模块 | +| **CheckpointService** | `common/jobs/` | 操作 job.data | ✅ 所有模块 | +| **任务拆分工具** | `common/jobs/utils.ts` | 智能拆分批次 | ✅ 所有模块 | + +### 智能双模式处理 + +```typescript +const QUEUE_THRESHOLD = 50; + +if (items.length >= 50) { + // 队列模式:可靠性优先 + - 任务拆分(50条/批) + - 断点续传(每10条保存) + - 自动重试(3次) + - 支持24小时长任务 +} else { + // 直接模式:性能优先 + - 快速响应(<1分钟) + - 无队列延迟 + - 适合小任务 +} +``` + +### 技术亮点 + +1. **Platform-Only 模式**(首创) + - 利用 pg-boss 的 `job.data` 字段统一管理 + - 业务表保持简洁,只存储业务信息 + - CheckpointService 真正做到平台级通用 + +2. **智能阈值判断** + - 根据数据量自动选择处理模式 + - 性能与可靠性的完美平衡 + - 用户体验优化 + +3. **零额外成本** + - 不引入 Redis(年省¥8400) + - 使用已有 Postgres 实现缓存和队列 + - 适合小团队快速迭代 + +4. **企业级可靠性** + - 断点续传:任务中断后可恢复 + - 自动重试:失败任务重试3次 + - 并发处理:支持多实例并行 + - 长任务支持:可运行24小时 + +### 适用模块 + +- ✅ ASL 筛选服务(已改造) +- ✅ DC 提取服务(已改造) +- 📋 SSA 统计分析(未来) +- 📋 RVW 文献综述(未来) + +### 详细文档 + +- [Postgres-Only 改造实施计划](../07-运维文档/09-Postgres-Only改造实施计划(完整版).md) +- [Postgres-Only 全能架构解决方案](../07-运维文档/08-Postgres-Only 全能架构解决方案.md) +- [工作总结(2025-12-13)](../08-项目管理/03-每周计划/2025-12-13-Postgres-Only架构改造完成.md) + +--- + ## 🌟 技术亮点 -1. ✅ **适配器模式**:存储/缓存/日志支持本地↔云端零代码切换 -2. ✅ **10个Schema一次性完成**:架构一次到位 -3. ✅ **Prisma自动路由**:Schema迁移后,代码无需修改 -4. ✅ **4个LLM集成**:DeepSeek、Qwen、GPT、Claude -5. ✅ **增量演进**:新旧并存,降低风险 -6. ✅ **云原生就绪**:为SAE部署做好准备 +1. ✅ **Platform-Only 架构**:统一任务管理,零代码重复 🏆 +2. ✅ **智能双模式处理**:小任务快速响应,大任务可靠执行 🏆 +3. ✅ **🎉 Dify 成功替换!自研 RAG 引擎生产上线!** 🏆 **2026-01-21** + - ✅ **彻底移除 Dify 依赖**:PKB 模块完全使用自研 pgvector 引擎 + - pgvector 向量检索 + DeepSeek V3 查询理解 + qwen3-rerank 重排序 + - 跨语言支持:中文查询匹配英文文档(准确率 +20.5%) +4. ✅ **🆕 OSS 存储集成完成!** 🏆 **2026-01-22** + - ✅ **阿里云 OSS 接入**:4个 Bucket(生产/开发 × 数据/静态) + - ✅ **存储适配器架构**:OSSAdapter + LocalAdapter,支持云端和私有化部署 + - ✅ **PKB 集成**:文档存储云端化,目录结构规范化 + - ✅ **开发规范建立**:`11-OSS存储开发规范.md` + - Brain-Hand 架构:业务层思考,引擎层执行 + - 成本:¥0.0025/次,延迟:2.5秒 +4. ✅ **适配器模式**:存储/缓存/日志支持本地↔云端零代码切换 +5. ✅ **13个Schema隔离**:架构一次到位(新增 ekb_schema) +6. ✅ **Prisma自动路由**:Schema迁移后,代码无需修改 +7. ✅ **4个LLM集成**:DeepSeek、Qwen、GPT、Claude +8. ✅ **增量演进**:新旧并存,降低风险 +9. ✅ **云原生就绪**:为SAE部署做好准备 --- @@ -385,10 +1485,119 @@ npm run dev # http://localhost:3000 --- -**文档版本**:v1.0 -**最后更新**:2025-11-28 -**下次更新**:ASL全文复筛前端完成 或 DC工具B完成 +**文档版本**:v4.2 +**最后更新**:2026-01-24 +**本次更新**:pg_bigm 扩展安装完成、异步队列安全规范升级 --- **🎉 祝新的AI助手工作顺利!所有信息已梳理完毕,可以无缝衔接!** + +--- + +## 📝 最新更新(2026-01-07) + +**PKB模块核心功能全部实现 🎉**: + +### 上午:前端V3设计实现 +1. ✅ **后端模块迁移**:迁移到 /modules/pkb,v2 API路由注册 +2. ✅ **Dashboard页面**:基于知识库仪表盘V5原型实现 +3. ✅ **Workspace页面**:基于工作台V3原型实现 + +### 下午:批处理完整流程验证通过 +4. ✅ **三种工作模式**:全文阅读、逐篇精读、批处理全部实现 +5. ✅ **批处理功能**: + - 模板选择(临床研究信息提取,8个字段) + - 文档选择(3-50篇) + - 实时进度显示 + - 结果表格(多行显示+Tooltip) + - CSV导出 +6. ✅ **Chat组件集成**:Ant Design X,支持流式响应和自定义渲染 +7. ✅ **文档上传功能**:拖拽上传 + 进度显示 + Modal弹窗 +8. ✅ **UI优化**:参考文献格式化、表格多行显示、输入框清除、自动滚动 + +### 技术亮点 +- **问题解决**:修复10+个技术问题(API路径、字段映射、状态同步等) +- **性能优化**:批处理3篇文档~17-28秒 +- **用户体验**:严格按照原型图实现,界面美观易用 + +### 里程碑意义 +**PKB模块已具备生产环境可用性**,为后续功能扩展奠定坚实基础! +4. ✅ **3种工作模式**:全文阅读、逐篇精读、批处理 +5. ✅ **Chat组件集成**:复用Ant Design X通用Chat组件 +6. ✅ **响应式布局**:单层Header + 紧凑工作模式栏 + 最大化聊天区域 + +**技术亮点**: +- 🔥 **模块化架构**:前后端完整迁移到新架构 +- 🔥 **复用通用组件**:shared/components/Chat +- 🔥 **Zustand状态管理**:轻量级状态管理 +- 🔥 **双路由兼容**:v1 + v2 API同时运行 + +**待解决问题**: +- 🔧 批处理API执行待调试 +- 🔧 知识资产页面导航条待完善 +- 🔧 UI与原型图精细化对比 + +**模块进度**:75%完成 + +--- + +**IIT Manager Agent Phase 1.5 完成(2026-01-03)**: +- ✅ AI对话集成完成:ChatService + SessionMemory +- ✅ REDCap数据查询集成:意图识别 + 数据注入LLM +- ✅ 解决LLM幻觉问题:AI基于真实数据回答 +- ✅ 测试通过:查询test0102项目,ID 7患者详细信息 + +**模块进度**:60%完成(Phase 1.5) + +--- + +**RVW稿件审查模块开发完成(2026-01-07 ~ 2026-01-10)**: + +### Phase 1:后端模块迁移与扩展 +- ✅ 创建 `backend/src/modules/rvw/` 模块结构 +- ✅ 迁移 reviewService、editorialService、methodologyService +- ✅ 实现智能体选择(selectedAgents) +- ✅ 实现批量运行API(batchRunReviewTasks) +- ✅ 替换 console.log 为 logger 服务 +- ✅ 注册 v2 API路由(/api/v2/rvw) +- ✅ 实现 pg-boss 异步任务处理(reviewWorker) + +### Phase 2:数据库字段扩展 +- ✅ 添加 selectedAgents、editorialScore、methodologyScore、methodologyStatus 字段 +- ✅ 添加 picoExtract、isArchived、archivedAt 字段 +- ✅ 使用 prisma db push 同步到数据库 + +### Phase 3:前端重构(frontend-v2) +- ✅ 创建 `frontend-v2/src/modules/rvw/` 完整模块目录结构 +- ✅ 实现 Dashboard 页面(任务列表、筛选、批量操作) +- ✅ 实现 TaskDetail 组件(审稿进度条、实时状态轮询) +- ✅ 实现 EditorialReport/MethodologyReport 组件 +- ✅ 实现 AgentModal 组件(智能体选择弹窗) +- ✅ 注册到 moduleRegistry.ts + +### Phase 4:集成测试与Bug修复(2026-01-10) +- ✅ 修复方法学分数不显示问题 +- ✅ 修复只选方法学时详情页不显示报告问题 +- ✅ 完整测试单智能体和双智能体审稿流程 + +### Phase 5:报告导出(2026-01-10) +- ✅ 安装 docx 和 file-saver 库 +- ✅ 实现 Word 文档导出功能 +- ✅ 支持结构化报告(标题、基本信息、稿约规范性、方法学评估) + +### Phase 6:Schema隔离迁移(2026-01-10) +- ✅ 将 review_tasks 表从 public 迁移到 rvw_schema +- ✅ 更新 Prisma schema 中的 @@schema 指令 +- ✅ 重新生成 Prisma Client +- ✅ 验证所有查询和关联正常工作 + +**技术亮点**: +- 🔥 **新旧API兼容**:v1 + v2 API同时运行 +- 🔥 **智能体可选**:用户可选择运行稿约规范性/方法学/两者 +- 🔥 **异步任务处理**:使用 pg-boss 队列处理长时间审稿任务 +- 🔥 **Word导出**:使用 docx 库生成专业格式的审稿报告 +- 🔥 **Schema隔离**:数据库表已迁移到独立的 rvw_schema +- 🔥 **云原生改造**:使用 logger 服务,遵循开发规范 + +**模块进度**:95%完成(Phase 1-6) diff --git a/docs/02-通用能力层/02-文档处理引擎/README.md b/docs/02-通用能力层/02-文档处理引擎/README.md index 3987c3ae..dacf7b1f 100644 --- a/docs/02-通用能力层/02-文档处理引擎/README.md +++ b/docs/02-通用能力层/02-文档处理引擎/README.md @@ -210,4 +210,4 @@ rispy>=0.7.0 --- -**维护人:** 技术架构师 +**维护人:** 技术架构师 \ No newline at end of file diff --git a/docs/02-通用能力层/03-RAG引擎/README.md b/docs/02-通用能力层/03-RAG引擎/README.md index 70eae812..544be2ca 100644 --- a/docs/02-通用能力层/03-RAG引擎/README.md +++ b/docs/02-通用能力层/03-RAG引擎/README.md @@ -346,8 +346,4 @@ class KnowledgeBaseEngine { ### 2025-11-06 初始版本 - 基于 Dify 实现 -- 仅 PKB 模块使用 - ---- - -**维护人:** 技术架构师 +- 仅 PKB 模块使用---**维护人:** 技术架构师 \ No newline at end of file diff --git a/docs/03-业务模块/ADMIN-运营管理端/00-Phase3.5完成总结.md b/docs/03-业务模块/ADMIN-运营管理端/00-Phase3.5完成总结.md index 2cf5c24d..0c974bac 100644 --- a/docs/03-业务模块/ADMIN-运营管理端/00-Phase3.5完成总结.md +++ b/docs/03-业务模块/ADMIN-运营管理端/00-Phase3.5完成总结.md @@ -307,10 +307,6 @@ Level 3: 兜底Prompt(缓存也失效) - - - - diff --git a/docs/03-业务模块/ADMIN-运营管理端/00-模块当前状态与开发指南.md b/docs/03-业务模块/ADMIN-运营管理端/00-模块当前状态与开发指南.md index 8b137891..ec5fc4d3 100644 --- a/docs/03-业务模块/ADMIN-运营管理端/00-模块当前状态与开发指南.md +++ b/docs/03-业务模块/ADMIN-运营管理端/00-模块当前状态与开发指南.md @@ -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**:数据库迁移 + +--- + +*祝开发顺利!🚀* diff --git a/docs/03-业务模块/ADMIN-运营管理端/04-开发计划/03-运营监控系统MVP开发计划.md b/docs/03-业务模块/ADMIN-运营管理端/04-开发计划/03-运营监控系统MVP开发计划.md new file mode 100644 index 00000000..d9b2ec9b --- /dev/null +++ b/docs/03-业务模块/ADMIN-运营管理端/04-开发计划/03-运营监控系统MVP开发计划.md @@ -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 = {}; + 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(基础设施),再逐步完成埋点集成和前端页面。 + diff --git a/docs/03-业务模块/ADMIN-运营管理端/04-开发计划/04-运营监控系统MVP实施记录.md b/docs/03-业务模块/ADMIN-运营管理端/04-开发计划/04-运营监控系统MVP实施记录.md new file mode 100644 index 00000000..e6824485 --- /dev/null +++ b/docs/03-业务模块/ADMIN-运营管理端/04-开发计划/04-运营监控系统MVP实施记录.md @@ -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 => { + const response = await apiClient.get('/api/admin/stats/overview'); + return response.data.data; +}; + +// 获取实时活动流 +export const getLiveFeed = async (limit?: number): Promise => { + 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* + diff --git a/docs/03-业务模块/ASL-AI智能文献/02-技术设计/02-API设计规范.md b/docs/03-业务模块/ASL-AI智能文献/02-技术设计/02-API设计规范.md index 86c8b8f6..739856af 100644 --- a/docs/03-业务模块/ASL-AI智能文献/02-技术设计/02-API设计规范.md +++ b/docs/03-业务模块/ASL-AI智能文献/02-技术设计/02-API设计规范.md @@ -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) diff --git a/docs/03-业务模块/RVW-稿件审查系统/00-模块当前状态与开发指南.md b/docs/03-业务模块/RVW-稿件审查系统/00-模块当前状态与开发指南.md index 0c10124a..40efaa7e 100644 --- a/docs/03-业务模块/RVW-稿件审查系统/00-模块当前状态与开发指南.md +++ b/docs/03-业务模块/RVW-稿件审查系统/00-模块当前状态与开发指南.md @@ -34,7 +34,7 @@ 1. **稿约规范性评估**(11项标准) - 文题字数、摘要结构、参考文献、图片DPI等 - - 基于《中华脑血管病杂志》稿约标准 + - 基于《中华医学超声杂志》稿约标准 2. **方法学评估**(3部分,20个检查点) - 科研设计评估(9个检查点) diff --git a/docs/05-部署文档/0126部署/07-0126部署状态真实记录.md b/docs/05-部署文档/0126部署/07-0126部署状态真实记录.md index 6c42c9da..158ab1de 100644 --- a/docs/05-部署文档/0126部署/07-0126部署状态真实记录.md +++ b/docs/05-部署文档/0126部署/07-0126部署状态真实记录.md @@ -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 容器内直接操作 +- ✅ **验证方法**:导入后立即查询中文字段,确认显示正确 diff --git a/docs/05-部署文档/0126部署/08-部署完成总结.md b/docs/05-部署文档/0126部署/08-部署完成总结.md index b902ebd6..b7bbf790 100644 --- a/docs/05-部署文档/0126部署/08-部署完成总结.md +++ b/docs/05-部署文档/0126部署/08-部署完成总结.md @@ -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(添加中文编码问题修复记录) > **维护人员**:开发团队 diff --git a/docs/06-测试文档/故障分析报告 (1).md b/docs/06-测试文档/故障分析报告 (1).md new file mode 100644 index 00000000..402e1e91 --- /dev/null +++ b/docs/06-测试文档/故障分析报告 (1).md @@ -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 个任务。 \ No newline at end of file diff --git a/docs/07-运维文档/01-PgBoss队列监控与维护.md b/docs/07-运维文档/01-PgBoss队列监控与维护.md new file mode 100644 index 00000000..d363f83d --- /dev/null +++ b/docs/07-运维文档/01-PgBoss队列监控与维护.md @@ -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) diff --git a/docs/07-运维文档/02-故障预防检查清单.md b/docs/07-运维文档/02-故障预防检查清单.md new file mode 100644 index 00000000..b18f6886 --- /dev/null +++ b/docs/07-运维文档/02-故障预防检查清单.md @@ -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) diff --git a/docs/07-运维文档/03-数据库迁移注意事项.md b/docs/07-运维文档/03-数据库迁移注意事项.md new file mode 100644 index 00000000..811d7ce7 --- /dev/null +++ b/docs/07-运维文档/03-数据库迁移注意事项.md @@ -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 任务无限循环故障 | diff --git a/docs/07-运维文档/README.md b/docs/07-运维文档/README.md index dffc51d8..588e3c84 100644 --- a/docs/07-运维文档/README.md +++ b/docs/07-运维文档/README.md @@ -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) diff --git a/docs/08-项目管理/PKB和RVW功能迁移计划.md b/docs/08-项目管理/PKB和RVW功能迁移计划.md index 5131d233..137a59fb 100644 --- a/docs/08-项目管理/PKB和RVW功能迁移计划.md +++ b/docs/08-项目管理/PKB和RVW功能迁移计划.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); + + diff --git a/earch b/earch deleted file mode 100644 index 04bcfda4..00000000 --- a/earch +++ /dev/null @@ -1,579 +0,0 @@ -warning: in the working copy of 'docs/00-系统总体设计/00-今日架构设计总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-系统总体设计/00-核心问题解答.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-系统总体设计/00-阅读指南.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-系统总体设计/03-数据库架构说明.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-系统总体设计/04-运营管理端架构设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-系统总体设计/05-Schema隔离方案与成本分析.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-系统总体设计/06-模块独立部署与单机版方案.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-系统总体设计/07-Monorepo架构评估.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-系统总体设计/08-架构设计全景图.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-系统总体设计/09-总体需求文档(PRD).md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-系统总体设计/10-核心业务规则总览.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-系统总体设计/99-下一步行动决策建议.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-项目概述/产品需求文档(PRD).md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-项目概述/技术架构总览.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-项目概述/文档梳理与差异分析.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-项目概述/最新需求与技术方案深度评估.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-项目概述/现有系统技术摸底报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/00-项目概述/设计文档完成总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/01-平台基础层/01-用户与权限中心(UAM)/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/01-平台基础层/02-存储服务/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/01-平台基础层/03-通知服务/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/01-平台基础层/04-监控与日志/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/01-平台基础层/05-系统配置/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/01-平台基础层/06-前端架构/01-前端总体架构设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/01-平台基础层/06-前端架构/02-导航结构设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/01-平台基础层/06-前端架构/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/01-平台基础层/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/02-通用能力层/01-LLM大模型网关/03-CloseAI集成指南.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/02-通用能力层/02-文档处理引擎/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/02-通用能力层/03-RAG引擎/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/02-通用能力层/04-数据ETL引擎/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/02-通用能力层/05-医学NLP引擎/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/02-通用能力层/Postgres-Only异步任务处理指南.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/02-通用能力层/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/02-通用能力层/通用能力层技术债务清单.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ADMIN-运营管理端/00-Phase3.5完成总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ADMIN-运营管理端/00-系统设计/00-权限与角色体系梳理报告_v1.0.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ADMIN-运营管理端/02-技术设计/03-Prompt管理系统快速参考.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ADMIN-运营管理端/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ADMIN运营与INST机构管理端-文档体系建立完成.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/AIA-AI智能问答/02-技术设计/01-数据库设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/AIA-AI智能问答/04-开发计划/01-AIA-V2.1开发计划.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/AIA-AI智能问答/04-开发计划/02-后端API设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/AIA-AI智能问答/04-开发计划/03-前端组件设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/AIA-AI智能问答/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/00-新AI交接文档.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/00-系统设计/01-系统架构设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/00-系统设计/02-模块关联关系设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/00-系统设计/03-数据流设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/00-系统设计/04-技术选型说明.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/01-需求分析/01-需求总览.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/01-需求分析/02-标题摘要初筛需求详述.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/01-需求分析/03-全文复筛需求详述.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/01-需求分析/04-用户故事和验收标准.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/01-需求分析/05-非功能性需求.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/02-技术设计/03-前端组件设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/02-技术设计/04-AI模型集成设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/02-技术设计/05-文件处理设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/02-技术设计/06-质量保障与可追溯策略.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/02-技术设计/07-文献处理技术选型.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/02-技术设计/07-智能Prompt生成模块开发计划.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/02-技术设计/08-全文复筛质量保障策略.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/03-UI设计/01-标题摘要初筛UI设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/03-UI设计/02-全文复筛UI设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/03-UI设计/03-设计规范.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/04-开发计划/04-Week4-结果展示与导出开发计划.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/04-开发计划/05-全文复筛前端开发计划.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/04-开发计划/全文复筛开发计划-更新说明.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端开发完成.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端逻辑调整.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-Prompt设计与测试完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-Week1完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-Week2-Day1-Bug修复.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-Week2-Day1完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-两步测试完整报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-今日工作完成总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-今日工作总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-全天开发总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-卒中数据泛化测试报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-架构重构完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-路由问题修复报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-19-Week2-Day2完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-19-Week2-Day3完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-21-Week4完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-21-字段映射问题修复.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-21-用户体验优化.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-21-真实LLM集成完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-22_Day2-Day3_LLM服务与验证系统开发.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_Day4_数据库设计与批处理服务开发.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_Day5_全文复筛API开发.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_数据库迁移状态说明.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-开发记录/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-测试文档/01-测试计划.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-测试文档/02-标题摘要初筛测试用例.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/05-测试文档/03-测试数据/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ASL-AI智能文献/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_AI_Few-shot示例库.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Bug修复总结_2025-12-08.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Day3开发计划.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Day4-5前端开发计划.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_MVP开发计划_V1.0.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Pivot列顺序优化总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_方案B实施总结_2025-12-09.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_缺失值处理_开发进度_2025-12-10.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_缺失值处理功能_更新说明.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-02_工作总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-06_工具C_Day1开发完成总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-06_工具C_Day2开发完成总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_AI对话核心功能增强总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Bug修复_DataGrid空数据防御.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Day5_Ant-Design-X重构完成.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Day5最终总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_UI优化与Bug修复.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_后端API完整对接完成.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_完整UI优化与功能增强.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_工具C_Day4前端基础完成.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/DC模块重建完成总结-Day1.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/Day2完成总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/Day3完成总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase1-Portal页面开发完成-2025-12-02.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase2-ToolB-Step1-2开发完成-2025-12-03.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/Portal页面UI优化-2025-12-02.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/Tool-B-MVP完成总结-2025-12-03.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-2025-12-03.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-Round2-2025-12-03.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB浏览器测试计划-2025-12-03.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/后端API测试报告-2025-12-02.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/待办事项-下一步工作.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/06-开发记录/数据库验证报告-2025-12-02.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/07-技术债务/Tool-B技术债务清单.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/DC-数据清洗整理/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/IIT Manager Agent/02-技术设计/IIT Manager Agent 完整技术开发方案 (V1.1).md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/IIT Manager Agent/02-技术设计/IIT Manager Agent 技术路径与架构设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/IIT Manager Agent/04-开发计划/REDCap对接技术方案与实施指南.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/IIT Manager Agent/04-开发计划/企业微信注册指南.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/IIT Manager Agent/06-开发记录/2026-01-04-Dify知识库集成开发记录.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/IIT Manager Agent/06-开发记录/Day2-REDCap实时集成开发完成记录.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成与端到端测试完成记录.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成开发完成记录.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/IIT Manager Agent/06-开发记录/Phase1.5-AI对话集成REDCap完成记录.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/IIT Manager Agent/06-开发记录/V1.1更新完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/IIT Manager Agent/07-技术债务/IIT Manager Agent 技术债务清单.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/INST-机构管理端/00-模块当前状态与开发指南.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/INST-机构管理端/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/PKB-个人知识库/00-模块当前状态与开发指南.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/PKB-个人知识库/02-技术设计/01-数据库设计.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/PKB-个人知识库/06-开发记录/2026-01-07-前端迁移与批处理功能完善.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/PKB-个人知识库/06-开发记录/2026-01-07_PKB模块前端V3设计实现.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/PKB-个人知识库/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/RVW-稿件审查系统/00-模块当前状态与开发指南.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/RVW-稿件审查系统/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/Redcap/00-模块概览/00-REDCap模块文档导航.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/Redcap/00-模块概览/02-REDCap对接总体方案.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/Redcap/00-模块概览/03-REDCap对接风险评估与技术挑战分析.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/Redcap/01-部署与配置/10-REDCap_Docker部署操作手册.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/Redcap/01-部署与配置/13-部署问题排查手册.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/Redcap/04-参考资料/02-REDCap部署指南与环境要求_旧版.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/Redcap/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/SSA-智能统计分析/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/03-业务模块/ST-统计分析工具/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/04-开发规范/01-数据库设计规范.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/04-开发规范/02-API设计规范.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/04-开发规范/03-数据库全局视图.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/04-开发规范/04-API路由总览.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/04-开发规范/09-数据库开发规范.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/04-开发规范/10-模块认证规范.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/05-部署文档/02-SAE部署完全指南(产品经理版).md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/05-部署文档/07-前端Nginx-SAE部署操作手册.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/05-部署文档/08-PostgreSQL数据库部署操作手册.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/05-部署文档/10-Node.js后端-Docker镜像构建手册.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/05-部署文档/11-Node.js后端-SAE部署配置清单.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/05-部署文档/12-Node.js后端-SAE部署操作手册.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/05-部署文档/13-Node.js后端-镜像修复记录.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/05-部署文档/14-Node.js后端-pino-pretty问题修复.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/05-部署文档/16-前端Nginx-部署成功总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/05-部署文档/17-完整部署实战手册-2025版.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/05-部署文档/18-部署文档使用指南.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/05-部署文档/19-日常更新快速操作手册.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/05-部署文档/文档修正报告-20251214.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/06-测试文档/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/07-运维文档/02-环境变量配置模板.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/07-运维文档/03-SAE环境变量配置指南.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/07-运维文档/05-Redis缓存与队列的区别说明.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/07-运维文档/06-长时间任务可靠性分析.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/07-运维文档/07-Redis使用需求分析(按模块).md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/03-每周计划/2025-11-12-工作总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/03-每周计划/2025-11-13-任务19完成总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/03-每周计划/2025-11-13-工作总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/03-每周计划/2025-11-14-任务19完成总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/03-每周计划/2025-11-16-平台基础设施规划完成总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/03-每周计划/2025-11-17-平台基础设施实施完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/03-每周计划/2025-11-17-平台基础设施验证报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/03-每周计划/2025-11-18-AI助手工作交接.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/03-每周计划/2025-12-13-Postgres-Only架构改造完成.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/03-每周计划/Week2-总结报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/04-技术决策/2025-11-18-MSE与ARMS采购决策分析.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/04-技术决策/2025-11-18-PostgreSQL版本选择建议.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/04-技术决策/2025-11-18-阿里云RDS系列选择建议.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/05-技术债务/通用对话服务抽取计划.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/2026-01-11-数据库事故总结.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/PKB前端问题修复报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/PKB前端验证指南.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/PKB功能审查报告-阶段0.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/PKB和RVW功能迁移计划.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/PKB精细化优化报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/PKB迁移-超级安全执行计划.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/PKB迁移-阶段1完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/PKB迁移-阶段2完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/PKB迁移-阶段2进行中.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/PKB迁移-阶段3完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/PKB迁移-阶段4完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/V2.2版本变化说明.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/下一阶段行动计划-V2.0-模块化架构优先.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/08-项目管理/下一阶段行动计划-V2.2-前端架构优先版.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/09-架构实施/01-Schema隔离架构设计(10个).md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/09-架构实施/04-平台基础设施规划.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/09-架构实施/Prisma配置完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/09-架构实施/Schema迁移完成报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/09-架构实施/前端模块注册机制实施报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/09-架构实施/后端代码分层-迁移计划.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/09-架构实施/后端代码分层实施报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/09-架构实施/后端架构增量演进方案.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/09-架构实施/快速功能测试报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/09-架构实施/数据库验证通过.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/09-架构实施/模块配置更新报告.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/09-架构实施/编码规范-UTF8最佳实践.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/_templates/API设计-模板.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/_templates/README.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/_templates/数据库设计-模板.md', CRLF will be replaced by LF the next time Git touches it -warning: in the working copy of 'docs/_templates/模块README-模板.md', CRLF will be replaced by LF the next time Git touches it -docs/00-系统总体设计/00-今日架构设计总结.md -docs/00-系统总体设计/00-核心问题解答.md -docs/00-系统总体设计/00-阅读指南.md -docs/00-系统总体设计/01-系统架构分层设计.md -docs/00-系统总体设计/02-文档体系重构方案.md -docs/00-系统总体设计/03-数据库架构说明.md -docs/00-系统总体设计/04-运营管理端架构设计.md -docs/00-系统总体设计/05-Schema隔离方案与成本分析.md -docs/00-系统总体设计/06-模块独立部署与单机版方案.md -docs/00-系统总体设计/07-Monorepo架构评估.md -docs/00-系统总体设计/08-架构设计全景图.md -docs/00-系统总体设计/09-总体需求文档(PRD).md -docs/00-系统总体设计/10-核心业务规则总览.md -docs/00-系统总体设计/99-下一步行动决策建议.md -docs/00-系统总体设计/README.md -docs/00-系统总体设计/前后端模块化架构设计-V2.md -docs/00-项目概述/产品需求文档(PRD).md -docs/00-项目概述/壹证循科技 AI科研产品需求文档.md -docs/00-项目概述/壹证循科技AI科研产品 - 技术架构白皮书.md -docs/00-项目概述/技术架构总览.md -docs/00-项目概述/文档梳理与差异分析.md -docs/00-项目概述/最新需求与技术方案深度评估.md -docs/00-项目概述/现有系统技术摸底报告.md -docs/00-项目概述/设计文档完成总结.md -docs/01-平台基础层/01-用户与权限中心(UAM)/README.md -docs/01-平台基础层/02-存储服务/README.md -docs/01-平台基础层/03-通知服务/README.md -docs/01-平台基础层/04-监控与日志/README.md -docs/01-平台基础层/05-系统配置/README.md -docs/01-平台基础层/06-前端架构/01-前端总体架构设计.md -docs/01-平台基础层/06-前端架构/02-导航结构设计.md -docs/01-平台基础层/06-前端架构/README.md -docs/01-平台基础层/README.md -docs/02-通用能力层/01-LLM大模型网关/03-CloseAI集成指南.md -docs/02-通用能力层/01-LLM大模型网关/README.md -docs/02-通用能力层/02-文档处理引擎/README.md -docs/02-通用能力层/03-RAG引擎/README.md -docs/02-通用能力层/04-数据ETL引擎/README.md -docs/02-通用能力层/05-医学NLP引擎/README.md -docs/02-通用能力层/Postgres-Only异步任务处理指南.md -docs/02-通用能力层/README.md -docs/02-通用能力层/通用能力层技术债务清单.md -docs/03-业务模块/ADMIN-运营管理端/00-Phase3.5完成总结.md -docs/03-业务模块/ADMIN-运营管理端/00-模块当前状态与开发指南.md -docs/03-业务模块/ADMIN-运营管理端/00-系统设计/00-权限与角色体系梳理报告_v1.0.md -docs/03-业务模块/ADMIN-运营管理端/00-系统设计/02-通用能力层_10-权限体系梳理反馈与修正建议.md -docs/03-业务模块/ADMIN-运营管理端/00-给新AI的快速指南.md -docs/03-业务模块/ADMIN-运营管理端/01-需求分析/02-通用能力层_07-运营与机构管理端PRD_v2.1.md -docs/03-业务模块/ADMIN-运营管理端/02-技术设计/02-通用能力层_03-Prompt管理系统与灰度预览设计方案.md -docs/03-业务模块/ADMIN-运营管理端/02-技术设计/03-Prompt管理系统快速参考.md -docs/03-业务模块/ADMIN-运营管理端/02-技术设计/Prompt管理后台设计.md -docs/03-业务模块/ADMIN-运营管理端/04-开发计划/00-总体开发计划.md -docs/03-业务模块/ADMIN-运营管理端/04-开发计划/01-TODO清单(可追踪).md -docs/03-业务模块/ADMIN-运营管理端/04-开发计划/02-Prompt管理系统开发计划.md -docs/03-业务模块/ADMIN-运营管理端/README.md -docs/03-业务模块/ADMIN运营与INST机构管理端-文档体系建立完成.md -docs/03-业务模块/AIA-AI智能问答/01-需求分析/AIA模块PRD.md -docs/03-业务模块/AIA-AI智能问答/02-技术设计/01-数据库设计.md -docs/03-业务模块/AIA-AI智能问答/04-开发计划/01-AIA-V2.1开发计划.md -docs/03-业务模块/AIA-AI智能问答/04-开发计划/02-后端API设计.md -docs/03-业务模块/AIA-AI智能问答/04-开发计划/03-前端组件设计.md -docs/03-业务模块/AIA-AI智能问答/README.md -docs/03-业务模块/ASL-AI智能文献/00-新AI交接文档.md -docs/03-业务模块/ASL-AI智能文献/00-模块当前状态与开发指南.md -docs/03-业务模块/ASL-AI智能文献/00-系统设计/01-系统架构设计.md -docs/03-业务模块/ASL-AI智能文献/00-系统设计/02-模块关联关系设计.md -docs/03-业务模块/ASL-AI智能文献/00-系统设计/03-数据流设计.md -docs/03-业务模块/ASL-AI智能文献/00-系统设计/04-技术选型说明.md -docs/03-业务模块/ASL-AI智能文献/01-需求分析/01-需求总览.md -docs/03-业务模块/ASL-AI智能文献/01-需求分析/02-标题摘要初筛需求详述.md -docs/03-业务模块/ASL-AI智能文献/01-需求分析/03-全文复筛需求详述.md -docs/03-业务模块/ASL-AI智能文献/01-需求分析/04-用户故事和验收标准.md -docs/03-业务模块/ASL-AI智能文献/01-需求分析/05-非功能性需求.md -docs/03-业务模块/ASL-AI智能文献/01-需求分析/AI智能文献PRD(1)-产品概览.md -docs/03-业务模块/ASL-AI智能文献/01-需求分析/AI智能文献PRD(2)-初筛与复筛.md -docs/03-业务模块/ASL-AI智能文献/01-需求分析/AI智能文献PRD(3)-提取与分析模块.md -docs/03-业务模块/ASL-AI智能文献/02-技术设计/01-数据库设计.md -docs/03-业务模块/ASL-AI智能文献/02-技术设计/02-API设计规范.md -docs/03-业务模块/ASL-AI智能文献/02-技术设计/03-前端组件设计.md -docs/03-业务模块/ASL-AI智能文献/02-技术设计/04-AI模型集成设计.md -docs/03-业务模块/ASL-AI智能文献/02-技术设计/05-文件处理设计.md -docs/03-业务模块/ASL-AI智能文献/02-技术设计/06-质量保障与可追溯策略.md -docs/03-业务模块/ASL-AI智能文献/02-技术设计/07-文献处理技术选型.md -docs/03-业务模块/ASL-AI智能文献/02-技术设计/07-智能Prompt生成模块开发计划.md -docs/03-业务模块/ASL-AI智能文献/02-技术设计/08-全文复筛质量保障策略.md -docs/03-业务模块/ASL-AI智能文献/03-UI设计/01-标题摘要初筛UI设计.md -docs/03-业务模块/ASL-AI智能文献/03-UI设计/02-全文复筛UI设计.md -docs/03-业务模块/ASL-AI智能文献/03-UI设计/03-设计规范.md -docs/03-业务模块/ASL-AI智能文献/04-开发计划/01-开发里程碑.md -docs/03-业务模块/ASL-AI智能文献/04-开发计划/02-标题摘要初筛开发计划.md -docs/03-业务模块/ASL-AI智能文献/04-开发计划/03-任务分解.md -docs/03-业务模块/ASL-AI智能文献/04-开发计划/04-Week4-结果展示与导出开发计划.md -docs/03-业务模块/ASL-AI智能文献/04-开发计划/04-全文复筛开发计划.md -docs/03-业务模块/ASL-AI智能文献/04-开发计划/05-全文复筛前端开发计划.md -docs/03-业务模块/ASL-AI智能文献/04-开发计划/全文复筛开发计划-更新说明.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端开发完成.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-01-23_全文复筛前端逻辑调整.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-Prompt设计与测试完成报告.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-Week1完成报告.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-Week2-Day1-Bug修复.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-Week2-Day1完成报告.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-两步测试完整报告.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-今日工作完成总结.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-今日工作总结.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-全天开发总结.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-卒中数据泛化测试报告.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-架构重构完成报告.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-18-路由问题修复报告.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-19-Week2-Day2完成报告.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-19-Week2-Day3完成报告.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-21-Week4完成报告.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-21-字段映射问题修复.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-21-用户体验优化.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-21-真实LLM集成完成报告.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-22_Day2-Day3_LLM服务与验证系统开发.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_Day4_数据库设计与批处理服务开发.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_Day5_全文复筛API开发.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_数据库迁移状态说明.md -docs/03-业务模块/ASL-AI智能文献/05-开发记录/README.md -docs/03-业务模块/ASL-AI智能文献/05-测试文档/01-测试计划.md -docs/03-业务模块/ASL-AI智能文献/05-测试文档/02-标题摘要初筛测试用例.md -docs/03-业务模块/ASL-AI智能文献/05-测试文档/03-测试数据/README.md -docs/03-业务模块/ASL-AI智能文献/06-技术债务/技术债务清单.md -docs/03-业务模块/ASL-AI智能文献/README.md -docs/03-业务模块/DC-数据清洗整理/00-工具C当前状态与开发指南.md -docs/03-业务模块/DC-数据清洗整理/00-模块当前状态与开发指南.md -docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD_工具C_科研数据编辑器_V6.md -docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD:Tool A - 医疗数据超级合并器 (The Super Merger).md -docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD:Tool B - 病历结构化机器人 (The AI Structurer).md -docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD:Tool C - 科研数据编辑器 (MVP V1.1).md -docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD:Tool C - 科研数据编辑器.md -docs/03-业务模块/DC-数据清洗整理/01-需求分析/PRD:智能数据清洗工作台 (The Data Cleaning Portal).md -docs/03-业务模块/DC-数据清洗整理/01-需求分析/总体 PRD:医疗科研智能数据清洗平台.md -docs/03-业务模块/DC-数据清洗整理/02-技术设计/API设计文档-DC模块(完整版).md -docs/03-业务模块/DC-数据清洗整理/02-技术设计/工具 C:AI 辅助医疗数据清洗场景分级清单.md -docs/03-业务模块/DC-数据清洗整理/02-技术设计/总体技术设计文档:医疗科研智能数据清洗平台.md -docs/03-业务模块/DC-数据清洗整理/02-技术设计/技术设计文档:工具 A - 医疗数据超级合并器 (The Super Merger).md -docs/03-业务模块/DC-数据清洗整理/02-技术设计/技术设计文档:工具 B - 病历结构化机器人 (The AI Structurer).md -docs/03-业务模块/DC-数据清洗整理/02-技术设计/技术设计文档:工具 C - 科研数据编辑器 (V7 云端沙箱抗风险版).md -docs/03-业务模块/DC-数据清洗整理/02-技术设计/技术设计文档:工具 C - 科研数据编辑器.md -docs/03-业务模块/DC-数据清洗整理/02-技术设计/数据库设计文档-DC模块(完整版).md -docs/03-业务模块/DC-数据清洗整理/03-UI设计/UIUX 视觉优化建议书.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/DC模块Tool-B开发任务清单.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/DC模块Tool-B开发计划.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_AI_Few-shot示例库.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Bug修复总结_2025-12-08.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Day3开发计划.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Day4-5前端开发计划.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_MVP开发_TODO清单.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_MVP开发计划_V1.0.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_NA处理功能开发总结.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_Pivot列顺序优化总结.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_功能按钮开发计划_V1.0.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_方案B实施总结_2025-12-09.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_缺失值处理_开发完成说明.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_缺失值处理_开发进度_2025-12-10.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_缺失值处理功能_更新说明.md -docs/03-业务模块/DC-数据清洗整理/04-开发计划/工具C_缺失值处理功能开发计划.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-02_工作总结.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-06_工具C_Day1开发完成总结.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-06_工具C_Day2开发完成总结.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-06_工具C_Day3开发完成总结.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_AI对话核心功能增强总结.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Bug修复_DataGrid空数据防御.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Day5_Ant-Design-6.0升级与通用Chat组件开发.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Day5_Ant-Design-X重构完成.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_Day5最终总结.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_UI优化与Bug修复.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_后端API完整对接完成.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_完整UI优化与功能增强.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/2025-12-07_工具C_Day4前端基础完成.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/DC模块重建完成总结-Day1.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/Day2完成总结.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/Day3完成总结.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase1-Portal页面开发完成-2025-12-02.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/Phase2-ToolB-Step1-2开发完成-2025-12-03.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/Portal页面UI优化-2025-12-02.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/Tool-B-MVP完成总结-2025-12-03.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-2025-12-03.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB-UI优化-Round2-2025-12-03.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/ToolB浏览器测试计划-2025-12-03.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/后端API测试报告-2025-12-02.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/待办事项-下一步工作.md -docs/03-业务模块/DC-数据清洗整理/06-开发记录/数据库验证报告-2025-12-02.md -docs/03-业务模块/DC-数据清洗整理/07-技术债务/Tool-B技术债务清单.md -docs/03-业务模块/DC-数据清洗整理/07-技术债务/Tool-C技术债务清单.md -docs/03-业务模块/DC-数据清洗整理/README.md -docs/03-业务模块/IIT Manager Agent/00-模块当前状态与开发指南.md -docs/03-业务模块/IIT Manager Agent/00-系统设计/IIT Manager Agent V4 完整产品需求文档.md -docs/03-业务模块/IIT Manager Agent/00-系统设计/IIT Manager Agent 技术架构白皮书.md -docs/03-业务模块/IIT Manager Agent/00-系统设计/IIT Manager Agent 项目实施战略与 MVP 路线图.md -docs/03-业务模块/IIT Manager Agent/02-技术设计/IIT Manager Agent 多端交互流程与权限设计 (V1.0).md -docs/03-业务模块/IIT Manager Agent/02-技术设计/IIT Manager Agent 多端角色与微信端开发指南.md -docs/03-业务模块/IIT Manager Agent/02-技术设计/IIT Manager Agent 完整技术开发方案 (V1.0).md -docs/03-业务模块/IIT Manager Agent/02-技术设计/IIT Manager Agent 完整技术开发方案 (V1.1).md -docs/03-业务模块/IIT Manager Agent/02-技术设计/IIT Manager Agent 技术路径与架构设计.md -docs/03-业务模块/IIT Manager Agent/02-技术设计/IIT Manager Agent 智能问答与混合检索解决方案 (ReAct 业务闭环版).md -docs/03-业务模块/IIT Manager Agent/02-技术设计/REDCap 原生录入与 AI Workbench 操作边界划分.md -docs/03-业务模块/IIT Manager Agent/02-技术设计/文档 B:EDC 适配器 (REDCap) 技术详细设计 (V1.0).md -docs/03-业务模块/IIT Manager Agent/04-开发计划/Day2-开发完成总结.md -docs/03-业务模块/IIT Manager Agent/04-开发计划/MVP开发任务清单.md -docs/03-业务模块/IIT Manager Agent/04-开发计划/Phase1.5-AI对话能力开发计划.md -docs/03-业务模块/IIT Manager Agent/04-开发计划/REDCap对接技术方案与实施指南.md -docs/03-业务模块/IIT Manager Agent/04-开发计划/企业微信注册指南.md -docs/03-业务模块/IIT Manager Agent/04-开发计划/最小MVP闭环开发计划.md -docs/03-业务模块/IIT Manager Agent/06-开发记录/2026-01-04-Dify知识库集成开发记录.md -docs/03-业务模块/IIT Manager Agent/06-开发记录/Day2-REDCap实时集成开发完成记录.md -docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成与端到端测试完成记录.md -docs/03-业务模块/IIT Manager Agent/06-开发记录/Day3-企业微信集成开发完成记录.md -docs/03-业务模块/IIT Manager Agent/06-开发记录/IIT Manager Agent 技术方案审查与补丁.md -docs/03-业务模块/IIT Manager Agent/06-开发记录/Phase1.5-AI对话集成REDCap完成记录.md -docs/03-业务模块/IIT Manager Agent/06-开发记录/V1.1更新完成报告.md -docs/03-业务模块/IIT Manager Agent/07-技术债务/IIT Manager Agent 技术债务清单.md -docs/03-业务模块/INST-机构管理端/00-模块当前状态与开发指南.md -docs/03-业务模块/INST-机构管理端/README.md -docs/03-业务模块/PKB-个人知识库/00-模块当前状态与开发指南.md -docs/03-业务模块/PKB-个人知识库/01-需求分析/AI 临床医生与医院知识库 - MVP 阶段产品需求文档 (PRD) V5.0.md -docs/03-业务模块/PKB-个人知识库/02-技术设计/01-数据库设计.md -docs/03-业务模块/PKB-个人知识库/05-测试文档/与原型图的差距.md -docs/03-业务模块/PKB-个人知识库/06-开发记录/2026-01-07-前端迁移与批处理功能完善.md -docs/03-业务模块/PKB-个人知识库/06-开发记录/2026-01-07_PKB模块前端V3设计实现.md -docs/03-业务模块/PKB-个人知识库/README.md -docs/03-业务模块/README.md -docs/03-业务模块/RVW-稿件审查系统/00-模块当前状态与开发指南.md -docs/03-业务模块/RVW-稿件审查系统/01-需求分析/智能期刊审稿系统 MVP 产品需求文档.md -docs/03-业务模块/RVW-稿件审查系统/04-开发计划/RVW模块迁移计划.md -docs/03-业务模块/RVW-稿件审查系统/README.md -docs/03-业务模块/Redcap/00-模块概览/00-REDCap模块文档导航.md -docs/03-业务模块/Redcap/00-模块概览/02-REDCap对接总体方案.md -docs/03-业务模块/Redcap/00-模块概览/03-REDCap对接风险评估与技术挑战分析.md -docs/03-业务模块/Redcap/00-模块概览/04-生产环境部署决策报告_ECS_vs_SAE.md -docs/03-业务模块/Redcap/01-部署与配置/10-REDCap_Docker部署操作手册.md -docs/03-业务模块/Redcap/01-部署与配置/13-部署问题排查手册.md -docs/03-业务模块/Redcap/03-API对接与开发/33-REDCap二次开发深度指南.md -docs/03-业务模块/Redcap/04-参考资料/02-REDCap部署指南与环境要求_旧版.md -docs/03-业务模块/Redcap/04-参考资料/03-REDCap本地Docker开发环境部署方案_旧版.md -docs/03-业务模块/Redcap/README.md -docs/03-业务模块/SSA-智能统计分析/README.md -docs/03-业务模块/ST-统计分析工具/README.md -docs/04-开发规范/01-数据库设计规范.md -docs/04-开发规范/02-API设计规范.md -docs/04-开发规范/03-数据库全局视图.md -docs/04-开发规范/04-API路由总览.md -docs/04-开发规范/05-代码规范.md -docs/04-开发规范/06-Git提交规范.md -docs/04-开发规范/08-云原生开发规范.md -docs/04-开发规范/09-数据库开发规范.md -docs/04-开发规范/10-模块认证规范.md -docs/04-开发规范/README.md -docs/05-部署文档/00-部署架构总览.md -docs/05-部署文档/00-阿里云SAE最新真实状态记录.md -docs/05-部署文档/01-快速部署SOP-零基础版.md -docs/05-部署文档/02-SAE部署完全指南(产品经理版).md -docs/05-部署文档/03-Dify-ECS部署完全指南.md -docs/05-部署文档/04-Python微服务-SAE容器部署指南.md -docs/05-部署文档/05-Node.js后端-SAE容器部署指南.md -docs/05-部署文档/06-前端Nginx-SAE容器部署指南.md -docs/05-部署文档/07-关键配置补充说明.md -docs/05-部署文档/07-前端Nginx-SAE部署操作手册.md -docs/05-部署文档/08-PostgreSQL数据库部署操作手册.md -docs/05-部署文档/08-部署检查清单.md -docs/05-部署文档/09-Python微服务-SAE部署操作手册.md -docs/05-部署文档/10-Node.js后端-Docker镜像构建手册.md -docs/05-部署文档/11-Node.js后端-SAE部署配置清单.md -docs/05-部署文档/12-Node.js后端-SAE部署操作手册.md -docs/05-部署文档/13-Node.js后端-镜像修复记录.md -docs/05-部署文档/14-Node.js后端-pino-pretty问题修复.md -docs/05-部署文档/15-Node.js后端-部署成功总结.md -docs/05-部署文档/16-前端Nginx-部署成功总结.md -docs/05-部署文档/17-完整部署实战手册-2025版.md -docs/05-部署文档/18-部署文档使用指南.md -docs/05-部署文档/19-日常更新快速操作手册.md -docs/05-部署文档/CTO 代码审查报告:AI临床研究平台部署架构.md -docs/05-部署文档/PostgreSQL部署策略-摸底报告.md -docs/05-部署文档/README.md -docs/05-部署文档/SSL 证书域名SAE配置指南.md -docs/05-部署文档/文档修正报告-20251214.md -docs/05-部署文档/集成部署补充指南:填补最后的缝隙.md -docs/06-测试文档/README.md -docs/07-运维文档/01-环境配置指南.md -docs/07-运维文档/02-环境变量配置模板.md -docs/07-运维文档/03-SAE环境变量配置指南.md -docs/07-运维文档/04-Redis改造实施计划.md -docs/07-运维文档/05-Redis缓存与队列的区别说明.md -docs/07-运维文档/06-长时间任务可靠性分析.md -docs/07-运维文档/07-Redis使用需求分析(按模块).md -docs/07-运维文档/08-Postgres-Only 全能架构解决方案.md -docs/07-运维文档/09-Postgres-Only改造实施计划(完整版).md -docs/07-运维文档/10-Postgres-Only改造进度追踪表.md -docs/07-运维文档/README.md -docs/08-项目管理/01-整体开发计划/README.md -docs/08-项目管理/02-里程碑规划/README.md -docs/08-项目管理/03-每周计划/2025-11-12-工作总结.md -docs/08-项目管理/03-每周计划/2025-11-13-任务19完成总结.md -docs/08-项目管理/03-每周计划/2025-11-13-工作总结.md -docs/08-项目管理/03-每周计划/2025-11-14-任务19完成总结.md -docs/08-项目管理/03-每周计划/2025-11-16-平台基础设施规划完成总结.md -docs/08-项目管理/03-每周计划/2025-11-17-平台基础设施实施完成报告.md -docs/08-项目管理/03-每周计划/2025-11-17-平台基础设施验证报告.md -docs/08-项目管理/03-每周计划/2025-11-18-AI助手工作交接.md -docs/08-项目管理/03-每周计划/2025-12-13-Postgres-Only架构改造完成.md -docs/08-项目管理/03-每周计划/README.md -docs/08-项目管理/03-每周计划/Week2-总结报告.md -docs/08-项目管理/04-技术决策/2025-11-18-MSE与ARMS采购决策分析.md -docs/08-项目管理/04-技术决策/2025-11-18-PostgreSQL版本选择建议.md -docs/08-项目管理/04-技术决策/2025-11-18-阿里云RDS系列选择建议.md -docs/08-项目管理/05-技术债务/通用对话服务抽取计划.md -docs/08-项目管理/2026-01-11-数据库事故总结.md -docs/08-项目管理/PKB前端问题修复报告.md -docs/08-项目管理/PKB前端验证指南.md -docs/08-项目管理/PKB功能审查报告-阶段0.md -docs/08-项目管理/PKB和RVW功能迁移计划.md -docs/08-项目管理/PKB精细化优化报告.md -docs/08-项目管理/PKB迁移-超级安全执行计划.md -docs/08-项目管理/PKB迁移-阶段1完成报告.md -docs/08-项目管理/PKB迁移-阶段2完成报告.md -docs/08-项目管理/PKB迁移-阶段2进行中.md -docs/08-项目管理/PKB迁移-阶段3完成报告.md -docs/08-项目管理/PKB迁移-阶段4完成报告.md -docs/08-项目管理/README.md -docs/08-项目管理/V2.2版本变化说明.md -docs/08-项目管理/下一阶段行动计划-V2.0-模块化架构优先.md -docs/08-项目管理/下一阶段行动计划-V2.1-务实版.md -docs/08-项目管理/下一阶段行动计划-V2.2-前端架构优先版.md -docs/08-项目管理/下一阶段行动计划-V2.2-完整版.md -docs/09-架构实施/01-Schema隔离架构设计(10个).md -docs/09-架构实施/02-数据库连接配置.md -docs/09-架构实施/03-云原生部署架构指南.md -docs/09-架构实施/04-平台基础设施规划.md -docs/09-架构实施/Frontend-v2创建完成报告.md -docs/09-架构实施/Prisma配置完成报告.md -docs/09-架构实施/README.md -docs/09-架构实施/Schema迁移完成报告.md -docs/09-架构实施/migration-scripts/README.md -docs/09-架构实施/前端模块注册机制实施报告.md -docs/09-架构实施/后端代码分层-迁移计划.md -docs/09-架构实施/后端代码分层实施报告.md -docs/09-架构实施/后端架构增量演进方案.md -docs/09-架构实施/快速功能测试报告.md -docs/09-架构实施/数据库验证通过.md -docs/09-架构实施/模块配置更新报告.md -docs/09-架构实施/编码规范-UTF8最佳实践.md -docs/README.md -docs/_templates/API设计-模板.md -docs/_templates/README.md -docs/_templates/数据库设计-模板.md -docs/_templates/模块README-模板.md -docs/文档整理清单.md diff --git a/frontend-v2/src/framework/modules/moduleRegistry.ts b/frontend-v2/src/framework/modules/moduleRegistry.ts index 197c4387..6f8cd96d 100644 --- a/frontend-v2/src/framework/modules/moduleRegistry.ts +++ b/frontend-v2/src/framework/modules/moduleRegistry.ts @@ -169,5 +169,4 @@ export const getAvailableModulesByCode = (userModuleCodes: string[]): ModuleDefi // 检查用户是否有该模块的访问权限 return userModuleCodes.includes(module.moduleCode); }); -} - +} \ No newline at end of file diff --git a/frontend-v2/src/modules/pkb/api/knowledgeBaseApi.ts b/frontend-v2/src/modules/pkb/api/knowledgeBaseApi.ts index c16928d6..f19bce0f 100644 --- a/frontend-v2/src/modules/pkb/api/knowledgeBaseApi.ts +++ b/frontend-v2/src/modules/pkb/api/knowledgeBaseApi.ts @@ -1,15 +1,20 @@ /** * PKB个人知识库 API * API路径: /api/v1/pkb/knowledge + * + * 修复说明(2026-01-27): + * - 移除硬编码的 localhost:3000,改用相对路径 + * - 使用相对路径后,Nginx 会自动代理到后端服务 */ import axios from 'axios'; import { getAccessToken } from '../../../framework/auth/api'; -const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3000'; +// 使用相对路径,生产环境由 Nginx 代理到后端 +const API_BASE_URL = '/api/v1/pkb/knowledge'; const api = axios.create({ - baseURL: `${API_BASE_URL}/api/v1/pkb/knowledge`, + baseURL: API_BASE_URL, timeout: 30000, headers: { 'Content-Type': 'application/json', diff --git a/frontend-v2/src/modules/rvw/components/AgentModal.tsx b/frontend-v2/src/modules/rvw/components/AgentModal.tsx index 2bfc8157..a85d19c1 100644 --- a/frontend-v2/src/modules/rvw/components/AgentModal.tsx +++ b/frontend-v2/src/modules/rvw/components/AgentModal.tsx @@ -10,9 +10,10 @@ interface AgentModalProps { taskCount: number; onClose: () => void; onConfirm: (agents: AgentType[]) => void; + isSubmitting?: boolean; // 🔒 防止重复提交 } -export default function AgentModal({ visible, taskCount, onClose, onConfirm }: AgentModalProps) { +export default function AgentModal({ visible, taskCount, onClose, onConfirm, isSubmitting = false }: AgentModalProps) { const [selectedAgents, setSelectedAgents] = useState(['editorial']); const toggleAgent = (agent: AgentType) => { @@ -110,10 +111,10 @@ export default function AgentModal({ visible, taskCount, onClose, onConfirm }: A @@ -140,6 +141,9 @@ export default function AgentModal({ visible, taskCount, onClose, onConfirm }: A + + + diff --git a/frontend-v2/src/modules/rvw/pages/Dashboard.tsx b/frontend-v2/src/modules/rvw/pages/Dashboard.tsx index f237568c..cd2fd0ab 100644 --- a/frontend-v2/src/modules/rvw/pages/Dashboard.tsx +++ b/frontend-v2/src/modules/rvw/pages/Dashboard.tsx @@ -26,6 +26,7 @@ export default function Dashboard() { const [filters, setFilters] = useState({ status: 'all', timeRange: 'all' }); const [agentModalVisible, setAgentModalVisible] = useState(false); const [pendingTaskForRun, setPendingTaskForRun] = useState(null); + const [isSubmitting, setIsSubmitting] = useState(false); // 🔒 防止重复提交 // 报告详情 const [reportDetail, setReportDetail] = useState(null); @@ -119,12 +120,19 @@ export default function Dashboard() { }; const handleConfirmRun = async (agents: AgentType[]) => { + // 🔒 防止重复提交 + if (isSubmitting) { + console.warn('[Dashboard] 已在提交中,忽略重复请求'); + return; + } + // 🔥 保存到局部变量,避免onClose后丢失 const taskToRun = pendingTaskForRun; - // 立即关闭弹窗 + // 立即关闭弹窗并设置提交状态 setAgentModalVisible(false); setPendingTaskForRun(null); + setIsSubmitting(true); // 🔒 锁定 try { if (taskToRun) { @@ -159,6 +167,8 @@ export default function Dashboard() { loadTasks(); } catch (error: any) { message.error({ content: error.message || '启动失败', key: 'run', duration: 3 }); + } finally { + setIsSubmitting(false); // 🔓 解锁 } }; @@ -277,6 +287,7 @@ export default function Dashboard() { setPendingTaskForRun(null); }} onConfirm={handleConfirmRun} + isSubmitting={isSubmitting} /> ); @@ -301,6 +312,9 @@ export default function Dashboard() { + + + diff --git a/frontend-v2/tsconfig.tsbuildinfo b/frontend-v2/tsconfig.tsbuildinfo index 2a447980..37cf7711 100644 --- a/frontend-v2/tsconfig.tsbuildinfo +++ b/frontend-v2/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/common/api/axios.ts","./src/framework/auth/authcontext.tsx","./src/framework/auth/api.ts","./src/framework/auth/index.ts","./src/framework/auth/moduleapi.ts","./src/framework/auth/types.ts","./src/framework/layout/adminlayout.tsx","./src/framework/layout/mainlayout.tsx","./src/framework/layout/orglayout.tsx","./src/framework/layout/topnavigation.tsx","./src/framework/modules/errorboundary.tsx","./src/framework/modules/moduleerrorfallback.tsx","./src/framework/modules/moduleregistry.ts","./src/framework/modules/types.ts","./src/framework/permission/permissioncontext.tsx","./src/framework/permission/index.ts","./src/framework/permission/types.ts","./src/framework/permission/usepermission.ts","./src/framework/router/permissiondenied.tsx","./src/framework/router/routeguard.tsx","./src/framework/router/index.ts","./src/modules/admin/index.tsx","./src/modules/admin/api/userapi.ts","./src/modules/admin/components/assigntenantmodal.tsx","./src/modules/admin/components/importusermodal.tsx","./src/modules/admin/components/modulepermissionmodal.tsx","./src/modules/admin/pages/userdetailpage.tsx","./src/modules/admin/pages/userformpage.tsx","./src/modules/admin/pages/userlistpage.tsx","./src/modules/admin/types/user.ts","./src/modules/aia/constants.ts","./src/modules/aia/index.tsx","./src/modules/aia/types.ts","./src/modules/aia/components/agentcard.tsx","./src/modules/aia/components/agenthub.tsx","./src/modules/aia/components/chatworkspace.tsx","./src/modules/aia/components/index.ts","./src/modules/asl/index.tsx","./src/modules/asl/api/index.ts","./src/modules/asl/components/asllayout.tsx","./src/modules/asl/components/conclusiontag.tsx","./src/modules/asl/components/detailreviewdrawer.tsx","./src/modules/asl/components/fulltextdetaildrawer.tsx","./src/modules/asl/components/judgmentbadge.tsx","./src/modules/asl/hooks/usefulltextresults.ts","./src/modules/asl/hooks/usefulltexttask.ts","./src/modules/asl/hooks/usescreeningresults.ts","./src/modules/asl/hooks/usescreeningtask.ts","./src/modules/asl/pages/fulltextprogress.tsx","./src/modules/asl/pages/fulltextresults.tsx","./src/modules/asl/pages/fulltextsettings.tsx","./src/modules/asl/pages/fulltextworkbench.tsx","./src/modules/asl/pages/screeningresults.tsx","./src/modules/asl/pages/screeningworkbench.tsx","./src/modules/asl/pages/titlescreeningsettings.tsx","./src/modules/asl/types/index.ts","./src/modules/asl/utils/excelexport.ts","./src/modules/asl/utils/excelutils.ts","./src/modules/asl/utils/tabletransform.ts","./src/modules/dc/index.tsx","./src/modules/dc/api/toolb.ts","./src/modules/dc/api/toolc.ts","./src/modules/dc/components/assetlibrary.tsx","./src/modules/dc/components/tasklist.tsx","./src/modules/dc/components/toolcard.tsx","./src/modules/dc/hooks/useassets.ts","./src/modules/dc/hooks/userecenttasks.ts","./src/modules/dc/pages/portal.tsx","./src/modules/dc/pages/tool-b/step1upload.tsx","./src/modules/dc/pages/tool-b/step2schema.tsx","./src/modules/dc/pages/tool-b/step3processing.tsx","./src/modules/dc/pages/tool-b/step4verify.tsx","./src/modules/dc/pages/tool-b/step5result.tsx","./src/modules/dc/pages/tool-b/index.tsx","./src/modules/dc/pages/tool-b/components/stepindicator.tsx","./src/modules/dc/pages/tool-c/index.tsx","./src/modules/dc/pages/tool-c/components/binningdialog.tsx","./src/modules/dc/pages/tool-c/components/binningdialog_improved.tsx","./src/modules/dc/pages/tool-c/components/computedialog.tsx","./src/modules/dc/pages/tool-c/components/conditionaldialog.tsx","./src/modules/dc/pages/tool-c/components/datagrid.tsx","./src/modules/dc/pages/tool-c/components/dropnadialog.tsx","./src/modules/dc/pages/tool-c/components/filterdialog.tsx","./src/modules/dc/pages/tool-c/components/header.tsx","./src/modules/dc/pages/tool-c/components/metrictimepanel.tsx","./src/modules/dc/pages/tool-c/components/missingvaluedialog.tsx","./src/modules/dc/pages/tool-c/components/multimetricpanel.tsx","./src/modules/dc/pages/tool-c/components/pivotdialog.tsx","./src/modules/dc/pages/tool-c/components/pivotpanel.tsx","./src/modules/dc/pages/tool-c/components/recodedialog.tsx","./src/modules/dc/pages/tool-c/components/sidebar.tsx","./src/modules/dc/pages/tool-c/components/streamingsteps.tsx","./src/modules/dc/pages/tool-c/components/toolbar.tsx","./src/modules/dc/pages/tool-c/components/transformdialog.tsx","./src/modules/dc/pages/tool-c/components/unpivotpanel.tsx","./src/modules/dc/pages/tool-c/hooks/usesessionstatus.ts","./src/modules/dc/pages/tool-c/types/index.ts","./src/modules/dc/types/portal.ts","./src/modules/pkb/index.tsx","./src/modules/pkb/api/knowledgebaseapi.ts","./src/modules/pkb/components/createkbdialog.tsx","./src/modules/pkb/components/documentlist.tsx","./src/modules/pkb/components/documentupload.tsx","./src/modules/pkb/components/editkbdialog.tsx","./src/modules/pkb/components/knowledgebaselist.tsx","./src/modules/pkb/components/workspace/batchmode.tsx","./src/modules/pkb/components/workspace/batchmodecomplete.tsx","./src/modules/pkb/components/workspace/deepreadmode.tsx","./src/modules/pkb/components/workspace/fulltextmode.tsx","./src/modules/pkb/components/workspace/workmodeselector.tsx","./src/modules/pkb/hooks/useworkmode.ts","./src/modules/pkb/pages/dashboardpage.tsx","./src/modules/pkb/pages/knowledgepage.tsx","./src/modules/pkb/pages/workspacepage.tsx","./src/modules/pkb/stores/useknowledgebasestore.ts","./src/modules/pkb/types/workspace.ts","./src/modules/rvw/index.tsx","./src/modules/rvw/api/index.ts","./src/modules/rvw/components/agentmodal.tsx","./src/modules/rvw/components/batchtoolbar.tsx","./src/modules/rvw/components/editorialreport.tsx","./src/modules/rvw/components/filterchips.tsx","./src/modules/rvw/components/header.tsx","./src/modules/rvw/components/methodologyreport.tsx","./src/modules/rvw/components/reportdetail.tsx","./src/modules/rvw/components/scorering.tsx","./src/modules/rvw/components/sidebar.tsx","./src/modules/rvw/components/taskdetail.tsx","./src/modules/rvw/components/tasktable.tsx","./src/modules/rvw/components/index.ts","./src/modules/rvw/pages/dashboard.tsx","./src/modules/rvw/types/index.ts","./src/modules/ssa/index.tsx","./src/modules/st/index.tsx","./src/pages/homepage.tsx","./src/pages/loginpage.tsx","./src/pages/admin/admindashboard.tsx","./src/pages/admin/prompteditorpage.tsx","./src/pages/admin/promptlistpage.tsx","./src/pages/admin/api/promptapi.ts","./src/pages/admin/components/prompteditor.tsx","./src/pages/admin/tenants/tenantdetailpage.tsx","./src/pages/admin/tenants/tenantlistpage.tsx","./src/pages/admin/tenants/api/tenantapi.ts","./src/pages/org/orgdashboard.tsx","./src/shared/components/placeholder.tsx","./src/shared/components/index.ts","./src/shared/components/chat/aistreamchat.tsx","./src/shared/components/chat/chatcontainer.tsx","./src/shared/components/chat/codeblockrenderer.tsx","./src/shared/components/chat/conversationlist.tsx","./src/shared/components/chat/messagerenderer.tsx","./src/shared/components/chat/thinkingblock.tsx","./src/shared/components/chat/index.ts","./src/shared/components/chat/types.ts","./src/shared/components/chat/hooks/index.ts","./src/shared/components/chat/hooks/useaistream.ts","./src/shared/components/chat/hooks/useconversations.ts"],"errors":true,"version":"5.9.3"} \ No newline at end of file +{"root":["./src/app.tsx","./src/main.tsx","./src/vite-env.d.ts","./src/common/api/axios.ts","./src/framework/auth/authcontext.tsx","./src/framework/auth/api.ts","./src/framework/auth/index.ts","./src/framework/auth/moduleapi.ts","./src/framework/auth/types.ts","./src/framework/layout/adminlayout.tsx","./src/framework/layout/mainlayout.tsx","./src/framework/layout/orglayout.tsx","./src/framework/layout/topnavigation.tsx","./src/framework/modules/errorboundary.tsx","./src/framework/modules/moduleerrorfallback.tsx","./src/framework/modules/moduleregistry.ts","./src/framework/modules/types.ts","./src/framework/permission/permissioncontext.tsx","./src/framework/permission/index.ts","./src/framework/permission/types.ts","./src/framework/permission/usepermission.ts","./src/framework/router/permissiondenied.tsx","./src/framework/router/routeguard.tsx","./src/framework/router/index.ts","./src/modules/admin/index.tsx","./src/modules/admin/api/statsapi.ts","./src/modules/admin/api/userapi.ts","./src/modules/admin/components/assigntenantmodal.tsx","./src/modules/admin/components/importusermodal.tsx","./src/modules/admin/components/modulepermissionmodal.tsx","./src/modules/admin/pages/statsdashboardpage.tsx","./src/modules/admin/pages/userdetailpage.tsx","./src/modules/admin/pages/userformpage.tsx","./src/modules/admin/pages/userlistpage.tsx","./src/modules/admin/types/user.ts","./src/modules/aia/constants.ts","./src/modules/aia/index.tsx","./src/modules/aia/types.ts","./src/modules/aia/components/agentcard.tsx","./src/modules/aia/components/agenthub.tsx","./src/modules/aia/components/chatworkspace.tsx","./src/modules/aia/components/index.ts","./src/modules/aia/protocol-agent/protocolagentpage.tsx","./src/modules/aia/protocol-agent/index.ts","./src/modules/aia/protocol-agent/types.ts","./src/modules/aia/protocol-agent/components/actioncard.tsx","./src/modules/aia/protocol-agent/components/chatarea.tsx","./src/modules/aia/protocol-agent/components/documentpanel.tsx","./src/modules/aia/protocol-agent/components/markdowncontent.tsx","./src/modules/aia/protocol-agent/components/reflexionmessage.tsx","./src/modules/aia/protocol-agent/components/resizablesplitpane.tsx","./src/modules/aia/protocol-agent/components/stagecard.tsx","./src/modules/aia/protocol-agent/components/stageeditmodal.tsx","./src/modules/aia/protocol-agent/components/statepanel.tsx","./src/modules/aia/protocol-agent/components/syncbutton.tsx","./src/modules/aia/protocol-agent/components/viewswitcher.tsx","./src/modules/aia/protocol-agent/components/index.ts","./src/modules/aia/protocol-agent/hooks/index.ts","./src/modules/aia/protocol-agent/hooks/useprotocolcontext.ts","./src/modules/aia/protocol-agent/hooks/useprotocolconversations.ts","./src/modules/aia/protocol-agent/hooks/useprotocolgeneration.ts","./src/modules/asl/index.tsx","./src/modules/asl/api/index.ts","./src/modules/asl/components/asllayout.tsx","./src/modules/asl/components/conclusiontag.tsx","./src/modules/asl/components/detailreviewdrawer.tsx","./src/modules/asl/components/fulltextdetaildrawer.tsx","./src/modules/asl/components/judgmentbadge.tsx","./src/modules/asl/hooks/usefulltextresults.ts","./src/modules/asl/hooks/usefulltexttask.ts","./src/modules/asl/hooks/usescreeningresults.ts","./src/modules/asl/hooks/usescreeningtask.ts","./src/modules/asl/pages/fulltextprogress.tsx","./src/modules/asl/pages/fulltextresults.tsx","./src/modules/asl/pages/fulltextsettings.tsx","./src/modules/asl/pages/fulltextworkbench.tsx","./src/modules/asl/pages/researchsearch.tsx","./src/modules/asl/pages/screeningresults.tsx","./src/modules/asl/pages/screeningworkbench.tsx","./src/modules/asl/pages/titlescreeningsettings.tsx","./src/modules/asl/types/index.ts","./src/modules/asl/utils/excelexport.ts","./src/modules/asl/utils/excelutils.ts","./src/modules/asl/utils/tabletransform.ts","./src/modules/dc/index.tsx","./src/modules/dc/api/toolb.ts","./src/modules/dc/api/toolc.ts","./src/modules/dc/components/assetlibrary.tsx","./src/modules/dc/components/tasklist.tsx","./src/modules/dc/components/toolcard.tsx","./src/modules/dc/hooks/useassets.ts","./src/modules/dc/hooks/userecenttasks.ts","./src/modules/dc/pages/portal.tsx","./src/modules/dc/pages/tool-b/step1upload.tsx","./src/modules/dc/pages/tool-b/step2schema.tsx","./src/modules/dc/pages/tool-b/step3processing.tsx","./src/modules/dc/pages/tool-b/step4verify.tsx","./src/modules/dc/pages/tool-b/step5result.tsx","./src/modules/dc/pages/tool-b/index.tsx","./src/modules/dc/pages/tool-b/components/stepindicator.tsx","./src/modules/dc/pages/tool-c/index.tsx","./src/modules/dc/pages/tool-c/components/binningdialog.tsx","./src/modules/dc/pages/tool-c/components/binningdialog_improved.tsx","./src/modules/dc/pages/tool-c/components/computedialog.tsx","./src/modules/dc/pages/tool-c/components/conditionaldialog.tsx","./src/modules/dc/pages/tool-c/components/datagrid.tsx","./src/modules/dc/pages/tool-c/components/dropnadialog.tsx","./src/modules/dc/pages/tool-c/components/filterdialog.tsx","./src/modules/dc/pages/tool-c/components/header.tsx","./src/modules/dc/pages/tool-c/components/metrictimepanel.tsx","./src/modules/dc/pages/tool-c/components/missingvaluedialog.tsx","./src/modules/dc/pages/tool-c/components/multimetricpanel.tsx","./src/modules/dc/pages/tool-c/components/pivotdialog.tsx","./src/modules/dc/pages/tool-c/components/pivotpanel.tsx","./src/modules/dc/pages/tool-c/components/recodedialog.tsx","./src/modules/dc/pages/tool-c/components/sidebar.tsx","./src/modules/dc/pages/tool-c/components/streamingsteps.tsx","./src/modules/dc/pages/tool-c/components/toolbar.tsx","./src/modules/dc/pages/tool-c/components/transformdialog.tsx","./src/modules/dc/pages/tool-c/components/unpivotpanel.tsx","./src/modules/dc/pages/tool-c/hooks/usesessionstatus.ts","./src/modules/dc/pages/tool-c/types/index.ts","./src/modules/dc/types/portal.ts","./src/modules/pkb/index.tsx","./src/modules/pkb/api/knowledgebaseapi.ts","./src/modules/pkb/components/createkbdialog.tsx","./src/modules/pkb/components/documentlist.tsx","./src/modules/pkb/components/documentupload.tsx","./src/modules/pkb/components/editkbdialog.tsx","./src/modules/pkb/components/knowledgebaselist.tsx","./src/modules/pkb/components/workspace/batchmode.tsx","./src/modules/pkb/components/workspace/batchmodecomplete.tsx","./src/modules/pkb/components/workspace/deepreadmode.tsx","./src/modules/pkb/components/workspace/fulltextmode.tsx","./src/modules/pkb/components/workspace/workmodeselector.tsx","./src/modules/pkb/hooks/useworkmode.ts","./src/modules/pkb/pages/dashboardpage.tsx","./src/modules/pkb/pages/knowledgepage.tsx","./src/modules/pkb/pages/workspacepage.tsx","./src/modules/pkb/stores/useknowledgebasestore.ts","./src/modules/pkb/types/workspace.ts","./src/modules/rvw/index.tsx","./src/modules/rvw/api/index.ts","./src/modules/rvw/components/agentmodal.tsx","./src/modules/rvw/components/batchtoolbar.tsx","./src/modules/rvw/components/editorialreport.tsx","./src/modules/rvw/components/filterchips.tsx","./src/modules/rvw/components/header.tsx","./src/modules/rvw/components/methodologyreport.tsx","./src/modules/rvw/components/reportdetail.tsx","./src/modules/rvw/components/scorering.tsx","./src/modules/rvw/components/sidebar.tsx","./src/modules/rvw/components/taskdetail.tsx","./src/modules/rvw/components/tasktable.tsx","./src/modules/rvw/components/index.ts","./src/modules/rvw/pages/dashboard.tsx","./src/modules/rvw/types/index.ts","./src/modules/ssa/index.tsx","./src/modules/st/index.tsx","./src/pages/homepage.tsx","./src/pages/loginpage.tsx","./src/pages/admin/admindashboard.tsx","./src/pages/admin/prompteditorpage.tsx","./src/pages/admin/promptlistpage.tsx","./src/pages/admin/api/promptapi.ts","./src/pages/admin/components/prompteditor.tsx","./src/pages/admin/tenants/tenantdetailpage.tsx","./src/pages/admin/tenants/tenantlistpage.tsx","./src/pages/admin/tenants/api/tenantapi.ts","./src/pages/org/orgdashboard.tsx","./src/shared/components/placeholder.tsx","./src/shared/components/index.ts","./src/shared/components/chat/aistreamchat.tsx","./src/shared/components/chat/chatcontainer.tsx","./src/shared/components/chat/codeblockrenderer.tsx","./src/shared/components/chat/conversationlist.tsx","./src/shared/components/chat/messagerenderer.tsx","./src/shared/components/chat/thinkingblock.tsx","./src/shared/components/chat/index.ts","./src/shared/components/chat/types.ts","./src/shared/components/chat/hooks/index.ts","./src/shared/components/chat/hooks/useaistream.ts","./src/shared/components/chat/hooks/useconversations.ts"],"errors":true,"version":"5.9.3"} \ No newline at end of file diff --git a/frontend/.env.example b/frontend/.env.example deleted file mode 100644 index e23b06ce..00000000 --- a/frontend/.env.example +++ /dev/null @@ -1,6 +0,0 @@ -# API -VITE_API_URL=http://localhost:3001 -VITE_API_TIMEOUT=30000 - -# Environment -VITE_NODE_ENV=development diff --git a/frontend/.gitignore b/frontend/.gitignore deleted file mode 100644 index 683eae4d..00000000 --- a/frontend/.gitignore +++ /dev/null @@ -1,32 +0,0 @@ -# Logs -logs -*.log -npm-debug.log* -yarn-debug.log* -yarn-error.log* -pnpm-debug.log* -lerna-debug.log* - -node_modules -dist -dist-ssr -*.local - -# Editor directories and files -.vscode/* -!.vscode/extensions.json -.idea -.DS_Store -*.suo -*.ntvs* -*.njsproj -*.sln -*.sw? - -# Environment variables -.env.local -.env.production - - - - diff --git a/frontend/README.md b/frontend/README.md deleted file mode 100644 index 49161a38..00000000 --- a/frontend/README.md +++ /dev/null @@ -1,240 +0,0 @@ -# AI临床研究平台 - 前端 - -基于 Vite + React + TypeScript + Ant Design 构建 - -## 🏠 导航 - -- [← 返回项目主页](../README.md) -- [📚 查看完整文档](../docs/README.md) -- [⚙️ 后端项目](../backend/README.md) -- [🚀 快速启动指南](../启动指南.md) - ---- - -## 🚀 快速开始 - -### 1. 安装依赖 -```bash -npm install -``` - -### 2. 启动开发服务器 -```bash -npm run dev -``` - -访问:http://localhost:3000 - -### 3. 构建生产版本 -```bash -npm run build -``` - -## 📦 技术栈 - -- **框架**: React 18 + TypeScript -- **构建工具**: Vite 6 -- **UI组件库**: Ant Design 5 -- **CSS框架**: Tailwind CSS 3 -- **路由**: React Router 6 -- **HTTP客户端**: Axios -- **状态管理**: React Hooks(Day 7+可能引入Zustand) - -## 📁 项目结构 - -``` -frontend/ -├── src/ -│ ├── api/ # API请求 -│ │ ├── request.ts # Axios配置 -│ │ └── index.ts # API接口 -│ ├── assets/ # 静态资源 -│ ├── components/ # 公共组件 -│ ├── layouts/ # 布局组件 -│ │ └── MainLayout.tsx -│ ├── pages/ # 页面组件 -│ │ ├── HomePage.tsx # 首页 -│ │ └── AgentPage.tsx # 智能体页面 -│ ├── types/ # TypeScript类型定义 -│ ├── utils/ # 工具函数 -│ ├── App.tsx # 根组件 -│ ├── main.tsx # 入口文件 -│ └── index.css # 全局样式 -├── index.html -├── package.json -├── tsconfig.json -├── vite.config.ts -└── tailwind.config.js -``` - -## 🎨 功能特性 - -### Day 6 已完成 -- ✅ 项目初始化(Vite + React + TypeScript) -- ✅ Ant Design UI组件库集成 -- ✅ Tailwind CSS配置 -- ✅ React Router路由配置 -- ✅ 主布局组件(侧边栏 + 头部 + 内容区) -- ✅ 首页展示(12个智能体卡片) -- ✅ 智能体页面(占位符) -- ✅ Axios请求配置 -- ✅ TypeScript类型定义 - -### Day 7-8 待开发 -- ⏳ 项目管理功能 -- ⏳ 知识库管理功能 -- ⏳ 个人中心页面 -- ⏳ 设置页面 - -### Day 9-10 待开发 -- ⏳ 智能对话组件 -- ⏳ 与后端API集成 -- ⏳ 模型切换功能 -- ⏳ 知识库@引用功能 - -## 🔧 开发规范 - -### 代码规范 -- 使用TypeScript类型检查 -- 使用ESLint代码检查 -- 组件使用函数式组件 + Hooks -- 样式优先使用Tailwind CSS -- 复杂样式使用Ant Design内联style - -### 命名规范 -- 组件文件:PascalCase(如:HomePage.tsx) -- 普通文件:camelCase(如:request.ts) -- 常量:UPPER_SNAKE_CASE(如:API_BASE_URL) - -### Git提交规范 -- feat: 新功能 -- fix: 修复bug -- docs: 文档更新 -- style: 代码格式调整 -- refactor: 代码重构 - -## 📡 API配置 - -后端API地址:http://localhost:3001/api/v1 - -代理配置已在`vite.config.ts`中设置: -```typescript -proxy: { - '/api': { - target: 'http://localhost:3001', - changeOrigin: true, - }, -} -``` - -## 🎯 智能体列表 - -本平台提供12个AI智能体,覆盖临床研究全流程: - -### 🌱 选题阶段 - -#### 1. 🎯 选题评价智能体 -- **路由ID**: `topic-evaluation` -- **功能**: 从创新性、临床价值、科学性和可行性等维度评估研究选题 -- **输入**: 用户提供的临床问题描述 -- **输出**: 结构化评价报告,包含SWOT分析及综合建议 - -#### 2. 🔬 科学问题梳理智能体 -- **路由ID**: `scientific-question` -- **功能**: 将模糊的研究想法提炼成清晰、具体、可验证的科学问题 -- **输入**: 初步研究想法(如"研究肠道菌群与抑郁症的关系") -- **输出**: 多个精炼后的科学问题选项 - ---- - -### 🔬 研究设计阶段 - -#### 3. 📋 PICOS构建智能体 -- **路由ID**: `picos-construction` -- **功能**: 按照PICOS原则结构化定义临床研究的核心要素 -- **输入**: 引导式表单或自由文本描述的研究问题 -- **输出**: 标准化的PICOS表格(P-患者/人群、I-干预、C-对照、O-结局、S-研究设计) - -#### 4. 📊 观察指标设计智能体 -- **路由ID**: `observation-design` -- **功能**: 基于研究设计推荐合适的观察指标集 -- **输入**: 已确定的PICOS框架或研究问题 -- **输出**: 包含主要、次要及安全性指标的详细清单 - -#### 5. 📝 CRF制定智能体 -- **路由ID**: `crf-development` -- **功能**: 自动生成结构化、符合规范的病例报告表(CRF)框架 -- **输入**: 研究方案、观察指标清单 -- **输出**: 可下载的CRF框架草稿(Word/Excel格式) - -#### 6. 🔢 样本量计算智能体 -- **路由ID**: `sample-size` -- **功能**: 根据研究类型、检验水准、把握度、效应量等参数估算样本量 -- **输入**: 引导式表单(研究设计、α、β、效应量等) -- **输出**: 详细的样本量计算结果,包含公式、参数说明和建议样本量 - -#### 7. 📄 临床研究方案撰写智能体 -- **路由ID**: `protocol-writing` -- **功能**: 基于科学问题、PICOS等信息,自动生成完整的临床研究设计方案 -- **输入**: 科学问题、PICOS、样本量等 -- **输出**: 包含研究背景、目的、设计、流程等章节的Word文档初稿 - ---- - -### 📝 论文阶段 - -#### 8. ✨ 论文润色智能体 -- **路由ID**: `paper-polishing` -- **功能**: 提供专业级的语言润色,修正语法、拼写和表达方式 -- **输入**: 上传的论文稿件(Word/Text),目标期刊名称(可选) -- **输出**: 带修订标记的润色后文稿及修改摘要说明 - -#### 9. 🌐 论文翻译智能体 -- **路由ID**: `paper-translation` -- **功能**: 提供专业、精准的中英互译服务,优化医学科研术语 -- **输入**: 上传的论文稿件,选择源语言和目标语言 -- **输出**: 翻译完成的稿件,保留原格式 - -#### 10. 🔍 方法学评审智能体 -- **路由ID**: `methodology-review` -- **功能**: 从研究问题、方案设计、实施可行性和临床意义等维度进行方法学评审 -- **输入**: 上传的研究方案或论文稿件 -- **输出**: 评审报告,分点列出优点、潜在缺陷及改进建议 - -#### 11. 📑 期刊方法学评审智能体 -- **路由ID**: `journal-methodology-review` -- **功能**: 模拟期刊审稿人视角,针对统计学、方法学问题进行挑战 -- **输入**: 上传的论文稿件,目标期刊 -- **输出**: 模拟的"审稿意见",列出可能被挑战的问题点 - -#### 12. ✅ 期刊稿约评审智能体 -- **路由ID**: `journal-guidelines-review` -- **功能**: 依据目标期刊的投稿要求,自动检查格式、字数、参考文献规范 -- **输入**: 上传的论文稿件,目标期刊的投稿指南 -- **输出**: 格式审查报告,清晰列出所有不符合项和修改建议 - ---- - -### 🔗 路由规则 - -所有智能体页面遵循统一路由规则: -``` -/agent/{路由ID} -``` - -**示例**: -- `/agent/topic-evaluation` - 选题评价智能体 -- `/agent/picos-construction` - PICOS构建智能体 -- `/agent/paper-polishing` - 论文润色智能体 - -## 📞 技术支持 - -详见项目根目录的完整文档: -- `docs/01-设计文档/API设计规范.md` -- `docs/02-开发规范/代码规范.md` -- `docs/04-开发计划/开发里程碑.md` - ---- - -**Day 6 完成!前端基础架构搭建完毕!** 🎉 - diff --git a/frontend/index.html b/frontend/index.html deleted file mode 100644 index dbd68063..00000000 --- a/frontend/index.html +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - AI临床研究平台 - - -
- - - - - - - diff --git a/frontend/package-lock.json b/frontend/package-lock.json deleted file mode 100644 index 2aa5e726..00000000 --- a/frontend/package-lock.json +++ /dev/null @@ -1,8090 +0,0 @@ -{ - "name": "ai-clinical-frontend", - "version": "1.0.0", - "lockfileVersion": 3, - "requires": true, - "packages": { - "": { - "name": "ai-clinical-frontend", - "version": "1.0.0", - "dependencies": { - "@ant-design/icons": "^5.5.2", - "@types/js-yaml": "^4.0.9", - "@types/react-syntax-highlighter": "^15.5.13", - "antd": "^5.22.5", - "axios": "^1.12.2", - "html2canvas": "^1.4.1", - "js-yaml": "^4.1.0", - "jspdf": "^3.0.3", - "lucide-react": "^0.562.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-markdown": "^10.1.0", - "react-router-dom": "^6.28.0", - "react-syntax-highlighter": "^15.6.6", - "rehype-raw": "^7.0.0", - "remark-gfm": "^4.0.1", - "xlsx": "^0.18.5" - }, - "devDependencies": { - "@types/react": "^18.3.18", - "@types/react-dom": "^18.3.5", - "@typescript-eslint/eslint-plugin": "^8.20.0", - "@typescript-eslint/parser": "^8.20.0", - "@vitejs/plugin-react": "^4.3.4", - "autoprefixer": "^10.4.20", - "eslint": "^9.18.0", - "eslint-plugin-react-hooks": "^5.1.0", - "eslint-plugin-react-refresh": "^0.4.16", - "postcss": "^8.4.49", - "tailwindcss": "^3.4.17", - "typescript": "^5.7.3", - "vite": "^6.0.7" - } - }, - "node_modules/@alloc/quick-lru": { - "version": "5.2.0", - "resolved": "https://registry.npmmirror.com/@alloc/quick-lru/-/quick-lru-5.2.0.tgz", - "integrity": "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/@ant-design/colors": { - "version": "7.2.1", - "resolved": "https://registry.npmmirror.com/@ant-design/colors/-/colors-7.2.1.tgz", - "integrity": "sha512-lCHDcEzieu4GA3n8ELeZ5VQ8pKQAWcGGLRTQ50aQM2iqPpq2evTxER84jfdPvsPAtEcZ7m44NI45edFMo8oOYQ==", - "license": "MIT", - "dependencies": { - "@ant-design/fast-color": "^2.0.6" - } - }, - "node_modules/@ant-design/cssinjs": { - "version": "1.24.0", - "resolved": "https://registry.npmmirror.com/@ant-design/cssinjs/-/cssinjs-1.24.0.tgz", - "integrity": "sha512-K4cYrJBsgvL+IoozUXYjbT6LHHNt+19a9zkvpBPxLjFHas1UpPM2A5MlhROb0BT8N8WoavM5VsP9MeSeNK/3mg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.11.1", - "@emotion/hash": "^0.8.0", - "@emotion/unitless": "^0.7.5", - "classnames": "^2.3.1", - "csstype": "^3.1.3", - "rc-util": "^5.35.0", - "stylis": "^4.3.4" - }, - "peerDependencies": { - "react": ">=16.0.0", - "react-dom": ">=16.0.0" - } - }, - "node_modules/@ant-design/cssinjs-utils": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/@ant-design/cssinjs-utils/-/cssinjs-utils-1.1.3.tgz", - "integrity": "sha512-nOoQMLW1l+xR1Co8NFVYiP8pZp3VjIIzqV6D6ShYF2ljtdwWJn5WSsH+7kvCktXL/yhEtWURKOfH5Xz/gzlwsg==", - "license": "MIT", - "dependencies": { - "@ant-design/cssinjs": "^1.21.0", - "@babel/runtime": "^7.23.2", - "rc-util": "^5.38.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/@ant-design/fast-color": { - "version": "2.0.6", - "resolved": "https://registry.npmmirror.com/@ant-design/fast-color/-/fast-color-2.0.6.tgz", - "integrity": "sha512-y2217gk4NqL35giHl72o6Zzqji9O7vHh9YmhUVkPtAOpoTCH4uWxo/pr4VE8t0+ChEPs0qo4eJRC5Q1eXWo3vA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.24.7" - }, - "engines": { - "node": ">=8.x" - } - }, - "node_modules/@ant-design/icons": { - "version": "5.6.1", - "resolved": "https://registry.npmmirror.com/@ant-design/icons/-/icons-5.6.1.tgz", - "integrity": "sha512-0/xS39c91WjPAZOWsvi1//zjx6kAp4kxWwctR6kuU6p133w8RU0D2dSCvZC19uQyharg/sAvYxGYWl01BbZZfg==", - "license": "MIT", - "dependencies": { - "@ant-design/colors": "^7.0.0", - "@ant-design/icons-svg": "^4.4.0", - "@babel/runtime": "^7.24.8", - "classnames": "^2.2.6", - "rc-util": "^5.31.1" - }, - "engines": { - "node": ">=8" - }, - "peerDependencies": { - "react": ">=16.0.0", - "react-dom": ">=16.0.0" - } - }, - "node_modules/@ant-design/icons-svg": { - "version": "4.4.2", - "resolved": "https://registry.npmmirror.com/@ant-design/icons-svg/-/icons-svg-4.4.2.tgz", - "integrity": "sha512-vHbT+zJEVzllwP+CM+ul7reTEfBR0vgxFe7+lREAsAA7YGsYpboiq2sQNeQeRvh09GfQgs/GyFEvZpJ9cLXpXA==", - "license": "MIT" - }, - "node_modules/@ant-design/react-slick": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/@ant-design/react-slick/-/react-slick-1.1.2.tgz", - "integrity": "sha512-EzlvzE6xQUBrZuuhSAFTdsr4P2bBBHGZwKFemEfq8gIGyIQCxalYfZW/T2ORbtQx5rU69o+WycP3exY/7T1hGA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.4", - "classnames": "^2.2.5", - "json2mq": "^0.2.0", - "resize-observer-polyfill": "^1.5.1", - "throttle-debounce": "^5.0.0" - }, - "peerDependencies": { - "react": ">=16.9.0" - } - }, - "node_modules/@babel/code-frame": { - "version": "7.27.1", - "resolved": "https://registry.npmmirror.com/@babel/code-frame/-/code-frame-7.27.1.tgz", - "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-validator-identifier": "^7.27.1", - "js-tokens": "^4.0.0", - "picocolors": "^1.1.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/compat-data": { - "version": "7.28.4", - "resolved": "https://registry.npmmirror.com/@babel/compat-data/-/compat-data-7.28.4.tgz", - "integrity": "sha512-YsmSKC29MJwf0gF8Rjjrg5LQCmyh+j/nD8/eP7f+BeoQTKYqs9RoWbjGOdy0+1Ekr68RJZMUOPVQaQisnIo4Rw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/core": { - "version": "7.28.4", - "resolved": "https://registry.npmmirror.com/@babel/core/-/core-7.28.4.tgz", - "integrity": "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-compilation-targets": "^7.27.2", - "@babel/helper-module-transforms": "^7.28.3", - "@babel/helpers": "^7.28.4", - "@babel/parser": "^7.28.4", - "@babel/template": "^7.27.2", - "@babel/traverse": "^7.28.4", - "@babel/types": "^7.28.4", - "@jridgewell/remapping": "^2.3.5", - "convert-source-map": "^2.0.0", - "debug": "^4.1.0", - "gensync": "^1.0.0-beta.2", - "json5": "^2.2.3", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/babel" - } - }, - "node_modules/@babel/core/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/generator": { - "version": "7.28.3", - "resolved": "https://registry.npmmirror.com/@babel/generator/-/generator-7.28.3.tgz", - "integrity": "sha512-3lSpxGgvnmZznmBkCRnVREPUFJv2wrv9iAoFDvADJc0ypmdOxdUtcLeBgBJ6zE0PMeTKnxeQzyk0xTBq4Ep7zw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.28.3", - "@babel/types": "^7.28.2", - "@jridgewell/gen-mapping": "^0.3.12", - "@jridgewell/trace-mapping": "^0.3.28", - "jsesc": "^3.0.2" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.27.2", - "resolved": "https://registry.npmmirror.com/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", - "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/compat-data": "^7.27.2", - "@babel/helper-validator-option": "^7.27.1", - "browserslist": "^4.24.0", - "lru-cache": "^5.1.1", - "semver": "^6.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-compilation-targets/node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmmirror.com/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - } - }, - "node_modules/@babel/helper-globals": { - "version": "7.28.0", - "resolved": "https://registry.npmmirror.com/@babel/helper-globals/-/helper-globals-7.28.0.tgz", - "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-imports": { - "version": "7.27.1", - "resolved": "https://registry.npmmirror.com/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", - "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/traverse": "^7.27.1", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-module-transforms": { - "version": "7.28.3", - "resolved": "https://registry.npmmirror.com/@babel/helper-module-transforms/-/helper-module-transforms-7.28.3.tgz", - "integrity": "sha512-gytXUbs8k2sXS9PnQptz5o0QnpLL51SwASIORY6XaBKF88nsOT0Zw9szLqlSGQDP/4TljBAD5y98p2U1fqkdsw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-module-imports": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1", - "@babel/traverse": "^7.28.3" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" - } - }, - "node_modules/@babel/helper-plugin-utils": { - "version": "7.27.1", - "resolved": "https://registry.npmmirror.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", - "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-string-parser": { - "version": "7.27.1", - "resolved": "https://registry.npmmirror.com/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", - "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-identifier": { - "version": "7.27.1", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", - "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-validator-option": { - "version": "7.27.1", - "resolved": "https://registry.npmmirror.com/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", - "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helpers": { - "version": "7.28.4", - "resolved": "https://registry.npmmirror.com/@babel/helpers/-/helpers-7.28.4.tgz", - "integrity": "sha512-HFN59MmQXGHVyYadKLVumYsA9dBFun/ldYxipEjzA4196jpLZd8UjEEBLkbEkvfYreDqJhZxYAWFPtrfhNpj4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/parser": { - "version": "7.28.4", - "resolved": "https://registry.npmmirror.com/@babel/parser/-/parser-7.28.4.tgz", - "integrity": "sha512-yZbBqeM6TkpP9du/I2pUZnJsRMGGvOuIrhjzC1AwHwW+6he4mni6Bp/m8ijn0iOuZuPI2BfkCoSRunpyjnrQKg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.4" - }, - "bin": { - "parser": "bin/babel-parser.js" - }, - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.27.1", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", - "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.27.1", - "resolved": "https://registry.npmmirror.com/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", - "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-plugin-utils": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0-0" - } - }, - "node_modules/@babel/runtime": { - "version": "7.28.4", - "resolved": "https://registry.npmmirror.com/@babel/runtime/-/runtime-7.28.4.tgz", - "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/template": { - "version": "7.27.2", - "resolved": "https://registry.npmmirror.com/@babel/template/-/template-7.27.2.tgz", - "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/parser": "^7.27.2", - "@babel/types": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.28.4", - "resolved": "https://registry.npmmirror.com/@babel/traverse/-/traverse-7.28.4.tgz", - "integrity": "sha512-YEzuboP2qvQavAcjgQNVgsvHIDv6ZpwXvcvjmyySP2DIMuByS/6ioU5G9pYrWHM6T2YDfc7xga9iNzYOs12CFQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/code-frame": "^7.27.1", - "@babel/generator": "^7.28.3", - "@babel/helper-globals": "^7.28.0", - "@babel/parser": "^7.28.4", - "@babel/template": "^7.27.2", - "@babel/types": "^7.28.4", - "debug": "^4.3.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/types": { - "version": "7.28.4", - "resolved": "https://registry.npmmirror.com/@babel/types/-/types-7.28.4.tgz", - "integrity": "sha512-bkFqkLhh3pMBUQQkpVgWDWq/lqzc2678eUyDlTBhRqhCHFguYYGM0Efga7tYk4TogG/3x0EEl66/OQ+WGbWB/Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/helper-string-parser": "^7.27.1", - "@babel/helper-validator-identifier": "^7.27.1" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@emotion/hash": { - "version": "0.8.0", - "resolved": "https://registry.npmmirror.com/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==", - "license": "MIT" - }, - "node_modules/@emotion/unitless": { - "version": "0.7.5", - "resolved": "https://registry.npmmirror.com/@emotion/unitless/-/unitless-0.7.5.tgz", - "integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg==", - "license": "MIT" - }, - "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/aix-ppc64/-/aix-ppc64-0.25.10.tgz", - "integrity": "sha512-0NFWnA+7l41irNuaSVlLfgNT12caWJVLzp5eAVhZ0z1qpxbockccEt3s+149rE64VUI3Ml2zt8Nv5JVc4QXTsw==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "aix" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/android-arm/-/android-arm-0.25.10.tgz", - "integrity": "sha512-dQAxF1dW1C3zpeCDc5KqIYuZ1tgAdRXNoZP7vkBIRtKZPYe2xVr/d3SkirklCHudW1B45tGiUlz2pUWDfbDD4w==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/android-arm64/-/android-arm64-0.25.10.tgz", - "integrity": "sha512-LSQa7eDahypv/VO6WKohZGPSJDq5OVOo3UoFR1E4t4Gj1W7zEQMUhI+lo81H+DtB+kP+tDgBp+M4oNCwp6kffg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/android-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/android-x64/-/android-x64-0.25.10.tgz", - "integrity": "sha512-MiC9CWdPrfhibcXwr39p9ha1x0lZJ9KaVfvzA0Wxwz9ETX4v5CHfF09bx935nHlhi+MxhA63dKRRQLiVgSUtEg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/darwin-arm64/-/darwin-arm64-0.25.10.tgz", - "integrity": "sha512-JC74bdXcQEpW9KkV326WpZZjLguSZ3DfS8wrrvPMHgQOIEIG/sPXEN/V8IssoJhbefLRcRqw6RQH2NnpdprtMA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/darwin-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/darwin-x64/-/darwin-x64-0.25.10.tgz", - "integrity": "sha512-tguWg1olF6DGqzws97pKZ8G2L7Ig1vjDmGTwcTuYHbuU6TTjJe5FXbgs5C1BBzHbJ2bo1m3WkQDbWO2PvamRcg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.10.tgz", - "integrity": "sha512-3ZioSQSg1HT2N05YxeJWYR+Libe3bREVSdWhEEgExWaDtyFbbXWb49QgPvFH8u03vUPX10JhJPcz7s9t9+boWg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/freebsd-x64/-/freebsd-x64-0.25.10.tgz", - "integrity": "sha512-LLgJfHJk014Aa4anGDbh8bmI5Lk+QidDmGzuC2D+vP7mv/GeSN+H39zOf7pN5N8p059FcOfs2bVlrRr4SK9WxA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm/-/linux-arm-0.25.10.tgz", - "integrity": "sha512-oR31GtBTFYCqEBALI9r6WxoU/ZofZl962pouZRTEYECvNF/dtXKku8YXcJkhgK/beU+zedXfIzHijSRapJY3vg==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-arm64/-/linux-arm64-0.25.10.tgz", - "integrity": "sha512-5luJWN6YKBsawd5f9i4+c+geYiVEw20FVW5x0v1kEMWNq8UctFjDiMATBxLvmmHA4bf7F6hTRaJgtghFr9iziQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ia32": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-ia32/-/linux-ia32-0.25.10.tgz", - "integrity": "sha512-NrSCx2Kim3EnnWgS4Txn0QGt0Xipoumb6z6sUtl5bOEZIVKhzfyp/Lyw4C1DIYvzeW/5mWYPBFJU3a/8Yr75DQ==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-loong64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-loong64/-/linux-loong64-0.25.10.tgz", - "integrity": "sha512-xoSphrd4AZda8+rUDDfD9J6FUMjrkTz8itpTITM4/xgerAZZcFW7Dv+sun7333IfKxGG8gAq+3NbfEMJfiY+Eg==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-mips64el/-/linux-mips64el-0.25.10.tgz", - "integrity": "sha512-ab6eiuCwoMmYDyTnyptoKkVS3k8fy/1Uvq7Dj5czXI6DF2GqD2ToInBI0SHOp5/X1BdZ26RKc5+qjQNGRBelRA==", - "cpu": [ - "mips64el" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-ppc64/-/linux-ppc64-0.25.10.tgz", - "integrity": "sha512-NLinzzOgZQsGpsTkEbdJTCanwA5/wozN9dSgEl12haXJBzMTpssebuXR42bthOF3z7zXFWH1AmvWunUCkBE4EA==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-riscv64/-/linux-riscv64-0.25.10.tgz", - "integrity": "sha512-FE557XdZDrtX8NMIeA8LBJX3dC2M8VGXwfrQWU7LB5SLOajfJIxmSdyL/gU1m64Zs9CBKvm4UAuBp5aJ8OgnrA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-s390x": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-s390x/-/linux-s390x-0.25.10.tgz", - "integrity": "sha512-3BBSbgzuB9ajLoVZk0mGu+EHlBwkusRmeNYdqmznmMc9zGASFjSsxgkNsqmXugpPk00gJ0JNKh/97nxmjctdew==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/linux-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/linux-x64/-/linux-x64-0.25.10.tgz", - "integrity": "sha512-QSX81KhFoZGwenVyPoberggdW1nrQZSvfVDAIUXr3WqLRZGZqWk/P4T8p2SP+de2Sr5HPcvjhcJzEiulKgnxtA==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.10.tgz", - "integrity": "sha512-AKQM3gfYfSW8XRk8DdMCzaLUFB15dTrZfnX8WXQoOUpUBQ+NaAFCP1kPS/ykbbGYz7rxn0WS48/81l9hFl3u4A==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/netbsd-x64/-/netbsd-x64-0.25.10.tgz", - "integrity": "sha512-7RTytDPGU6fek/hWuN9qQpeGPBZFfB4zZgcz2VK2Z5VpdUxEI8JKYsg3JfO0n/Z1E/6l05n0unDCNc4HnhQGig==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "netbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.10.tgz", - "integrity": "sha512-5Se0VM9Wtq797YFn+dLimf2Zx6McttsH2olUBsDml+lm0GOCRVebRWUvDtkY4BWYv/3NgzS8b/UM3jQNh5hYyw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/openbsd-x64/-/openbsd-x64-0.25.10.tgz", - "integrity": "sha512-XkA4frq1TLj4bEMB+2HnI0+4RnjbuGZfet2gs/LNs5Hc7D89ZQBHQ0gL2ND6Lzu1+QVkjp3x1gIcPKzRNP8bXw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openbsd" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/openharmony-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.10.tgz", - "integrity": "sha512-AVTSBhTX8Y/Fz6OmIVBip9tJzZEUcY8WLh7I59+upa5/GPhh2/aM6bvOMQySspnCCHvFi79kMtdJS1w0DXAeag==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/sunos-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/sunos-x64/-/sunos-x64-0.25.10.tgz", - "integrity": "sha512-fswk3XT0Uf2pGJmOpDB7yknqhVkJQkAQOcW/ccVOtfx05LkbWOaRAtn5SaqXypeKQra1QaEa841PgrSL9ubSPQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "sunos" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-arm64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/win32-arm64/-/win32-arm64-0.25.10.tgz", - "integrity": "sha512-ah+9b59KDTSfpaCg6VdJoOQvKjI33nTaQr4UluQwW7aEwZQsbMCfTmfEO4VyewOxx4RaDT/xCy9ra2GPWmO7Kw==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-ia32": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/win32-ia32/-/win32-ia32-0.25.10.tgz", - "integrity": "sha512-QHPDbKkrGO8/cz9LKVnJU22HOi4pxZnZhhA2HYHez5Pz4JeffhDjf85E57Oyco163GnzNCVkZK0b/n4Y0UHcSw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@esbuild/win32-x64": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/@esbuild/win32-x64/-/win32-x64-0.25.10.tgz", - "integrity": "sha512-9KpxSVFCu0iK1owoez6aC/s/EdUQLDN3adTxGCqxMVhrPDj6bt5dbrHDXUuq+Bs2vATFBBrQS5vdQ/Ed2P+nbw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">=18" - } - }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.9.0", - "resolved": "https://registry.npmmirror.com/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", - "integrity": "sha512-ayVFHdtZ+hsq1t2Dy24wCmGXGe4q9Gu3smhLYALJrr473ZH27MsnSL+LKUlimp4BWJqMDMLmPpx/Q9R3OAlL4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "eslint-visitor-keys": "^3.4.3" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" - } - }, - "node_modules/@eslint-community/regexpp": { - "version": "4.12.1", - "resolved": "https://registry.npmmirror.com/@eslint-community/regexpp/-/regexpp-4.12.1.tgz", - "integrity": "sha512-CCZCDJuduB9OUkFkY2IgppNZMi2lBQgD2qzwXkEia16cge2pijY/aXi96CJMquDMn3nJdlPV1A5KrJEXwfLNzQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" - } - }, - "node_modules/@eslint/config-array": { - "version": "0.21.0", - "resolved": "https://registry.npmmirror.com/@eslint/config-array/-/config-array-0.21.0.tgz", - "integrity": "sha512-ENIdc4iLu0d93HeYirvKmrzshzofPw6VkZRKQGe9Nv46ZnWUzcF1xV01dcvEg/1wXUR61OmmlSfyeyO7EvjLxQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/object-schema": "^2.1.6", - "debug": "^4.3.1", - "minimatch": "^3.1.2" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/config-array/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/config-array/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/config-helpers": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@eslint/config-helpers/-/config-helpers-0.4.0.tgz", - "integrity": "sha512-WUFvV4WoIwW8Bv0KeKCIIEgdSiFOsulyN0xrMu+7z43q/hkOLXjvb5u7UC9jDxvRzcrbEmuZBX5yJZz1741jog==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.16.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/core": { - "version": "0.16.0", - "resolved": "https://registry.npmmirror.com/@eslint/core/-/core-0.16.0.tgz", - "integrity": "sha512-nmC8/totwobIiFcGkDza3GIKfAw1+hLiYVrh3I1nIomQ8PEr5cxg34jnkmGawul/ep52wGRAcyeDCNtWKSOj4Q==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@types/json-schema": "^7.0.15" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/eslintrc": { - "version": "3.3.1", - "resolved": "https://registry.npmmirror.com/@eslint/eslintrc/-/eslintrc-3.3.1.tgz", - "integrity": "sha512-gtF186CXhIl1p4pJNGZw8Yc6RlshoePRvE0X91oPGb3vZ8pM3qOS9W9NGPat9LziaBV7XrJWGylNQXkGcnM3IQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ajv": "^6.12.4", - "debug": "^4.3.2", - "espree": "^10.0.1", - "globals": "^14.0.0", - "ignore": "^5.2.0", - "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", - "minimatch": "^3.1.2", - "strip-json-comments": "^3.1.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/@eslint/eslintrc/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/@eslint/js": { - "version": "9.37.0", - "resolved": "https://registry.npmmirror.com/@eslint/js/-/js-9.37.0.tgz", - "integrity": "sha512-jaS+NJ+hximswBG6pjNX0uEJZkrT0zwpVi3BA3vX22aFGjJjmgSTSmPpZCRKmoBL5VY/M6p0xsSJx7rk7sy5gg==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - } - }, - "node_modules/@eslint/object-schema": { - "version": "2.1.6", - "resolved": "https://registry.npmmirror.com/@eslint/object-schema/-/object-schema-2.1.6.tgz", - "integrity": "sha512-RBMg5FRL0I0gs51M/guSAj5/e14VQ4tpZnQNWwuDT66P14I43ItmPfIZRhO9fUVIPOAQXU47atlywZ/czoqFPA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@eslint/plugin-kit": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/@eslint/plugin-kit/-/plugin-kit-0.4.0.tgz", - "integrity": "sha512-sB5uyeq+dwCWyPi31B2gQlVlo+j5brPlWx4yZBrEaRo/nhdDE8Xke1gsGgtiBdaBTxuTkceLVuVt/pclrasb0A==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@eslint/core": "^0.16.0", - "levn": "^0.4.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - } - }, - "node_modules/@humanfs/core": { - "version": "0.19.1", - "resolved": "https://registry.npmmirror.com/@humanfs/core/-/core-0.19.1.tgz", - "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanfs/node": { - "version": "0.16.7", - "resolved": "https://registry.npmmirror.com/@humanfs/node/-/node-0.16.7.tgz", - "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", - "dev": true, - "license": "Apache-2.0", - "dependencies": { - "@humanfs/core": "^0.19.1", - "@humanwhocodes/retry": "^0.4.0" - }, - "engines": { - "node": ">=18.18.0" - } - }, - "node_modules/@humanwhocodes/module-importer": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", - "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=12.22" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@humanwhocodes/retry": { - "version": "0.4.3", - "resolved": "https://registry.npmmirror.com/@humanwhocodes/retry/-/retry-0.4.3.tgz", - "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": ">=18.18" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, - "node_modules/@isaacs/cliui": { - "version": "8.0.2", - "resolved": "https://registry.npmmirror.com/@isaacs/cliui/-/cliui-8.0.2.tgz", - "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", - "dev": true, - "license": "ISC", - "dependencies": { - "string-width": "^5.1.2", - "string-width-cjs": "npm:string-width@^4.2.0", - "strip-ansi": "^7.0.1", - "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", - "wrap-ansi": "^8.1.0", - "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" - }, - "engines": { - "node": ">=12" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.13", - "resolved": "https://registry.npmmirror.com/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", - "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/sourcemap-codec": "^1.5.0", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/remapping": { - "version": "2.3.5", - "resolved": "https://registry.npmmirror.com/@jridgewell/remapping/-/remapping-2.3.5.tgz", - "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.24" - } - }, - "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", - "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.5.5", - "resolved": "https://registry.npmmirror.com/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", - "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", - "dev": true, - "license": "MIT" - }, - "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.31", - "resolved": "https://registry.npmmirror.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", - "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/resolve-uri": "^3.1.0", - "@jridgewell/sourcemap-codec": "^1.4.14" - } - }, - "node_modules/@nodelib/fs.scandir": { - "version": "2.1.5", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", - "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "2.0.5", - "run-parallel": "^1.1.9" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.stat": { - "version": "2.0.5", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", - "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/@nodelib/fs.walk": { - "version": "1.2.8", - "resolved": "https://registry.npmmirror.com/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", - "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.scandir": "2.1.5", - "fastq": "^1.6.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmmirror.com/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/@rc-component/async-validator": { - "version": "5.0.4", - "resolved": "https://registry.npmmirror.com/@rc-component/async-validator/-/async-validator-5.0.4.tgz", - "integrity": "sha512-qgGdcVIF604M9EqjNF0hbUTz42bz/RDtxWdWuU5EQe3hi7M8ob54B6B35rOsvX5eSvIHIzT9iH1R3n+hk3CGfg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.24.4" - }, - "engines": { - "node": ">=14.x" - } - }, - "node_modules/@rc-component/color-picker": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/@rc-component/color-picker/-/color-picker-2.0.1.tgz", - "integrity": "sha512-WcZYwAThV/b2GISQ8F+7650r5ZZJ043E57aVBFkQ+kSY4C6wdofXgB0hBx+GPGpIU0Z81eETNoDUJMr7oy/P8Q==", - "license": "MIT", - "dependencies": { - "@ant-design/fast-color": "^2.0.6", - "@babel/runtime": "^7.23.6", - "classnames": "^2.2.6", - "rc-util": "^5.38.1" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/@rc-component/context": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/@rc-component/context/-/context-1.4.0.tgz", - "integrity": "sha512-kFcNxg9oLRMoL3qki0OMxK+7g5mypjgaaJp/pkOis/6rVxma9nJBF/8kCIuTYHUQNr0ii7MxqE33wirPZLJQ2w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "rc-util": "^5.27.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/@rc-component/mini-decimal": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/@rc-component/mini-decimal/-/mini-decimal-1.1.0.tgz", - "integrity": "sha512-jS4E7T9Li2GuYwI6PyiVXmxTiM6b07rlD9Ge8uGZSCz3WlzcG5ZK7g5bbuKNeZ9pgUuPK/5guV781ujdVpm4HQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.0" - }, - "engines": { - "node": ">=8.x" - } - }, - "node_modules/@rc-component/mutate-observer": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/@rc-component/mutate-observer/-/mutate-observer-1.1.0.tgz", - "integrity": "sha512-QjrOsDXQusNwGZPf4/qRQasg7UFEj06XiCJ8iuiq/Io7CrHrgVi6Uuetw60WAMG1799v+aM8kyc+1L/GBbHSlw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.0", - "classnames": "^2.3.2", - "rc-util": "^5.24.4" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/@rc-component/portal": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/@rc-component/portal/-/portal-1.1.2.tgz", - "integrity": "sha512-6f813C0IsasTZms08kfA8kPAGxbbkYToa8ALaiDIGGECU4i9hj8Plgbx0sNJDrey3EtHO30hmdaxtT0138xZcg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.0", - "classnames": "^2.3.2", - "rc-util": "^5.24.4" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/@rc-component/qrcode": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/@rc-component/qrcode/-/qrcode-1.0.1.tgz", - "integrity": "sha512-g8eeeaMyFXVlq8cZUeaxCDhfIYjpao0l9cvm5gFwKXy/Vm1yDWV7h2sjH5jHYzdFedlVKBpATFB1VKMrHzwaWQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.24.7", - "classnames": "^2.3.2" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/@rc-component/tour": { - "version": "1.15.1", - "resolved": "https://registry.npmmirror.com/@rc-component/tour/-/tour-1.15.1.tgz", - "integrity": "sha512-Tr2t7J1DKZUpfJuDZWHxyxWpfmj8EZrqSgyMZ+BCdvKZ6r1UDsfU46M/iWAAFBy961Ssfom2kv5f3UcjIL2CmQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.0", - "@rc-component/portal": "^1.0.0-9", - "@rc-component/trigger": "^2.0.0", - "classnames": "^2.3.2", - "rc-util": "^5.24.4" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/@rc-component/trigger": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/@rc-component/trigger/-/trigger-2.3.0.tgz", - "integrity": "sha512-iwaxZyzOuK0D7lS+0AQEtW52zUWxoGqTGkke3dRyb8pYiShmRpCjB/8TzPI4R6YySCH7Vm9BZj/31VPiiQTLBg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.2", - "@rc-component/portal": "^1.1.0", - "classnames": "^2.3.2", - "rc-motion": "^2.0.0", - "rc-resize-observer": "^1.3.1", - "rc-util": "^5.44.0" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/@remix-run/router": { - "version": "1.23.0", - "resolved": "https://registry.npmmirror.com/@remix-run/router/-/router-1.23.0.tgz", - "integrity": "sha512-O3rHJzAQKamUz1fvE0Qaw0xSFqsA/yafi2iqeE0pvdFtCO1viYx8QL6f3Ln/aCCTLxs68SLf0KPM9eSeM8yBnA==", - "license": "MIT", - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/@rolldown/pluginutils": { - "version": "1.0.0-beta.27", - "resolved": "https://registry.npmmirror.com/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", - "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.52.4.tgz", - "integrity": "sha512-BTm2qKNnWIQ5auf4deoetINJm2JzvihvGb9R6K/ETwKLql/Bb3Eg2H1FBp1gUb4YGbydMA3jcmQTR73q7J+GAA==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-android-arm64": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.52.4.tgz", - "integrity": "sha512-P9LDQiC5vpgGFgz7GSM6dKPCiqR3XYN1WwJKA4/BUVDjHpYsf3iBEmVz62uyq20NGYbiGPR5cNHI7T1HqxNs2w==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "android" - ] - }, - "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.52.4.tgz", - "integrity": "sha512-QRWSW+bVccAvZF6cbNZBJwAehmvG9NwfWHwMy4GbWi/BQIA/laTIktebT2ipVjNncqE6GLPxOok5hsECgAxGZg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.52.4.tgz", - "integrity": "sha512-hZgP05pResAkRJxL1b+7yxCnXPGsXU0fG9Yfd6dUaoGk+FhdPKCJ5L1Sumyxn8kvw8Qi5PvQ8ulenUbRjzeCTw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ] - }, - "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.52.4.tgz", - "integrity": "sha512-xmc30VshuBNUd58Xk4TKAEcRZHaXlV+tCxIXELiE9sQuK3kG8ZFgSPi57UBJt8/ogfhAF5Oz4ZSUBN77weM+mQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.52.4.tgz", - "integrity": "sha512-WdSLpZFjOEqNZGmHflxyifolwAiZmDQzuOzIq9L27ButpCVpD7KzTRtEG1I0wMPFyiyUdOO+4t8GvrnBLQSwpw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "freebsd" - ] - }, - "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.52.4.tgz", - "integrity": "sha512-xRiOu9Of1FZ4SxVbB0iEDXc4ddIcjCv2aj03dmW8UrZIW7aIQ9jVJdLBIhxBI+MaTnGAKyvMwPwQnoOEvP7FgQ==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.52.4.tgz", - "integrity": "sha512-FbhM2p9TJAmEIEhIgzR4soUcsW49e9veAQCziwbR+XWB2zqJ12b4i/+hel9yLiD8pLncDH4fKIPIbt5238341Q==", - "cpu": [ - "arm" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.52.4.tgz", - "integrity": "sha512-4n4gVwhPHR9q/g8lKCyz0yuaD0MvDf7dV4f9tHt0C73Mp8h38UCtSCSE6R9iBlTbXlmA8CjpsZoujhszefqueg==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.52.4.tgz", - "integrity": "sha512-u0n17nGA0nvi/11gcZKsjkLj1QIpAuPFQbR48Subo7SmZJnGxDpspyw2kbpuoQnyK+9pwf3pAoEXerJs/8Mi9g==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-loong64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.52.4.tgz", - "integrity": "sha512-0G2c2lpYtbTuXo8KEJkDkClE/+/2AFPdPAbmaHoE870foRFs4pBrDehilMcrSScrN/fB/1HTaWO4bqw+ewBzMQ==", - "cpu": [ - "loong64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-ppc64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.52.4.tgz", - "integrity": "sha512-teSACug1GyZHmPDv14VNbvZFX779UqWTsd7KtTM9JIZRDI5NUwYSIS30kzI8m06gOPB//jtpqlhmraQ68b5X2g==", - "cpu": [ - "ppc64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.52.4.tgz", - "integrity": "sha512-/MOEW3aHjjs1p4Pw1Xk4+3egRevx8Ji9N6HUIA1Ifh8Q+cg9dremvFCUbOX2Zebz80BwJIgCBUemjqhU5XI5Eg==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-riscv64-musl": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.52.4.tgz", - "integrity": "sha512-1HHmsRyh845QDpEWzOFtMCph5Ts+9+yllCrREuBR/vg2RogAQGGBRC8lDPrPOMnrdOJ+mt1WLMOC2Kao/UwcvA==", - "cpu": [ - "riscv64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.52.4.tgz", - "integrity": "sha512-seoeZp4L/6D1MUyjWkOMRU6/iLmCU2EjbMTyAG4oIOs1/I82Y5lTeaxW0KBfkUdHAWN7j25bpkt0rjnOgAcQcA==", - "cpu": [ - "s390x" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.52.4.tgz", - "integrity": "sha512-Wi6AXf0k0L7E2gteNsNHUs7UMwCIhsCTs6+tqQ5GPwVRWMaflqGec4Sd8n6+FNFDw9vGcReqk2KzBDhCa1DLYg==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.52.4.tgz", - "integrity": "sha512-dtBZYjDmCQ9hW+WgEkaffvRRCKm767wWhxsFW3Lw86VXz/uJRuD438/XvbZT//B96Vs8oTA8Q4A0AfHbrxP9zw==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "linux" - ] - }, - "node_modules/@rollup/rollup-openharmony-arm64": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.52.4.tgz", - "integrity": "sha512-1ox+GqgRWqaB1RnyZXL8PD6E5f7YyRUJYnCqKpNzxzP0TkaUh112NDrR9Tt+C8rJ4x5G9Mk8PQR3o7Ku2RKqKA==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "openharmony" - ] - }, - "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.52.4.tgz", - "integrity": "sha512-8GKr640PdFNXwzIE0IrkMWUNUomILLkfeHjXBi/nUvFlpZP+FA8BKGKpacjW6OUUHaNI6sUURxR2U2g78FOHWQ==", - "cpu": [ - "arm64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.52.4.tgz", - "integrity": "sha512-AIy/jdJ7WtJ/F6EcfOb2GjR9UweO0n43jNObQMb6oGxkYTfLcnN7vYYpG+CN3lLxrQkzWnMOoNSHTW54pgbVxw==", - "cpu": [ - "ia32" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-gnu": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.52.4.tgz", - "integrity": "sha512-UF9KfsH9yEam0UjTwAgdK0anlQ7c8/pWPU2yVjyWcF1I1thABt6WXE47cI71pGiZ8wGvxohBoLnxM04L/wj8mQ==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.52.4.tgz", - "integrity": "sha512-bf9PtUa0u8IXDVxzRToFQKsNCRz9qLYfR/MpECxl4mRoWYjAeFjgxj1XdZr2M/GNVpT05p+LgQOHopYDlUu6/w==", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT", - "optional": true, - "os": [ - "win32" - ] - }, - "node_modules/@types/babel__core": { - "version": "7.20.5", - "resolved": "https://registry.npmmirror.com/@types/babel__core/-/babel__core-7.20.5.tgz", - "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.20.7", - "@babel/types": "^7.20.7", - "@types/babel__generator": "*", - "@types/babel__template": "*", - "@types/babel__traverse": "*" - } - }, - "node_modules/@types/babel__generator": { - "version": "7.27.0", - "resolved": "https://registry.npmmirror.com/@types/babel__generator/-/babel__generator-7.27.0.tgz", - "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__template": { - "version": "7.4.4", - "resolved": "https://registry.npmmirror.com/@types/babel__template/-/babel__template-7.4.4.tgz", - "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/parser": "^7.1.0", - "@babel/types": "^7.0.0" - } - }, - "node_modules/@types/babel__traverse": { - "version": "7.28.0", - "resolved": "https://registry.npmmirror.com/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", - "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/types": "^7.28.2" - } - }, - "node_modules/@types/debug": { - "version": "4.1.12", - "resolved": "https://registry.npmmirror.com/@types/debug/-/debug-4.1.12.tgz", - "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", - "license": "MIT", - "dependencies": { - "@types/ms": "*" - } - }, - "node_modules/@types/estree": { - "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/@types/estree/-/estree-1.0.8.tgz", - "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", - "license": "MIT" - }, - "node_modules/@types/estree-jsx": { - "version": "1.0.5", - "resolved": "https://registry.npmmirror.com/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", - "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", - "license": "MIT", - "dependencies": { - "@types/estree": "*" - } - }, - "node_modules/@types/hast": { - "version": "3.0.4", - "resolved": "https://registry.npmmirror.com/@types/hast/-/hast-3.0.4.tgz", - "integrity": "sha512-WPs+bbQw5aCj+x6laNGWLH3wviHtoCv/P3+otBhbOhJgG8qtpdAMlTCxLtsTWA7LH1Oh/bFCHsBn0TPS5m30EQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/js-yaml": { - "version": "4.0.9", - "resolved": "https://registry.npmmirror.com/@types/js-yaml/-/js-yaml-4.0.9.tgz", - "integrity": "sha512-k4MGaQl5TGo/iipqb2UDG2UwjXziSWkh0uysQelTlJpX1qGlpUZYm8PnO4DxG1qBomtJUdYJ6qR6xdIah10JLg==", - "license": "MIT" - }, - "node_modules/@types/json-schema": { - "version": "7.0.15", - "resolved": "https://registry.npmmirror.com/@types/json-schema/-/json-schema-7.0.15.tgz", - "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", - "dev": true, - "license": "MIT" - }, - "node_modules/@types/mdast": { - "version": "4.0.4", - "resolved": "https://registry.npmmirror.com/@types/mdast/-/mdast-4.0.4.tgz", - "integrity": "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA==", - "license": "MIT", - "dependencies": { - "@types/unist": "*" - } - }, - "node_modules/@types/ms": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/@types/ms/-/ms-2.1.0.tgz", - "integrity": "sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==", - "license": "MIT" - }, - "node_modules/@types/pako": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/@types/pako/-/pako-2.0.4.tgz", - "integrity": "sha512-VWDCbrLeVXJM9fihYodcLiIv0ku+AlOa/TQ1SvYOaBuyrSKgEcro95LJyIsJ4vSo6BXIxOKxiJAat04CmST9Fw==", - "license": "MIT" - }, - "node_modules/@types/prop-types": { - "version": "15.7.15", - "resolved": "https://registry.npmmirror.com/@types/prop-types/-/prop-types-15.7.15.tgz", - "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", - "license": "MIT" - }, - "node_modules/@types/raf": { - "version": "3.4.3", - "resolved": "https://registry.npmmirror.com/@types/raf/-/raf-3.4.3.tgz", - "integrity": "sha512-c4YAvMedbPZ5tEyxzQdMoOhhJ4RD3rngZIdwC2/qDN3d7JpEhB6fiBRKVY1lg5B7Wk+uPBjn5f39j1/2MY1oOw==", - "license": "MIT", - "optional": true - }, - "node_modules/@types/react": { - "version": "18.3.26", - "resolved": "https://registry.npmmirror.com/@types/react/-/react-18.3.26.tgz", - "integrity": "sha512-RFA/bURkcKzx/X9oumPG9Vp3D3JUgus/d0b67KB0t5S/raciymilkOa66olh78MUI92QLbEJevO7rvqU/kjwKA==", - "license": "MIT", - "dependencies": { - "@types/prop-types": "*", - "csstype": "^3.0.2" - } - }, - "node_modules/@types/react-dom": { - "version": "18.3.7", - "resolved": "https://registry.npmmirror.com/@types/react-dom/-/react-dom-18.3.7.tgz", - "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "@types/react": "^18.0.0" - } - }, - "node_modules/@types/react-syntax-highlighter": { - "version": "15.5.13", - "resolved": "https://registry.npmmirror.com/@types/react-syntax-highlighter/-/react-syntax-highlighter-15.5.13.tgz", - "integrity": "sha512-uLGJ87j6Sz8UaBAooU0T6lWJ0dBmjZgN1PZTrj05TNql2/XpC6+4HhMT5syIdFUUt+FASfCeLLv4kBygNU+8qA==", - "license": "MIT", - "dependencies": { - "@types/react": "*" - } - }, - "node_modules/@types/trusted-types": { - "version": "2.0.7", - "resolved": "https://registry.npmmirror.com/@types/trusted-types/-/trusted-types-2.0.7.tgz", - "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "license": "MIT", - "optional": true - }, - "node_modules/@types/unist": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-3.0.3.tgz", - "integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==", - "license": "MIT" - }, - "node_modules/@typescript-eslint/eslint-plugin": { - "version": "8.46.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.46.0.tgz", - "integrity": "sha512-hA8gxBq4ukonVXPy0OKhiaUh/68D0E88GSmtC1iAEnGaieuDi38LhS7jdCHRLi6ErJBNDGCzvh5EnzdPwUc0DA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "8.46.0", - "@typescript-eslint/type-utils": "8.46.0", - "@typescript-eslint/utils": "8.46.0", - "@typescript-eslint/visitor-keys": "8.46.0", - "graphemer": "^1.4.0", - "ignore": "^7.0.0", - "natural-compare": "^1.4.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "@typescript-eslint/parser": "^8.46.0", - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/parser": { - "version": "8.46.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/parser/-/parser-8.46.0.tgz", - "integrity": "sha512-n1H6IcDhmmUEG7TNVSspGmiHHutt7iVKtZwRppD7e04wha5MrkV1h3pti9xQLcCMt6YWsncpoT0HMjkH1FNwWQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/scope-manager": "8.46.0", - "@typescript-eslint/types": "8.46.0", - "@typescript-eslint/typescript-estree": "8.46.0", - "@typescript-eslint/visitor-keys": "8.46.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/project-service": { - "version": "8.46.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/project-service/-/project-service-8.46.0.tgz", - "integrity": "sha512-OEhec0mH+U5Je2NZOeK1AbVCdm0ChyapAyTeXVIYTPXDJ3F07+cu87PPXcGoYqZ7M9YJVvFnfpGg1UmCIqM+QQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/tsconfig-utils": "^8.46.0", - "@typescript-eslint/types": "^8.46.0", - "debug": "^4.3.4" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "8.46.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/scope-manager/-/scope-manager-8.46.0.tgz", - "integrity": "sha512-lWETPa9XGcBes4jqAMYD9fW0j4n6hrPtTJwWDmtqgFO/4HF4jmdH/Q6wggTw5qIT5TXjKzbt7GsZUBnWoO3dqw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.46.0", - "@typescript-eslint/visitor-keys": "8.46.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/tsconfig-utils": { - "version": "8.46.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.46.0.tgz", - "integrity": "sha512-WrYXKGAHY836/N7zoK/kzi6p8tXFhasHh8ocFL9VZSAkvH956gfeRfcnhs3xzRy8qQ/dq3q44v1jvQieMFg2cw==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/type-utils": { - "version": "8.46.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/type-utils/-/type-utils-8.46.0.tgz", - "integrity": "sha512-hy+lvYV1lZpVs2jRaEYvgCblZxUoJiPyCemwbQZ+NGulWkQRy0HRPYAoef/CNSzaLt+MLvMptZsHXHlkEilaeg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.46.0", - "@typescript-eslint/typescript-estree": "8.46.0", - "@typescript-eslint/utils": "8.46.0", - "debug": "^4.3.4", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/types": { - "version": "8.46.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/types/-/types-8.46.0.tgz", - "integrity": "sha512-bHGGJyVjSE4dJJIO5yyEWt/cHyNwga/zXGJbJJ8TiO01aVREK6gCTu3L+5wrkb1FbDkQ+TKjMNe9R/QQQP9+rA==", - "dev": true, - "license": "MIT", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/typescript-estree": { - "version": "8.46.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/typescript-estree/-/typescript-estree-8.46.0.tgz", - "integrity": "sha512-ekDCUfVpAKWJbRfm8T1YRrCot1KFxZn21oV76v5Fj4tr7ELyk84OS+ouvYdcDAwZL89WpEkEj2DKQ+qg//+ucg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/project-service": "8.46.0", - "@typescript-eslint/tsconfig-utils": "8.46.0", - "@typescript-eslint/types": "8.46.0", - "@typescript-eslint/visitor-keys": "8.46.0", - "debug": "^4.3.4", - "fast-glob": "^3.3.2", - "is-glob": "^4.0.3", - "minimatch": "^9.0.4", - "semver": "^7.6.0", - "ts-api-utils": "^2.1.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/utils": { - "version": "8.46.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/utils/-/utils-8.46.0.tgz", - "integrity": "sha512-nD6yGWPj1xiOm4Gk0k6hLSZz2XkNXhuYmyIrOWcHoPuAhjT9i5bAG+xbWPgFeNR8HPHHtpNKdYUXJl/D3x7f5g==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.7.0", - "@typescript-eslint/scope-manager": "8.46.0", - "@typescript-eslint/types": "8.46.0", - "@typescript-eslint/typescript-estree": "8.46.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0", - "typescript": ">=4.8.4 <6.0.0" - } - }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.46.0", - "resolved": "https://registry.npmmirror.com/@typescript-eslint/visitor-keys/-/visitor-keys-8.46.0.tgz", - "integrity": "sha512-FrvMpAK+hTbFy7vH5j1+tMYHMSKLE6RzluFJlkFNKD0p9YsUT75JlBSmr5so3QRzvMwU5/bIEdeNrxm8du8l3Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "@typescript-eslint/types": "8.46.0", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, - "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/@ungap/structured-clone": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", - "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", - "license": "ISC" - }, - "node_modules/@vitejs/plugin-react": { - "version": "4.7.0", - "resolved": "https://registry.npmmirror.com/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", - "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@babel/core": "^7.28.0", - "@babel/plugin-transform-react-jsx-self": "^7.27.1", - "@babel/plugin-transform-react-jsx-source": "^7.27.1", - "@rolldown/pluginutils": "1.0.0-beta.27", - "@types/babel__core": "^7.20.5", - "react-refresh": "^0.17.0" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" - } - }, - "node_modules/acorn": { - "version": "8.15.0", - "resolved": "https://registry.npmmirror.com/acorn/-/acorn-8.15.0.tgz", - "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", - "dev": true, - "license": "MIT", - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-jsx": { - "version": "5.3.2", - "resolved": "https://registry.npmmirror.com/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" - } - }, - "node_modules/adler-32": { - "version": "1.3.1", - "resolved": "https://registry.npmmirror.com/adler-32/-/adler-32-1.3.1.tgz", - "integrity": "sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmmirror.com/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "license": "MIT", - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/ansi-regex": { - "version": "6.2.2", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-6.2.2.tgz", - "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-regex?sponsor=1" - } - }, - "node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/antd": { - "version": "5.27.4", - "resolved": "https://registry.npmmirror.com/antd/-/antd-5.27.4.tgz", - "integrity": "sha512-rhArohoAUCxhkPjGI/BXthOrrjaElL4Fb7d4vEHnIR3DpxFXfegd4rN21IgGdiF+Iz4EFuUZu8MdS8NuJHLSVQ==", - "license": "MIT", - "dependencies": { - "@ant-design/colors": "^7.2.1", - "@ant-design/cssinjs": "^1.23.0", - "@ant-design/cssinjs-utils": "^1.1.3", - "@ant-design/fast-color": "^2.0.6", - "@ant-design/icons": "^5.6.1", - "@ant-design/react-slick": "~1.1.2", - "@babel/runtime": "^7.26.0", - "@rc-component/color-picker": "~2.0.1", - "@rc-component/mutate-observer": "^1.1.0", - "@rc-component/qrcode": "~1.0.0", - "@rc-component/tour": "~1.15.1", - "@rc-component/trigger": "^2.3.0", - "classnames": "^2.5.1", - "copy-to-clipboard": "^3.3.3", - "dayjs": "^1.11.11", - "rc-cascader": "~3.34.0", - "rc-checkbox": "~3.5.0", - "rc-collapse": "~3.9.0", - "rc-dialog": "~9.6.0", - "rc-drawer": "~7.3.0", - "rc-dropdown": "~4.2.1", - "rc-field-form": "~2.7.0", - "rc-image": "~7.12.0", - "rc-input": "~1.8.0", - "rc-input-number": "~9.5.0", - "rc-mentions": "~2.20.0", - "rc-menu": "~9.16.1", - "rc-motion": "^2.9.5", - "rc-notification": "~5.6.4", - "rc-pagination": "~5.1.0", - "rc-picker": "~4.11.3", - "rc-progress": "~4.0.0", - "rc-rate": "~2.13.1", - "rc-resize-observer": "^1.4.3", - "rc-segmented": "~2.7.0", - "rc-select": "~14.16.8", - "rc-slider": "~11.1.9", - "rc-steps": "~6.0.1", - "rc-switch": "~4.1.0", - "rc-table": "~7.53.0", - "rc-tabs": "~15.7.0", - "rc-textarea": "~1.10.2", - "rc-tooltip": "~6.4.0", - "rc-tree": "~5.13.1", - "rc-tree-select": "~5.27.0", - "rc-upload": "~4.9.2", - "rc-util": "^5.44.4", - "scroll-into-view-if-needed": "^3.1.0", - "throttle-debounce": "^5.0.2" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/ant-design" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/any-promise": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/any-promise/-/any-promise-1.3.0.tgz", - "integrity": "sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==", - "dev": true, - "license": "MIT" - }, - "node_modules/anymatch": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/anymatch/-/anymatch-3.1.3.tgz", - "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", - "dev": true, - "license": "ISC", - "dependencies": { - "normalize-path": "^3.0.0", - "picomatch": "^2.0.4" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/arg": { - "version": "5.0.2", - "resolved": "https://registry.npmmirror.com/arg/-/arg-5.0.2.tgz", - "integrity": "sha512-PYjyFOLKQ9y57JvQ6QLo8dAgNqswh8M1RMJYdQduT6xbWSgK36P/Z/v+p888pM69jMMfS8Xd8F6I1kQ/I9HUGg==", - "dev": true, - "license": "MIT" - }, - "node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "license": "Python-2.0" - }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "license": "MIT" - }, - "node_modules/autoprefixer": { - "version": "10.4.21", - "resolved": "https://registry.npmmirror.com/autoprefixer/-/autoprefixer-10.4.21.tgz", - "integrity": "sha512-O+A6LWV5LDHSJD3LjHYoNi4VLsj/Whi7k6zG12xTYaU4cQ8oxQGckXNX8cRHK5yOZ/ppVHe0ZBXGzSV9jXdVbQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/autoprefixer" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "browserslist": "^4.24.4", - "caniuse-lite": "^1.0.30001702", - "fraction.js": "^4.3.7", - "normalize-range": "^0.1.2", - "picocolors": "^1.1.1", - "postcss-value-parser": "^4.2.0" - }, - "bin": { - "autoprefixer": "bin/autoprefixer" - }, - "engines": { - "node": "^10 || ^12 || >=14" - }, - "peerDependencies": { - "postcss": "^8.1.0" - } - }, - "node_modules/axios": { - "version": "1.12.2", - "resolved": "https://registry.npmmirror.com/axios/-/axios-1.12.2.tgz", - "integrity": "sha512-vMJzPewAlRyOgxV2dU0Cuz2O8zzzx9VYtbJOaBgXFeLc4IV/Eg50n4LowmehOOR61S8ZMpc2K5Sa7g6A4jfkUw==", - "license": "MIT", - "dependencies": { - "follow-redirects": "^1.15.6", - "form-data": "^4.0.4", - "proxy-from-env": "^1.1.0" - } - }, - "node_modules/bail": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/bail/-/bail-2.0.2.tgz", - "integrity": "sha512-0xO6mYd7JB2YesxDKplafRpsiOzPt9V02ddPCLbY1xYGPOX24NTyN50qnUxgCPcSoYMhKpAuBTjQoRZCAkUDRw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/base64-arraybuffer": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/base64-arraybuffer/-/base64-arraybuffer-1.0.2.tgz", - "integrity": "sha512-I3yl4r9QB5ZRY3XuJVEPfc2XhZO6YweFPI+UovAzn+8/hb3oJ6lnysaFcjVpkCPfVWFUDvoZ8kmVDP7WyRtYtQ==", - "license": "MIT", - "engines": { - "node": ">= 0.6.0" - } - }, - "node_modules/baseline-browser-mapping": { - "version": "2.8.15", - "resolved": "https://registry.npmmirror.com/baseline-browser-mapping/-/baseline-browser-mapping-2.8.15.tgz", - "integrity": "sha512-qsJ8/X+UypqxHXN75M7dF88jNK37dLBRW7LeUzCPz+TNs37G8cfWy9nWzS+LS//g600zrt2le9KuXt0rWfDz5Q==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "baseline-browser-mapping": "dist/cli.js" - } - }, - "node_modules/binary-extensions": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/binary-extensions/-/binary-extensions-2.3.0.tgz", - "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/brace-expansion": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-2.0.2.tgz", - "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "license": "MIT", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/browserslist": { - "version": "4.26.3", - "resolved": "https://registry.npmmirror.com/browserslist/-/browserslist-4.26.3.tgz", - "integrity": "sha512-lAUU+02RFBuCKQPj/P6NgjlbCnLBMp4UtgTx7vNHd3XSIJF87s9a5rA3aH2yw3GS9DqZAUbOtZdCCiZeVRqt0w==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "baseline-browser-mapping": "^2.8.9", - "caniuse-lite": "^1.0.30001746", - "electron-to-chromium": "^1.5.227", - "node-releases": "^2.0.21", - "update-browserslist-db": "^1.1.3" - }, - "bin": { - "browserslist": "cli.js" - }, - "engines": { - "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" - } - }, - "node_modules/call-bind-apply-helpers": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", - "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/callsites": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/callsites/-/callsites-3.1.0.tgz", - "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/camelcase-css": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/camelcase-css/-/camelcase-css-2.0.1.tgz", - "integrity": "sha512-QOSvevhslijgYwRx6Rv7zKdMF8lbRmx+uQGx2+vDc+KI/eBnsy9kit5aj23AgGu3pa4t9AgwbnXWqS+iOY+2aA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/caniuse-lite": { - "version": "1.0.30001749", - "resolved": "https://registry.npmmirror.com/caniuse-lite/-/caniuse-lite-1.0.30001749.tgz", - "integrity": "sha512-0rw2fJOmLfnzCRbkm8EyHL8SvI2Apu5UbnQuTsJ0ClgrH8hcwFooJ1s5R0EP8o8aVrFu8++ae29Kt9/gZAZp/Q==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/caniuse-lite" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "CC-BY-4.0" - }, - "node_modules/canvg": { - "version": "3.0.11", - "resolved": "https://registry.npmmirror.com/canvg/-/canvg-3.0.11.tgz", - "integrity": "sha512-5ON+q7jCTgMp9cjpu4Jo6XbvfYwSB2Ow3kzHKfIyJfaCAOHLbdKPQqGKgfED/R5B+3TFFfe8pegYA+b423SRyA==", - "license": "MIT", - "optional": true, - "dependencies": { - "@babel/runtime": "^7.12.5", - "@types/raf": "^3.4.0", - "core-js": "^3.8.3", - "raf": "^3.4.1", - "regenerator-runtime": "^0.13.7", - "rgbcolor": "^1.0.1", - "stackblur-canvas": "^2.0.0", - "svg-pathdata": "^6.0.3" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/ccount": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/ccount/-/ccount-2.0.1.tgz", - "integrity": "sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/cfb": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/cfb/-/cfb-1.2.2.tgz", - "integrity": "sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==", - "license": "Apache-2.0", - "dependencies": { - "adler-32": "~1.3.0", - "crc-32": "~1.2.0" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmmirror.com/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/character-entities": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/character-entities/-/character-entities-2.0.2.tgz", - "integrity": "sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-html4": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/character-entities-html4/-/character-entities-html4-2.1.0.tgz", - "integrity": "sha512-1v7fgQRj6hnSwFpq1Eu0ynr/CDEw0rXo2B61qXrLNdHZmPKgb7fqS1a2JwF0rISo9q77jDI8VMEHoApn8qDoZA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-entities-legacy": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/character-entities-legacy/-/character-entities-legacy-3.0.0.tgz", - "integrity": "sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/character-reference-invalid": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/character-reference-invalid/-/character-reference-invalid-2.0.1.tgz", - "integrity": "sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/chokidar": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/chokidar/-/chokidar-3.6.0.tgz", - "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "anymatch": "~3.1.2", - "braces": "~3.0.2", - "glob-parent": "~5.1.2", - "is-binary-path": "~2.1.0", - "is-glob": "~4.0.1", - "normalize-path": "~3.0.0", - "readdirp": "~3.6.0" - }, - "engines": { - "node": ">= 8.10.0" - }, - "funding": { - "url": "https://paulmillr.com/funding/" - }, - "optionalDependencies": { - "fsevents": "~2.3.2" - } - }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/classnames": { - "version": "2.5.1", - "resolved": "https://registry.npmmirror.com/classnames/-/classnames-2.5.1.tgz", - "integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow==", - "license": "MIT" - }, - "node_modules/codepage": { - "version": "1.15.0", - "resolved": "https://registry.npmmirror.com/codepage/-/codepage-1.15.0.tgz", - "integrity": "sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true, - "license": "MIT" - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", - "license": "MIT", - "dependencies": { - "delayed-stream": "~1.0.0" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/comma-separated-tokens": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/comma-separated-tokens/-/comma-separated-tokens-2.0.3.tgz", - "integrity": "sha512-Fu4hJdvzeylCfQPp9SGWidpzrMs7tTrlu6Vb8XGaRGck8QSNZJJp538Wrb60Lax4fPwR64ViY468OIUTbRlGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/commander": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/commander/-/commander-4.1.1.tgz", - "integrity": "sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/compute-scroll-into-view": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/compute-scroll-into-view/-/compute-scroll-into-view-3.1.1.tgz", - "integrity": "sha512-VRhuHOLoKYOy4UbilLbUzbYg93XLjv2PncJC50EuTWPA3gaja1UjBsUP/D/9/juV3vQFr6XBEzn9KCAHdUvOHw==", - "license": "MIT" - }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmmirror.com/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true, - "license": "MIT" - }, - "node_modules/convert-source-map": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/convert-source-map/-/convert-source-map-2.0.0.tgz", - "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true, - "license": "MIT" - }, - "node_modules/copy-to-clipboard": { - "version": "3.3.3", - "resolved": "https://registry.npmmirror.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz", - "integrity": "sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA==", - "license": "MIT", - "dependencies": { - "toggle-selection": "^1.0.6" - } - }, - "node_modules/core-js": { - "version": "3.46.0", - "resolved": "https://registry.npmmirror.com/core-js/-/core-js-3.46.0.tgz", - "integrity": "sha512-vDMm9B0xnqqZ8uSBpZ8sNtRtOdmfShrvT6h2TuQGLs0Is+cR0DYbj/KWP6ALVNbWPpqA/qPLoOuppJN07humpA==", - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/core-js" - } - }, - "node_modules/crc-32": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/crc-32/-/crc-32-1.2.2.tgz", - "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", - "license": "Apache-2.0", - "bin": { - "crc32": "bin/crc32.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/cross-spawn": { - "version": "7.0.6", - "resolved": "https://registry.npmmirror.com/cross-spawn/-/cross-spawn-7.0.6.tgz", - "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", - "dev": true, - "license": "MIT", - "dependencies": { - "path-key": "^3.1.0", - "shebang-command": "^2.0.0", - "which": "^2.0.1" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/css-line-break": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz", - "integrity": "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w==", - "license": "MIT", - "dependencies": { - "utrie": "^1.0.2" - } - }, - "node_modules/cssesc": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/cssesc/-/cssesc-3.0.0.tgz", - "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", - "dev": true, - "license": "MIT", - "bin": { - "cssesc": "bin/cssesc" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/csstype": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/csstype/-/csstype-3.1.3.tgz", - "integrity": "sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==", - "license": "MIT" - }, - "node_modules/dayjs": { - "version": "1.11.18", - "resolved": "https://registry.npmmirror.com/dayjs/-/dayjs-1.11.18.tgz", - "integrity": "sha512-zFBQ7WFRvVRhKcWoUh+ZA1g2HVgUbsZm9sbddh8EC5iv93sui8DVVz1Npvz+r6meo9VKfa8NyLWBsQK1VvIKPA==", - "license": "MIT" - }, - "node_modules/debug": { - "version": "4.4.3", - "resolved": "https://registry.npmmirror.com/debug/-/debug-4.4.3.tgz", - "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", - "license": "MIT", - "dependencies": { - "ms": "^2.1.3" - }, - "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } - } - }, - "node_modules/decode-named-character-reference": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/decode-named-character-reference/-/decode-named-character-reference-1.2.0.tgz", - "integrity": "sha512-c6fcElNV6ShtZXmsgNgFFV5tVX2PaV4g+MOAkb8eXHvn6sryJBrZa9r0zV6+dtTyoCKxtDy5tyQ5ZwQuidtd+Q==", - "license": "MIT", - "dependencies": { - "character-entities": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmmirror.com/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "license": "MIT", - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/dequal": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/dequal/-/dequal-2.0.3.tgz", - "integrity": "sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/devlop": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/devlop/-/devlop-1.1.0.tgz", - "integrity": "sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==", - "license": "MIT", - "dependencies": { - "dequal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/didyoumean": { - "version": "1.2.2", - "resolved": "https://registry.npmmirror.com/didyoumean/-/didyoumean-1.2.2.tgz", - "integrity": "sha512-gxtyfqMg7GKyhQmb056K7M3xszy/myH8w+B4RT+QXBQsvAOdc3XymqDDPHx1BgPgsdAA5SIifona89YtRATDzw==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/dlv": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/dlv/-/dlv-1.1.3.tgz", - "integrity": "sha512-+HlytyjlPKnIG8XuRG8WvmBP8xs8P71y+SKKS6ZXWoEgLuePxtDoUEiH7WkdePWrQ5JBpE6aoVqfZfJUQkjXwA==", - "dev": true, - "license": "MIT" - }, - "node_modules/dompurify": { - "version": "3.3.0", - "resolved": "https://registry.npmmirror.com/dompurify/-/dompurify-3.3.0.tgz", - "integrity": "sha512-r+f6MYR1gGN1eJv0TVQbhA7if/U7P87cdPl3HN5rikqaBSBxLiCb/b9O+2eG0cxz0ghyU+mU1QkbsOwERMYlWQ==", - "license": "(MPL-2.0 OR Apache-2.0)", - "optional": true, - "optionalDependencies": { - "@types/trusted-types": "^2.0.7" - } - }, - "node_modules/dunder-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/dunder-proto/-/dunder-proto-1.0.1.tgz", - "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.1", - "es-errors": "^1.3.0", - "gopd": "^1.2.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/eastasianwidth": { - "version": "0.2.0", - "resolved": "https://registry.npmmirror.com/eastasianwidth/-/eastasianwidth-0.2.0.tgz", - "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true, - "license": "MIT" - }, - "node_modules/electron-to-chromium": { - "version": "1.5.234", - "resolved": "https://registry.npmmirror.com/electron-to-chromium/-/electron-to-chromium-1.5.234.tgz", - "integrity": "sha512-RXfEp2x+VRYn8jbKfQlRImzoJU01kyDvVPBmG39eU2iuRVhuS6vQNocB8J0/8GrIMLnPzgz4eW6WiRnJkTuNWg==", - "dev": true, - "license": "ISC" - }, - "node_modules/emoji-regex": { - "version": "9.2.2", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-9.2.2.tgz", - "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true, - "license": "MIT" - }, - "node_modules/entities": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/entities/-/entities-6.0.1.tgz", - "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.12" - }, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, - "node_modules/es-define-property": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/es-define-property/-/es-define-property-1.0.1.tgz", - "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-errors": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/es-errors/-/es-errors-1.3.0.tgz", - "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-object-atoms": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz", - "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/es-set-tostringtag": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", - "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", - "license": "MIT", - "dependencies": { - "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.6", - "has-tostringtag": "^1.0.2", - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/esbuild": { - "version": "0.25.10", - "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.25.10.tgz", - "integrity": "sha512-9RiGKvCwaqxO2owP61uQ4BgNborAQskMR6QusfWzQqv7AZOg5oGehdY2pRJMTKuwxd1IDBP4rSbI5lHzU7SMsQ==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "bin": { - "esbuild": "bin/esbuild" - }, - "engines": { - "node": ">=18" - }, - "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.10", - "@esbuild/android-arm": "0.25.10", - "@esbuild/android-arm64": "0.25.10", - "@esbuild/android-x64": "0.25.10", - "@esbuild/darwin-arm64": "0.25.10", - "@esbuild/darwin-x64": "0.25.10", - "@esbuild/freebsd-arm64": "0.25.10", - "@esbuild/freebsd-x64": "0.25.10", - "@esbuild/linux-arm": "0.25.10", - "@esbuild/linux-arm64": "0.25.10", - "@esbuild/linux-ia32": "0.25.10", - "@esbuild/linux-loong64": "0.25.10", - "@esbuild/linux-mips64el": "0.25.10", - "@esbuild/linux-ppc64": "0.25.10", - "@esbuild/linux-riscv64": "0.25.10", - "@esbuild/linux-s390x": "0.25.10", - "@esbuild/linux-x64": "0.25.10", - "@esbuild/netbsd-arm64": "0.25.10", - "@esbuild/netbsd-x64": "0.25.10", - "@esbuild/openbsd-arm64": "0.25.10", - "@esbuild/openbsd-x64": "0.25.10", - "@esbuild/openharmony-arm64": "0.25.10", - "@esbuild/sunos-x64": "0.25.10", - "@esbuild/win32-arm64": "0.25.10", - "@esbuild/win32-ia32": "0.25.10", - "@esbuild/win32-x64": "0.25.10" - } - }, - "node_modules/escalade": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/escalade/-/escalade-3.2.0.tgz", - "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/eslint": { - "version": "9.37.0", - "resolved": "https://registry.npmmirror.com/eslint/-/eslint-9.37.0.tgz", - "integrity": "sha512-XyLmROnACWqSxiGYArdef1fItQd47weqB7iwtfr9JHwRrqIXZdcFMvvEcL9xHCmL0SNsOvF0c42lWyM1U5dgig==", - "dev": true, - "license": "MIT", - "dependencies": { - "@eslint-community/eslint-utils": "^4.8.0", - "@eslint-community/regexpp": "^4.12.1", - "@eslint/config-array": "^0.21.0", - "@eslint/config-helpers": "^0.4.0", - "@eslint/core": "^0.16.0", - "@eslint/eslintrc": "^3.3.1", - "@eslint/js": "9.37.0", - "@eslint/plugin-kit": "^0.4.0", - "@humanfs/node": "^0.16.6", - "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.4.2", - "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", - "ajv": "^6.12.4", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.6", - "debug": "^4.3.2", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.4.0", - "eslint-visitor-keys": "^4.2.1", - "espree": "^10.4.0", - "esquery": "^1.5.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^8.0.0", - "find-up": "^5.0.0", - "glob-parent": "^6.0.2", - "ignore": "^5.2.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.3" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://eslint.org/donate" - }, - "peerDependencies": { - "jiti": "*" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - } - } - }, - "node_modules/eslint-plugin-react-hooks": { - "version": "5.2.0", - "resolved": "https://registry.npmmirror.com/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-5.2.0.tgz", - "integrity": "sha512-+f15FfK64YQwZdJNELETdn5ibXEUQmW1DZL6KXhNnc2heoy/sg9VJJeT7n8TlMWouzWqSWavFkIhHyIbIAEapg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "peerDependencies": { - "eslint": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0" - } - }, - "node_modules/eslint-plugin-react-refresh": { - "version": "0.4.23", - "resolved": "https://registry.npmmirror.com/eslint-plugin-react-refresh/-/eslint-plugin-react-refresh-0.4.23.tgz", - "integrity": "sha512-G4j+rv0NmbIR45kni5xJOrYvCtyD3/7LjpVH8MPPcudXDcNu8gv+4ATTDXTtbRR8rTCM5HxECvCSsRmxKnWDsA==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "eslint": ">=8.40" - } - }, - "node_modules/eslint-scope": { - "version": "8.4.0", - "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-8.4.0.tgz", - "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "3.4.3", - "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", - "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/brace-expansion": { - "version": "1.1.12", - "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.12.tgz", - "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/eslint/node_modules/ignore": { - "version": "5.3.2", - "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.3.2.tgz", - "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/espree": { - "version": "10.4.0", - "resolved": "https://registry.npmmirror.com/espree/-/espree-10.4.0.tgz", - "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "acorn": "^8.15.0", - "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.2.1" - }, - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", - "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", - "dev": true, - "license": "Apache-2.0", - "engines": { - "node": "^18.18.0 || ^20.9.0 || >=21.1.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/esquery": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/esquery/-/esquery-1.6.0.tgz", - "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", - "dev": true, - "license": "BSD-3-Clause", - "dependencies": { - "estraverse": "^5.1.0" - }, - "engines": { - "node": ">=0.10" - } - }, - "node_modules/esrecurse": { - "version": "4.3.0", - "resolved": "https://registry.npmmirror.com/esrecurse/-/esrecurse-4.3.0.tgz", - "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "estraverse": "^5.2.0" - }, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmmirror.com/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=4.0" - } - }, - "node_modules/estree-util-is-identifier-name": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/estree-util-is-identifier-name/-/estree-util-is-identifier-name-3.0.0.tgz", - "integrity": "sha512-hFtqIDZTIUZ9BXLb8y4pYGyk6+wekIivNVTcmvk8NoOh+VeRn5y6cEHzbURrWbfp1fIqdVipilzj+lfaadNZmg==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "license": "BSD-2-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extend": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/extend/-/extend-3.0.2.tgz", - "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", - "license": "MIT" - }, - "node_modules/fast-deep-equal": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-glob": { - "version": "3.3.3", - "resolved": "https://registry.npmmirror.com/fast-glob/-/fast-glob-3.3.3.tgz", - "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", - "dev": true, - "license": "MIT", - "dependencies": { - "@nodelib/fs.stat": "^2.0.2", - "@nodelib/fs.walk": "^1.2.3", - "glob-parent": "^5.1.2", - "merge2": "^1.3.0", - "micromatch": "^4.0.8" - }, - "engines": { - "node": ">=8.6.0" - } - }, - "node_modules/fast-glob/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/fast-json-stable-stringify": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", - "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmmirror.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true, - "license": "MIT" - }, - "node_modules/fast-png": { - "version": "6.4.0", - "resolved": "https://registry.npmmirror.com/fast-png/-/fast-png-6.4.0.tgz", - "integrity": "sha512-kAqZq1TlgBjZcLr5mcN6NP5Rv4V2f22z00c3g8vRrwkcqjerx7BEhPbOnWCPqaHUl2XWQBJQvOT/FQhdMT7X/Q==", - "license": "MIT", - "dependencies": { - "@types/pako": "^2.0.3", - "iobuffer": "^5.3.2", - "pako": "^2.1.0" - } - }, - "node_modules/fastq": { - "version": "1.19.1", - "resolved": "https://registry.npmmirror.com/fastq/-/fastq-1.19.1.tgz", - "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", - "dev": true, - "license": "ISC", - "dependencies": { - "reusify": "^1.0.4" - } - }, - "node_modules/fault": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/fault/-/fault-1.0.4.tgz", - "integrity": "sha512-CJ0HCB5tL5fYTEA7ToAq5+kTwd++Borf1/bifxd9iT70QcXr4MRrO3Llf8Ifs70q+SJcGHFtnIE/Nw6giCtECA==", - "license": "MIT", - "dependencies": { - "format": "^0.2.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/fflate": { - "version": "0.8.2", - "resolved": "https://registry.npmmirror.com/fflate/-/fflate-0.8.2.tgz", - "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==", - "license": "MIT" - }, - "node_modules/file-entry-cache": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/file-entry-cache/-/file-entry-cache-8.0.0.tgz", - "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "flat-cache": "^4.0.0" - }, - "engines": { - "node": ">=16.0.0" - } - }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmmirror.com/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "license": "MIT", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "license": "MIT", - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/flat-cache": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/flat-cache/-/flat-cache-4.0.1.tgz", - "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", - "dev": true, - "license": "MIT", - "dependencies": { - "flatted": "^3.2.9", - "keyv": "^4.5.4" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/flatted": { - "version": "3.3.3", - "resolved": "https://registry.npmmirror.com/flatted/-/flatted-3.3.3.tgz", - "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", - "dev": true, - "license": "ISC" - }, - "node_modules/follow-redirects": { - "version": "1.15.11", - "resolved": "https://registry.npmmirror.com/follow-redirects/-/follow-redirects-1.15.11.tgz", - "integrity": "sha512-deG2P0JfjrTxl50XGCDyfI97ZGVCxIpfKYmfyrQ54n5FO/0gfIES8C/Psl6kWVDolizcaaxZJnTS0QSMxvnsBQ==", - "funding": [ - { - "type": "individual", - "url": "https://github.com/sponsors/RubenVerborgh" - } - ], - "license": "MIT", - "engines": { - "node": ">=4.0" - }, - "peerDependenciesMeta": { - "debug": { - "optional": true - } - } - }, - "node_modules/foreground-child": { - "version": "3.3.1", - "resolved": "https://registry.npmmirror.com/foreground-child/-/foreground-child-3.3.1.tgz", - "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", - "dev": true, - "license": "ISC", - "dependencies": { - "cross-spawn": "^7.0.6", - "signal-exit": "^4.0.1" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/form-data": { - "version": "4.0.4", - "resolved": "https://registry.npmmirror.com/form-data/-/form-data-4.0.4.tgz", - "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", - "license": "MIT", - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "es-set-tostringtag": "^2.1.0", - "hasown": "^2.0.2", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/format": { - "version": "0.2.2", - "resolved": "https://registry.npmmirror.com/format/-/format-0.2.2.tgz", - "integrity": "sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww==", - "engines": { - "node": ">=0.4.x" - } - }, - "node_modules/frac": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/frac/-/frac-1.1.2.tgz", - "integrity": "sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/fraction.js": { - "version": "4.3.7", - "resolved": "https://registry.npmmirror.com/fraction.js/-/fraction.js-4.3.7.tgz", - "integrity": "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==", - "dev": true, - "license": "MIT", - "engines": { - "node": "*" - }, - "funding": { - "type": "patreon", - "url": "https://github.com/sponsors/rawify" - } - }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmmirror.com/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, - "node_modules/function-bind": { - "version": "1.1.2", - "resolved": "https://registry.npmmirror.com/function-bind/-/function-bind-1.1.2.tgz", - "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", - "license": "MIT", - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/gensync": { - "version": "1.0.0-beta.2", - "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz", - "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/get-intrinsic": { - "version": "1.3.0", - "resolved": "https://registry.npmmirror.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz", - "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", - "license": "MIT", - "dependencies": { - "call-bind-apply-helpers": "^1.0.2", - "es-define-property": "^1.0.1", - "es-errors": "^1.3.0", - "es-object-atoms": "^1.1.1", - "function-bind": "^1.1.2", - "get-proto": "^1.0.1", - "gopd": "^1.2.0", - "has-symbols": "^1.1.0", - "hasown": "^2.0.2", - "math-intrinsics": "^1.1.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/get-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/get-proto/-/get-proto-1.0.1.tgz", - "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", - "license": "MIT", - "dependencies": { - "dunder-proto": "^1.0.1", - "es-object-atoms": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/glob": { - "version": "10.4.5", - "resolved": "https://registry.npmmirror.com/glob/-/glob-10.4.5.tgz", - "integrity": "sha512-7Bv8RF0k6xjo7d4A/PxYLbUCfb6c+Vpd2/mB2yRDlew7Jb5hEXiCD9ibfO7wpk8i4sevK6DFny9h7EYbM3/sHg==", - "dev": true, - "license": "ISC", - "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" - }, - "bin": { - "glob": "dist/esm/bin.mjs" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmmirror.com/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "license": "ISC", - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/globals": { - "version": "14.0.0", - "resolved": "https://registry.npmmirror.com/globals/-/globals-14.0.0.tgz", - "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/gopd": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/gopd/-/gopd-1.2.0.tgz", - "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/graphemer": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/graphemer/-/graphemer-1.4.0.tgz", - "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", - "dev": true, - "license": "MIT" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/has-symbols": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/has-symbols/-/has-symbols-1.1.0.tgz", - "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-tostringtag": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz", - "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", - "license": "MIT", - "dependencies": { - "has-symbols": "^1.0.3" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/hasown": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/hasown/-/hasown-2.0.2.tgz", - "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", - "license": "MIT", - "dependencies": { - "function-bind": "^1.1.2" - }, - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/hast-util-from-parse5": { - "version": "8.0.3", - "resolved": "https://registry.npmmirror.com/hast-util-from-parse5/-/hast-util-from-parse5-8.0.3.tgz", - "integrity": "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "devlop": "^1.0.0", - "hastscript": "^9.0.0", - "property-information": "^7.0.0", - "vfile": "^6.0.0", - "vfile-location": "^5.0.0", - "web-namespaces": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/hast-util-parse-selector": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/hast-util-parse-selector/-/hast-util-parse-selector-4.0.0.tgz", - "integrity": "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-from-parse5/node_modules/hastscript": { - "version": "9.0.1", - "resolved": "https://registry.npmmirror.com/hastscript/-/hastscript-9.0.1.tgz", - "integrity": "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "hast-util-parse-selector": "^4.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-parse-selector": { - "version": "2.2.5", - "resolved": "https://registry.npmmirror.com/hast-util-parse-selector/-/hast-util-parse-selector-2.2.5.tgz", - "integrity": "sha512-7j6mrk/qqkSehsM92wQjdIgWM2/BW61u/53G6xmC8i1OmEdKLHbk419QKQUjz6LglWsfqoiHmyMRkP1BGjecNQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-raw": { - "version": "9.1.0", - "resolved": "https://registry.npmmirror.com/hast-util-raw/-/hast-util-raw-9.1.0.tgz", - "integrity": "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "@ungap/structured-clone": "^1.0.0", - "hast-util-from-parse5": "^8.0.0", - "hast-util-to-parse5": "^8.0.0", - "html-void-elements": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "parse5": "^7.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-jsx-runtime": { - "version": "2.3.6", - "resolved": "https://registry.npmmirror.com/hast-util-to-jsx-runtime/-/hast-util-to-jsx-runtime-2.3.6.tgz", - "integrity": "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg==", - "license": "MIT", - "dependencies": { - "@types/estree": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/unist": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "estree-util-is-identifier-name": "^3.0.0", - "hast-util-whitespace": "^3.0.0", - "mdast-util-mdx-expression": "^2.0.0", - "mdast-util-mdx-jsx": "^3.0.0", - "mdast-util-mdxjs-esm": "^2.0.0", - "property-information": "^7.0.0", - "space-separated-tokens": "^2.0.0", - "style-to-js": "^1.0.0", - "unist-util-position": "^5.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/hast-util-to-parse5/-/hast-util-to-parse5-8.0.0.tgz", - "integrity": "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "comma-separated-tokens": "^2.0.0", - "devlop": "^1.0.0", - "property-information": "^6.0.0", - "space-separated-tokens": "^2.0.0", - "web-namespaces": "^2.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hast-util-to-parse5/node_modules/property-information": { - "version": "6.5.0", - "resolved": "https://registry.npmmirror.com/property-information/-/property-information-6.5.0.tgz", - "integrity": "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hast-util-whitespace": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/hast-util-whitespace/-/hast-util-whitespace-3.0.0.tgz", - "integrity": "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/hastscript/-/hastscript-6.0.0.tgz", - "integrity": "sha512-nDM6bvd7lIqDUiYEiu5Sl/+6ReP0BMk/2f4U/Rooccxkj0P5nm+acM5PrGJ/t5I8qPGiqZSE6hVAwZEdZIvP4w==", - "license": "MIT", - "dependencies": { - "@types/hast": "^2.0.0", - "comma-separated-tokens": "^1.0.0", - "hast-util-parse-selector": "^2.0.0", - "property-information": "^5.0.0", - "space-separated-tokens": "^1.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/hastscript/node_modules/@types/hast": { - "version": "2.3.10", - "resolved": "https://registry.npmmirror.com/@types/hast/-/hast-2.3.10.tgz", - "integrity": "sha512-McWspRw8xx8J9HurkVBfYj0xKoE25tOFlHGdx4MJ5xORQrMGZNqJhVQWaIbm6Oyla5kYOXtDiopzKRJzEOkwJw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2" - } - }, - "node_modules/hastscript/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/hastscript/node_modules/comma-separated-tokens": { - "version": "1.0.8", - "resolved": "https://registry.npmmirror.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz", - "integrity": "sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hastscript/node_modules/property-information": { - "version": "5.6.0", - "resolved": "https://registry.npmmirror.com/property-information/-/property-information-5.6.0.tgz", - "integrity": "sha512-YUHSPk+A30YPv+0Qf8i9Mbfe/C0hdPXk1s1jPVToV8pk8BQtpw10ct89Eo7OWkutrwqvT0eicAxlOg3dOAu8JA==", - "license": "MIT", - "dependencies": { - "xtend": "^4.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/hastscript/node_modules/space-separated-tokens": { - "version": "1.1.5", - "resolved": "https://registry.npmmirror.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz", - "integrity": "sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/highlight.js": { - "version": "10.7.3", - "resolved": "https://registry.npmmirror.com/highlight.js/-/highlight.js-10.7.3.tgz", - "integrity": "sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==", - "license": "BSD-3-Clause", - "engines": { - "node": "*" - } - }, - "node_modules/highlightjs-vue": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/highlightjs-vue/-/highlightjs-vue-1.0.0.tgz", - "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", - "license": "CC0-1.0" - }, - "node_modules/html-url-attributes": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/html-url-attributes/-/html-url-attributes-3.0.1.tgz", - "integrity": "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ==", - "license": "MIT", - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/html-void-elements": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/html-void-elements/-/html-void-elements-3.0.0.tgz", - "integrity": "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/html2canvas": { - "version": "1.4.1", - "resolved": "https://registry.npmmirror.com/html2canvas/-/html2canvas-1.4.1.tgz", - "integrity": "sha512-fPU6BHNpsyIhr8yyMpTLLxAbkaK8ArIBcmZIRiBLiDhjeqvXolaEmDGmELFuX9I4xDcaKKcJl+TKZLqruBbmWA==", - "license": "MIT", - "dependencies": { - "css-line-break": "^2.1.0", - "text-segmentation": "^1.0.3" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/ignore": { - "version": "7.0.5", - "resolved": "https://registry.npmmirror.com/ignore/-/ignore-7.0.5.tgz", - "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 4" - } - }, - "node_modules/import-fresh": { - "version": "3.3.1", - "resolved": "https://registry.npmmirror.com/import-fresh/-/import-fresh-3.3.1.tgz", - "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "parent-module": "^1.0.0", - "resolve-from": "^4.0.0" - }, - "engines": { - "node": ">=6" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/imurmurhash": { - "version": "0.1.4", - "resolved": "https://registry.npmmirror.com/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.8.19" - } - }, - "node_modules/inline-style-parser": { - "version": "0.2.4", - "resolved": "https://registry.npmmirror.com/inline-style-parser/-/inline-style-parser-0.2.4.tgz", - "integrity": "sha512-0aO8FkhNZlj/ZIbNi7Lxxr12obT7cL1moPfE4tg1LkX7LlLfC6DeX4l2ZEud1ukP9jNQyNnfzQVqwbwmAATY4Q==", - "license": "MIT" - }, - "node_modules/iobuffer": { - "version": "5.4.0", - "resolved": "https://registry.npmmirror.com/iobuffer/-/iobuffer-5.4.0.tgz", - "integrity": "sha512-DRebOWuqDvxunfkNJAlc3IzWIPD5xVxwUNbHr7xKB8E6aLJxIPfNX3CoMJghcFjpv6RWQsrcJbghtEwSPoJqMA==", - "license": "MIT" - }, - "node_modules/is-alphabetical": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/is-alphabetical/-/is-alphabetical-2.0.1.tgz", - "integrity": "sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-alphanumerical": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/is-alphanumerical/-/is-alphanumerical-2.0.1.tgz", - "integrity": "sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^2.0.0", - "is-decimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-binary-path": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/is-binary-path/-/is-binary-path-2.1.0.tgz", - "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", - "dev": true, - "license": "MIT", - "dependencies": { - "binary-extensions": "^2.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/is-core-module": { - "version": "2.16.1", - "resolved": "https://registry.npmmirror.com/is-core-module/-/is-core-module-2.16.1.tgz", - "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", - "dev": true, - "license": "MIT", - "dependencies": { - "hasown": "^2.0.2" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/is-decimal": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/is-decimal/-/is-decimal-2.0.1.tgz", - "integrity": "sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-extglob": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/is-extglob/-/is-extglob-2.1.1.tgz", - "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-fullwidth-code-point": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", - "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/is-glob": { - "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz", - "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-extglob": "^2.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-hexadecimal": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/is-hexadecimal/-/is-hexadecimal-2.0.1.tgz", - "integrity": "sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmmirror.com/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/is-plain-obj": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/is-plain-obj/-/is-plain-obj-4.1.0.tgz", - "integrity": "sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true, - "license": "ISC" - }, - "node_modules/jackspeak": { - "version": "3.4.3", - "resolved": "https://registry.npmmirror.com/jackspeak/-/jackspeak-3.4.3.tgz", - "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "@isaacs/cliui": "^8.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/jiti": { - "version": "1.21.7", - "resolved": "https://registry.npmmirror.com/jiti/-/jiti-1.21.7.tgz", - "integrity": "sha512-/imKNG4EbWNrVjoNC/1H5/9GFy+tqjGBHCaSsN+P2RnPqjsLmv6UD3Ej+Kj8nBWaRAwyk7kK5ZUc+OEatnTR3A==", - "dev": true, - "license": "MIT", - "bin": { - "jiti": "bin/jiti.js" - } - }, - "node_modules/js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "license": "MIT" - }, - "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "license": "MIT", - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, - "node_modules/jsesc": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-3.1.0.tgz", - "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", - "dev": true, - "license": "MIT", - "bin": { - "jsesc": "bin/jsesc" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmmirror.com/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true, - "license": "MIT" - }, - "node_modules/json-stable-stringify-without-jsonify": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/json2mq": { - "version": "0.2.0", - "resolved": "https://registry.npmmirror.com/json2mq/-/json2mq-0.2.0.tgz", - "integrity": "sha512-SzoRg7ux5DWTII9J2qkrZrqV1gt+rTaoufMxEzXbS26Uid0NwaJd123HcoB80TgubEppxxIGdNxCx50fEoEWQA==", - "license": "MIT", - "dependencies": { - "string-convert": "^0.2.0" - } - }, - "node_modules/json5": { - "version": "2.2.3", - "resolved": "https://registry.npmmirror.com/json5/-/json5-2.2.3.tgz", - "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", - "dev": true, - "license": "MIT", - "bin": { - "json5": "lib/cli.js" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/jspdf": { - "version": "3.0.3", - "resolved": "https://registry.npmmirror.com/jspdf/-/jspdf-3.0.3.tgz", - "integrity": "sha512-eURjAyz5iX1H8BOYAfzvdPfIKK53V7mCpBTe7Kb16PaM8JSXEcUQNBQaiWMI8wY5RvNOPj4GccMjTlfwRBd+oQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.26.9", - "fast-png": "^6.2.0", - "fflate": "^0.8.1" - }, - "optionalDependencies": { - "canvg": "^3.0.11", - "core-js": "^3.6.0", - "dompurify": "^3.2.4", - "html2canvas": "^1.0.0-rc.5" - } - }, - "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmmirror.com/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", - "dev": true, - "license": "MIT", - "dependencies": { - "json-buffer": "3.0.1" - } - }, - "node_modules/levn": { - "version": "0.4.1", - "resolved": "https://registry.npmmirror.com/levn/-/levn-0.4.1.tgz", - "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1", - "type-check": "~0.4.0" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/lilconfig": { - "version": "3.1.3", - "resolved": "https://registry.npmmirror.com/lilconfig/-/lilconfig-3.1.3.tgz", - "integrity": "sha512-/vlFKAoH5Cgt3Ie+JLhRbwOsCQePABiU3tJ1egGvyQ+33R/vcwM2Zl2QR/LzjsBeItPt3oSVXapn+m4nQDvpzw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/antonk52" - } - }, - "node_modules/lines-and-columns": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", - "dev": true, - "license": "MIT" - }, - "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lodash.merge": { - "version": "4.6.2", - "resolved": "https://registry.npmmirror.com/lodash.merge/-/lodash.merge-4.6.2.tgz", - "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/longest-streak": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/longest-streak/-/longest-streak-3.1.0.tgz", - "integrity": "sha512-9Ri+o0JYgehTaVBBDoMqIl8GXtbWg711O3srftcHhZ0dqnETqLaoIK0x17fUw9rFSlK/0NlsKe0Ahhyl5pXE2g==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/loose-envify": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/loose-envify/-/loose-envify-1.4.0.tgz", - "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", - "license": "MIT", - "dependencies": { - "js-tokens": "^3.0.0 || ^4.0.0" - }, - "bin": { - "loose-envify": "cli.js" - } - }, - "node_modules/lowlight": { - "version": "1.20.0", - "resolved": "https://registry.npmmirror.com/lowlight/-/lowlight-1.20.0.tgz", - "integrity": "sha512-8Ktj+prEb1RoCPkEOrPMYUN/nCggB7qAWe3a7OpMjWQkh3l2RD5wKRQ+o8Q8YuI9RG/xs95waaI/E6ym/7NsTw==", - "license": "MIT", - "dependencies": { - "fault": "^1.0.0", - "highlight.js": "~10.7.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/lru-cache": { - "version": "5.1.1", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-5.1.1.tgz", - "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^3.0.2" - } - }, - "node_modules/lucide-react": { - "version": "0.562.0", - "resolved": "https://registry.npmmirror.com/lucide-react/-/lucide-react-0.562.0.tgz", - "integrity": "sha512-82hOAu7y0dbVuFfmO4bYF1XEwYk/mEbM5E+b1jgci/udUBEE/R7LF5Ip0CCEmXe8AybRM8L+04eP+LGZeDvkiw==", - "license": "ISC", - "peerDependencies": { - "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" - } - }, - "node_modules/markdown-table": { - "version": "3.0.4", - "resolved": "https://registry.npmmirror.com/markdown-table/-/markdown-table-3.0.4.tgz", - "integrity": "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/math-intrinsics": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz", - "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", - "license": "MIT", - "engines": { - "node": ">= 0.4" - } - }, - "node_modules/mdast-util-find-and-replace": { - "version": "3.0.2", - "resolved": "https://registry.npmmirror.com/mdast-util-find-and-replace/-/mdast-util-find-and-replace-3.0.2.tgz", - "integrity": "sha512-Tmd1Vg/m3Xz43afeNxDIhWRtFZgM2VLyaf4vSTYwudTyeuTneoL3qtWMA5jeLyz/O1vDJmmV4QuScFCA2tBPwg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "escape-string-regexp": "^5.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-find-and-replace/node_modules/escape-string-regexp": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/escape-string-regexp/-/escape-string-regexp-5.0.0.tgz", - "integrity": "sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==", - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/mdast-util-from-markdown": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/mdast-util-from-markdown/-/mdast-util-from-markdown-2.0.2.tgz", - "integrity": "sha512-uZhTV/8NBuw0WHkPTrCqDOl0zVe1BIng5ZtHoDk49ME1qqcjYmmLmOf0gELgcRMxN4w2iuIeVso5/6QymSrgmA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark": "^4.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/mdast-util-gfm/-/mdast-util-gfm-3.1.0.tgz", - "integrity": "sha512-0ulfdQOM3ysHhCJ1p06l0b0VKlhU0wuQs3thxZQagjcjPrlFRqY215uZGHHJan9GEAXd9MbfPjFJz+qMkVR6zQ==", - "license": "MIT", - "dependencies": { - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-gfm-autolink-literal": "^2.0.0", - "mdast-util-gfm-footnote": "^2.0.0", - "mdast-util-gfm-strikethrough": "^2.0.0", - "mdast-util-gfm-table": "^2.0.0", - "mdast-util-gfm-task-list-item": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-autolink-literal": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/mdast-util-gfm-autolink-literal/-/mdast-util-gfm-autolink-literal-2.0.1.tgz", - "integrity": "sha512-5HVP2MKaP6L+G6YaxPNjuL0BPrq9orG3TsrZ9YXbA3vDw/ACI4MEsnoDpn6ZNm7GnZgtAcONJyPhOP8tNJQavQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "ccount": "^2.0.0", - "devlop": "^1.0.0", - "mdast-util-find-and-replace": "^3.0.0", - "micromark-util-character": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/mdast-util-gfm-footnote/-/mdast-util-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-sqpDWlsHn7Ac9GNZQMeUzPQSMzR6Wv0WKRNvQRg0KqHh02fpTz69Qc1QSseNX29bhz1ROIyNyxExfawVKTm1GQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-strikethrough": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/mdast-util-gfm-strikethrough/-/mdast-util-gfm-strikethrough-2.0.0.tgz", - "integrity": "sha512-mKKb915TF+OC5ptj5bJ7WFRPdYtuHv0yTRxK2tJvi+BDqbkiG7h7u/9SI89nRAYcmap2xHQL9D+QG/6wSrTtXg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-table": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/mdast-util-gfm-table/-/mdast-util-gfm-table-2.0.0.tgz", - "integrity": "sha512-78UEvebzz/rJIxLvE7ZtDd/vIQ0RHv+3Mh5DR96p7cS7HsBhYIICDBCu8csTNWNO6tBWfqXPWekRuj2FNOGOZg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "markdown-table": "^3.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-gfm-task-list-item": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/mdast-util-gfm-task-list-item/-/mdast-util-gfm-task-list-item-2.0.0.tgz", - "integrity": "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-expression": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/mdast-util-mdx-expression/-/mdast-util-mdx-expression-2.0.1.tgz", - "integrity": "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdx-jsx": { - "version": "3.2.0", - "resolved": "https://registry.npmmirror.com/mdast-util-mdx-jsx/-/mdast-util-mdx-jsx-3.2.0.tgz", - "integrity": "sha512-lj/z8v0r6ZtsN/cGNNtemmmfoLAFZnjMbNyLzBafjzikOM+glrjNHPlf6lQDOTccj9n5b0PPihEBbhneMyGs1Q==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "ccount": "^2.0.0", - "devlop": "^1.1.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0", - "parse-entities": "^4.0.0", - "stringify-entities": "^4.0.0", - "unist-util-stringify-position": "^4.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-mdxjs-esm": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/mdast-util-mdxjs-esm/-/mdast-util-mdxjs-esm-2.0.1.tgz", - "integrity": "sha512-EcmOpxsZ96CvlP03NghtH1EsLtr0n9Tm4lPUJUBccV9RwUOneqSycg19n5HGzCf+10LozMRSObtVr3ee1WoHtg==", - "license": "MIT", - "dependencies": { - "@types/estree-jsx": "^1.0.0", - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "mdast-util-from-markdown": "^2.0.0", - "mdast-util-to-markdown": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-phrasing": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/mdast-util-phrasing/-/mdast-util-phrasing-4.1.0.tgz", - "integrity": "sha512-TqICwyvJJpBwvGAMZjj4J2n0X8QWp21b9l0o7eXyVJ25YNWYbJDVIyD1bZXE6WtV6RmKJVYmQAKWa0zWOABz2w==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-hast": { - "version": "13.2.0", - "resolved": "https://registry.npmmirror.com/mdast-util-to-hast/-/mdast-util-to-hast-13.2.0.tgz", - "integrity": "sha512-QGYKEuUsYT9ykKBCMOEDLsU5JRObWQusAolFMeko/tYPufNkRffBAQjIE+99jbA87xv6FgmjLtwjh9wBWajwAA==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "@ungap/structured-clone": "^1.0.0", - "devlop": "^1.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "trim-lines": "^3.0.0", - "unist-util-position": "^5.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-markdown": { - "version": "2.1.2", - "resolved": "https://registry.npmmirror.com/mdast-util-to-markdown/-/mdast-util-to-markdown-2.1.2.tgz", - "integrity": "sha512-xj68wMTvGXVOKonmog6LwyJKrYXZPvlwabaryTjLh9LuvovB/KAH+kvi8Gjj+7rJjsFi23nkUxRQv1KqSroMqA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "@types/unist": "^3.0.0", - "longest-streak": "^3.0.0", - "mdast-util-phrasing": "^4.0.0", - "mdast-util-to-string": "^4.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-decode-string": "^2.0.0", - "unist-util-visit": "^5.0.0", - "zwitch": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/mdast-util-to-string": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz", - "integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/merge2": { - "version": "1.4.1", - "resolved": "https://registry.npmmirror.com/merge2/-/merge2-1.4.1.tgz", - "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, - "node_modules/micromark": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/micromark/-/micromark-4.0.2.tgz", - "integrity": "sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "@types/debug": "^4.0.0", - "debug": "^4.0.0", - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-core-commonmark": { - "version": "2.0.3", - "resolved": "https://registry.npmmirror.com/micromark-core-commonmark/-/micromark-core-commonmark-2.0.3.tgz", - "integrity": "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "devlop": "^1.0.0", - "micromark-factory-destination": "^2.0.0", - "micromark-factory-label": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-factory-title": "^2.0.0", - "micromark-factory-whitespace": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-html-tag-name": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-subtokenize": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-extension-gfm": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/micromark-extension-gfm/-/micromark-extension-gfm-3.0.0.tgz", - "integrity": "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w==", - "license": "MIT", - "dependencies": { - "micromark-extension-gfm-autolink-literal": "^2.0.0", - "micromark-extension-gfm-footnote": "^2.0.0", - "micromark-extension-gfm-strikethrough": "^2.0.0", - "micromark-extension-gfm-table": "^2.0.0", - "micromark-extension-gfm-tagfilter": "^2.0.0", - "micromark-extension-gfm-task-list-item": "^2.0.0", - "micromark-util-combine-extensions": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-autolink-literal": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/micromark-extension-gfm-autolink-literal/-/micromark-extension-gfm-autolink-literal-2.1.0.tgz", - "integrity": "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==", - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-footnote": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/micromark-extension-gfm-footnote/-/micromark-extension-gfm-footnote-2.1.0.tgz", - "integrity": "sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-core-commonmark": "^2.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-normalize-identifier": "^2.0.0", - "micromark-util-sanitize-uri": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-strikethrough": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/micromark-extension-gfm-strikethrough/-/micromark-extension-gfm-strikethrough-2.1.0.tgz", - "integrity": "sha512-ADVjpOOkjz1hhkZLlBiYA9cR2Anf8F4HqZUO6e5eDcPQd0Txw5fxLzzxnEkSkfnD0wziSGiv7sYhk/ktvbf1uw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-classify-character": "^2.0.0", - "micromark-util-resolve-all": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-table": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/micromark-extension-gfm-table/-/micromark-extension-gfm-table-2.1.1.tgz", - "integrity": "sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-tagfilter": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/micromark-extension-gfm-tagfilter/-/micromark-extension-gfm-tagfilter-2.0.0.tgz", - "integrity": "sha512-xHlTOmuCSotIA8TW1mDIM6X2O1SiX5P9IuDtqGonFhEK0qgRI4yeC6vMxEV2dgyr2TiD+2PQ10o+cOhdVAcwfg==", - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-extension-gfm-task-list-item": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/micromark-extension-gfm-task-list-item/-/micromark-extension-gfm-task-list-item-2.1.0.tgz", - "integrity": "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw==", - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/micromark-factory-destination": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-factory-destination/-/micromark-factory-destination-2.0.1.tgz", - "integrity": "sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-label": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-factory-label/-/micromark-factory-label-2.0.1.tgz", - "integrity": "sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-space": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-factory-space/-/micromark-factory-space-2.0.1.tgz", - "integrity": "sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-title": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-factory-title/-/micromark-factory-title-2.0.1.tgz", - "integrity": "sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-factory-whitespace": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-factory-whitespace/-/micromark-factory-whitespace-2.0.1.tgz", - "integrity": "sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-factory-space": "^2.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-character": { - "version": "2.1.1", - "resolved": "https://registry.npmmirror.com/micromark-util-character/-/micromark-util-character-2.1.1.tgz", - "integrity": "sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-chunked": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-chunked/-/micromark-util-chunked-2.0.1.tgz", - "integrity": "sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-classify-character": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-classify-character/-/micromark-util-classify-character-2.0.1.tgz", - "integrity": "sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-combine-extensions": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-combine-extensions/-/micromark-util-combine-extensions-2.0.1.tgz", - "integrity": "sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-chunked": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-numeric-character-reference": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/micromark-util-decode-numeric-character-reference/-/micromark-util-decode-numeric-character-reference-2.0.2.tgz", - "integrity": "sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-decode-string": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-decode-string/-/micromark-util-decode-string-2.0.1.tgz", - "integrity": "sha512-nDV/77Fj6eH1ynwscYTOsbK7rR//Uj0bZXBwJZRfaLEJ1iGBR6kIfNmlNqaqJf649EP0F3NWNdeJi03elllNUQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "decode-named-character-reference": "^1.0.0", - "micromark-util-character": "^2.0.0", - "micromark-util-decode-numeric-character-reference": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-encode": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-encode/-/micromark-util-encode-2.0.1.tgz", - "integrity": "sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-html-tag-name": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-html-tag-name/-/micromark-util-html-tag-name-2.0.1.tgz", - "integrity": "sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-normalize-identifier": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-normalize-identifier/-/micromark-util-normalize-identifier-2.0.1.tgz", - "integrity": "sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-resolve-all": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-resolve-all/-/micromark-util-resolve-all-2.0.1.tgz", - "integrity": "sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-sanitize-uri": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-sanitize-uri/-/micromark-util-sanitize-uri-2.0.1.tgz", - "integrity": "sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "micromark-util-character": "^2.0.0", - "micromark-util-encode": "^2.0.0", - "micromark-util-symbol": "^2.0.0" - } - }, - "node_modules/micromark-util-subtokenize": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/micromark-util-subtokenize/-/micromark-util-subtokenize-2.1.0.tgz", - "integrity": "sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT", - "dependencies": { - "devlop": "^1.0.0", - "micromark-util-chunked": "^2.0.0", - "micromark-util-symbol": "^2.0.0", - "micromark-util-types": "^2.0.0" - } - }, - "node_modules/micromark-util-symbol": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/micromark-util-symbol/-/micromark-util-symbol-2.0.1.tgz", - "integrity": "sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromark-util-types": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/micromark-util-types/-/micromark-util-types-2.0.2.tgz", - "integrity": "sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==", - "funding": [ - { - "type": "GitHub Sponsors", - "url": "https://github.com/sponsors/unifiedjs" - }, - { - "type": "OpenCollective", - "url": "https://opencollective.com/unified" - } - ], - "license": "MIT" - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmmirror.com/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmmirror.com/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", - "license": "MIT", - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmmirror.com/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", - "license": "MIT", - "dependencies": { - "mime-db": "1.52.0" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", - "dev": true, - "license": "ISC", - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmmirror.com/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/ms": { - "version": "2.1.3", - "resolved": "https://registry.npmmirror.com/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "license": "MIT" - }, - "node_modules/mz": { - "version": "2.7.0", - "resolved": "https://registry.npmmirror.com/mz/-/mz-2.7.0.tgz", - "integrity": "sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0", - "object-assign": "^4.0.1", - "thenify-all": "^1.0.0" - } - }, - "node_modules/nanoid": { - "version": "3.3.11", - "resolved": "https://registry.npmmirror.com/nanoid/-/nanoid-3.3.11.tgz", - "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, - "node_modules/natural-compare": { - "version": "1.4.0", - "resolved": "https://registry.npmmirror.com/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true, - "license": "MIT" - }, - "node_modules/node-releases": { - "version": "2.0.23", - "resolved": "https://registry.npmmirror.com/node-releases/-/node-releases-2.0.23.tgz", - "integrity": "sha512-cCmFDMSm26S6tQSDpBCg/NR8NENrVPhAJSf+XbxBG4rPFaaonlEoE9wHQmun+cls499TQGSb7ZyPBRlzgKfpeg==", - "dev": true, - "license": "MIT" - }, - "node_modules/normalize-path": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/normalize-path/-/normalize-path-3.0.0.tgz", - "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/normalize-range": { - "version": "0.1.2", - "resolved": "https://registry.npmmirror.com/normalize-range/-/normalize-range-0.1.2.tgz", - "integrity": "sha512-bdok/XvKII3nUpklnV6P2hxtMNrCboOjAcyBuQnWEhO665FwrSNRxU+AqpsyvO6LgGYPspN+lu5CLtw4jPRKNA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-assign": { - "version": "4.1.1", - "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz", - "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-hash": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/object-hash/-/object-hash-3.0.0.tgz", - "integrity": "sha512-RSn9F68PjH9HqtltsSnqYC1XXoWe9Bju5+213R98cNGttag9q9yAOTzdbsqvIa7aNm5WffBZFpWYr2aWrklWAw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/optionator": { - "version": "0.9.4", - "resolved": "https://registry.npmmirror.com/optionator/-/optionator-0.9.4.tgz", - "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", - "dev": true, - "license": "MIT", - "dependencies": { - "deep-is": "^0.1.3", - "fast-levenshtein": "^2.0.6", - "levn": "^0.4.1", - "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.5" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "license": "MIT", - "dependencies": { - "p-limit": "^3.0.2" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/package-json-from-dist": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", - "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", - "dev": true, - "license": "BlueOak-1.0.0" - }, - "node_modules/pako": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/pako/-/pako-2.1.0.tgz", - "integrity": "sha512-w+eufiZ1WuJYgPXbV/PO3NCMEc3xqylkKHzp8bxp1uW4qaSNQUkwmLLEc3kKsfz8lpV1F8Ht3U1Cm+9Srog2ug==", - "license": "(MIT AND Zlib)" - }, - "node_modules/parent-module": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/parent-module/-/parent-module-1.0.1.tgz", - "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", - "dev": true, - "license": "MIT", - "dependencies": { - "callsites": "^3.0.0" - }, - "engines": { - "node": ">=6" - } - }, - "node_modules/parse-entities": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/parse-entities/-/parse-entities-4.0.2.tgz", - "integrity": "sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^2.0.0", - "character-entities-legacy": "^3.0.0", - "character-reference-invalid": "^2.0.0", - "decode-named-character-reference": "^1.0.0", - "is-alphanumerical": "^2.0.0", - "is-decimal": "^2.0.0", - "is-hexadecimal": "^2.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/parse-entities/node_modules/@types/unist": { - "version": "2.0.11", - "resolved": "https://registry.npmmirror.com/@types/unist/-/unist-2.0.11.tgz", - "integrity": "sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==", - "license": "MIT" - }, - "node_modules/parse5": { - "version": "7.3.0", - "resolved": "https://registry.npmmirror.com/parse5/-/parse5-7.3.0.tgz", - "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", - "license": "MIT", - "dependencies": { - "entities": "^6.0.0" - }, - "funding": { - "url": "https://github.com/inikulin/parse5?sponsor=1" - } - }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-key": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/path-key/-/path-key-3.1.1.tgz", - "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/path-parse": { - "version": "1.0.7", - "resolved": "https://registry.npmmirror.com/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmmirror.com/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", - "dev": true, - "license": "BlueOak-1.0.0", - "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" - }, - "engines": { - "node": ">=16 || 14 >=14.18" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/path-scurry/node_modules/lru-cache": { - "version": "10.4.3", - "resolved": "https://registry.npmmirror.com/lru-cache/-/lru-cache-10.4.3.tgz", - "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", - "dev": true, - "license": "ISC" - }, - "node_modules/performance-now": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/performance-now/-/performance-now-2.1.0.tgz", - "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==", - "license": "MIT", - "optional": true - }, - "node_modules/picocolors": { - "version": "1.1.1", - "resolved": "https://registry.npmmirror.com/picocolors/-/picocolors-1.1.1.tgz", - "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", - "dev": true, - "license": "ISC" - }, - "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8.6" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmmirror.com/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pirates": { - "version": "4.0.7", - "resolved": "https://registry.npmmirror.com/pirates/-/pirates-4.0.7.tgz", - "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, - "node_modules/postcss": { - "version": "8.5.6", - "resolved": "https://registry.npmmirror.com/postcss/-/postcss-8.5.6.tgz", - "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/postcss" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "nanoid": "^3.3.11", - "picocolors": "^1.1.1", - "source-map-js": "^1.2.1" - }, - "engines": { - "node": "^10 || ^12 || >=14" - } - }, - "node_modules/postcss-import": { - "version": "15.1.0", - "resolved": "https://registry.npmmirror.com/postcss-import/-/postcss-import-15.1.0.tgz", - "integrity": "sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==", - "dev": true, - "license": "MIT", - "dependencies": { - "postcss-value-parser": "^4.0.0", - "read-cache": "^1.0.0", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "postcss": "^8.0.0" - } - }, - "node_modules/postcss-js": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/postcss-js/-/postcss-js-4.1.0.tgz", - "integrity": "sha512-oIAOTqgIo7q2EOwbhb8UalYePMvYoIeRY2YKntdpFQXNosSu3vLrniGgmH9OKs/qAkfoj5oB3le/7mINW1LCfw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "camelcase-css": "^2.0.1" - }, - "engines": { - "node": "^12 || ^14 || >= 16" - }, - "peerDependencies": { - "postcss": "^8.4.21" - } - }, - "node_modules/postcss-load-config": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/postcss-load-config/-/postcss-load-config-6.0.1.tgz", - "integrity": "sha512-oPtTM4oerL+UXmx+93ytZVN82RrlY/wPUV8IeDxFrzIjXOLF1pN+EmKPLbubvKHT2HC20xXsCAH2Z+CKV6Oz/g==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "lilconfig": "^3.1.1" - }, - "engines": { - "node": ">= 18" - }, - "peerDependencies": { - "jiti": ">=1.21.0", - "postcss": ">=8.0.9", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "jiti": { - "optional": true - }, - "postcss": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/postcss-nested": { - "version": "6.2.0", - "resolved": "https://registry.npmmirror.com/postcss-nested/-/postcss-nested-6.2.0.tgz", - "integrity": "sha512-HQbt28KulC5AJzG+cZtj9kvKB93CFCdLvog1WFLf1D+xmMvPGlBstkpTEZfK5+AN9hfJocyBFCNiqyS48bpgzQ==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/postcss/" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "postcss-selector-parser": "^6.1.1" - }, - "engines": { - "node": ">=12.0" - }, - "peerDependencies": { - "postcss": "^8.2.14" - } - }, - "node_modules/postcss-selector-parser": { - "version": "6.1.2", - "resolved": "https://registry.npmmirror.com/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz", - "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==", - "dev": true, - "license": "MIT", - "dependencies": { - "cssesc": "^3.0.0", - "util-deprecate": "^1.0.2" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/postcss-value-parser": { - "version": "4.2.0", - "resolved": "https://registry.npmmirror.com/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz", - "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/prelude-ls": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/prelude-ls/-/prelude-ls-1.2.1.tgz", - "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/prismjs": { - "version": "1.30.0", - "resolved": "https://registry.npmmirror.com/prismjs/-/prismjs-1.30.0.tgz", - "integrity": "sha512-DEvV2ZF2r2/63V+tK8hQvrR2ZGn10srHbXviTlcv7Kpzw8jWiNTqbVgjO3IY8RxrrOUF8VPMQQFysYYYv0YZxw==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/property-information": { - "version": "7.1.0", - "resolved": "https://registry.npmmirror.com/property-information/-/property-information-7.1.0.tgz", - "integrity": "sha512-TwEZ+X+yCJmYfL7TPUOcvBZ4QfoT5YenQiJuX//0th53DE6w0xxLEtfK3iyryQFddXuvkIk51EEgrJQ0WJkOmQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/proxy-from-env": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/proxy-from-env/-/proxy-from-env-1.1.0.tgz", - "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", - "license": "MIT" - }, - "node_modules/punycode": { - "version": "2.3.1", - "resolved": "https://registry.npmmirror.com/punycode/-/punycode-2.3.1.tgz", - "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/queue-microtask": { - "version": "1.2.3", - "resolved": "https://registry.npmmirror.com/queue-microtask/-/queue-microtask-1.2.3.tgz", - "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT" - }, - "node_modules/raf": { - "version": "3.4.1", - "resolved": "https://registry.npmmirror.com/raf/-/raf-3.4.1.tgz", - "integrity": "sha512-Sq4CW4QhwOHE8ucn6J34MqtZCeWFP2aQSmrlroYgqAV1PjStIhJXxYuTgUIfkEk7zTLjmIjLmU5q+fbD1NnOJA==", - "license": "MIT", - "optional": true, - "dependencies": { - "performance-now": "^2.1.0" - } - }, - "node_modules/rc-cascader": { - "version": "3.34.0", - "resolved": "https://registry.npmmirror.com/rc-cascader/-/rc-cascader-3.34.0.tgz", - "integrity": "sha512-KpXypcvju9ptjW9FaN2NFcA2QH9E9LHKq169Y0eWtH4e/wHQ5Wh5qZakAgvb8EKZ736WZ3B0zLLOBsrsja5Dag==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.25.7", - "classnames": "^2.3.1", - "rc-select": "~14.16.2", - "rc-tree": "~5.13.0", - "rc-util": "^5.43.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-checkbox": { - "version": "3.5.0", - "resolved": "https://registry.npmmirror.com/rc-checkbox/-/rc-checkbox-3.5.0.tgz", - "integrity": "sha512-aOAQc3E98HteIIsSqm6Xk2FPKIER6+5vyEFMZfo73TqM+VVAIqOkHoPjgKLqSNtVLWScoaM7vY2ZrGEheI79yg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.3.2", - "rc-util": "^5.25.2" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-collapse": { - "version": "3.9.0", - "resolved": "https://registry.npmmirror.com/rc-collapse/-/rc-collapse-3.9.0.tgz", - "integrity": "sha512-swDdz4QZ4dFTo4RAUMLL50qP0EY62N2kvmk2We5xYdRwcRn8WcYtuetCJpwpaCbUfUt5+huLpVxhvmnK+PHrkA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "2.x", - "rc-motion": "^2.3.4", - "rc-util": "^5.27.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-dialog": { - "version": "9.6.0", - "resolved": "https://registry.npmmirror.com/rc-dialog/-/rc-dialog-9.6.0.tgz", - "integrity": "sha512-ApoVi9Z8PaCQg6FsUzS8yvBEQy0ZL2PkuvAgrmohPkN3okps5WZ5WQWPc1RNuiOKaAYv8B97ACdsFU5LizzCqg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "@rc-component/portal": "^1.0.0-8", - "classnames": "^2.2.6", - "rc-motion": "^2.3.0", - "rc-util": "^5.21.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-drawer": { - "version": "7.3.0", - "resolved": "https://registry.npmmirror.com/rc-drawer/-/rc-drawer-7.3.0.tgz", - "integrity": "sha512-DX6CIgiBWNpJIMGFO8BAISFkxiuKitoizooj4BDyee8/SnBn0zwO2FHrNDpqqepj0E/TFTDpmEBCyFuTgC7MOg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.23.9", - "@rc-component/portal": "^1.1.1", - "classnames": "^2.2.6", - "rc-motion": "^2.6.1", - "rc-util": "^5.38.1" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-dropdown": { - "version": "4.2.1", - "resolved": "https://registry.npmmirror.com/rc-dropdown/-/rc-dropdown-4.2.1.tgz", - "integrity": "sha512-YDAlXsPv3I1n42dv1JpdM7wJ+gSUBfeyPK59ZpBD9jQhK9jVuxpjj3NmWQHOBceA1zEPVX84T2wbdb2SD0UjmA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "@rc-component/trigger": "^2.0.0", - "classnames": "^2.2.6", - "rc-util": "^5.44.1" - }, - "peerDependencies": { - "react": ">=16.11.0", - "react-dom": ">=16.11.0" - } - }, - "node_modules/rc-field-form": { - "version": "2.7.0", - "resolved": "https://registry.npmmirror.com/rc-field-form/-/rc-field-form-2.7.0.tgz", - "integrity": "sha512-hgKsCay2taxzVnBPZl+1n4ZondsV78G++XVsMIJCAoioMjlMQR9YwAp7JZDIECzIu2Z66R+f4SFIRrO2DjDNAA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.0", - "@rc-component/async-validator": "^5.0.3", - "rc-util": "^5.32.2" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-image": { - "version": "7.12.0", - "resolved": "https://registry.npmmirror.com/rc-image/-/rc-image-7.12.0.tgz", - "integrity": "sha512-cZ3HTyyckPnNnUb9/DRqduqzLfrQRyi+CdHjdqgsyDpI3Ln5UX1kXnAhPBSJj9pVRzwRFgqkN7p9b6HBDjmu/Q==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.11.2", - "@rc-component/portal": "^1.0.2", - "classnames": "^2.2.6", - "rc-dialog": "~9.6.0", - "rc-motion": "^2.6.2", - "rc-util": "^5.34.1" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-input": { - "version": "1.8.0", - "resolved": "https://registry.npmmirror.com/rc-input/-/rc-input-1.8.0.tgz", - "integrity": "sha512-KXvaTbX+7ha8a/k+eg6SYRVERK0NddX8QX7a7AnRvUa/rEH0CNMlpcBzBkhI0wp2C8C4HlMoYl8TImSN+fuHKA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.11.1", - "classnames": "^2.2.1", - "rc-util": "^5.18.1" - }, - "peerDependencies": { - "react": ">=16.0.0", - "react-dom": ">=16.0.0" - } - }, - "node_modules/rc-input-number": { - "version": "9.5.0", - "resolved": "https://registry.npmmirror.com/rc-input-number/-/rc-input-number-9.5.0.tgz", - "integrity": "sha512-bKaEvB5tHebUURAEXw35LDcnRZLq3x1k7GxfAqBMzmpHkDGzjAtnUL8y4y5N15rIFIg5IJgwr211jInl3cipag==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "@rc-component/mini-decimal": "^1.0.1", - "classnames": "^2.2.5", - "rc-input": "~1.8.0", - "rc-util": "^5.40.1" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-mentions": { - "version": "2.20.0", - "resolved": "https://registry.npmmirror.com/rc-mentions/-/rc-mentions-2.20.0.tgz", - "integrity": "sha512-w8HCMZEh3f0nR8ZEd466ATqmXFCMGMN5UFCzEUL0bM/nGw/wOS2GgRzKBcm19K++jDyuWCOJOdgcKGXU3fXfbQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.22.5", - "@rc-component/trigger": "^2.0.0", - "classnames": "^2.2.6", - "rc-input": "~1.8.0", - "rc-menu": "~9.16.0", - "rc-textarea": "~1.10.0", - "rc-util": "^5.34.1" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-menu": { - "version": "9.16.1", - "resolved": "https://registry.npmmirror.com/rc-menu/-/rc-menu-9.16.1.tgz", - "integrity": "sha512-ghHx6/6Dvp+fw8CJhDUHFHDJ84hJE3BXNCzSgLdmNiFErWSOaZNsihDAsKq9ByTALo/xkNIwtDFGIl6r+RPXBg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "@rc-component/trigger": "^2.0.0", - "classnames": "2.x", - "rc-motion": "^2.4.3", - "rc-overflow": "^1.3.1", - "rc-util": "^5.27.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-motion": { - "version": "2.9.5", - "resolved": "https://registry.npmmirror.com/rc-motion/-/rc-motion-2.9.5.tgz", - "integrity": "sha512-w+XTUrfh7ArbYEd2582uDrEhmBHwK1ZENJiSJVb7uRxdE7qJSYjbO2eksRXmndqyKqKoYPc9ClpPh5242mV1vA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.11.1", - "classnames": "^2.2.1", - "rc-util": "^5.44.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-notification": { - "version": "5.6.4", - "resolved": "https://registry.npmmirror.com/rc-notification/-/rc-notification-5.6.4.tgz", - "integrity": "sha512-KcS4O6B4qzM3KH7lkwOB7ooLPZ4b6J+VMmQgT51VZCeEcmghdeR4IrMcFq0LG+RPdnbe/ArT086tGM8Snimgiw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "2.x", - "rc-motion": "^2.9.0", - "rc-util": "^5.20.1" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-overflow": { - "version": "1.4.1", - "resolved": "https://registry.npmmirror.com/rc-overflow/-/rc-overflow-1.4.1.tgz", - "integrity": "sha512-3MoPQQPV1uKyOMVNd6SZfONi+f3st0r8PksexIdBTeIYbMX0Jr+k7pHEDvsXtR4BpCv90/Pv2MovVNhktKrwvw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.11.1", - "classnames": "^2.2.1", - "rc-resize-observer": "^1.0.0", - "rc-util": "^5.37.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-pagination": { - "version": "5.1.0", - "resolved": "https://registry.npmmirror.com/rc-pagination/-/rc-pagination-5.1.0.tgz", - "integrity": "sha512-8416Yip/+eclTFdHXLKTxZvn70duYVGTvUUWbckCCZoIl3jagqke3GLsFrMs0bsQBikiYpZLD9206Ej4SOdOXQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.3.2", - "rc-util": "^5.38.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-picker": { - "version": "4.11.3", - "resolved": "https://registry.npmmirror.com/rc-picker/-/rc-picker-4.11.3.tgz", - "integrity": "sha512-MJ5teb7FlNE0NFHTncxXQ62Y5lytq6sh5nUw0iH8OkHL/TjARSEvSHpr940pWgjGANpjCwyMdvsEV55l5tYNSg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.24.7", - "@rc-component/trigger": "^2.0.0", - "classnames": "^2.2.1", - "rc-overflow": "^1.3.2", - "rc-resize-observer": "^1.4.0", - "rc-util": "^5.43.0" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "date-fns": ">= 2.x", - "dayjs": ">= 1.x", - "luxon": ">= 3.x", - "moment": ">= 2.x", - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - }, - "peerDependenciesMeta": { - "date-fns": { - "optional": true - }, - "dayjs": { - "optional": true - }, - "luxon": { - "optional": true - }, - "moment": { - "optional": true - } - } - }, - "node_modules/rc-progress": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/rc-progress/-/rc-progress-4.0.0.tgz", - "integrity": "sha512-oofVMMafOCokIUIBnZLNcOZFsABaUw8PPrf1/y0ZBvKZNpOiu5h4AO9vv11Sw0p4Hb3D0yGWuEattcQGtNJ/aw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.6", - "rc-util": "^5.16.1" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-rate": { - "version": "2.13.1", - "resolved": "https://registry.npmmirror.com/rc-rate/-/rc-rate-2.13.1.tgz", - "integrity": "sha512-QUhQ9ivQ8Gy7mtMZPAjLbxBt5y9GRp65VcUyGUMF3N3fhiftivPHdpuDIaWIMOTEprAjZPC08bls1dQB+I1F2Q==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.5", - "rc-util": "^5.0.1" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-resize-observer": { - "version": "1.4.3", - "resolved": "https://registry.npmmirror.com/rc-resize-observer/-/rc-resize-observer-1.4.3.tgz", - "integrity": "sha512-YZLjUbyIWox8E9i9C3Tm7ia+W7euPItNWSPX5sCcQTYbnwDb5uNpnLHQCG1f22oZWUhLw4Mv2tFmeWe68CDQRQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.20.7", - "classnames": "^2.2.1", - "rc-util": "^5.44.1", - "resize-observer-polyfill": "^1.5.1" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-segmented": { - "version": "2.7.0", - "resolved": "https://registry.npmmirror.com/rc-segmented/-/rc-segmented-2.7.0.tgz", - "integrity": "sha512-liijAjXz+KnTRVnxxXG2sYDGd6iLL7VpGGdR8gwoxAXy2KglviKCxLWZdjKYJzYzGSUwKDSTdYk8brj54Bn5BA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.11.1", - "classnames": "^2.2.1", - "rc-motion": "^2.4.4", - "rc-util": "^5.17.0" - }, - "peerDependencies": { - "react": ">=16.0.0", - "react-dom": ">=16.0.0" - } - }, - "node_modules/rc-select": { - "version": "14.16.8", - "resolved": "https://registry.npmmirror.com/rc-select/-/rc-select-14.16.8.tgz", - "integrity": "sha512-NOV5BZa1wZrsdkKaiK7LHRuo5ZjZYMDxPP6/1+09+FB4KoNi8jcG1ZqLE3AVCxEsYMBe65OBx71wFoHRTP3LRg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "@rc-component/trigger": "^2.1.1", - "classnames": "2.x", - "rc-motion": "^2.0.1", - "rc-overflow": "^1.3.1", - "rc-util": "^5.16.1", - "rc-virtual-list": "^3.5.2" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, - "node_modules/rc-slider": { - "version": "11.1.9", - "resolved": "https://registry.npmmirror.com/rc-slider/-/rc-slider-11.1.9.tgz", - "integrity": "sha512-h8IknhzSh3FEM9u8ivkskh+Ef4Yo4JRIY2nj7MrH6GQmrwV6mcpJf5/4KgH5JaVI1H3E52yCdpOlVyGZIeph5A==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.5", - "rc-util": "^5.36.0" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-steps": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/rc-steps/-/rc-steps-6.0.1.tgz", - "integrity": "sha512-lKHL+Sny0SeHkQKKDJlAjV5oZ8DwCdS2hFhAkIjuQt1/pB81M0cA0ErVFdHq9+jmPmFw1vJB2F5NBzFXLJxV+g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.16.7", - "classnames": "^2.2.3", - "rc-util": "^5.16.1" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-switch": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/rc-switch/-/rc-switch-4.1.0.tgz", - "integrity": "sha512-TI8ufP2Az9oEbvyCeVE4+90PDSljGyuwix3fV58p7HV2o4wBnVToEyomJRVyTaZeqNPAp+vqeo4Wnj5u0ZZQBg==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.21.0", - "classnames": "^2.2.1", - "rc-util": "^5.30.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-table": { - "version": "7.53.1", - "resolved": "https://registry.npmmirror.com/rc-table/-/rc-table-7.53.1.tgz", - "integrity": "sha512-firAd7Z+liqIDS5TubJ1qqcoBd6YcANLKWQDZhFf3rfoOTt/UNPj4n3O+2vhl+z4QMqwPEUVAil661WHA8H8Aw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "@rc-component/context": "^1.4.0", - "classnames": "^2.2.5", - "rc-resize-observer": "^1.1.0", - "rc-util": "^5.44.3", - "rc-virtual-list": "^3.14.2" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-tabs": { - "version": "15.7.0", - "resolved": "https://registry.npmmirror.com/rc-tabs/-/rc-tabs-15.7.0.tgz", - "integrity": "sha512-ZepiE+6fmozYdWf/9gVp7k56PKHB1YYoDsKeQA1CBlJ/POIhjkcYiv0AGP0w2Jhzftd3AVvZP/K+V+Lpi2ankA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.11.2", - "classnames": "2.x", - "rc-dropdown": "~4.2.0", - "rc-menu": "~9.16.0", - "rc-motion": "^2.6.2", - "rc-resize-observer": "^1.0.0", - "rc-util": "^5.34.1" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-textarea": { - "version": "1.10.2", - "resolved": "https://registry.npmmirror.com/rc-textarea/-/rc-textarea-1.10.2.tgz", - "integrity": "sha512-HfaeXiaSlpiSp0I/pvWpecFEHpVysZ9tpDLNkxQbMvMz6gsr7aVZ7FpWP9kt4t7DB+jJXesYS0us1uPZnlRnwQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "^2.2.1", - "rc-input": "~1.8.0", - "rc-resize-observer": "^1.0.0", - "rc-util": "^5.27.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-tooltip": { - "version": "6.4.0", - "resolved": "https://registry.npmmirror.com/rc-tooltip/-/rc-tooltip-6.4.0.tgz", - "integrity": "sha512-kqyivim5cp8I5RkHmpsp1Nn/Wk+1oeloMv9c7LXNgDxUpGm+RbXJGL+OPvDlcRnx9DBeOe4wyOIl4OKUERyH1g==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.11.2", - "@rc-component/trigger": "^2.0.0", - "classnames": "^2.3.1", - "rc-util": "^5.44.3" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-tree": { - "version": "5.13.1", - "resolved": "https://registry.npmmirror.com/rc-tree/-/rc-tree-5.13.1.tgz", - "integrity": "sha512-FNhIefhftobCdUJshO7M8uZTA9F4OPGVXqGfZkkD/5soDeOhwO06T/aKTrg0WD8gRg/pyfq+ql3aMymLHCTC4A==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.10.1", - "classnames": "2.x", - "rc-motion": "^2.0.1", - "rc-util": "^5.16.1", - "rc-virtual-list": "^3.5.1" - }, - "engines": { - "node": ">=10.x" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, - "node_modules/rc-tree-select": { - "version": "5.27.0", - "resolved": "https://registry.npmmirror.com/rc-tree-select/-/rc-tree-select-5.27.0.tgz", - "integrity": "sha512-2qTBTzwIT7LRI1o7zLyrCzmo5tQanmyGbSaGTIf7sYimCklAToVVfpMC6OAldSKolcnjorBYPNSKQqJmN3TCww==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.25.7", - "classnames": "2.x", - "rc-select": "~14.16.2", - "rc-tree": "~5.13.0", - "rc-util": "^5.43.0" - }, - "peerDependencies": { - "react": "*", - "react-dom": "*" - } - }, - "node_modules/rc-upload": { - "version": "4.9.2", - "resolved": "https://registry.npmmirror.com/rc-upload/-/rc-upload-4.9.2.tgz", - "integrity": "sha512-nHx+9rbd1FKMiMRYsqQ3NkXUv7COHPBo3X1Obwq9SWS6/diF/A0aJ5OHubvwUAIDs+4RMleljV0pcrNUc823GQ==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "classnames": "^2.2.5", - "rc-util": "^5.2.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-util": { - "version": "5.44.4", - "resolved": "https://registry.npmmirror.com/rc-util/-/rc-util-5.44.4.tgz", - "integrity": "sha512-resueRJzmHG9Q6rI/DfK6Kdv9/Lfls05vzMs1Sk3M2P+3cJa+MakaZyWY8IPfehVuhPJFKrIY1IK4GqbiaiY5w==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.18.3", - "react-is": "^18.2.0" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/rc-virtual-list": { - "version": "3.19.2", - "resolved": "https://registry.npmmirror.com/rc-virtual-list/-/rc-virtual-list-3.19.2.tgz", - "integrity": "sha512-Ys6NcjwGkuwkeaWBDqfI3xWuZ7rDiQXlH1o2zLfFzATfEgXcqpk8CkgMfbJD81McqjcJVez25a3kPxCR807evA==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.20.0", - "classnames": "^2.2.6", - "rc-resize-observer": "^1.0.0", - "rc-util": "^5.36.0" - }, - "engines": { - "node": ">=8.x" - }, - "peerDependencies": { - "react": ">=16.9.0", - "react-dom": ">=16.9.0" - } - }, - "node_modules/react": { - "version": "18.3.1", - "resolved": "https://registry.npmmirror.com/react/-/react-18.3.1.tgz", - "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-dom": { - "version": "18.3.1", - "resolved": "https://registry.npmmirror.com/react-dom/-/react-dom-18.3.1.tgz", - "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0", - "scheduler": "^0.23.2" - }, - "peerDependencies": { - "react": "^18.3.1" - } - }, - "node_modules/react-is": { - "version": "18.3.1", - "resolved": "https://registry.npmmirror.com/react-is/-/react-is-18.3.1.tgz", - "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", - "license": "MIT" - }, - "node_modules/react-markdown": { - "version": "10.1.0", - "resolved": "https://registry.npmmirror.com/react-markdown/-/react-markdown-10.1.0.tgz", - "integrity": "sha512-qKxVopLT/TyA6BX3Ue5NwabOsAzm0Q7kAPwq6L+wWDwisYs7R8vZ0nRXqq6rkueboxpkjvLGU9fWifiX/ZZFxQ==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "devlop": "^1.0.0", - "hast-util-to-jsx-runtime": "^2.0.0", - "html-url-attributes": "^3.0.0", - "mdast-util-to-hast": "^13.0.0", - "remark-parse": "^11.0.0", - "remark-rehype": "^11.0.0", - "unified": "^11.0.0", - "unist-util-visit": "^5.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - }, - "peerDependencies": { - "@types/react": ">=18", - "react": ">=18" - } - }, - "node_modules/react-refresh": { - "version": "0.17.0", - "resolved": "https://registry.npmmirror.com/react-refresh/-/react-refresh-0.17.0.tgz", - "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/react-router": { - "version": "6.30.1", - "resolved": "https://registry.npmmirror.com/react-router/-/react-router-6.30.1.tgz", - "integrity": "sha512-X1m21aEmxGXqENEPG3T6u0Th7g0aS4ZmoNynhbs+Cn+q+QGTLt+d5IQ2bHAXKzKcxGJjxACpVbnYQSCRcfxHlQ==", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.23.0" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8" - } - }, - "node_modules/react-router-dom": { - "version": "6.30.1", - "resolved": "https://registry.npmmirror.com/react-router-dom/-/react-router-dom-6.30.1.tgz", - "integrity": "sha512-llKsgOkZdbPU1Eg3zK8lCn+sjD9wMRZZPuzmdWWX5SUs8OFkN5HnFVC0u5KMeMaC9aoancFI/KoLuKPqN+hxHw==", - "license": "MIT", - "dependencies": { - "@remix-run/router": "1.23.0", - "react-router": "6.30.1" - }, - "engines": { - "node": ">=14.0.0" - }, - "peerDependencies": { - "react": ">=16.8", - "react-dom": ">=16.8" - } - }, - "node_modules/react-syntax-highlighter": { - "version": "15.6.6", - "resolved": "https://registry.npmmirror.com/react-syntax-highlighter/-/react-syntax-highlighter-15.6.6.tgz", - "integrity": "sha512-DgXrc+AZF47+HvAPEmn7Ua/1p10jNoVZVI/LoPiYdtY+OM+/nG5yefLHKJwdKqY1adMuHFbeyBaG9j64ML7vTw==", - "license": "MIT", - "dependencies": { - "@babel/runtime": "^7.3.1", - "highlight.js": "^10.4.1", - "highlightjs-vue": "^1.0.0", - "lowlight": "^1.17.0", - "prismjs": "^1.30.0", - "refractor": "^3.6.0" - }, - "peerDependencies": { - "react": ">= 0.14.0" - } - }, - "node_modules/read-cache": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/read-cache/-/read-cache-1.0.0.tgz", - "integrity": "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA==", - "dev": true, - "license": "MIT", - "dependencies": { - "pify": "^2.3.0" - } - }, - "node_modules/readdirp": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/readdirp/-/readdirp-3.6.0.tgz", - "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", - "dev": true, - "license": "MIT", - "dependencies": { - "picomatch": "^2.2.1" - }, - "engines": { - "node": ">=8.10.0" - } - }, - "node_modules/refractor": { - "version": "3.6.0", - "resolved": "https://registry.npmmirror.com/refractor/-/refractor-3.6.0.tgz", - "integrity": "sha512-MY9W41IOWxxk31o+YvFCNyNzdkc9M20NoZK5vq6jkv4I/uh2zkWcfudj0Q1fovjUQJrNewS9NMzeTtqPf+n5EA==", - "license": "MIT", - "dependencies": { - "hastscript": "^6.0.0", - "parse-entities": "^2.0.0", - "prismjs": "~1.27.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/refractor/node_modules/character-entities": { - "version": "1.2.4", - "resolved": "https://registry.npmmirror.com/character-entities/-/character-entities-1.2.4.tgz", - "integrity": "sha512-iBMyeEHxfVnIakwOuDXpVkc54HijNgCyQB2w0VfGQThle6NXn50zU6V/u+LDhxHcDUPojn6Kpga3PTAD8W1bQw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/refractor/node_modules/character-entities-legacy": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/character-entities-legacy/-/character-entities-legacy-1.1.4.tgz", - "integrity": "sha512-3Xnr+7ZFS1uxeiUDvV02wQ+QDbc55o97tIV5zHScSPJpcLm/r0DFPcoY3tYRp+VZukxuMeKgXYmsXQHO05zQeA==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/refractor/node_modules/character-reference-invalid": { - "version": "1.1.4", - "resolved": "https://registry.npmmirror.com/character-reference-invalid/-/character-reference-invalid-1.1.4.tgz", - "integrity": "sha512-mKKUkUbhPpQlCOfIuZkvSEgktjPFIsZKRRbC6KWVEMvlzblj3i3asQv5ODsrwt0N3pHAEvjP8KTQPHkp0+6jOg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/refractor/node_modules/is-alphabetical": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/is-alphabetical/-/is-alphabetical-1.0.4.tgz", - "integrity": "sha512-DwzsA04LQ10FHTZuL0/grVDk4rFoVH1pjAToYwBrHSxcrBIGQuXrQMtD5U1b0U2XVgKZCTLLP8u2Qxqhy3l2Vg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/refractor/node_modules/is-alphanumerical": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/is-alphanumerical/-/is-alphanumerical-1.0.4.tgz", - "integrity": "sha512-UzoZUr+XfVz3t3v4KyGEniVL9BDRoQtY7tOyrRybkVNjDFWyo1yhXNGrrBTQxp3ib9BLAWs7k2YKBQsFRkZG9A==", - "license": "MIT", - "dependencies": { - "is-alphabetical": "^1.0.0", - "is-decimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/refractor/node_modules/is-decimal": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/is-decimal/-/is-decimal-1.0.4.tgz", - "integrity": "sha512-RGdriMmQQvZ2aqaQq3awNA6dCGtKpiDFcOzrTWrDAT2MiWrKQVPmxLGHl7Y2nNu6led0kEyoX0enY0qXYsv9zw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/refractor/node_modules/is-hexadecimal": { - "version": "1.0.4", - "resolved": "https://registry.npmmirror.com/is-hexadecimal/-/is-hexadecimal-1.0.4.tgz", - "integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/refractor/node_modules/parse-entities": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/parse-entities/-/parse-entities-2.0.0.tgz", - "integrity": "sha512-kkywGpCcRYhqQIchaWqZ875wzpS/bMKhz5HnN3p7wveJTkTtyAB/AlnS0f8DFSqYW1T82t6yEAkEcB+A1I3MbQ==", - "license": "MIT", - "dependencies": { - "character-entities": "^1.0.0", - "character-entities-legacy": "^1.0.0", - "character-reference-invalid": "^1.0.0", - "is-alphanumerical": "^1.0.0", - "is-decimal": "^1.0.0", - "is-hexadecimal": "^1.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/refractor/node_modules/prismjs": { - "version": "1.27.0", - "resolved": "https://registry.npmmirror.com/prismjs/-/prismjs-1.27.0.tgz", - "integrity": "sha512-t13BGPUlFDR7wRB5kQDG4jjl7XeuH6jbJGt11JHPL96qwsEHNX2+68tFXqc1/k+/jALsbSWJKUOT/hcYAZ5LkA==", - "license": "MIT", - "engines": { - "node": ">=6" - } - }, - "node_modules/regenerator-runtime": { - "version": "0.13.11", - "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", - "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", - "license": "MIT", - "optional": true - }, - "node_modules/rehype-raw": { - "version": "7.0.0", - "resolved": "https://registry.npmmirror.com/rehype-raw/-/rehype-raw-7.0.0.tgz", - "integrity": "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "hast-util-raw": "^9.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-gfm": { - "version": "4.0.1", - "resolved": "https://registry.npmmirror.com/remark-gfm/-/remark-gfm-4.0.1.tgz", - "integrity": "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-gfm": "^3.0.0", - "micromark-extension-gfm": "^3.0.0", - "remark-parse": "^11.0.0", - "remark-stringify": "^11.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-parse": { - "version": "11.0.0", - "resolved": "https://registry.npmmirror.com/remark-parse/-/remark-parse-11.0.0.tgz", - "integrity": "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-from-markdown": "^2.0.0", - "micromark-util-types": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-rehype": { - "version": "11.1.2", - "resolved": "https://registry.npmmirror.com/remark-rehype/-/remark-rehype-11.1.2.tgz", - "integrity": "sha512-Dh7l57ianaEoIpzbp0PC9UKAdCSVklD8E5Rpw7ETfbTl3FqcOOgq5q2LVDhgGCkaBv7p24JXikPdvhhmHvKMsw==", - "license": "MIT", - "dependencies": { - "@types/hast": "^3.0.0", - "@types/mdast": "^4.0.0", - "mdast-util-to-hast": "^13.0.0", - "unified": "^11.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/remark-stringify": { - "version": "11.0.0", - "resolved": "https://registry.npmmirror.com/remark-stringify/-/remark-stringify-11.0.0.tgz", - "integrity": "sha512-1OSmLd3awB/t8qdoEOMazZkNsfVTeY4fTsgzcQFdXNq8ToTN4ZGwrMnlda4K6smTFKD+GRV6O48i6Z4iKgPPpw==", - "license": "MIT", - "dependencies": { - "@types/mdast": "^4.0.0", - "mdast-util-to-markdown": "^2.0.0", - "unified": "^11.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/resize-observer-polyfill": { - "version": "1.5.1", - "resolved": "https://registry.npmmirror.com/resize-observer-polyfill/-/resize-observer-polyfill-1.5.1.tgz", - "integrity": "sha512-LwZrotdHOo12nQuZlHEmtuXdqGoOD0OhaxopaNFxWzInpEgaLWoVuAMbTzixuosCx2nEG58ngzW3vxdWoxIgdg==", - "license": "MIT" - }, - "node_modules/resolve": { - "version": "1.22.10", - "resolved": "https://registry.npmmirror.com/resolve/-/resolve-1.22.10.tgz", - "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-core-module": "^2.16.0", - "path-parse": "^1.0.7", - "supports-preserve-symlinks-flag": "^1.0.0" - }, - "bin": { - "resolve": "bin/resolve" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/resolve-from": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/resolve-from/-/resolve-from-4.0.0.tgz", - "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=4" - } - }, - "node_modules/reusify": { - "version": "1.1.0", - "resolved": "https://registry.npmmirror.com/reusify/-/reusify-1.1.0.tgz", - "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", - "dev": true, - "license": "MIT", - "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" - } - }, - "node_modules/rgbcolor": { - "version": "1.0.1", - "resolved": "https://registry.npmmirror.com/rgbcolor/-/rgbcolor-1.0.1.tgz", - "integrity": "sha512-9aZLIrhRaD97sgVhtJOW6ckOEh6/GnvQtdVNfdZ6s67+3/XwLS9lBcQYzEEhYVeUowN7pRzMLsyGhK2i/xvWbw==", - "license": "MIT OR SEE LICENSE IN FEEL-FREE.md", - "optional": true, - "engines": { - "node": ">= 0.8.15" - } - }, - "node_modules/rollup": { - "version": "4.52.4", - "resolved": "https://registry.npmmirror.com/rollup/-/rollup-4.52.4.tgz", - "integrity": "sha512-CLEVl+MnPAiKh5pl4dEWSyMTpuflgNQiLGhMv8ezD5W/qP8AKvmYpCOKRRNOh7oRKnauBZ4SyeYkMS+1VSyKwQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@types/estree": "1.0.8" - }, - "bin": { - "rollup": "dist/bin/rollup" - }, - "engines": { - "node": ">=18.0.0", - "npm": ">=8.0.0" - }, - "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.52.4", - "@rollup/rollup-android-arm64": "4.52.4", - "@rollup/rollup-darwin-arm64": "4.52.4", - "@rollup/rollup-darwin-x64": "4.52.4", - "@rollup/rollup-freebsd-arm64": "4.52.4", - "@rollup/rollup-freebsd-x64": "4.52.4", - "@rollup/rollup-linux-arm-gnueabihf": "4.52.4", - "@rollup/rollup-linux-arm-musleabihf": "4.52.4", - "@rollup/rollup-linux-arm64-gnu": "4.52.4", - "@rollup/rollup-linux-arm64-musl": "4.52.4", - "@rollup/rollup-linux-loong64-gnu": "4.52.4", - "@rollup/rollup-linux-ppc64-gnu": "4.52.4", - "@rollup/rollup-linux-riscv64-gnu": "4.52.4", - "@rollup/rollup-linux-riscv64-musl": "4.52.4", - "@rollup/rollup-linux-s390x-gnu": "4.52.4", - "@rollup/rollup-linux-x64-gnu": "4.52.4", - "@rollup/rollup-linux-x64-musl": "4.52.4", - "@rollup/rollup-openharmony-arm64": "4.52.4", - "@rollup/rollup-win32-arm64-msvc": "4.52.4", - "@rollup/rollup-win32-ia32-msvc": "4.52.4", - "@rollup/rollup-win32-x64-gnu": "4.52.4", - "@rollup/rollup-win32-x64-msvc": "4.52.4", - "fsevents": "~2.3.2" - } - }, - "node_modules/run-parallel": { - "version": "1.2.0", - "resolved": "https://registry.npmmirror.com/run-parallel/-/run-parallel-1.2.0.tgz", - "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", - "dev": true, - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ], - "license": "MIT", - "dependencies": { - "queue-microtask": "^1.2.2" - } - }, - "node_modules/scheduler": { - "version": "0.23.2", - "resolved": "https://registry.npmmirror.com/scheduler/-/scheduler-0.23.2.tgz", - "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", - "license": "MIT", - "dependencies": { - "loose-envify": "^1.1.0" - } - }, - "node_modules/scroll-into-view-if-needed": { - "version": "3.1.0", - "resolved": "https://registry.npmmirror.com/scroll-into-view-if-needed/-/scroll-into-view-if-needed-3.1.0.tgz", - "integrity": "sha512-49oNpRjWRvnU8NyGVmUaYG4jtTkNonFZI86MmGRDqBphEK2EXT9gdEUoQPZhuBM8yWHxCWbobltqYO5M4XrUvQ==", - "license": "MIT", - "dependencies": { - "compute-scroll-into-view": "^3.0.2" - } - }, - "node_modules/semver": { - "version": "7.7.3", - "resolved": "https://registry.npmmirror.com/semver/-/semver-7.7.3.tgz", - "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", - "dev": true, - "license": "ISC", - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/shebang-command": { - "version": "2.0.0", - "resolved": "https://registry.npmmirror.com/shebang-command/-/shebang-command-2.0.0.tgz", - "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "shebang-regex": "^3.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/shebang-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmmirror.com/shebang-regex/-/shebang-regex-3.0.0.tgz", - "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/signal-exit": { - "version": "4.1.0", - "resolved": "https://registry.npmmirror.com/signal-exit/-/signal-exit-4.1.0.tgz", - "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/source-map-js": { - "version": "1.2.1", - "resolved": "https://registry.npmmirror.com/source-map-js/-/source-map-js-1.2.1.tgz", - "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", - "dev": true, - "license": "BSD-3-Clause", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/space-separated-tokens": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/space-separated-tokens/-/space-separated-tokens-2.0.2.tgz", - "integrity": "sha512-PEGlAwrG8yXGXRjW32fGbg66JAlOAwbObuqVoJpv/mRgoWDQfgH1wDPvtzWyUSNAXBGSk8h755YDbbcEy3SH2Q==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ssf": { - "version": "0.11.2", - "resolved": "https://registry.npmmirror.com/ssf/-/ssf-0.11.2.tgz", - "integrity": "sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==", - "license": "Apache-2.0", - "dependencies": { - "frac": "~1.1.2" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/stackblur-canvas": { - "version": "2.7.0", - "resolved": "https://registry.npmmirror.com/stackblur-canvas/-/stackblur-canvas-2.7.0.tgz", - "integrity": "sha512-yf7OENo23AGJhBriGx0QivY5JP6Y1HbrrDI6WLt6C5auYZXlQrheoY8hD4ibekFKz1HOfE48Ww8kMWMnJD/zcQ==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=0.1.14" - } - }, - "node_modules/string-convert": { - "version": "0.2.1", - "resolved": "https://registry.npmmirror.com/string-convert/-/string-convert-0.2.1.tgz", - "integrity": "sha512-u/1tdPl4yQnPBjnVrmdLo9gtuLvELKsAoRapekWggdiQNvvvum+jYF329d84NAa660KQw7pB2n36KrIKVoXa3A==", - "license": "MIT" - }, - "node_modules/string-width": { - "version": "5.1.2", - "resolved": "https://registry.npmmirror.com/string-width/-/string-width-5.1.2.tgz", - "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", - "dev": true, - "license": "MIT", - "dependencies": { - "eastasianwidth": "^0.2.0", - "emoji-regex": "^9.2.2", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/string-width-cjs": { - "name": "string-width", - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/string-width-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/string-width-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/stringify-entities": { - "version": "4.0.4", - "resolved": "https://registry.npmmirror.com/stringify-entities/-/stringify-entities-4.0.4.tgz", - "integrity": "sha512-IwfBptatlO+QCJUo19AqvrPNqlVMpW9YEL2LIVY+Rpv2qsjCGxaDLNRgeGsQWJhfItebuJhsGSLjaBbNSQ+ieg==", - "license": "MIT", - "dependencies": { - "character-entities-html4": "^2.0.0", - "character-entities-legacy": "^3.0.0" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/strip-ansi": { - "version": "7.1.2", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-7.1.2.tgz", - "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^6.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/strip-ansi?sponsor=1" - } - }, - "node_modules/strip-ansi-cjs": { - "name": "strip-ansi", - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/strip-json-comments": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/strip-json-comments/-/strip-json-comments-3.1.1.tgz", - "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/style-to-js": { - "version": "1.1.17", - "resolved": "https://registry.npmmirror.com/style-to-js/-/style-to-js-1.1.17.tgz", - "integrity": "sha512-xQcBGDxJb6jjFCTzvQtfiPn6YvvP2O8U1MDIPNfJQlWMYfktPy+iGsHE7cssjs7y84d9fQaK4UF3RIJaAHSoYA==", - "license": "MIT", - "dependencies": { - "style-to-object": "1.0.9" - } - }, - "node_modules/style-to-object": { - "version": "1.0.9", - "resolved": "https://registry.npmmirror.com/style-to-object/-/style-to-object-1.0.9.tgz", - "integrity": "sha512-G4qppLgKu/k6FwRpHiGiKPaPTFcG3g4wNVX/Qsfu+RqQM30E7Tyu/TEgxcL9PNLF5pdRLwQdE3YKKf+KF2Dzlw==", - "license": "MIT", - "dependencies": { - "inline-style-parser": "0.2.4" - } - }, - "node_modules/stylis": { - "version": "4.3.6", - "resolved": "https://registry.npmmirror.com/stylis/-/stylis-4.3.6.tgz", - "integrity": "sha512-yQ3rwFWRfwNUY7H5vpU0wfdkNSnvnJinhF9830Swlaxl03zsOjCfmX0ugac+3LtK0lYSgwL/KXc8oYL3mG4YFQ==", - "license": "MIT" - }, - "node_modules/sucrase": { - "version": "3.35.0", - "resolved": "https://registry.npmmirror.com/sucrase/-/sucrase-3.35.0.tgz", - "integrity": "sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==", - "dev": true, - "license": "MIT", - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.2", - "commander": "^4.0.0", - "glob": "^10.3.10", - "lines-and-columns": "^1.1.6", - "mz": "^2.7.0", - "pirates": "^4.0.1", - "ts-interface-checker": "^0.1.9" - }, - "bin": { - "sucrase": "bin/sucrase", - "sucrase-node": "bin/sucrase-node" - }, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmmirror.com/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "license": "MIT", - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/supports-preserve-symlinks-flag": { - "version": "1.0.0", - "resolved": "https://registry.npmmirror.com/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", - "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/svg-pathdata": { - "version": "6.0.3", - "resolved": "https://registry.npmmirror.com/svg-pathdata/-/svg-pathdata-6.0.3.tgz", - "integrity": "sha512-qsjeeq5YjBZ5eMdFuUa4ZosMLxgr5RZ+F+Y1OrDhuOCEInRMA3x74XdBtggJcj9kOeInz0WE+LgCPDkZFlBYJw==", - "license": "MIT", - "optional": true, - "engines": { - "node": ">=12.0.0" - } - }, - "node_modules/tailwindcss": { - "version": "3.4.18", - "resolved": "https://registry.npmmirror.com/tailwindcss/-/tailwindcss-3.4.18.tgz", - "integrity": "sha512-6A2rnmW5xZMdw11LYjhcI5846rt9pbLSabY5XPxo+XWdxwZaFEn47Go4NzFiHu9sNNmr/kXivP1vStfvMaK1GQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@alloc/quick-lru": "^5.2.0", - "arg": "^5.0.2", - "chokidar": "^3.6.0", - "didyoumean": "^1.2.2", - "dlv": "^1.1.3", - "fast-glob": "^3.3.2", - "glob-parent": "^6.0.2", - "is-glob": "^4.0.3", - "jiti": "^1.21.7", - "lilconfig": "^3.1.3", - "micromatch": "^4.0.8", - "normalize-path": "^3.0.0", - "object-hash": "^3.0.0", - "picocolors": "^1.1.1", - "postcss": "^8.4.47", - "postcss-import": "^15.1.0", - "postcss-js": "^4.0.1", - "postcss-load-config": "^4.0.2 || ^5.0 || ^6.0", - "postcss-nested": "^6.2.0", - "postcss-selector-parser": "^6.1.2", - "resolve": "^1.22.8", - "sucrase": "^3.35.0" - }, - "bin": { - "tailwind": "lib/cli.js", - "tailwindcss": "lib/cli.js" - }, - "engines": { - "node": ">=14.0.0" - } - }, - "node_modules/text-segmentation": { - "version": "1.0.3", - "resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz", - "integrity": "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw==", - "license": "MIT", - "dependencies": { - "utrie": "^1.0.2" - } - }, - "node_modules/thenify": { - "version": "3.3.1", - "resolved": "https://registry.npmmirror.com/thenify/-/thenify-3.3.1.tgz", - "integrity": "sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==", - "dev": true, - "license": "MIT", - "dependencies": { - "any-promise": "^1.0.0" - } - }, - "node_modules/thenify-all": { - "version": "1.6.0", - "resolved": "https://registry.npmmirror.com/thenify-all/-/thenify-all-1.6.0.tgz", - "integrity": "sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==", - "dev": true, - "license": "MIT", - "dependencies": { - "thenify": ">= 3.1.0 < 4" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/throttle-debounce": { - "version": "5.0.2", - "resolved": "https://registry.npmmirror.com/throttle-debounce/-/throttle-debounce-5.0.2.tgz", - "integrity": "sha512-B71/4oyj61iNH0KeCamLuE2rmKuTO5byTOSVwECM5FA7TiAiAW+UqTKZ9ERueC4qvgSttUhdmq1mXC3kJqGX7A==", - "license": "MIT", - "engines": { - "node": ">=12.22" - } - }, - "node_modules/tinyglobby": { - "version": "0.2.15", - "resolved": "https://registry.npmmirror.com/tinyglobby/-/tinyglobby-0.2.15.tgz", - "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "fdir": "^6.5.0", - "picomatch": "^4.0.3" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/SuperchupuDev" - } - }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, - "node_modules/toggle-selection": { - "version": "1.0.6", - "resolved": "https://registry.npmmirror.com/toggle-selection/-/toggle-selection-1.0.6.tgz", - "integrity": "sha512-BiZS+C1OS8g/q2RRbJmy59xpyghNBqrr6k5L/uKBGRsTfxmu3ffiRnd8mlGPUVayg8pvfi5urfnu8TU7DVOkLQ==", - "license": "MIT" - }, - "node_modules/trim-lines": { - "version": "3.0.1", - "resolved": "https://registry.npmmirror.com/trim-lines/-/trim-lines-3.0.1.tgz", - "integrity": "sha512-kRj8B+YHZCc9kQYdWfJB2/oUl9rA99qbowYYBtr4ui4mZyAQ2JpvVBd/6U2YloATfqBhBTSMhTpgBHtU0Mf3Rg==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/trough": { - "version": "2.2.0", - "resolved": "https://registry.npmmirror.com/trough/-/trough-2.2.0.tgz", - "integrity": "sha512-tmMpK00BjZiUyVyvrBK7knerNgmgvcV/KLVyuma/SC+TQN167GrMRciANTz09+k3zW8L8t60jWO1GpfkZdjTaw==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/ts-api-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmmirror.com/ts-api-utils/-/ts-api-utils-2.1.0.tgz", - "integrity": "sha512-CUgTZL1irw8u29bzrOD/nH85jqyc74D6SshFgujOIA7osm2Rz7dYH77agkx7H4FBNxDq7Cjf+IjaX/8zwFW+ZQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=18.12" - }, - "peerDependencies": { - "typescript": ">=4.8.4" - } - }, - "node_modules/ts-interface-checker": { - "version": "0.1.13", - "resolved": "https://registry.npmmirror.com/ts-interface-checker/-/ts-interface-checker-0.1.13.tgz", - "integrity": "sha512-Y/arvbn+rrz3JCKl9C4kVNfTfSm2/mEp5FSz5EsZSANGPSlQrpRI5M4PKF+mJnE52jOO90PnPSc3Ur3bTQw0gA==", - "dev": true, - "license": "Apache-2.0" - }, - "node_modules/type-check": { - "version": "0.4.0", - "resolved": "https://registry.npmmirror.com/type-check/-/type-check-0.4.0.tgz", - "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", - "dev": true, - "license": "MIT", - "dependencies": { - "prelude-ls": "^1.2.1" - }, - "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/typescript": { - "version": "5.9.3", - "resolved": "https://registry.npmmirror.com/typescript/-/typescript-5.9.3.tgz", - "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, - "license": "Apache-2.0", - "bin": { - "tsc": "bin/tsc", - "tsserver": "bin/tsserver" - }, - "engines": { - "node": ">=14.17" - } - }, - "node_modules/unified": { - "version": "11.0.5", - "resolved": "https://registry.npmmirror.com/unified/-/unified-11.0.5.tgz", - "integrity": "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "bail": "^2.0.0", - "devlop": "^1.0.0", - "extend": "^3.0.0", - "is-plain-obj": "^4.0.0", - "trough": "^2.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-is": { - "version": "6.0.0", - "resolved": "https://registry.npmmirror.com/unist-util-is/-/unist-util-is-6.0.0.tgz", - "integrity": "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-position": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/unist-util-position/-/unist-util-position-5.0.0.tgz", - "integrity": "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-stringify-position": { - "version": "4.0.0", - "resolved": "https://registry.npmmirror.com/unist-util-stringify-position/-/unist-util-stringify-position-4.0.0.tgz", - "integrity": "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit": { - "version": "5.0.0", - "resolved": "https://registry.npmmirror.com/unist-util-visit/-/unist-util-visit-5.0.0.tgz", - "integrity": "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0", - "unist-util-visit-parents": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/unist-util-visit-parents": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/unist-util-visit-parents/-/unist-util-visit-parents-6.0.1.tgz", - "integrity": "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-is": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmmirror.com/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", - "dev": true, - "funding": [ - { - "type": "opencollective", - "url": "https://opencollective.com/browserslist" - }, - { - "type": "tidelift", - "url": "https://tidelift.com/funding/github/npm/browserslist" - }, - { - "type": "github", - "url": "https://github.com/sponsors/ai" - } - ], - "license": "MIT", - "dependencies": { - "escalade": "^3.2.0", - "picocolors": "^1.1.1" - }, - "bin": { - "update-browserslist-db": "cli.js" - }, - "peerDependencies": { - "browserslist": ">= 4.21.0" - } - }, - "node_modules/uri-js": { - "version": "4.4.1", - "resolved": "https://registry.npmmirror.com/uri-js/-/uri-js-4.4.1.tgz", - "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", - "dev": true, - "license": "BSD-2-Clause", - "dependencies": { - "punycode": "^2.1.0" - } - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", - "dev": true, - "license": "MIT" - }, - "node_modules/utrie": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/utrie/-/utrie-1.0.2.tgz", - "integrity": "sha512-1MLa5ouZiOmQzUbjbu9VmjLzn1QLXBhwpUa7kdLUQK+KQ5KA9I1vk5U4YHe/X2Ch7PYnJfWuWT+VbuxbGwljhw==", - "license": "MIT", - "dependencies": { - "base64-arraybuffer": "^1.0.2" - } - }, - "node_modules/vfile": { - "version": "6.0.3", - "resolved": "https://registry.npmmirror.com/vfile/-/vfile-6.0.3.tgz", - "integrity": "sha512-KzIbH/9tXat2u30jf+smMwFCsno4wHVdNmzFyL+T/L3UGqqk6JKfVqOFOZEpZSHADH1k40ab6NUIXZq422ov3Q==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile-message": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-location": { - "version": "5.0.3", - "resolved": "https://registry.npmmirror.com/vfile-location/-/vfile-location-5.0.3.tgz", - "integrity": "sha512-5yXvWDEgqeiYiBe1lbxYF7UMAIm/IcopxMHrMQDq3nvKcjPKIhZklUKL+AE7J7uApI4kwe2snsK+eI6UTj9EHg==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "vfile": "^6.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vfile-message": { - "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/vfile-message/-/vfile-message-4.0.3.tgz", - "integrity": "sha512-QTHzsGd1EhbZs4AsQ20JX1rC3cOlt/IWJruk893DfLRr57lcnOeMaWG4K0JrRta4mIJZKth2Au3mM3u03/JWKw==", - "license": "MIT", - "dependencies": { - "@types/unist": "^3.0.0", - "unist-util-stringify-position": "^4.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/unified" - } - }, - "node_modules/vite": { - "version": "6.3.6", - "resolved": "https://registry.npmmirror.com/vite/-/vite-6.3.6.tgz", - "integrity": "sha512-0msEVHJEScQbhkbVTb/4iHZdJ6SXp/AvxL2sjwYQFfBqleHtnCqv1J3sa9zbWz/6kW1m9Tfzn92vW+kZ1WV6QA==", - "dev": true, - "license": "MIT", - "dependencies": { - "esbuild": "^0.25.0", - "fdir": "^6.4.4", - "picomatch": "^4.0.2", - "postcss": "^8.5.3", - "rollup": "^4.34.9", - "tinyglobby": "^0.2.13" - }, - "bin": { - "vite": "bin/vite.js" - }, - "engines": { - "node": "^18.0.0 || ^20.0.0 || >=22.0.0" - }, - "funding": { - "url": "https://github.com/vitejs/vite?sponsor=1" - }, - "optionalDependencies": { - "fsevents": "~2.3.3" - }, - "peerDependencies": { - "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", - "jiti": ">=1.21.0", - "less": "*", - "lightningcss": "^1.21.0", - "sass": "*", - "sass-embedded": "*", - "stylus": "*", - "sugarss": "*", - "terser": "^5.16.0", - "tsx": "^4.8.1", - "yaml": "^2.4.2" - }, - "peerDependenciesMeta": { - "@types/node": { - "optional": true - }, - "jiti": { - "optional": true - }, - "less": { - "optional": true - }, - "lightningcss": { - "optional": true - }, - "sass": { - "optional": true - }, - "sass-embedded": { - "optional": true - }, - "stylus": { - "optional": true - }, - "sugarss": { - "optional": true - }, - "terser": { - "optional": true - }, - "tsx": { - "optional": true - }, - "yaml": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/fdir": { - "version": "6.5.0", - "resolved": "https://registry.npmmirror.com/fdir/-/fdir-6.5.0.tgz", - "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12.0.0" - }, - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.3", - "resolved": "https://registry.npmmirror.com/picomatch/-/picomatch-4.0.3.tgz", - "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, - "node_modules/web-namespaces": { - "version": "2.0.1", - "resolved": "https://registry.npmmirror.com/web-namespaces/-/web-namespaces-2.0.1.tgz", - "integrity": "sha512-bKr1DkiNa2krS7qxNtdrtHAmzuYGFQLiQ13TsorsdT6ULTkPLKuu5+GsFpDlg6JFjUTwX2DyhMPG2be8uPrqsQ==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - }, - "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmmirror.com/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", - "dev": true, - "license": "ISC", - "dependencies": { - "isexe": "^2.0.0" - }, - "bin": { - "node-which": "bin/node-which" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/wmf": { - "version": "1.0.2", - "resolved": "https://registry.npmmirror.com/wmf/-/wmf-1.0.2.tgz", - "integrity": "sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/word": { - "version": "0.3.0", - "resolved": "https://registry.npmmirror.com/word/-/word-0.3.0.tgz", - "integrity": "sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==", - "license": "Apache-2.0", - "engines": { - "node": ">=0.8" - } - }, - "node_modules/word-wrap": { - "version": "1.2.5", - "resolved": "https://registry.npmmirror.com/word-wrap/-/word-wrap-1.2.5.tgz", - "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/wrap-ansi": { - "version": "8.1.0", - "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz", - "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^6.1.0", - "string-width": "^5.0.1", - "strip-ansi": "^7.0.1" - }, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs": { - "name": "wrap-ansi", - "version": "7.0.0", - "resolved": "https://registry.npmmirror.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz", - "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-styles": "^4.0.0", - "string-width": "^4.1.0", - "strip-ansi": "^6.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/wrap-ansi?sponsor=1" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { - "version": "5.0.1", - "resolved": "https://registry.npmmirror.com/ansi-regex/-/ansi-regex-5.0.1.tgz", - "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmmirror.com/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true, - "license": "MIT" - }, - "node_modules/wrap-ansi-cjs/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmmirror.com/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "license": "MIT", - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmmirror.com/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "license": "MIT", - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "6.2.3", - "resolved": "https://registry.npmmirror.com/ansi-styles/-/ansi-styles-6.2.3.tgz", - "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/xlsx": { - "version": "0.18.5", - "resolved": "https://registry.npmmirror.com/xlsx/-/xlsx-0.18.5.tgz", - "integrity": "sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==", - "license": "Apache-2.0", - "dependencies": { - "adler-32": "~1.3.0", - "cfb": "~1.2.1", - "codepage": "~1.15.0", - "crc-32": "~1.2.1", - "ssf": "~0.11.2", - "wmf": "~1.0.1", - "word": "~0.3.0" - }, - "bin": { - "xlsx": "bin/xlsx.njs" - }, - "engines": { - "node": ">=0.8" - } - }, - "node_modules/xtend": { - "version": "4.0.2", - "resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz", - "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==", - "license": "MIT", - "engines": { - "node": ">=0.4" - } - }, - "node_modules/yallist": { - "version": "3.1.1", - "resolved": "https://registry.npmmirror.com/yallist/-/yallist-3.1.1.tgz", - "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", - "dev": true, - "license": "ISC" - }, - "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmmirror.com/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/zwitch": { - "version": "2.0.4", - "resolved": "https://registry.npmmirror.com/zwitch/-/zwitch-2.0.4.tgz", - "integrity": "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==", - "license": "MIT", - "funding": { - "type": "github", - "url": "https://github.com/sponsors/wooorm" - } - } - } -} diff --git a/frontend/package.json b/frontend/package.json deleted file mode 100644 index a0d0b1da..00000000 --- a/frontend/package.json +++ /dev/null @@ -1,46 +0,0 @@ -{ - "name": "ai-clinical-frontend", - "version": "1.0.0", - "type": "module", - "description": "AI Clinical Research Platform - Frontend", - "scripts": { - "dev": "vite", - "build": "tsc && vite build", - "preview": "vite preview", - "lint": "eslint . --ext ts,tsx --report-unused-disable-directives --max-warnings 0" - }, - "dependencies": { - "@ant-design/icons": "^5.5.2", - "@types/js-yaml": "^4.0.9", - "@types/react-syntax-highlighter": "^15.5.13", - "antd": "^5.22.5", - "axios": "^1.12.2", - "html2canvas": "^1.4.1", - "js-yaml": "^4.1.0", - "jspdf": "^3.0.3", - "lucide-react": "^0.562.0", - "react": "^18.3.1", - "react-dom": "^18.3.1", - "react-markdown": "^10.1.0", - "react-router-dom": "^6.28.0", - "react-syntax-highlighter": "^15.6.6", - "rehype-raw": "^7.0.0", - "remark-gfm": "^4.0.1", - "xlsx": "^0.18.5" - }, - "devDependencies": { - "@types/react": "^18.3.18", - "@types/react-dom": "^18.3.5", - "@typescript-eslint/eslint-plugin": "^8.20.0", - "@typescript-eslint/parser": "^8.20.0", - "@vitejs/plugin-react": "^4.3.4", - "autoprefixer": "^10.4.20", - "eslint": "^9.18.0", - "eslint-plugin-react-hooks": "^5.1.0", - "eslint-plugin-react-refresh": "^0.4.16", - "postcss": "^8.4.49", - "tailwindcss": "^3.4.17", - "typescript": "^5.7.3", - "vite": "^6.0.7" - } -} diff --git a/frontend/postcss.config.js b/frontend/postcss.config.js deleted file mode 100644 index 7139293e..00000000 --- a/frontend/postcss.config.js +++ /dev/null @@ -1,10 +0,0 @@ -export default { - plugins: { - tailwindcss: {}, - autoprefixer: {}, - }, -} - - - - diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx deleted file mode 100644 index 73b663da..00000000 --- a/frontend/src/App.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import { Routes, Route, Navigate } from 'react-router-dom' -import MainLayout from './layouts/MainLayout' -import HomePage from './pages/HomePage' -import AgentChatPage from './pages/AgentChatPage' -import ChatPage from './pages/ChatPage' -import KnowledgePage from './pages/KnowledgePage' -import HistoryPage from './pages/HistoryPage' -import ReviewPage from './pages/ReviewPage' -import { RvwDashboard } from './pages/rvw' - -function App() { - return ( - - {/* RVW模块 - 独立布局 */} - } /> - - {/* 主系统 */} - }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - - ) -} - -export default App - diff --git a/frontend/src/api/agentApi.ts b/frontend/src/api/agentApi.ts deleted file mode 100644 index fc3142b5..00000000 --- a/frontend/src/api/agentApi.ts +++ /dev/null @@ -1,71 +0,0 @@ -import request from './request'; - -export interface AgentConfig { - id: string; - name: string; - nameEn: string; - description: string; - category: string; - icon: string; - enabled: boolean; - systemPromptFile: string; - userPromptTemplateFile: string; - models: { - [modelName: string]: { - temperature: number; - maxTokens: number; - topP?: number; - }; - }; - ragEnabled: boolean; - requiresProject: boolean; - outputFormat: 'text' | 'structured' | 'document'; - tags: string[]; -} - -export interface ApiResponse { - success: boolean; - data?: T; - message?: string; - error?: string; -} - -export const agentApi = { - // 获取所有智能体列表 - getAll: async (): Promise> => { - return await request.get('/agents'); - }, - - // 获取启用的智能体列表 - getEnabled: async (): Promise> => { - return await request.get('/agents/enabled'); - }, - - // 获取单个智能体详情 - getById: async (id: string): Promise> => { - return await request.get(`/agents/${id}`); - }, - - // 根据分类获取智能体 - getByCategory: async (category: string): Promise> => { - return await request.get(`/agents/by-category?category=${encodeURIComponent(category)}`); - }, - - // 获取智能体的系统Prompt - getSystemPrompt: async (id: string): Promise> => { - return await request.get(`/agents/${id}/system-prompt`); - }, - - // 渲染用户Prompt - renderPrompt: async ( - id: string, - data: { - projectBackground?: string; - userInput: string; - knowledgeBaseContext?: string; - } - ): Promise> => { - return await request.post(`/agents/${id}/render-prompt`, data); - }, -}; - diff --git a/frontend/src/api/batchApi.ts b/frontend/src/api/batchApi.ts deleted file mode 100644 index 2b3d021b..00000000 --- a/frontend/src/api/batchApi.ts +++ /dev/null @@ -1,146 +0,0 @@ -/** - * Phase 3: 批处理模式 - API封装 - */ - -import request from './request'; - -// ==================== 类型定义 ==================== - -export interface BatchTemplate { - id: string; - name: string; - description: string; - output_fields: Array<{ - key: string; - label: string; - type: string; - description?: string; - }>; -} - -export interface ExecuteBatchParams { - kb_id: string; - document_ids: string[]; - template_type: 'preset' | 'custom'; - template_id?: string; - custom_prompt?: string; - model_type: string; - task_name?: string; -} - -export interface BatchTask { - id: string; - name: string; - status: 'processing' | 'completed' | 'failed'; - total_documents: number; - completed_count: number; - failed_count: number; - model_type: string; - started_at: string; - completed_at?: string; - duration_seconds?: number; - created_at: string; -} - -export interface BatchResult { - id: string; - index: number; - document_id: string; - document_name: string; - status: 'success' | 'failed'; - data: any; - raw_output?: string; - error_message?: string; - processing_time_ms?: number; - tokens_used?: number; - created_at: string; -} - -export interface BatchTaskWithResults { - task: BatchTask; - results: BatchResult[]; -} - -// ==================== API函数 ==================== - -/** - * 获取所有预设模板 - */ -export async function getTemplates(): Promise { - const response = await request.get('/batch/templates'); - return response.data; -} - -/** - * 执行批处理任务 - */ -export async function executeBatch(params: ExecuteBatchParams): Promise<{ - task_id: string; - status: string; - websocket_event: string; -}> { - const response = await request.post('/batch/execute', params); - return response.data; -} - -/** - * 获取任务状态 - */ -export async function getTask(taskId: string): Promise { - const response = await request.get(`/batch/tasks/${taskId}`); - return response.data; -} - -/** - * 获取任务结果 - */ -export async function getTaskResults(taskId: string): Promise { - const response = await request.get(`/batch/tasks/${taskId}/results`); - return response.data; -} - -/** - * 重试失败的文档 - */ -export async function retryFailed(taskId: string): Promise { - await request.post(`/batch/tasks/${taskId}/retry-failed`); -} - -/** - * 轮询任务状态直到完成 - */ -export async function pollTaskUntilComplete( - taskId: string, - onProgress?: (task: BatchTask) => void, - maxAttempts: number = 120, // 最多10分钟(120次 * 5秒) -): Promise { - let attempts = 0; - - while (attempts < maxAttempts) { - const task = await getTask(taskId); - - if (onProgress) { - onProgress(task); - } - - if (task.status === 'completed' || task.status === 'failed') { - return task; - } - - // 等待5秒后再次查询 - await new Promise(resolve => setTimeout(resolve, 5000)); - attempts++; - } - - throw new Error('任务执行超时'); -} - -export default { - getTemplates, - executeBatch, - getTask, - getTaskResults, - retryFailed, - pollTaskUntilComplete, -}; - diff --git a/frontend/src/api/chatApi.ts b/frontend/src/api/chatApi.ts deleted file mode 100644 index c7f25478..00000000 --- a/frontend/src/api/chatApi.ts +++ /dev/null @@ -1,142 +0,0 @@ -/** - * 通用对话API - * 无需项目和智能体概念,纯大模型对话 - */ - -export interface GeneralConversation { - id: string - userId: string - title: string - modelName?: string - createdAt: string - updatedAt: string -} - -export interface GeneralMessage { - id: string - conversationId: string - role: 'user' | 'assistant' - content: string - model?: string - tokens?: number - createdAt: string -} - -export interface SendChatMessageData { - content: string - modelType: string - knowledgeBaseIds?: string[] - documentIds?: string[] // Phase 2: 逐篇精读模式 - 限定文档范围(RAG检索) - fullTextDocumentIds?: string[] // Phase 2: 全文阅读模式 - 传递完整全文 - conversationId?: string -} - -export interface ApiResponse { - success: boolean - data?: T - message?: string -} - -/** - * 发送消息(流式输出) - */ -export const sendMessageStream = async ( - data: SendChatMessageData, - onChunk: (content: string) => void, - onComplete: (conversationId: string) => void, - onError: (error: Error) => void -) => { - try { - console.log('🚀 [chatApi] 发送通用对话消息', data) - - const response = await fetch('/api/v1/chat/stream', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(data), - }) - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`) - } - - const reader = response.body?.getReader() - if (!reader) { - throw new Error('Response body is null') - } - - const decoder = new TextDecoder() - let buffer = '' - let conversationId = data.conversationId || '' - - while (true) { - const { done, value } = await reader.read() - - if (done) { - break - } - - buffer += decoder.decode(value, { stream: true }) - const lines = buffer.split('\n') - buffer = lines.pop() || '' - - for (const line of lines) { - const trimmedLine = line.trim() - if (!trimmedLine || !trimmedLine.startsWith('data:')) { - continue - } - - const dataStr = trimmedLine.slice(5).trim() - - if (dataStr === '[DONE]') { - console.log('✅ [chatApi] 流式输出完成') - onComplete(conversationId) - return - } - - try { - const chunk = JSON.parse(dataStr) - if (chunk.content) { - onChunk(chunk.content) - } - // 从第一个chunk中提取conversationId(如果是新对话) - if (!conversationId && chunk.conversationId) { - conversationId = chunk.conversationId - } - } catch (e) { - console.error('Failed to parse SSE chunk:', e) - } - } - } - } catch (error) { - console.error('❌ [chatApi] 发送消息失败:', error) - onError(error as Error) - } -} - -/** - * 获取对话列表 - */ -export const getConversations = async (): Promise> => { - const response = await fetch('/api/v1/chat/conversations') - return response.json() -} - -/** - * 删除对话 - */ -export const deleteConversation = async (id: string): Promise => { - const response = await fetch(`/api/v1/chat/conversations/${id}`, { - method: 'DELETE', - }) - return response.json() -} - -export default { - sendMessageStream, - getConversations, - deleteConversation, -} - - diff --git a/frontend/src/api/conversationApi.ts b/frontend/src/api/conversationApi.ts deleted file mode 100644 index 25d5530b..00000000 --- a/frontend/src/api/conversationApi.ts +++ /dev/null @@ -1,170 +0,0 @@ -import request from './request'; -import type { ModelType } from '../components/chat/ModelSelector'; - -export interface ApiResponse { - success: boolean; - data?: T; - message?: string; -} - -export interface Message { - id: string; - conversationId: string; - role: 'user' | 'assistant'; - content: string; - model?: string; - metadata?: any; - tokens?: number; - createdAt: string; -} - -export interface Conversation { - id: string; - userId: string; - projectId: string; - agentId: string; - title: string; - modelName: string; - messageCount: number; - totalTokens: number; - metadata?: any; - createdAt: string; - updatedAt: string; - project?: { - id: string; - name: string; - background?: string; - researchType?: string; - }; - messages?: Message[]; -} - -export interface CreateConversationData { - projectId: string; - agentId: string; - title?: string; -} - -export interface SendMessageData { - conversationId: string; - content: string; - modelType: ModelType; - knowledgeBaseIds?: string[]; -} - -export interface SendMessageResponse { - userMessage: Message; - assistantMessage: Message; - usage?: { - promptTokens: number; - completionTokens: number; - totalTokens: number; - }; -} - -// 创建对话 -export const createConversation = (data: CreateConversationData) => { - return request.post>('/conversations', data); -}; - -// 获取对话列表 -export const getConversations = (projectId?: string) => { - const params = projectId ? { projectId } : {}; - return request.get>('/conversations', { params }); -}; - -// 获取对话详情 -export const getConversationById = (id: string) => { - return request.get>(`/conversations/${id}`); -}; - -// 发送消息(非流式) -export const sendMessage = (data: SendMessageData) => { - return request.post>('/conversations/message', data); -}; - -// 发送消息(流式输出) -export const sendMessageStream = async ( - data: SendMessageData, - onChunk: (content: string) => void, - onComplete: () => void, - onError: (error: Error) => void -) => { - try { - // 使用相对路径,通过Vite代理转发到后端 - const response = await fetch('/api/v1/conversations/message/stream', { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(data), - }); - - if (!response.ok) { - throw new Error(`HTTP error! status: ${response.status}`); - } - - const reader = response.body?.getReader(); - if (!reader) { - throw new Error('Response body is null'); - } - - const decoder = new TextDecoder(); - let buffer = ''; - - while (true) { - const { done, value } = await reader.read(); - - if (done) { - break; - } - - buffer += decoder.decode(value, { stream: true }); - const lines = buffer.split('\n'); - buffer = lines.pop() || ''; - - for (const line of lines) { - const trimmedLine = line.trim(); - if (!trimmedLine || !trimmedLine.startsWith('data:')) { - continue; - } - - const dataStr = trimmedLine.slice(5).trim(); - - if (dataStr === '[DONE]') { - onComplete(); - return; - } - - try { - const chunk = JSON.parse(dataStr); - if (chunk.content) { - onChunk(chunk.content); - } - } catch (parseError) { - console.error('Failed to parse SSE data:', parseError); - } - } - } - - onComplete(); - } catch (error) { - console.error('Stream error:', error); - onError(error as Error); - } -}; - -// 删除对话 -export const deleteConversation = (id: string) => { - return request.delete(`/conversations/${id}`); -}; - -export default { - createConversation, - getConversations, - getConversationById, - sendMessage, - sendMessageStream, - deleteConversation, -}; - diff --git a/frontend/src/api/index.ts b/frontend/src/api/index.ts deleted file mode 100644 index eaa09a63..00000000 --- a/frontend/src/api/index.ts +++ /dev/null @@ -1,29 +0,0 @@ -import request from './request' - -// 健康检查 -export const healthCheck = () => { - return request.get('/health') -} - -// API信息 -export const getApiInfo = () => { - return request.get('/') -} - -// 导出各模块API -export * from './projectApi' -export * from './conversationApi' -export * from './knowledgeBaseApi' -export * from './agentApi' -export * from './chatApi' -export * from './batchApi' -export * from './reviewApi' - -export default { - healthCheck, - getApiInfo, -} - - - - diff --git a/frontend/src/api/knowledgeBaseApi.ts b/frontend/src/api/knowledgeBaseApi.ts deleted file mode 100644 index dd0d905b..00000000 --- a/frontend/src/api/knowledgeBaseApi.ts +++ /dev/null @@ -1,238 +0,0 @@ -import axios from 'axios'; - -const API_BASE_URL = import.meta.env.VITE_API_BASE_URL || 'http://localhost:3001'; - -const api = axios.create({ - baseURL: `${API_BASE_URL}/api/v1/pkb/knowledge`, - timeout: 30000, - headers: { - 'Content-Type': 'application/json', - }, -}); - -// 请求拦截器 - 添加认证token -api.interceptors.request.use( - (config) => { - const token = localStorage.getItem('token'); - if (token && config.headers) { - config.headers.Authorization = `Bearer ${token}`; - } - return config; - }, - (error) => { - return Promise.reject(error); - } -); - -// 响应拦截器 - 处理401未授权 -api.interceptors.response.use( - (response) => response, - (error) => { - if (error.response?.status === 401) { - localStorage.removeItem('token'); - window.location.href = '/login'; - } - return Promise.reject(error); - } -); - -/** - * 知识库类型定义 - */ -export interface KnowledgeBase { - id: string; - userId: string; - name: string; - description?: string; - difyDatasetId: string; - fileCount: number; - totalSizeBytes: number; - createdAt: string; - updatedAt: string; - _count?: { - documents: number; - }; -} - -export interface Document { - id: string; - kbId: string; - userId: string; - filename: string; - fileType: string; - fileSizeBytes: number; - fileUrl: string; - difyDocumentId: string; - status: 'uploading' | 'parsing' | 'indexing' | 'completed' | 'error'; - progress: number; - errorMessage?: string; - segmentsCount?: number; - tokensCount?: number; - uploadedAt: string; - processedAt?: string; -} - -export interface CreateKnowledgeBaseRequest { - name: string; - description?: string; -} - -export interface UpdateKnowledgeBaseRequest { - name?: string; - description?: string; -} - -export interface KnowledgeBaseStats { - totalDocuments: number; - completedDocuments: number; - processingDocuments: number; - errorDocuments: number; - totalSizeBytes: number; - totalTokens: number; -} - -/** - * 知识库管理API - */ -export const knowledgeBaseApi = { - /** - * 获取知识库列表 - */ - async getList(): Promise { - const response = await api.get('/knowledge-bases'); - return response.data.data; - }, - - /** - * 获取知识库详情 - */ - async getById(id: string): Promise { - const response = await api.get(`/knowledge-bases/${id}`); - return response.data.data; - }, - - /** - * 创建知识库 - */ - async create(data: CreateKnowledgeBaseRequest): Promise { - const response = await api.post('/knowledge-bases', data); - return response.data.data; - }, - - /** - * 更新知识库 - */ - async update(id: string, data: UpdateKnowledgeBaseRequest): Promise { - const response = await api.put(`/knowledge-bases/${id}`, data); - return response.data.data; - }, - - /** - * 删除知识库 - */ - async delete(id: string): Promise { - await api.delete(`/knowledge-bases/${id}`); - }, - - /** - * 获取知识库统计信息 - */ - async getStats(id: string): Promise { - const response = await api.get(`/knowledge-bases/${id}/stats`); - return response.data.data; - }, - - /** - * 检索知识库 - */ - async search(id: string, query: string, topK: number = 3): Promise { - const response = await api.get(`/knowledge-bases/${id}/search`, { - params: { query, top_k: topK }, - }); - return response.data.data; - }, -}; - -/** - * 文档管理API - */ -export const documentApi = { - /** - * 获取文档列表 - */ - async getList(kbId: string): Promise { - const response = await api.get(`/knowledge-bases/${kbId}/documents`); - return response.data.data; - }, - - /** - * 获取文档详情 - */ - async getById(id: string): Promise { - const response = await api.get(`/documents/${id}`); - return response.data.data; - }, - - /** - * 上传文档 - */ - async upload(kbId: string, file: File, onProgress?: (progress: number) => void): Promise { - const formData = new FormData(); - formData.append('file', file); - - const response = await api.post(`/knowledge-bases/${kbId}/documents`, formData, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - onUploadProgress: (progressEvent) => { - if (progressEvent.total && onProgress) { - const percentCompleted = Math.round((progressEvent.loaded * 100) / progressEvent.total); - onProgress(percentCompleted); - } - }, - }); - return response.data.data; - }, - - /** - * 删除文档 - */ - async delete(id: string): Promise { - await api.delete(`/documents/${id}`); - }, - - /** - * 重新处理文档 - */ - async reprocess(id: string): Promise { - await api.post(`/documents/${id}/reprocess`); - }, - - /** - * Phase 2: 获取文档全文(用于逐篇精读模式) - */ - async getFullText(id: string): Promise { - const response = await api.get(`/documents/${id}/full-text`); - return response.data; - }, -}; - -/** - * Phase 2: 文档选择API(用于全文阅读模式) - */ -export const documentSelectionApi = { - /** - * 获取知识库的文档选择结果 - */ - async getSelection(kbId: string, maxFiles?: number, maxTokens?: number): Promise { - const params = new URLSearchParams(); - if (maxFiles) params.append('max_files', maxFiles.toString()); - if (maxTokens) params.append('max_tokens', maxTokens.toString()); - - const url = `/knowledge-bases/${kbId}/document-selection${params.toString() ? '?' + params.toString() : ''}`; - const response = await api.get(url); - return response.data.data; // 修复:返回内层的data对象 - }, -}; - - diff --git a/frontend/src/api/projectApi.ts b/frontend/src/api/projectApi.ts deleted file mode 100644 index c7d8f676..00000000 --- a/frontend/src/api/projectApi.ts +++ /dev/null @@ -1,52 +0,0 @@ -import request from './request'; -import type { Project } from '../stores/useProjectStore'; - -export interface ApiResponse { - success: boolean; - data?: T; - message?: string; - error?: string; -} - -export interface CreateProjectRequest { - name: string; - background: string; - researchType: 'observational' | 'interventional'; -} - -export interface UpdateProjectRequest { - name?: string; - background?: string; - researchType?: 'observational' | 'interventional'; -} - -export const projectApi = { - // 获取项目列表 - getProjects: async (): Promise> => { - return await request.get('/projects'); - }, - - // 获取单个项目详情 - getProjectById: async (id: string): Promise> => { - return await request.get(`/projects/${id}`); - }, - - // 创建项目 - createProject: async (data: CreateProjectRequest): Promise> => { - return await request.post('/projects', data); - }, - - // 更新项目 - updateProject: async ( - id: string, - data: UpdateProjectRequest - ): Promise> => { - return await request.put(`/projects/${id}`, data); - }, - - // 删除项目 - deleteProject: async (id: string): Promise => { - return await request.delete(`/projects/${id}`); - }, -}; - diff --git a/frontend/src/api/request.ts b/frontend/src/api/request.ts deleted file mode 100644 index 4f44686d..00000000 --- a/frontend/src/api/request.ts +++ /dev/null @@ -1,59 +0,0 @@ -import axios, { AxiosInstance, AxiosResponse } from 'axios' - -// 创建axios实例 -const request: AxiosInstance = axios.create({ - baseURL: '/api/v1', - timeout: 30000, - headers: { - 'Content-Type': 'application/json', - }, -}) - -// 请求拦截器 -request.interceptors.request.use( - (config) => { - // 添加token - const token = localStorage.getItem('token') - if (token && config.headers) { - config.headers.Authorization = `Bearer ${token}` - } - return config - }, - (error) => { - return Promise.reject(error) - } -) - -// 响应拦截器 -request.interceptors.response.use( - (response: AxiosResponse) => { - return response.data - }, - (error) => { - if (error.response) { - const { status } = error.response - switch (status) { - case 401: - // 未授权,清除token并跳转登录 - localStorage.removeItem('token') - window.location.href = '/login' - break - case 403: - console.error('没有权限访问该资源') - break - case 404: - console.error('请求的资源不存在') - break - case 500: - console.error('服务器错误') - break - default: - console.error(`请求错误: ${status}`) - } - } - return Promise.reject(error) - } -) - -export default request - diff --git a/frontend/src/api/reviewApi.ts b/frontend/src/api/reviewApi.ts deleted file mode 100644 index 4c7bd0ba..00000000 --- a/frontend/src/api/reviewApi.ts +++ /dev/null @@ -1,360 +0,0 @@ -/** - * 稿件审查功能 - API封装 - */ - -import request from './request'; -import axios from 'axios'; - -// ==================== 类型定义 ==================== - -/** - * 稿约规范性评估 - 单项 - */ -export interface EditorialItem { - criterion: string; - status: 'pass' | 'warning' | 'fail'; - score: number; - issues: string[]; - suggestions: string[]; -} - -/** - * 稿约规范性评估结果 - */ -export interface EditorialReview { - overall_score: number; - summary: string; - items: EditorialItem[]; -} - -/** - * 方法学评估 - 问题项 - */ -export interface MethodologyIssue { - type: string; - severity: 'major' | 'minor'; - description: string; - location: string; - suggestion: string; -} - -/** - * 方法学评估 - 部分 - */ -export interface MethodologyPart { - part: string; - score: number; - issues: MethodologyIssue[]; -} - -/** - * 方法学评估结果 - */ -export interface MethodologyReview { - overall_score: number; - summary: string; - parts: MethodologyPart[]; -} - -/** - * 审查任务状态 - */ -export type ReviewTaskStatus = - | 'pending' // 等待处理 - | 'extracting' // 提取文档文本 - | 'reviewing_editorial' // 稿约规范性评估 - | 'reviewing_methodology' // 方法学评估 - | 'completed' // 完成 - | 'failed'; // 失败 - -/** - * 审查任务 - */ -export interface ReviewTask { - id: string; - fileName: string; - fileSize: number; - status: ReviewTaskStatus; - wordCount?: number; - overallScore?: number; - modelUsed?: string; - createdAt: string; - startedAt?: string; - completedAt?: string; - durationSeconds?: number; - errorMessage?: string; -} - -/** - * 完整审查报告 - */ -export interface ReviewReport { - taskId: string; - fileName: string; - wordCount?: number; - modelUsed?: string; - overallScore?: number; - editorialReview?: EditorialReview; - methodologyReview?: MethodologyReview; - completedAt?: string; - durationSeconds?: number; -} - -/** - * 任务列表项(简化版) - */ -export interface ReviewTaskListItem { - id: string; - fileName: string; - fileSize: number; - status: ReviewTaskStatus; - overallScore?: number; - modelUsed?: string; - createdAt: string; - completedAt?: string; - durationSeconds?: number; - wordCount?: number; -} - -/** - * 任务列表响应 - */ -export interface ReviewTaskListResponse { - tasks: ReviewTaskListItem[]; - pagination: { - page: number; - limit: number; - total: number; - totalPages: number; - }; -} - -/** - * 上传参数 - */ -export interface UploadManuscriptParams { - file: File; - modelType?: 'deepseek-v3' | 'qwen3-72b' | 'qwen-long'; -} - -// ==================== API函数 ==================== - -/** - * 上传稿件并开始审查 - */ -export async function uploadManuscript(params: UploadManuscriptParams): Promise<{ - taskId: string; - fileName: string; - status: ReviewTaskStatus; - createdAt: string; -}> { - const formData = new FormData(); - formData.append('file', params.file); - formData.append('modelType', params.modelType || 'deepseek-v3'); - - // 使用axios直接调用,因为FormData需要特殊处理 - const response = await axios.post('/api/v1/review/upload', formData, { - headers: { - 'Content-Type': 'multipart/form-data', - }, - timeout: 30000, - }); - - return response.data.data; -} - -/** - * 获取任务状态 - */ -export async function getTaskStatus(taskId: string): Promise { - const response = await request.get(`/review/tasks/${taskId}`); - return response.data; -} - -/** - * 获取审查报告(完整) - */ -export async function getTaskReport(taskId: string): Promise { - const response = await request.get(`/review/tasks/${taskId}/report`); - return response.data; -} - -/** - * 获取任务列表 - */ -export async function getTaskList(page: number = 1, limit: number = 20): Promise { - const response = await request.get('/review/tasks', { - params: { page, limit }, - }); - - return { - tasks: response.data, - pagination: response.pagination, - }; -} - -/** - * 删除任务 - */ -export async function deleteTask(taskId: string): Promise { - await request.delete(`/review/tasks/${taskId}`); -} - -/** - * 轮询任务状态直到完成 - * @param taskId 任务ID - * @param onProgress 进度回调 - * @param maxAttempts 最大尝试次数(默认60次,即5分钟) - * @returns 完成的任务 - */ -export async function pollTaskUntilComplete( - taskId: string, - onProgress?: (task: ReviewTask) => void, - maxAttempts: number = 60, -): Promise { - let attempts = 0; - - while (attempts < maxAttempts) { - const task = await getTaskStatus(taskId); - - if (onProgress) { - onProgress(task); - } - - if (task.status === 'completed' || task.status === 'failed') { - return task; - } - - // 等待5秒后再次查询 - await new Promise(resolve => setTimeout(resolve, 5000)); - attempts++; - } - - throw new Error('任务执行超时(超过5分钟)'); -} - -/** - * 获取任务状态的中文描述 - */ -export function getStatusText(status: ReviewTaskStatus): string { - const statusMap: Record = { - pending: '等待处理', - extracting: '提取文档文本', - reviewing_editorial: '稿约规范性评估', - reviewing_methodology: '方法学评估', - completed: '评估完成', - failed: '评估失败', - }; - return statusMap[status] || status; -} - -/** - * 获取任务状态的颜色 - */ -export function getStatusColor(status: ReviewTaskStatus): string { - const colorMap: Record = { - pending: 'default', - extracting: 'processing', - reviewing_editorial: 'processing', - reviewing_methodology: 'processing', - completed: 'success', - failed: 'error', - }; - return colorMap[status] || 'default'; -} - -/** - * 格式化文件大小 - */ -export function formatFileSize(bytes: number): string { - if (bytes < 1024) return `${bytes} B`; - if (bytes < 1024 * 1024) return `${(bytes / 1024).toFixed(1)} KB`; - return `${(bytes / 1024 / 1024).toFixed(1)} MB`; -} - -/** - * 格式化时长 - */ -export function formatDuration(seconds: number): string { - if (seconds < 60) return `${seconds}秒`; - const minutes = Math.floor(seconds / 60); - const remainingSeconds = seconds % 60; - return `${minutes}分${remainingSeconds}秒`; -} - -/** - * 获取评分等级 - */ -export function getScoreLevel(score: number): { - level: 'excellent' | 'good' | 'fair' | 'poor'; - text: string; - color: string; -} { - if (score >= 90) { - return { level: 'excellent', text: '优秀', color: '#52c41a' }; - } else if (score >= 80) { - return { level: 'good', text: '良好', color: '#1890ff' }; - } else if (score >= 60) { - return { level: 'fair', text: '及格', color: '#faad14' }; - } else { - return { level: 'poor', text: '不及格', color: '#f5222d' }; - } -} - -// 默认导出 -export default { - uploadManuscript, - getTaskStatus, - getTaskReport, - getTaskList, - deleteTask, - pollTaskUntilComplete, - getStatusText, - getStatusColor, - formatFileSize, - formatDuration, - getScoreLevel, -}; - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/frontend/src/components/CreateProjectDialog.tsx b/frontend/src/components/CreateProjectDialog.tsx deleted file mode 100644 index ba53fcab..00000000 --- a/frontend/src/components/CreateProjectDialog.tsx +++ /dev/null @@ -1,105 +0,0 @@ -import { useState } from 'react'; -import { Modal, Form, Input, Radio, message } from 'antd'; -import { useProjectStore } from '../stores/useProjectStore'; -import { projectApi } from '../api/projectApi'; - -const { TextArea } = Input; - -export const CreateProjectDialog = () => { - const [form] = Form.useForm(); - const [loading, setLoading] = useState(false); - const { showCreateDialog, setShowCreateDialog, addProject } = useProjectStore(); - - const handleOk = async () => { - try { - const values = await form.validateFields(); - setLoading(true); - - // 调用真实API创建项目 - const response = await projectApi.createProject({ - name: values.name, - background: values.background || '', - researchType: values.researchType, - }); - - if (response.success && response.data) { - addProject(response.data); - message.success(response.message || '项目创建成功'); - form.resetFields(); - setShowCreateDialog(false); - } else { - message.error(response.message || '项目创建失败'); - } - } catch (error) { - console.error('创建项目失败:', error); - message.error('项目创建失败'); - } finally { - setLoading(false); - } - }; - - const handleCancel = () => { - form.resetFields(); - setShowCreateDialog(false); - }; - - return ( - -
- - - - - - - 观察性研究 - 干预性研究 - - - - - -
- - -
- - - - - - - diff --git a/【给新AI】快速开始.md b/【给新AI】快速开始.md deleted file mode 100644 index a7c646f7..00000000 --- a/【给新AI】快速开始.md +++ /dev/null @@ -1,229 +0,0 @@ -# 【给新AI】ASL模块 - 5分钟快速上手 - -**当前时间**: 2025-11-18 -**当前任务**: Week 2 前端UI开发 -**阅读时间**: 3分钟 - ---- - -## 这是什么项目? - -**AIclinicalresearch** = 医学文献AI筛选平台 - -**当前开发**: ASL模块(AI Smart Literature)- 帮助医生用AI筛选医学文献 - ---- - -## 当前进度 - -``` -✅ Week 1: 后端完成(数据库+API+LLM筛选) -🔄 Week 2: 前端开发 ← 你现在在这里 -⬜ Week 3: 批量筛选 -⬜ Week 4: 测试上线 -``` - ---- - -## Week 2 要做什么? - -### 3个页面开发 - -**Day 1-2**: 项目管理 -- 项目列表页 -- 创建项目表单(含PICOS + 筛选风格选择) - -**Day 3-4**: 文献导入 -- Excel上传界面 -- 文献预览表格 - -**Day 5**: 筛选结果展示 ⭐ **重点** -- 结果列表 -- **显示两个模型的判断理由**(最重要!) - ---- - -## 核心功能:双模型筛选 - -``` -用户上传文献 - ↓ -DeepSeek-V3 ────┐ - ├─→ 对比 → 最终决策 -Qwen-Max ───────┘ - -一致 → 采纳 -冲突 → 人工复核 -``` - -**关键**: 前端必须显示**两个模型的完整理由** - ---- - -## 技术栈 - -``` -前端: React 18 + Ant Design 5 + TypeScript -后端: Fastify + Prisma + PostgreSQL -LLM: DeepSeek-V3 + Qwen-Max(已集成✅) -``` - ---- - -## 快速启动 - -```bash -# 1. 启动后端 -cd backend && npm run dev -# → http://localhost:3001 - -# 2. 启动前端 -cd frontend-v2 && npm run dev -# → http://localhost:5173 - -# 3. 测试API -curl http://localhost:3001/api/v1/asl/health -``` - ---- - -## 关键文档(只需看这3个) - -1. **任务清单**: `docs/03-业务模块/ASL-AI智能文献/04-开发计划/03-任务分解.md` - - Week 2详细任务 - -2. **UI原型**: `docs/03-业务模块/ASL-AI智能文献/03-UI设计/AI智能文献-标题摘要初筛原型.html` - - 界面参考 - -3. **API文档**: `docs/03-业务模块/ASL-AI智能文献/02-技术设计/02-API设计规范.md` - - 接口调用 - ---- - -## API示例 - -### 创建项目 -```typescript -POST /api/v1/asl/projects -{ - "projectName": "卒中预防研究", - "picoCriteria": { - "population": "非心源性缺血性卒中患者", - "intervention": "抗血小板药物", - "comparison": "安慰剂", - "outcome": "卒中复发", - "studyDesign": "RCT" - }, - "inclusionCriteria": "...", - "exclusionCriteria": "...", - "screeningConfig": { - "style": "lenient", // ← 筛选风格:lenient/standard/strict - "models": ["deepseek-chat", "qwen-max"] - } -} -``` - -### 获取筛选结果 -```typescript -GET /api/v1/asl/projects/:id/results - -Response: -{ - "literatureId": "uuid", - "title": "...", - "finalDecision": "pending", - - // ⭐ 两个模型的完整结果 - "model1Result": { - "modelName": "DeepSeek-V3", - "conclusion": "exclude", - "confidence": 0.92, - "reason": "虽然P、I、S维度匹配,但对照组..." // ← 必须显示 - }, - "model2Result": { - "modelName": "Qwen-Max", - "conclusion": "include", - "confidence": 0.85, - "reason": "研究人群和干预措施匹配..." // ← 必须显示 - }, - - "hasConflict": true -} -``` - ---- - -## Week 2 关键任务 ⭐ - -### 1. 筛选风格选择器 -```jsx - - 宽松模式(初筛) - 标准模式 - 严格模式 - -``` - -### 2. 模型理由展示(最重要!) -```jsx - - - - {model1.conclusion} -

理由: {model1.reason}

-
- - - - {model2.conclusion} -

理由: {model2.reason}

-
- -
-``` - ---- - -## 重要提醒 - -1. ✅ **后端已完成** - 10个API都可以直接调用 -2. ✅ **三种Prompt已实现** - 后端支持style参数 -3. ⭐ **理由展示是核心** - 用户强调必须看到AI思考过程 -4. ⚠️ **JWT暂时绕过** - 使用测试用户(生产需修复) - ---- - -## 成功标准 - -- [ ] 3个页面开发完成 -- [ ] 用户可以选择筛选风格 -- [ ] **两个模型的理由清晰展示** -- [ ] 冲突文献有明显标记 -- [ ] UI符合Ant Design规范 - ---- - -## 需要帮助? - -**详细交接文档**: `docs/03-业务模块/ASL-AI智能文献/00-新AI交接文档.md` - -**系统总览**: `docs/00-系统总体设计/00-系统当前状态与开发指南.md` - ---- - -**祝开发顺利!** 🚀 - ---- - -**文档版本**: v1.0 - 极简版 -**用途**: 新AI 3分钟快速上手 -**详细版本**: 见`00-新AI交接文档.md` - - - - - - - - - diff --git a/一键启动.bat b/一键启动.bat deleted file mode 100644 index 78ce59a3..00000000 --- a/一键启动.bat +++ /dev/null @@ -1,63 +0,0 @@ -@echo off -echo ======================================== -echo AI Clinical Research Platform -echo One-click Startup Script -echo ======================================== -echo. - -echo [1/7] Checking Docker status... -docker ps >nul 2>&1 -if errorlevel 1 ( - echo ERROR: Docker is not running. Please start Docker Desktop first. - pause - exit /b 1 -) -echo OK: Docker is running - -echo [2/7] Starting PostgreSQL and Redis containers... -cd /d "%~dp0" -docker-compose up -d -if errorlevel 1 ( - echo ERROR: Failed to start Docker containers - pause - exit /b 1 -) -echo OK: Database containers started - -echo [3/7] Waiting for database to be ready... -timeout /t 5 /nobreak >nul -echo OK: Database is ready - -echo [4/7] Starting Python extraction service (new window)... -start "Document Extraction Service" cmd /k "cd /d %~dp0extraction_service && venv\Scripts\python.exe -m uvicorn main:app --host 0.0.0.0 --port 8000" -echo OK: Python service starting... - -echo [5/7] Waiting for extraction service... -timeout /t 3 /nobreak >nul - -echo [6/7] Starting backend server (new window)... -start "AI Clinical Research - Backend" cmd /k "cd /d %~dp0backend && npm run dev" -echo OK: Backend starting... - -echo [7/7] Waiting for backend, then starting frontend... -timeout /t 3 /nobreak >nul - -start "AI Clinical Research - Frontend" cmd /k "cd /d %~dp0frontend && npm run dev" -echo OK: Frontend starting... - -echo. -echo ======================================== -echo Startup Complete! -echo ======================================== -echo. -echo Python Service: http://localhost:8000 (Docs: /docs) -echo Backend: http://localhost:3001 -echo Frontend: http://localhost:5173 -echo. -echo Please wait 10 seconds before accessing the frontend... -echo. -timeout /t 10 /nobreak -start http://localhost:5173 -echo. -echo Press any key to close this window... -pause >nul diff --git a/优化方案总结.md b/优化方案总结.md deleted file mode 100644 index 4f466bc7..00000000 --- a/优化方案总结.md +++ /dev/null @@ -1,416 +0,0 @@ -# AI科研助手 - 优化方案总结 - -## 📋 需求澄清与方案调整 - -### 🔄 重要需求变更:知识库规模 - -**原PRD描述:** -> "后期考虑,增加基于大规模(1000篇以内)文献的读取、识别、内容提取的工作" - -**实际需求(已明确):** -- ✅ 每个用户最多创建 **3个知识库** -- ✅ 每个知识库最多上传 **50个文件** -- ✅ 主要格式:**PDF、DOCX** -- ✅ 单用户最大文档量:**150个文件** - -**影响:** -- 🎉 技术难度从 ⭐⭐⭐⭐⭐ 降至 ⭐⭐⭐ -- 🎉 **Dify完全满足需求**,无需自建RAG系统 -- 🎉 开发成本进一步降低 -- 🎉 性能优化需求降低 - ---- - -## 📋 您提出的问题与解决方案 - -### 问题1:聊天功能如何实现? - -**您的担忧:** 不用LobeChat整体,如何保证聊天功能稳定? - -**我的方案:** ✅ **参考LobeChat开源实现,复用核心聊天组件** - -``` -策略: -├── ❌ 不采用:LobeChat整体(缺少项目管理、知识库功能) -├── ❌ 不采用:Dify + LobeChat组合(集成复杂,定制成本高) -├── ✅ 采用:提取LobeChat的聊天UI组件到我们的React项目 -└── ✅ 采用:参考其流式输出、Markdown渲染的实现逻辑 -``` - -**具体实施:** -1. 克隆LobeChat源码(MIT开源许可,可自由使用) -2. 提取核心组件:ChatMessage、ChatInput、StreamRenderer、MarkdownContent -3. 移植到我们的React+Vite项目中 -4. 自研业务逻辑:项目管理、智能体编排、知识库集成 - -**优势:** -- 聊天体验有保障(LobeChat已被4万+用户验证) -- 节省开发时间(9.5天 → 见详细方案) -- 保持架构灵活性(不被整体框架绑定) - ---- - -### 问题2:向量数据库还需要考虑吗? - -**回答:** ❌ **完全不需要考虑** - -**原因:** -- Dify内置了Qdrant向量数据库 -- 文档解析、向量化、检索全由Dify处理 -- 我们只需调用Dify的REST API即可 - -**实际需求下更加确定:** -``` -知识库规模: -- 3个知识库/用户 -- 50个文件/知识库 -- 150个文件/用户最大 - -Dify的能力: -- 单个知识库支持上万个文档片段 -- 我们只需150个文件,远低于上限 -- Dify内置Qdrant完全够用!✅ -``` - -**我们需要做的:** -```javascript -// 只需调用Dify API -const results = await difyClient.queryKnowledgeBase({ - datasetId: 'kb-xxx', - query: '骨质疏松的治疗方案', - topK: 5 -}); - -// Dify自动处理: -// 1. 向量检索(Qdrant) -// 2. 混合检索(关键词+语义) -// 3. 重排序(Reranking) -// 4. 返回结果 -``` - -**数据库设计(业务层):** -```sql --- 我们只需要管理知识库的元数据 -CREATE TABLE knowledge_bases ( - id VARCHAR(50) PRIMARY KEY, - user_id VARCHAR(50) NOT NULL, - name VARCHAR(100) NOT NULL, - dify_dataset_id VARCHAR(100) NOT NULL, -- Dify知识库ID - file_count INT DEFAULT 0, - -- 限制:每个用户最多3个 - CONSTRAINT check_kb_limit CHECK ( - (SELECT COUNT(*) FROM knowledge_bases WHERE user_id = user_id) <= 3 - ) -); - --- 文档表 -CREATE TABLE documents ( - id VARCHAR(50) PRIMARY KEY, - kb_id VARCHAR(50) NOT NULL, - filename VARCHAR(255) NOT NULL, - file_size_bytes BIGINT NOT NULL, - status VARCHAR(20) DEFAULT 'processing', -- processing, completed, failed - dify_document_id VARCHAR(100) NOT NULL, - -- 限制:每个知识库最多50个 - CONSTRAINT check_doc_limit CHECK ( - (SELECT COUNT(*) FROM documents WHERE kb_id = kb_id) <= 50 - ) -); -``` - ---- - -### 问题3:删除运营端功能后的方案 - -**已删除功能:** ✅ 降低开发成本 - -| 功能 | 状态 | 替代方案 | -|------|------|---------| -| 模型管理 | ❌ 删除 | 配置文件:`config/models.yaml` | -| 智能体管理 | ❌ 删除 | 配置文件:`config/agents.yaml` | -| 智能体配置 | ❌ 删除 | Prompt文件:`prompts/*.txt` | -| Prompt版本管理 | ❌ 删除 | Git版本控制 | -| 用户管理 | ✅ 保留 | 简化版(列表、禁用、查看) | -| 数据统计 | ✅ 保留 | 简化版(基础仪表盘) | -| 对话查看 | ✅ 保留 | 仅管理员可查看 | - -**配置文件示例:** - -```yaml -# config/agents.yaml -agents: - - id: agent-picos - name: PICOS构建 - system_prompt_file: prompts/picos_system.txt - models: - deepseek-v3: - temperature: 0.3 - max_tokens: 2500 - rag_enabled: true - status: active -``` - -**优势:** -- 开发时间节省:约3周 -- 开发成本降低:¥77,000 → ¥49,300(节省36%) -- 维护更简单:直接修改配置文件,无需后台界面 -- 版本控制:所有配置由Git管理 - ---- - -### 问题4:大模型优先级 - -**您的要求:** 优先DeepSeek-V3和Qwen3 - -**已更新方案:** ✅ - -**模型配置:** -```yaml -# config/models.yaml -models: - primary: - - name: deepseek-v3 - provider: deepseek - api_key: ${DEEPSEEK_API_KEY} - base_url: https://api.deepseek.com/v1 - pricing: - input: 1 # ¥1/百万tokens - output: 2 # ¥2/百万tokens - features: - - reasoning # 推理能力强 - - long_context # 支持128k上下文 - - secondary: - - name: qwen3-72b - provider: aliyun - api_key: ${DASHSCOPE_API_KEY} - base_url: https://dashscope.aliyuncs.com/api/v1 - pricing: - input: 4 # ¥4/百万tokens - output: 4 - features: - - chinese # 中文理解好 - - stable # 国内稳定 - - optional: - - name: gemini-2.0-flash - provider: google - api_key: ${GEMINI_API_KEY} - # 可选,国际用户使用 -``` - -**成本对比(1000用户/月):** - -| 模型 | 月度成本 | 年度成本 | 性价比 | -|------|---------|---------|--------| -| **DeepSeek-V3** ⭐ | **¥150** | **¥1,800** | **最高** | -| Qwen3-72B | ¥600 | ¥7,200 | 高 | -| Gemini Pro | ¥300 | ¥3,600 | 中 | -| GPT-4 | ¥2,500 | ¥30,000 | 低 | - -**建议:** -- 90%的请求使用DeepSeek-V3(极具性价比) -- 10%的请求使用Qwen3(需要更强中文理解时) -- 年度可节省LLM成本:¥1,200-1,500 - ---- - -## 🎯 最终优化方案总结 - -### 核心架构 - -``` -┌─────────────────────────────────────────┐ -│ 自定义前端(React + Vite) │ -│ - 项目管理(自研) │ -│ - 智能体选择(自研) │ -│ - 聊天界面(参考LobeChat)⭐ │ -│ - 知识库管理(自研) │ -└─────────────────────────────────────────┘ - ↓ REST API -┌─────────────────────────────────────────┐ -│ 业务层 (Node.js/TypeScript) │ -│ - 智能体路由(配置化)⭐ │ -│ - 上下文组装 │ -│ - 用户认证 │ -│ - 简化运营后台⭐ │ -└─────────────────────────────────────────┘ - ↓ ↓ -┌──────────────────┐ ┌─────────────────┐ -│ Dify (RAG) │ │ LLM API │ -│ - 知识库 │ │ - DeepSeek-V3⭐│ -│ - 文档解析 │ │ - Qwen3 ⭐ │ -│ - Qdrant(内置)⭐│ │ - Gemini(可选) │ -└──────────────────┘ └─────────────────┘ -``` - -### 技术栈 - -**前端:** -- React 18 + TypeScript + Vite -- TailwindCSS + HeadlessUI -- Zustand(状态管理) -- **LobeChat聊天组件(复用)** ⭐ - -**后端:** -- Node.js + Fastify + TypeScript -- Prisma(ORM)+ PostgreSQL -- Redis(缓存) - -**RAG:** -- Dify(Docker部署,内置Qdrant)⭐ - -**LLM:** -- DeepSeek-V3(主力)⭐ -- Qwen3-72B(备用)⭐ - -**配置管理:** -- agents.yaml(智能体配置)⭐ -- models.yaml(模型配置)⭐ -- prompts/*.txt(Prompt文件)⭐ - -### 开发计划 - -| 阶段 | 内容 | 时间 | -|------|------|------| -| 阶段1 | 基础搭建 + 复用LobeChat组件 | 1.5周 | -| 阶段2 | 核心功能(12个智能体) | 3.5周 | -| 阶段3 | 高级功能(RAG、文档生成) | 2周 | -| 阶段4 | 简化运营后台 | 1周 | -| 阶段5 | 测试优化 | 2周 | -| **总计** | **10周(2.5个月)** | ⭐ | - -### 成本估算 - -**开发成本(知识库需求明确后):** -- 人力:4人月 × ¥12k = ¥48,000 -- 服务器(开发):¥1,250 -- LLM API(测试):¥50(DeepSeek-V3极便宜) -- **总计:¥49,300** ⭐ - -**月度运营成本(1000用户):** -- 基础设施:¥1,200 -- LLM API:¥180(主要用DeepSeek-V3) -- 对象存储(知识库文件):¥140(750GB存储 + 流量)⭐ -- 人力(运维):¥15,000 -- **总计:¥16,520/月** - -**成本节省:** -- vs 原方案(大规模文献):节省约 ¥500/月 -- vs 纯手写:节省 ¥30,700+(38%+) - -**知识库成本明细:** -``` -单用户:150个文件 × 5MB = 750MB -1000用户:750GB总存储 - -对象存储(阿里云OSS): -- 存储费:750GB × ¥0.12/GB = ¥90/月 -- 流量费:100GB × ¥0.5/GB = ¥50/月 -- 总计:¥140/月 - -注:比原估算的"大规模文献处理"节省60%成本 -``` - ---- - -## ✅ 优势总结 - -### 1. 聊天功能有保障 -- ✅ 参考LobeChat成熟实现 -- ✅ 流式输出、Markdown渲染开箱即用 -- ✅ 节省9.5天开发时间 - -### 2. 向量数据库无需关心(知识库需求明确后更加确定) -- ✅ Dify内置Qdrant,自动处理 -- ✅ 只需调用API,降低技术难度 -- ✅ 150个文件/用户,远低于Dify上限 -- ✅ 无需考虑性能优化 - -### 3. 开发成本大幅降低 -- ✅ 删除复杂后台功能,节省3周 -- ✅ 配置文件管理,易于维护 -- ✅ 总成本:¥49,300(vs ¥77,000) - -### 4. LLM成本最优 -- ✅ DeepSeek-V3:¥1/百万tokens -- ✅ 月度LLM成本:¥180(vs GPT-4的¥2,500) -- ✅ 年度节省:¥27,840 - -### 5. 开发周期短 -- ✅ 2.5个月可上线MVP -- ✅ 复用成熟组件,降低风险 - ---- - -## 📝 下一步行动 - -### 立即可做: - -**1. 确认技术方案** ✅ -- 是否采用优化后的混合架构? -- 是否同意删除后台管理功能? - -**2. 准备环境** -```bash -# 申请LLM API Key -- DeepSeek API Key (https://platform.deepseek.com) -- 阿里云DashScope Key (https://dashscope.aliyun.com) - -# 准备服务器 -- 云服务器 4核8G(开发环境) -- PostgreSQL 数据库 -- Redis 缓存 - -# 准备代码仓库 -- GitHub/GitLab 仓库 -``` - -**3. 组建团队** -- 1名全栈开发(Node.js + React) -- 1名前端开发(React + TailwindCSS) -- 预计:2.5个月完成 - -**4. 启动开发** - -**第1周:** -- [ ] 搭建前后端框架 -- [ ] 部署Dify(Docker) -- [ ] 克隆LobeChat,提取聊天组件 - -**第2-4周:** -- [ ] 实现3个核心智能体 -- [ ] 集成DeepSeek-V3 -- [ ] 完成基础对话功能 - -**第5-7周:** -- [ ] 完善12个智能体 -- [ ] 集成Dify RAG -- [ ] 项目管理功能 - -**第8-10周:** -- [ ] 简化运营后台 -- [ ] 测试优化 -- [ ] 上线MVP - ---- - -## 🎉 总结 - -**优化后的方案:** -- ✅ 聊天功能:参考LobeChat,稳定可靠 -- ✅ 向量数据库:Dify内置,无需关心 -- ✅ 运营后台:配置化,大幅简化 -- ✅ 大模型:DeepSeek-V3优先,成本最优 -- ✅ 开发周期:2.5个月 -- ✅ 开发成本:¥49,300 -- ✅ 月度成本:¥16,400(1000用户) - -**这是最适合您项目的技术方案!** 🚀 - ---- - -**文档版本:v2.0(优化版)** -**更新时间:2025-10-10** -**作者:AI技术顾问** - diff --git a/启动Dify.bat b/启动Dify.bat deleted file mode 100644 index 1a3422d8..00000000 --- a/启动Dify.bat +++ /dev/null @@ -1,30 +0,0 @@ -@echo off -chcp 65001 >nul -echo ==================================== -echo 启动 Dify 服务 -echo ==================================== -echo. -cd /d %~dp0..\dify\docker -echo 当前目录: %CD% -echo. -echo 正在启动Dify服务,这可能需要几分钟... -echo. -docker compose up -d -echo. -echo ==================================== -echo Dify 服务状态 -echo ==================================== -echo. -docker compose ps -echo. -echo ==================================== -echo. -echo 如果服务启动成功,请访问: -echo http://localhost -echo. -echo 按任意键退出... -pause >nul - - - - diff --git a/启动指南.md b/启动指南.md deleted file mode 100644 index 60de13c4..00000000 --- a/启动指南.md +++ /dev/null @@ -1,242 +0,0 @@ -# AI临床研究平台 - 快速启动指南 - -## 📋 前提条件 - -确保以下服务已启动: - -```bash -# 检查Docker服务 -docker ps - -# 应该看到以下容器: -# - ai_clinical_postgres (PostgreSQL 15) -# - ai_clinical_redis (Redis 7) -``` - ---- - -## 🚀 启动后端服务 - -### 方法1:使用启动脚本(推荐) - -**Windows:** -```bash -双击运行:backend\启动后端.bat -``` - -**命令行:** -```bash -cd backend -npm run dev -``` - -### 方法2:手动启动 - -```bash -# 1. 进入后端目录 -cd backend - -# 2. 安装依赖(首次运行) -npm install - -# 3. 生成Prisma Client(首次运行) -npm run prisma:generate - -# 4. 启动开发服务器 -npm run dev -``` - ---- - -## ✅ 验证服务 - -### 1. 检查控制台输出 - -应该看到以下输出: - -``` -🔍 正在测试数据库连接... -✅ 数据库连接成功! -📊 数据库版本: PostgreSQL 15.14 - -============================================================ -🚀 AI临床研究平台 - 后端服务器启动成功! -============================================================ -📍 服务地址: http://localhost:3001 -🔍 健康检查: http://localhost:3001/health -📡 API入口: http://localhost:3001/api/v1 -🌍 运行环境: development -============================================================ -``` - -### 2. 访问健康检查端点 - -**浏览器访问:** -``` -http://localhost:3001/health -``` - -**预期响应:** -```json -{ - "status": "ok", - "database": "connected", - "timestamp": "2025-10-10T07:50:03.123Z", - "uptime": 123.456 -} -``` - -### 3. 访问API入口 - -**浏览器访问:** -``` -http://localhost:3001/api/v1 -``` - -**预期响应:** -```json -{ - "message": "AI Clinical Research Platform API", - "version": "1.0.0", - "environment": "development" -} -``` - -### 4. 使用PowerShell测试 - -```powershell -# 测试健康检查 -Invoke-WebRequest -Uri http://localhost:3001/health -UseBasicParsing | Select-Object -ExpandProperty Content - -# 测试API入口 -Invoke-WebRequest -Uri http://localhost:3001/api/v1 -UseBasicParsing | Select-Object -ExpandProperty Content -``` - ---- - -## 🛠️ 故障排查 - -### 问题1:数据库连接失败 - -**错误信息:** -``` -❌ 数据库连接失败,无法启动服务器 -``` - -**解决方案:** -```bash -# 1. 检查PostgreSQL容器是否运行 -docker ps | findstr postgres - -# 2. 如果没有运行,启动Docker服务 -cd D:\MyCursor\AIclinicalresearch -docker-compose up -d - -# 3. 检查.env文件中的DATABASE_URL是否正确 -# DATABASE_URL="postgresql://ai_clinical:clinical123@localhost:5432/ai_clinical?schema=public" -``` - -### 问题2:端口3001已被占用 - -**错误信息:** -``` -Error: listen EADDRINUSE: address already in use :::3001 -``` - -**解决方案:** -```powershell -# 1. 查找占用端口的进程 -netstat -ano | findstr :3001 - -# 2. 结束该进程(PID是最后一列的数字) -taskkill /PID <进程ID> /F - -# 3. 或者修改.env文件中的PORT -# PORT=3002 -``` - -### 问题3:Prisma Client未生成 - -**错误信息:** -``` -Error: Cannot find module '@prisma/client' -``` - -**解决方案:** -```bash -cd backend -npm run prisma:generate -``` - -### 问题4:依赖包缺失 - -**错误信息:** -``` -Error: Cannot find module 'xxx' -``` - -**解决方案:** -```bash -cd backend -npm install -``` - ---- - -## 📚 常用命令 - -### 数据库管理 - -```bash -cd backend - -# 生成Prisma Client -npm run prisma:generate - -# 创建新的迁移 -npm run prisma:migrate - -# 打开Prisma Studio(可视化数据库管理) -npm run prisma:studio -``` - -### 开发命令 - -```bash -# 启动开发服务器(热重载) -npm run dev - -# 构建生产版本 -npm run build - -# 启动生产服务器 -npm start -``` - ---- - -## 🎯 下一步 - -后端服务启动成功后,您可以: - -1. **查看数据库**:运行 `npm run prisma:studio` -2. **查看API文档**:`docs/01-设计文档/API设计规范.md` -3. **开始开发前端**:进入Day 6的前端开发任务 - ---- - -## 📞 技术支持 - -如果遇到其他问题,请: - -1. 查看详细文档:`backend/README.md` -2. 查看Day 5总结:`docs/05-每日进度/Day05-后端基础架构完成.md` -3. 查看开发里程碑:`docs/04-开发计划/开发里程碑.md` - ---- - -**🎉 祝开发顺利!** - - - - diff --git a/如何测试Phase2.md b/如何测试Phase2.md deleted file mode 100644 index bb0d77a8..00000000 --- a/如何测试Phase2.md +++ /dev/null @@ -1,438 +0,0 @@ -# 📖 如何测试 Phase 2 - 使用说明 - -**Phase 2完成日期**:2025-10-13 -**待测试内容**:双模式智能问答系统(全文阅读 + 逐篇精读) - ---- - -## 🎯 测试目标 - -验证以下功能: -1. ✅ Python微服务(文档提取) -2. ✅ 多格式文档支持(PDF/Docx/Txt) -3. ✅ Token计数和智能管理 -4. ✅ 全文阅读模式(35-50篇文献综合分析) -5. ✅ 逐篇精读模式(1-5篇深度分析) -6. ✅ 文献切换和对话历史管理 - ---- - -## 📚 测试资料清单 - -我已经为你准备了**4个测试工具**: - -### 1. 🔍 环境检查脚本(推荐第一步) -**文件**:`检查测试环境.bat` -**用途**:自动检查测试环境是否就绪 -**耗时**:1分钟 - -**使用方法**: -```bash -# 双击运行 -检查测试环境.bat -``` - -**输出示例**: -``` -✅ 通过: 8/10 -⚠️ 警告: 2/10 -❌ 失败: 0/10 - -🟢 状态: 测试环境完全就绪! -``` - ---- - -### 2. ⚡ 快速测试清单(推荐日常使用) -**文件**:`Phase2-快速测试清单.md` -**用途**:30分钟快速验证核心功能 -**耗时**:30分钟 -**适合场景**: -- 代码更新后的快速验证 -- 日常开发测试 -- 问题修复后的回归测试 - -**内容结构**: -``` -✅ 启动服务(2分钟) -✅ 准备数据(10分钟) -✅ 核心测试(15分钟) - - 全文阅读模式(5分钟) - - 逐篇精读模式(10分钟) -✅ 快速验收(3分钟) -``` - ---- - -### 3. 📋 完整测试指南(推荐正式验收) -**文件**:`Phase2-测试指南.md` -**用途**:90分钟完整功能验证 -**耗时**:60-90分钟 -**适合场景**: -- Phase 2正式验收 -- 上线前的完整测试 -- 详细的功能测试 - -**内容结构**: -``` -Part 1: 环境准备(10分钟) -Part 2: 服务启动(5分钟) -Part 3: 知识库准备(15-20分钟) -Part 4: 全文阅读模式(15分钟) - - 8个详细测试用例 -Part 5: 逐篇精读模式(20分钟) - - 8个详细测试用例 -Part 6: 端到端场景(15分钟) - - 3个真实用户场景 -Part 7: 问题记录(5分钟) -附录: 常见问题排查 -``` - ---- - -### 4. 🚀 一键启动脚本(服务启动) -**文件**:`一键启动.bat` -**用途**:自动启动所有服务 -**耗时**:30秒启动,10秒等待 - -**启动内容**: -- PostgreSQL容器 -- Redis容器 -- Python微服务(端口8000) -- Backend服务(端口3001) -- Frontend服务(端口3000) - ---- - -## 🏃 快速开始(5分钟上手) - -### Step 1: 检查环境(1分钟) -```bash -# 双击运行 -检查测试环境.bat -``` -**看到"🟢 测试环境完全就绪"** → 进入Step 2 -**看到"🔴 或 🟡"** → 按照提示修复问题 - ---- - -### Step 2: 启动服务(2分钟) -```bash -# 双击运行 -一键启动.bat -``` -**等待**:30秒,看到3个窗口打开 -**验证**:自动打开浏览器 http://localhost:3000 - ---- - -### Step 3: 选择测试方式(2分钟) - -#### 方式A:快速测试(30分钟)⭐ 推荐 -```bash -# 打开文档 -Phase2-快速测试清单.md -``` -**特点**:快速、核心功能验证 - -#### 方式B:完整测试(90分钟) -```bash -# 打开文档 -Phase2-测试指南.md -``` -**特点**:详细、全面、适合正式验收 - ---- - -## 📊 推荐测试流程 - -### 🎯 首次测试(推荐完整测试) - -```mermaid -graph LR - A[检查环境] --> B[启动服务] - B --> C[完整测试指南] - C --> D[记录问题] - D --> E{有严重问题?} - E -->|是| F[修复问题] - E -->|否| G[验收通过] - F --> A -``` - -**预计时间**:90-120分钟(含问题修复) - ---- - -### 🔄 日常测试(推荐快速测试) - -```mermaid -graph LR - A[检查环境] --> B[启动服务] - B --> C[快速测试清单] - C --> D{测试通过?} - D -->|是| E[继续开发] - D -->|否| F[快速修复] - F --> C -``` - -**预计时间**:30分钟 - ---- - -## 🎯 测试策略建议 - -### 阶段1:初次验证(第一次测试) -**目标**:全面了解系统功能和稳定性 - -**步骤**: -1. ✅ 运行环境检查 -2. ✅ 使用**完整测试指南**(90分钟) -3. ✅ 记录所有发现的问题 -4. ✅ 按优先级修复问题 -5. ✅ 重新测试验证 - -**预期产出**: -- 完整的测试报告 -- 问题清单(按优先级) -- 修复计划 - ---- - -### 阶段2:问题修复验证(修复后) -**目标**:验证问题是否修复 - -**步骤**: -1. ✅ 运行环境检查 -2. ✅ 针对问题点重新测试 -3. ✅ 使用**快速测试清单**回归测试 - -**预期产出**: -- 问题修复确认 -- 回归测试通过 - ---- - -### 阶段3:正式验收(准备上线) -**目标**:最终验收决策 - -**步骤**: -1. ✅ 运行环境检查 -2. ✅ 使用**完整测试指南**再次测试 -3. ✅ 端到端场景测试 -4. ✅ 性能指标验证 -5. ✅ 最终验收决策 - -**验收标准**: -- [ ] 所有核心功能正常 -- [ ] 无阻断性问题 -- [ ] 性能指标达标 -- [ ] 用户体验良好 - ---- - -## 🔧 常见场景处理 - -### 场景1:环境检查失败 -**现象**:运行`检查测试环境.bat`后显示🔴 - -**处理步骤**: -1. 查看哪些检查项标记为❌ -2. 按照提示安装缺失的软件 -3. 重新运行检查脚本 -4. 直到显示🟢才能继续测试 - -**常见问题**: -- Docker未启动 → 启动Docker Desktop -- Python未安装 → 安装Python 3.11+ -- 虚拟环境缺失 → `cd extraction_service && .\install.bat` - ---- - -### 场景2:服务启动失败 -**现象**:一键启动后某个服务报错 - -**处理步骤**: -1. 查看报错的服务窗口 -2. 记录错误信息 -3. 查看测试指南"附录A:常见问题排查" -4. 按照排查步骤解决 - -**常见问题**: -- 端口被占用 → 关闭占用程序或重启电脑 -- 依赖未安装 → 运行`npm install`或`pip install` -- 数据库连接失败 → 检查Docker容器状态 - ---- - -### 场景3:功能测试失败 -**现象**:某个功能不工作或报错 - -**处理步骤**: -1. 在测试文档中记录具体问题 -2. 截图保存关键错误 -3. 记录复现步骤 -4. 查看浏览器Console(F12)的错误信息 -5. 提交问题清单 - -**问题记录模板**: -``` -问题描述:____________ -复现步骤: - 1. ____________ - 2. ____________ -预期结果:____________ -实际结果:____________ -截图:[附件] -Console错误:____________ -``` - ---- - -## 📝 测试记录管理 - -### 建议的文档结构 - -创建一个测试记录文件夹: -``` -AIclinicalresearch/ -├── 测试记录/ -│ ├── 2025-10-13-首次测试.md -│ ├── 2025-10-14-问题修复验证.md -│ ├── 2025-10-15-正式验收.md -│ └── 问题清单.md -``` - -### 问题清单模板 - -```markdown -# Phase 2 问题清单 - -## 🔴 严重问题(阻断性) -1. [问题描述] - [状态:待修复/已修复] - [负责人] - -## 🟡 中等问题(影响使用) -1. [问题描述] - [状态:待修复/已修复] - [负责人] - -## 🟢 轻微问题(不影响主流程) -1. [问题描述] - [状态:待修复/已修复] - [负责人] -``` - ---- - -## 🎊 测试完成后 - -### ✅ 如果验收通过 - -**恭喜!Phase 2验收通过!** 🎉 - -**下一步选择**: - -#### 选项A:开发Phase 3(批处理模式)⭐ 推荐 -- **时间**:1-2天 -- **价值**:完善知识库功能闭环 -- **任务**:批量文献处理、结果汇总、导出功能 - -#### 选项B:开发里程碑2(11个智能体) -- **时间**:2-3周 -- **价值**:产品功能完整度大幅提升 -- **任务**:开发其余11个专业智能体 - -**建议**:先完成Phase 3(1-2天),再进入里程碑2 - ---- - -### ⚠️ 如果有问题需要修复 - -**不要慌!**按照以下步骤处理: - -1. **问题分类** - - 将问题分为:严重、中等、轻微 - -2. **优先级排序** - - 严重问题:立即修复 - - 中等问题:尽快修复 - - 轻微问题:可延后 - -3. **修复计划** - - 为每个问题分配修复时间 - - 预估总修复时间 - -4. **修复后验证** - - 使用快速测试清单回归测试 - - 确认问题已解决 - ---- - -## 📞 需要帮助? - -### 测试过程中遇到问题 - -1. **查看测试指南的"附录A:常见问题排查"** -2. **查看Console错误信息(F12)** -3. **记录详细的问题描述和截图** -4. **整理问题清单待Review** - -### 不确定如何判断测试结果 - -**参考标准**: -- ✅ **通过**:功能正常工作,无错误 -- ⚠️ **有条件通过**:功能基本工作,有小问题但不影响主流程 -- ❌ **不通过**:功能无法工作或有严重错误 - ---- - -## 📚 相关文档索引 - -### Phase 2开发文档 -- [Phase2-最终技术方案.md](./docs/05-每日进度/Phase2-最终技术方案.md) -- [Phase2-完成报告.md](./docs/05-每日进度/Phase2-完成报告.md) -- [Phase2-最终总结.md](./docs/05-每日进度/Phase2-最终总结.md) - -### 测试文档 -- [Phase2-测试指南.md](./Phase2-测试指南.md) - 完整版 -- [Phase2-快速测试清单.md](./Phase2-快速测试清单.md) - 快速版 - -### 开发计划文档 -- [开发里程碑.md](./docs/04-开发计划/开发里程碑.md) - ---- - -## 🎯 总结 - -### 测试工具选择 - -| 场景 | 推荐工具 | 耗时 | -|------|---------|------| -| 首次测试 | 完整测试指南 | 90分钟 | -| 日常开发测试 | 快速测试清单 | 30分钟 | -| 问题修复后 | 快速测试清单 | 30分钟 | -| 正式验收 | 完整测试指南 | 90分钟 | -| 环境检查 | 环境检查脚本 | 1分钟 | - -### 测试流程建议 - -``` -第1次: 环境检查 → 完整测试 → 记录问题 -第2次: 环境检查 → 快速测试 → 验证修复 -第3次: 环境检查 → 完整测试 → 最终验收 -``` - ---- - -**准备好了吗?立即开始测试!** 🚀 - -1. 运行 `检查测试环境.bat` -2. 运行 `一键启动.bat` -3. 打开 `Phase2-快速测试清单.md` 或 `Phase2-测试指南.md` -4. 开始测试! - -**祝测试顺利!** 🎉 - - - - - - - - - diff --git a/对话系统实现方案对比.md b/对话系统实现方案对比.md deleted file mode 100644 index d5fa0cb4..00000000 --- a/对话系统实现方案对比.md +++ /dev/null @@ -1,860 +0,0 @@ -# 对话系统实现方案详细对比 - -## 🎯 核心问题 - -**如果不使用LobeChat整体框架,基于大模型的对话聊天该怎么实现?** - -让我为您详细对比3种方案: - ---- - -## 方案A:使用Dify的对话功能 - -### Dify的对话能力 - -Dify确实提供了完整的对话功能,包括: -- ✅ LLM对话管理 -- ✅ 流式输出 -- ✅ 对话历史管理 -- ✅ RAG知识库集成 -- ✅ 提供WebApp界面 - -### 架构图 - -``` -用户浏览器 - ↓ -Dify自带的WebApp界面 - ↓ -Dify后端(对话+RAG) - ↓ -LLM API (DeepSeek/Qwen) -``` - -### 实现方式 - -**方式1:直接使用Dify的WebApp** -```bash -# 部署Dify后,直接访问其Web界面 -docker-compose up -d -# 访问 http://localhost:3000 -# 创建12个应用(对应12个智能体) -``` - -**方式2:通过Dify API集成** -```javascript -// 调用Dify的对话API -const response = await fetch('https://api.dify.ai/v1/chat-messages', { - method: 'POST', - headers: { - 'Authorization': `Bearer ${DIFY_API_KEY}`, - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - inputs: {}, - query: "请帮我构建PICOS", - response_mode: "streaming", // 流式输出 - conversation_id: "conv-xxx", // 对话ID - user: "user-123" - }) -}); - -// 接收流式响应 -const reader = response.body.getReader(); -while (true) { - const { done, value } = await reader.read(); - if (done) break; - // 处理流式数据 -} -``` - -### 优点 ✅ - -1. **开发成本极低** - - 不需要自己实现对话逻辑 - - 不需要自己实现流式输出 - - 不需要自己管理对话历史 - - RAG功能开箱即用 - -2. **功能完整** - - 对话管理 - - Prompt管理 - - 流式输出 - - 知识库集成 - - 文件上传 - -3. **快速上线** - - 1-2周即可搭建MVP - - 无需深入理解LLM API细节 - -### 缺点 ❌ - -1. **12个智能体管理复杂** - ``` - 问题:需要在Dify中创建12个独立的应用(App) - - Dify界面: - ├── 应用1: 选题评价 - ├── 应用2: PICOS构建 - ├── 应用3: 论文润色 - ├── ... (共12个) - └── 应用12: 期刊稿约评审 - - 影响: - - 用户需要在12个应用间切换(体验差) - - 无法统一管理对话历史 - - 无法共享项目上下文 - ``` - -2. **项目管理功能缺失** - ``` - 我们的需求: - - 用户创建"项目/课题" - - 在项目内使用多个智能体 - - 项目背景信息自动注入 - - 对话可"固定"到项目背景 - - Dify的能力: - - ❌ 没有"项目"概念 - - ❌ 无法跨应用共享上下文 - - ❌ 每个对话是独立的 - ``` - -3. **前端定制困难** - ``` - 我们的原型图: - - 左侧:项目列表 + 智能体列表 - - 中间:智能体卡片选择 - - 右侧:对话界面 - - Dify的界面: - - 固定的单应用界面 - - 定制需要修改Dify源码(复杂) - ``` - -4. **业务逻辑受限** - ``` - 我们的特殊需求: - - 全局快速问答 vs 项目内深度研究 - - 动态背景信息管理(固定功能) - - 对话溯源(引用知识库) - - 多模型即时切换 - - Dify的支持: - - 🟡 部分支持,但需要复杂配置 - - 🟡 或需要修改源码 - ``` - -### 适用场景 ✅ - -**适合:** 如果您的需求是: -- 简单的知识库问答 -- 单一的对话场景 -- 快速验证想法(MVP) - -**不适合:** 我们的项目 -- ❌ 12个智能体需要统一管理 -- ❌ 项目管理是核心功能 -- ❌ 需要高度定制的UI - ---- - -## 方案B:参考LobeChat + 自研后端(推荐 ⭐) - -### 核心思路 - -``` -前端:参考LobeChat的UI组件(不用整体框架) -后端:自己实现对话逻辑(调用LLM API) -RAG:使用Dify(专注知识库检索) -``` - -### 架构图 - -``` -┌─────────────────────────────────────────┐ -│ 前端(React + Vite) │ -│ ┌──────────────────────────────────┐ │ -│ │ 聊天UI(参考LobeChat实现) │ │ -│ │ - ChatMessage 组件 │ │ -│ │ - ChatInput 组件 │ │ -│ │ - StreamRenderer 组件 │ │ -│ │ - MarkdownContent 组件 │ │ -│ └──────────────────────────────────┘ │ -│ ┌──────────────────────────────────┐ │ -│ │ 业务界面(自己实现) │ │ -│ │ - 项目管理 │ │ -│ │ - 智能体选择 │ │ -│ │ - 历史记录 │ │ -│ └──────────────────────────────────┘ │ -└─────────────────────────────────────────┘ - ↓ REST API -┌─────────────────────────────────────────┐ -│ 后端(Node.js + TypeScript) │ -│ ┌──────────────────────────────────┐ │ -│ │ 对话服务(自己实现) │ │ -│ │ - 接收用户消息 │ │ -│ │ - 组装上下文(项目背景+历史) │ │ -│ │ - 调用LLM API │ │ -│ │ - 流式返回结果 │ │ -│ └──────────────────────────────────┘ │ -│ ┌──────────────────────────────────┐ │ -│ │ 业务服务(自己实现) │ │ -│ │ - 项目管理 │ │ -│ │ - 智能体路由 │ │ -│ │ - 权限控制 │ │ -│ └──────────────────────────────────┘ │ -└─────────────────────────────────────────┘ - ↓ ↓ -┌──────────────────┐ ┌─────────────────┐ -│ Dify (RAG) │ │ LLM API │ -│ - 知识库检索 │ │ - DeepSeek-V3 │ -│ (仅RAG) │ │ - Qwen3 │ -└──────────────────┘ └─────────────────┘ -``` - -### 详细实现 - -#### 1. 前端聊天UI(参考LobeChat) - -**提取LobeChat的核心组件:** - -```tsx -// src/components/chat/ChatMessage.tsx -// 从LobeChat移植,做少量修改 - -import React from 'react'; -import ReactMarkdown from 'react-markdown'; -import { Prism as SyntaxHighlighter } from 'react-syntax-highlighter'; - -interface Message { - id: string; - role: 'user' | 'assistant'; - content: string; - timestamp: number; -} - -export function ChatMessage({ message, onCopy, onPin }: Props) { - const isUser = message.role === 'user'; - - return ( -
- {/* 头像 */} - - - {/* 消息内容 */} -
-
- {isUser ? ( -

{message.content}

- ) : ( - - {String(children).replace(/\n$/, '')} - - ) : ( - - {children} - - ); - } - }} - > - {message.content} - - )} -
- - {/* 操作按钮 */} - {!isUser && ( -
- - - -
- )} -
-
- ); -} -``` - -```tsx -// src/components/chat/ChatInput.tsx -// 从LobeChat移植 - -export function ChatInput({ onSend, onUpload, onKbSelect }: Props) { - const [message, setMessage] = useState(''); - - const handleSend = () => { - if (!message.trim()) return; - onSend(message); - setMessage(''); - }; - - return ( -
-