更新开发文档,强调接口路径必须按使用方区分,禁止通用路径混用。新增小程序分享功能,统一使用推荐码,确保用户体验一致性。

This commit is contained in:
Alex-larget
2026-02-25 11:47:36 +08:00
parent 8e4d61e22b
commit 52c5a8abab
145 changed files with 20844 additions and 30 deletions

View File

@@ -0,0 +1,54 @@
# miniprogram 功能还原分析报告
## 一、对比结论
| 项目 | miniprogram甲方 | miniprogram2你写的 |
|------|-------------------|------------------------|
| 页面分享 | 仅 read、referral 有 | 几乎所有页面都有 |
| scene 解析 | 无 | 有 utils/scene.js |
| 推荐码获取 | 分散userInfo?.referralCode 等) | 统一 app.getMyReferralCode() |
| 书籍 API | /api/book/all-chapters | /api/miniprogram/book/all-chapters |
| 特有页面 | vip、member-detail | scan、profile-edit |
## 二、已完成的还原项
### 1. 基础能力app.js + utils/scene.js
- **新增** `utils/scene.js`:扫码 scene 参数编解码,支持 `mid``id``ref`
- **app.js**
- 引入 `parseScene``handleReferralCode` 支持 `options.scene` 解析
- 新增 `getMyReferralCode()`:统一获取邀请码
- 新增 `getSectionMid(sectionId)`:根据 id 查 mid
- `loadBookData` 改为 `/api/miniprogram/book/all-chapters`
### 2. 页面分享onShareAppMessage
已为以下页面补充分享,路径统一带 `ref` 参数:
- index、chapters、match、my
- read、referral原有已统一用 getMyReferralCode
- search、settings、purchases、privacy
- withdraw-records、addresses、addresses/edit
- agreement、about、vip、member-detail
### 3. read.js 分享逻辑
- 使用 `app.getMyReferralCode()` 替代 `userInfo?.referralCode || wx.getStorageSync('referralCode')`
- 保持 `onShareAppMessage``onShareTimeline` 行为不变
### 4. API 路径修正
- `app.loadBookData``/api/book/all-chapters``/api/miniprogram/book/all-chapters`
- `index.loadBookData``loadFeaturedFromServer``loadLatestChapters`:同上
- `chapters.loadDailyChapters`:同上
## 三、未改动项(保留甲方逻辑)
- **vip 相关接口**`/api/vip/members``/api/vip/status``/api/vip/profile` 仍为原路径,未改为 `/api/miniprogram/*`(若 soul-api 无对应 miniprogram 接口,需后端补充)
- **页面结构**:保留 vip、member-detail未引入 scan、profile-edit
## 四、后续建议
1. **soul-api 路由**:确认 `/api/miniprogram/book/all-chapters` 已注册;若 vip 需在小程序使用,建议在 miniprogram 组下增加等价接口。
2. **referral.js**:检查是否已使用 `app.getMyReferralCode()`,若仍用旧方式可统一替换。
3. **read.js 的 mid 支持**miniprogram2 的 read 支持 `mid` 参数(便于扫码直达),若需要可在 miniprogram 的 read 中补充 `sectionMid``getShareConfig` 的 mid 逻辑。

View File

