gongxiangp2/升级到新版本flnxi_sucai方案.md

43 KiB
Raw Blame History

升级到新版本 flnxi_sucai 的安全实施方案(按“精选能力”增量升级)

目标:在不丢失任何素材、人员、权限和组织架构数据的前提下,将当前运行中的「由壹点 3D 素材平台」逐步升级,引入 flnxi_sucai 中经过验证的核心能力:

  • 存储 & S3:采用更健壮的 S3 工具和 URL 处理逻辑(但 继续使用当前固定的 S3 地址和桶,不再迁移旧 S3)。
  • 素材模型 & 列表:保留现有品牌/系列/SKU 体系之外的部分,仅引入 软删除 + URL 修正 + displayImages 统一处理。
  • 视频 & 下载:全面升级为新版本的视频处理与下载逻辑,解决客户反馈的“安装视频/主图视频问题”。
  • 权限 & 用户侧:引入 软删除日志、收藏、用户个人中心(我的素材/收藏)以及系统状态/监控 API,精品素材沿用当前系统机制。

原则:数据优先、安全可回滚、逐步灰度、随时观测;在不改变“上传形式和整体 UI 交互习惯”的前提下完成底层能力升级。


进度摘要(简版)

  • 已完成:

    • Docker 构建链路稳定(sharp/ffmpeg/本地 npm 缓存)与部署脚本,已多次成功发布
    • 首页:点赞/收藏分离;右上类型角标;视频预览、时长角标、转码进度与重试
    • 个人中心:我的素材/收藏;头像上传后 session.update 刷新 Header
    • 后台:分页/无限滚动;软删除/回收站;批量恢复/永久删除
    • API:/api/materials/like、/api/materials/favorite、/api/user/favorites
    • URL 重写:rewriteMaterialStorageUrlsForClient 已接入
    • 影子系统:已部署,独立端口用于验证
    • 体验修复:排行榜悬浮窗拖拽/关闭后重开已顺畅且位置同步;网络诊断(10MB 上下行、存储/穿透连通性检测)前后端已上线并可复制结果
    • 生产库迁移:已在 lobechat 执行 20251206000000_add_soft_delete_and_favorites;容器已重启(运行容器不含 schema,Prisma Client 在构建阶段已生成,运行态无需重复 generate)
    • 下载行为优化:Content-Type/扩展名推断,支持 Range 流式下载;前端本地即时 + 排行榜事件刷新下载数
    • 数据模型统一处理:displayImages 字段统一解析(从 JSON 字符串解析为数组),在所有 API 中统一应用;isFavorited 状态已在所有列表 API 中返回
    • 用户侧功能完善:个人中心页面完整实现"我的素材"和"我的收藏";首页收藏按钮已实现(书签样式)
  • 进行中:

    • (灰度前)继续只读验证影子系统,确认素材/权限/组织架构正确
  • 待办:

一、整体升级思路(分层而不是“一键替换”)

  • 保持一套生产数据库 + 一套生产 MinIO/S3 + 一套 NAS/uploads 不变,只在应用层升级代码和配置。
  • 使用 “影子环境 / 灰度环境 + 配置切换” 的方式,且明确区分两套系统:
    • 影子系统(Shadow):基于当前正式系统的源码(本仓库),按本方案“增量引入 flnxi 能力”,在独立端口和独立数据库上运行,用于验证改造是否安全可靠。
    • 正式系统(Production):当前线上在跑的 youyidian-3d 实例,端口为 4320,继续稳定对外提供服务,只在验证通过后才逐步切换到改造后的镜像。
    • flnxi_sucai 代码库作为参考实现与能力来源,不直接对外暴露,而是分批将其中的 S3 工具、日志、收藏、视频处理等能力迁移进“影子系统”源码中。
  • 全程配备:
    • 数据库备份 + 恢复脚本;
    • MinIO/S3 配置备份;
    • 应用回滚脚本(docker-compose 切版本);
    • 验证清单(重点验证:素材可见性、搜索/筛选、下载、上传、权限、组织架构、钉钉登录)。

二、升级前必做的备份与记录

2.1 数据库备份(PostgreSQL)

  1. 在服务器上执行数据库全量备份(以下命令需要根据实际 DATABASE_URL 调整):

    # 示例:备份到 /mnt/PoolOne/backups 目录
    mkdir -p /mnt/PoolOne/backups
    pg_dump "postgresql://gongxiangpan:gongxiangpan123@192.168.58.20:5432/lobechat" \
      -Fc -f "/mnt/PoolOne/backups/lobechat_before_flnxi_$(date +%Y%m%d_%H%M%S).dump"
    
  2. 记录备份文件路径,确认备份成功(文件大小 > 0):

    ls -lh /mnt/PoolOne/backups/lobechat_before_flnxi_*.dump
    
  3. 预演一次恢复命令(不用真正恢复,只是记下命令以备不时之需):

    # 回滚时可以用:
    pg_restore -c -d lobechat \
      " /mnt/PoolOne/backups/lobechat_before_flnxi_xxx.dump"
    

2.2 NAS / 本地上传目录备份

  • 当前应用使用的上传目录(以 docker-compose 为准,一般为 /mnt/tank/uploads 或 /app/uploads):

    # 示例:在宿主机上查看
    ls -lh /mnt/tank/uploads | head
    
  • 建议至少做一次 快照/rsync 备份(可由 NAS/存储管理员执行):

    # 示例:复制到备份目录(仅示意,实际路径按运维标准)
    rsync -av /mnt/tank/uploads/ /mnt/PoolOne/uploads_backup_before_flnxi/
    

2.3 MinIO / S3 配置与桶信息记录

记录当前线上使用的 S3 相关环境变量(可从正在运行的 app 容器中导出):

