This commit is contained in:
Alex-larget
2026-03-09 16:17:00 +08:00
parent 07e8a43bff
commit f00315d785
7 changed files with 548 additions and 4 deletions

View File

@@ -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、UsersPageRFM、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 已包含更多 handleryongxu 若有 soul-api 独有改动需手工核对
4. **开发文档**:若需保留,可从 yongxu 或历史 commit 恢复 `开发文档/` 目录
### 5.2 冲突处理优先级
- 冲突时以 **devlop 为主**,再手工补回 yongxu 中你确认必须保留的功能
---
## 六、会议决议
1. **小程序**:边界合规;需核对 @提及、一键收款、mid 在 devlop 中是否完整
2. **管理端**:边界合规;新增接口均有对应后端
3. **后端**:路由分组正确;/api/orders 鉴权待补
4. **开发文档**:若需保留,可从 yongxu 恢复
---
*报告生成时间2026-03-09 | 基于 devlop 分支与 yongxu 比较*

View File

@@ -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_DSNCkbLeadRecord 迁移移除需与老板确认
---
## 四、实施记录
- [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*

View File

@@ -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、UsersPageRFM、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_DSNCkbLeadRecord 迁移移除需确认 |
| 小程序 | 已补入 @提及 | 见 2026-03-09_合并策略与执行清单.md |
---
*分析完成时间2026-03-09*

View File

@@ -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) |

View File

@@ -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 })

View File

@@ -42,11 +42,19 @@
<view class="skeleton skeleton-5"></view>
</view>
<!-- 完整内容 - 免费或已购买 -->
<!-- 完整内容 - 免费或已购买(支持 @ 高亮可点) -->
<view class="article" wx:if="{{accessState === 'free' || accessState === 'unlocked_purchased'}}">
<view class="paragraph" wx:for="{{contentParagraphs}}" wx:key="index" wx:if="{{item}}">
{{item}}
<block wx:if="{{contentSegments.length}}">
<view class="paragraph" wx:for="{{contentSegments}}" wx:key="index" wx:if="{{item && item.length}}">
<block wx:for="{{item}}" wx:key="index" wx:for-item="seg">
<text wx:if="{{seg.type === 'text'}}">{{seg.text}}</text>
<text wx:elif="{{seg.type === 'mention'}}" class="mention" bindtap="onMentionTap" data-user-id="{{seg.userId}}" data-nickname="{{seg.nickname}}">@{{seg.nickname}}</text>
</block>
</view>
</block>
<block wx:else>
<view class="paragraph" wx:for="{{contentParagraphs}}" wx:key="index" wx:if="{{item}}">{{item}}</view>
</block>
<!-- 章节导航 -->
<view class="chapter-nav">

View File

@@ -182,6 +182,12 @@
text-align: justify;
}
/* 正文内 @某人 高亮可点 */
.paragraph .mention {
color: #00CED1;
font-weight: 500;
}
.preview {
position: relative;
}