docs: align deployment and storage runbooks

This commit is contained in:
chenpei 2026-05-07 17:53:26 +08:00
parent edee901790
commit 340f688b7b
25 changed files with 643 additions and 1351 deletions

View File

@ -231,35 +231,34 @@ ssh root@192.168.58.20 "cat /root/youyidian-3d/.env 2>/dev/null || echo 'No .env
## 部署
项目使用 **远程构建部署** 方式,通过 PowerShell 脚本:
当前默认使用 **Git 增量部署**,统一通过 `scripts/git-incremental-deploy.ps1` 执行:
```powershell
# 预检
.\scripts\git-incremental-deploy.ps1 -DryRun
# 部署到生产环境
.\deploy-remote-build.ps1
.\scripts\git-incremental-deploy.ps1
# 部署到影子环境
.\deploy-remote-build.ps1 -Target shadow
# 强制重新构建(无缓存)
.\deploy-remote-build.ps1 -NoCache
# 深度清理构建缓存(默认保留缓存以加快构建)
.\deploy-remote-build.ps1 -AggressiveBuildCacheCleanup
.\scripts\git-incremental-deploy.ps1 -Target shadow
```
**部署流程:**
1. 本地打包代码(排除 node_modules、.next、.git)
2. 上传到构建服务器 (192.168.42.134)
3. 在构建服务器上构建 Docker 镜像
4. 推送到本地镜像仓库 (192.168.42.134:5000)
5. 拉取到生产服务器 (192.168.58.20)
6. 在端口 4320(生产)或 5320(影子)启动容器
**当前部署流程:**
1. 源码先提交并推送到 Git 仓库
2. 构建服务器 (192.168.42.134) 增量拉取指定提交
3. 构建服务器构建 Docker 镜像并推送到本地镜像仓库 (192.168.42.134:5000)
4. 生产服务器 (192.168.58.20) 拉取指定提交镜像
5. 替换正式容器或影子容器
6. 检查 `/api/health`,失败时默认尝试回滚旧镜像
**部署脚本优化(2026-05-05):**
- 默认保留构建缓存,加快小改动的构建速度
- 打包完整性校验:SHA256 哈希验证
- Alpine 镜像源:使用清华源 `https://mirrors.tuna.tsinghua.edu.cn/alpine`
- 临时文件处理:避免 `tar: .: file changed as we read it` 错误
`deploy-remote-build.ps1` 属于本地历史脚本名,仅作为识别应急资料使用,不是仓库交付入口,也不再作为默认部署入口。旧的“本机打包源码 -> 上传到构建机”链路不再作为当前标准口径。
详细部署规范见:
- `docs/Git增量部署方案.md`
- `docs/部署规范.md`
- `docs/部署脚本安全说明.md`
## 架构

View File

