更新小程序接口路径,统一为 /api/miniprogram/*,确保各页面正确调用 VIP 相关接口。增强分享功能,支持通过 mid 参数分享章节,优化用户体验。调整文档,明确后端待办事项。

This commit is contained in:
Alex-larget
2026-02-25 11:50:07 +08:00
parent 52c5a8abab
commit 1f9eee0fd7
7 changed files with 84 additions and 40 deletions

View File

@@ -31,24 +31,36 @@
- withdraw-records、addresses、addresses/edit
- agreement、about、vip、member-detail
### 3. read.js 分享逻辑
### 3. read.js 分享逻辑与 mid 支持
- 使用 `app.getMyReferralCode()` 替代 `userInfo?.referralCode || wx.getStorageSync('referralCode')`
- 保持 `onShareAppMessage``onShareTimeline` 行为不变
- **mid 支持**onLoad 支持 `options.scene``options.mid`mid 有值无 id 时从 bookData 或 `/api/miniprogram/book/chapter/by-mid/:mid` 解析 id
- 分享 path 优先用 `mid=`(扫码/海报闭环),无则用 `id=`API 返回 `res.mid` 时写入 `sectionMid`
### 4. API 路径修正
- `app.loadBookData``/api/book/all-chapters``/api/miniprogram/book/all-chapters`
- `index.loadBookData``loadFeaturedFromServer``loadLatestChapters`:同上
- `chapters.loadDailyChapters`:同上
- **VIP 接口**`/api/vip/*``/api/miniprogram/vip/*`vip.js、my.js、index.js、member-detail.js
## 三、未改动项(保留甲方逻辑)
## 三、已处理项
- **vip 相关接口**`/api/vip/members``/api/vip/status``/api/vip/profile` 仍为原路径,未改为 `/api/miniprogram/*`(若 soul-api 无对应 miniprogram 接口,需后端补充)
- **vip 相关接口**已改为 `/api/miniprogram/vip/*`members、status、profile符合项目边界。**后端需在 soul-api miniprogram 组下挂对应路由**,可复用现有 handler。
- **页面结构**:保留 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 逻辑。
soul-api 需在 miniprogram 组下增加以下 VIP 路由(可复用现有 handler
| 路径 | 方法 | 用途 |
|------|------|------|
| `/api/miniprogram/vip/status` | GET | 查询用户 VIP 状态 |
| `/api/miniprogram/vip/profile` | GET/POST | 获取/更新 VIP 资料 |
| `/api/miniprogram/vip/members` | GET | 获取 VIP 会员列表(首页超级个体、会员详情) |
## 五、后续建议
1. **soul-api 路由**:确认 `/api/miniprogram/book/all-chapters` 已注册VIP 接口见「四、后端待办」。
2. **referral.js**:已统一使用 `app.getMyReferralCode()` 作为回退。
3. **read.js mid 支持**:已完成。若 soul-api 未提供 `/api/miniprogram/book/chapter/by-mid/:mid`,扫码带 mid 时需依赖 bookData 已加载完成;建议后端补充 by-mid 接口。

View File

@@ -8,7 +8,10 @@ const { parseScene } = require('./utils/scene.js')
App({
globalData: {
// API基础地址 - 连接真实后端
baseUrl: 'https://soul.quwanzhi.com',
// baseUrl: 'https://soulapi.quwanzhi.com',
// baseUrl: 'https://souldev.quwanzhi.com',
baseUrl: 'http://localhost:8080',
// 小程序配置 - 真实AppID
appId: 'wxb8bbb2b10dec74aa',

View File

@@ -120,7 +120,7 @@ Page({
// 优先加载VIP会员
let members = []
try {
const res = await app.request({ url: '/api/vip/members', silent: true })
const res = await app.request({ url: '/api/miniprogram/vip/members', silent: true })
if (res && res.success && res.data) {
members = res.data.filter(u => u.avatar || u.vip_avatar).slice(0, 4).map(u => ({
id: u.id, name: u.vip_name || u.nickname || '会员',

View File

@@ -17,7 +17,7 @@ Page({
async loadMember(id) {
try {
const res = await app.request({ url: `/api/vip/members?id=${id}`, silent: true })
const res = await app.request({ url: `/api/miniprogram/vip/members?id=${id}`, silent: true })
if (res?.success && res.data) {
const d = Array.isArray(res.data) ? res.data[0] : res.data
if (d) { this.setData({ member: this.enrichAndFormat(d), loading: false }); return }

View File

@@ -641,7 +641,7 @@ Page({
const userId = app.globalData.userInfo?.id
if (!userId) return
try {
const res = await app.request({ url: `/api/vip/status?userId=${userId}`, silent: true })
const res = await app.request({ url: `/api/miniprogram/vip/status?userId=${userId}`, silent: true })
if (res?.success) {
this.setData({ isVip: res.data?.isVip, vipExpireDate: res.data?.expireDate || '' })
}

View File

@@ -12,6 +12,7 @@
import accessManager from '../../utils/chapterAccessManager'
import readingTracker from '../../utils/readingTracker'
const { parseScene } = require('../../utils/scene.js')
const app = getApp()
@@ -66,21 +67,53 @@ Page({
isGeneratingPoster: false,
// 免费章节
freeIds: ['preface', 'epilogue', '1.1', 'appendix-1', 'appendix-2', 'appendix-3']
freeIds: ['preface', 'epilogue', '1.1', 'appendix-1', 'appendix-2', 'appendix-3'],
// 章节 mid扫码/海报分享用,便于分享 path 带 mid
sectionMid: null
},
async onLoad(options) {
const { id, ref } = options
// 支持 scene扫码、mid、id、ref
const sceneStr = (options && options.scene) || ''
const parsed = parseScene(sceneStr)
const mid = options.mid ? parseInt(options.mid, 10) : (parsed.mid || app.globalData.initialSectionMid || 0)
let id = options.id || parsed.id || app.globalData.initialSectionId
const ref = options.ref || parsed.ref
if (app.globalData.initialSectionMid) delete app.globalData.initialSectionMid
if (app.globalData.initialSectionId) delete app.globalData.initialSectionId
// mid 有值但无 id 时,从 bookData 或 API 解析 id
if (mid && !id) {
const bookData = app.globalData.bookData || []
const ch = bookData.find(c => c.mid == mid || (c.mid && Number(c.mid) === Number(mid)))
if (ch?.id) {
id = ch.id
} else {
try {
const chRes = await app.request({ url: `/api/miniprogram/book/chapter/by-mid/${mid}`, silent: true })
if (chRes && chRes.id) id = chRes.id
} catch (e) {
console.warn('[Read] by-mid 解析失败:', e)
}
}
}
if (!id) {
wx.showToast({ title: '章节参数缺失', icon: 'none' })
this.setData({ accessState: 'error', loading: false })
return
}
this.setData({
statusBarHeight: app.globalData.statusBarHeight,
navBarHeight: app.globalData.navBarHeight,
sectionId: id,
sectionMid: mid || null,
loading: true,
accessState: 'unknown'
})
// 处理推荐码绑定(异步不阻塞)
if (ref) {
console.log('[Read] 检测到推荐码:', ref)
wx.setStorageSync('referral_code', ref)
@@ -88,7 +121,6 @@ Page({
}
try {
// 【标准流程】1. 拉取最新配置(免费列表、价格)
const config = await accessManager.fetchLatestConfig()
this.setData({
freeIds: config.freeChapters,
@@ -96,7 +128,6 @@ Page({
fullBookPrice: config.prices?.fullbook ?? 9.9
})
// 【标准流程】2. 确定权限状态
const accessState = await accessManager.determineAccessState(id, config.freeChapters)
const canAccess = accessManager.canAccessFullContent(accessState)
@@ -175,14 +206,15 @@ Page({
if (res && res.content) {
const lines = res.content.split('\n').filter(line => line.trim())
const previewCount = Math.ceil(lines.length * 0.2)
this.setData({
const updates = {
content: res.content,
contentParagraphs: lines,
previewParagraphs: lines.slice(0, previewCount),
partTitle: res.partTitle || '',
chapterTitle: res.chapterTitle || ''
})
}
if (res.mid) updates.sectionMid = res.mid
this.setData(updates)
// 如果有权限,标记为已读
if (accessManager.canAccessFullContent(accessState)) {
@@ -454,31 +486,28 @@ Page({
})
},
// 分享到微信 - 自动带分享人ID(统一使用 app.getMyReferralCode
// 分享到微信 - 自动带分享人ID;优先用 mid扫码/海报闭环),无则用 id
onShareAppMessage() {
const { section, sectionId } = this.data
const { section, sectionId, sectionMid } = this.data
const ref = app.getMyReferralCode()
// 分享标题优化
const q = sectionMid ? `mid=${sectionMid}` : `id=${sectionId}`
const shareTitle = section?.title
? `📚 ${section.title.length > 20 ? section.title.slice(0, 20) + '...' : section.title}`
: '📚 Soul创业派对 - 真实商业故事'
return {
title: shareTitle,
path: `/pages/read/read?id=${sectionId}${ref ? '&ref=' + ref : ''}`,
imageUrl: '/assets/share-cover.png' // 可配置分享封面图
path: ref ? `/pages/read/read?${q}&ref=${ref}` : `/pages/read/read?${q}`,
imageUrl: '/assets/share-cover.png'
}
},
// 分享到朋友圈
onShareTimeline() {
const { section, sectionId } = this.data
const { section, sectionId, sectionMid } = this.data
const ref = app.getMyReferralCode()
const q = sectionMid ? `mid=${sectionMid}` : `id=${sectionId}`
return {
title: `${section?.title || 'Soul创业派对'} - 来自派对房的真实故事`,
query: `id=${sectionId}${ref ? '&ref=' + ref : ''}`
query: ref ? `${q}&ref=${ref}` : q
}
},

View File

@@ -33,7 +33,7 @@ Page({
const userId = app.globalData.userInfo?.id
if (!userId) return
try {
const res = await app.request({ url: `/api/vip/status?userId=${userId}`, silent: true })
const res = await app.request({ url: `/api/miniprogram/vip/status?userId=${userId}`, silent: true })
if (res?.success) {
const d = res.data
let expStr = ''
@@ -54,7 +54,7 @@ Page({
async loadProfile(userId) {
try {
const res = await app.request(`/api/vip/profile?userId=${userId}`)
const res = await app.request(`/api/miniprogram/vip/profile?userId=${userId}`)
if (res?.success) this.setData({ profile: res.data })
} catch (e) { console.log('[VIP] 资料加载失败', e) }
},
@@ -120,7 +120,7 @@ Page({
if (!userId) return
const p = this.data.profile
try {
const res = await app.request('/api/vip/profile', {
const res = await app.request('/api/miniprogram/vip/profile', {
method: 'POST', data: { userId, name: p.name, project: p.project, contact: p.contact, bio: p.bio }
})
if (res?.success) wx.showToast({ title: '资料已保存', icon: 'success' })