cd /mnt/PoolOne/apps/youyidian-3d
docker compose exec app env | grep -E "S3_|IMAGE_REMOTE_HOSTS|SERVER_IP|APP_PUBLIC_URL"

重点记录:

  • S3_ENDPOINT
  • S3_BUCKET
  • S3_ACCESS_KEY_ID
  • S3_SECRET_ACCESS_KEY
  • S3_PUBLIC_DOMAIN
  • S3_FILE_PATH / NEXT_PUBLIC_S3_FILE_PATH
  • (新版本增加)S3_ENV_PREFIX
  • IMAGE_REMOTE_HOSTS

这些值在新版本 flnxi_sucai 中必须保持兼容,否则会导致老素材的 URL 与新应用逻辑不一致。

2.3.1 老素材存储路径记录(保持原样,不迁移)

重要决策:升级过程中不迁移老素材源文件,保持老素材按照原有存储路径访问。

老素材存储位置

类型 存储位置 说明
老素材源文件 外网服务器 103.118.245.6 上的指定目录 通过 FRP 穿透访问,保持原有路径不变
老素材预览图 MinIO 桶 gongxiangpan/由壹点AI/ S3 存储,通过 https://s3.yydd.ai 访问
新素材源文件 内网 /mnt/PoolOne/apps/youyidian-3d/uploads 本地 NAS 存储
新素材预览图 MinIO 桶 gongxiangpan/ S3 存储,通过 https://s3.yydd.ai 访问

访问策略

  • 老素材:数据库中的 zipFilePath 字段保持指向外网服务器的原有路径,通过应用层的 URL 重写逻辑(rewriteMaterialStorageUrlsForClient)自动修正访问地址
  • 新素材:使用新的存储方案,源文件存放在内网 NAS,预览图存放在 S3

兼容性处理

在下载 API 和列表 API 中,需要区分老素材和新素材:

// 示例:下载 API 中的兼容性处理
if (material.zipFilePath) {
  // 检查是否是老素材(外网路径)
  if (material.zipFilePath.includes('103.118.245.6') || material.zipFilePath.startsWith('http')) {
    // 老素材:直接使用原有路径
    const fileUrl = material.zipFilePath;
  } else {
    // 新素材:使用本地路径
    const filePath = path.join(process.cwd(), material.zipFilePath);
  }
}

9.5 版本 2.2.0(已发布 2025-12-08)对外说明

  • feature · 个人中心上线 我的素材/我的收藏一站式查看,卡片支持预览与视频弹窗。

  • feature · 收藏(书签)按钮 卡片新增“收藏/取消收藏”,与点赞清晰区分;收藏列表在个人中心查看。

  • feature · 回收站与批量操作(管理员) 删除默认进入回收站;支持批量恢复与永久删除;关键操作留痕。

  • feature · 头像上传与即时刷新 头像选择-预览-上传一体化;提交后 Header 头像即时更新,无需整页刷新。

  • feature · 一键复制分享链接 卡片操作区支持复制分享链接,便于外发协作。

  • improvement · 收藏按钮样式与反馈优化 采用书签样式,未收藏/已收藏状态对比更明显,悬停反馈更清晰。

  • improvement · 预览视频编码与清晰度升级 预览视频改用 H.265 (HEVC) 编码,并提升分辨率与质量;画质更清晰。由于清晰度与质量目标提升,网络带宽占用与转码耗时会相应增加(弱网环境建议暂停自动播放)。

  • improvement · 转码失败一键重试 在卡片上即可重试转码,结合实时进度提示,处理更直观。

  • fix · 首页卡片稳定性 修复极端情况下预览区不渲染的问题,提高渲染稳定性。

2.4 现有权限与组织架构导出(只读检查)

使用 SQL 只读导出当前关键业务数据,用于升级后比对:

-- 用户数量与角色分布
SELECT role, COUNT(*) FROM "users" GROUP BY role;

-- 部门列表
SELECT * FROM "departments" ORDER BY "name";

-- 用户与部门分布(如果有 department 字段)
SELECT department, COUNT(*) FROM "users" GROUP BY department;

-- 素材总量与类型分布
SELECT "materialType", COUNT(*) FROM "materials" GROUP BY "materialType" ORDER BY COUNT(*) DESC;

将上述结果截图或保存为文本文件(例如 升级前数据快照.md),方便升级后对比。


三、参考新版本的实现规范与约束

重要提示:在改造过程中,必须参考 flnxi_sucai 新版本的逻辑和代码,同时参考新版本的布局设计。以下列出关键参考点和约束:

3.0.1 代码实现参考规范

核心原则:

  • ✅ 逻辑优先参考新版本:所有功能实现应参考 flnxi_sucai 中的对应实现
  • ✅ 布局参考新版本:UI 组件和页面布局应参考新版本的设计
  • ✅ 保持兼容性:改造后的代码必须兼容旧数据库(字段不存在时降级处理)
  • ✅ 渐进式迁移:不一次性替换,而是逐步引入新功能