@ -234,6 +234,23 @@ npm run prisma:studio
## 部署说明
### 当前标准部署
当前线上默认使用 Git 增量部署:
```powershell
.\scripts\git-incremental-deploy.ps1 -DryRun
.\scripts\git-incremental-deploy.ps1
```
部署链路是:源码推送到 Git -> 构建机增量拉取并构建镜像 -> 生产机拉取镜像并替换应用容器。旧的 `deploy-remote-build.ps1` 属于本地历史脚本名,只作为识别应急资料使用,不是仓库交付入口,也不再作为默认推荐方式。
详细说明见:
- `docs/Git增量部署方案.md`
- `docs/部署规范.md`
- `docs/部署脚本安全说明.md`
### 本地或单机生产启动
```bash
@ -264,8 +281,10 @@ npm run start
## 相关文档
- `docs/NETWORK_TOPOLOGY.md`:当前网络拓扑与故障排查
- `docs/部署规范.md`:当前部署规范
- `docs/Git增量部署方案.md`:Git 增量部署方案
- `docs/生产存储桶接入信息.md`:当前 MinIO / uploads 存储事实
- `docs/CODEX_MULTI_AGENTS.md`:项目内 Codex 团队协作规则
- `部署前安全检查清单.md`:上线前检查项
- `docs/外部接口文档.md`:当前对外接口说明
## 说明

View File

@ -1,6 +1,7 @@
# 由壹点3D素材 - 构建基础镜像脚本
# 功能:一次性构建包含所有依赖的基础镜像
# 用途:当依赖变化时运行,后续部署直接使用此镜像
# 状态:旧构建缓存方案,不是当前 Git 增量部署的默认前置步骤
param(
[string]$BuildServerIP = '192.168.42.134',

View File

@ -1,3 +1,6 @@
# 当前影子环境发布不使用本文件作为入口。
# 影子发布以 scripts/git-incremental-deploy.ps1 -Target shadow 为准;本文件仅作本地/单机/基础环境参考。
# 不要按此文件推断当前线上影子容器启动参数、uploads 挂载或健康检查。
version: '3.8'
services:

View File

@ -1,3 +1,6 @@
# 当前生产发布不使用本文件作为入口。
# 生产发布以 scripts/git-incremental-deploy.ps1 为准;本文件仅作本地/单机/基础环境参考。
# 不要按此文件推断当前线上容器启动参数、uploads 挂载或健康检查。
version: '3.8'
services:

View File

@ -1,335 +0,0 @@
# AI 生图平台桶缓存落地对接清单
> 交付对象:AI 生图平台负责人
> 当前主接口域名:`https://sc.jiansun.vip`
> 当前资源主域名:`https://s3.jiansun.vip`
> 历史旧资源域名:`https://s3.yydd.ai`(不再作为默认入口)
> 文档时间:`2026-04-29`
> 当前状态:`180.97.215.57` 读缓存入口已停用,本文保留对接口径和历史方案说明。
## 1. 这次要做的事情
原计划把新入口服务器 `180.97.215.57` 上独立数据盘中的 `180G` 空间,用作 AI 生图平台相关资源的**读取缓存**。该入口现在已经停用,不再作为正式资源入口或联调目标。
当前目标调整为:
- 对外资源域名继续统一使用 `https://s3.jiansun.vip`
- 展示型图片继续优先写入 `/gongxiangpan/public-display/`
- 排障和联调优先检查 `180.97.221.140 -> FRP -> 192.168.58.20:9000` 链路
## 2. 这次不会变的东西
下面这些默认都**不变**:
- 桶本体位置不变,真实存储仍在生产机 `192.168.58.20:9000`
- AI 生图平台的接口入口仍然是 `https://sc.jiansun.vip`
- 对外资源主域名仍然是 `https://s3.jiansun.vip`
- 历史旧资源域名 `https://s3.yydd.ai` 不再作为默认对接地址
- 不迁桶,上传链路不因 `180.97.215.57` 停用而变化
一句话说清楚:
**`180.97.215.57` 读缓存入口已经停用,但桶没有搬走,正式资源域名仍然是 `s3.jiansun.vip`。**
## 3. 当前已经确认的事实
当前资源入口链路是:
```text
s3.jiansun.vip
-> 180.97.221.140
-> nginx
-> 127.0.0.1:2323
-> FRP
-> 192.168.58.20:9000
```
历史缓存盘配置曾经是:
- 缓存目录:`/data/nginx-cache/s3`
- 缓存上限:`180g`
- 旧入口机资源盘:`/data`,总容量约 `200G`
- 当前不再把这层缓存作为正式链路门禁
`2026-03-18` 实测真实桶链路时,使用了一个约 `145MB` 的真实桶对象做下载测试:
- 直回源 `127.0.0.1:29000`:约 `23.2 MB/s`
- 走 `https://s3.jiansun.vip` 第一次请求:`X-Cache-Status: MISS`,约 `22.7 MB/s`
- 同一个 URL 第二次请求:`X-Cache-Status: HIT`,约 `63.2 MB/s`
这些历史结果只说明旧缓存方案曾经可用:
- 首次访问主要受回源链路影响
- 命中缓存后,速度会明显提升
基于当前入口调整,需要明确 3 个结论:
- `180.97.215.57` 读缓存方案已经停用
- `s3.jiansun.vip` 仍是唯一正式资源域名
- 后续验证重点是 `180.97.221.140` 入口、FRP 和 MinIO 回源,而不是旧缓存命中率
## 4. 我还需要 AI 生图平台这边提供什么
为了让 AI 生图平台稳定使用正式资源链路,还需要下面这些信息。
### 4.1 需要确认的资源范围
请提供:
- AI 生图平台当前实际会读取的资源类型
- 这些资源对应的 URL 样例
- 最常访问的路径前缀
最好直接给:
- 近 3 天最常访问的 20 个资源 URL 样例
- 主要路径前缀清单
例如是否主要集中在:
- 生图输入参考图
- 生图结果图
- 放大图、变体图、蒙版图、压缩包等固定路径前缀
### 4.2 需要确认是否只使用一个主资源域名
这个点非常关键。
请确认:
- AI 生图平台后续是否统一只读 `https://s3.jiansun.vip`
- 历史旧资源域名 `https://s3.yydd.ai` 是否已从日常默认调用中完全移除
推荐口径:
- 主用:`https://s3.jiansun.vip`
- 历史旧域名:`https://s3.yydd.ai`(仅用于识别历史链接)
如果旧域名和新域名长期同时对外使用,那么**同一份内容就可能同时出现两个公网 URL**。
### 4.3 需要确认文件是否“同 key 覆盖”
这个点决定缓存能不能放心加大。
请确认:
- 同一张图后续会不会“覆盖原来的对象 key”
- 还是每次生成新图都使用新文件名 / 新路径
最推荐的方式是:
- **新内容永远用新 key**
比如:
- `image-20260318-001.png`
- `image-20260318-002.png`
不推荐的方式是:
- 总是写回同一个 URL
比如始终覆盖:
- `latest.png`
原因很简单:
- 如果同一个 URL 对应的文件内容被覆盖了,缓存里可能暂时还是旧版本
- 这样就会出现“URL 没变,但内容短时间还是旧图”的情况
### 4.4 需要确认时效要求
请确认:
- 图片更新后,允许多久内出现旧图
- 删除后,是否允许短时间还能访问
- 是否存在“必须秒级生效”的路径
如果某些资源要求非常高的实时性,就不能和普通静态资源用完全同一套缓存策略。
### 4.5 需要确认流量规模
请提供一个大致量级即可,不需要特别精确。
需要知道:
- 峰值并发大概多少
- 每分钟 / 每小时大概多少请求
- 热门图片是否会被大量重复读取
- 单张图通常多大
- 是否有明显的突发高峰时段
### 4.6 需要一批联调样本
请提供:
- 5 到 10 个真实可访问的 AI 生图平台资源 URL
- 最好覆盖“结果图 / 参考图 / 较大文件”几类
这样我可以直接按真实资源做灰度验证,而不是拿不相关对象代替。
### 4.7 这次不需要 AI 生图平台配合改的内容
为了避免沟通误差,这里也明确一下当前**不要求**你们先改的东西:
- 不要求先改接口域名,接口仍然走 `https://sc.jiansun.vip`
- 不要求先迁桶,桶本体仍然在 `192.168.58.20:9000`
- 不要求先切到 `s3.yydd.ai`
- 不要求先改上传链路
这次优先要拿到的是“真实读流量信息”,不是让业务先大改。
## 5. 对正常使用有没有影响
正常情况下,**对用户是无感的**。
前提是满足下面两个条件:
- 对外主资源域名统一使用 `https://s3.jiansun.vip`
- 资源尽量使用“新内容新 key”的方式,不要反复覆盖同一个 URL
只要做到这两点,链路效果就是:
- 统一从 `s3.jiansun.vip` 进入
- 通过当前正式入口回源到生产 MinIO
- 用户看到的还是同一个公网 URL
## 6. 会不会出现“同一张图两个 URL”
### 情况 1:旧缓存本身
**不会,但该方案已经停用。**
缓存不会凭空再生成一个给用户使用的新 URL。
用户访问的仍然是原来的公网地址,比如:
```text
https://s3.jiansun.vip/xxx.png
```
旧缓存只是入口服务器内部把这个 URL 对应的内容存到磁盘里。
也就是说:
- 对用户看到的 URL 不变
- 对程序调用的 URL 不变
- 只是旧入口机内部多了一份缓存文件
### 情况 2:两个域名同时都在用
**会。**
如果下面两个地址同时长期对外使用:
- `https://s3.jiansun.vip/xxx.png`
- 历史旧链接:`https://s3.yydd.ai/xxx.png`
那么从“公网 URL”角度看,就是两条 URL 指向了同一份内容。
所以当前建议非常明确:
- `s3.jiansun.vip` 主用
- `s3.yydd.ai` 不再作为默认入口
不要两个一起作为默认入口使用。
## 7. 会不会出现“缓存里一张图,桶里又是另一个 URL”
从用户角度看,**不会多出一个可见 URL**。
但有一个需要特别注意的现实问题:
### 如果同一个 URL 被反复覆盖内容
那么就可能出现:
- 桶里已经是新图
- 缓存里短时间还是旧图
这时候不是“两个 URL”,而是:
- **同一个 URL,在不同时间点可能先返回旧内容,再返回新内容**
这也是为什么我强烈建议:
- 新图使用新 key
- 不要反复覆盖同一个对象地址
如果业务上确实必须覆盖同一个 URL,那就必须额外做:
- 清缓存机制
- 或单独降低这类路径的缓存时间
## 8. 推荐的最终口径
建议 AI 生图平台后续按下面这套规则执行:
### 域名规则
- 正式资源域名:`https://s3.jiansun.vip`
- 历史旧域名:`https://s3.yydd.ai`(仅识别,不再默认使用)
### URL 规则
- 对外只认主域名,不混用两个资源域名
- 新内容尽量使用新 key,不覆盖老 key
### 链路规则
- 把 AI 生图平台的热点读流量优先收敛到 `s3.jiansun.vip`
- 当前正式入口为 `180.97.221.140`
- `180.97.215.57` 不再作为正式资源入口或缓存入口
## 9. 现在就可以推进的下一步
如果 AI 生图平台负责人能补齐下面 4 项,我就可以继续往下收口:
1. 确认主用资源域名是否统一为 `s3.jiansun.vip`
2. 提供 AI 生图平台真实会读取的路径前缀或 URL 样例
3. 明确是否存在“同 key 覆盖”
4. 提供一组真实联调 URL 用于灰度测试
拿到这些信息后,我这边会继续做:
1. 按真实热点路径确认是否需要“分路径缓存策略”
2. 区分哪些资源适合长缓存,哪些资源要短缓存
3. 如果存在“同 key 覆盖”,评估是否需要补清缓存机制
4. 用真实 URL 做灰度验证,再决定是否正式放量
## 10. 建议负责人直接按下面模板回复
为了节省来回沟通时间,建议 AI 生图平台负责人直接按这个格式回:
```text
1. 主用资源域名:
例如:统一使用 https://s3.jiansun.vip
2. 历史旧域名是否已停止默认调用:
例如:是,`https://s3.yydd.ai` 不再作为默认入口
3. 主要读取的资源类型:
例如:生图结果图、参考图、放大图、变体图、压缩包
4. 主要路径前缀或 URL 样例:
例如:/gongxiangpan/ai-output/... 或 /gongxiangpan/ai-input/...
5. 是否存在同 key 覆盖:
例如:有 / 没有
6. 时效要求:
例如:更新后允许 5 分钟内旧图;某些路径要求秒级生效
7. 流量规模:
例如:高峰每分钟约多少次读取,热门图片是否会被重复访问
8. 联调样本:
例如:提供 5 到 10 个真实可访问 URL
```
---
如果只要一句最短版本,可以直接转给对方:
**这次不是迁桶;`180.97.215.57` 读缓存入口已停用,正式资源域名仍然是 `s3.jiansun.vip`,当前应通过 `180.97.221.140` 入口回源到 `192.168.58.20:9000`。要继续推进,需要 AI 生图平台确认主用域名、真实热点路径、是否会覆盖同一 URL、以及给一组联调样本。**

View File

@ -1,8 +1,8 @@
# AI 生图平台第一层桶缓存落地方案
> 更新时间:2026-04-29
> 更新时间:2026-05-07
> 当前正式资源域名:`https://s3.jiansun.vip`
> 当前正式入口机:`180.97.221.140`
> 当前正式入口机:`103.118.245.6`
> 已停用缓存入口机:`180.97.215.57`
> 当前桶本体:`192.168.58.20:9000`
@ -21,7 +21,7 @@ AI 生图平台不再沿用“整个 `/uploads/` 都进长缓存”的口径,
```text
AI 生图平台
-> https://s3.jiansun.vip/gongxiangpan/public-display/...
-> 180.97.221.140
-> 103.118.245.6
-> nginx
-> 127.0.0.1:2323
-> FRP
@ -32,6 +32,7 @@ AI 生图平台
### 2.2 关于旧缓存入口
- `180.97.215.57` 已停用,不再作为 `s3.jiansun.vip` 的正式链路。
- `180.97.221.140` 是历史入口,不再作为当前默认排障入口。
- 旧的 `180G` nginx 磁盘缓存方案只保留历史记录,不再作为联调或排障门禁。
## 3. 为什么改成新前缀
@ -68,8 +69,10 @@ AI 生图平台
- 展示图对象优先写入 `/gongxiangpan/public-display/`
- `uploads` 保留为历史混合区,不作为默认展示资源路径
- 源文件 / ZIP / PSD / 视频源等下载型对象继续落在生产机 uploads:`/mnt/PoolOne/apps/youyidian-3d/uploads`
- `s3.1080p.hk/jsxiangche` 只保留极少数老素材兼容,不作为新素材或新预览写入目标
- 当前不再以 `X-Cache-Status` 作为正式验证标准
- 当前优先验证 `s3.jiansun.vip -> 180.97.221.140 -> 192.168.58.20:9000` 是否可达
- 当前优先验证 `s3.jiansun.vip -> 103.118.245.6 -> 192.168.58.20:9000` 是否可达
## 6. 历史验证结果
@ -120,4 +123,4 @@ AI 生图平台后续如果要对接正式展示图,统一按下面这个口
AI 生图平台现在的第一层桶缓存口径是:
**新生成的展示型图片统一写到 `s3.jiansun.vip` 的 `/gongxiangpan/public-display/`,当前正式入口收敛到 `180.97.221.140`;`180.97.215.57` 缓存入口已停用,历史 `/uploads/` 继续可访问,但不再作为默认展示路径。**
**新生成的展示型图片统一写到 `s3.jiansun.vip` 的 `/gongxiangpan/public-display/`,当前正式入口收敛到 `103.118.245.6`;`180.97.215.57` 缓存入口已停用,历史 `/uploads/` 继续可访问,但不再作为默认展示路径。**

View File

@ -1,6 +1,6 @@
# AI 生图平台素材接口文档
> 用途:AI 生图平台统一通过同一条接口,查询 `标准模型白底图` 或 `任务-详情整套预览下载链接`
> 用途:AI 生图平台统一通过同一条接口,查询 `标准模型白底图` 或 `任务-详情整套预览直链`
> 当前主域名:`https://sc.jiansun.vip`
> 当前资源主域名:`https://s3.jiansun.vip`
> 历史旧资源域名:`https://s3.yydd.ai`(仅用于识别历史链接,不再作为对接口径)
@ -18,8 +18,9 @@
### 1.1 当前网络口径
- 接口查询、鉴权和下载 token 入口统一使用 `https://sc.jiansun.vip`
- `white-background` 分支里返回的 `previewUrl`,当前默认也是 `https://sc.jiansun.vip/uploads/...`
- `white-background` 分支里返回的 `previewUrl`,新预览默认走 `https://s3.jiansun.vip/gongxiangpan/public-display/white-background/...`
- `task-detail` 分支返回的预览图直链,当前默认资源域名使用 `https://s3.jiansun.vip`
- `/uploads/...` 只作为源文件下载、内网访问或少量历史预览兼容路径,不作为新预览默认路径
- 历史旧资源域名 `https://s3.yydd.ai` 不再作为默认对接口径
返回统一格式:
@ -137,7 +138,7 @@ X-API-Key: your-api-key
"label": "左前45°",
"fileName": "TEXT001452-旗舰店-左前45°.webp",
"fileSize": 123456,
"previewUrl": "https://sc.jiansun.vip/uploads/skus/484bd047-c956-48cf-a0f4-401f696aef8d/white-background/sku-id-1/preview/TEXT001452-%E6%97%97%E8%88%B0%E5%BA%97-%E5%B7%A6%E5%89%8D45%C2%B0.webp",
"previewUrl": "https://s3.jiansun.vip/gongxiangpan/public-display/white-background/484bd047-c956-48cf-a0f4-401f696aef8d/sku-id-1/TEXT001452-leftFront45.webp",
"downloadUrl": "https://sc.jiansun.vip/api/external/download?token=xxx"
},
{
@ -145,7 +146,7 @@ X-API-Key: your-api-key
"label": "右前45°",
"fileName": "TEXT001452-旗舰店-右前45°.webp",
"fileSize": 123456,
"previewUrl": "https://sc.jiansun.vip/uploads/skus/484bd047-c956-48cf-a0f4-401f696aef8d/white-background/sku-id-1/preview/TEXT001452-%E6%97%97%E8%88%B0%E5%BA%97-%E5%8F%B3%E5%89%8D45%C2%B0.webp",
"previewUrl": "https://s3.jiansun.vip/gongxiangpan/public-display/white-background/484bd047-c956-48cf-a0f4-401f696aef8d/sku-id-1/TEXT001452-rightFront45.webp",
"downloadUrl": "https://sc.jiansun.vip/api/external/download?token=yyy"
}
],
@ -155,7 +156,7 @@ X-API-Key: your-api-key
"label": "其他图片1",
"fileName": "01-detail-1.webp",
"fileSize": 123456,
"previewUrl": "https://sc.jiansun.vip/uploads/skus/484bd047-c956-48cf-a0f4-401f696aef8d/white-background/sku-id-1/preview/other/01-detail-1.webp",
"previewUrl": "https://s3.jiansun.vip/gongxiangpan/public-display/white-background/484bd047-c956-48cf-a0f4-401f696aef8d/sku-id-1/other/01-detail-1.webp",
"downloadUrl": "https://sc.jiansun.vip/api/external/download?token=zzz"
}
]
@ -192,7 +193,7 @@ X-API-Key: your-api-key
- `previewUrl` 是预览专用小图,优先给页面展示或 AI 平台取图使用。
- `downloadUrl` 是原图下载链接,当前仍走 token 下载接口。
- 对于后续新上传白底图,正式存储和下载格式统一为 `image/webp`,并保留透明通道。
- 历史白底图如果还没有派生预览图,`previewUrl` 会临时回退到原图路径。
- 历史白底图如果还没有派生预览图,`previewUrl` 可能临时回退到原图路径或 `/uploads/...` 兼容路径。
固定角度顺序固定为:
@ -246,7 +247,7 @@ X-API-Key: your-api-key
"hasWhiteBackground": true,
"whiteBackgroundCount": 2,
"previewImage": {
"previewUrl": "https://sc.jiansun.vip/uploads/skus/484bd047-c956-48cf-a0f4-401f696aef8d/white-background/sku-id-1/preview/TEXT001452-%E6%97%97%E8%88%B0%E5%BA%97-%E5%8F%B3%E5%89%8D45%C2%B0.webp",
"previewUrl": "https://s3.jiansun.vip/gongxiangpan/public-display/white-background/484bd047-c956-48cf-a0f4-401f696aef8d/sku-id-1/TEXT001452-rightFront45.webp",
"downloadUrl": "https://sc.jiansun.vip/api/external/download?token=xxx",
"fileName": "TEXT001452-旗舰店-右前45°.webp",
"fileSize": 123456
@ -348,18 +349,18 @@ X-API-Key: your-api-key
"isFullSet": true,
"downloadUrls": {
"main": [
"https://s3.jiansun.vip/gongxiangpan/preview/main-1.jpg"
"https://s3.jiansun.vip/gongxiangpan/public-display/task-detail/material-2/main/main-1.webp"
],
"sku": [
"https://s3.jiansun.vip/gongxiangpan/preview/sku-1.jpg"
"https://s3.jiansun.vip/gongxiangpan/public-display/task-detail/material-2/sku/sku-1.webp"
],
"detail": [
"https://s3.jiansun.vip/gongxiangpan/preview/detail-1.jpg"
"https://s3.jiansun.vip/gongxiangpan/public-display/task-detail/material-2/detail/detail-1.webp"
],
"all": [
"https://s3.jiansun.vip/gongxiangpan/preview/main-1.jpg",
"https://s3.jiansun.vip/gongxiangpan/preview/sku-1.jpg",
"https://s3.jiansun.vip/gongxiangpan/preview/detail-1.jpg"
"https://s3.jiansun.vip/gongxiangpan/public-display/task-detail/material-2/main/main-1.webp",
"https://s3.jiansun.vip/gongxiangpan/public-display/task-detail/material-2/sku/sku-1.webp",
"https://s3.jiansun.vip/gongxiangpan/public-display/task-detail/material-2/detail/detail-1.webp"
]
}
}
@ -410,18 +411,18 @@ X-API-Key: your-api-key
"isFullSet": true,
"downloadUrls": {
"main": [
"https://s3.jiansun.vip/gongxiangpan/preview/main-1.jpg"
"https://s3.jiansun.vip/gongxiangpan/public-display/task-detail/material-2/main/main-1.webp"
],
"sku": [
"https://s3.jiansun.vip/gongxiangpan/preview/sku-1.jpg"
"https://s3.jiansun.vip/gongxiangpan/public-display/task-detail/material-2/sku/sku-1.webp"
],
"detail": [
"https://s3.jiansun.vip/gongxiangpan/preview/detail-1.jpg"
"https://s3.jiansun.vip/gongxiangpan/public-display/task-detail/material-2/detail/detail-1.webp"
],
"all": [
"https://s3.jiansun.vip/gongxiangpan/preview/main-1.jpg",
"https://s3.jiansun.vip/gongxiangpan/preview/sku-1.jpg",
"https://s3.jiansun.vip/gongxiangpan/preview/detail-1.jpg"
"https://s3.jiansun.vip/gongxiangpan/public-display/task-detail/material-2/main/main-1.webp",
"https://s3.jiansun.vip/gongxiangpan/public-display/task-detail/material-2/sku/sku-1.webp",
"https://s3.jiansun.vip/gongxiangpan/public-display/task-detail/material-2/detail/detail-1.webp"
]
}
}
@ -500,6 +501,7 @@ GET https://sc.jiansun.vip/api/external/download?token=xxx
补充说明:
- 当前默认回包中的预览图资源域名为 `https://s3.jiansun.vip`
- 新任务详情预览路径默认位于 `/gongxiangpan/public-display/task-detail/...`
- 历史旧资源域名 `https://s3.yydd.ai` 不再作为当前默认对接地址
## 6. 错误码

