43 KiB
升级到新版本 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)
-
在服务器上执行数据库全量备份(以下命令需要根据实际
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" -
记录备份文件路径,确认备份成功(文件大小 > 0):
ls -lh /mnt/PoolOne/backups/lobechat_before_flnxi_*.dump -
预演一次恢复命令(不用真正恢复,只是记下命令以备不时之需):
# 回滚时可以用: 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_ENDPOINTS3_BUCKETS3_ACCESS_KEY_IDS3_SECRET_ACCESS_KEYS3_PUBLIC_DOMAINS3_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 组件和页面布局应参考新版本的设计
- ✅ 保持兼容性:改造后的代码必须兼容旧数据库(字段不存在时降级处理)
- ✅ 渐进式迁移:不一次性替换,而是逐步引入新功能
关键参考文件清单:
-
软删除实现参考:
flnxi_sucai/app/api/materials/route.ts(DELETE 方法,第 318-482 行)flnxi_sucai/app/api/materials/restore/route.ts(恢复功能)- 实现要点:
- 使用
deletedAt字段标记软删除 - 永久删除前记录日志并清理文件(S3 + 本地)
- 支持
permanent=true参数进行永久删除 - 恢复时设置
deletedAt = null
- 使用
-
收藏功能参考:
flnxi_sucai/app/api/user/favorites/route.ts(GET 方法获取收藏列表)flnxi_sucai/app/api/materials/favorite/route.ts(如果存在,收藏/取消收藏)- 实现要点:
- 使用
MaterialFavorite表记录收藏关系 - 支持
isFavorited状态在列表 API 中返回 - 收藏按钮在前端显示
- 使用
-
个人中心参考:
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
-
URL 重写参考:
flnxi_sucai/lib/url-rewrite.ts(完整的 URL 重写工具)- 实现要点:
rewriteStorageUrlForClient处理单个 URLrewriteMaterialStorageUrlsForClient批量处理素材对象- 自动修正
localhost/127.0.0.1为实际服务器地址
-
素材日志参考:
flnxi_sucai/lib/material-log.ts(日志工具库)- 实现要点:
createMaterialLog函数统一创建日志- 支持自动创建表(如果不存在)
- 日志格式:
{ action, changes: { fieldName: { oldValue, newValue } } }
-
列表 API 参考:
flnxi_sucai/app/api/materials/route.ts(GET 方法,第 39-317 行)- 实现要点:
- 使用
andConditions数组构建查询条件 - 软删除过滤:
{ deletedAt: null }或{ deletedAt: { not: null } } - 支持
showDeleted=true查看已删除 - 返回前统一应用 URL 重写和状态标记(
isLiked,isFavorited)
- 使用
-
删除文件清理参考:
flnxi_sucai/app/api/materials/route.ts(DELETE 方法,第 370-442 行)- 实现要点:
- 收集所有相关文件 URL(预览图、视频、展示图等)
- 区分 S3 URL 和本地文件,分别处理
- 使用
deleteFromS3删除 S3 文件 - 使用
fs/promises.unlink删除本地文件 - 记录删除成功/失败数量
UI/布局参考规范:
-
个人中心页面布局:
- 参考
flnxi_sucai/app/profile/page.tsx - 使用标签页(Tabs)切换"我的素材"和"我的收藏"
- 素材卡片样式与首页保持一致
- 保持当前系统的苹果风格设计
- 参考
-
收藏按钮位置:
- 参考新版本中收藏按钮的样式和位置
- 在素材卡片上添加收藏图标
- 点击后调用
/api/materials/favoriteAPI
-
软删除管理界面:
- 管理员后台添加"已删除素材"列表
- 支持恢复和永久删除操作
- 显示删除时间和操作人
代码约束:
-
兼容性处理:
// 所有新字段/新表的使用都必须有降级处理 try { // 使用新字段/新表 } catch (error: any) { if (error?.meta?.column?.includes('deletedAt') || error?.meta?.table?.includes('material_favorites')) { // 降级处理:使用旧逻辑或返回友好错误 } } -
类型安全:
- 使用
as any临时绕过类型检查(在兼容旧数据库时) - 添加注释说明为什么使用
as any - 优先使用 Prisma 生成的类型
- 使用
-
错误处理:
- 所有数据库操作都要有 try-catch
- 日志记录失败不应影响主流程
- 返回友好的错误消息给前端
-
代码组织:
- 工具函数放在
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.tsDELETE 方法):- ✅ 支持软删除(设置
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 请求和部分内容下载)
- ✅ 根据 Content-Type 聪明推断扩展名,避免出现无扩展名/错误扩展名的下载文件(已实现,使用
说明:在 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:
/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布局,使用两个独立卡片展示)
- ✅ API:
- ✅ 精品素材:
- ✅ 继续沿用当前系统内已有的精品标记方式(如需要可参考
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:在开发/预发布环境完成端到端验证(可选,基于备份)
- 在一台独立机器或独立 docker-compose 环境中:
- 使用 生产数据库的一份备份(
pg_restore到临时库,例如lobechat_stage); - 使用 NAS/uploads 的一份备份;
- 使用 MinIO/S3 的 stage 环境(可以配置新的
S3_ENV_PREFIX=stage,避免覆盖线上文件)。
- 使用 生产数据库的一份备份(
- 在该环境中部署
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)。
- 复制
- 完整回归以下场景:
- 首页:列表、搜索、筛选、排序、分页;
- 素材详情/预览:主图、视频、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
- Linux/服务器:
执行步骤:
-
备份数据库(强烈推荐):
# 在服务器上执行数据库备份 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" -
在影子系统测试迁移(先指向 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表已创建 - ✅ 所有索引和外键约束已创建
- ✅
-
验证关键表结构(只读,确认 stage 库已包含改造所需字段)✅ 已验证:
- ✅ 新字段和表已成功创建
-
测试通过后,在生产库执行迁移⏳ 待执行:
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" -
重新生成 Prisma Client(迁移后必须执行)⏳ 待执行:
docker compose exec app npx prisma generate -
重新启动影子系统与正式系统,验证⏳ 待执行:
- 影子系统在新 schema 下读写正常;
- 正式系统在新 schema 下也能正常工作(特别是列表与上传功能)。
这一阶段的目标是:先让数据库 schema 升级为“新旧代码都兼容”的状态,同时通过影子系统验证改造逻辑。
阶段 3:双应用并行、灰度执行写操作验证
-
在维护窗口内选取少量测试账号,只对它们开放在新应用 flnxi 上的写操作(上传/编辑/删除等)。
-
在测试账号下执行一轮完整操作:
- 上传各种类型素材(特别是视频、PSD、买家秀等复杂类型);
- 编辑已有素材、删除/恢复、标记精品、收藏/点赞;
- 调整部门、角色、下载权限;
- 验证钉钉登录与权限继承。
-
使用 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:正式切流到新应用
- 在确认影子系统(youyidian-3d-shadow)读写均运行稳定后,计划正式切流:
- Nginx 将生产域名从
youyidian-3d-material容器指向基于影子系统镜像构建的正式容器; - 或在维护窗口内停止旧
youyidian-3d-material容器,用同一个代码版本和镜像在 4320 端口启动新的正式实例。
- Nginx 将生产域名从
- 切流前后短时间内开启加强监控:
- HTTP 5xx 比例;
- 数据库错误日志;
- S3 上传/下载错误数量;
- 用户反馈(上传/下载/登录是否异常)。
- 切流成功后,保留旧应用容器镜像与相关配置一段时间(例如 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 处于可接受范围。
七、建议的实施顺序(摘要)
- 预发布环境完整回归(强烈推荐)
在隔离环境中对flnxi_sucai做端到端验证。 - 生产预备:备份 + 影子应用 + 只读验证
确保新代码在真实生产数据上能正常展示。 - 数据库迁移(Prisma migrate)
先升级 schema,保证新旧应用均可工作。 - 灰度写操作验证
小范围用户在新应用上执行写操作,观察数据一致性与稳定性。 - 正式切流 & 监控
将流量切到新应用,开启加强监控。 - 观察期 & 渐进优化
观察 1–2 周,逐步启用新功能(模板管理、日志、精品、收藏等)。
只要严格按照以上步骤执行,并在每个阶段完成验证清单,素材、人员信息、组织架构与权限数据都可以在升级过程中保持安全与一致,不会出现"素材看不见、人没了、权限错乱"的风险。
九、本次改造更新内容记录(用于更新 UpdateLogModal)
用途:以下内容用于后续更新
components/UpdateLogModal.tsx中的更新记录,方便用户了解系统改进。
9.1 新功能(Features)
-
素材软删除功能
- 素材删除现在默认变为软删除,管理员可以在后台查看并恢复已删除的素材
- 支持永久删除(会清理相关文件),删除操作会记录详细日志
- 已删除的素材不会在首页显示,但可以通过
showDeleted=true参数查看
-
素材收藏功能
- 用户可以收藏喜欢的素材,方便后续快速查找
- 在素材卡片上添加了收藏按钮,点击即可收藏/取消收藏
- 收藏状态会在素材列表中显示(
isFavorited字段)
-
个人中心页面
- 新增个人中心页面(
/profile),包含"我的素材"和"我的收藏"两个标签页 - "我的素材":显示当前用户上传的所有素材
- "我的收藏":显示当前用户收藏的所有素材
- 在页面右上角头像下拉菜单中添加了"个人中心"入口
- 新增个人中心页面(
-
系统监控功能(管理员)
- 新增系统状态监控 API(
/api/admin/system-status),可查看用户统计、素材统计、趋势数据等 - 新增视频转码统计 API(
/api/admin/video-transcode-stats),可查看转码队列状况、失败任务等 - 帮助管理员快速定位生产问题和系统健康状况
- 新增系统状态监控 API(
9.2 优化改进(Improvements)
-
URL 重写优化
- 自动修正素材 URL 中的
localhost/127.0.0.1为实际服务器地址 - 确保所有素材预览图、视频等资源都能正常访问,避免 404 错误
- 统一处理
previewUrl、videoCoverUrl、videoPreview*、displayImages等字段
- 自动修正素材 URL 中的
-
素材列表 API 优化
- 默认只返回未删除的素材,提升查询性能
- 支持
showDeleted=true查看已删除素材(管理员功能) - 返回数据中包含点赞状态(
isLiked)和收藏状态(isFavorited)
-
删除功能增强
- 永久删除时会自动清理 S3 和本地文件,避免存储空间浪费
- 所有删除操作都会记录详细日志,便于追溯和审计
- 支持恢复已删除的素材,避免误删导致的数据丢失
9.3 技术改进(Technical)
-
数据库 Schema 升级
- 添加
deletedAt字段到materials表(软删除) - 新增
material_favorites表(收藏关系) - 新增
material_logs表(操作日志) - 新增
material_download_logs表(下载日志,可选)
- 添加
-
代码兼容性
- 所有新功能都兼容旧数据库(字段/表不存在时自动降级处理)
- 使用类型断言(
as any)临时绕过类型检查,确保在旧数据库环境下也能正常工作 - 添加了完善的错误处理和降级逻辑
-
参考新版本实现
- 所有功能实现都参考了
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: '永久删除素材时,系统会自动清理相关的预览图、视频文件等,避免占用存储空间。所有删除操作都会记录详细日志,便于追溯和审计。'
}
]
}
更新步骤:
- 在
components/UpdateLogModal.tsx中,将CURRENT_VERSION更新为'2.2.0' - 将上述内容添加到
UPDATE_LOGS数组的最前面(作为最新版本) - 根据实际发布日期调整
date字段 - 测试更新记录弹窗显示是否正常
注意:以上内容需要在数据库迁移执行完成并测试通过后,再更新到
UpdateLogModal.tsx中。
附录:常见问题与解决方案
A. 文件下载相关问题
A.1 下载 API 404 错误
问题描述:
- 浏览器返回 404 错误,无法下载素材
- 错误信息:
GET /api/materials/download?id=xxx 404 (Not Found)
根本原因:
-
路由未被正确注册:TypeScript 编译时出错导致 Next.js 无法识别 API 路由
- 通常是因为项目中有编译错误(如导入不存在的模块)
- 导致整个构建失败,API 路由没有被正确编译到
.next目录
-
容器未挂载文件目录:即使路由正确,也可能因为容器内找不到文件而返回 404
- 老素材文件存储在宿主机的
/mnt/PoolOne/apps/youyidian-3d/uploads/目录 - 如果容器启动时没有挂载这个目录,容器内的
/app/uploads/就是空的 - 下载 API 会因为找不到文件而返回 404
- 老素材文件存储在宿主机的
解决方案:
-
检查编译错误:
# 本地运行构建,查看是否有 TypeScript 错误 npm run build # 常见错误:项目中有无关的目录导致编译失败 # 例如:flnxi_sucai 目录(参考项目副本) # 解决:在 tsconfig.json 中排除这个目录 -
修改
tsconfig.json:{ "compilerOptions": { ... }, "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"], "exclude": ["node_modules", "flnxi_sucai"] // 排除无关目录 } -
正确挂载文件目录:
# 启动容器时,必须挂载老素材目录 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 -
验证路由是否正确注册:
# 查看编译后的路由清单 docker exec youyidian-3d-material cat /app/.next/app-path-routes-manifest.json | grep download # 应该看到:"/api/materials/download/route": "/api/materials/download" -
验证文件是否可访问:
# 检查容器内的文件目录 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
解决方案:
-
短期方案(开发/测试环境):
- 这个警告不影响功能,只是浏览器的安全提示
- 在生产环境中使用 HTTPS 可以消除这个警告
-
长期方案(生产环境):
# 配置 HTTPS(使用 Let's Encrypt 证书) # 在 Nginx 或反向代理中配置 SSL 证书 # 确保应用通过 HTTPS 访问 -
应用层配置:
- 确保
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 镜像,导致镜像过大
解决方案:
- 在
tsconfig.json中排除无关目录 - 在
.dockerignore中排除无关目录 - 确保构建时只包含必要的源代码
检查清单:
# 查看镜像大小
docker images | grep youyidian-3d-material
# 应该在 100-150 MB 之间(合理范围)