chore: update project index and meeting notes for April 2, 2026
- Added new entries for meetings discussing work progress and requirement synchronization across various project components. - Updated the last modified date to April 2, 2026, in multiple project index files. - Documented changes in the sync log for assistant-doc-sync regarding the meeting outcomes.
This commit is contained in:
7
.cursor/agent/产品经理/evolution/2026-04-02.md
Normal file
7
.cursor/agent/产品经理/evolution/2026-04-02.md
Normal file
@@ -0,0 +1,7 @@
|
||||
# 产品经理 经验记录 - 2026-04-02
|
||||
|
||||
## 会议:工作进度与需求同步会
|
||||
|
||||
- **场景**:4 月初全员同步进度,衔接 2026-03-31 超级个体与 @ 列表融合待办。
|
||||
- **要点**:融合类需求需同步明确「一期验收口径」(UsersPage @ 状态、列表入口是否收敛),并写入需求汇总/运营与变更,避免实现先行、验收滞后。
|
||||
- **待办**:问题与作答区 Q1 由产品补充作答后闭环。
|
||||
8
.cursor/agent/后端工程师/evolution/2026-04-02.md
Normal file
8
.cursor/agent/后端工程师/evolution/2026-04-02.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# 后端工程师 经验记录 - 2026-04-02
|
||||
|
||||
## 会议:工作进度与需求同步会
|
||||
|
||||
- **场景**:进度同步 + deploy 目录变更与业务待续项并行。
|
||||
- **要点**:`soul-api/deploy` 改动合并前应用 compose(或等价)验证,README 与 compose 环境变量保持一致;vip-members 聚合 Person 字段优先,满足管理端 UsersPage 联调。
|
||||
- **路由**:miniprogram / admin / db 分组边界不变。
|
||||
- **待办**:问题与作答区 Q2、Q3 作答后闭环。
|
||||
8
.cursor/agent/团队/evolution/2026-04-02.md
Normal file
8
.cursor/agent/团队/evolution/2026-04-02.md
Normal file
@@ -0,0 +1,8 @@
|
||||
# 团队 经验记录 - 2026-04-02
|
||||
|
||||
## 会议:工作进度与需求同步会
|
||||
|
||||
- **跨角色决议**:
|
||||
- 融合方案落地顺序:后端 vip-members 聚合 → 管理端 UsersPage → 测试全量回归清单;小程序回归为主。
|
||||
- 部署变更与业务 PR 区分 scope,deploy 合并前环境验证必做。
|
||||
- **详见**:`.cursor/meeting/2026-04-02_工作进度与需求同步会.md`
|
||||
6
.cursor/agent/小程序开发工程师/evolution/2026-04-02.md
Normal file
6
.cursor/agent/小程序开发工程师/evolution/2026-04-02.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# 小程序开发工程师 经验记录 - 2026-04-02
|
||||
|
||||
## 会议:工作进度与需求同步会
|
||||
|
||||
- **场景**:超级个体与 @ 列表融合对 C 端以回归为主。
|
||||
- **要点**:`app.js` / 全局 `app.request` 类改动需列出必测页面(读/我的/登录/提现主路径);融合不默认改 miniprogram 接口路径。
|
||||
@@ -29,9 +29,10 @@ Soul 创业派对产品定位:面向创业者的社区/工具型小程序。
|
||||
| 2026-03-24 | 会议:开发进度同步;项目索引补齐 2026-03-19/20 及之后记录;文档同步原则确认 | 已完成 |
|
||||
| 2026-03-24 | 会议:需求与进度及三端闭环评审;规则/风控缺口清单化;VIP 支付前资料规则待拍板 | 已完成 |
|
||||
| 2026-03-31 | 会议:超级个体列表与 @ 列表融合方案(先数据互链再单页);一期验收口径待拍板 | 待续 |
|
||||
| 2026-04-02 | 会议:工作进度与需求同步;一期融合验收口径澄清、各端索引与需求 delta 跟进 | 进行中 |
|
||||
|
||||
> **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD,状态用:已完成 / 进行中 / 待续 / 搁置
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2026-03-31
|
||||
**最后更新**:2026-04-02
|
||||
|
||||
@@ -24,9 +24,10 @@
|
||||
| 2026-03-17 | 吸收新需求:代付统一到代付页(gift=1&ref redirectTo)→ 需求汇总、找朋友代付流程、运营与变更 | 已完成 |
|
||||
| 2026-03-17 | 会议收尾:新版管理端迁移到稳定版实施方案确认;纪要、各角色经验入库、项目索引更新 | 已完成 |
|
||||
| 2026-03-17 | 会议收尾:源码优化完成与测试流程定稿;纪要、经验入库、开发文档同步 | 已完成 |
|
||||
| 2026-04-02 | 会议收尾:工作进度与需求同步会;纪要、各角色 evolution、项目索引与会议索引更新 | 已完成 |
|
||||
|
||||
> **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD,状态用:已完成 / 进行中 / 待续 / 搁置
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2026-03-17(会议收尾:源码优化完成与测试流程定稿)
|
||||
**最后更新**:2026-04-02(会议收尾:工作进度与需求同步会)
|
||||
|
||||
@@ -42,9 +42,10 @@ soul-api(Go + Gin + GORM + MySQL)提供三组路由:`/api/miniprogram/*`
|
||||
| 2026-03-24 | 会议:开发进度同步;项目索引补齐;router/ossConfig 待确认 | 已完成 |
|
||||
| 2026-03-24 | 会议:三端闭环评审;提现/CKB/匹配后端校验与 errorCode 为 P0/P1 | 已完成 |
|
||||
| 2026-03-31 | 会议:超级个体与 @ 列表融合;优先扩展 vip-members 聚合 Person 关联字段 | 待续 |
|
||||
| 2026-04-02 | 会议:工作进度与需求同步;deploy 验证后提交;vip-members 聚合优先;router/ossConfig 历史待办跟进 | 进行中 |
|
||||
|
||||
> **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD,状态用:已完成 / 进行中 / 待续 / 搁置
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2026-03-31
|
||||
**最后更新**:2026-04-02
|
||||
|
||||
@@ -19,7 +19,6 @@ Soul 创业派对全项目架构与约定:路由隔离(miniprogram/admin/db
|
||||
| 2026-03-08 | 文章阅读付费规则澄清:免费章节以 free_chapters 为准;VIP 全章免费;后端 book.go 合并配置修复 | 已完成 |
|
||||
| 2026-03-10 | 会议:管理端迁移 Mycontent-temp,新菜单/布局作为团队规范基线,避免两套后台并行发散 | 待续 |
|
||||
| 2026-03-10 | 新旧版代码对比方法论:以功能完整性而非日期判断优劣,批量 diff + 分类取舍 + 移植修复 | 已完成 |
|
||||
|
||||
| 2026-03-11 | 以界面定需求文档建立;需求基准与业务逻辑对齐(用户/VIP 资料、三端路由);会议收尾纪要与各角色经验入库 | 已完成 |
|
||||
| 2026-03-12 | 密钥/token 设计:关联小程序 key、@ 人物 token;不暴露真实密钥、服务端兑换 | 已完成 |
|
||||
| 2026-03-12 | 9.9 买断团队约定:后端统一输出 hasFullBook/has_full_book,小程序和管理端只认该信号;支持通过用户资料开关 OR 订单赠送全书且不影响 VIP 逻辑 | 已完成 |
|
||||
@@ -33,9 +32,10 @@ Soul 创业派对全项目架构与约定:路由隔离(miniprogram/admin/db
|
||||
| 2026-03-18 | 会议:超级个体开通后自动创建@人统一走 Person;幂等键绑定 userId;默认资料 flags 后端输出 | 已完成 |
|
||||
| 2026-03-24 | 会议:需求与进度及三端闭环评审;主路径闭环成立、规则闭环待加固;失败响应与 err.response 契约 | 已完成 |
|
||||
| 2026-03-31 | 会议:超级个体与 @ 列表融合;先数据与入口融合、路由边界不变 | 已完成 |
|
||||
| 2026-04-02 | 会议:工作进度与需求同步;融合落地顺序与 deploy 验证门禁、问题与作答区待闭环 | 已完成 |
|
||||
|
||||
> **格式说明**:每次架构级讨论后在此追加一行,日期格式 YYYY-MM-DD
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2026-03-31
|
||||
**最后更新**:2026-04-02
|
||||
|
||||
@@ -48,9 +48,10 @@
|
||||
| 2026-03-24 | 落地:app.request 挂 err.response;soulBridge 留资扩展;阅读页带码与 referral/my 提现 catch 对齐 | 已完成 |
|
||||
| 2026-03-30 | 会议:文章详情 # 跳转关联小程序(如 MBTI)时 path 追加 `phone`;read.js 已实现 | 已完成 |
|
||||
| 2026-03-31 | 会议:超级个体与 @ 列表融合;C 端默认不改接口,仅回归 | 已完成 |
|
||||
| 2026-04-02 | 会议:工作进度与需求同步;全局 app/request 类改动需主路径冒烟;融合仍以回归为主 | 已完成 |
|
||||
|
||||
> **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD,状态用:已完成 / 进行中 / 待续 / 搁置
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2026-03-31
|
||||
**最后更新**:2026-04-02
|
||||
|
||||
@@ -35,7 +35,8 @@
|
||||
| 2026-03-24 | 会议:开发进度同步;项目索引补齐;提现/登录/收益/推广设置用例补充;singlePage、getPhoneNumber 边界 | 已完成 |
|
||||
| 2026-03-24 | 会议:三端闭环评审;API 绕过负例与双入口提现回归待契约落地后执行 | 已完成 |
|
||||
| 2026-03-31 | 会议:超级个体与 @ 列表融合;回归清单含排序/@/绑定/Webhook 边界 | 待续 |
|
||||
| 2026-04-02 | 会议:工作进度与需求同步;融合清单待联调后执行;deploy PR 增加 compose/health/smoke | 待续 |
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2026-03-31
|
||||
**最后更新**:2026-04-02
|
||||
|
||||
@@ -47,11 +47,12 @@
|
||||
| 2026-03-24 | 会议:开发进度同步;项目索引补齐;DistributionPage Order.description 待修 | 已完成 |
|
||||
| 2026-03-24 | 会议:三端闭环评审;管理端与小程序配套整体齐全;类型债择机修 | 已完成 |
|
||||
| 2026-03-31 | 会议:超级个体与 @ 列表融合;UsersPage 表格加 @ 状态与跳转内容页 | 待续 |
|
||||
| 2026-04-02 | 会议:工作进度与需求同步;UsersPage 待接口字段就绪后联调;deploy 文档基址与后端对齐 | 进行中 |
|
||||
|
||||
> **格式说明**:每次开发后在此追加一行,日期格式 YYYY-MM-DD,状态用:已完成 / 进行中 / 待续 / 搁置
|
||||
|
||||
---
|
||||
|
||||
**最后更新**:2026-03-31
|
||||
**最后更新**:2026-04-02
|
||||
|
||||
> 注:soul-admin 构建仍有 DistributionPage Order.description 类型错误(与本次迁移无关),待修复。
|
||||
|
||||
6
.cursor/agent/管理端开发工程师/evolution/2026-04-02.md
Normal file
6
.cursor/agent/管理端开发工程师/evolution/2026-04-02.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# 管理端开发工程师 经验记录 - 2026-04-02
|
||||
|
||||
## 会议:工作进度与需求同步会
|
||||
|
||||
- **场景**:UsersPage @ 状态与跳转内容页依赖后端 vip-members 扩展。
|
||||
- **要点**:先锁定聚合接口字段契约再改表格列与跳转;deploy 文档若涉及管理端访问基址,与后端 README 对齐。
|
||||
6
.cursor/agent/软件测试/evolution/2026-04-02.md
Normal file
6
.cursor/agent/软件测试/evolution/2026-04-02.md
Normal file
@@ -0,0 +1,6 @@
|
||||
# 软件测试 经验记录 - 2026-04-02
|
||||
|
||||
## 会议:工作进度与需求同步会
|
||||
|
||||
- **场景**:融合方案回归与部署类变更验证策略。
|
||||
- **要点**:3/31 清单(排序、@、绑定、Webhook)待联调就绪后执行;含 `deploy/` 的 PR 建议增加 compose 起服务、`/health`、核心阅读/下单 smoke。
|
||||
116
.cursor/meeting/2026-04-02_工作进度与需求同步会.md
Normal file
116
.cursor/meeting/2026-04-02_工作进度与需求同步会.md
Normal file
@@ -0,0 +1,116 @@
|
||||
# 会议纪要 - 2026-04-02 | 工作进度与需求同步会
|
||||
|
||||
> 本文件由**助理橙子**在会议结束后自动生成。
|
||||
|
||||
---
|
||||
|
||||
## 基本信息
|
||||
|
||||
- **时间**:2026-04-02
|
||||
- **议题**:同步各端工作进度与需求口径,衔接 2026-03-31 以来待办
|
||||
- **触发方式**:开个会议,同步大家工作进度和需求
|
||||
- **参与角色**:产品经理、后端开发、管理端开发工程师、小程序开发工程师、测试人员、团队(乘风主持)
|
||||
|
||||
---
|
||||
|
||||
## 各角色发言
|
||||
|
||||
### 【产品经理】
|
||||
|
||||
- 自 [2026-03-31 超级个体与 @ 列表融合](2026-03-31_超级个体与@列表融合.md) 后,**一期验收口径**(UsersPage @ 状态展示、vip-members 与 Person 关联展示边界)仍需与业务方确认后写入需求汇总/运营与变更。
|
||||
- 《以界面定需求》仍为验收基准;若 4 月有新入口或列表合并,需补界面清单与优先级。
|
||||
- 建议各端在索引中标注「3/31 融合方案」相关项的**完成度**(未开始/开发中/已联调)。
|
||||
|
||||
### 【后端开发】
|
||||
|
||||
- 当前仓库工作区显示 **`soul-api/deploy/`**(Dockerfile、docker-compose.yml、README)有未提交修改,属**部署与运维文档**迭代,合并前应在目标环境或本地 compose 跑通并核对环境变量说明。
|
||||
- 3/31 纪要待续:**vip-members 聚合 Person 关联字段**优先于管理端表格增强;历史待办 **router 补齐、ossConfig 确认**仍建议按迁移清单闭环,避免预发与生产配置漂移。
|
||||
- 路由分组原则不变:小程序仅 `miniprogram`,管理端 `admin`/`db`,禁止混挂。
|
||||
|
||||
### 【管理端开发工程师】
|
||||
|
||||
- 3/31 待续:**UsersPage** 表格增加 @ 状态与跳转内容页依赖后端 vip-members(或等价接口)字段稳定后再收尾联调。
|
||||
- 若 deploy 文档更新涉及管理端访问地址或 CORS/网关,与后端对齐 README 中的示例 URL。
|
||||
|
||||
### 【小程序开发工程师】
|
||||
|
||||
- 融合方案约定:**C 端默认不改接口、以回归为主**;若 `app.js` 或全局 `app.request` 有变更,需同步走提现/登录/阅读主路径冒烟。
|
||||
- 文章详情、`#` 跳转关联小程序传 `phone` 等已落地能力纳入常规回归,避免大文件改动时漏测。
|
||||
|
||||
### 【测试人员】
|
||||
|
||||
- 3/31 会议已列回归关注点:**排序、@ 展示、绑定、Webhook 边界**;待后端与管理端联调就绪后执行一轮三端联调。
|
||||
- **部署变更**合并后建议增加:**compose 起服务、`/health`、核心下单/阅读** 的轻量 smoke,防止仅文档改动的 PR 遗漏镜像/端口问题。
|
||||
|
||||
---
|
||||
|
||||
## 讨论过程
|
||||
|
||||
- **【乘风】**:衔接 2026-03-24 同步会与 2026-03-31 融合会,4 月初重点是把「融合方案待续」从纸面推进到**可测状态**,同时不阻塞部署侧改进。
|
||||
- **【后端开发】回复【产品经理】**:vip-members 扩展字段确定后,可在接口文档或 OpenAPI 注释中标明,便于产品写验收点。
|
||||
- **【测试人员】回复【后端开发】**:deploy 变更若影响数据库或 Redis 连接串示例,请在 README 标明「必填/可选」,减少环境误配。
|
||||
|
||||
---
|
||||
|
||||
## 会议决议
|
||||
|
||||
1. **进度同步机制**:自 2026-04-02 起,各角色在实现跨 3/31 边界的改动时,**当日或次日**在对应 `项目索引/*.md` 追加一行进度;产品侧大块需求变更同步《需求汇总》或《运营与变更》。
|
||||
2. **部署变更**:`soul-api/deploy/*` 合并前完成 **compose(或等价)验证**,README 与 compose 中镜像名、端口、环境变量保持一致。
|
||||
3. **超级个体与 @ 列表融合**:执行顺序为 **后端 vip-members 聚合字段 → 管理端 UsersPage 联调 → 测试按 3/31 清单回归**;小程序按原约定以回归为主。
|
||||
4. **待确认项**:一期验收口径(@ 状态定义、列表合并后是否隐藏旧入口)由产品在本周内澄清并回写「问题与作答区」。
|
||||
|
||||
---
|
||||
|
||||
## 待办事项
|
||||
|
||||
| 责任角色 | 任务 | 优先级 | 截止建议 |
|
||||
|---------|------|--------|---------|
|
||||
| 产品经理 | 澄清一期融合验收口径并更新需求/运营与变更 | 高 | 2026-04-09 前 |
|
||||
| 后端开发 | vip-members 聚合 Person 关联;deploy 改动验证后提交 | 高 | 按迭代节奏 |
|
||||
| 管理端开发工程师 | UsersPage @ 状态与跳转,待接口字段就绪后联调 | 中 | 接口就绪后 3 个工作日内 |
|
||||
| 小程序开发工程师 | 全局请求/登录相关改动后主路径冒烟 | 中 | 每次相关 PR 前 |
|
||||
| 测试人员 | 融合清单回归 + deploy 合并后 smoke | 中 | 联调就绪后 |
|
||||
|
||||
---
|
||||
|
||||
## 问题与作答区
|
||||
|
||||
> 会议中提出的待确认问题在此列出;作答区域供后续补充答案,便于追溯闭环。
|
||||
|
||||
| # | 问题 | 责任角色 | 作答 |
|
||||
|---|------|---------|------|
|
||||
| 1 | 超级个体与 @ 列表融合「一期」验收:UsersPage @ 状态的具体展示规则(文案/图标/空态)是否已定? | 产品经理 | (待补充) |
|
||||
| 2 | soul-api deploy 修改是否计划同步到预发/生产流水线(CI 变量、镜像仓库)?由谁 owner? | 后端开发 / 团队 | (待补充) |
|
||||
| 3 | router 补齐(users/rfm、journey-stats、shensheshou)与 ossConfig 当前环境是否已全部一致? | 后端开发 | (待补充) |
|
||||
|
||||
---
|
||||
|
||||
## 各角色经验与业务理解更新
|
||||
|
||||
### 产品经理
|
||||
|
||||
- 大方案(如列表融合)会后需**显式一期验收口径**,避免开发按技术合并做完后产品口径滞后。
|
||||
|
||||
### 后端开发
|
||||
|
||||
- **Deploy 与业务代码**同仓时,PR 应区分「纯部署」与「接口变更」,便于测试选 smoke 范围。
|
||||
|
||||
### 管理端开发工程师
|
||||
|
||||
- 表格增强依赖聚合接口时,先锁定**字段契约**再改列与跳转,减少联调往返。
|
||||
|
||||
### 小程序开发工程师
|
||||
|
||||
- 全局 `app.js` 改动影响面大,变更说明中写明**必测页面列表**。
|
||||
|
||||
### 测试人员
|
||||
|
||||
- 部署类 PR 默认增加 **compose + health + 主路径** 最小验证集。
|
||||
|
||||
### 团队共享
|
||||
|
||||
- **融合方案执行顺序**:后端数据与接口 → 管理端展示 → 小程序回归 → 全量回归清单;三端路由隔离不变。
|
||||
|
||||
---
|
||||
|
||||
*会议纪要由助理橙子生成 | 各角色经验已同步至 `agent/{角色}/evolution/2026-04-02.md`*
|
||||
@@ -84,3 +84,4 @@ YYYY-MM-DD_会议主题.md
|
||||
| 2026-03-24 | 需求与进度及三端闭环评审(业务闭环、核心功能、小程序与管理端配套) | 产品、后端、管理端、小程序、测试 | [2026-03-24_需求与进度及三端闭环评审.md](2026-03-24_需求与进度及三端闭环评审.md) |
|
||||
| 2026-03-30 | 文章详情 # 标签跳转 MBTI 小程序传手机号 | 产品、后端、管理端、小程序、测试 | [2026-03-30_文章详情跳转MBTI小程序传手机号.md](2026-03-30_文章详情跳转MBTI小程序传手机号.md) |
|
||||
| 2026-03-31 | 超级个体列表与 @ 列表融合方案 | 产品、后端、管理端、小程序、测试、团队 | [2026-03-31_超级个体与@列表融合.md](2026-03-31_超级个体与@列表融合.md) |
|
||||
| 2026-04-02 | 工作进度与需求同步会 | 产品、后端、管理端、小程序、测试、团队 | [2026-04-02_工作进度与需求同步会.md](2026-04-02_工作进度与需求同步会.md) |
|
||||
|
||||
@@ -6,6 +6,10 @@
|
||||
|
||||
---
|
||||
|
||||
## 2026-04-02
|
||||
|
||||
- 会议收尾:工作进度与需求同步会;纪要 `.cursor/meeting/2026-04-02_工作进度与需求同步会.md`;各角色 evolution、项目索引(产品/后端/管理端/小程序/测试/团队)、meeting/README 已更新
|
||||
|
||||
## 2026-03-31
|
||||
|
||||
- 会议收尾:超级个体列表与 @ 列表融合;纪要 `.cursor/meeting/2026-03-31_超级个体与@列表融合.md`;各角色 evolution 与项目索引已更新
|
||||
|
||||
@@ -12,7 +12,9 @@ const DEFAULT_MCH_ID = '1318592501'
|
||||
const DEFAULT_WITHDRAW_TMPL_ID = 'u3MbZGPRkrZIk-I7QdpwzFxnO_CeQPaCWF2FkiIablE'
|
||||
// baseUrl 手动切换(注释方式):
|
||||
// const API_BASE_URL = 'http://localhost:9090'
|
||||
const API_BASE_URL = 'https://soulapi.quwanzhi.com'
|
||||
// const API_BASE_URL = 'https://soulapi.quwanzhi.com'
|
||||
const API_BASE_URL = 'https://souldev.quwanzhi.com'
|
||||
|
||||
const CONFIG_CACHE_KEY = 'mpConfigCacheV1'
|
||||
/** 新用户未完成头像+昵称前,强制留在资料引导(与 avatar-nickname 页配合) */
|
||||
const PROFILE_GUIDE_REQUIRED_KEY = 'profile_guide_required'
|
||||
|
||||
1042
soul-admin/dist/assets/index-BSvDfqaj.js
vendored
1042
soul-admin/dist/assets/index-BSvDfqaj.js
vendored
File diff suppressed because one or more lines are too long
1047
soul-admin/dist/assets/index-zk0czXxa.js
vendored
Normal file
1047
soul-admin/dist/assets/index-zk0czXxa.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
soul-admin/dist/index.html
vendored
2
soul-admin/dist/index.html
vendored
@@ -4,7 +4,7 @@
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>管理后台 - Soul创业派对</title>
|
||||
<script type="module" crossorigin src="/assets/index-BSvDfqaj.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index-zk0czXxa.js"></script>
|
||||
<link rel="stylesheet" crossorigin href="/assets/index-CuUAjfcM.css">
|
||||
</head>
|
||||
<body>
|
||||
|
||||
@@ -51,6 +51,7 @@ import {
|
||||
Pencil,
|
||||
Users,
|
||||
Share2,
|
||||
Webhook,
|
||||
} from 'lucide-react'
|
||||
import { get, put, post, del, SAVE_REQUEST_TIMEOUT } from '@/api/client'
|
||||
import { ChapterTree } from './ChapterTree'
|
||||
@@ -317,6 +318,11 @@ export function ContentPage() {
|
||||
const [personModalOpen, setPersonModalOpen] = useState(false)
|
||||
const [editingPerson, setEditingPerson] = useState<PersonItem | null>(null)
|
||||
const [personToDelete, setPersonToDelete] = useState<PersonItem | null>(null)
|
||||
/** 超级个体用户 id → 飞书群 Webhook(与 /api/db/vip-members 一致,供 @列表展示与编辑) */
|
||||
const [vipWebhookByUserId, setVipWebhookByUserId] = useState<Record<string, string>>({})
|
||||
const [personVipWebhookTarget, setPersonVipWebhookTarget] = useState<{ userId: string; name: string } | null>(null)
|
||||
const [personVipWebhookInput, setPersonVipWebhookInput] = useState('')
|
||||
const [personVipWebhookSaving, setPersonVipWebhookSaving] = useState(false)
|
||||
// CKB 获客统计(按人物 token 聚合)
|
||||
const [ckbLeadCounts, setCkbLeadCounts] = useState<Record<string, number>>({})
|
||||
const [ckbLeadDetailOpen, setCkbLeadDetailOpen] = useState(false)
|
||||
@@ -585,7 +591,21 @@ export function ContentPage() {
|
||||
personSource?: string
|
||||
isPinned?: boolean
|
||||
}
|
||||
const data = await get<{ success?: boolean; persons?: PersonResp[] }>('/api/db/persons')
|
||||
type VipRow = { id: string; webhookUrl?: string | null }
|
||||
const [data, vipRes] = await Promise.all([
|
||||
get<{ success?: boolean; persons?: PersonResp[] }>('/api/db/persons'),
|
||||
get<{ success?: boolean; data?: VipRow[]; error?: string }>('/api/db/vip-members?limit=500').catch(() => null),
|
||||
])
|
||||
const whMap: Record<string, string> = {}
|
||||
if (vipRes?.success && Array.isArray(vipRes.data)) {
|
||||
for (const m of vipRes.data) {
|
||||
const uid = (m.id || '').trim()
|
||||
if (!uid) continue
|
||||
const w = m.webhookUrl != null ? String(m.webhookUrl).trim() : ''
|
||||
if (w) whMap[uid] = w
|
||||
}
|
||||
}
|
||||
setVipWebhookByUserId(whMap)
|
||||
if (data?.success && data.persons) {
|
||||
setPersons(
|
||||
data.persons.map((p) => {
|
||||
@@ -643,6 +663,50 @@ export function ContentPage() {
|
||||
[loadPersons],
|
||||
)
|
||||
|
||||
const openPersonVipWebhook = useCallback((p: PersonItem) => {
|
||||
const uid = (p.userId || '').trim()
|
||||
if (!uid) {
|
||||
toast.info('未绑定会员的人物无法配置:飞书群 Webhook 按「超级个体用户」维度保存,请先为该人物绑定会员用户。')
|
||||
return
|
||||
}
|
||||
setPersonVipWebhookTarget({ userId: uid, name: p.name })
|
||||
setPersonVipWebhookInput((vipWebhookByUserId[uid] || '').trim())
|
||||
}, [vipWebhookByUserId])
|
||||
|
||||
const handleSavePersonVipWebhook = useCallback(async () => {
|
||||
if (!personVipWebhookTarget) return
|
||||
const uid = personVipWebhookTarget.userId
|
||||
const val = personVipWebhookInput.trim()
|
||||
if (val && !/^https?:\/\//i.test(val)) {
|
||||
toast.error('Webhook 地址需以 http/https 开头')
|
||||
return
|
||||
}
|
||||
setPersonVipWebhookSaving(true)
|
||||
try {
|
||||
const res = await put<{ success?: boolean; error?: string }>('/api/db/vip-members/webhook', {
|
||||
userId: uid,
|
||||
webhookUrl: val,
|
||||
})
|
||||
if (!res?.success) {
|
||||
toast.error(res?.error || '保存飞书群 Webhook 失败')
|
||||
return
|
||||
}
|
||||
toast.success(val ? '已保存该超级个体的飞书群 Webhook' : '已清空该超级个体的飞书群 Webhook')
|
||||
setPersonVipWebhookTarget(null)
|
||||
setPersonVipWebhookInput('')
|
||||
setVipWebhookByUserId((prev) => {
|
||||
const next = { ...prev }
|
||||
if (val) next[uid] = val
|
||||
else delete next[uid]
|
||||
return next
|
||||
})
|
||||
} catch {
|
||||
toast.error('保存飞书群 Webhook 失败')
|
||||
} finally {
|
||||
setPersonVipWebhookSaving(false)
|
||||
}
|
||||
}, [personVipWebhookTarget, personVipWebhookInput])
|
||||
|
||||
const loadLinkTags = useCallback(async () => {
|
||||
try {
|
||||
const data = await get<{
|
||||
@@ -2764,6 +2828,9 @@ export function ContentPage() {
|
||||
<p>
|
||||
<span className="text-purple-300">超级个体</span>:开通且昵称合法会自动进本列表可 @;共用「超级个体拉群」统一获客计划(话术由技术侧在系统里配置,本页不填 plan/apiKey,避免误操作)。
|
||||
</p>
|
||||
<p>
|
||||
<span className="text-[#38bdac]">飞书群 Webhook</span>(按人推线索到群):在下方表格「飞书群」列查看状态,操作栏点 Webhook 图标配置;与绑定会员用户维度一致。
|
||||
</p>
|
||||
<p>点「添加」新建的人物:每人单独一条存客宝计划(SOUL链接人与事-名称)。</p>
|
||||
</div>
|
||||
</CardHeader>
|
||||
@@ -2806,6 +2873,7 @@ export function ContentPage() {
|
||||
<th className="text-left py-1.5 px-3 w-[100px] font-normal text-xs">会员</th>
|
||||
<th className="py-1.5 px-3 w-16 font-normal text-center">获客数</th>
|
||||
<th className="text-left py-1.5 px-3 font-normal">获客计划</th>
|
||||
<th className="text-left py-1.5 px-2 w-[72px] font-normal text-xs">飞书群</th>
|
||||
<th className="text-center py-1.5 px-2 w-14 font-normal text-xs">置顶</th>
|
||||
<th className="text-left py-1.5 px-3 w-16 font-normal">状态</th>
|
||||
<th className="text-left py-1.5 px-2 w-24 font-normal">操作</th>
|
||||
@@ -2874,6 +2942,17 @@ export function ContentPage() {
|
||||
</span>
|
||||
</div>
|
||||
</td>
|
||||
<td className="py-2 px-2 text-xs">
|
||||
{p.userId ? (
|
||||
vipWebhookByUserId[(p.userId || '').trim()] ? (
|
||||
<span className="text-[#38bdac]" title={vipWebhookByUserId[(p.userId || '').trim()]}>已配置</span>
|
||||
) : (
|
||||
<span className="text-gray-500">未配置</span>
|
||||
)
|
||||
) : (
|
||||
<span className="text-gray-600">—</span>
|
||||
)}
|
||||
</td>
|
||||
<td className="py-2 px-2 text-center">
|
||||
<Button
|
||||
type="button"
|
||||
@@ -2899,6 +2978,16 @@ export function ContentPage() {
|
||||
</td>
|
||||
<td className="py-2 px-2">
|
||||
<div className="flex items-center gap-0">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className={`h-6 px-2 ${p.userId ? 'text-[#38bdac] hover:text-[#5fe0cd]' : 'text-gray-600 cursor-not-allowed opacity-50'}`}
|
||||
title={p.userId ? '设置飞书群 Webhook(点击头像留资推送)' : '未绑定会员,无法配置'}
|
||||
disabled={!p.userId}
|
||||
onClick={() => openPersonVipWebhook(p)}
|
||||
>
|
||||
<Webhook className="w-3 h-3" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
@@ -3631,6 +3720,59 @@ export function ContentPage() {
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* @列表 — 超级个体飞书群 Webhook(用户维度,PUT /api/db/vip-members/webhook) */}
|
||||
<Dialog
|
||||
open={!!personVipWebhookTarget}
|
||||
onOpenChange={(open) => {
|
||||
if (!open) {
|
||||
setPersonVipWebhookTarget(null)
|
||||
setPersonVipWebhookInput('')
|
||||
}
|
||||
}}
|
||||
>
|
||||
<DialogContent className="bg-[#0f2137] border-gray-700 text-white max-w-xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-white flex items-center gap-2">
|
||||
<Webhook className="w-5 h-5 text-[#38bdac]" />
|
||||
设置飞书群 Webhook — {personVipWebhookTarget?.name}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4 py-4">
|
||||
<Label className="text-gray-300 text-sm">VOX Webhook 地址(留空即清空)</Label>
|
||||
<Input
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
placeholder="https://open.feishu.cn/open-apis/bot/v2/hook/..."
|
||||
value={personVipWebhookInput}
|
||||
onChange={(e) => setPersonVipWebhookInput(e.target.value)}
|
||||
/>
|
||||
<p className="text-xs text-gray-500">
|
||||
当用户点击该超级个体头像并提交线索时,线索将优先推送到这里配置的飞书群。
|
||||
</p>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button
|
||||
variant="outline"
|
||||
onClick={() => {
|
||||
setPersonVipWebhookTarget(null)
|
||||
setPersonVipWebhookInput('')
|
||||
}}
|
||||
className="border-gray-600 text-gray-300 hover:bg-gray-700/50 bg-transparent"
|
||||
>
|
||||
<X className="w-4 h-4 mr-2" />
|
||||
取消
|
||||
</Button>
|
||||
<Button
|
||||
onClick={() => void handleSavePersonVipWebhook()}
|
||||
disabled={personVipWebhookSaving}
|
||||
className="bg-[#38bdac] hover:bg-[#2da396] text-white"
|
||||
>
|
||||
<Save className="w-4 h-4 mr-2" />
|
||||
{personVipWebhookSaving ? '保存中...' : '保存'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* 删除人物二次确认弹窗 */}
|
||||
<Dialog open={!!personToDelete} onOpenChange={(open) => { if (!open) setPersonToDelete(null) }}>
|
||||
<DialogContent showCloseButton={true} className="bg-[#0f2137] border-gray-700 text-white max-w-md p-4 gap-3">
|
||||
|
||||
@@ -152,7 +152,6 @@ interface VipMember {
|
||||
mbti?: string | null
|
||||
vipRole?: string | null
|
||||
vipSort?: number | null
|
||||
webhookUrl?: string | null
|
||||
/** 首页超级个体卡片点击次数(/api/db/vip-members 聚合 user_tracks) */
|
||||
clickCount?: number | null
|
||||
/** 绑定人物后的去重获客人数 */
|
||||
@@ -826,11 +825,6 @@ export function UsersPage() {
|
||||
const [vipRoleModalMember, setVipRoleModalMember] = useState<VipMember | null>(null)
|
||||
const [vipRoleInput, setVipRoleInput] = useState('')
|
||||
const [vipRoleSaving, setVipRoleSaving] = useState(false)
|
||||
const [showVipWebhookModal, setShowVipWebhookModal] = useState(false)
|
||||
const [vipWebhookModalMember, setVipWebhookModalMember] = useState<VipMember | null>(null)
|
||||
const [vipWebhookInput, setVipWebhookInput] = useState('')
|
||||
const [vipWebhookSaving, setVipWebhookSaving] = useState(false)
|
||||
|
||||
const VIP_ROLE_PRESETS = ['创业者', '资源整合者', '技术达人', '投资人', '产品经理', '流量操盘手']
|
||||
|
||||
const openVipRoleModal = (member: VipMember) => {
|
||||
@@ -839,12 +833,6 @@ export function UsersPage() {
|
||||
setShowVipRoleModal(true)
|
||||
}
|
||||
|
||||
const openVipWebhookModal = (member: VipMember) => {
|
||||
setVipWebhookModalMember(member)
|
||||
setVipWebhookInput((member.webhookUrl || '').trim())
|
||||
setShowVipWebhookModal(true)
|
||||
}
|
||||
|
||||
const handleSetVipRole = async (value: string) => {
|
||||
const trimmed = value.trim()
|
||||
if (!vipRoleModalMember) return
|
||||
@@ -873,34 +861,6 @@ export function UsersPage() {
|
||||
}
|
||||
}
|
||||
|
||||
const handleSetVipWebhook = async () => {
|
||||
if (!vipWebhookModalMember) return
|
||||
const val = vipWebhookInput.trim()
|
||||
if (val && !/^https?:\/\//i.test(val)) {
|
||||
toast.error('Webhook 地址需以 http/https 开头')
|
||||
return
|
||||
}
|
||||
setVipWebhookSaving(true)
|
||||
try {
|
||||
const res = await put<{ success?: boolean; error?: string }>('/api/db/vip-members/webhook', {
|
||||
userId: vipWebhookModalMember.id,
|
||||
webhookUrl: val,
|
||||
})
|
||||
if (!res?.success) {
|
||||
toast.error(res?.error || '保存飞书群 Webhook 失败')
|
||||
return
|
||||
}
|
||||
toast.success(val ? '已保存该超级个体的飞书群 Webhook' : '已清空该超级个体的飞书群 Webhook')
|
||||
setShowVipWebhookModal(false)
|
||||
setVipWebhookModalMember(null)
|
||||
await loadVipMembers()
|
||||
} catch {
|
||||
toast.error('保存飞书群 Webhook 失败')
|
||||
} finally {
|
||||
setVipWebhookSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
const [showVipSortModal, setShowVipSortModal] = useState(false)
|
||||
const [vipSortModalMember, setVipSortModalMember] = useState<VipMember | null>(null)
|
||||
const [vipSortInput, setVipSortInput] = useState('')
|
||||
@@ -2212,6 +2172,11 @@ export function UsersPage() {
|
||||
<p className="text-gray-400 text-sm">
|
||||
展示当前所有有效的超级个体(VIP 用户),用于检查会员信息与排序值。
|
||||
</p>
|
||||
<p className="text-xs text-gray-500">
|
||||
飞书群 Webhook(按人推线索到飞书)已迁至{' '}
|
||||
<span className="text-[#38bdac]">内容管理 → 链接人与事 → @列表</span>
|
||||
,在绑定会员的人物行操作栏配置。
|
||||
</p>
|
||||
<p className="text-xs text-[#38bdac]">
|
||||
提示:按住任意一行即可拖拽排序,释放后将同步更新小程序展示顺序。
|
||||
</p>
|
||||
@@ -2253,8 +2218,7 @@ export function UsersPage() {
|
||||
<TableHead className="text-gray-400 w-16 text-center">头像点击</TableHead>
|
||||
<TableHead className="text-gray-400 w-16 text-center">获客数</TableHead>
|
||||
<TableHead className="text-gray-400 w-20">排序值</TableHead>
|
||||
<TableHead className="text-gray-400 w-36">飞书群</TableHead>
|
||||
<TableHead className="text-gray-400 w-36 text-right">操作</TableHead>
|
||||
<TableHead className="text-gray-400 w-28 text-right">操作</TableHead>
|
||||
</TableRow>
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
@@ -2315,15 +2279,6 @@ export function UsersPage() {
|
||||
<TableCell className="text-gray-300">
|
||||
{m.vipSort ?? index + 1}
|
||||
</TableCell>
|
||||
<TableCell className="text-xs">
|
||||
{m.webhookUrl ? (
|
||||
<span className="text-[#38bdac] truncate block max-w-[180px]" title={m.webhookUrl}>
|
||||
已配置
|
||||
</span>
|
||||
) : (
|
||||
<span className="text-gray-500">未配置</span>
|
||||
)}
|
||||
</TableCell>
|
||||
<TableCell className="text-right text-xs text-gray-300">
|
||||
<div className="inline-flex items-center gap-1.5">
|
||||
<Button
|
||||
@@ -2335,15 +2290,6 @@ export function UsersPage() {
|
||||
>
|
||||
<Tag className="w-3.5 h-3.5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
className="h-7 w-7 px-0 text-[#38bdac] hover:text-[#5fe0cd]"
|
||||
onClick={() => openVipWebhookModal(m)}
|
||||
title="编辑飞书群Webhook"
|
||||
>
|
||||
<Edit3 className="w-3.5 h-3.5" />
|
||||
</Button>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
@@ -2454,38 +2400,6 @@ export function UsersPage() {
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
{/* 设置超级个体飞书群 Webhook */}
|
||||
<Dialog open={showVipWebhookModal} onOpenChange={(open) => { setShowVipWebhookModal(open); if (!open) setVipWebhookModalMember(null) }}>
|
||||
<DialogContent className="bg-[#0f2137] border-gray-700 text-white max-w-xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="text-white flex items-center gap-2">
|
||||
<Edit3 className="w-5 h-5 text-[#38bdac]" />
|
||||
设置飞书群 Webhook — {vipWebhookModalMember?.name}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4 py-4">
|
||||
<Label className="text-gray-300 text-sm">VOX Webhook 地址(留空即清空)</Label>
|
||||
<Input
|
||||
className="bg-[#0a1628] border-gray-700 text-white"
|
||||
placeholder="https://open.feishu.cn/open-apis/bot/v2/hook/..."
|
||||
value={vipWebhookInput}
|
||||
onChange={(e) => setVipWebhookInput(e.target.value)}
|
||||
/>
|
||||
<p className="text-xs text-gray-500">
|
||||
当用户点击该超级个体头像并提交链接时,线索将优先推送到这里配置的飞书群。
|
||||
</p>
|
||||
</div>
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => setShowVipWebhookModal(false)} className="border-gray-600 text-gray-300 hover:bg-gray-700/50 bg-transparent">
|
||||
<X className="w-4 h-4 mr-2" />取消
|
||||
</Button>
|
||||
<Button onClick={handleSetVipWebhook} disabled={vipWebhookSaving} className="bg-[#38bdac] hover:bg-[#2da396] text-white">
|
||||
<Save className="w-4 h-4 mr-2" />{vipWebhookSaving ? '保存中...' : '保存'}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<Dialog open={showUserModal} onOpenChange={setShowUserModal}>
|
||||
<DialogContent className="bg-[#0f2137] border-gray-700 text-white max-w-lg">
|
||||
<DialogHeader><DialogTitle className="text-white flex items-center gap-2">{editingUser ? <Edit3 className="w-5 h-5 text-[#38bdac]" /> : <UserPlus className="w-5 h-5 text-[#38bdac]" />}{editingUser ? '编辑用户' : '添加用户'}</DialogTitle></DialogHeader>
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
# 以下由「部署管理器」根据项目配置在保存时自动生成/更新
|
||||
# 根据「构建命令」解析编译目标;可在项目表单中修改 buildCmd
|
||||
FROM golang:1.25 AS builder
|
||||
|
||||
WORKDIR /src
|
||||
@@ -7,7 +9,6 @@ RUN go mod download
|
||||
|
||||
COPY . .
|
||||
|
||||
# Build server binary
|
||||
RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -ldflags="-s -w" -o /out/soul-api ./cmd/server
|
||||
|
||||
FROM alpine:3.19
|
||||
@@ -18,17 +19,16 @@ RUN apk add --no-cache ca-certificates tzdata wget && \
|
||||
addgroup -S app && adduser -S -G app app
|
||||
|
||||
COPY --from=builder /out/soul-api /app/soul-api
|
||||
COPY certs/ /app/certs/
|
||||
|
||||
# Runtime directories used by the app
|
||||
# 若需要微信支付证书,创建 certs/ 后取消下行注释
|
||||
# COPY certs/ /app/certs/
|
||||
|
||||
RUN mkdir -p /app/uploads /app/log && chown -R app:app /app
|
||||
|
||||
ENV APP_ENV=production
|
||||
ENV GIN_MODE=release
|
||||
ENV PORT=8080
|
||||
ENV UPLOAD_DIR=/app/uploads
|
||||
ENV WECHAT_CERT_PATH=/app/certs/apiclient_cert.pem
|
||||
ENV WECHAT_KEY_PATH=/app/certs/apiclient_key.pem
|
||||
|
||||
EXPOSE 8080
|
||||
|
||||
|
||||
@@ -1,56 +1,8 @@
|
||||
# soul-api Docker 部署说明
|
||||
# 以下由「部署管理器」根据项目配置在保存时自动生成/更新
|
||||
# 测试环境soul · deploy
|
||||
|
||||
## 1) 打包运行后必需的文件和目录
|
||||
- **.env**:Compose 变量(`COMPOSE_PROJECT`、宿主机上传/日志目录、网关镜像等),与部署管理器远端流程一致。
|
||||
- **docker-compose.bg.yml** / **nginx.bg.conf**:蓝绿 + 网关;容器端口 **8080**,健康检查 **/health**。
|
||||
- **Dockerfile**(`deploy/Dockerfile`):由表单「构建命令」解析 `go build -o` 目标。
|
||||
|
||||
### 必需文件
|
||||
- `soul-api` 可执行文件(Docker 镜像内已包含)
|
||||
- `.env`(或等价环境变量;本方案使用 `deploy/app.env`)
|
||||
- `certs/apiclient_cert.pem`
|
||||
- `certs/apiclient_key.pem`
|
||||
|
||||
### 必需目录
|
||||
- `uploads/`:上传文件持久化目录
|
||||
- `log/`:订单同步等日志目录
|
||||
|
||||
> 说明:`/app/uploads`、`/app/log` 是容器内目录;本方案已挂载到服务器目录:
|
||||
> - `/www/wwwroot/self/soul-dev/uploads -> /app/uploads`
|
||||
> - `/www/wwwroot/self/soul-dev/log -> /app/log`
|
||||
|
||||
## 2) 本地/服务器快速启动
|
||||
|
||||
在 `soul-api/deploy` 目录下执行:
|
||||
|
||||
1. 复制环境文件:
|
||||
- `cp app.env.example app.env`
|
||||
- 按实际环境填写 `DB_DSN`、微信和管理端密钥
|
||||
2. 启动:
|
||||
- `docker compose up -d --build`
|
||||
3. 查看状态:
|
||||
- `docker compose ps`
|
||||
- `docker compose logs -f app`
|
||||
4. 健康检查:
|
||||
- `http://服务器IP:APP_PORT/health`
|
||||
|
||||
## 3) 给宝塔的推荐方式
|
||||
|
||||
- 方式 A(推荐):在宝塔 Docker/Compose 项目里直接使用 `deploy/docker-compose.yml`
|
||||
- 方式 B:先 `docker build -f deploy/Dockerfile -t soul-api:latest ..`,再在宝塔按镜像创建容器
|
||||
|
||||
## 4) 目录结构(deploy)
|
||||
|
||||
- `deploy/Dockerfile`:多阶段构建 Go 应用镜像
|
||||
- `deploy/docker-compose.yml`:单容器运行与数据卷持久化
|
||||
- `deploy/app.env.example`:环境变量模板
|
||||
|
||||
## 5) 离线镜像部署(不走远端拉取)
|
||||
|
||||
你可以把本地导出的 `deploy/soul-api_latest.tar` 上传到服务器,然后:
|
||||
|
||||
1. 导入镜像:
|
||||
- `docker load -i soul-api_latest.tar`
|
||||
2. 准备配置:
|
||||
- 把 `deploy/app.env` 放到服务器同目录(或按需改 `env_file`)
|
||||
3. 启动:
|
||||
- `docker compose up -d --no-build`
|
||||
|
||||
> 这种方式不会再从 Docker Hub 拉取 `soul-api` 业务镜像;直接使用你导入的本地镜像运行。
|
||||
修改构建配置后请再次保存项目以同步本目录。
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
# 以下由「部署管理器」根据项目配置在保存时自动生成/更新
|
||||
version: "3.8"
|
||||
|
||||
services:
|
||||
@@ -5,13 +6,13 @@ services:
|
||||
build:
|
||||
context: ..
|
||||
dockerfile: deploy/Dockerfile
|
||||
image: soul-api:latest
|
||||
container_name: soul-api
|
||||
image: soul-dev:latest
|
||||
container_name: soul-dev
|
||||
restart: unless-stopped
|
||||
env_file:
|
||||
- ./app.env
|
||||
ports:
|
||||
- "${APP_PORT:-8080}:${PORT:-8080}"
|
||||
- "${APP_PORT:-9092}:${PORT:-8080}"
|
||||
volumes:
|
||||
- "${HOST_UPLOAD_DIR:-./data/uploads}:/app/uploads"
|
||||
- "${HOST_LOG_DIR:-./data/log}:/app/log"
|
||||
|
||||
Reference in New Issue
Block a user