View File

@ -32,6 +32,18 @@
- 正式容器:`youyidian-3d-material`
- 正式端口:`4320`
## 首次运行前准备
本脚本由维护人员执行,不要求普通用户手动运行。首次在一台新维护机上运行前,需要确认:
- 本机可用 PowerShell 5.1+。
- 本机已安装 PuTTY 工具,`plink.exe` 和 `pscp.exe` 可在 PATH 中找到,或位于 `C:\Program Files\PuTTY\`。
- 本机能访问构建机 `192.168.42.134`。
- 构建机能访问 Git 仓库和生产机 `192.168.58.20`。
- 构建机本地 Registry 端口 `5000` 可用。
- 已通过环境变量或参数提供构建机密码:`BUILD_SERVER_PASSWORD` 或 `-BuildServerPassword`。
- 私有 Git 仓库认证通过 `GIT_DEPLOY_USERNAME` / `GIT_DEPLOY_PASSWORD`,或 `GIT_AUTH_HEADER` 提供;不要把密码写进仓库 URL。
## 安全边界
- 不从本机上传源码压缩包。

View File

@ -161,8 +161,8 @@ X-API-Key: your-api-key-here
"main": [
{
"id": "asset-uuid-1",
"url": "https://s3.jiansun.vip/...",
"fileName": "主图1.jpg",
"url": "uploads/materials/YYD-001_张三_天猫旗舰店_main.zip",
"fileName": "YYD-001_张三_天猫旗舰店_main.zip",
"fileSize": 1024000,
"sortOrder": 0
}
@ -170,8 +170,8 @@ X-API-Key: your-api-key-here
"sku": [
{
"id": "asset-uuid-2",
"url": "https://s3.jiansun.vip/...",
"fileName": "SKU白色.jpg",
"url": "uploads/materials/YYD-001_张三_天猫旗舰店_sku.zip",
"fileName": "YYD-001_张三_天猫旗舰店_sku.zip",
"fileSize": 512000,
"sortOrder": 1
}
@ -179,8 +179,8 @@ X-API-Key: your-api-key-here
"detail": [
{
"id": "asset-uuid-3",
"url": "https://s3.jiansun.vip/...",
"fileName": "详情1.jpg",
"url": "uploads/materials/YYD-001_张三_天猫旗舰店_detail.zip",
"fileName": "YYD-001_张三_天猫旗舰店_detail.zip",
"fileSize": 2048000,
"sortOrder": 0
}
@ -188,7 +188,7 @@ X-API-Key: your-api-key-here
"psd": [
{
"id": "asset-uuid-5",
"url": "uploads/product-package/xxx/psd/psd-xxx.psd",
"url": "uploads/materials/psd-source.psd",
"fileName": "源文件1.psd",
"fileSize": 52428800,
"sortOrder": 0
@ -249,7 +249,7 @@ X-API-Key: your-api-key-here
| sceneName | string | 场景类别;`任务-其他` 固定返回 `通用` |
| isFullSet | boolean | 是否整套;当前按素材类型和资产情况自动判定 |
| status | string | 状态:completed/fetched |
| assets | object | **仅 `任务-详情` 类型**:资产分类对象(包含 `main`、`sku`、`detail`、`psd`) |
| assets | object | **仅 `任务-详情` 类型**:分类 ZIP / PSD 资产对象(包含 `main`、`sku`、`detail`、`psd`) |
| files | array | **仅非 `任务-详情` 类型**:文件列表(统一结构) |
| downloadUrl | string | **仅 `任务-详情`**:兼容字段,值与 `downloadUrls.all` 相同 |
| downloadUrls.all | string | **所有类型**:全部文件下载链接 |
@ -341,12 +341,14 @@ curl -X GET \
**资产对象字段说明(任务-详情)**
`/api/external/product-assets` 返回的 `assets.main` / `assets.sku` / `assets.detail` 是分类 ZIP 资产,不是单张预览图列表。`assets.psd` 是 PSD 源文件资产。任务-详情预览直链请使用 `/api/external/standard-model-white-background?resourceType=task-detail` 获取。
每个资产对象包含以下字段:
| 字段 | 类型 | 说明 |
|-----|------|------|
| id | string | 资产唯一标识符 |
| url | string | 资源URL(S3地址或本地路径,仅用于预览) |
| url | string | 分类 ZIP / PSD 的存储路径,外部下载优先使用 `downloadUrls.all` |
| fileName | string | 文件名 |
| fileSize | number | 文件大小(字节) |
| sortOrder | number | 排序序号(主图 / SKU 图 / 详情图 / PSD 均会显式返回) |
@ -518,7 +520,7 @@ X-API-Key: your-api-key-here
供 AI 生图平台统一通过同一条接口查询:
- `标准模型白底图`
- `任务-详情整套预览下载链接`
- `任务-详情整套预览直链`
主接口:
@ -529,8 +531,9 @@ GET /api/external/standard-model-white-background
当前网络口径:
- 接口查询与下载 token 统一走 `https://sc.jiansun.vip`
- `white-background` 分支的 `previewUrl` 默认走 `https://sc.jiansun.vip/uploads/...`
- `white-background` 分支的 `previewUrl` 新预览默认走 `https://s3.jiansun.vip/gongxiangpan/public-display/white-background/...`
- `task-detail` 分支里返回的预览图直链,当前默认资源域名是 `https://s3.jiansun.vip`
- `/uploads/...` 只作为源文件下载、内网访问或少量历史预览兼容路径,不作为新预览默认路径
- 历史旧资源域名 `https://s3.yydd.ai` 不再作为对接口径
**请求头**
@ -843,7 +846,7 @@ AI 生图平台的详细字段说明、完整响应示例、错误码和 cURL
### 打包下载说明
- `任务-详情` 的 `downloadUrls.all` 包含:主图 + SKU 图 + 详情图(**不含 PSD**)
- `任务-详情` 的 `downloadUrls.all` 包含:主图分类 ZIP + SKU 分类 ZIP + 详情分类 ZIP(**不含 PSD**)
- 当前 `任务-详情` 接口不再返回 `downloadUrls.psd`
- 当前 `任务-详情` 接口也不再给每个 asset 返回单独 `downloadUrl`
- ZIP 文件内按文件夹分类:`main/`、`sku/`、`detail/`

View File

