# 设计文档:从正式系统恢复源码 ## 概述 本设计文档描述了如何从正式系统的 Docker 容器中提取源码,对比本地差异,恢复缺失文件,并部署到影子系统进行测试验证的完整方案。 ## 架构 ### 系统组件 ``` ┌─────────────────────────────────────────────────────────────┐ │ 源码恢复流程 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 步骤1: 连接正式系统容器 │ │ - SSH 到服务器 192.168.58.20 │ │ - 访问容器 youyidian-3d-material │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 步骤2: 提取源码文件列表 │ │ - 列出容器内 /app 目录的所有文件 │ │ - 排除 node_modules, .next, uploads 等 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 步骤3: 对比本地源码 │ │ - 检查本地缺失的文件 │ │ - 检查内容不一致的文件 │ │ - 生成差异报告 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 步骤4: 复制缺失文件 │ │ - 使用 docker cp 或 scp 复制文件 │ │ - 保持目录结构 │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 步骤5: 验证构建 │ │ - npm install │ │ - npm run build │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 步骤6: 部署到影子系统 │ │ - 运行 deploy-local-build.ps1 -Target shadow │ └─────────────────────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────────────────────┐ │ 步骤7: 功能测试 │ │ - 访问 http://192.168.58.20:5320 │ │ - 测试关键功能 │ └─────────────────────────────────────────────────────────────┘ ``` ## 组件和接口 ### 1. 源码提取工具 **功能**: 从 Docker 容器中提取源码文件 **方法**: - 方法 A: 使用 `docker cp` 命令 ```powershell ssh root@192.168.58.20 "docker cp youyidian-3d-material:/app /tmp/source" scp -r root@192.168.58.20:/tmp/source/* D:\gongxiangp2\ ``` - 方法 B: 使用 `docker exec` + tar ```powershell ssh root@192.168.58.20 "docker exec youyidian-3d-material tar czf /tmp/source.tar.gz -C /app ." scp root@192.168.58.20:/tmp/source.tar.gz D:\gongxiangp2\ tar -xzf source.tar.gz ``` **推荐**: 方法 B,因为可以一次性打包,传输更快 ### 2. 文件对比工具 **功能**: 对比本地源码和容器源码的差异 **实现**: ```powershell # 获取容器内文件列表 ssh root@192.168.58.20 "docker exec youyidian-3d-material find /app -type f" > container-files.txt # 获取本地文件列表 Get-ChildItem -Recurse -File | Select-Object FullName > local-files.txt # 对比差异 Compare-Object (Get-Content container-files.txt) (Get-Content local-files.txt) ``` ### 3. 构建验证工具 **功能**: 验证恢复后的源码能否正常构建 **步骤**: 1. 安装依赖: `npm install` 2. 构建项目: `npm run build` 3. 检查构建输出: `.next` 目录应该存在且包含构建文件 ### 4. 部署工具 **功能**: 将恢复的源码部署到影子系统 **使用**: ```powershell .\deploy-local-build.ps1 -Target shadow ``` **该脚本应该**: 1. 构建 Docker 镜像 2. 停止旧的影子系统容器 3. 启动新的影子系统容器 4. 验证容器状态 ## 数据模型 ### 文件差异报告 ```typescript interface FileDifference { path: string; // 文件路径 status: 'missing' | 'different' | 'extra'; // 状态 importance: 'critical' | 'important' | 'optional'; // 重要性 category: 'api' | 'page' | 'component' | 'lib' | 'config' | 'other'; // 分类 } interface DifferenceReport { timestamp: Date; // 报告生成时间 totalFiles: number; // 总文件数 missingFiles: FileDifference[]; // 缺失的文件 differentFiles: FileDifference[]; // 内容不同的文件 extraFiles: FileDifference[]; // 本地多余的文件 } ``` ### 功能验证清单 ```typescript interface FeatureTest { name: string; // 功能名称 category: string; // 功能分类 tested: boolean; // 是否已测试 passed: boolean; // 是否通过 notes: string; // 备注 } interface FeatureReport { timestamp: Date; // 报告生成时间 totalFeatures: number; // 总功能数 testedFeatures: number; // 已测试功能数 passedFeatures: number; // 通过的功能数 features: FeatureTest[]; // 功能列表 } ``` ## 正确性属性 *属性是一个特征或行为,应该在系统的所有有效执行中保持为真——本质上是关于系统应该做什么的正式陈述。属性作为人类可读规范和机器可验证正确性保证之间的桥梁。* ### 属性 1: 文件完整性 *对于任何*从容器提取的源码文件,该文件应该在本地存在且内容一致 **验证**: 需求 1.2, 1.3 ### 属性 2: 目录结构一致性 *对于任何*容器中的目录结构,本地应该有相同的目录结构 **验证**: 需求 1.2 ### 属性 3: 构建可重复性 *对于任何*恢复后的源码,执行 `npm run build` 应该成功生成 `.next` 目录 **验证**: 需求 5.2 ### 属性 4: 功能等价性 *对于任何*正式系统中的功能,影子系统应该有相同的功能表现 **验证**: 需求 7.1, 7.2, 7.3, 7.4, 7.5, 7.6, 7.7 ## 错误处理 ### 1. SSH 连接失败 - **错误**: 无法连接到服务器 192.168.58.20 - **处理**: 检查网络连接,确认 SSH 密钥配置正确 ### 2. Docker 容器不存在 - **错误**: 容器 youyidian-3d-material 不存在 - **处理**: 检查容器名称,确认容器正在运行 ### 3. 文件复制失败 - **错误**: 无法复制文件到本地 - **处理**: 检查磁盘空间,确认文件权限 ### 4. 构建失败 - **错误**: npm run build 失败 - **处理**: 1. 检查 node_modules 是否完整 2. 检查 package.json 依赖版本 3. 查看构建错误日志 4. 检查环境变量配置 ### 5. 部署失败 - **错误**: 部署脚本执行失败 - **处理**: 1. 检查 Docker 服务状态 2. 检查端口占用情况 3. 查看部署脚本日志 ## 测试策略 ### 单元测试 - 测试文件对比逻辑 - 测试文件复制功能 - 测试差异报告生成 ### 集成测试 - 测试完整的源码提取流程 - 测试构建验证流程 - 测试部署流程 ### 功能测试 - 在影子系统上测试所有关键功能 - 对比正式系统和影子系统的功能表现 - 记录测试结果 ### 测试清单 #### 用户认证 - [ ] 钉钉登录 - [ ] 邮箱密码登录 - [ ] 用户注册 #### 素材管理 - [ ] 素材上传(所有类型) - [ ] 素材浏览(瀑布流) - [ ] 素材筛选(类型、店铺、场景、产品类别) - [ ] 素材搜索(防抖处理) - [ ] 素材详情 - [ ] 素材编辑 - [ ] 素材下载(包括模特类自动打包) - [ ] 点赞功能 - [ ] 收藏功能 - [ ] 分享功能 #### 滚动位置保存 - [ ] 从详情页返回首页时保持滚动位置 - [ ] 筛选条件保存和恢复 #### 个人中心 - [ ] 我的素材 - [ ] 我的收藏 - [ ] 头像上传 - [ ] 个人信息修改 #### 管理员功能 - [ ] 用户管理 - [ ] 素材管理 - [ ] 分类管理(店铺、场景、平台、产品类别) - [ ] 部门管理 - [ ] 系统设置 - [ ] 回收站 #### 辅助功能 - [ ] 排行榜 - [ ] AI 聊天 - [ ] 弹幕 - [ ] 网络诊断 - [ ] 更新日志 #### 视频处理 - [ ] 视频上传 - [ ] 视频转码 - [ ] 视频预览 ## 实施步骤 ### 阶段 1: 源码提取(预计 30 分钟) 1. 连接到服务器 2. 从容器打包源码 3. 下载到本地 4. 解压并检查 ### 阶段 2: 差异分析(预计 20 分钟) 1. 生成文件列表 2. 对比差异 3. 生成差异报告 4. 标注重要文件 ### 阶段 3: 文件恢复(预计 10 分钟) 1. 复制缺失文件 2. 更新不一致文件 3. 验证文件完整性 ### 阶段 4: 构建验证(预计 15 分钟) 1. 安装依赖 2. 执行构建 3. 检查构建输出 4. 修复构建错误(如有) ### 阶段 5: 部署测试(预计 20 分钟) 1. 部署到影子系统 2. 验证容器状态 3. 访问影子系统 4. 初步功能测试 ### 阶段 6: 功能验证(预计 60 分钟) 1. 测试用户认证 2. 测试素材管理 3. 测试筛选和搜索 4. 测试滚动位置保存 5. 测试管理员功能 6. 测试辅助功能 7. 记录测试结果 ### 阶段 7: 文档更新(预计 15 分钟) 1. 更新功能恢复进度文档 2. 记录遇到的问题和解决方案 3. 更新部署文档 **总预计时间**: 约 2.5 小时 ## 风险和缓解措施 ### 风险 1: 源码提取不完整 - **影响**: 恢复的源码缺少关键文件 - **缓解**: 1. 提取前先列出所有文件 2. 提取后验证文件数量 3. 对比文件大小 ### 风险 2: 构建失败 - **影响**: 无法生成可部署的镜像 - **缓解**: 1. 先在本地测试构建 2. 检查依赖版本 3. 查看构建日志 ### 风险 3: 功能不一致 - **影响**: 影子系统功能与正式系统不一致 - **缓解**: 1. 详细的功能测试清单 2. 对比测试结果 3. 记录差异并修复 ### 风险 4: 数据库不兼容 - **影响**: 影子系统无法连接数据库或数据结构不匹配 - **缓解**: 1. 检查数据库连接配置 2. 验证数据库 schema 3. 执行必要的迁移 ## 生产系统问题分析(2025-12-20) ### 🔍 数据不一致问题调查结果 #### 数据库状态(正常) - **生产数据库** (`lobechat` @ 192.168.58.20:5432): - 总记录数: 1530条 - 未删除素材: 1524条 - 软删除素材: 6条 - **结论**: 数据库没有被覆盖,数据完整 - **影子数据库** (`lobechat_stage` @ 192.168.58.20:5432): - 未删除素材: 1049条 - **结论**: 影子系统数据独立,没有混乱 #### 发现的BUG ##### BUG 1: 统计API包含软删除数据 - **API**: `/api/materials/stats` - **问题**: 返回 `total: 1530`(包含6条软删除数据) - **正确值**: 应该是 `1524` - **影响**: "使用店铺"的"全部"按钮显示错误数字(1530) - **修复**: 添加 `WHERE "deletedAt" IS NULL` 过滤条件 ##### BUG 2: 分页API返回全部数据 - **API**: `/api/materials?page=X&pageSize=20` - **问题**: 无论请求哪一页,都返回全部1524条数据 - **影响**: - 前端性能严重下降(一次加载1524条数据) - 用户看到的"瀑布流"实际上是全部数据 - 用户报告滚动到底部只显示1026个素材(可能是前端渲染限制) - **修复**: 修复分页逻辑,确保正确使用 `skip` 和 `take` ##### BUG 3: 分类统计API未过滤软删除 - **API**: `/api/categories` - **问题**: 统计每个分类的素材数量时未过滤软删除数据 - **影响**: 分类按钮显示的数字可能包含已删除素材 - **修复**: 添加 `WHERE "deletedAt" IS NULL` 过滤条件 #### 容器状态 - **正式系统容器**: 不在本地服务器上(在192.168.58.20服务器上运行) - **端口4320**: 正常运行,可以访问 - **本地源码**: 与生产系统不一致,需要从生产容器恢复 ### 📁 素材存储路径问题 #### 重要说明:新老素材存储位置不同 ##### 老素材存储(2025-10-27之前) - **预览图**: MinIO S3(老bucket配置) - Endpoint: `https://s3.1080p.hk` - Bucket: `jsxiangche`(老bucket,无路径前缀) - URL格式: `https://s3.1080p.hk/jsxiangche/preview-{timestamp}-{hash}.webp` - 示例: `https://s3.1080p.hk/jsxiangche/preview-1761537819211-edb4a190180c24f9.webp` - **源文件(压缩包)**: 本地文件系统 - 路径: `uploads/material-{timestamp}-{hash}.zip` - 相对路径格式(数据库存储) - 示例: `uploads/material-1761537819728-8fb5090670b5b986.zip` ##### 新素材存储(2025-10-27之后) - **预览图/视频缩略图**: MinIO S3(新路径配置) - Endpoint: `https://s3.1080p.hk` - Bucket: `jsxiangche`(同一bucket,但有路径前缀) - 路径前缀: `由壹点AI/` - URL格式: `https://s3.1080p.hk/jsxiangche/由壹点AI/video-thumb-{timestamp}-{hash}.jpg` - 示例: `https://s3.1080p.hk/jsxiangche/由壹点AI/video-thumb-1766220802404-d6c20cf8f82c0c80.jpg` - **源文件(压缩包/视频)**: 本地文件系统 - 路径: `uploads/video-{timestamp}-{hash}.mp4` 或 `uploads/material-{timestamp}-{hash}.zip` - 相对路径格式(数据库存储) - 示例: `uploads/video-1766220805356-02770716caf02d9c.mp4` ##### 处理策略 - ⚠️ **不要移动或修改老素材** - 保持老素材在原位置,确保能正常访问和下载 - 老素材的路径信息已存储在数据库中 - 所有新上传的素材统一使用新位置(带 `由壹点AI/` 前缀) ##### 兼容性要求 - 下载功能已兼容新老路径(使用相同的 `./uploads` 目录) - 根据数据库中的路径信息自动判断使用哪个存储位置 - 确保老素材和新素材都能正常下载 - S3 预览图通过 `previewUrl` 字段直接访问,无需特殊处理 #### 数据库字段说明 ```typescript // materials 表 { zipFilePath: string; // 本地压缩包路径(可能是老路径或新路径) previewUrl: string; // 预览图URL(S3或老存储) // ... 其他字段 } ``` ### 🎯 源码恢复优先级 #### 高优先级(必须恢复) 1. `/api/materials/route.ts` - 修复分页BUG 2. `/api/materials/stats/route.ts` - 修复统计BUG 3. `/api/categories/route.ts` - 修复分类统计BUG 4. `/api/materials/download/route.ts` - 确保支持新老路径 5. 所有素材上传相关代码 - 确保新素材使用新路径 #### 中优先级(建议恢复) - 所有 `/api` 目录下的文件 - 所有 `/app` 目录下的页面文件 - 所有 `/components` 目录下的组件文件 #### 低优先级(可选) - 配置文件(如果本地已正确配置) - 文档文件 ### ⚠️ 恢复注意事项 1. **不要直接修改生产系统** - 只从生产系统读取和复制文件 - 所有修改先在本地测试 - 测试通过后部署到影子系统 - 影子系统验证通过后才部署到生产 2. **保护用户数据** - `uploads` 目录不要删除或覆盖 - 老素材路径不要修改 - 数据库操作前必须备份 3. **环境隔离** - 生产系统(4320): 数据库 `lobechat`, Redis 6379 - 影子系统(5320): 数据库 `lobechat_stage`, Redis 6380 - 确保环境变量正确配置 ## 后续工作 1. **建立版本控制**: 恢复完成后立即初始化 Git 仓库 2. **设置 CI/CD**: 配置自动化构建和部署流程 3. **代码审查**: 审查恢复的代码,添加必要的注释 4. **性能优化**: 识别并优化性能瓶颈 5. **安全加固**: 检查安全漏洞,更新依赖包 6. **修复已知BUG**: 修复上述发现的3个BUG 7. **路径兼容性测试**: 确保新老素材都能正常下载