feat(privacy): 修复微信隐私协议授权流程

- 新增全局隐私协议授权管理器 privacyManager,统一管理弹窗显示和授权结果
- 用 PrivacyModal 组件替代原先 Taro.showModal 实现,使用 open-type="agreePrivacyAuthorization" 的 Button 触发微信认可的隐私授权
- 在 app.tsx 中注册 onNeedPrivacyAuthorization 回调,通过 privacyManager.show 弹出隐私弹窗
- 登录和注册页面 useDidShow 钩子调用 ensurePrivacyAuthorized 函数预检隐私授权状态,避免授权失败
- 修复登录/注册接口调用,统一使用 request.post + SERVER_API_URL,修正错误接口地址及导入问题
- 新增隐私弹窗样式和逻辑,实现用户同意或拒绝后的正确流程处理
- 解决微信基础库 3.16.1+ 强制隐私协议授权导致敏感 API 调用失败问题
This commit is contained in:
2026-07-16 01:11:54 +08:00
parent af90bd7a55
commit 1b72ae37a5
7 changed files with 253 additions and 96 deletions

View File

@@ -1,79 +1,47 @@
import Taro from '@tarojs/taro'
/**
* 在调用隐私相关 API / 组件前,确保用户已完成微信隐私协议授权
* 基础库 3.16.1+ 强制要求,未授权会抛 errno:112 / buttonId is wrong 等错误
* 旧基础库或不支持隐私协议的版本直接放行
* 微信隐私协议授权全局管理器
* 用于在基础库 3.16.1+ 下处理 onNeedPrivacyAuthorization 回调
* 必须在页面中渲染 <PrivacyModal /> 并绑定本管理器
*/
export const ensurePrivacyAuthorized = (): Promise<void> => {
return new Promise((resolve) => {
const wxAny: any = Taro
if (typeof wxAny.requirePrivacyAuthorize !== 'function') {
resolve()
return
}
if (typeof wxAny.getPrivacySetting === 'function') {
wxAny.getPrivacySetting({
success: (res: any) => {
if (res && res.needAuthorization) {
wxAny.requirePrivacyAuthorize({
success: () => resolve(),
fail: () => resolve(),
})
} else {
resolve()
}
},
fail: () => resolve(),
})
} else {
wxAny.requirePrivacyAuthorize({
success: () => resolve(),
fail: () => resolve(),
})
}
})
}
/**
* 打开官方隐私协议页面(半屏协议)。
* 返回是否成功打开。
*/
export const openPrivacyContract = (): Promise<boolean> => {
return new Promise((resolve) => {
const wxAny: any = Taro
if (typeof wxAny.openPrivacyContract !== 'function') {
resolve(false)
return
}
wxAny.openPrivacyContract({
success: () => resolve(true),
fail: () => resolve(false),
})
})
}
type PrivacyResolve = (result: { event: 'agree' | 'disagree'; button: string }) => void
/**
* 检查当前用户是否需要同意隐私协议。
*/
export const checkPrivacyAuthorization = (): Promise<{
needAuthorization: boolean
isAuthorization: boolean
}> => {
return new Promise((resolve) => {
const wxAny: any = Taro
if (typeof wxAny.getPrivacySetting !== 'function') {
resolve({ needAuthorization: false, isAuthorization: true })
return
}
wxAny.getPrivacySetting({
success: (res: any) => {
resolve({
needAuthorization: !!res?.needAuthorization,
isAuthorization: !!res?.isAuthorization,
})
},
fail: () => resolve({ needAuthorization: false, isAuthorization: true }),
})
})
let currentResolve: PrivacyResolve | null = null
let showCallback: ((show: boolean) => void) | null = null
let privacyContractName = '《用户隐私保护指引》'
export const privacyManager = {
/** 设置弹窗显示/隐藏回调 */
setShowCallback(cb: (show: boolean) => void) {
showCallback = cb
},
/** 设置隐私协议名称(从 getPrivacySetting 读取) */
setPrivacyContractName(name: string) {
privacyContractName = name || '《用户隐私保护指引》'
},
getPrivacyContractName() {
return privacyContractName
},
/** 展示隐私协议授权弹窗 */
show(resolve: PrivacyResolve) {
currentResolve = resolve
showCallback?.(true)
},
/** 用户点击同意(由 open-type=agreePrivacyAuthorization 的 button 触发) */
agree() {
currentResolve?.({ event: 'agree', button: 'agree' })
currentResolve = null
showCallback?.(false)
},
/** 用户点击拒绝 */
disagree() {
currentResolve?.({ event: 'disagree', button: 'disagree' })
currentResolve = null
showCallback?.(false)
},
}