@ -1,199 +1,144 @@
# 生产存储桶接入信息
适用范围:本文记录当前线上 `MinIO(对象存储)` / `S3(对象存储兼容接口)` 的已确认接入信息,供其它项目接入同一个生产存储桶时参考。
> 更新时间:2026-05-07
> 本文记录当前素材平台的 MinIO / S3 / uploads 存储事实。
最后核对时间:`2026-04-14`
核对方式:生产服务器只读核对 + 生产应用容器环境变量核对 + 宿主机挂载路径核对
## 1. 当前结论
## 1. 已确认的生产接入信息
- 新素材预览 / 展示文件:写入生产 MinIO 新桶路径,公开 URL 统一为 `https://s3.jiansun.vip/gongxiangpan/public-display/...`。
- 源文件 / 下载型文件:写入生产服务器 uploads 目录:`/mnt/PoolOne/apps/youyidian-3d/uploads`。
- 旧 `1080p.hk` 桶:只用于极少数老素材兼容,不作为新素材写入目标。
- `/gongxiangpan/uploads/` 是历史混合区,不再作为新展示资源的默认路径。
### 1.1 存储服务
## 2. 生产 MinIO 信息
- 存储服务:`MinIO(对象存储服务)`
- 存储服务:`MinIO`
- 生产服务器:`192.168.58.20`
- API 端口:`9000`
- 控制台端口:`9001`
- 线上应用容器:`youyidian-3d-material`
- 存储服务容器:`minio`
- 线上应用容器:`youyidian-3d-material`
### 1.2 S3 兼容接入参数
- `S3_ENDPOINT(对象存储接口地址)`:`http://192.168.58.20:9000`
- `S3_BUCKET(桶名)`:`gongxiangpan`
- `S3_REGION(区域)`:`us-east-1`
- `S3_ENABLE_PATH_STYLE(路径风格访问)`:`1`
- `S3_SET_ACL(上传后写 public-read)`:`1`
- `S3_PREVIEW_URL_EXPIRE_IN(预览链接过期秒数)`:`7200`
### 1.3 对外访问域名
- `S3_PUBLIC_DOMAIN(公开访问域名)`:`https://s3.jiansun.vip/gongxiangpan`
当前项目最终对象 URL(访问地址)规则:
```text
https://s3.jiansun.vip/gongxiangpan/<前缀><文件名>
```
例如当前项目使用的前缀是:
- `S3_FILE_PATH(对象前缀)`:`由壹点AI/`
所以当前项目上传出来的对象通常长这样:
```text
https://s3.jiansun.vip/gongxiangpan/由壹点AI/<文件名>
```
## 2. 宿主机真实挂载路径
### 2.1 MinIO 数据目录
- 宿主机数据路径:`/mnt/tank/apps/minio/data`
- 容器内路径:`/data`
### 2.2 MinIO 配置目录
- 宿主机配置路径:`/mnt/tank/apps/minio/config`
- 容器内路径:`/root/.minio`
### 2.3 当前项目本地上传目录
这个不是存储桶,但很多视频源文件会先落这里:
- 宿主机路径:`/mnt/PoolOne/apps/youyidian-3d/uploads`
- 容器内路径:`/app/uploads`
## 3. 新项目接入时建议怎么用
### 3.1 可以共用什么
新项目如果要共用这一个生产桶,可以共用这些固定信息:
- `S3_ENDPOINT`
- `S3_BUCKET`
- `S3_REGION`
- `S3_PUBLIC_DOMAIN`
- `path-style(路径风格)`
### 3.2 不建议直接共用什么
下面两项不建议直接原样复用:
- 当前项目的 `S3_FILE_PATH(对象前缀)`
- 当前项目正在使用的长期访问密钥
原因很直接:
1. 如果多个项目共用同一个前缀,文件名一旦撞上,后面排查和清理会很乱。
2. 如果多个项目长期共用同一套生产密钥,任何一个项目泄露,整个生产桶都会一起暴露。
### 3.3 推荐接法
新项目建议这样配:
应用实际接入参数:
```env
S3_ENDPOINT=http://192.168.58.20:9000
S3_BUCKET=gongxiangpan
S3_REGION=us-east-1
S3_ENABLE_PATH_STYLE=1
S3_PUBLIC_DOMAIN=https://s3.jiansun.vip/gongxiangpan
S3_FILE_PATH=<新项目自己的前缀>/
```
例如:
代码固定行为:
```env
S3_FILE_PATH=新项目名/
- S3 客户端使用 path-style 访问,代码里固定 `forcePathStyle: true`。
- 上传对象默认设置 `public-read` ACL,代码里固定处理。
- `S3_PUBLIC_DOMAIN` 用来拼出公开访问地址。
下面这些不是当前代码必须读取的部署变量,不要再作为新项目接入必填项传播:
- `S3_ENABLE_PATH_STYLE`
- `S3_SET_ACL`
- `S3_PREVIEW_URL_EXPIRE_IN`
## 3. 对象路径规则
### 3.1 新预览 / 展示资源
正式路径:
```text
https://s3.jiansun.vip/gongxiangpan/public-display/...
```
或者:
常见前缀:
```env
S3_FILE_PATH=业务线A/新项目名/
```text
public-display/materials/...
public-display/task-detail/...
public-display/white-background/...
public-display/certificate-report/...
```
## 4. 密钥处理建议
这些资源用于页面展示、AI 平台预览、钉钉返回地址和外部接口展示直链。
风险等级:`高`
### 3.2 默认 S3 业务前缀
当前生产环境里确实存在可用的 `Access Key(访问密钥)` / `Secret Key(私钥)`,但本文不直接记录明文。
生产环境仍可能存在默认业务前缀:
不建议:
```text
由壹点AI/
```
- 直接把 `MinIO Root(管理员根账号)` 给业务项目使用
- 让多个项目长期共用同一套生产访问密钥
它主要用于旧逻辑或非 `public-display` 的对象,不代表新素材展示图默认落点。
建议:
## 4. uploads 目录规则
1. 给每个新项目单独发一套对象存储访问密钥
2. 至少给每个项目分独立前缀
3. 如果条件允许,进一步按项目拆权限策略
生产 uploads 目录:
## 5. 如果必须核对线上实际配置
```text
/mnt/PoolOne/apps/youyidian-3d/uploads
```
### 5.1 看当前应用实际在用的 S3 配置
容器内路径:
```text
/app/uploads
```
当前会落在这里的内容包括:
- 素材源文件
- ZIP / PSD / PDF 等下载型文件
- 视频源文件
- 音频源文件
- 分片上传合并后的最终文件
- 上传过程中的临时文件
对外访问时,代码会根据场景返回:
- 内网:可能返回 `/uploads/...` 或内网 MinIO / 生产机地址。
- 外网:通常通过 `/api/uploads/...` 或带 token 的下载接口流式返回。
不要把 uploads 理解成“少量视频临时区”。它是当前生产源文件和下载型文件的核心落点。
## 5. 历史兼容
### 5.1 旧 `1080p.hk` 桶
`s3.1080p.hk/jsxiangche` 只保留给极少数老素材兼容。新素材、批量新录入素材、后续上传素材都不应继续写入旧桶。
### 5.2 旧资源域名
`s3.yydd.ai` 不再作为默认对接口径,只用于识别历史链接。
### 5.3 历史混合区
`/gongxiangpan/uploads/` 保留历史兼容,不再作为新展示图默认路径。
## 6. 只读核对命令
核对应用环境变量:
```powershell
ssh root@192.168.58.20 "docker exec youyidian-3d-material sh -lc 'env'"
ssh root@192.168.58.20 "docker exec youyidian-3d-material sh -lc 'env | grep -E \"^(S3_|UPLOAD_DIR|APP_PUBLIC_URL|NEXTAUTH_URL)\"'"
```
然后重点看:
- `S3_ENDPOINT`
- `S3_BUCKET`
- `S3_REGION`
- `S3_PUBLIC_DOMAIN`
- `S3_FILE_PATH`
- `S3_ACCESS_KEY_ID`
- `S3_SECRET_ACCESS_KEY`
### 5.2 看 MinIO 挂载
核对 MinIO 挂载:
```powershell
ssh root@192.168.58.20 "docker inspect minio --format '{{json .Mounts}}'"
```
### 5.3 看 MinIO 本机配置文件
核对 uploads 目录:
```powershell
ssh root@192.168.58.20 "sed -n '1,200p' /mnt/tank/apps/minio/config/minio.env"
ssh root@192.168.58.20 "ls -la /mnt/PoolOne/apps/youyidian-3d/uploads | head -20"
```
## 6. 当前结论
核对资源域名解析:
- 生产对象存储实例可以复用
- 生产桶名已经确认是 `gongxiangpan`
- 公开访问域名已经确认是 `https://s3.jiansun.vip/gongxiangpan`
- 当前项目对象前缀是 `由壹点AI/`
- 其它项目如果接入,建议“共用桶,不共用前缀,不共用长期密钥”
```powershell
Resolve-DnsName s3.jiansun.vip
```
## 7. 另一个项目临时接入说明
已确认另一个项目使用下面这套固定配置:
- `S3_ENDPOINT=http://192.168.58.20:9000`
- `S3_BUCKET=gongxiangpan`
- `S3_REGION=us-east-1`
- `S3_ENABLE_PATH_STYLE=1`
- `S3_PUBLIC_DOMAIN=https://s3.jiansun.vip/gongxiangpan`
- `S3_FILE_PATH=3danzhuang/furniture-3d-system/`
风险等级:`高`
说明:
- 另一个项目当前要求的是“先能接上生产桶”
- 目前本项目已经掌握现有生产项目使用中的对象存储访问密钥
- 但把明文密钥直接写进仓库文档不安全,后续极易被提交扩散
因此实际可用的临时接入环境变量,已单独写入本地忽略文件:
- [`.env.furniture-3d-system.local`](/D:/AI%20Code/gongxiangp2/.env.furniture-3d-system.local)
这份文件可直接给另一个项目参考或落到新服务器。
不建议长期这样复用当前生产项目密钥。更稳的最终方案仍然是:
1. 给另一个项目单独创建一套 `Access Key / Secret Key(访问密钥 / 密钥)`
2. 继续保留它自己的对象前缀 `3danzhuang/furniture-3d-system/`
当前资源域名应解析到正式入口 `103.118.245.6`,再经入口机 nginx / FRP 回源到 `192.168.58.20:9000`。

View File

@ -1,13 +1,15 @@
# 素材库平台预览缓存方案
> 更新时间:2026-04-29
> 更新时间:2026-05-07
> 状态:`180.97.215.57` 缓存入口已停用,本文保留历史方案和当前展示资源口径。
## 1. 当前口径
- 资源入口:`s3.jiansun.vip -> 180.97.221.140`
- 资源入口:`s3.jiansun.vip -> 103.118.245.6`
- 桶本体:`192.168.58.20:9000`
- 已停用缓存入口:`180.97.215.57`
- 历史入口:`180.97.221.140` 不再作为当前默认排障入口
- 旧桶兼容:`s3.1080p.hk/jsxiangche` 只保留给极少数老素材,不作为新素材写入目标
- 旧缓存目录:`/data/nginx-cache/s3`,不再作为当前正式链路门禁
- 旧缓存上限:`180g`,只作为历史记录保留
@ -47,12 +49,13 @@
- 视频大文件
- 压缩包
- 源文件下载
- 生产机 uploads 源文件目录:`/mnt/PoolOne/apps/youyidian-3d/uploads`
## 4. 历史缓存策略
- 下面规则属于 `180.97.215.57` 缓存方案的历史记录。
- 当前正式链路不再用 `X-Cache-Status` 作为排障门禁。
- 当前优先验证 `s3.jiansun.vip -> 180.97.221.140 -> 192.168.58.20:9000` 是否可达。
- 当前优先验证 `s3.jiansun.vip -> 103.118.245.6 -> 192.168.58.20:9000` 是否可达。
## 5. 历史验证结果
@ -82,4 +85,4 @@
- 以后只要是“页面上要展示给人看”的成品图,就优先写 `public-display`
- 只要是“下载型、源文件型、大对象”,就不要硬塞进展示资源口径
- 对外说明时,统一写 `s3.jiansun.vip`,不要再把 `s3.yydd.ai` 当正式入口
- 排障时不要再默认检查 `180.97.215.57`,先检查 `180.97.221.140` 入口和 FRP 回源链路
- 排障时不要再默认检查 `180.97.215.57` 或 `180.97.221.140`,先检查 `103.118.245.6` 入口和 FRP 回源链路

View File

@ -1,6 +1,6 @@
# 统一流量访问规则
> 更新时间:2026-04-29
> 更新时间:2026-05-07
> 目标:统一正式入口、展示资源口径和内外网分工,减少历史残留带来的分流与误判。
## 1. 统一原则
@ -9,6 +9,8 @@
- 服务端访问 MinIO 继续走内网 `192.168.58.20:9000`。
- 展示型图片统一写入 `/gongxiangpan/public-display/`。
- `/gongxiangpan/uploads/` 保留为混合区,不再默认承担展示资源职责。
- 源文件 / 下载型文件默认落在生产机 uploads:`/mnt/PoolOne/apps/youyidian-3d/uploads`。
- `1080p.hk` 旧桶只用于极少数老素材兼容,不作为新素材写入目标。
- `180.97.215.57` 已停用,不再作为正式资源入口或缓存入口。
## 2. 正式入口
@ -17,6 +19,7 @@
| --- | --- | --- |
| 主站 | `https://sc.jiansun.vip` | 唯一正式主站入口 |
| 资源 / S3 | `https://s3.jiansun.vip` | 唯一正式资源入口 |
| 当前公网入口机 | `103.118.245.6` | 主站和资源域名当前都解析到这台入口机 |
| 内网生产机 | `192.168.58.20` | 只给服务端和内网运维链路使用 |
## 3. 访问规则
@ -37,6 +40,7 @@
- 正式访问口径同样优先走正式域名
- 只有诊断、运维或服务端内部访问,才直接使用内网地址
- 媒体资源返回值会按代码做内外网改写;内网请求仍可能拿到 `SERVER_IP:MINIO_API_PORT`、`/uploads/...` 或 `/api/uploads/...` 这类内网/本机链路。
## 4. 写入规则
@ -46,7 +50,7 @@
| 任务-详情预览 | `/gongxiangpan/public-display/task-detail/...` | 是 |
| 通用预览 / 展示图 | `/gongxiangpan/public-display/materials/...` | 是 |
| 历史混合对象 | `/gongxiangpan/uploads/...` | 否,默认不按展示缓存处理 |
| 压缩包 / 视频源文件 / 大下载对象 | 原业务路径 | 否 |
| 压缩包 / 视频源文件 / 大下载对象 | 生产机 uploads 目录 | 否 |
## 5. 展示资源规则
@ -99,5 +103,5 @@
1. 是主站慢,还是资源图慢。
2. 如果是资源图慢,先看链接是不是 `s3.jiansun.vip`。
3. 再看路径是不是 `/gongxiangpan/public-display/`。
4. 再看 `s3.jiansun.vip` 是否解析到当前正式入口 `180.97.221.140`。
5. 最后再看 `180.97.221.140 -> 127.0.0.1:2323 -> 192.168.58.20:9000` 回源链路。
4. 再看 `s3.jiansun.vip` 是否解析到当前正式入口 `103.118.245.6`。
5. 最后再看 `103.118.245.6 -> 127.0.0.1:2323 -> 192.168.58.20:9000` 回源链路。

View File