关键参考文件清单:

  1. 软删除实现参考:

    • flnxi_sucai/app/api/materials/route.ts (DELETE 方法,第 318-482 行)
    • flnxi_sucai/app/api/materials/restore/route.ts (恢复功能)
    • 实现要点:
      • 使用 deletedAt 字段标记软删除
      • 永久删除前记录日志并清理文件(S3 + 本地)
      • 支持 permanent=true 参数进行永久删除
      • 恢复时设置 deletedAt = null
  2. 收藏功能参考:

    • flnxi_sucai/app/api/user/favorites/route.ts (GET 方法获取收藏列表)
    • flnxi_sucai/app/api/materials/favorite/route.ts (如果存在,收藏/取消收藏)
    • 实现要点:
      • 使用 MaterialFavorite 表记录收藏关系
      • 支持 isFavorited 状态在列表 API 中返回
      • 收藏按钮在前端显示
  3. 个人中心参考:

    • flnxi_sucai/app/api/user/materials/route.ts (我的素材列表)
    • flnxi_sucai/app/api/user/favorites/route.ts (我的收藏列表)
    • flnxi_sucai/app/profile/page.tsx (个人中心页面布局)
    • 实现要点:
      • 个人中心页面包含"我的素材"和"我的收藏"两个标签页
      • API 返回格式统一,包含 URL 重写后的数据
      • 使用 rewriteMaterialStorageUrlsForClient 统一处理 URL
  4. URL 重写参考:

    • flnxi_sucai/lib/url-rewrite.ts (完整的 URL 重写工具)
    • 实现要点:
      • rewriteStorageUrlForClient 处理单个 URL
      • rewriteMaterialStorageUrlsForClient 批量处理素材对象
      • 自动修正 localhost/127.0.0.1 为实际服务器地址
  5. 素材日志参考:

    • flnxi_sucai/lib/material-log.ts (日志工具库)
    • 实现要点:
      • createMaterialLog 函数统一创建日志
      • 支持自动创建表(如果不存在)
      • 日志格式:{ action, changes: { fieldName: { oldValue, newValue } } }
  6. 列表 API 参考:

    • flnxi_sucai/app/api/materials/route.ts (GET 方法,第 39-317 行)
    • 实现要点:
      • 使用 andConditions 数组构建查询条件
      • 软删除过滤:{ deletedAt: null } 或 { deletedAt: { not: null } }
      • 支持 showDeleted=true 查看已删除
      • 返回前统一应用 URL 重写和状态标记(isLiked, isFavorited)
  7. 删除文件清理参考:

    • flnxi_sucai/app/api/materials/route.ts (DELETE 方法,第 370-442 行)
    • 实现要点:
      • 收集所有相关文件 URL(预览图、视频、展示图等)
      • 区分 S3 URL 和本地文件,分别处理
      • 使用 deleteFromS3 删除 S3 文件
      • 使用 fs/promises.unlink 删除本地文件
      • 记录删除成功/失败数量

UI/布局参考规范:

  1. 个人中心页面布局:

    • 参考 flnxi_sucai/app/profile/page.tsx
    • 使用标签页(Tabs)切换"我的素材"和"我的收藏"
    • 素材卡片样式与首页保持一致
    • 保持当前系统的苹果风格设计
  2. 收藏按钮位置:

    • 参考新版本中收藏按钮的样式和位置
    • 在素材卡片上添加收藏图标
    • 点击后调用 /api/materials/favorite API
  3. 软删除管理界面:

    • 管理员后台添加"已删除素材"列表
    • 支持恢复和永久删除操作
    • 显示删除时间和操作人

代码约束:

  1. 兼容性处理:

    // 所有新字段/新表的使用都必须有降级处理
    try {
      // 使用新字段/新表
    } catch (error: any) {
      if (error?.meta?.column?.includes('deletedAt') || 
          error?.meta?.table?.includes('material_favorites')) {
        // 降级处理:使用旧逻辑或返回友好错误
      }
    }
    
  2. 类型安全:

    • 使用 as any 临时绕过类型检查(在兼容旧数据库时)
    • 添加注释说明为什么使用 as any
    • 优先使用 Prisma 生成的类型
  3. 错误处理:

    • 所有数据库操作都要有 try-catch
    • 日志记录失败不应影响主流程
    • 返回友好的错误消息给前端
  4. 代码组织:

    • 工具函数放在 lib/ 目录
    • API 路由放在 app/api/ 目录
    • 参考新版本的文件结构

三、flnxi_sucai 新版本与当前版本的核心差异(本次计划采纳范围)

3.1 存储与 URL 处理(采纳大部分)✅ 部分完成

已完成:

  • ✅ S3 工具核心能力已实现:
    • ✅ extractS3Key:统一处理 URL/key,提升删除、下载等操作对历史数据的兼容性
    • ✅ createPresignedDownloadUrl / getS3ObjectStream:为视频流式播放和安全下载提供基础
    • ✅ 内网 S3 端点智能切换(getOptimalS3Endpoint):自动检测并使用内网地址,提升上传速度
  • ✅ 视频处理链路已完善:
    • ✅ 区分源视频文件与预览视频/短片/封面图
    • ✅ ffmpeg 转码与错误回退逻辑
    • ✅ S3 上传重试与错误处理

已完成:

  • ✅ 实现 rewriteMaterialStorageUrlsForClient 功能(参考 flnxi_sucai/lib/url-rewrite.ts)
    • ✅ 统一修正 previewUrl、videoCoverUrl、videoPreview*、displayImages 中潜在的 localhost/127.0.0.1 或历史域名
    • ✅ 已在 app/api/materials/route.ts 中应用

