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; }