@ -1,255 +1,181 @@
# 部署脚本安全说明
## ⚠️ 重要提示
> 更新时间:2026-05-07
> 当前默认部署脚本:`scripts/git-incremental-deploy.ps1`
所有部署脚本都经过严格设计,确保**绝对不会**删除或修改生产服务器上的任何文件和目录。
## 1. 当前结论
---
当前 Git 增量部署脚本只负责同步源码、构建镜像、推送镜像、替换应用容器和清理 Docker 无用资源。它不直接改业务数据,不直接操作数据库,不直接操作 MinIO 桶,也不清理生产 uploads 目录。
## 🔒 安全保证
普通上传、下载、转码等业务写入仍由应用运行时完成,不属于部署脚本的文件操作。
### 1. uploads 目录保护
## 2. uploads 目录边界
**绝对不会**对 `/mnt/PoolOne/apps/youyidian-3d/uploads` 目录进行任何修改操作:
生产 uploads 目录:
- ✅ **只挂载,不修改**:`-v /mnt/PoolOne/apps/youyidian-3d/uploads:/app/uploads`
- ✅ **容器内只读访问**:容器只能读取 uploads 目录中的文件
- ✅ **不执行任何删除命令**:脚本中没有 `rm`、`rmdir`、`delete` 等针对 uploads 的操作
```text
/mnt/PoolOne/apps/youyidian-3d/uploads
```
### 2. 数据库保护
容器挂载:
**绝对不会**对数据库进行任何操作:
- ❌ **不执行任何 SQL 命令**:没有 `psql`、`DROP`、`DELETE`、`UPDATE`、`INSERT`、`TRUNCATE`
- ❌ **不操作数据库容器**:不会 `docker stop`、`docker rm`、`docker exec` 数据库容器
- ❌ **不执行数据库迁移**:没有 `prisma migrate`、`prisma db push`
- ✅ **只通过环境变量连接**:应用容器通过 `DATABASE_URL` 连接数据库(只读/写业务数据)
**数据库容器**:
- `gongxiangpan-postgres-1`(PostgreSQL)- **不会被操作**
- 数据库文件:`/var/lib/postgresql/data`(在容器内)- **不会被访问**
### 3. 存储桶保护
**绝对不会**对 MinIO 存储桶进行任何操作:
- ❌ **不执行任何 S3 命令**:没有 `aws s3`、`mc`(MinIO Client)
- ❌ **不操作 MinIO 容器**:不会 `docker stop`、`docker rm`、`docker exec` MinIO 容器
- ❌ **不删除任何对象**:没有 `s3 rm`、`mc rm`
- ✅ **只通过环境变量连接**:应用容器通过 `S3_ENDPOINT`、`S3_BUCKET` 连接存储桶(只读/写业务数据)
**MinIO 容器**:
- `minio`(MinIO 存储服务)- **不会被操作**
- 存储桶:`gongxiangpan`(Bucket)- **不会被操作**
- 存储路径:`由壹点AI/`(前缀)- **不会被操作**
### 2. 临时文件管理
所有临时文件都存放在 `/tmp/` 目录:
- 镜像 tar 文件:`/tmp/youyidian-3d-material-latest.tar`
- 部署完成后自动清理
### 3. Docker 资源清理
只清理 Docker 相关资源,不涉及文件系统:
- ✅ 清理旧镜像(`docker image prune`)
- ✅ 清理未使用的容器(`docker container prune`)
- ✅ 清理未使用的网络(`docker network prune`)
- ✅ 清理构建缓存(`docker builder prune`)
- ❌ **不清理任何文件目录**
---
## 📋 脚本安全检查清单
### setup-build-server.ps1(配置构建机)
**操作范围**:只在构建机(192.168.42.134)上操作
| 操作 | 说明 | 安全性 |
|------|------|--------|
| 安装 Docker Compose | 下载到 `/usr/local/bin/docker-compose` | ✅ 安全 |
| 创建 Registry 目录 | `/opt/docker-registry` | ✅ 安全 |
| 启动 Registry 容器 | Docker 容器,不涉及文件系统 | ✅ 安全 |
**不涉及生产服务器**:此脚本只配置构建机,不会连接生产服务器。
---
### setup-production-server.ps1(配置生产服务器)
**操作范围**:只修改 Docker 配置,不涉及任何文件目录
| 操作 | 说明 | 安全性 |
|------|------|--------|
| 备份 Docker 配置 | `/etc/docker/daemon.json` → `/etc/docker/daemon.json.backup.*` | ✅ 安全 |
| 修改 Docker 配置 | 添加 `insecure-registries` 配置 | ✅ 安全 |
| 重启 Docker 服务 | `systemctl restart docker` | ✅ 安全(容器自动重启)|
| 验证配置 | 测试连接 Registry | ✅ 安全 |
**不涉及文件系统**:
- ❌ 不修改 uploads 目录
- ❌ 不修改任何应用文件
- ❌ 不删除任何数据
**重启影响**:
- Docker 服务重启时间:约 5-10 秒
- 所有容器自动重启(`restart: always`)
- 服务短暂中断后自动恢复
---
### deploy-build-server.ps1(部署脚本)
**操作范围**:构建镜像 → 传输镜像 → 启动容器
| 步骤 | 操作 | 涉及目录 | 安全性 |
|------|------|----------|--------|
| 1. 同步代码 | 打包代码上传到构建机 | `/opt/youyidian-3d-build` | ✅ 安全(构建机) |
| 2. 构建镜像 | 在构建机上构建 Docker 镜像 | Docker 镜像层 | ✅ 安全 |
| 3. 推送镜像 | 推送到构建机 Registry | `/opt/docker-registry/data` | ✅ 安全(构建机) |
| 4. 拉取镜像 | 生产服务器从 Registry 拉取 | Docker 镜像层 | ✅ 安全 |
| 5. 启动容器 | 停止旧容器,启动新容器 | 无 | ✅ 安全 |
| 6. 清理资源 | 清理 Docker 资源 | 无 | ✅ 安全 |
**uploads 目录处理**:
```bash
-v /mnt/PoolOne/apps/youyidian-3d/uploads:/app/uploads
```
- ✅ **只挂载**:将宿主机目录挂载到容器内
- ✅ **只读访问**:容器只能读取文件,不能修改
- ✅ **不执行任何删除操作**
**临时文件**:
- 构建机:`/opt/youyidian-3d-build/*`(代码临时目录)
- 生产服务器:`/tmp/youyidian-3d-material-latest.tar`(镜像临时文件)
- 部署完成后自动清理
部署脚本的边界:
**清理操作**:
- ✅ 清理 Docker 镜像(`docker image prune`)
- ✅ 清理 Docker 容器(`docker container prune`)
- ✅ 清理 Docker 网络(`docker network prune`)
- ✅ 清理构建缓存(`docker builder prune`)
- ❌ **不清理任何文件目录**
- ❌ **不清理 uploads 目录**
- ❌ **不清理应用数据**
- 只检查该目录是否存在。
- 只把该目录挂载进应用容器。
- 不对该目录执行 `rm`、`mv`、`cp`、`find -delete` 或清空操作。
- 不改变该目录内已有源文件、视频文件、压缩包和上传临时数据。
---
需要注意:容器内应用对 `/app/uploads` 不是只读挂载。上传、下载、转码和清理临时上传会按业务逻辑读写这个目录,这和部署脚本本身的操作边界是两件事。
## 🔍 与现有脚本对比
## 3. 数据库边界
### deploy-local-build.ps1(现有脚本)
部署脚本不会执行下面操作:
```powershell
# 启动容器命令
docker run -d --name $containerName \
-p $port:3000 \
-v /mnt/PoolOne/apps/youyidian-3d/uploads:/app/uploads \
--env-file /mnt/PoolOne/apps/youyidian-3d/$envFile \
-e DISABLE_CACHE=true \
-e DEPLOYMENT_VERSION=$deploymentVersion \
-e DOWNLOAD_TOKEN_SECRET=$downloadTokenSecret \
$ImageName:$ImageTag
- 不执行 SQL。
- 不运行 Prisma migrate / db push。
- 不停止、不删除 PostgreSQL 容器。
- 不进入数据库容器执行命令。
部署后应用容器会通过生产 `.env` 中的 `DATABASE_URL` 正常连接数据库,这是业务运行必需行为。
## 4. MinIO / S3 边界
部署脚本不会执行下面操作:
- 不执行 `aws s3` 或 `mc` 命令。
- 不停止、不删除 MinIO 容器。
- 不删除桶对象。
- 不迁移桶数据。
部署后应用容器会通过 `S3_ENDPOINT`、`S3_BUCKET`、`S3_PUBLIC_DOMAIN` 等环境变量正常访问对象存储,这是业务运行必需行为。
当前素材存储口径:
- 新素材预览 / 展示文件:写入新 MinIO 桶,公开 URL 统一为 `https://s3.jiansun.vip/gongxiangpan/public-display/...`。
- 旧 `1080p.hk` 桶:只用于极少数老素材兼容,不作为新素材写入目标。
- 源文件 / 下载型文件:落在生产机 uploads 目录,即 `/mnt/PoolOne/apps/youyidian-3d/uploads`。
## 5. 构建机操作范围
构建机:`192.168.42.134`
Git 增量部署脚本会在构建机上使用下面目录作为 Git 工作区:
```text
/root/youyidian-3d-git-build
```
### deploy-build-server.ps1(新脚本)
脚本会在这个 Git 工作区内执行:
- `git fetch --prune`
- `git reset --hard`
- `git clean -fdx`
- `docker build`
- `docker push`
这些清理只针对构建机上的 Git 工作区和 Docker 虚拟资源,不涉及生产机 uploads、数据库或 MinIO 数据目录。
## 6. 生产机操作范围
生产机:`192.168.58.20`
脚本会在生产机上执行:
- 拉取构建机 Registry 中的应用镜像。
- 停止并删除旧应用容器。
- 用新镜像启动新应用容器。
- 调用 `http://127.0.0.1:4320/api/health` 或影子环境健康检查。
- 健康检查失败时,默认尝试用旧镜像回滚应用容器。
脚本只操作应用容器:
- 正式:`youyidian-3d-material`
- 影子:`youyidian-3d-shadow`
不会操作:
- PostgreSQL 容器
- Redis 容器
- MinIO 容器
- FRP 容器或服务
- uploads 目录内容
## 7. 清理策略
默认清理内容:
- 构建机悬空镜像。
- 生产机 6 小时前的旧镜像。
- 生产机 6 小时前的未使用容器。
默认不会清理:
- uploads 目录。
- MinIO 数据目录。
- PostgreSQL 数据目录。
- Docker volume。
- 当前运行中的应用容器和镜像。
- Docker build cache。
只有显式传入 `-AggressiveBuildCacheCleanup` 时,脚本才会额外清理较旧构建缓存。这个选项仍然只作用于 Docker 构建缓存,不作用于业务文件。
## 8. DryRun 安全性
DryRun 命令:
```powershell
# 启动容器命令(完全相同)
docker run -d --name $containerName \
-p $port:3000 \
-v /mnt/PoolOne/apps/youyidian-3d/uploads:/app/uploads \
--env-file /mnt/PoolOne/apps/youyidian-3d/$envFile \
-e DISABLE_CACHE=true \
-e DEPLOYMENT_VERSION=$deploymentVersion \
-e DOWNLOAD_TOKEN_SECRET=$downloadTokenSecret \
$ImageName:$ImageTag
.\scripts\git-incremental-deploy.ps1 -DryRun
```
**结论**:新脚本的容器启动命令与现有脚本**完全相同**,安全性一致。
DryRun 只验证:
---
- 构建机 SSH 可达。
- 构建机能读取 Git 仓库。
- 构建机能连接生产机。
- 构建机 Registry 可用。
## ✅ 安全承诺
DryRun 不会:
### 绝对不会操作的资源
- 同步代码到构建目录。
- 构建镜像。
- 推送镜像。
- 停止或重启生产容器。
- 修改业务文件。
1. ❌ **uploads 目录**:`/mnt/PoolOne/apps/youyidian-3d/uploads`
- 不删除
- 不修改
- 不移动
- 只挂载(只读访问)
## 9. 旧脚本说明
2. ❌ **数据库**:
- 不执行任何 SQL 命令
- 不操作数据库容器(`gongxiangpan-postgres-1`)
- 不执行数据库迁移
- 只通过应用连接(业务读写)
旧版 `deploy-remote-build.ps1`、`deploy-local-build.ps1`、`deploy-public-build.ps1` 这类 `deploy-*.ps1` 属于本地历史脚本名,受 `.gitignore` 保护,不作为仓库交付入口。它们对应的“本机打包源码上传到构建机”、本地构建、公网穿透构建等链路,只作为识别历史资料使用。历史文档中提到的镜像 tar 临时文件、`deploy-build-server.ps1`、`deploy-local-build.ps1` 对比口径,不再代表当前默认部署链路。
3. ❌ **存储桶**:
- 不执行任何 S3/MinIO 命令
- 不操作 MinIO 容器(`minio`)
- 不删除任何对象
- 只通过应用连接(业务读写)
当前默认链路不再把镜像 tar 文件复制到生产机,而是由生产机直接从构建机 Registry 拉取提交标签镜像。
4. ❌ **其他容器**:
- 不操作 Redis 容器(`youyidian-3d-redis-shadow`)
- 不操作 frp 容器(`frpc-minio`)
- 只操作应用容器(`youyidian-3d-material`、`youyidian-3d-shadow`)
## 10. 出问题时怎么查
### 只操作的资源
查看应用日志:
1. ✅ **Docker 镜像**:清理旧镜像(虚拟资源)
2. ✅ **Docker 容器**:停止/启动应用容器
3. ✅ **Docker 网络**:清理未使用的网络(虚拟资源)
4. ✅ **Docker 缓存**:清理构建缓存(虚拟资源)
5. ✅ **临时文件**:`/tmp/` 目录中的镜像 tar 文件
```bash
ssh root@192.168.58.20 'docker logs --tail 120 youyidian-3d-material'
```
### 操作日志
检查健康接口:
所有操作都有详细的日志输出,可以追溯每一步操作。
```bash
ssh root@192.168.58.20 'curl -fsS http://127.0.0.1:4320/api/health'
```
---
检查 uploads 目录是否还在:
## 🚨 如果出现问题
```bash
ssh root@192.168.58.20 'ls -la /mnt/PoolOne/apps/youyidian-3d/uploads | head -20'
```
### 回滚方案
检查 Docker 镜像和容器:
1. **容器启动失败**:
```bash
# 查看日志
ssh root@192.168.58.20 'docker logs youyidian-3d-material'
# 重启容器
ssh root@192.168.58.20 'docker restart youyidian-3d-material'
```
2. **Docker 配置问题**:
```bash
# 恢复备份
ssh root@192.168.58.20 'cp /etc/docker/daemon.json.backup.* /etc/docker/daemon.json'
ssh root@192.168.58.20 'systemctl restart docker'
```
3. **uploads 目录检查**:
```bash
# 检查目录是否存在
ssh root@192.168.58.20 'ls -la /mnt/PoolOne/apps/youyidian-3d/uploads | head -20'
# 检查文件数量
ssh root@192.168.58.20 'find /mnt/PoolOne/apps/youyidian-3d/uploads -type f | wc -l'
```
---
## 📞 联系方式
如有任何安全疑问,请立即停止操作并联系开发团队。
---
**文档版本**:v1.0
**更新时间**:2025-01-17
**维护人员**:开发团队
```bash
ssh root@192.168.58.20 'docker ps --format "table {{.Names}}\t{{.Image}}\t{{.Status}}"'
ssh root@192.168.58.20 'docker images | grep youyidian-3d-material'
```

