fix: 小程序结果/选题页与 Test 路由及分享逻辑

1、修复了 result 与 test 各题型页及 test-select 的跳转与数据问题。

2、调整 api/Test 与 route 注册。

3、优化 app 入口与 share 工具。

Made-with: Cursor
This commit is contained in:
Ghost
2026-04-14 10:39:14 +08:00
parent fe2d1a5cb0
commit 0aab56c842
13 changed files with 468 additions and 98 deletions

View File

@@ -41,6 +41,7 @@ Page({
],
payInfo: { requiresPayment: false, isPaid: false, amountYuan: 0 },
testResultId: null,
shareToken: '',
hasReloadedAfterPay: false,
},
@@ -49,7 +50,11 @@ Page({
const type = options && options.type
if (id && type === 'disc') {
this.setData({ testResultId: id })
this.loadDetail(id)
if (options.st) {
this.loadShareDetail(id, options.st)
} else {
this.loadDetail(id)
}
return
}
const raw = wx.getStorageSync('discResult')
@@ -64,6 +69,26 @@ Page({
}
},
applyDetailPayload(payload) {
const data = payload.data || payload
const isPaid = !!payload.isPaid
const paidAmount = payload.paidAmount != null ? Number(payload.paidAmount) : 0
const amountYuan = payload.amountYuan != null ? Number(payload.amountYuan) : (paidAmount > 0 ? paidAmount / 100 : 0)
const needPaymentToUnlock = payload.needPaymentToUnlock === true || (!!payload.requiresPayment && !isPaid && paidAmount > 0)
const r = withPercentagesInt(data)
this.setData({
result: r,
typeSummaryLine: getTypeOnly(data, 'disc'),
shareToken: payload.shareToken || ''
})
const payInfo = {
requiresPayment: needPaymentToUnlock,
isPaid,
amountYuan: needPaymentToUnlock ? amountYuan : 0
}
this.setData({ payInfo })
},
loadDetail(id) {
const apiBase = app.globalData?.apiBase || ''
const token = app.globalData?.token || wx.getStorageSync('token') || ''
@@ -76,23 +101,7 @@ Page({
data: { id },
success: (res) => {
if (res.statusCode === 200 && res.data && res.data.code === 200) {
const payload = res.data.data || {}
const data = payload.data || payload
const isPaid = !!payload.isPaid
const paidAmount = payload.paidAmount != null ? Number(payload.paidAmount) : 0
const amountYuan = payload.amountYuan != null ? Number(payload.amountYuan) : (paidAmount > 0 ? paidAmount / 100 : 0)
const needPaymentToUnlock = payload.needPaymentToUnlock === true || (!!payload.requiresPayment && !isPaid && paidAmount > 0)
const r = withPercentagesInt(data)
this.setData({
result: r,
typeSummaryLine: getTypeOnly(data, 'disc')
})
const payInfo = {
requiresPayment: needPaymentToUnlock,
isPaid,
amountYuan: needPaymentToUnlock ? amountYuan : 0
}
this.setData({ payInfo })
this.applyDetailPayload(res.data.data || {})
} else {
wx.showToast({ title: res.data?.message || '加载失败', icon: 'none' })
}
@@ -102,6 +111,26 @@ Page({
})
},
loadShareDetail(id, st) {
const apiBase = app.globalData?.apiBase || ''
if (!apiBase) { wx.showToast({ title: '配置异常', icon: 'none' }); return }
wx.showLoading({ title: '加载中...' })
wx.request({
url: `${apiBase}/api/test/share-detail`,
method: 'GET',
data: { id, st },
success: (res) => {
if (res.statusCode === 200 && res.data && res.data.code === 200) {
this.applyDetailPayload(res.data.data || {})
} else {
wx.showToast({ title: res.data?.message || '分享链接无效或已失效', icon: 'none' })
}
},
fail: () => wx.showToast({ title: '网络错误', icon: 'none' }),
complete: () => wx.hideLoading()
})
},
onShow() {
if (this.data.testResultId) return
const raw = wx.getStorageSync('discResult')
@@ -167,19 +196,27 @@ Page({
onShareAppMessage() {
const result = this.data.result
const { getSharePathByScope } = require('../../utils/share')
const { getResultSharePath } = require('../../utils/share')
return {
title: `我的DISC类型是${result?.dominantType}型(${result?.description?.title}),来测测你的吧!`,
path: getSharePathByScope('/pages/index/index')
path: getResultSharePath('/pages/result/disc', {
id: this.data.testResultId,
type: 'disc',
shareToken: this.data.shareToken
})
}
},
onShareTimeline() {
const result = this.data.result
const { buildShareQuery } = require('../../utils/share')
const { getResultShareTimelineQuery } = require('../../utils/share')
return {
title: `我的DISC类型是${result?.dominantType}型(${result?.description?.title}),来测测你的吧!`,
query: buildShareQuery()
query: getResultShareTimelineQuery({
id: this.data.testResultId,
type: 'disc',
shareToken: this.data.shareToken
})
}
}
})

View File

@@ -26,6 +26,8 @@ Page({
amountYuan: 0
},
testResultId: null,
/** 后端 HMAC用于好友打开结果详情来自 /api/test/detail 或 /api/test/share-detail */
shareToken: '',
hasReloadedAfterPay: false,
hasPhone: false,
},
@@ -36,7 +38,11 @@ Page({
if (id && type === 'mbti') {
this.setData({ testResultId: id })
this.loadDetail(id)
if (options.st) {
this.loadShareDetail(id, options.st)
} else {
this.loadDetail(id)
}
return
}
@@ -65,6 +71,24 @@ Page({
wx.navigateTo({ url: '/pages/user-profile/index' })
},
applyDetailPayload(payload) {
const data = payload.data || payload
const isPaid = !!payload.isPaid
const paidAmount = payload.paidAmount != null ? Number(payload.paidAmount) : 0
const amountYuan = payload.amountYuan != null ? Number(payload.amountYuan) : (paidAmount > 0 ? paidAmount / 100 : 0)
const needPaymentToUnlock = payload.needPaymentToUnlock === true || (!!payload.requiresPayment && !isPaid && paidAmount > 0)
this.applyResult(data)
const payInfo = {
requiresPayment: needPaymentToUnlock,
isPaid,
amountYuan: needPaymentToUnlock ? amountYuan : 0
}
this.setData({
payInfo,
shareToken: payload.shareToken || ''
})
},
loadDetail(id) {
const apiBase = app.globalData?.apiBase || ''
const token = app.globalData?.token || wx.getStorageSync('token') || ''
@@ -80,19 +104,7 @@ Page({
data: { id },
success: (res) => {
if (res.statusCode === 200 && res.data && res.data.code === 200) {
const payload = res.data.data || {}
const data = payload.data || payload
const isPaid = !!payload.isPaid
const paidAmount = payload.paidAmount != null ? Number(payload.paidAmount) : 0
const amountYuan = payload.amountYuan != null ? Number(payload.amountYuan) : (paidAmount > 0 ? paidAmount / 100 : 0)
const needPaymentToUnlock = payload.needPaymentToUnlock === true || (!!payload.requiresPayment && !isPaid && paidAmount > 0)
this.applyResult(data)
const payInfo = {
requiresPayment: needPaymentToUnlock,
isPaid,
amountYuan: needPaymentToUnlock ? amountYuan : 0
}
this.setData({ payInfo })
this.applyDetailPayload(res.data.data || {})
} else {
wx.showToast({ title: res.data?.message || '加载失败', icon: 'none' })
}
@@ -102,6 +114,29 @@ Page({
})
},
loadShareDetail(id, st) {
const apiBase = app.globalData?.apiBase || ''
if (!apiBase) {
wx.showToast({ title: '配置异常', icon: 'none' })
return
}
wx.showLoading({ title: '加载中...' })
wx.request({
url: `${apiBase}/api/test/share-detail`,
method: 'GET',
data: { id, st },
success: (res) => {
if (res.statusCode === 200 && res.data && res.data.code === 200) {
this.applyDetailPayload(res.data.data || {})
} else {
wx.showToast({ title: res.data?.message || '分享链接无效或已失效', icon: 'none' })
}
},
fail: () => wx.showToast({ title: '网络错误', icon: 'none' }),
complete: () => wx.hideLoading()
})
},
applyResult(result) {
if (!result) return
const desc = result.description || {}
@@ -219,20 +254,28 @@ Page({
onShareAppMessage() {
const result = this.data.result
const { getSharePathByScope } = require('../../utils/share')
const { getResultSharePath } = require('../../utils/share')
return {
title: `我的MBTI类型是${result?.mbtiType}${result?.description?.name}),来测测你的吧!`,
path: getSharePathByScope('/pages/index/index'),
path: getResultSharePath('/pages/result/mbti', {
id: this.data.testResultId,
type: 'mbti',
shareToken: this.data.shareToken
}),
imageUrl: '/images/share-mbti.png'
}
},
onShareTimeline() {
const result = this.data.result
const { buildShareQuery } = require('../../utils/share')
const { getResultShareTimelineQuery } = require('../../utils/share')
return {
title: `我的MBTI类型是${result?.mbtiType}${result?.description?.name}),来测测你的吧!`,
query: buildShareQuery()
query: getResultShareTimelineQuery({
id: this.data.testResultId,
type: 'mbti',
shareToken: this.data.shareToken
})
}
}
})

View File

@@ -44,6 +44,7 @@ Page({
],
payInfo: { requiresPayment: false, isPaid: false, amountYuan: 0 },
testResultId: null,
shareToken: '',
hasReloadedAfterPay: false,
},
@@ -52,7 +53,11 @@ Page({
const type = options && options.type
if (id && type === 'pdp') {
this.setData({ testResultId: id })
this.loadDetail(id)
if (options.st) {
this.loadShareDetail(id, options.st)
} else {
this.loadDetail(id)
}
return
}
const result = wx.getStorageSync('pdpResult')
@@ -68,6 +73,25 @@ Page({
}
},
applyDetailPayload(payload) {
const data = payload.data || payload
const isPaid = !!payload.isPaid
const paidAmount = payload.paidAmount != null ? Number(payload.paidAmount) : 0
const amountYuan = payload.amountYuan != null ? Number(payload.amountYuan) : (paidAmount > 0 ? paidAmount / 100 : 0)
const needPaymentToUnlock = payload.needPaymentToUnlock === true || (!!payload.requiresPayment && !isPaid && paidAmount > 0)
this.setData({
result: withPercentagesInt(data),
typeSummaryLine: getTypeOnly(data, 'pdp'),
shareToken: payload.shareToken || ''
})
const payInfo = {
requiresPayment: needPaymentToUnlock,
isPaid,
amountYuan: needPaymentToUnlock ? amountYuan : 0
}
this.setData({ payInfo })
},
loadDetail(id) {
const apiBase = app.globalData?.apiBase || ''
const token = app.globalData?.token || wx.getStorageSync('token') || ''
@@ -80,22 +104,7 @@ Page({
data: { id },
success: (res) => {
if (res.statusCode === 200 && res.data && res.data.code === 200) {
const payload = res.data.data || {}
const data = payload.data || payload
const isPaid = !!payload.isPaid
const paidAmount = payload.paidAmount != null ? Number(payload.paidAmount) : 0
const amountYuan = payload.amountYuan != null ? Number(payload.amountYuan) : (paidAmount > 0 ? paidAmount / 100 : 0)
const needPaymentToUnlock = payload.needPaymentToUnlock === true || (!!payload.requiresPayment && !isPaid && paidAmount > 0)
this.setData({
result: withPercentagesInt(data),
typeSummaryLine: getTypeOnly(data, 'pdp')
})
const payInfo = {
requiresPayment: needPaymentToUnlock,
isPaid,
amountYuan: needPaymentToUnlock ? amountYuan : 0
}
this.setData({ payInfo })
this.applyDetailPayload(res.data.data || {})
} else {
wx.showToast({ title: res.data?.message || '加载失败', icon: 'none' })
}
@@ -105,6 +114,26 @@ Page({
})
},
loadShareDetail(id, st) {
const apiBase = app.globalData?.apiBase || ''
if (!apiBase) { wx.showToast({ title: '配置异常', icon: 'none' }); return }
wx.showLoading({ title: '加载中...' })
wx.request({
url: `${apiBase}/api/test/share-detail`,
method: 'GET',
data: { id, st },
success: (res) => {
if (res.statusCode === 200 && res.data && res.data.code === 200) {
this.applyDetailPayload(res.data.data || {})
} else {
wx.showToast({ title: res.data?.message || '分享链接无效或已失效', icon: 'none' })
}
},
fail: () => wx.showToast({ title: '网络错误', icon: 'none' }),
complete: () => wx.hideLoading()
})
},
onShow() {
if (this.data.testResultId) return
const raw = wx.getStorageSync('pdpResult')
@@ -172,19 +201,27 @@ Page({
onShareAppMessage() {
const line = this.data.typeSummaryLine || this.data.result?.description?.type || ''
const { getSharePathByScope } = require('../../utils/share')
const { getResultSharePath } = require('../../utils/share')
return {
title: `我的PDP类型是${line},来测测你的吧!`,
path: getSharePathByScope('/pages/index/index')
path: getResultSharePath('/pages/result/pdp', {
id: this.data.testResultId,
type: 'pdp',
shareToken: this.data.shareToken
})
}
},
onShareTimeline() {
const line = this.data.typeSummaryLine || this.data.result?.description?.type || ''
const { buildShareQuery } = require('../../utils/share')
const { getResultShareTimelineQuery } = require('../../utils/share')
return {
title: `我的PDP类型是${line},来测测你的吧!`,
query: buildShareQuery()
query: getResultShareTimelineQuery({
id: this.data.testResultId,
type: 'pdp',
shareToken: this.data.shareToken
})
}
}
})

View File

@@ -52,6 +52,7 @@ Page({
amountYuan: 0
},
testResultId: null,
shareToken: '',
hasReloadedAfterPay: false,
hasPhone: false
},
@@ -62,7 +63,11 @@ Page({
if (id && type === 'sbti') {
this.setData({ testResultId: id })
this.loadDetail(id)
if (options.st) {
this.loadShareDetail(id, options.st)
} else {
this.loadDetail(id)
}
return
}
@@ -91,6 +96,24 @@ Page({
wx.navigateTo({ url: '/pages/user-profile/index' })
},
applyDetailPayload(payload) {
const data = payload.data || payload
const isPaid = !!payload.isPaid
const paidAmount = payload.paidAmount != null ? Number(payload.paidAmount) : 0
const amountYuan = payload.amountYuan != null ? Number(payload.amountYuan) : (paidAmount > 0 ? paidAmount / 100 : 0)
const needPaymentToUnlock = payload.needPaymentToUnlock === true || (!!payload.requiresPayment && !isPaid && paidAmount > 0)
this.applyResult(data)
const payInfo = {
requiresPayment: needPaymentToUnlock,
isPaid,
amountYuan: needPaymentToUnlock ? amountYuan : 0
}
this.setData({
payInfo,
shareToken: payload.shareToken || ''
})
},
loadDetail(id) {
const apiBase = app.globalData?.apiBase || ''
const token = app.globalData?.token || wx.getStorageSync('token') || ''
@@ -106,19 +129,7 @@ Page({
data: { id },
success: (res) => {
if (res.statusCode === 200 && res.data && res.data.code === 200) {
const payload = res.data.data || {}
const data = payload.data || payload
const isPaid = !!payload.isPaid
const paidAmount = payload.paidAmount != null ? Number(payload.paidAmount) : 0
const amountYuan = payload.amountYuan != null ? Number(payload.amountYuan) : (paidAmount > 0 ? paidAmount / 100 : 0)
const needPaymentToUnlock = payload.needPaymentToUnlock === true || (!!payload.requiresPayment && !isPaid && paidAmount > 0)
this.applyResult(data)
const payInfo = {
requiresPayment: needPaymentToUnlock,
isPaid,
amountYuan: needPaymentToUnlock ? amountYuan : 0
}
this.setData({ payInfo })
this.applyDetailPayload(res.data.data || {})
} else {
wx.showToast({ title: res.data?.message || '加载失败', icon: 'none' })
}
@@ -128,6 +139,29 @@ Page({
})
},
loadShareDetail(id, st) {
const apiBase = app.globalData?.apiBase || ''
if (!apiBase) {
wx.showToast({ title: '配置异常', icon: 'none' })
return
}
wx.showLoading({ title: '加载中...' })
wx.request({
url: `${apiBase}/api/test/share-detail`,
method: 'GET',
data: { id, st },
success: (res) => {
if (res.statusCode === 200 && res.data && res.data.code === 200) {
this.applyDetailPayload(res.data.data || {})
} else {
wx.showToast({ title: res.data?.message || '分享链接无效或已失效', icon: 'none' })
}
},
fail: () => wx.showToast({ title: '网络错误', icon: 'none' }),
complete: () => wx.hideLoading()
})
},
applyResult(result) {
if (!result) return
const normalized = normalizeSbtiResultForDisplay(result)
@@ -239,25 +273,33 @@ Page({
onShareAppMessage() {
const result = this.data.result
const { getSharePathByScope } = require('../../utils/share')
const { getResultSharePath } = require('../../utils/share')
const label = result?.sbtiCn || result?.finalType?.cn || 'SBTI'
const code = result?.sbtiType || result?.finalType?.code || ''
const img = this.data.typeImageUrl || '/images/share-mbti.png'
return {
title: `我的 SBTI 类型是 ${code}${label}),来测测你的吧!`,
path: getSharePathByScope('/pages/index/index'),
path: getResultSharePath('/pages/result/sbti', {
id: this.data.testResultId,
type: 'sbti',
shareToken: this.data.shareToken
}),
imageUrl: img
}
},
onShareTimeline() {
const result = this.data.result
const { buildShareQuery } = require('../../utils/share')
const { getResultShareTimelineQuery } = require('../../utils/share')
const label = result?.sbtiCn || result?.finalType?.cn || 'SBTI'
const code = result?.sbtiType || result?.finalType?.code || ''
return {
title: `我的 SBTI 类型是 ${code}${label}),来测测你的吧!`,
query: buildShareQuery()
query: getResultShareTimelineQuery({
id: this.data.testResultId,
type: 'sbti',
shareToken: this.data.shareToken
})
}
}
})

View File

@@ -14,6 +14,9 @@ Page({
try {
require('../../utils/thirdPartyContext.js').ingestThirdPartyOnPageLoad(options || {}, app)
} catch (e) {}
try {
wx.showShareMenu({ withShareTicket: true, menus: ['shareAppMessage', 'shareTimeline'] })
} catch (e) {}
this._syncPerms()
},
@@ -46,5 +49,21 @@ Page({
goDISC() {
wx.navigateTo({ url: '/pages/test/disc' })
},
onShareAppMessage() {
const { getSharePath } = require('../../utils/share')
return {
title: '4 大详细性格测试,来测测你的性格类型',
path: getSharePath('/pages/test-select/index')
}
},
onShareTimeline() {
const { buildShareQuery } = require('../../utils/share')
return {
title: '4 大详细性格测试,来测测你的性格类型',
query: buildShareQuery()
}
}
})

View File

@@ -47,6 +47,9 @@ Page({
try {
require('../../utils/analytics').track('test_start', { type: 'disc', total: questions.length })
} catch (e) {}
try {
wx.showShareMenu({ withShareTicket: true, menus: ['shareAppMessage', 'shareTimeline'] })
} catch (e) {}
this.startTimer()
})
.catch((err) => {
@@ -190,5 +193,21 @@ Page({
}
wx.redirectTo({ url: '/pages/result/disc' })
},
onShareAppMessage() {
const { getSharePath } = require('../../utils/share')
return {
title: '来测测你的 DISC 性格类型',
path: getSharePath('/pages/test/disc')
}
},
onShareTimeline() {
const { buildShareQuery } = require('../../utils/share')
return {
title: '来测测你的 DISC 性格类型',
query: buildShareQuery()
}
}
})

View File

@@ -52,6 +52,9 @@ Page({
try {
require('../../utils/analytics').track('test_start', { type: 'mbti', total })
} catch (e) {}
try {
wx.showShareMenu({ withShareTicket: true, menus: ['shareAppMessage', 'shareTimeline'] })
} catch (e) {}
this.startTimer()
})
.catch((err) => {
@@ -280,5 +283,21 @@ Page({
confidence,
description: mbtiDescriptions[mbtiType] || {}
}
},
onShareAppMessage() {
const { getSharePath } = require('../../utils/share')
return {
title: '来测测你的 MBTI 性格类型',
path: getSharePath('/pages/test/mbti')
}
},
onShareTimeline() {
const { buildShareQuery } = require('../../utils/share')
return {
title: '来测测你的 MBTI 性格类型',
query: buildShareQuery()
}
}
})

View File

@@ -47,6 +47,9 @@ Page({
try {
require('../../utils/analytics').track('test_start', { type: 'pdp', total: questions.length })
} catch (e) {}
try {
wx.showShareMenu({ withShareTicket: true, menus: ['shareAppMessage', 'shareTimeline'] })
} catch (e) {}
this.startTimer()
})
.catch((err) => {
@@ -193,5 +196,21 @@ Page({
}
wx.redirectTo({ url: '/pages/result/pdp' })
},
onShareAppMessage() {
const { getSharePath } = require('../../utils/share')
return {
title: '来测测你的 PDP 性格类型',
path: getSharePath('/pages/test/pdp')
}
},
onShareTimeline() {
const { buildShareQuery } = require('../../utils/share')
return {
title: '来测测你的 PDP 性格类型',
query: buildShareQuery()
}
}
})

View File

@@ -56,6 +56,9 @@ Page({
try {
require('../../utils/analytics').track('test_start', { type: 'sbti', total })
} catch (e) {}
try {
wx.showShareMenu({ withShareTicket: true, menus: ['shareAppMessage', 'shareTimeline'] })
} catch (e) {}
this.startTimer()
})
.catch((err) => {
@@ -245,5 +248,21 @@ Page({
wx.redirectTo({
url: '/pages/result/sbti'
})
},
onShareAppMessage() {
const { getSharePath } = require('../../utils/share')
return {
title: '来测测你的 SBTI 类型',
path: getSharePath('/pages/test/sbti')
}
},
onShareTimeline() {
const { buildShareQuery } = require('../../utils/share')
return {
title: '来测测你的 SBTI 类型',
query: buildShareQuery()
}
}
})