chore: 清理敏感与开发文档,仅同步代码

- 永久忽略并从仓库移除 开发文档/
- 移除并忽略 .env 与小程序私有配置
- 同步小程序/管理端/API与脚本改动

Made-with: Cursor
This commit is contained in:
卡若
2026-03-17 17:50:12 +08:00
parent 868b0a10d9
commit 76965adb23
443 changed files with 24175 additions and 64154 deletions

View File

@@ -5,22 +5,43 @@
const { parseScene } = require('./utils/scene.js')
const DEFAULT_BASE_URL = 'https://soulapi.quwanzhi.com'
const DEFAULT_APP_ID = 'wxb8bbb2b10dec74aa'
const DEFAULT_MCH_ID = '1318592501'
const DEFAULT_WITHDRAW_TMPL_ID = 'u3MbZGPRkrZIk-I7QdpwzFxnO_CeQPaCWF2FkiIablE'
function getRuntimeBootstrapConfig() {
try {
const extCfg = wx.getExtConfigSync ? (wx.getExtConfigSync() || {}) : {}
return {
baseUrl: extCfg.apiBaseUrl || wx.getStorageSync('apiBaseUrl') || DEFAULT_BASE_URL,
appId: extCfg.appId || DEFAULT_APP_ID,
mchId: extCfg.mchId || DEFAULT_MCH_ID,
withdrawSubscribeTmplId: extCfg.withdrawSubscribeTmplId || DEFAULT_WITHDRAW_TMPL_ID
}
} catch (_) {
return {
baseUrl: DEFAULT_BASE_URL,
appId: DEFAULT_APP_ID,
mchId: DEFAULT_MCH_ID,
withdrawSubscribeTmplId: DEFAULT_WITHDRAW_TMPL_ID
}
}
}
const bootstrapConfig = getRuntimeBootstrapConfig()
App({
globalData: {
// API基础地址 - 连接真实后端
baseUrl: 'https://soulapi.quwanzhi.com',
// baseUrl: 'https://souldev.quwanzhi.com',
// baseUrl: 'http://localhost:8080',
// 小程序配置 - 真实AppID
appId: 'wxb8bbb2b10dec74aa',
// 运行配置:优先外部配置/缓存,其次默认值
baseUrl: bootstrapConfig.baseUrl,
appId: bootstrapConfig.appId,
// 订阅消息:用户点击「申请提现」→「立即提现」时会先弹出订阅授权窗
withdrawSubscribeTmplId: 'u3MbZGPRkrZIk-I7QdpwzFxnO_CeQPaCWF2FkiIablE',
withdrawSubscribeTmplId: bootstrapConfig.withdrawSubscribeTmplId,
// 微信支付配置
mchId: '1318592501', // 商户号
mchId: bootstrapConfig.mchId,
// 用户信息
userInfo: null,
@@ -29,11 +50,15 @@ App({
// 书籍数据
bookData: null,
totalSections: 62,
totalSections: 0,
supportWechat: '',
// 购买记录
purchasedSections: [],
hasFullBook: false,
// VIP 会员365天包含增值版免费与 hasFullBook=9.9 买断不同)
isVip: false,
vipExpireDate: '',
// 已读章节(仅统计有权限打开过的章节,用于首页「已读/待读」)
readSectionIds: [],
@@ -58,17 +83,35 @@ App({
// TabBar相关
currentTab: 0,
// 是否处于「单页模式」(如朋友圈文章里的单页预览)
// 用于在受限环境下给出引导文案,提示用户点击底部「前往小程序」进入完整体验
isSinglePageMode: false,
// 更新检测:上次检测时间戳,避免频繁请求
lastUpdateCheck: 0
lastUpdateCheck: 0,
// 审核模式:后端 /api/miniprogram/config 返回 auditMode=true 时隐藏所有支付相关UI
auditMode: false
},
onLaunch(options) {
this.globalData.readSectionIds = wx.getStorageSync('readSectionIds') || []
// 获取系统信息
this.getSystemInfo()
// 场景值兜底1154 为「朋友圈单页模式」进入
try {
const launchOpts = wx.getLaunchOptionsSync ? wx.getLaunchOptionsSync() : null
if (launchOpts && launchOpts.scene === 1154) {
this.globalData.isSinglePageMode = true
}
} catch (e) {
console.warn('[App] 读取 LaunchOptions 失败:', e)
}
// 检查登录状态
this.checkLoginStatus()
this.loadRuntimeConfig()
// 加载书籍数据
this.loadBookData()
@@ -190,6 +233,31 @@ App({
return code.replace(/[\s\-_]/g, '').toUpperCase().trim()
},
// 判断用户资料是否完善(昵称 + 头像)
_isProfileIncomplete(user) {
if (!user) return true
const nickname = (user.nickname || '').trim()
const avatar = (user.avatar || '').trim()
const isDefaultNickname = !nickname || nickname === '微信用户'
const noAvatar = !avatar
return isDefaultNickname || noAvatar
},
// 登录后若资料未完善,引导跳转到资料编辑页
_ensureProfileCompletedAfterLogin(user) {
try {
if (!user || !this._isProfileIncomplete(user)) return
const pages = getCurrentPages()
const current = pages[pages.length - 1]
// 避免在资料页内重复跳转
if (current && current.route === 'pages/profile-edit/profile-edit') return
wx.showToast({ title: '请先完善头像和昵称', icon: 'none', duration: 2000 })
wx.navigateTo({ url: '/pages/profile-edit/profile-edit' })
} catch (e) {
console.warn('[App] 跳转资料编辑页失败:', e)
}
},
// 根据业务 id 从 bookData 查 mid用于跳转
getSectionMid(sectionId) {
const list = this.globalData.bookData || []
@@ -224,6 +292,11 @@ App({
const systemInfo = wx.getSystemInfoSync()
this.globalData.systemInfo = systemInfo
this.globalData.statusBarHeight = systemInfo.statusBarHeight || 44
// 微信在单页模式下会在 systemInfo.mode 标记 singlePage
if (systemInfo.mode === 'singlePage') {
this.globalData.isSinglePageMode = true
}
// 计算导航栏高度
const menuButton = wx.getMenuButtonBoundingClientRect()
@@ -235,6 +308,33 @@ App({
}
},
/**
* 若当前处于朋友圈等「单页模式」,在尝试登录/购买前给用户友好提示,
* 引导用户点击底部「前往小程序」进入完整小程序再操作。
* 返回 false 表示应中断当前操作。
*/
ensureFullAppForAuth() {
// 每次调用时再做一次兜底检测,避免全局标记遗漏
try {
const sys = wx.getSystemInfoSync()
if (sys && sys.mode === 'singlePage') {
this.globalData.isSinglePageMode = true
}
} catch (e) {
console.warn('[App] ensureFullAppForAuth getSystemInfoSync error:', e)
}
if (!this.globalData.isSinglePageMode) return true
wx.showModal({
title: '请前往完整小程序',
content: '当前为朋友圈单页,仅支持部分浏览。如需登录和解锁内容,请点击底部「前往小程序」后再操作。',
showCancel: false,
confirmText: '我知道了',
})
return false
},
// 检查登录状态
checkLoginStatus() {
try {
@@ -246,12 +346,32 @@ App({
this.globalData.isLoggedIn = true
this.globalData.purchasedSections = userInfo.purchasedSections || []
this.globalData.hasFullBook = userInfo.hasFullBook || false
this.globalData.isVip = userInfo.isVip || false
this.globalData.vipExpireDate = userInfo.vipExpireDate || ''
}
} catch (e) {
console.error('检查登录状态失败:', e)
}
},
async loadRuntimeConfig() {
try {
const res = await this.request({ url: '/api/miniprogram/config', silent: true, timeout: 5000 })
const mpConfig = res?.mpConfig || {}
this.globalData.baseUrl = mpConfig.apiDomain || this.globalData.baseUrl
this.globalData.appId = mpConfig.appId || this.globalData.appId
this.globalData.mchId = mpConfig.mchId || this.globalData.mchId
this.globalData.withdrawSubscribeTmplId = mpConfig.withdrawSubscribeTmplId || this.globalData.withdrawSubscribeTmplId
this.globalData.supportWechat = mpConfig.supportWechat || mpConfig.customerWechat || mpConfig.serviceWechat || ''
this.globalData.auditMode = mpConfig.auditMode || false
try {
wx.setStorageSync('apiBaseUrl', this.globalData.baseUrl)
} catch (_) {}
} catch (e) {
console.warn('[App] 加载运行配置失败,继续使用默认配置:', e)
}
},
// 加载书籍数据
async loadBookData() {
try {
@@ -266,6 +386,7 @@ App({
if (res && (res.data || res.chapters)) {
const chapters = res.data || res.chapters || []
this.globalData.bookData = chapters
this.globalData.totalSections = res.total || chapters.length || 0
wx.setStorageSync('bookData', chapters)
}
} catch (e) {
@@ -355,6 +476,7 @@ App({
url: this.globalData.baseUrl + url,
method: options.method || 'GET',
data: options.data || {},
timeout: options.timeout || 15000,
header: {
'Content-Type': 'application/json',
'Authorization': token ? `Bearer ${token}` : '',
@@ -366,6 +488,11 @@ App({
// 业务失败success === falsesoul-api 用 message 或 error 返回原因
if (data && data.success === false) {
const msg = this._getApiErrorMsg(data, '操作失败')
// 登录态不一致:本地有 token/userInfo但后端查不到该用户
// 典型原因:切换环境(baseUrl)、换库/清库、用户被删除、token 与用户不匹配
if (msg && (msg.includes('用户不存在') || msg.toLowerCase().includes('user not found'))) {
this.logout()
}
showError(msg)
reject(new Error(msg))
return
@@ -386,7 +513,7 @@ App({
},
fail: (err) => {
const msg = (err && err.errMsg) ? (err.errMsg.indexOf('timeout') !== -1 ? '请求超时,请重试' : '网络异常,请重试') : '网络异常,请重试'
showError(msg)
console.warn('[App] request fail:', url, msg)
reject(new Error(msg))
}
})
@@ -395,13 +522,15 @@ App({
// 登录方法 - 获取openId用于支付加固错误处理避免审核报“登录报错”
async login() {
if (!this.ensureFullAppForAuth()) {
return null
}
try {
const loginRes = await new Promise((resolve, reject) => {
wx.login({ success: resolve, fail: reject })
})
if (!loginRes || !loginRes.code) {
console.warn('[App] wx.login 未返回 code')
wx.showToast({ title: '获取登录态失败,请重试', icon: 'none' })
return null
}
try {
@@ -420,12 +549,13 @@ App({
// 保存用户信息
if (res.data.user) {
this.globalData.userInfo = res.data.user
const user = res.data.user
this.globalData.userInfo = user
this.globalData.isLoggedIn = true
this.globalData.purchasedSections = res.data.user.purchasedSections || []
this.globalData.hasFullBook = res.data.user.hasFullBook || false
this.globalData.purchasedSections = user.purchasedSections || []
this.globalData.hasFullBook = user.hasFullBook || false
wx.setStorageSync('userInfo', res.data.user)
wx.setStorageSync('userInfo', user)
wx.setStorageSync('token', res.data.token || '')
// 登录成功后,检查待绑定的推荐码并执行绑定
@@ -434,27 +564,30 @@ App({
console.log('[App] 登录后自动绑定推荐码:', pendingRef)
this.bindReferralCode(pendingRef)
}
// 登录后引导完善资料
this._ensureProfileCompletedAfterLogin(user)
}
return res.data
}
} catch (apiError) {
console.log('[App] API登录失败:', apiError.message)
// 不使用模拟登录,提示用户网络问题
wx.showToast({ title: '网络异常,请重试', icon: 'none' })
console.warn('[App] API登录失败:', apiError.message)
return null
}
return null
} catch (e) {
console.error('[App] 登录失败:', e)
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
console.warn('[App] 登录失败:', e)
return null
}
},
// 获取openId (支付必需)
async getOpenId() {
if (!this.ensureFullAppForAuth()) {
return null
}
// 先检查缓存
const cachedOpenId = wx.getStorageSync('openId')
if (cachedOpenId) {
@@ -478,42 +611,42 @@ App({
wx.setStorageSync('openId', res.data.openId)
// 接口同时返回 user 时视为登录,补全登录态并从登录开始绑定推荐码
if (res.data.user) {
this.globalData.userInfo = res.data.user
const user = res.data.user
this.globalData.userInfo = user
this.globalData.isLoggedIn = true
this.globalData.purchasedSections = res.data.user.purchasedSections || []
this.globalData.hasFullBook = res.data.user.hasFullBook || false
wx.setStorageSync('userInfo', res.data.user)
this.globalData.purchasedSections = user.purchasedSections || []
this.globalData.hasFullBook = user.hasFullBook || false
wx.setStorageSync('userInfo', user)
wx.setStorageSync('token', res.data.token || '')
const pendingRef = wx.getStorageSync('pendingReferralCode') || this.globalData.pendingReferralCode
if (pendingRef) {
console.log('[App] getOpenId 登录后自动绑定推荐码:', pendingRef)
this.bindReferralCode(pendingRef)
}
// 登录后引导完善资料
this._ensureProfileCompletedAfterLogin(user)
}
return res.data.openId
}
} catch (e) {
console.error('[App] 获取openId失败:', e)
console.warn('[App] 获取openId失败:', e)
}
return null
},
// 模拟登录已废弃 - 不再使用
// 现在必须使用真实的微信登录获取openId作为唯一标识
mockLogin() {
console.warn('[App] mockLogin已废弃请使用真实登录')
return null
},
// 手机号登录:需同时传 wx.login 的 code 与 getPhoneNumber 的 phoneCode
async loginWithPhone(phoneCode) {
if (!this.ensureFullAppForAuth()) {
return null
}
try {
const loginRes = await new Promise((resolve, reject) => {
wx.login({ success: resolve, fail: reject })
})
if (!loginRes.code) {
wx.showToast({ title: '获取登录态失败', icon: 'none' })
console.warn('[App] loginWithPhone: wx.login 未返回 code')
return null
}
const res = await this.request('/api/miniprogram/phone-login', {
@@ -522,12 +655,13 @@ App({
})
if (res.success && res.data) {
this.globalData.userInfo = res.data.user
const user = res.data.user
this.globalData.userInfo = user
this.globalData.isLoggedIn = true
this.globalData.purchasedSections = res.data.user.purchasedSections || []
this.globalData.hasFullBook = res.data.user.hasFullBook || false
this.globalData.purchasedSections = user.purchasedSections || []
this.globalData.hasFullBook = user.hasFullBook || false
wx.setStorageSync('userInfo', res.data.user)
wx.setStorageSync('userInfo', user)
wx.setStorageSync('token', res.data.token)
// 登录成功后绑定推荐码
@@ -536,12 +670,14 @@ App({
console.log('[App] 手机号登录后自动绑定推荐码:', pendingRef)
this.bindReferralCode(pendingRef)
}
// 登录后引导完善资料
this._ensureProfileCompletedAfterLogin(user)
return res.data
}
} catch (e) {
console.log('[App] 手机号登录失败:', e)
wx.showToast({ title: '登录失败,请重试', icon: 'none' })
console.warn('[App] 手机号登录失败:', e)
}
return null