View File

@ -1,221 +1,200 @@
# 部署规范
## ⚠️ 重要声明
> 更新时间:2026-05-07
> 当前默认部署方式:`scripts/git-incremental-deploy.ps1`
**本项目强制使用 `deploy-remote-build.ps1` 脚本进行部署,除非项目负责人明确要求使用其他方式。**
## 1. 当前标准部署方式
---
本项目当前以 **Git 增量部署** 作为默认发布链路。源码先推送到 Git 仓库,构建机只从 Git 拉取增量代码并构建镜像,生产服务器只拉取镜像并替换应用容器。
## 标准部署方式
### `deploy-remote-build.ps1` ⚡ **唯一推荐**
**专用构建机部署方案** - 保护本地 SSD,提高构建效率
**架构**:
```
你的电脑(内网)
↓ SSH (192.168.42.134)
↓ 传输代码(1-5MB)
↓
构建机 192.168.42.134
├─ Docker Registry (端口 5000)
├─ Registry UI (端口 5001)
└─ 构建镜像(~180MB)
↓
↓ 内网传输镜像
↓
生产服务器 192.168.58.20
└─ 部署容器
```text
本机提交并推送源码到 Git
-> 构建机 git fetch / reset 到指定提交
-> 构建机 docker build,复用 Docker / Next 缓存
-> 构建机推送 production-<commit> 和 production-latest 镜像
-> 生产机拉取指定提交镜像
-> 停旧应用容器,启动新应用容器
-> 检查 /api/health,失败时默认尝试回滚旧镜像
```
**核心优势**:
1. **保护本地 SSD**:构建过程在远程服务器,不占用本地磁盘
2. **构建速度快**:Ubuntu 服务器性能强,构建速度比 Windows Docker Desktop 快
3. **可视化管理**:Web UI 查看所有镜像版本(http://192.168.42.134:5001)
4. **自动清理**:保持构建机干净整洁
5. **内网高速**:镜像传输走内网,速度快(1Gbps)
**使用方法**:
```powershell
# 在项目根目录执行
# 部署到正式系统(端口 4320)
.\deploy-remote-build.ps1
# 部署到影子系统(端口 5320)
.\deploy-remote-build.ps1 -Target shadow
# 跳过代码同步(代码已在构建机上)
.\deploy-remote-build.ps1 -SkipSync
# 跳过镜像构建(镜像已存在)
.\deploy-remote-build.ps1 -SkipBuild
# 不使用构建缓存(彻底重新构建)
.\deploy-remote-build.ps1 -NoCache
# 保留旧镜像(不自动清理)
.\deploy-remote-build.ps1 -KeepOldImages
```
**参数说明**:
- `-Target`: 部署目标,`production`(正式系统,端口4320)或 `shadow`(影子系统,端口5320)
- `-BuildServerIP`: 构建机 IP(默认:192.168.42.134)
- `-ProdServerIP`: 生产服务器 IP(默认:192.168.58.20)
- `-SkipSync`: 跳过代码同步
- `-SkipBuild`: 跳过镜像构建
- `-NoCache`: 不使用构建缓存
- `-KeepOldImages`: 保留旧镜像
**时间消耗**(每次部署):
- 代码打包:5-10秒
- 代码上传:10-30秒(1-5MB)
- 镜像构建:1-3分钟(首次更长)
- 镜像推送到 Registry:10-20秒
- 镜像拉取到生产服务器:30-60秒(180MB,内网 1Gbps)
- 容器启动:10-20秒
- **总计**:2-5分钟/次
---
## 🚫 其他部署方式(不推荐)
以下部署方式仅供参考,**不推荐使用**,除非项目负责人明确要求:
### `deploy-local-build.ps1` ❌
**本地构建部署** - 会在本地产生大量 Docker 缓存,磨损 SSD
**问题**:
- 本地构建占用大量磁盘空间(~2GB)
- SSD 磨损严重(频繁读写)
- 构建速度慢(Windows Docker Desktop 性能差)
- 上传镜像慢(~180MB 通过网络)
### `deploy-public-build.ps1` ⚠️
**公网穿透部署** - 仅在特殊情况下使用(外网访问生产环境)
**用途**:从公网访问生产环境进行部署(非常规操作)
### `deploy-git-build.ps1` ⚠️
**Git 工作流部署** - 需要配置 Git 仓库,复杂度高
**问题**:
- 需要配置 Git 仓库
- 流程复杂(本地提交 → 推送 → 服务器拉取 → 构建)
- 不适合快速迭代
---
## Registry UI 使用
浏览器访问:`http://192.168.42.134:5001`
**功能**:
- 查看镜像列表
- 查看镜像标签(版本)
- 查看镜像详情(大小、创建时间、层信息)
- 删除不需要的镜像
**镜像命名规范**:
```
youyidian-3d-material:latest # 最新版本
youyidian-3d-material:20260126-1430 # 带时间戳的版本
youyidian-3d-material:v1.0.0 # 带版本号的版本
```
---
## 清理策略
### 自动清理(脚本默认行为)
1. **构建机**:
- 清理 6 小时前的构建缓存
- 清理悬空镜像(dangling images)
- 保留最近使用的基础镜像
2. **生产服务器**:
- 清理 6 小时前的旧镜像
- 清理未使用的容器
- 保留当前运行的容器和镜像
### 手动清理
```bash
# SSH 登录构建机
ssh root@192.168.42.134
# 清理所有未使用的资源(谨慎使用)
docker system prune -af --volumes
# 清理构建缓存(保留 5GB)
docker builder prune -af --filter "until=24h"
# 清理悬空镜像
docker image prune -f
# 查看磁盘使用情况
docker system df
df -h
```
---
## 故障排查
### 构建机连接失败
脚本路径:
```powershell
# 测试网络连接
ping 192.168.42.134
# 测试 SSH 连接
ssh root@192.168.42.134
.\scripts\git-incremental-deploy.ps1
```
### Registry 无法访问
默认参数:
- Git 仓库:`http://git.1080p.hk/lunanting/gongxiangp2.git`
- 分支:`main`
- 构建机:`192.168.42.134`
- 生产机:`192.168.58.20`
- 正式容器:`youyidian-3d-material`
- 正式端口:`4320`
- 影子容器:`youyidian-3d-shadow`
- 影子端口:`5320`
正式部署由维护人员执行,不要求普通用户手动运行。
## 2. 常用命令
### 2.1 运行前准备
维护机需要具备:
- PowerShell 5.1+。
- PuTTY 工具:`plink.exe`、`pscp.exe`。
- 构建机 SSH 凭据,通过 `BUILD_SERVER_PASSWORD` 或 `-BuildServerPassword` 提供。
- Git 仓库认证,通过 `GIT_DEPLOY_USERNAME` / `GIT_DEPLOY_PASSWORD` 或 `GIT_AUTH_HEADER` 提供。
不要把 Git 密码写进仓库 URL,也不要把临时部署密码写入文档或提交到仓库。
### 2.2 部署命令
正式部署前先做 DryRun:
```powershell
.\scripts\git-incremental-deploy.ps1 -DryRun
```
部署正式环境:
```powershell
.\scripts\git-incremental-deploy.ps1
```
部署影子环境:
```powershell
.\scripts\git-incremental-deploy.ps1 -Target shadow
```
部署指定提交:
```powershell
.\scripts\git-incremental-deploy.ps1 -Commit <commit-sha>
```
必要时彻底重新构建:
```powershell
.\scripts\git-incremental-deploy.ps1 -NoCache
```
## 3. 参数说明
- `-Target`:部署目标,`production` 或 `shadow`。
- `-RepoUrl`:Git 仓库地址,默认使用 `lunanting/gongxiangp2`。
- `-Branch`:部署分支,默认 `main`。
- `-Commit`:指定提交;为空时部署目标分支最新提交。
- `-DryRun`:只做连通性与依赖预检,不构建、不推镜像、不重启容器。
- `-NoCache`:Docker 构建不使用缓存。
- `-KeepOldImages`:跳过自动 Docker 镜像清理。
- `-AggressiveBuildCacheCleanup`:额外清理较旧构建缓存;默认不启用。
- `-SkipBuild` / `-SkipPush` / `-SkipDeploy`:只用于排障,不作为常规发布方式。
## 4. 安全边界
- 不从本机上传源码压缩包。
- 不在生产机上执行 `npm install` 或 `next build`。
- 不修改生产机 `.env` 文件。
- 不删除、不移动、不覆盖生产机 uploads 目录内容。
- 不直接操作 PostgreSQL、Redis 或 MinIO 容器。
- 生产机只替换应用容器,并保留 `/mnt/PoolOne/apps/youyidian-3d/uploads:/app/uploads` 挂载。
- 新容器健康检查失败时,脚本默认用旧镜像回滚应用容器。
详细安全说明见:[部署脚本安全说明](./部署脚本安全说明.md)。
## 5. 镜像标签
每次部署生成确定提交标签和移动标签:
```text
production-<commit> # 正式环境确定版本
production-latest # 正式环境最新版本
shadow-<commit> # 影子环境确定版本
shadow-latest # 影子环境最新版本
```
Registry UI:
```text
http://192.168.42.134:5001
```
## 6. 清理策略
脚本默认只清理 Docker 虚拟资源,不清理业务文件目录。
构建机:
- 清理悬空镜像。
- 默认保留 Docker build cache,保证增量构建速度。
- 只有传入 `-AggressiveBuildCacheCleanup` 时,才额外清理较旧构建缓存。
生产服务器:
- 清理 6 小时前的旧镜像。
- 清理 6 小时前的未使用容器。
- 保留当前运行的应用容器、uploads 挂载目录、数据库和 MinIO 数据。
## 7. 历史脚本口径
### 本地历史脚本名
`deploy-remote-build.ps1`、`deploy-local-build.ps1`、`deploy-public-build.ps1` 这类 `deploy-*.ps1` 属于本地历史脚本名,受 `.gitignore` 保护,不作为仓库交付入口。
它们对应的是旧的“本机打包代码并上传到构建机”或“本地 / 公网穿透构建”链路,只能用于识别历史资料,不再是当前默认方式。
旧链路:
```text
本地电脑打包源码
-> SSH 上传源码压缩包到构建机
-> 构建机构建镜像
-> 推送 Registry
-> 生产机拉取镜像并替换容器
```
### `deploy-git-build.ps1`
历史 Git 部署脚本已被 `scripts/git-incremental-deploy.ps1` 替代。后续涉及 Git 增量部署时,只看新脚本和 [Git 增量部署方案](./Git增量部署方案.md)。
## 8. 故障排查顺序
### DryRun 失败
优先检查:
- 构建机 SSH 是否可达。
- 构建机能否读取 Git 仓库。
- 构建机能否 SSH 到生产服务器。
- 构建机本地 Registry 是否可用。
### Git 同步失败
优先检查:
- Git 仓库地址是否正确。
- Git 凭据是否正确。
- `main` 分支是否已经推送最新代码。
### 生产容器启动失败
优先检查:
```bash
# 检查 Registry 状态
ssh root@192.168.42.134
cd /opt/docker-registry
docker-compose ps
# 查看 Registry 日志
docker-compose logs -f registry
# 重启 Registry
docker-compose restart
ssh root@192.168.58.20 'docker logs --tail 120 youyidian-3d-material'
ssh root@192.168.58.20 'curl -fsS http://127.0.0.1:4320/api/health'
```
### 镜像推送失败
### uploads 目录检查
```bash
# 检查 Registry 配置
docker info | grep Registry
# 手动推送测试
docker tag youyidian-3d-material:latest localhost:5000/youyidian-3d-material:latest
docker push localhost:5000/youyidian-3d-material:latest
ssh root@192.168.58.20 'ls -la /mnt/PoolOne/apps/youyidian-3d/uploads | head -20'
```
### 生产服务器拉取失败
## 9. 版本历史
```bash
# 检查 insecure-registry 配置
ssh root@192.168.58.20
docker info | grep -A 5 "Insecure Registries"
# 测试连接
curl http://192.168.42.134:5000/v2/_catalog
# 手动拉取测试
docker pull 192.168.42.134:5000/youyidian-3d-material:latest
```
---
## 版本历史
- **v1.0** (2026-01-26): 初始版本,强制使用 `deploy-remote-build.ps1` 部署
- **v2.0** (2026-05-07): 默认部署方式切换为 `scripts/git-incremental-deploy.ps1`。
- **v1.0** (2026-01-26): 旧版远程构建机脚本规范,现已降级为应急回退资料。