@@ -3,6 +3,8 @@
* 开发: 卡若
*/
const { parseScene } = require('./utils/scene.js')
App({
globalData: {
// API基础地址 - 连接真实后端
@@ -77,11 +79,17 @@ App({
this.handleReferralCode(options)
},
// 处理推荐码绑定
// 处理推荐码绑定:官方以 options.scene 接收扫码参数(可同时带 mid/id + ref与 utils/scene 解析闭环
handleReferralCode(options) {
const query = options?.query || {}
const refCode = query.ref || query.referralCode
let refCode = query.ref || query.referralCode
const sceneStr = (options && (typeof options.scene === 'string' ? options.scene : '')) || ''
if (sceneStr) {
const parsed = parseScene(sceneStr)
if (parsed.mid) this.globalData.initialSectionMid = parsed.mid
if (parsed.id) this.globalData.initialSectionId = parsed.id
if (parsed.ref) refCode = parsed.ref
}
if (refCode) {
console.log('[App] 检测到推荐码:', refCode)
@@ -156,6 +164,22 @@ App({
}
},
// 根据业务 id 从 bookData 查 mid用于跳转
getSectionMid(sectionId) {
const list = this.globalData.bookData || []
const ch = list.find(c => c.id === sectionId)
return ch?.mid || 0
},
// 获取当前用户的邀请码(用于分享带 ref未登录返回空字符串
getMyReferralCode() {
const user = this.globalData.userInfo
if (!user) return ''
if (user.referralCode) return user.referralCode
if (user.id) return 'SOUL' + String(user.id).toUpperCase().slice(-6)
return ''
},
// 获取系统信息
getSystemInfo() {
try {
@@ -200,7 +224,7 @@ App({
}
// 从服务器获取最新数据
const res = await this.request('/api/book/all-chapters')
const res = await this.request('/api/miniprogram/book/all-chapters')
if (res && (res.data || res.chapters)) {
const chapters = res.data || res.chapters || []
this.globalData.bookData = chapters

View File

@@ -77,5 +77,13 @@ Page({
// 返回
goBack() {
wx.navigateBack()
},
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - 关于',
path: ref ? `/pages/about/about?ref=${ref}` : '/pages/about/about'
}
}
})

View File

@@ -119,5 +119,13 @@ Page({
// 返回
goBack() {
wx.navigateBack()
},
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - 地址管理',
path: ref ? `/pages/addresses/addresses?ref=${ref}` : '/pages/addresses/addresses'
}
}
})

View File

@@ -197,5 +197,13 @@ Page({
// 返回
goBack() {
wx.navigateBack()
},
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - 编辑地址',
path: ref ? `/pages/addresses/edit?ref=${ref}` : '/pages/addresses/edit'
}
}
})

View File

@@ -17,5 +17,13 @@ Page({
goBack() {
wx.navigateBack()
},
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - 用户协议',
path: ref ? `/pages/agreement/agreement?ref=${ref}` : '/pages/agreement/agreement'
}
}
})

View File

@@ -217,7 +217,7 @@ Page({
async loadTotalFromServer() {
try {
const res = await app.request({ url: '/api/book/all-chapters', silent: true })
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
if (res && res.total) {
this.setData({ totalSections: res.total })
}
@@ -270,7 +270,7 @@ Page({
async loadDailyChapters() {
try {
const res = await app.request({ url: '/api/book/all-chapters', silent: true })
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
const chapters = (res && res.data) || (res && res.chapters) || []
const daily = chapters
.filter(c => (c.sectionOrder || c.sort_order || 0) > 62)
@@ -294,5 +294,13 @@ Page({
// 跳转到搜索页
goToSearch() {
wx.navigateTo({ url: '/pages/search/search' })
},
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - 目录',
path: ref ? `/pages/chapters/chapters?ref=${ref}` : '/pages/chapters/chapters'
}
}
})

View File

@@ -149,7 +149,7 @@ Page({
// 从服务端获取精选推荐加权算法阅读量50% + 时效30% + 付款率20%)和最新更新
async loadFeaturedFromServer() {
try {
const res = await app.request({ url: '/api/book/all-chapters', silent: true })
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
const chapters = (res && res.data) ? res.data : (res && res.chapters) ? res.chapters : []
let featured = (res && res.featuredSections) ? res.featuredSections : []
// 服务端未返回精选时从前端按更新时间取前3条有效章节作为回退
@@ -199,7 +199,7 @@ Page({
async loadBookData() {
try {
const res = await app.request({ url: '/api/book/all-chapters', silent: true })
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
if (res && (res.data || res.chapters)) {
const chapters = res.data || res.chapters || []
this.setData({
@@ -254,7 +254,7 @@ Page({
async loadLatestChapters() {
try {
const res = await app.request({ url: '/api/book/all-chapters', silent: true })
const res = await app.request({ url: '/api/miniprogram/book/all-chapters', silent: true })
const chapters = (res && res.data) || (res && res.chapters) || []
const latest = chapters
.filter(c => (c.sectionOrder || c.sort_order || 0) > 62)
@@ -288,5 +288,13 @@ Page({
await this.initData()
this.updateUserStatus()
wx.stopPullDownRefresh()
},
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - 真实商业故事',
path: ref ? `/pages/index/index?ref=${ref}` : '/pages/index/index'
}
}
})

View File

@@ -659,5 +659,13 @@ Page({
},
// 阻止事件冒泡
preventBubble() {}
preventBubble() {},
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - 找伙伴',
path: ref ? `/pages/match/match?ref=${ref}` : '/pages/match/match'
}
}
})

View File

@@ -87,5 +87,14 @@ Page({
goToMatch() { wx.switchTab({ url: '/pages/match/match' }) },
goToVip() { wx.navigateTo({ url: '/pages/vip/vip' }) },
goBack() { wx.navigateBack() }
goBack() { wx.navigateBack() },
onShareAppMessage() {
const ref = app.getMyReferralCode()
const id = this.data.member?.id
return {
title: 'Soul创业派对 - 创业者详情',
path: id && ref ? `/pages/member-detail/member-detail?id=${id}&ref=${ref}` : id ? `/pages/member-detail/member-detail?id=${id}` : ref ? `/pages/member-detail/member-detail?ref=${ref}` : '/pages/member-detail/member-detail'
}
}
})

View File

@@ -711,5 +711,13 @@ Page({
},
// 阻止冒泡
stopPropagation() {}
stopPropagation() {},
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - 我的',
path: ref ? `/pages/my/my?ref=${ref}` : '/pages/my/my'
}
}
})

View File

@@ -17,5 +17,13 @@ Page({
goBack() {
wx.navigateBack()
},
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - 隐私政策',
path: ref ? `/pages/privacy/privacy?ref=${ref}` : '/pages/privacy/privacy'
}
}
})

View File

@@ -63,5 +63,13 @@ Page({
wx.navigateTo({ url: `/pages/read/read?id=${id}` })
},
goBack() { wx.navigateBack() }
goBack() { wx.navigateBack() },
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - 购买记录',
path: ref ? `/pages/purchases/purchases?ref=${ref}` : '/pages/purchases/purchases'
}
}
})

View File

@@ -454,11 +454,10 @@ Page({
})
},
// 分享到微信 - 自动带分享人ID
// 分享到微信 - 自动带分享人ID(统一使用 app.getMyReferralCode
onShareAppMessage() {
const { section, sectionId } = this.data
const userInfo = app.globalData.userInfo
const referralCode = userInfo?.referralCode || wx.getStorageSync('referralCode') || ''
const ref = app.getMyReferralCode()
// 分享标题优化
const shareTitle = section?.title
@@ -467,7 +466,7 @@ Page({
return {
title: shareTitle,
path: `/pages/read/read?id=${sectionId}${referralCode ? '&ref=' + referralCode : ''}`,
path: `/pages/read/read?id=${sectionId}${ref ? '&ref=' + ref : ''}`,
imageUrl: '/assets/share-cover.png' // 可配置分享封面图
}
},
@@ -475,12 +474,11 @@ Page({
// 分享到朋友圈
onShareTimeline() {
const { section, sectionId } = this.data
const userInfo = app.globalData.userInfo
const referralCode = userInfo?.referralCode || ''
const ref = app.getMyReferralCode()
return {
title: `${section?.title || 'Soul创业派对'} - 来自派对房的真实故事`,
query: `id=${sectionId}${referralCode ? '&ref=' + referralCode : ''}`
query: `id=${sectionId}${ref ? '&ref=' + ref : ''}`
}
},

View File

@@ -877,12 +877,13 @@ Page({
})
},
// 分享 - 带推荐码
// 分享 - 带推荐码(优先用页面数据,空时用 app.getMyReferralCode
onShareAppMessage() {
console.log('[Referral] 分享给好友,推荐码:', this.data.referralCode)
const ref = this.data.referralCode || app.getMyReferralCode()
console.log('[Referral] 分享给好友,推荐码:', ref)
return {
title: 'Soul创业派对 - 来自派对房的真实商业故事',
path: `/pages/index/index?ref=${this.data.referralCode}`
path: ref ? `/pages/index/index?ref=${ref}` : '/pages/index/index'
// 不设置 imageUrl使用小程序默认截图
// 如需自定义图片,请将图片放在 /assets/ 目录并配置路径
}
@@ -890,10 +891,11 @@ Page({
// 分享到朋友圈
onShareTimeline() {
console.log('[Referral] 分享到朋友圈,推荐码:', this.data.referralCode)
const ref = this.data.referralCode || app.getMyReferralCode()
console.log('[Referral] 分享到朋友圈,推荐码:', ref)
return {
title: `Soul创业派对 - 62个真实商业案例`,
query: `ref=${this.data.referralCode}`
query: ref ? `ref=${ref}` : ''
// 不设置 imageUrl使用小程序默认截图
}
},

View File

@@ -105,5 +105,13 @@ Page({
// 返回上一页
goBack() {
wx.navigateBack()
},
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - 搜索',
path: ref ? `/pages/search/search?ref=${ref}` : '/pages/search/search'
}
}
})

View File

@@ -493,5 +493,13 @@ Page({
// 跳转到地址管理页
goToAddresses() {
wx.navigateTo({ url: '/pages/addresses/addresses' })
},
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - 设置',
path: ref ? `/pages/settings/settings?ref=${ref}` : '/pages/settings/settings'
}
}
})

View File

@@ -128,5 +128,13 @@ Page({
} catch (e) { wx.showToast({ title: '保存失败', icon: 'none' }) }
},
goBack() { wx.navigateBack() }
goBack() { wx.navigateBack() },
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - VIP会员',
path: ref ? `/pages/vip/vip?ref=${ref}` : '/pages/vip/vip'
}
}
})

View File

@@ -119,5 +119,13 @@ Page({
wx.hideLoading()
wx.showToast({ title: '网络异常,请重试', icon: 'none' })
}
},
onShareAppMessage() {
const ref = app.getMyReferralCode()
return {
title: 'Soul创业派对 - 提现记录',
path: ref ? `/pages/withdraw-records/withdraw-records?ref=${ref}` : '/pages/withdraw-records/withdraw-records'
}
}
})

View File

@@ -0,0 +1,45 @@
/**
* Soul创业派对 - 小程序码 scene 参数统一编解码(海报生成 ↔ 扫码解析闭环)
* 官方以 options.scene 接收扫码参数;后端生成码时会把 & 转为 _故解析时同时支持 & 和 _
* scene 同时可带两个参数:章节标识(mid/id) + 推荐人(ref)
*/
const SEP = '_' // 生成时统一用 _与微信实际存储一致且不占 32 字符限制
/**
* 编码:生成海报/分享时组 scene 字符串(同时带 mid或id + ref
* @param {{ mid?: number, id?: string, ref?: string }} opts
* @returns {string} 如 "mid=1_ref=ogpTW5fmXR" 或 "id=1.1_ref=xxx"
*/
function buildScene(opts) {
const parts = []
if (opts.mid != null && opts.mid !== '') parts.push(`mid=${opts.mid}`)
if (opts.id) parts.push(`id=${opts.id}`)
if (opts.ref) parts.push(`ref=${opts.ref}`)
return parts.join(SEP)
}
/**
* 解码:从 options.scene 解析出 mid、id、ref支持 & 或 _ 分隔)
* @param {string} sceneStr 原始 scene可能未 decodeURIComponent
* @returns {{ mid: number, id: string, ref: string }}
*/
function parseScene(sceneStr) {
const res = { mid: 0, id: '', ref: '' }
if (!sceneStr || typeof sceneStr !== 'string') return res
const decoded = decodeURIComponent(String(sceneStr)).trim()
const parts = decoded.split(/[&_]/)
for (const part of parts) {
const eq = part.indexOf('=')
if (eq > 0) {
const k = part.slice(0, eq)
const v = part.slice(eq + 1)
if (k === 'mid') res.mid = parseInt(v, 10) || 0
if (k === 'id' && v) res.id = v
if (k === 'ref' && v) res.ref = v
}
}
return res
}
module.exports = { buildScene, parseScene }