待完成:

  • ⏳ 继续沿用当前生产 S3 桶与域名(如 https://s3.yydd.ai/gongxiangpan/由壹点AI/),不再迁移到新桶,也不调整老数据的存储位置。
  • ⏳ 更安全的 key 前缀构造(保留当前 S3_FILE_PATH,如有需要可预留 S3_ENV_PREFIX 但生产暂时不启用)

说明:虽然当前 S3 地址已经固定,但在"安装视频上传失败 / 老 URL 残留本机地址"的场景下,引入这些工具可以显著提升稳定性和排障效率。

3.2 数据模型与 API(只采纳:软删除 + URL 修正 + displayImages + 收藏等)⏳ 待实施

已完成:

  • ✅ 视频相关字段已存在:
    • ✅ videoSourceUrl、videoPreview540Url、videoPreviewClipUrl、videoCoverUrl、videoDuration、videoProcessingStatus
    • ✅ transcodeLastError、transcodeAttempts(转码错误处理)
  • ✅ displayImages 字段已存在(String[] 数组格式)

已完成:

  • ✅ Prisma Schema 已更新(prisma/schema.prisma):
    • ✅ deletedAt 字段已添加到 Material 模型
    • ✅ MaterialFavorite 表已定义
    • ✅ MaterialLog 表已定义
    • ✅ MaterialDownloadLog 表已定义(可选)
  • ✅ 列表 API 已调整(参考 flnxi_sucai/app/api/materials/route.ts):
    • ✅ 默认只返回 deletedAt IS NULL 的素材(使用 andConditions 数组)
    • ✅ 支持 showDeleted=true 查看已删除
    • ✅ 已应用 URL 重写逻辑(rewriteMaterialStorageUrlsForClient)
    • ✅ 已附带 isLiked 状态(点赞)
  • ✅ 软删除 API 已实现(参考 flnxi_sucai/app/api/materials/route.ts DELETE 方法):
    • ✅ 支持软删除(设置 deletedAt)
    • ✅ 支持永久删除(permanent=true,清理文件并硬删除)
    • ✅ 删除文件清理逻辑(S3 + 本地文件)
    • ✅ 删除日志记录(使用 createMaterialLog)
  • ✅ 恢复 API 已实现(app/api/materials/restore/route.ts,参考 flnxi_sucai/app/api/materials/restore/route.ts)
  • ✅ 素材日志工具已实现(lib/material-log.ts,参考 flnxi_sucai/lib/material-log.ts)

待完成:

  • ⏳ 运行数据库迁移(创建新字段和新表)✅ 已完成(已在生产库执行迁移)
  • ✅ 在返回前统一处理:
    • ✅ 将 displayImages 从 JSON 字符串解析为数组(已实现 parseDisplayImages 函数,参考新版本)
    • ✅ 附带 isFavorited(收藏)状态(已在列表 API 和详情 API 中实现,查询 MaterialFavorite 表)

说明:品牌/系列/SKU 等高级筛选相关字段暂不引入,保持现有业务模型不变,避免对现有数据结构和业务带来额外复杂度。

3.3 视频 & 下载相关的区别(全部采纳,首要改进点)✅ 基本完成

已完成:

  • ✅ 视频处理链路:
    • ✅ 区分 源视频文件 与 预览视频/短片/封面图,为后续优化播放体验预留空间
    • ✅ ffmpeg 转码与错误回退逻辑:转码失败时合理退回原始文件或给出明确错误
    • ✅ 视频转码进度实时显示(前端卡片显示转码状态和进度条)
  • ✅ S3 上传重试与错误处理:
    • ✅ 明确区分是 S3 连接问题、权限问题还是桶/路径错误
    • ✅ 出错时将状态标记为"失败"并给出用户可理解的提示(transcodeLastError 字段)
    • ✅ 内网 S3 端点智能切换,大幅提升上传速度
  • ✅ 视频播放:
    • ✅ 首页卡片自动播放 10 秒预览片段
    • ✅ 转码完成后显示预览视频,不再是静态封面

待优化:

  • ✅ 下载行为优化:
    • ✅ 根据 Content-Type 聪明推断扩展名,避免出现无扩展名/错误扩展名的下载文件(已实现,使用 mime-types 库)
    • ✅ 在前端本地更新下载计数,并同步刷新排行榜,而无需整页刷新(已实现,通过全局事件 material-download-success)
    • ✅ 为视频下载/在线播放提前支持流式传输与 Range 请求(已实现,支持 Range 请求和部分内容下载)

说明:在 UI 层面仍保留当前"点击卡片弹出视频播放器"的交互,不强制用新 UI 组件,只更换底层处理逻辑。

3.4 权限 / 用户侧能力(精选采纳)⏳ 待实施

已完成:

  • ✅ 软删除 + 日志(参考 flnxi_sucai/app/api/materials/route.ts):
    • ✅ 素材删除默认变为软删除(deletedAt 标记)
    • ✅ 永久删除前会记录详细日志(使用 createMaterialLog)
    • ✅ 永久删除时会清理关联文件(S3 + 本地,参考新版本第 370-442 行)
    • ✅ 恢复功能已实现(app/api/materials/restore/route.ts)
  • ✅ 收藏机制 API(参考 flnxi_sucai/app/api/user/favorites/route.ts):
    • ✅ 收藏/取消收藏 API 已实现(app/api/materials/favorite/route.ts)
    • ✅ 在前端新增/保留"收藏按钮"(首页已实现收藏按钮 UI,书签样式)
    • ✅ 提供 "我的收藏列表" API(已实现 /api/user/favorites)
  • ✅ 用户素材列表 / 个人中心(需要):
    • ✅ API:/api/user/materials(已实现,参考 flnxi_sucai/app/api/user/materials/route.ts)
    • ✅ API:/api/user/favorites(已实现,参考 flnxi_sucai/app/api/user/favorites/route.ts)
    • ✅ 页面:个人中心内"我的素材"、"我的收藏"(已实现,参考 flnxi_sucai/app/profile/page.tsx 布局,使用两个独立卡片展示)
  • ✅ 精品素材:
    • ✅ 继续沿用当前系统内已有的精品标记方式(如需要可参考 isPremium 字段)
  • ✅ 系统状态 / 监控 API(需要):
    • ✅ 已实现 /api/admin/system-status(参考 flnxi_sucai/app/api/admin/system-status/route.ts)
    • ✅ 已实现 /api/admin/video-transcode-stats(参考 flnxi_sucai/app/api/admin/video-transcode-stats/route.ts,适配当前系统使用 materialType)
    • ✅ 用于查看视频转码队列状况、S3 错误统计、上传/下载趋势等,帮助快速定位生产问题

UI 上仍然 保持当前风格和入口位置,只在需要的地方"加按钮/加入口",不整套替换为 flnxi 的新 UI 设计系统。

3.5 配置 & 运维层面的区别(按需采纳)

结合当前现状(S3 域名和桶已固定、暂不考虑再次迁移),本次升级在配置层面计划:

  • 保持现有 S3 环境变量不变,只增加少量可选变量以支持新逻辑:
    • IMAGE_REMOTE_HOSTS:用于 Next.js 图片远程域名白名单,可包含 s3.yydd.ai 与服务器 IP;
    • ALLOWED_ORIGINS:用于 CORS 白名单配置,方便未来支持更多访问入口;
    • (可选)S3_ENV_PREFIX:预留环境前缀能力,生产环境初期可留空。
  • Next.js 配置方面:
    • 采纳 compress / swcMinify 等生产优化设置;
    • 图片 remotePatterns 改为基于 IMAGE_REMOTE_HOSTS + 默认列表 生成,避免下次换域名还要改代码。
  • 中间件方面:
    • 保留现有的认证保护逻辑(首页 + /admin);
    • CORS 逻辑调整为优先使用白名单,仍兼容当前使用方式。

总体策略:不改变现有 S3 地址和桶,只增强容错和可配置性,为后续环境扩展预留空间。


四、推荐的分阶段升级步骤

阶段 0:在开发/预发布环境完成端到端验证(可选,基于备份)

  1. 在一台独立机器或独立 docker-compose 环境中:
    • 使用 生产数据库的一份备份(pg_restore 到临时库,例如 lobechat_stage);
    • 使用 NAS/uploads 的一份备份;
    • 使用 MinIO/S3 的 stage 环境(可以配置新的 S3_ENV_PREFIX=stage,避免覆盖线上文件)。
  2. 在该环境中部署 flnxi_sucai:
    • 复制 config-bianliang.env 或 .env.local,根据 stage 环境调整;
    • 确保 DATABASE_URL 指向 stage 库,S3_* 指向 stage MinIO;
    • 跑一次 npm run prisma:generate && npm run prisma:push(或 prisma migrate deploy)。
  3. 完整回归以下场景:
    • 首页:列表、搜索、筛选、排序、分页;
    • 素材详情/预览:主图、视频、PSD 预览、买家秀、多图展示;
    • 上传:3D 场景、PSD、视频、买家秀、小红书、品牌 VI 等;
    • 下载:3D 场景源文件、大文件压缩包、视频播放流式访问;
    • 权限:普通用户/管理员/(若有)部门管理员的可见性与操作范围;
    • 组织架构:部门列表、用户分布、部门管理页面;
    • 钉钉登录(如有):绑定、补充信息、权限继承。

只有当预发布环境的回归测试通过后,才进行生产环境升级。

阶段 1:在生产环境提前部署"影子系统"实例(只读验证)✅ 已完成

已完成:

  • ✅ 影子系统已部署在生产服务器:/mnt/PoolOne/apps/youyidian-3d-shadow
  • ✅ 独立的 docker-compose 配置:docker-compose.shadow.yml
  • ✅ 配置详情:
    • ✅ 镜像与源码:基于当前项目源码构建(包含视频上传、本地优先等改造)
    • ✅ 数据库:DATABASE_URL 指向 stage 库(lobechat_stage),绝不直接连生产库
    • ✅ S3:与正式系统使用相同的桶与前缀,验证 URL 兼容性
    • ✅ 端口:影子系统监听 5320 端口(5320:3000),避免与正式系统 4320 端口冲突
    • ✅ NEXTAUTH_URL / APP_PUBLIC_URL 设置为影子系统地址(http://192.168.58.20:5320)
  • ✅ 影子系统已启动并可访问:http://192.168.58.20:5320
  • ✅ 已修复影子系统数据库兼容性问题(缺少转码字段时的降级查询)

待验证:

  • ⏳ 通过内网访问影子系统,进行 只读验证:
    • ⏳ 确认所有已有素材 都能看到
    • ⏳ 确认组织架构与权限与旧系统一致
    • ⏳ 确认素材预览图、视频等资源正常显示

如果只读展示都存在问题(例如大量素材缺图/缺视频),应先在影子环境修正配置与 URL 逻辑,再考虑切流到新版本。

阶段 2:数据库迁移(在维护窗口内执行)✅ 影子系统已完成

重要: 强烈建议在低峰时段,短暂冻结「上传/删除/编辑」操作后再执行。

迁移文件已准备:

  • ✅ Migration 文件:prisma/migrations/20251206000000_add_soft_delete_and_favorites/migration.sql
  • ✅ 迁移脚本:
    • Linux/服务器:scripts/apply-migration.sh
    • Windows:scripts/apply-migration.ps1

执行步骤:

  1. 备份数据库(强烈推荐):

    # 在服务器上执行数据库备份
    pg_dump "postgresql://gongxiangpan:gongxiangpan123@192.168.58.20:5432/lobechat" \
      -Fc -f "/mnt/PoolOne/backups/lobechat_before_migration_$(date +%Y%m%d_%H%M%S).dump"
    
  2. 在影子系统测试迁移(先指向 stage 库测试)✅ 已完成:

    执行方式: 通过 PostgreSQL 容器直接执行 SQL 迁移文件

    # 已执行:通过 gongxiangpan-postgres-1 容器执行迁移
    docker exec -i gongxiangpan-postgres-1 psql -U gongxiangpan -d lobechat_stage < /tmp/migration.sql
    

    迁移结果: ✅ 成功

    • ✅ materials 表已添加 deletedAt 字段
    • ✅ material_favorites 表已创建
    • ✅ material_logs 表已创建
    • ✅ material_download_logs 表已创建
    • ✅ 所有索引和外键约束已创建
  3. 验证关键表结构(只读,确认 stage 库已包含改造所需字段)✅ 已验证:

    • ✅ 新字段和表已成功创建
  4. 测试通过后,在生产库执行迁移⏳ 待执行:

    cd /mnt/PoolOne/apps/youyidian-3d
    # 确保 DATABASE_URL 指向生产库 lobechat
    docker exec -i gongxiangpan-postgres-1 psql -U gongxiangpan -d lobechat < /tmp/migration.sql
    

    或者在 Windows 环境下使用 PowerShell 脚本:

    cd d:\gongxiangp2
    .\scripts\apply-migration.ps1 -ContainerName "youyidian-3d-material"
    
  5. 重新生成 Prisma Client(迁移后必须执行)⏳ 待执行:

    docker compose exec app npx prisma generate
    
  6. 重新启动影子系统与正式系统,验证⏳ 待执行:

    • 影子系统在新 schema 下读写正常;
    • 正式系统在新 schema 下也能正常工作(特别是列表与上传功能)。

这一阶段的目标是:先让数据库 schema 升级为“新旧代码都兼容”的状态,同时通过影子系统验证改造逻辑。

阶段 3:双应用并行、灰度执行写操作验证

  1. 在维护窗口内选取少量测试账号,只对它们开放在新应用 flnxi 上的写操作(上传/编辑/删除等)。

  2. 在测试账号下执行一轮完整操作:

    • 上传各种类型素材(特别是视频、PSD、买家秀等复杂类型);
    • 编辑已有素材、删除/恢复、标记精品、收藏/点赞;
    • 调整部门、角色、下载权限;
    • 验证钉钉登录与权限继承。
  3. 使用 SQL 检查新产生的数据是否与预期一致:

    -- 最近 N 条素材
    SELECT id, "productModel", "materialType", "previewUrl", "zipFileName", "createdAt"
    FROM "materials"
    ORDER BY "createdAt" DESC
    LIMIT 50;
    
    -- 最近 N 条用户变更
    SELECT * FROM "users" ORDER BY "updatedAt" DESC LIMIT 50;
    

如发现任何数据异常,可以立即停止在新应用上的写操作,仅保留旧应用写入,并在迁移方案中修复问题后再继续。

阶段 4:正式切流到新应用

  1. 在确认影子系统(youyidian-3d-shadow)读写均运行稳定后,计划正式切流:
    • Nginx 将生产域名从 youyidian-3d-material 容器指向基于影子系统镜像构建的正式容器;
    • 或在维护窗口内停止旧 youyidian-3d-material 容器,用同一个代码版本和镜像在 4320 端口启动新的正式实例。
  2. 切流前后短时间内开启加强监控:
    • HTTP 5xx 比例;
    • 数据库错误日志;
    • S3 上传/下载错误数量;
    • 用户反馈(上传/下载/登录是否异常)。
  3. 切流成功后,保留旧应用容器镜像与相关配置一段时间(例如 1–2 周),便于紧急回滚。

五、回滚策略和注意事项

5.1 应用层回滚

  • 如果发现新应用在功能层面存在严重问题,但数据库 schema 已迁移:
    • 可以在短时间内 切回 youyidian 旧应用代码;
    • 只要新旧版本使用的是兼容的 schema(即新字段对旧应用是“可选”的),旧应用仍然可以工作。

5.2 数据层回滚(仅在严重数据损坏时使用)

  • 如因错误脚本导致大量数据被误删/覆盖(例如错误的批量 UPDATE/DELETE):
    • 立即停止所有应用写操作;
    • 使用 2.1 中的备份恢复数据库;
    • 恢复过程中,必须配合恢复相同时间点的 NAS/uploads 与 S3 状态,否则会出现“数据库有记录、文件不存在”的不一致。

建议:除非确认数据严重损坏,否则优先采用 应用层回滚,避免频繁做数据库回滚。


六、升级过程中的验证清单(Checklist)

在每个关键阶段,建议按以下清单逐项验证:

6.1 核心业务验证

  • 首页能看到所有历史素材卡片(对比升级前截图)。
  • 搜索(按 SPU / 店铺 / 场景 / 作者)结果与升级前一致。
  • 各种素材类型(3D 场景、PSD、视频、买家秀、小红书等):
    • 主图/预览图正常显示;
    • PSD 自动预览正确;
    • 视频能正常播放(含主图视频/安装视频/种草视频);
    • 多图展示顺序正确。
  • 下载功能:
    • 大文件压缩包能顺利下载,文件大小与原始上传一致;
    • 视频下载/播放流畅,无明显卡顿或 404。

6.2 权限与组织架构验证

  • 普通用户只能看到自己有权限的素材,不能进入管理员页面;
  • 管理员拥有完整权限(管理用户/素材/分类/模板等);
  • 部门信息、用户所属部门与升级前一致;
  • 特殊角色(如部门管理员、超级管理员)在新后台中角色显示与权限行为正确。

6.3 系统配置与存储验证

  • 系统设置页面中显示的主题色、LOGO、站点名称与预期一致;
  • 新上传的预览图与视频文件路径均为新桶地址(如 https://s3.yydd.ai/...);
  • 旧素材仍能通过原有 URL 访问,不存在大量 404。

6.4 日志与监控

  • 应用日志中无持续性 S3上传失败 / UnknownError / 数据库连接错误;
  • materials 列表 API 的错误日志(500)保持在极低水平;
  • 上传/下载高峰期 CPU / 内存 / 磁盘 IO 处于可接受范围。

七、建议的实施顺序(摘要)

  1. 预发布环境完整回归(强烈推荐)
    在隔离环境中对 flnxi_sucai 做端到端验证。
  2. 生产预备:备份 + 影子应用 + 只读验证
    确保新代码在真实生产数据上能正常展示。
  3. 数据库迁移(Prisma migrate)
    先升级 schema,保证新旧应用均可工作。
  4. 灰度写操作验证
    小范围用户在新应用上执行写操作,观察数据一致性与稳定性。
  5. 正式切流 & 监控
    将流量切到新应用,开启加强监控。
  6. 观察期 & 渐进优化
    观察 1–2 周,逐步启用新功能(模板管理、日志、精品、收藏等)。

只要严格按照以上步骤执行,并在每个阶段完成验证清单,素材、人员信息、组织架构与权限数据都可以在升级过程中保持安全与一致,不会出现"素材看不见、人没了、权限错乱"的风险。


九、本次改造更新内容记录(用于更新 UpdateLogModal)

用途:以下内容用于后续更新 components/UpdateLogModal.tsx 中的更新记录,方便用户了解系统改进。

9.1 新功能(Features)

  1. 素材软删除功能

    • 素材删除现在默认变为软删除,管理员可以在后台查看并恢复已删除的素材
    • 支持永久删除(会清理相关文件),删除操作会记录详细日志
    • 已删除的素材不会在首页显示,但可以通过 showDeleted=true 参数查看
  2. 素材收藏功能

    • 用户可以收藏喜欢的素材,方便后续快速查找
    • 在素材卡片上添加了收藏按钮,点击即可收藏/取消收藏
    • 收藏状态会在素材列表中显示(isFavorited 字段)
  3. 个人中心页面

    • 新增个人中心页面(/profile),包含"我的素材"和"我的收藏"两个标签页
    • "我的素材":显示当前用户上传的所有素材
    • "我的收藏":显示当前用户收藏的所有素材
    • 在页面右上角头像下拉菜单中添加了"个人中心"入口
  4. 系统监控功能(管理员)

    • 新增系统状态监控 API(/api/admin/system-status),可查看用户统计、素材统计、趋势数据等
    • 新增视频转码统计 API(/api/admin/video-transcode-stats),可查看转码队列状况、失败任务等
    • 帮助管理员快速定位生产问题和系统健康状况

9.2 优化改进(Improvements)

  1. URL 重写优化

    • 自动修正素材 URL 中的 localhost/127.0.0.1 为实际服务器地址
    • 确保所有素材预览图、视频等资源都能正常访问,避免 404 错误
    • 统一处理 previewUrl、videoCoverUrl、videoPreview*、displayImages 等字段
  2. 素材列表 API 优化

    • 默认只返回未删除的素材,提升查询性能
    • 支持 showDeleted=true 查看已删除素材(管理员功能)
    • 返回数据中包含点赞状态(isLiked)和收藏状态(isFavorited)
  3. 删除功能增强

    • 永久删除时会自动清理 S3 和本地文件,避免存储空间浪费
    • 所有删除操作都会记录详细日志,便于追溯和审计
    • 支持恢复已删除的素材,避免误删导致的数据丢失

9.3 技术改进(Technical)

  1. 数据库 Schema 升级

    • 添加 deletedAt 字段到 materials 表(软删除)
    • 新增 material_favorites 表(收藏关系)
    • 新增 material_logs 表(操作日志)
    • 新增 material_download_logs 表(下载日志,可选)
  2. 代码兼容性

    • 所有新功能都兼容旧数据库(字段/表不存在时自动降级处理)
    • 使用类型断言(as any)临时绕过类型检查,确保在旧数据库环境下也能正常工作
    • 添加了完善的错误处理和降级逻辑
  3. 参考新版本实现

    • 所有功能实现都参考了 flnxi_sucai 新版本的代码和逻辑
    • UI 布局参考新版本设计,保持一致的交互体验
    • 代码组织遵循新版本的文件结构和命名规范

9.4 更新记录文案建议(用户友好版本)

版本 2.2.0(待发布)

以下内容可以直接用于更新 components/UpdateLogModal.tsx 中的 UPDATE_LOGS 数组:

{
  version: '2.2.0',
  date: '2025-12-06', // 根据实际发布日期调整
  title: '系统更新',
  items: [
    {
      type: 'feature',
      title: '素材软删除功能',
      description: '现在删除素材时,系统会先标记为"已删除"而不是直接删除。管理员可以在后台查看并恢复误删的素材,避免重要数据丢失。永久删除时会自动清理相关文件,节省存储空间。'
    },
    {
      type: 'feature',
      title: '素材收藏功能',
      description: '现在可以收藏喜欢的素材了!在素材卡片上点击收藏按钮,就能把素材添加到收藏列表。在个人中心可以快速查看所有收藏的素材,方便后续使用。'
    },
    {
      type: 'feature',
      title: '个人中心页面',
      description: '新增了个人中心页面,包含"我的素材"和"我的收藏"两个标签页。"我的素材"显示您上传的所有素材,"我的收藏"显示您收藏的所有素材。在页面右上角头像下拉菜单中可以进入个人中心。'
    },
    {
      type: 'feature',
      title: '系统监控功能(管理员)',
      description: '管理员现在可以查看系统整体状态和视频转码统计了。包括用户统计、素材统计、趋势数据、转码队列状况等,帮助快速定位和解决生产问题。'
    },
    {
      type: 'improvement',
      title: 'URL 自动修正',
      description: '系统现在会自动修正素材预览图、视频等资源的访问地址,修复了部分素材因为地址问题无法显示的问题。现在所有素材的预览图和视频都能正常显示了。'
    },
    {
      type: 'improvement',
      title: '素材列表优化',
      description: '素材列表现在默认只显示未删除的素材,查找更快速。已删除的素材不会在首页显示,让您看到的都是可用的素材。'
    },
    {
      type: 'improvement',
      title: '删除功能增强',
      description: '永久删除素材时,系统会自动清理相关的预览图、视频文件等,避免占用存储空间。所有删除操作都会记录详细日志,便于追溯和审计。'
    }
  ]
}

更新步骤:

  1. 在 components/UpdateLogModal.tsx 中,将 CURRENT_VERSION 更新为 '2.2.0'
  2. 将上述内容添加到 UPDATE_LOGS 数组的最前面(作为最新版本)
  3. 根据实际发布日期调整 date 字段
  4. 测试更新记录弹窗显示是否正常

注意:以上内容需要在数据库迁移执行完成并测试通过后,再更新到 UpdateLogModal.tsx 中。


附录:常见问题与解决方案

A. 文件下载相关问题

A.1 下载 API 404 错误

问题描述:

  • 浏览器返回 404 错误,无法下载素材
  • 错误信息:GET /api/materials/download?id=xxx 404 (Not Found)

根本原因:

  1. 路由未被正确注册:TypeScript 编译时出错导致 Next.js 无法识别 API 路由

    • 通常是因为项目中有编译错误(如导入不存在的模块)
    • 导致整个构建失败,API 路由没有被正确编译到 .next 目录
  2. 容器未挂载文件目录:即使路由正确,也可能因为容器内找不到文件而返回 404

    • 老素材文件存储在宿主机的 /mnt/PoolOne/apps/youyidian-3d/uploads/ 目录
    • 如果容器启动时没有挂载这个目录,容器内的 /app/uploads/ 就是空的
    • 下载 API 会因为找不到文件而返回 404

解决方案:

  1. 检查编译错误:

    # 本地运行构建,查看是否有 TypeScript 错误
    npm run build
    
    # 常见错误:项目中有无关的目录导致编译失败
    # 例如:flnxi_sucai 目录(参考项目副本)
    # 解决:在 tsconfig.json 中排除这个目录
    
  2. 修改 tsconfig.json:

    {
      "compilerOptions": { ... },
      "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
      "exclude": ["node_modules", "flnxi_sucai"]  // 排除无关目录
    }
    
  3. 正确挂载文件目录:

    # 启动容器时,必须挂载老素材目录
    docker run -d \
      --name youyidian-3d-material \
      -p 4320:3000 \
      -v /mnt/PoolOne/apps/youyidian-3d/uploads:/app/uploads \
      --env-file /mnt/PoolOne/apps/youyidian-3d/.env \
      youyidian-3d-material:latest
    
  4. 验证路由是否正确注册:

    # 查看编译后的路由清单
    docker exec youyidian-3d-material cat /app/.next/app-path-routes-manifest.json | grep download
    
    # 应该看到:"/api/materials/download/route": "/api/materials/download"
    
  5. 验证文件是否可访问:

    # 检查容器内的文件目录
    docker exec youyidian-3d-material ls -la /app/uploads/ | head -20
    
    # 应该看到老素材的 zip 文件列表
    

A.2 HTTPS 警告:文件通过不安全连接加载

问题描述:

  • 浏览器控制台警告:The file at 'blob:http://192.168.58.20:4320/...' was loaded over an insecure connection. This file should be served over HTTPS.
  • 下载功能正常,但浏览器认为不安全

根本原因:

  • 应用通过 HTTP 协议访问(http://192.168.58.20:4320)
  • 浏览器下载文件时使用了 blob URL,但来自 HTTP 源
  • 现代浏览器要求文件下载必须通过 HTTPS

解决方案:

  1. 短期方案(开发/测试环境):

    • 这个警告不影响功能,只是浏览器的安全提示
    • 在生产环境中使用 HTTPS 可以消除这个警告
  2. 长期方案(生产环境):

    # 配置 HTTPS(使用 Let's Encrypt 证书)
    # 在 Nginx 或反向代理中配置 SSL 证书
    # 确保应用通过 HTTPS 访问
    
  3. 应用层配置:

    • 确保 NEXTAUTH_URL 使用 HTTPS 协议
    • 确保 S3_PUBLIC_DOMAIN 使用 HTTPS 协议
    • 在下载 API 中,返回的文件 URL 应该是 HTTPS

示例配置:

# .env 文件中
NEXTAUTH_URL=https://yourdomain.com  # 使用 HTTPS
S3_PUBLIC_DOMAIN=https://s3.yydd.ai/gongxiangpan  # 使用 HTTPS

B. 老素材存储路径

重要决策(2025-12-07):

  • 不迁移老素材源文件,保持原有存储位置
  • 老素材源文件存储路径:/mnt/PoolOne/apps/youyidian-3d/uploads/
  • 新素材源文件存储路径:/mnt/PoolOne/apps/youyidian-3d/uploads/(同一目录)
  • 老素材预览图存储:MinIO 桶 gongxiangpan/由壹点AI/
  • 新素材预览图存储:MinIO 桶 gongxiangpan/

容器挂载配置:

# 必须在 docker run 时添加 volume 挂载
-v /mnt/PoolOne/apps/youyidian-3d/uploads:/app/uploads

C. 镜像优化

问题:镜像大小过大(800+ MB)

原因:

  • 项目中包含无关的目录(如 flnxi_sucai 参考项目)
  • 这些目录被打包进 Docker 镜像,导致镜像过大

解决方案:

  1. 在 tsconfig.json 中排除无关目录
  2. 在 .dockerignore 中排除无关目录
  3. 确保构建时只包含必要的源代码

检查清单:

# 查看镜像大小
docker images | grep youyidian-3d-material

# 应该在 100-150 MB 之间(合理范围)