View File

@ -2,13 +2,13 @@
## 1. 对接目标
钉钉流程填写以下 5 个字段后,调用素材库接口判断是否有符合条件的素材:
钉钉流程调用素材库接口判断是否有符合条件的素材。当前生产接口只要求 `SPU` 必填,其它筛选字段都是选填。
- 需求类型
- 品牌
- 场景类别
- 产品类别
- SPU
- SPU(必填)
- 需求类型(选填)
- 品牌(选填)
- 场景类别(选填)
- 产品类别(选填)
返回规则:
@ -71,7 +71,7 @@ X-API-Key: 5073534d7a59babbc45397a8eac5943b
钉钉负责人配置时要注意:
- `apiKey` 是固定参数,不要做成表单字段
- 普通用户只需要填写或选择:需求类型、品牌、场景类别、产品类别、SPU
- 普通用户只需要填写 `SPU`;需求类型、品牌、场景类别、产品类别可作为选填筛选条件
- 如果钉钉侧有“连接器公共请求头”或“固定请求头”配置,也可以把 `X-API-Key` 配在那里
- 如果没有固定请求头能力,就直接使用本文档里的 `?apiKey=...` 地址
@ -137,7 +137,7 @@ X-API-Key: 5073534d7a59babbc45397a8eac5943b
### 5.1 用途
用户选择完 5 个字段后,调用这个接口判断有没有素材。
用户填写 `SPU` 后即可调用这个接口判断有没有素材。其它字段如果传入,会作为附加筛选条件;不传则只按 SPU 查询。
### 5.2 请求信息
@ -158,16 +158,16 @@ X-API-Key: 5073534d7a59babbc45397a8eac5943b
请求参数:
- `apiKey`:固定鉴权密钥,由钉钉负责人固定配置,不给普通用户填写
- `requirementType`:需求类型
- `brand`:品牌(实际是店铺)
- `sceneCategory`:场景类别
- `productCategory`:产品类别
- `spu`:SPU
- `spu`:SPU,必填
- `requirementType`:需求类型,选填
- `brand`:品牌(实际是店铺),选填
- `sceneCategory`:场景类别,选填
- `productCategory`:产品类别,选填
### 5.3 请求示例
```http
GET https://sc.jiansun.vip/api/external/dingtalk-material-match?apiKey=5073534d7a59babbc45397a8eac5943b&requirementType=任务-详情&brand=顾家家居&sceneCategory=客厅&productCategory=沙发&spu=SPU001
GET https://sc.jiansun.vip/api/external/dingtalk-material-match?apiKey=5073534d7a59babbc45397a8eac5943b&spu=SPU001
```
---
@ -187,7 +187,7 @@ GET https://sc.jiansun.vip/api/external/dingtalk-material-match?apiKey=5073534d7
"brand": "顾家家居",
"sceneCategory": "客厅",
"productCategory": "沙发",
"address": "https://sc.jiansun.vip/uploads/previews/spu001.jpg",
"address": "https://s3.jiansun.vip/gongxiangpan/public-display/materials/preview-spu001.webp",
"materialId": "material-1",
"updatedAt": "2026-04-30T08:00:00.000Z"
}
@ -228,10 +228,10 @@ GET https://sc.jiansun.vip/api/external/dingtalk-material-match?apiKey=5073534d7
推荐按以下顺序接入:
1. 先调用“下拉选项接口”
2. 用返回结果生成钉钉表单下拉
3. 用户选择完 5 个字段后
4. 再调用“素材匹配接口”
1. 钉钉流程固定配置 `apiKey`
2. 用户填写必填 `SPU`
3. 如需缩小范围,再选择需求类型、品牌、场景类别、产品类别
4. 调用“素材匹配接口”
5. 根据 `matched` 和 `address` 做后续流程判断
---
@ -239,7 +239,9 @@ GET https://sc.jiansun.vip/api/external/dingtalk-material-match?apiKey=5073534d7
## 8. 注意事项
- `SPU` 为必填
- 其余字段建议全部走下拉,不要手输
- 其余字段均为选填;如果要使用,建议走下拉,不要手输
- 新素材预览地址默认是 `https://s3.jiansun.vip/gongxiangpan/public-display/...`
- 只有少量历史兼容数据才可能返回 `/uploads/...` 或旧资源域名
- “品牌”不是独立品牌库字段,实际对应店铺
- “需求类型”不是自由文本,实际对应素材类型
- 如果接口返回 `INVALID_API_KEY`,说明 `apiKey` 参数或 `X-API-Key` 请求头没有正确配置

View File

