From f00315d7854bdf6ecc0484776f5e99372f739573 Mon Sep 17 00:00:00 2001
From: Alex-larget <33240357+Alex-larget@users.noreply.github.com>
Date: Mon, 9 Mar 2026 16:17:00 +0800
Subject: [PATCH] 1
---
...3-09_devlop与yongxu比较及各角色边界分析.md | 173 ++++++++++++++++++
.../meeting/2026-03-09_合并策略与执行清单.md | 73 ++++++++
.../meeting/2026-03-09_管理端与API合并分析.md | 166 +++++++++++++++++
.cursor/meeting/README.md | 3 +
miniprogram/pages/read/read.js | 115 ++++++++++++
miniprogram/pages/read/read.wxml | 16 +-
miniprogram/pages/read/read.wxss | 6 +
7 files changed, 548 insertions(+), 4 deletions(-)
create mode 100644 .cursor/meeting/2026-03-09_devlop与yongxu比较及各角色边界分析.md
create mode 100644 .cursor/meeting/2026-03-09_合并策略与执行清单.md
create mode 100644 .cursor/meeting/2026-03-09_管理端与API合并分析.md
diff --git a/.cursor/meeting/2026-03-09_devlop与yongxu比较及各角色边界分析.md b/.cursor/meeting/2026-03-09_devlop与yongxu比较及各角色边界分析.md
new file mode 100644
index 00000000..ca8a20c5
--- /dev/null
+++ b/.cursor/meeting/2026-03-09_devlop与yongxu比较及各角色边界分析.md
@@ -0,0 +1,173 @@
+# devlop 与 yongxu 比较及各角色边界分析 - 2026-03-09
+
+> 用户已切换至 devlop 分支。本文档对比老板(devlop)与用户(yongxu)的改动,各角色分析自身边界代码。
+
+---
+
+## 一、分支对比概览
+
+| 项目 | yongxu(你的) | devlop(当前/老板的) |
+|------|----------------|----------------------|
+| 最新 commit | c3de123e | 07e8a43b |
+| 主要改动 | @提及、一键收款、goBackOrToHome、推荐码、mid 优先跳转 | 内容管理深度优化、FindPartnerPage、神射手、RFM、dashboard-stats、推荐码自绑拦截 |
+| 开发文档 | 保留 | **已删除**(07e8a43b chore) |
+
+---
+
+## 二、【小程序开发工程师】边界分析
+
+### 2.1 API 路径合规性 ✅
+
+| 检查项 | 结果 |
+|--------|------|
+| 是否仅调用 `/api/miniprogram/*` | ✅ 是 |
+| 是否调用 `/api/admin/*` 或 `/api/db/*` | ⚠️ **read.js.backup** 调用了 `/api/db/config`(备份文件,非运行代码) |
+
+**结论**:当前运行代码(read.js、app.js、my.js 等)全部使用 `/api/miniprogram/*`,符合边界。
+
+### 2.2 devlop 中老板的改动(miniprogram)
+
+| 文件 | 改动摘要 |
+|------|----------|
+| app.js | 推荐码绑定优化:不能绑定自己的推荐码;新增 `_normalizeReferralCode`;错误处理优化 |
+| app.json | 配置调整 |
+| chapters.js | 章节列表逻辑调整(约 227 行变更) |
+| index.js | 首页逻辑调整(约 31 行) |
+| my.js | 新增 `loadDashboardStats`,调用 `/api/miniprogram/user/dashboard-stats` |
+| read.js | 阅读页逻辑调整(约 270 行变更) |
+| read.wxss | 样式调整(-7 行) |
+| readingTracker.js | 3 行变更 |
+| project.private.config.json | 配置调整 |
+
+### 2.3 yongxu 独有功能(可能被 devlop 覆盖)
+
+| 功能 | 说明 | 当前 devlop 是否保留 |
+|------|------|---------------------|
+| goBackOrToHome | 集中返回逻辑 | ✅ 保留(app.js 有) |
+| baseUrl 真实后端 | soulapi.quwanzhi.com | ✅ 保留 |
+| @提及解析与高亮 | 阅读页 contentSegments、点击添加好友 | ❌ **未保留**(read.js 无 contentSegments/mention) |
+| 一键收款 | 待确认收款、confirm-received | ✅ 保留(my.js 有) |
+| mid 优先跳转 | 分享带 mid、by-mid 接口 | ⚠️ 需核对 read.js onLoad 与 getChapterUrl |
+| 推荐码 visit/bind | 访问记录、绑定 | ✅ 保留并增强(自绑拦截) |
+
+### 2.4 待办
+
+- [ ] **@提及功能缺失**:devlop 的 read.js 无 contentSegments、ckb/lead 点击逻辑,需从 yongxu 合并或重新实现
+- [ ] 核对 read.js 的 mid 支持(onLoad、getChapterUrl、by-mid)
+- [ ] 删除或归档 read.js.backup(避免误用 /api/db/config)
+
+---
+
+## 三、【管理端开发工程师】边界分析
+
+### 3.1 API 路径合规性 ✅
+
+| 检查项 | 结果 |
+|--------|------|
+| 是否仅调用 `/api/admin/*`、`/api/db/*`、`/api/orders` | ✅ 是 |
+| 是否调用 `/api/miniprogram/*` | ✅ 否(SettingsPage 注释中提及为文档说明,非调用) |
+
+### 3.2 devlop 新增/变更的接口调用
+
+| 接口 | 页面 | 后端是否注册 |
+|------|------|-------------|
+| /api/admin/dashboard/overview | DashboardPage | ✅ |
+| /api/admin/shensheshou/query | UserDetailModal | ✅ |
+| /api/admin/shensheshou/enrich | UserDetailModal | ✅ |
+| /api/admin/shensheshou/ingest | UserDetailModal | ✅ |
+| /api/db/match-records?stats=true | CKBStatsTab | ✅ |
+| /api/db/ckb-plan-stats | CKBStatsTab | ✅ |
+| /api/db/match-pool-counts | MatchPoolTab | ✅ |
+| /api/db/users/rfm | UsersPage | ✅ |
+| /api/db/users/journey-stats | UsersPage | ✅ |
+| /api/db/user-rules | UsersPage | ✅ |
+| /api/db/persons | ContentPage | ✅ |
+| /api/db/link-tags | ContentPage | ✅ |
+| /api/db/book?action=section-orders | ContentPage | ✅ |
+| /api/db/book?action=read | ContentPage | ✅ |
+| /api/db/config/full?key=article_ranking_weights 等 | ContentPage | ✅ |
+
+**结论**:管理端新增接口均在后端 router 中注册,无 404 风险。
+
+### 3.3 devlop 新增页面
+
+| 页面 | 路由 | 说明 |
+|------|------|------|
+| FindPartnerPage | /find-partner | 找伙伴管理(含 CKB 配置、匹配池、资源对接等 Tab) |
+| ChaptersPage | /chapters | 章节管理(若为新增) |
+| RichEditor | 组件 | 富文本编辑 |
+
+### 3.4 待办
+
+- [ ] 联调验证:Dashboard、FindPartner、ContentPage、UsersPage(RFM、user-rules、journey-stats)是否正常
+
+---
+
+## 四、【后端开发】边界分析
+
+### 4.1 路由分组合规性 ✅
+
+| 路由组 | 前缀 | 使用方 | 状态 |
+|--------|------|--------|------|
+| miniprogram | /api/miniprogram/* | 小程序 | ✅ |
+| admin | /api/admin/* | 管理端 | ✅ |
+| db | /api/db/* | 管理端 | ✅ |
+
+### 4.2 devlop 新增/变更的 handler
+
+| Handler | 路由 | 用途 |
+|---------|------|------|
+| AdminDashboardOverview | GET /api/admin/dashboard/overview | 仪表盘概览 |
+| AdminShensheShouQuery | GET /api/admin/shensheshou/query | 神射手查询 |
+| AdminShensheShouIngest | POST /api/admin/shensheshou/ingest | 神射手入库 |
+| AdminShensheShouEnrich | POST /api/admin/shensheshou/enrich | 神射手 enrich |
+| UserDashboardStatsGet | GET /api/miniprogram/user/dashboard-stats | 小程序「我的」阅读统计 |
+| DBBookAction | 扩展 | action=section-orders、read 等 |
+| DBPersonList/Save/Delete | /api/db/persons | 人物管理 |
+| DBLinkTagList/Save/Delete | /api/db/link-tags | 链接标签 |
+| DBUserRulesList/Action | /api/db/user-rules | 用户规则 |
+| DBUsersRFM | GET /api/db/users/rfm | RFM 估值 |
+| DBUsersJourneyStats | GET /api/db/users/journey-stats | 用户旅程统计 |
+| DBMatchPoolCounts | GET /api/db/match-pool-counts | 匹配池统计 |
+| CKBPlanStats | GET /api/db/ckb-plan-stats | CKB 计划统计 |
+
+### 4.3 待确认
+
+| 项目 | 说明 |
+|------|------|
+| /api/orders 鉴权 | 仍在 api 根下,未经过 AdminAuth,存在未授权访问风险 |
+| 开发文档删除 | 07e8a43b 删除了开发文档目录,若需保留需从 yongxu 或历史恢复 |
+
+---
+
+## 五、合并建议
+
+### 5.1 若需将 yongxu 的改动合入 devlop
+
+1. **小程序**:`git cherry-pick` 或手工合并关键提交:
+ - @提及(若 devlop 已覆盖需确认)
+ - 一键收款(若 devlop 已覆盖需确认)
+ - 其他你独有的优化
+
+2. **管理端**:devlop 已大幅领先,无需从 yongxu 合并管理端代码
+
+3. **后端**:devlop 已包含更多 handler,yongxu 若有 soul-api 独有改动需手工核对
+
+4. **开发文档**:若需保留,可从 yongxu 或历史 commit 恢复 `开发文档/` 目录
+
+### 5.2 冲突处理优先级
+
+- 冲突时以 **devlop 为主**,再手工补回 yongxu 中你确认必须保留的功能
+
+---
+
+## 六、会议决议
+
+1. **小程序**:边界合规;需核对 @提及、一键收款、mid 在 devlop 中是否完整
+2. **管理端**:边界合规;新增接口均有对应后端
+3. **后端**:路由分组正确;/api/orders 鉴权待补
+4. **开发文档**:若需保留,可从 yongxu 恢复
+
+---
+
+*报告生成时间:2026-03-09 | 基于 devlop 分支与 yongxu 比较*
diff --git a/.cursor/meeting/2026-03-09_合并策略与执行清单.md b/.cursor/meeting/2026-03-09_合并策略与执行清单.md
new file mode 100644
index 00000000..3666cd51
--- /dev/null
+++ b/.cursor/meeting/2026-03-09_合并策略与执行清单.md
@@ -0,0 +1,73 @@
+# devlop + yongxu 合并策略 - 2026-03-09
+
+> 以 devlop 为基准,补入 yongxu 小程序侧缺失功能。管理端、后端以 devlop 为主(已领先)。
+
+---
+
+## 一、合并原则
+
+| 原则 | 说明 |
+|------|------|
+| 以 devlop 为基准 | 老板的改动(内容管理、FindPartner、神射手、RFM、dashboard-stats 等)全部保留 |
+| 补入 yongxu 独有 | 小程序侧 devlop 缺失的功能从 yongxu 合并 |
+| 保留 devlop 优化 | app.js 的推荐码自绑拦截、_normalizeReferralCode 等保留 |
+| 不恢复开发文档 | 开发文档已删除,暂不恢复(可按需从 yongxu 单独拷贝) |
+
+---
+
+## 二、小程序合并清单
+
+### 2.1 app.js ✅ 保留 devlop
+
+- devlop 已有:推荐码自绑拦截、_normalizeReferralCode
+- yongxu 无额外独有改动
+- **操作**:不修改
+
+### 2.2 read.js + read.wxml + read.wxss ⬅️ 补入 @提及
+
+| 项目 | 说明 |
+|------|------|
+| parseLineToSegments | 解析 `{{@userId:昵称}}` 为 segments |
+| contentSegments | 每行 `[{type:'text'\|'mention', text?, userId?, nickname?}]` |
+| onMentionTap | 点击 @ 触发确认弹窗 |
+| _doMentionAddFriend | 登录/资料校验 → POST ckb/lead |
+| read.wxml | 用 contentSegments 渲染,mention 可点击 |
+| read.wxss | 新增 `.mention` 样式 |
+
+**操作**:已执行合并(见下方实施记录)
+
+### 2.3 chapters.js、index.js、my.js
+
+| 文件 | devlop 状态 | yongxu 独有 | 建议 |
+|------|-------------|-------------|------|
+| chapters.js | 227 行差异 | 待核对 | 若 yongxu 有重要优化可手工对比 |
+| index.js | 31 行差异 | ckb/lead 等 | devlop 已有 ckb/lead,基本一致 |
+| my.js | 56 行差异 | 一键收款、dashboard-stats | devlop 已有一键收款 + dashboard-stats |
+
+**操作**:暂不合并,以 devlop 为准。若有具体问题再逐项对比。
+
+### 2.4 其他
+
+| 项目 | 建议 |
+|------|------|
+| read.js.backup | 删除或移出(含 /api/db/config 边界违规) |
+| app.json 拆行 | 可选,第 19 行多页面拆行便于维护 |
+
+---
+
+## 三、管理端、后端
+
+- **管理端**:devlop 已大幅领先,不合并 yongxu。详见 [2026-03-09_管理端与API合并分析.md](2026-03-09_管理端与API合并分析.md)
+- **后端**:devlop 已包含全部新 handler,不合并 yongxu。部署需配置 DB_DSN;CkbLeadRecord 迁移移除需与老板确认
+
+---
+
+## 四、实施记录
+
+- [x] 2026-03-09:合并 read.js 的 @提及(parseLineToSegments、contentSegments、onMentionTap、_doMentionAddFriend)
+- [x] 2026-03-09:合并 read.wxml 的 contentSegments 展示
+- [x] 2026-03-09:补充 read.wxss 的 .mention 样式
+
+---
+
+*策略制定:2026-03-09*
diff --git a/.cursor/meeting/2026-03-09_管理端与API合并分析.md b/.cursor/meeting/2026-03-09_管理端与API合并分析.md
new file mode 100644
index 00000000..b651018f
--- /dev/null
+++ b/.cursor/meeting/2026-03-09_管理端与API合并分析.md
@@ -0,0 +1,166 @@
+# 管理端与 API 合并分析 - 2026-03-09
+
+> devlop vs yongxu 在 soul-admin、soul-api 的差异分析,边界合规性,合并建议。
+
+---
+
+## 一、管理端(soul-admin)
+
+### 1.1 差异概览
+
+| 方向 | 说明 |
+|------|------|
+| **devlop 新增** | FindPartnerPage、ChaptersPage、RichEditor、ContentPage 深度优化、Dashboard 概览、UserDetailModal 神射手、UsersPage RFM/用户规则/旅程统计 |
+| **yongxu** | 管理端无独有改动(yongxu 主要在小程序侧) |
+
+**结论**:管理端以 devlop 为准,无需从 yongxu 合并。
+
+### 1.2 devlop 新增/变更的页面与接口
+
+| 页面/组件 | 路由 | 主要接口 | 后端是否注册 |
+|-----------|------|----------|-------------|
+| FindPartnerPage | /find-partner | /api/db/match-records、ckb-plan-stats、match-pool-counts、ckb-leads、config/full?key=ckb_config | ✅ |
+| ChaptersPage | /chapters | /api/admin/chapters | ✅ |
+| ContentPage | /content | /api/db/book、persons、link-tags、config | ✅ |
+| DashboardPage | /dashboard | /api/admin/dashboard/overview | ✅ |
+| UserDetailModal | - | /api/admin/shensheshou/query、enrich、ingest | ✅ |
+| UsersPage | /users | /api/db/users/rfm、user-rules、users/journey-stats | ✅ |
+| RichEditor | 组件 | - | - |
+
+### 1.3 管理端 API 调用清单(devlop 当前)
+
+| 接口 | 使用页面 | 说明 |
+|------|----------|------|
+| /api/admin/dashboard/overview | DashboardPage | 数据概览 |
+| /api/admin/chapters | ChaptersPage | 章节树 CRUD |
+| /api/admin/shensheshou/* | UserDetailModal | 神射手查询/入库/enrich |
+| /api/admin/distribution/overview | DistributionPage | 分销概览 |
+| /api/admin/withdrawals | WithdrawalsPage、DistributionPage | 提现审核 |
+| /api/admin/orders/refund | OrdersPage、DistributionPage | 订单退款 |
+| /api/admin/referral-settings | ReferralSettingsPage | 推广设置 |
+| /api/admin/settings | SettingsPage | 系统设置 |
+| /api/admin/author-settings | AuthorSettingsPage | 作者设置 |
+| /api/admin/users | AdminUsersPage | 管理员用户 |
+| /api/admin/logout | AdminLayout | 登出 |
+| /api/db/users | 多页 | 用户 CRUD |
+| /api/db/users/rfm | UsersPage | RFM 估值 |
+| /api/db/users/referrals | UsersPage、UserDetailModal | 推荐关系 |
+| /api/db/users/journey-stats | UsersPage | 用户旅程统计 |
+| /api/db/user-rules | UsersPage | 用户规则 |
+| /api/db/book | ContentPage | 内容/章节 CRUD |
+| /api/db/persons | ContentPage | 人物管理 |
+| /api/db/link-tags | ContentPage | 链接标签 |
+| /api/db/config、config/full | 多页 | 配置 |
+| /api/db/match-records | FindPartner、MatchRecords | 匹配记录 |
+| /api/db/match-pool-counts | MatchPoolTab | 匹配池统计 |
+| /api/db/ckb-plan-stats | CKBStatsTab | CKB 计划统计 |
+| /api/db/ckb-leads | CKBConfigPanel | CKB 线索明细 |
+| /api/db/vip-roles | VipRolesPage、SetVipModal | VIP 角色 |
+| /api/db/mentors | MentorsPage | 导师 |
+| /api/db/mentor-consultations | MentorConsultationsPage | 导师预约 |
+| /api/orders | OrdersPage | 订单列表 |
+
+### 1.4 边界合规性 ✅
+
+- 仅调用 `/api/admin/*`、`/api/db/*`、`/api/orders`
+- 未调用 `/api/miniprogram/*`
+
+### 1.5 合并建议
+
+- **不合并**:管理端以 devlop 为准
+- **联调验证**:Dashboard、FindPartner、ContentPage、UsersPage(RFM、user-rules、journey-stats)与后端接口联调
+
+---
+
+## 二、API 后端(soul-api)
+
+### 2.1 差异概览
+
+| 项目 | yongxu | devlop |
+|------|--------|--------|
+| config | CkbLeadAPIKey、DB_DSN 默认本地、SyncOrdersInterval 默认 0 | 移除 CkbLeadAPIKey、DB_DSN 必填(Fatal)、SyncOrdersInterval 默认 5 |
+| database | 迁移 CkbSubmitRecord、CkbLeadRecord | 迁移 UserRule、Person、LinkTag、Chapter;移除 CkbSubmitRecord、CkbLeadRecord 迁移;seedDefaultRules |
+| handler | 基础 handler | 新增 admin_dashboard、admin_shensheshou、db_book 扩展、db_person、db_link_tag、db_user_rules、db_users_rfm、db_users_journey_stats、db_match_pool_counts、db_ckb_plan_stats、db_ckb_leads 等 |
+| router | 基础路由 | 新增上述路由 |
+
+### 2.2 devlop 配置变更(需关注)
+
+| 配置项 | yongxu | devlop | 影响 |
+|--------|--------|--------|------|
+| DB_DSN | 默认 `user:pass@tcp(127.0.0.1:3306)/soul?...` | 未配置则 Fatal 退出 | 部署需显式配置 DB_DSN |
+| CkbLeadAPIKey | 从环境变量读取 | 已移除 | ckb.go 使用硬编码 ckbAPIKey,不影响运行 |
+| SyncOrdersIntervalMinutes | 默认 0 | 默认 5 | 订单对账定时任务每 5 分钟执行 |
+
+### 2.3 devlop 数据库迁移变更
+
+| 项目 | yongxu | devlop |
+|------|--------|--------|
+| CkbSubmitRecord | 迁移 | 移除迁移 |
+| CkbLeadRecord | 迁移 | 移除迁移 |
+| UserRule | - | 新增迁移 + seedDefaultRules |
+| Person | - | 新增迁移 |
+| LinkTag | - | 新增迁移 |
+| Chapter | - | 新增迁移(hot_score_override) |
+
+**说明**:CkbLeadRecord、CkbSubmitRecord 迁移被移除,但 model 与 handler 仍存在。DBCKBLeadList、CKBLead 可能依赖存客宝 API 或本地表——若本地表仍在使用,需确认迁移策略。
+
+### 2.4 路由分组合规性 ✅
+
+| 路由组 | 前缀 | 使用方 | 状态 |
+|--------|------|--------|------|
+| miniprogram | /api/miniprogram/* | 小程序 | ✅ |
+| admin | /api/admin/* | 管理端 | ✅ |
+| db | /api/db/* | 管理端 | ✅ |
+
+### 2.5 待确认项
+
+| 项目 | 说明 |
+|------|------|
+| /api/orders 鉴权 | 仍在 api 根下,未经过 AdminAuth |
+| CkbLeadRecord 表 | 迁移已移除,若 DBCKBLeadList 或 CKBLead 仍写本地表,需恢复迁移或改实现 |
+| router.go 格式 | 第 51 行 admin.GET("/distribution/overview") 缩进异常,建议修正 |
+
+### 2.6 合并建议
+
+- **以 devlop 为准**:API 侧 devlop 已大幅领先
+- **yongxu 无独有 API 改动**:无需从 yongxu 合并
+- **部署注意**:devlop 要求 DB_DSN 必填,需在 .env 中配置
+- **可选恢复**:若 CKB 线索需落本地表,可考虑恢复 CkbLeadRecord 迁移(与老板确认)
+
+---
+
+## 三、管理端 ↔ API 接口对应检查
+
+| 管理端调用 | soul-api 路由 | 状态 |
+|------------|---------------|------|
+| /api/admin/dashboard/overview | admin.GET("/dashboard/overview") | ✅ |
+| /api/admin/chapters | admin.GET/POST/PUT/DELETE("/chapters") | ✅ |
+| /api/admin/shensheshou/query | admin.GET("/shensheshou/query") | ✅ |
+| /api/admin/shensheshou/enrich | admin.POST("/shensheshou/enrich") | ✅ |
+| /api/admin/shensheshou/ingest | admin.POST("/shensheshou/ingest") | ✅ |
+| /api/db/ckb-leads | db.GET("/ckb-leads") | ✅ |
+| /api/db/ckb-plan-stats | db.GET("/ckb-plan-stats") | ✅ |
+| /api/db/match-pool-counts | db.GET("/match-pool-counts") | ✅ |
+| /api/db/users/rfm | db.GET("/users/rfm") | ✅ |
+| /api/db/users/journey-stats | db.GET("/users/journey-stats") | ✅ |
+| /api/db/user-rules | db.GET/POST/PUT/DELETE("/user-rules") | ✅ |
+| /api/db/persons | db.GET/POST/DELETE("/persons") | ✅ |
+| /api/db/link-tags | db.GET/POST/DELETE("/link-tags") | ✅ |
+| /api/db/book?action=section-orders | db.GET("/book") | ✅ |
+| 其他 | 见 router.go | ✅ |
+
+**结论**:管理端调用的接口均在 soul-api 中注册,无 404 风险。
+
+---
+
+## 四、总结
+
+| 端 | 合并策略 | 备注 |
+|----|----------|------|
+| 管理端 | 以 devlop 为准,不合并 | yongxu 无管理端独有改动 |
+| API | 以 devlop 为准,不合并 | 部署需配置 DB_DSN;CkbLeadRecord 迁移移除需确认 |
+| 小程序 | 已补入 @提及 | 见 2026-03-09_合并策略与执行清单.md |
+
+---
+
+*分析完成时间:2026-03-09*
diff --git a/.cursor/meeting/README.md b/.cursor/meeting/README.md
index 5bf6539a..36e90698 100644
--- a/.cursor/meeting/README.md
+++ b/.cursor/meeting/README.md
@@ -65,3 +65,6 @@ YYYY-MM-DD_会议主题.md
| 2026-03-05 | 分支冲突后功能完整性分析 | 产品、后端、管理端、小程序、测试 | [2026-03-05_分支冲突后功能完整性分析.md](2026-03-05_分支冲突后功能完整性分析.md) |
| 2026-03-05 | 超级个体解锁眼睛需求分析 | 产品、小程序 | [2026-03-05_超级个体解锁眼睛需求分析.md](2026-03-05_超级个体解锁眼睛需求分析.md) |
| 2026-03-05 | 文章详情 @某人 高亮与一键加好友方案讨论 | 产品、后端、管理端、小程序、测试 | [2026-03-05_文章详情@某人加好友方案讨论.md](2026-03-05_文章详情@某人加好友方案讨论.md) |
+| 2026-03-09 | devlop 与 yongxu 比较及各角色边界分析 | 产品、后端、管理端、小程序、测试 | [2026-03-09_devlop与yongxu比较及各角色边界分析.md](2026-03-09_devlop与yongxu比较及各角色边界分析.md) |
+| 2026-03-09 | 合并策略与执行清单 | 产品、后端、管理端、小程序、测试 | [2026-03-09_合并策略与执行清单.md](2026-03-09_合并策略与执行清单.md) |
+| 2026-03-09 | 管理端与 API 合并分析 | 产品、后端、管理端、小程序、测试 | [2026-03-09_管理端与API合并分析.md](2026-03-09_管理端与API合并分析.md) |
diff --git a/miniprogram/pages/read/read.js b/miniprogram/pages/read/read.js
index 9862b698..5983c7c1 100644
--- a/miniprogram/pages/read/read.js
+++ b/miniprogram/pages/read/read.js
@@ -8,6 +8,9 @@
* - 引入阅读追踪器(readingTracker)记录阅读进度、时长、是否读完
* - 使用状态机(accessState)规范权限流转
* - 异常统一保守处理,避免误解锁
+ *
+ * 更新: 正文 @某人({{@userId:昵称}})
+ * - contentSegments 解析每行,mention 高亮可点;点击→确认→登录/资料校验→POST /api/miniprogram/ckb/lead
*/
import accessManager from '../../utils/chapterAccessManager'
@@ -16,6 +19,25 @@ const { parseScene } = require('../../utils/scene.js')
const app = getApp()
+// 解析单行中的 {{@userId:昵称}} 为片段数组,用于阅读页 @ 高亮与点击
+function parseLineToSegments(line) {
+ const segments = []
+ const re = /\{\{@([^:]+):(.*?)\}\}/g
+ let lastEnd = 0
+ let m
+ while ((m = re.exec(line)) !== null) {
+ if (m.index > lastEnd) {
+ segments.push({ type: 'text', text: line.slice(lastEnd, m.index) })
+ }
+ segments.push({ type: 'mention', userId: String(m[1]).trim(), nickname: String(m[2] || '').trim() })
+ lastEnd = re.lastIndex
+ }
+ if (lastEnd < line.length) {
+ segments.push({ type: 'text', text: line.slice(lastEnd) })
+ }
+ return segments.length ? segments : [{ type: 'text', text: line }]
+}
+
Page({
data: {
// 系统信息
@@ -32,6 +54,7 @@ Page({
content: '',
previewContent: '',
contentParagraphs: [],
+ contentSegments: [], // 每行解析为 [{type:'text'|'mention', text?, userId?, nickname?}],支持 @ 高亮可点
previewParagraphs: [],
loading: true,
@@ -218,6 +241,7 @@ Page({
const updates = {
content: res.content,
contentParagraphs: lines,
+ contentSegments: lines.map(parseLineToSegments),
previewParagraphs: lines.slice(0, previewCount),
partTitle: res.partTitle || '',
chapterTitle: res.chapterTitle || '',
@@ -245,6 +269,7 @@ Page({
this.setData({
content: cached.content,
contentParagraphs: lines,
+ contentSegments: lines.map(parseLineToSegments),
previewParagraphs: lines.slice(0, previewCount),
shareTimelineTitle: shareTimelineTitle || 'Soul创业派对 - 真实商业故事'
})
@@ -350,6 +375,7 @@ Page({
// 3. 都失败,显示加载中并持续重试
this.setData({
contentParagraphs: ['章节内容加载中...', '正在尝试连接服务器,请稍候...'],
+ contentSegments: [parseLineToSegments('章节内容加载中...'), parseLineToSegments('正在尝试连接服务器,请稍候...')],
previewParagraphs: ['章节内容加载中...']
})
@@ -395,6 +421,7 @@ Page({
content: res.content,
previewContent: lines.slice(0, previewCount).join('\n'),
contentParagraphs: lines,
+ contentSegments: lines.map(parseLineToSegments),
previewParagraphs: lines.slice(0, previewCount),
partTitle: res.partTitle || '',
chapterTitle: sectionTitle,
@@ -420,6 +447,7 @@ Page({
if (currentRetry >= maxRetries) {
this.setData({
contentParagraphs: ['内容加载失败', '请检查网络连接后下拉刷新重试'],
+ contentSegments: [parseLineToSegments('内容加载失败'), parseLineToSegments('请检查网络连接后下拉刷新重试')],
previewParagraphs: ['内容加载失败']
})
return
@@ -497,6 +525,93 @@ Page({
getApp().goBackOrToHome()
},
+ // 点击正文中的 @某人:确认弹窗 → 登录/资料校验 → 调用 ckb/lead 加好友留资
+ onMentionTap(e) {
+ const userId = e.currentTarget.dataset.userId
+ const nickname = (e.currentTarget.dataset.nickname || '').trim() || 'TA'
+ if (!userId) return
+ wx.showModal({
+ title: '添加好友',
+ content: `是否添加 @${nickname} ?`,
+ confirmText: '确定',
+ cancelText: '取消',
+ success: (res) => {
+ if (!res.confirm) return
+ this._doMentionAddFriend(userId, nickname)
+ }
+ })
+ },
+
+ async _doMentionAddFriend(targetUserId, targetNickname) {
+ if (!app.globalData.isLoggedIn || !app.globalData.userInfo) {
+ wx.showModal({
+ title: '提示',
+ content: '请先登录后再添加好友',
+ confirmText: '去登录',
+ cancelText: '取消',
+ success: (res) => {
+ if (res.confirm) wx.switchTab({ url: '/pages/my/my' })
+ }
+ })
+ return
+ }
+ const myUserId = app.globalData.userInfo.id
+ let phone = (app.globalData.userInfo.phone || '').trim()
+ let wechatId = (app.globalData.userInfo.wechatId || app.globalData.userInfo.wechat_id || '').trim()
+ if (!phone && !wechatId) {
+ try {
+ const profileRes = await app.request({ url: `/api/miniprogram/user/profile?userId=${myUserId}`, silent: true })
+ if (profileRes?.success && profileRes.data) {
+ phone = (profileRes.data.phone || '').trim()
+ wechatId = (profileRes.data.wechatId || profileRes.data.wechat_id || '').trim()
+ }
+ } catch (e) {}
+ }
+ if (!phone && !wechatId) {
+ wx.showModal({
+ title: '完善资料',
+ content: '请先填写手机号或微信号,以便对方联系您',
+ confirmText: '去填写',
+ cancelText: '取消',
+ success: (res) => {
+ if (res.confirm) wx.navigateTo({ url: '/pages/profile-edit/profile-edit' })
+ }
+ })
+ return
+ }
+ const leadLastTs = wx.getStorageSync('lead_last_submit_ts') || 0
+ if (Date.now() - leadLastTs < 2 * 60 * 1000) {
+ wx.showToast({ title: '操作太频繁,请2分钟后再试', icon: 'none' })
+ return
+ }
+ wx.showLoading({ title: '提交中...', mask: true })
+ try {
+ const res = await app.request({
+ url: '/api/miniprogram/ckb/lead',
+ method: 'POST',
+ data: {
+ userId: myUserId,
+ phone: phone || undefined,
+ wechatId: wechatId || undefined,
+ name: (app.globalData.userInfo.nickname || '').trim() || undefined,
+ targetUserId,
+ targetNickname: targetNickname || undefined,
+ source: 'article_mention'
+ }
+ })
+ wx.hideLoading()
+ if (res && res.success) {
+ wx.setStorageSync('lead_last_submit_ts', Date.now())
+ wx.showToast({ title: res.message || '提交成功,对方会尽快联系您', icon: 'success' })
+ } else {
+ wx.showToast({ title: (res && res.message) || '提交失败', icon: 'none' })
+ }
+ } catch (e) {
+ wx.hideLoading()
+ wx.showToast({ title: (e && e.message) || '提交失败', icon: 'none' })
+ }
+ },
+
// 分享弹窗
showShare() {
this.setData({ showShareModal: true })
diff --git a/miniprogram/pages/read/read.wxml b/miniprogram/pages/read/read.wxml
index 44b2d21f..c3114cc0 100644
--- a/miniprogram/pages/read/read.wxml
+++ b/miniprogram/pages/read/read.wxml
@@ -42,11 +42,19 @@
-
+
-
- {{item}}
-
+
+
+
+ {{seg.text}}
+ @{{seg.nickname}}
+
+
+
+
+ {{item}}
+
diff --git a/miniprogram/pages/read/read.wxss b/miniprogram/pages/read/read.wxss
index 8b0ecca2..0b1fe466 100644
--- a/miniprogram/pages/read/read.wxss
+++ b/miniprogram/pages/read/read.wxss
@@ -182,6 +182,12 @@
text-align: justify;
}
+/* 正文内 @某人 高亮可点 */
+.paragraph .mention {
+ color: #00CED1;
+ font-weight: 500;
+}
+
.preview {
position: relative;
}