@ -1,148 +1,104 @@
# 脚本说明
## ⚠️ 重要声明
> 更新时间:2026-05-07
> 部署相关脚本以 `scripts/git-incremental-deploy.ps1` 为当前默认口径。
**本项目强制使用 `deploy-remote-build.ps1` 脚本进行部署**,除非项目负责人明确要求使用其他方式。
详细说明:
**详细说明**:[部署规范](../docs/部署规范.md)
- [部署规范](../docs/部署规范.md)
- [Git 增量部署方案](../docs/Git增量部署方案.md)
- [部署脚本安全说明](../docs/部署脚本安全说明.md)
---
## 1. 核心部署脚本
## 核心部署脚本
### `scripts/git-incremental-deploy.ps1` ⭐⭐⭐ 当前推荐
### `scripts/git-incremental-deploy.ps1` ⭐⭐⭐ **Git 增量部署**
**新推荐方向**:以 Git 仓库为唯一源码入口,构建机增量拉取源码并构建镜像,生产服务器只拉取镜像并替换容器。
以 Git 仓库为唯一源码入口,构建机增量拉取源码并构建镜像,生产服务器只拉取镜像并替换容器。
特点:
- 不从本机上传源码压缩包
- 构建机执行 `git fetch/reset`
- Docker 构建继续复用缓存
- 镜像按提交打标签,便于回滚
- 生产机不构建源码,只拉镜像运行
- 默认失败时尝试回滚旧镜像
- 不从本机上传源码压缩包。
- 构建机执行 `git fetch/reset`。
- Docker 构建继续复用缓存。
- 镜像按提交打标签,便于回滚。
- 生产机不构建源码,只拉镜像运行。
- 默认失败时尝试回滚旧镜像。
- 默认清理 Docker 无用镜像/容器,保留构建缓存。
运行前准备:
- 本机可用 PowerShell 5.1+。
- 本机已安装 PuTTY,`plink.exe` 和 `pscp.exe` 可用。
- 构建机密码通过 `BUILD_SERVER_PASSWORD` 或 `-BuildServerPassword` 提供。
- Git 认证通过 `GIT_DEPLOY_USERNAME` / `GIT_DEPLOY_PASSWORD` 或 `GIT_AUTH_HEADER` 提供。
预检:
```powershell
.\scripts\git-incremental-deploy.ps1 -DryRun
```
正式环境:
```powershell
.\scripts\git-incremental-deploy.ps1
```
影子环境:
```powershell
.\scripts\git-incremental-deploy.ps1 -Target shadow
```
部署指定提交:
```powershell
.\scripts\git-incremental-deploy.ps1 -Commit <commit-sha>
```
正式部署由维护人员执行,不要求普通用户手动运行。
### `deploy-remote-build.ps1` ⭐⭐⭐ **唯一推荐**
**专用构建机部署脚本** - 保护本地 SSD,提高构建效率
## 2. 历史部署脚本
**文件位置**:项目根目录(`deploy-remote-build.ps1`)
### `deploy-*.ps1` 本地历史脚本名
**架构流程**:
```
本地电脑 → SSH 上传代码(1-5MB) → 构建机(192.168.42.134) → 构建镜像 → 推送到Registry → 生产服务器拉取 → 启动容器
```
根目录 `deploy-*.ps1` 属于本地历史脚本名,受 `.gitignore` 保护,不作为仓库交付入口。
**核心优势**:
1. 保护本地 SSD(构建在远程)
2. 构建速度快(Linux 原生)
3. 可视化管理(Registry UI: http://192.168.42.134:5001)
4. 镜像版本管理
5. 内网高速传输(1Gbps)
曾经出现过的旧链路包括:
**使用方式:**
```powershell
# 在项目根目录执行
# 部署到正式系统(端口 4320)
.\deploy-remote-build.ps1
- `deploy-remote-build.ps1`:本机打包源码并上传到构建机。
- `deploy-local-build.ps1`:本地构建部署。
- `deploy-public-build.ps1`:公网穿透部署。
# 部署到影子系统(端口 5320)
.\deploy-remote-build.ps1 -Target shadow
这些名字只用于识别历史资料,不再作为当前推荐方式。
# 跳过代码同步
.\deploy-remote-build.ps1 -SkipSync
### `deploy-git-build.ps1` ⚠️ 已替代
# 跳过镜像构建
.\deploy-remote-build.ps1 -SkipBuild
旧 Git 工作流脚本已被 `scripts/git-incremental-deploy.ps1` 替代。后续 Git 增量部署只看新脚本。
# 不使用构建缓存
.\deploy-remote-build.ps1 -NoCache
## 3. 旧缓存脚本
# 保留旧镜像
.\deploy-remote-build.ps1 -KeepOldImages
```
下面这些脚本属于早期构建缓存方案,不是当前 Git 增量部署的默认前置步骤:
**参数说明:**
- `-Target`: 部署目标,`production`(正式系统,端口4320)或 `shadow`(影子系统,端口5320)
- `-BuildServerIP`: 构建机 IP(默认:192.168.42.134)
- `-ProdServerIP`: 生产服务器 IP(默认:192.168.58.20)
- `-SkipSync`: 跳过代码同步
- `-SkipBuild`: 跳过镜像构建
- `-NoCache`: 不使用构建缓存
- `-KeepOldImages`: 保留旧镜像
- `setup-build-cache.ps1`
- `build-base-image.ps1`
- `scripts/manage-build-cache.ps1`
---
只有在明确要维护旧缓存方案时才参考它们。当前常规部署优先使用构建机上的 Docker / Next 缓存,不再传播 `save-base-images.ps1`、`load-base-images.ps1`、`save-npm-packages.ps1` 这类已不存在的脚本名。
## 🚫 其他部署方式(不推荐)
## 4. 数据库迁移脚本
以下脚本仅作为参考,**不推荐使用**,除非项目负责人明确要求:
### `scripts/execute-migration.ps1`
### `deploy-local-build.ps1` ❌
**本地构建部署** - 会在本地产生大量 Docker 缓存,磨损 SSD
历史一次性迁移工具,只针对 `20251206000000_add_soft_delete_and_favorites` 这一个迁移,不是通用生产迁移入口。
**问题**:
- 本地构建占用大量磁盘空间(~2GB)
- SSD 磨损严重(频繁读写)
- 构建速度慢(Windows Docker Desktop 性能差)
- 上传镜像慢(~180MB 通过网络)
高风险要求:
### `deploy-git-build.ps1` ⚠️
**Git 工作流部署** - 需要配置 Git 仓库,复杂度高
- 先确认该迁移在生产库尚未执行。
- 先确认生产库备份可用。
- 不在用户高峰期执行。
- 不把它当作普通部署流程的一部分。
**问题**:
- 需要配置 Git 仓库
- 流程复杂(本地提交 → 推送 → 服务器拉取 → 构建)
- 不适合快速迭代
### `deploy-public-build.ps1` ⚠️
**公网穿透部署** - 仅在特殊情况下使用(外网访问生产环境)
**用途**:从公网访问生产环境进行部署(非常规操作)
---
**⚠️ 再次提醒**:请使用 `deploy-remote-build.ps1` 进行部署,除非项目负责人明确要求其他方式。
---
## 基础镜像管理
### `save-base-images.ps1`
下载并保存基础镜像到本地缓存
```powershell
.\save-base-images.ps1
```
### `load-base-images.ps1`
从本地加载基础镜像(部署脚本会自动调用)
```powershell
.\load-base-images.ps1
```
### `save-npm-packages.ps1`(可选)
缓存 npm 包到本地(网络不稳定时使用)
```powershell
.\save-npm-packages.ps1
```
## 数据库迁移脚本
### `execute-migration.ps1`
在生产库上执行数据库迁移(Windows PowerShell)
```powershell
.\execute-migration.ps1
```
### `execute-migration.sh`
在生产库上执行数据库迁移(Linux/Mac)
```bash
chmod +x execute-migration.sh
./execute-migration.sh
```
### `scripts/execute-migration.sh`
Linux / macOS 版本的一次性迁移工具,适用范围同上。

View File

@ -1,5 +1,6 @@
# 生产库迁移执行脚本
# 用途:在生产库上执行数据库迁移,添加 deletedAt、收藏、日志等新表和字段
# 历史一次性生产库迁移执行脚本
# 用途:只针对 20251206000000_add_soft_delete_and_favorites 迁移
# 状态:不是通用生产迁移入口,不属于当前 Git 增量部署流程
# 注意:执行前请确保已备份生产库!
param(

View File

@ -1,7 +1,8 @@
#!/bin/bash
# 生产库迁移执行脚本(Linux/Mac 版本)
# 用途:在生产库上执行数据库迁移
# 历史一次性生产库迁移执行脚本(Linux/Mac 版本)
# 用途:只针对 20251206000000_add_soft_delete_and_favorites 迁移
# 状态:不是通用生产迁移入口,不属于当前 Git 增量部署流程
set -e

View File

@ -1,5 +1,6 @@
# 构建缓存管理脚本
# 功能:保存和恢复 node_modules 缓存,加速 Docker 构建
# 状态:旧构建缓存方案,不是当前 Git 增量部署的默认前置步骤
param(
[ValidateSet('save', 'check', 'clean')]

View File

@ -1,43 +0,0 @@
# 更新 Docker 构建缓存
# 用途:更新 npm-packages.tgz 缓存,加速后续构建
Write-Host "🔄 更新 Docker 构建缓存" -ForegroundColor Cyan
Write-Host ""
# 检查 node_modules 是否存在
if (-not (Test-Path "node_modules")) {
Write-Host "❌ node_modules 不存在,请先运行 npm install" -ForegroundColor Red
exit 1
}
# 创建缓存目录
if (-not (Test-Path ".docker-cache")) {
New-Item -ItemType Directory -Path ".docker-cache" | Out-Null
}
# 打包 node_modules
Write-Host "📦 正在打包 node_modules..." -ForegroundColor Yellow
$tarFile = ".docker-cache/npm-packages.tgz"
# 使用 tar 命令打包(Windows 10+ 自带)
tar -czf $tarFile node_modules
if (Test-Path $tarFile) {
$fileSize = (Get-Item $tarFile).Length / 1MB
Write-Host "✅ 缓存已更新:$([Math]::Round($fileSize, 2)) MB" -ForegroundColor Green
# 更新缓存信息
$cacheInfo = @{
"created" = (Get-Date).ToString("yyyy-MM-dd HH:mm:ss")
"size_mb" = [Math]::Round($fileSize, 2)
"node_version" = (node -v)
"npm_version" = (npm -v)
}
$cacheInfo | ConvertTo-Json | Out-File ".docker-cache/cache-info.json" -Encoding UTF8
Write-Host ""
Write-Host "💡 提示:下次构建镜像时会使用此缓存,速度会快很多" -ForegroundColor Cyan
} else {
Write-Host "❌ 打包失败" -ForegroundColor Red
exit 1
}

View File

@ -1,5 +1,6 @@
# 由壹点3D素材 - 构建机缓存初始化脚本
# 功能:一次性准备所有缓存文件,后续构建直接使用,零解压、零网络
# 状态:旧构建缓存方案,不是当前 Git 增量部署的默认前置步骤
param(
[string]$BuildServerIP = '192.168.42.134',

View File

@ -316,8 +316,9 @@ EOFCONFIG
Write-Info "1. 在浏览器访问 Registry UI:http://$BuildServerIP`:5001"
Write-Info "2. 配置生产服务器(运行下面的命令):"
Write-Host " .\setup-production-server.ps1" -ForegroundColor Yellow
Write-Info "3. 测试部署:"
Write-Host " .\deploy-build-server.ps1 -Target shadow" -ForegroundColor Yellow
Write-Info "3. 测试 Git 增量部署:"
Write-Host " .\scripts\git-incremental-deploy.ps1 -DryRun" -ForegroundColor Yellow
Write-Host " .\scripts\git-incremental-deploy.ps1 -Target shadow" -ForegroundColor Yellow
Write-Host ""
}

View File

@ -300,10 +300,12 @@ EOFCONFIG
Write-Host ""
Write-Host "🎯 下一步操作" -ForegroundColor Magenta
Write-Host "━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━" -ForegroundColor Cyan
Write-Info "1. 测试部署到影子系统:"
Write-Host " .\deploy-build-server.ps1 -Target shadow" -ForegroundColor Yellow
Write-Info "2. 如果测试成功,部署到正式系统:"
Write-Host " .\deploy-build-server.ps1 -Target production" -ForegroundColor Yellow
Write-Info "1. 先做 Git 增量部署预检:"
Write-Host " .\scripts\git-incremental-deploy.ps1 -DryRun" -ForegroundColor Yellow
Write-Info "2. 测试部署到影子系统:"
Write-Host " .\scripts\git-incremental-deploy.ps1 -Target shadow" -ForegroundColor Yellow
Write-Info "3. 如果测试成功,部署到正式系统:"
Write-Host " .\scripts\git-incremental-deploy.ps1" -ForegroundColor Yellow
Write-Host ""
}

View File

@ -1,197 +0,0 @@
# 部署前安全检查清单
## ✅ 运行脚本前请确认
### 1. 构建机配置(setup-build-server.ps1)
**操作范围**:只在构建机(192.168.42.134)
- [ ] 确认目标服务器是构建机:`192.168.42.134`
- [ ] 确认不会连接生产服务器
- [ ] 确认只安装 Docker Compose 和 Registry
- [ ] 确认不会操作任何数据
**安全保证**:
- ✅ 不会连接生产服务器
- ✅ 不会操作数据库
- ✅ 不会操作存储桶
- ✅ 不会操作 uploads 目录
---
### 2. 生产服务器配置(setup-production-server.ps1)
**操作范围**:只修改 Docker 配置
- [ ] 确认目标服务器是生产服务器:`192.168.58.20`
- [ ] 确认只修改 `/etc/docker/daemon.json`
- [ ] 确认会备份现有配置
- [ ] 确认重启 Docker 时间约 5-10 秒
**安全保证**:
- ✅ 不会操作 uploads 目录
- ✅ 不会操作数据库
- ✅ 不会操作存储桶
- ✅ 不会删除任何文件
- ✅ 容器会自动重启
**重启影响**:
- ⚠️ Docker 服务重启:5-10 秒
- ⚠️ 所有容器短暂中断后自动恢复
- ✅ 数据不会丢失
---
### 3. 部署脚本(deploy-build-server.ps1)
**操作范围**:构建镜像 → 传输镜像 → 启动容器
- [ ] 确认部署目标(shadow 或 production)
- [ ] 确认只操作应用容器
- [ ] 确认 uploads 目录只挂载,不修改
- [ ] 确认临时文件在 /tmp/ 目录
**安全保证**:
- ✅ uploads 目录:只挂载,不修改
- ✅ 数据库:不操作(只通过应用连接)
- ✅ 存储桶:不操作(只通过应用连接)
- ✅ 其他容器:不操作(PostgreSQL、MinIO、Redis、frp)
- ✅ 临时文件:自动清理
**操作的容器**:
- ✅ `youyidian-3d-material`(正式系统)
- ✅ `youyidian-3d-shadow`(影子系统)
**不操作的容器**:
- ❌ `gongxiangpan-postgres-1`(数据库)
- ❌ `minio`(存储服务)
- ❌ `youyidian-3d-redis-shadow`(Redis)
- ❌ `frpc-minio`(frp 客户端)
---
## 🔍 运行前检查命令
### 检查生产服务器状态
```powershell
# 检查运行中的容器
& "C:\Program Files\PuTTY\plink.exe" -batch -pw "jsjj8888" root@192.168.58.20 "docker ps --format 'table {{.Names}}\t{{.Status}}'"
# 检查 uploads 目录(只看前 20 个文件)
& "C:\Program Files\PuTTY\plink.exe" -batch -pw "jsjj8888" root@192.168.58.20 "ls -la /mnt/PoolOne/apps/youyidian-3d/uploads | head -20"
# 检查 uploads 文件数量
& "C:\Program Files\PuTTY\plink.exe" -batch -pw "jsjj8888" root@192.168.58.20 "find /mnt/PoolOne/apps/youyidian-3d/uploads -type f | wc -l"
```
### 检查构建机状态
```powershell
# 检查 Docker 版本
& "C:\Program Files\PuTTY\plink.exe" -batch -pw "Jsjj@8888" root@192.168.42.134 "docker --version"
# 检查磁盘空间
& "C:\Program Files\PuTTY\plink.exe" -batch -pw "Jsjj@8888" root@192.168.42.134 "df -h /"
```
---
## 📋 运行步骤
### 第一次配置(只需执行一次)
1. **配置构建机**:
```powershell
.\setup-build-server.ps1
```
- 预计时间:2-3 分钟
- 操作:安装 Docker Compose + Registry
- 影响:无(只在构建机)
2. **检查生产服务器**(不修改):
```powershell
.\setup-production-server.ps1 -DryRun
```
- 预计时间:30 秒
- 操作:只检查,不修改
- 影响:无
3. **配置生产服务器**:
```powershell
.\setup-production-server.ps1
```
- 预计时间:1 分钟
- 操作:配置 insecure-registry
- 影响:Docker 重启 5-10 秒
### 日常部署(每次部署)
1. **部署到影子系统**(测试):
```powershell
.\deploy-build-server.ps1 -Target shadow
```
- 预计时间:2-5 分钟
- 操作:构建 → 传输 → 部署
- 影响:只影响影子系统(端口 5320)
2. **部署到正式系统**(生产):
```powershell
.\deploy-build-server.ps1 -Target production
```
- 预计时间:2-5 分钟
- 操作:构建 → 传输 → 部署
- 影响:正式系统短暂重启(约 10 秒)
---
## 🚨 紧急回滚
### 如果部署出现问题
1. **查看容器日志**:
```powershell
& "C:\Program Files\PuTTY\plink.exe" -batch -pw "jsjj8888" root@192.168.58.20 "docker logs youyidian-3d-material --tail 100"
```
2. **重启容器**:
```powershell
& "C:\Program Files\PuTTY\plink.exe" -batch -pw "jsjj8888" root@192.168.58.20 "docker restart youyidian-3d-material"
```
3. **恢复 Docker 配置**(如果配置有问题):
```powershell
& "C:\Program Files\PuTTY\plink.exe" -batch -pw "jsjj8888" root@192.168.58.20 "cp /etc/docker/daemon.json.backup.* /etc/docker/daemon.json && systemctl restart docker"
```
4. **检查 uploads 目录**(确认没有被删除):
```powershell
& "C:\Program Files\PuTTY\plink.exe" -batch -pw "jsjj8888" root@192.168.58.20 "ls -la /mnt/PoolOne/apps/youyidian-3d/uploads | head -20"
```
---
## ✅ 最终确认
在运行任何脚本前,请确认:
- [ ] 我已阅读并理解所有安全保证
- [ ] 我确认脚本不会操作 uploads 目录
- [ ] 我确认脚本不会操作数据库
- [ ] 我确认脚本不会操作存储桶
- [ ] 我知道如何查看日志和回滚
- [ ] 我已备份重要数据(如果是第一次运行)
---
**准备好了吗?开始配置!**
1. 先运行:`.\setup-build-server.ps1`
2. 然后运行:`.\setup-production-server.ps1 -DryRun`(检查)
3. 确认无误后:`.\setup-production-server.ps1`(配置)
4. 最后测试:`.\deploy-build-server.ps1 -Target shadow`(部署到影子系统)
---
**文档版本**:v1.0
**更新时间**:2025-01-17