diff --git a/miniprogram/app.js b/miniprogram/app.js index 2bd161cf..4d7ad146 100644 --- a/miniprogram/app.js +++ b/miniprogram/app.js @@ -32,6 +32,8 @@ App({ // 购买记录 purchasedSections: [], hasFullBook: false, + matchCount: 0, + matchQuota: null, // 已读章节(仅统计有权限打开过的章节,用于首页「已读/待读」) readSectionIds: [], @@ -482,7 +484,9 @@ App({ this.globalData.isLoggedIn = false this.globalData.purchasedSections = [] this.globalData.hasFullBook = false - + this.globalData.matchCount = 0 + this.globalData.matchQuota = null + wx.removeStorageSync('userInfo') wx.removeStorageSync('token') }, diff --git a/miniprogram/pages/match/match.js b/miniprogram/pages/match/match.js index 6054c1b8..104fb88c 100644 --- a/miniprogram/pages/match/match.js +++ b/miniprogram/pages/match/match.js @@ -38,6 +38,7 @@ Page({ todayMatchCount: 0, totalMatchesAllowed: FREE_MATCH_LIMIT, matchesRemaining: FREE_MATCH_LIMIT, + showQuotaExhausted: false, needPayToMatch: false, // 匹配状态 @@ -88,8 +89,7 @@ Page({ }) this.loadMatchConfig() this.loadStoredContact() - this.loadTodayMatchCount() - this.initUserStatus() + this.refreshMatchCountAndStatus() }, onShow() { @@ -102,7 +102,7 @@ Page({ } } this.loadStoredContact() - this.initUserStatus() + this.refreshMatchCountAndStatus() }, // 加载匹配配置 @@ -147,49 +147,46 @@ Page({ }) }, - // 加载今日匹配次数 - loadTodayMatchCount() { - try { - const today = new Date().toISOString().split('T')[0] - const stored = wx.getStorageSync('match_count_data') - if (stored) { - const data = typeof stored === 'string' ? JSON.parse(stored) : stored - if (data.date === today) { - this.setData({ todayMatchCount: data.count }) + // 从服务端刷新匹配配额并初始化用户状态(前后端双向校验,服务端为权威) + async refreshMatchCountAndStatus() { + if (app.globalData.isLoggedIn && app.globalData.userInfo?.id) { + try { + const res = await app.request(`/api/miniprogram/user/purchase-status?userId=${encodeURIComponent(app.globalData.userInfo.id)}`) + if (res.success && res.data) { + app.globalData.matchCount = res.data.matchCount ?? 0 + app.globalData.matchQuota = res.data.matchQuota || null } + } catch (e) { + console.log('[Match] 拉取 matchQuota 失败:', e) } - } catch (e) { - console.error('加载匹配次数失败:', e) } + this.initUserStatus() }, - // 保存今日匹配次数 - saveTodayMatchCount(count) { - const today = new Date().toISOString().split('T')[0] - wx.setStorageSync('match_count_data', { date: today, count }) - }, - - // 初始化用户状态 + // 初始化用户状态(matchQuota 服务端纯计算:订单+match_records) initUserStatus() { const { isLoggedIn, hasFullBook, purchasedSections } = app.globalData - - // 获取额外购买的匹配次数 - const extraMatches = wx.getStorageSync('extra_match_count') || 0 - - // 总匹配次数 = 每日免费(3) + 额外购买次数 - // 全书用户无限制 - const totalMatchesAllowed = hasFullBook ? 999999 : FREE_MATCH_LIMIT + extraMatches - const matchesRemaining = hasFullBook ? 999999 : Math.max(0, totalMatchesAllowed - this.data.todayMatchCount) - const needPayToMatch = !hasFullBook && matchesRemaining <= 0 - + const quota = app.globalData.matchQuota + + // 今日剩余次数、今日已用:来自服务端 matchQuota(未登录无法计算,不能显示已用完) + const remainToday = quota?.remainToday ?? 0 + const matchesUsedToday = quota?.matchesUsedToday ?? 0 + const purchasedRemain = quota?.purchasedRemain ?? 0 + const totalMatchesAllowed = hasFullBook ? 999999 : (quota ? remainToday + matchesUsedToday : FREE_MATCH_LIMIT) + // 仅登录且服务端返回配额时,才判断是否已用完;未登录时显示「开始匹配」 + const needPayToMatch = isLoggedIn && !hasFullBook && (quota ? remainToday <= 0 : false) + const showQuotaExhausted = isLoggedIn && !hasFullBook && (quota ? remainToday <= 0 : false) + this.setData({ isLoggedIn, hasFullBook, - hasPurchased: true, // 所有用户都可以使用匹配功能 + hasPurchased: true, + todayMatchCount: matchesUsedToday, totalMatchesAllowed, - matchesRemaining, + matchesRemaining: hasFullBook ? 999999 : (isLoggedIn && quota ? remainToday : (isLoggedIn ? 0 : FREE_MATCH_LIMIT)), needPayToMatch, - extraMatches + showQuotaExhausted, + extraMatches: purchasedRemain }) }, @@ -224,7 +221,7 @@ Page({ confirmText: '去购买', success: (res) => { if (res.confirm) { - wx.switchTab({ url: '/pages/catalog/catalog' }) + wx.switchTab({ url: '/pages/chapters/chapters' }) } } }) @@ -257,12 +254,12 @@ Page({ return } - // 创业合伙类型 - 真正的匹配功能 - if (this.data.needPayToMatch) { + // 创业合伙类型 - 真正的匹配功能(仅当登录且服务端确认次数用尽时,弹出购买) + if (this.data.showQuotaExhausted) { this.setData({ showUnlockModal: true }) return } - + this.startMatch() }, @@ -314,6 +311,7 @@ Page({ // 开始匹配 - 只匹配数据库中的真实用户 async startMatch() { + this.loadStoredContact() this.setData({ isMatching: true, matchAttempts: 0, @@ -325,20 +323,28 @@ Page({ this.setData({ matchAttempts: this.data.matchAttempts + 1 }) }, 1000) - // 从数据库获取真实用户匹配 + // 从数据库获取真实用户匹配(后端会校验剩余次数) let matchedUser = null + let quotaExceeded = false try { + const ui = app.globalData.userInfo || {} + const phone = (wx.getStorageSync('user_phone') || ui.phone || this.data.phoneNumber || '').trim() + const wechatId = (wx.getStorageSync('user_wechat') || ui.wechat || ui.wechatId || this.data.wechatId || '').trim() const res = await app.request('/api/miniprogram/match/users', { method: 'POST', data: { matchType: this.data.selectedType, - userId: app.globalData.userInfo?.id || '' + userId: app.globalData.userInfo?.id || '', + phone, + wechatId } }) - + if (res.success && res.data) { matchedUser = res.data console.log('[Match] 从数据库匹配到用户:', matchedUser.nickname) + } else if (res.code === 'QUOTA_EXCEEDED') { + quotaExceeded = true } } catch (e) { console.log('[Match] 数据库匹配失败:', e) @@ -348,7 +354,22 @@ Page({ const delay = Math.random() * 2000 + 2000 setTimeout(() => { clearInterval(timer) - + + // 次数用尽(后端校验) + if (quotaExceeded) { + this.setData({ isMatching: false }) + wx.showModal({ + title: '今日匹配次数已用完', + content: '请购买更多次数继续匹配', + confirmText: '去购买', + success: (r) => { + if (r.confirm) this.setData({ showUnlockModal: true }) + } + }) + this.refreshMatchCountAndStatus() + return + } + // 如果没有匹配到用户,提示用户 if (!matchedUser) { this.setData({ isMatching: false }) @@ -360,23 +381,17 @@ Page({ }) return } - - // 增加今日匹配次数 - const newCount = this.data.todayMatchCount + 1 - const matchesRemaining = this.data.hasFullBook ? 999999 : Math.max(0, this.data.totalMatchesAllowed - newCount) - + + // 匹配成功:从服务端刷新配额(后端已写入 match_records) this.setData({ isMatching: false, currentMatch: matchedUser, - todayMatchCount: newCount, - matchesRemaining, - needPayToMatch: !this.data.hasFullBook && matchesRemaining <= 0 + needPayToMatch: false }) - this.saveTodayMatchCount(newCount) - + this.refreshMatchCountAndStatus() + // 上报匹配行为到存客宝 this.reportMatch(matchedUser) - }, delay) }, @@ -665,7 +680,8 @@ Page({ try { const result = await app.login() if (result) { - this.initUserStatus() + // 登录成功后必须拉取 matchQuota,否则无法正确显示剩余次数 + await this.refreshMatchCountAndStatus() this.setData({ showLoginModal: false, agreeProtocol: false }) wx.showToast({ title: '登录成功', icon: 'success' }) } else { @@ -777,7 +793,24 @@ Page({ this.setData({ showUnlockModal: false }) }, - // 购买匹配次数 + // 支付成功后立即查询订单状态并刷新(首轮 0 延迟,之后每 800ms 重试) + async pollOrderAndRefresh(orderSn) { + const maxAttempts = 12 + const interval = 800 + for (let i = 0; i < maxAttempts; i++) { + try { + const r = await app.request(`/api/miniprogram/pay?orderSn=${encodeURIComponent(orderSn)}`, { method: 'GET', silent: true }) + if (r?.data?.status === 'paid') { + await this.refreshMatchCountAndStatus() + return + } + } catch (_) {} + if (i < maxAttempts - 1) await new Promise(r => setTimeout(r, interval)) + } + await this.refreshMatchCountAndStatus() + }, + + // 购买匹配次数(与购买章节逻辑一致,写入订单) async buyMatchCount() { this.setData({ showUnlockModal: false }) @@ -793,16 +826,17 @@ Page({ return } + const matchPrice = this.data.matchPrice || 1 // 邀请码:与章节支付一致,写入订单便于分销归属与对账 const referralCode = wx.getStorageSync('referral_code') || '' - // 调用支付接口购买匹配次数 + // 调用支付接口购买匹配次数(productType: match,订单类型:购买匹配次数) const res = await app.request('/api/miniprogram/pay', { method: 'POST', data: { openId, productType: 'match', productId: 'match_1', - amount: 1, + amount: matchPrice, description: '匹配次数x1', userId: app.globalData.userInfo?.id || '', referralCode: referralCode || undefined @@ -810,6 +844,7 @@ Page({ }) if (res.success && res.data?.payParams) { + const orderSn = res.data.orderSn // 调用微信支付 await new Promise((resolve, reject) => { wx.requestPayment({ @@ -818,13 +853,9 @@ Page({ fail: reject }) }) - - // 支付成功,增加匹配次数 - const extraMatches = (wx.getStorageSync('extra_match_count') || 0) + 1 - wx.setStorageSync('extra_match_count', extraMatches) - wx.showToast({ title: '购买成功', icon: 'success' }) - this.initUserStatus() + // 轮询订单状态,确认已支付后再刷新(不依赖 PayNotify 回调时机) + this.pollOrderAndRefresh(orderSn) } else { throw new Error(res.error || '创建订单失败') } @@ -832,14 +863,13 @@ Page({ if (e.errMsg && e.errMsg.includes('cancel')) { wx.showToast({ title: '已取消', icon: 'none' }) } else { - // 测试模式 + // 测试模式(无支付环境时本地模拟) wx.showModal({ title: '支付服务暂不可用', content: '是否使用测试模式购买?', success: (res) => { if (res.confirm) { - const extraMatches = (wx.getStorageSync('extra_match_count') || 0) + 1 - wx.setStorageSync('extra_match_count', extraMatches) + app.globalData.matchCount = (app.globalData.matchCount ?? 0) + 1 wx.showToast({ title: '测试购买成功', icon: 'success' }) this.initUserStatus() } diff --git a/miniprogram/pages/match/match.wxml b/miniprogram/pages/match/match.wxml index df47b547..2efda433 100644 --- a/miniprogram/pages/match/match.wxml +++ b/miniprogram/pages/match/match.wxml @@ -15,8 +15,8 @@ - - + + 今日免费次数已用完 购买次数 diff --git a/miniprogram/pages/my/my.js b/miniprogram/pages/my/my.js index 6a308a54..6a94ddfe 100644 --- a/miniprogram/pages/my/my.js +++ b/miniprogram/pages/my/my.js @@ -110,6 +110,27 @@ Page({ } }, + // 登录后刷新购买状态(与 match/read 一致,避免其他页面用旧数据) + async refreshPurchaseStatus() { + const userId = app.globalData.userInfo?.id + if (!userId) return + try { + const res = await app.request(`/api/miniprogram/user/purchase-status?userId=${encodeURIComponent(userId)}`) + if (res.success && res.data) { + app.globalData.hasFullBook = res.data.hasFullBook || false + app.globalData.purchasedSections = res.data.purchasedSections || [] + app.globalData.matchCount = res.data.matchCount ?? 0 + app.globalData.matchQuota = res.data.matchQuota || null + const userInfo = app.globalData.userInfo || {} + userInfo.hasFullBook = res.data.hasFullBook + userInfo.purchasedSections = res.data.purchasedSections + wx.setStorageSync('userInfo', userInfo) + } + } catch (e) { + console.log('[My] 刷新购买状态失败:', e) + } + }, + // 初始化用户状态 initUserStatus() { const { isLoggedIn, userInfo } = app.globalData @@ -538,6 +559,7 @@ Page({ try { const result = await app.login() if (result) { + await this.refreshPurchaseStatus() this.initUserStatus() this.setData({ showLoginModal: false, agreeProtocol: false }) wx.showToast({ title: '登录成功', icon: 'success' }) @@ -566,6 +588,7 @@ Page({ try { const result = await app.loginWithPhone(e.detail.code) if (result) { + await this.refreshPurchaseStatus() this.initUserStatus() this.setData({ showLoginModal: false }) wx.showToast({ title: '登录成功', icon: 'success' }) diff --git a/miniprogram/pages/purchases/purchases.js b/miniprogram/pages/purchases/purchases.js index f691ab39..46cbbca7 100644 --- a/miniprogram/pages/purchases/purchases.js +++ b/miniprogram/pages/purchases/purchases.js @@ -15,6 +15,10 @@ Page({ this.loadOrders() }, + onShow() { + this.loadOrders() + }, + async loadOrders() { this.setData({ loading: true }) try { diff --git a/miniprogram/pages/read/read.js b/miniprogram/pages/read/read.js index daf040a7..eab1ad7d 100644 --- a/miniprogram/pages/read/read.js +++ b/miniprogram/pages/read/read.js @@ -880,7 +880,9 @@ Page({ // 更新全局购买状态 app.globalData.hasFullBook = res.data.hasFullBook app.globalData.purchasedSections = res.data.purchasedSections || [] - + app.globalData.matchCount = res.data.matchCount ?? 0 + app.globalData.matchQuota = res.data.matchQuota || null + // 更新用户信息中的购买记录 const userInfo = app.globalData.userInfo || {} userInfo.hasFullBook = res.data.hasFullBook @@ -890,7 +892,8 @@ Page({ console.log('[Pay] ✅ 购买状态已刷新:', { hasFullBook: res.data.hasFullBook, - purchasedCount: res.data.purchasedSections.length + purchasedCount: res.data.purchasedSections.length, + matchCount: res.data.matchCount }) } } catch (e) { diff --git a/miniprogram/pages/referral/referral.js b/miniprogram/pages/referral/referral.js index fa4db3fd..6df3905e 100644 --- a/miniprogram/pages/referral/referral.js +++ b/miniprogram/pages/referral/referral.js @@ -59,6 +59,7 @@ Page({ showPosterModal: false, isGeneratingPoster: false, posterQrSrc: '', + posterQrFilePath: '', posterReferralLink: '', posterNickname: '', posterNicknameInitial: '', @@ -283,53 +284,6 @@ Page({ }) }, - // 分享到朋友圈 - 1:1 迁移 Next.js 的 handleShareToWechat - shareToWechat() { - const { referralCode } = this.data - const referralLink = `https://soul.quwanzhi.com/?ref=${referralCode}` - - // 与 Next.js 完全相同的文案 - const shareText = `📖 推荐一本好书《一场SOUL的创业实验场》 - -这是卡若每天早上6-9点在Soul派对房分享的真实商业故事,55个真实案例,讲透创业的底层逻辑。 - -👉 点击阅读: ${referralLink} - -#创业 #商业思维 #Soul派对` - - wx.setClipboardData({ - data: shareText, - success: () => { - wx.showModal({ - title: '朋友圈文案已复制!', - content: '打开微信 → 发朋友圈 → 粘贴即可', - showCancel: false, - confirmText: '知道了' - }) - } - }) - }, - - // 更多分享方式 - 1:1 迁移 Next.js 的 handleShare - handleMoreShare() { - const { referralCode } = this.data - const referralLink = `https://soul.quwanzhi.com/?ref=${referralCode}` - - // 与 Next.js 完全相同的文案 - const shareText = `我正在读《一场SOUL的创业实验场》,每天6-9点的真实商业故事,推荐给你!${referralLink}` - - wx.setClipboardData({ - data: shareText, - success: () => { - wx.showToast({ - title: '分享文案已复制', - icon: 'success', - duration: 2000 - }) - } - }) - }, - // 生成推广海报 - 1:1 对齐 Next.js 设计 async generatePoster() { wx.showLoading({ title: '生成中...', mask: true }) @@ -352,15 +306,18 @@ Page({ }, }) - if (!res || !res.success || !res.image) { + // 接口返回 { success, image: "data:image/png;base64,...", scene } + const imageData = res?.image || res?.data?.image + if (!res || !res.success || !imageData) { console.error('[Poster] 生成小程序码失败:', res) - throw new Error(res?.error || '生成小程序码失败') + throw new Error(res?.error || res?.message || '生成小程序码失败') } - // 后端返回的是 data:image/png;base64,... 需要先写入本地临时文件,再作为 的 src - const base64Data = String(res.image).replace(/^data:image\/\w+;base64,/, '') + // 小程序 image 组件支持 base64 格式,直接使用;同时写入本地供预览用 + const base64Str = String(imageData).trim() const fs = wx.getFileSystemManager() const filePath = `${wx.env.USER_DATA_PATH}/poster_qrcode_${Date.now()}.png` + const base64Data = base64Str.replace(/^data:image\/\w+;base64,/, '') await new Promise((resolve, reject) => { fs.writeFile({ @@ -375,10 +332,10 @@ Page({ }) }) - console.log('[Poster] 小程序码已保存到本地:', filePath) - + // 优先用 base64 直接显示(兼容性更好);预览时用本地路径 this.setData({ - posterQrSrc: filePath, + posterQrSrc: base64Str, + posterQrFilePath: filePath, posterReferralLink: '', // 小程序版本不再使用 H5 链接 posterNickname: nickname, posterNicknameInitial: (nickname || '用').charAt(0), @@ -389,7 +346,7 @@ Page({ console.error('[Poster] 生成二维码失败:', e) wx.hideLoading() wx.showToast({ title: '生成失败', icon: 'none' }) - this.setData({ showPosterModal: false, isGeneratingPoster: false, posterQrSrc: '', posterReferralLink: '' }) + this.setData({ showPosterModal: false, isGeneratingPoster: false, posterQrSrc: '', posterQrFilePath: '', posterReferralLink: '' }) } }, @@ -570,56 +527,15 @@ Page({ // 预览二维码 previewPosterQr() { - const { posterQrSrc } = this.data - if (!posterQrSrc) return - wx.previewImage({ urls: [posterQrSrc] }) + const { posterQrSrc, posterQrFilePath } = this.data + const url = posterQrFilePath || posterQrSrc + if (!url) return + wx.previewImage({ urls: [url] }) }, // 阻止冒泡 stopPropagation() {}, - // 分享到朋友圈 - 随机文案 - shareToMoments() { - // 10条随机文案,基于书的内容 - const shareTexts = [ - `🔥 在派对房里听到的真实故事,比虚构的小说精彩100倍!\n\n电动车出租月入5万、私域一年赚1000万、一个人的公司月入10万...\n\n62个真实案例,搜"Soul创业派对"小程序看全部!\n\n#创业 #私域 #商业`, - - `💡 今天终于明白:会赚钱的人,都在用"流量杠杆"\n\n抖音、Soul、飞书...同一套内容,撬动不同平台的流量。\n\n《Soul创业派对》里的实战方法,受用终身!\n\n#流量 #副业 #创业派对`, - - `📚 一个70后大健康私域,一个月150万流水是怎么做到的?\n\n答案在《Soul创业派对》第9章,全是干货。\n\n搜小程序"Soul创业派对",我在里面等你\n\n#大健康 #私域运营 #真实案例`, - - `🎯 "分钱不是分你的钱,是分不属于对方的钱"\n\n这句话改变了我对商业合作的认知。\n\n推荐《Soul创业派对》,创业者必读!\n\n#云阿米巴 #商业思维 #创业`, - - `✨ 资源整合高手的社交方法论,在派对房里学到了\n\n"先让对方赚到钱,自己才能长久赚钱"\n\n这本《Soul创业派对》,每章都是实战经验\n\n#资源整合 #社交 #创业故事`, - - `🚀 AI工具推广:一个隐藏的高利润赛道\n\n客单价高、复购率高、需求旺盛...\n\n《Soul创业派对》里的商业机会,你发现了吗?\n\n#AI #副业 #商业机会`, - - `💰 美业整合:一个人的公司如何月入十万?\n\n不开店、不囤货、轻资产运营...\n\n《Soul创业派对》告诉你答案!\n\n#美业 #轻创业 #月入十万`, - - `🌟 3000万流水是怎么跑出来的?\n\n不是靠运气,是靠系统。\n\n《Soul创业派对》里的电商底层逻辑,值得反复看\n\n#电商 #创业 #商业系统`, - - `📖 "人与人之间的关系,归根结底就三个东西:利益、情感、价值观"\n\n在派对房里聊出的金句,都在《Soul创业派对》里\n\n#人性 #商业 #创业派对`, - - `🔔 未来职业的三个方向:技术型、资源型、服务型\n\n你属于哪一种?\n\n《Soul创业派对》帮你找到答案!\n\n#职业规划 #创业 #未来` - ] - - // 随机选择一条文案 - const randomIndex = Math.floor(Math.random() * shareTexts.length) - const shareText = shareTexts[randomIndex] - - wx.setClipboardData({ - data: shareText, - success: () => { - wx.showModal({ - title: '文案已复制', - content: '请打开微信朋友圈,粘贴分享文案,配合推广海报一起发布效果更佳!\n\n再次点击可获取新的随机文案', - showCancel: false, - confirmText: '去发朋友圈' - }) - } - }) - }, - // 提现 - 直接到微信零钱 async handleWithdraw() { const availableEarnings = this.data.availableEarningsNum || 0 @@ -891,16 +807,6 @@ Page({ } }, - // 分享到朋友圈 - onShareTimeline() { - console.log('[Referral] 分享到朋友圈,推荐码:', this.data.referralCode) - return { - title: `Soul创业派对 - 62个真实商业案例`, - query: `ref=${this.data.referralCode}` - // 不设置 imageUrl,使用小程序默认截图 - } - }, - goBack() { wx.navigateBack() }, diff --git a/miniprogram/pages/referral/referral.wxml b/miniprogram/pages/referral/referral.wxml index eb129a40..d6940044 100644 --- a/miniprogram/pages/referral/referral.wxml +++ b/miniprogram/pages/referral/referral.wxml @@ -9,9 +9,6 @@ - - - 分销中心 @@ -179,28 +176,6 @@ - - - - - - - - - - - - - - - - - - - - - - @@ -239,14 +214,7 @@ - - - - - - 暂无收益记录 - 分享专属链接,好友购买即可获得 {{shareRate}}% 返利 - + @@ -315,7 +283,7 @@ - + 0 { + return int(v) + } + return defaultFreeMatchLimit +} + +// GetMatchQuota 根据订单和 match_records 纯计算用户匹配配额 +func GetMatchQuota(db *gorm.DB, userID string, freeLimit int) MatchQuota { + if freeLimit <= 0 { + freeLimit = defaultFreeMatchLimit + } + var purchasedTotal int64 + db.Model(&model.Order{}).Where("user_id = ? AND product_type = ? AND status = ?", userID, "match", "paid").Count(&purchasedTotal) + var matchesToday int64 + db.Model(&model.MatchRecord{}).Where("user_id = ? AND created_at >= CURDATE()", userID).Count(&matchesToday) + // 历史每日超出免费部分之和 = 已消耗的购买次数 + var purchasedUsed int64 + db.Raw(` + SELECT COALESCE(SUM(cnt - ?), 0) FROM ( + SELECT DATE(created_at) AS d, COUNT(*) AS cnt + FROM match_records WHERE user_id = ? + GROUP BY DATE(created_at) + HAVING cnt > ? + ) t + `, freeLimit, userID, freeLimit).Scan(&purchasedUsed) + freeUsed := matchesToday + if freeUsed > int64(freeLimit) { + freeUsed = int64(freeLimit) + } + freeRemain := int64(freeLimit) - freeUsed + if freeRemain < 0 { + freeRemain = 0 + } + purchasedRemain := purchasedTotal - purchasedUsed + if purchasedRemain < 0 { + purchasedRemain = 0 + } + remainToday := freeRemain + purchasedRemain + return MatchQuota{ + PurchasedTotal: purchasedTotal, + PurchasedUsed: purchasedUsed, + MatchesUsedToday: matchesToday, + FreeRemainToday: freeRemain, + PurchasedRemain: purchasedRemain, + RemainToday: remainToday, + } +} + var defaultMatchTypes = []gin.H{ gin.H{"id": "partner", "label": "创业合伙", "matchLabel": "创业伙伴", "icon": "⭐", "matchFromDB": true, "showJoinAfterMatch": false, "price": 1, "enabled": true}, gin.H{"id": "investor", "label": "资源对接", "matchLabel": "资源对接", "icon": "👥", "matchFromDB": false, "showJoinAfterMatch": true, "price": 1, "enabled": true}, @@ -83,17 +155,38 @@ func MatchUsers(c *gin.Context) { var body struct { UserID string `json:"userId" binding:"required"` MatchType string `json:"matchType"` + Phone string `json:"phone"` + WechatID string `json:"wechatId"` } if err := c.ShouldBindJSON(&body); err != nil { c.JSON(http.StatusBadRequest, gin.H{"success": false, "message": "缺少用户ID"}) return } + db := database.DB() + // 全书用户无限制,否则校验今日剩余次数 + var user model.User + skipQuota := false + if err := db.Where("id = ?", body.UserID).First(&user).Error; err == nil { + skipQuota = user.HasFullBook != nil && *user.HasFullBook + } + if !skipQuota { + freeLimit := getFreeMatchLimit(db) + quota := GetMatchQuota(db, body.UserID, freeLimit) + if quota.RemainToday <= 0 { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": "今日匹配次数已用完,请购买更多次数", + "code": "QUOTA_EXCEEDED", + }) + return + } + } // 只匹配已绑定微信或手机号的用户 var users []model.User - q := database.DB().Where("id != ?", body.UserID). + q := db.Where("id != ?", body.UserID). Where("((wechat_id IS NOT NULL AND wechat_id != '') OR (phone IS NOT NULL AND phone != ''))") if err := q.Order("created_at DESC").Limit(20).Find(&users).Error; err != nil || len(users) == 0 { - c.JSON(http.StatusOK, gin.H{"success": false, "message": "暂无匹配用户", "data": nil}) + c.JSON(http.StatusOK, gin.H{"success": false, "message": "暂无匹配用户", "data": nil, "code": "NO_USERS"}) return } // 随机选一个 @@ -124,6 +217,25 @@ func MatchUsers(c *gin.Context) { if tag == "" { tag = "找伙伴" } + // 写入匹配记录(含发起者的 phone/wechat_id 便于后续联系) + rec := model.MatchRecord{ + ID: fmt.Sprintf("mr_%d", time.Now().UnixNano()), + UserID: body.UserID, + MatchedUserID: r.ID, + MatchType: body.MatchType, + } + if body.MatchType == "" { + rec.MatchType = "partner" + } + if body.Phone != "" { + rec.Phone = &body.Phone + } + if body.WechatID != "" { + rec.WechatID = &body.WechatID + } + if err := db.Create(&rec).Error; err != nil { + fmt.Printf("[MatchUsers] 写入 match_records 失败: %v\n", err) + } c.JSON(http.StatusOK, gin.H{ "success": true, "data": gin.H{ diff --git a/soul-api/internal/handler/miniprogram.go b/soul-api/internal/handler/miniprogram.go index 175835a4..9341295c 100644 --- a/soul-api/internal/handler/miniprogram.go +++ b/soul-api/internal/handler/miniprogram.go @@ -219,6 +219,8 @@ func miniprogramPayPost(c *gin.Context) { if description == "" { if req.ProductType == "fullbook" { description = "《一场Soul的创业实验》全书" + } else if req.ProductType == "match" { + description = "购买匹配次数" } else { description = fmt.Sprintf("章节购买-%s", req.ProductID) } @@ -311,7 +313,7 @@ func miniprogramPayPost(c *gin.Context) { }) } -// GET - 查询订单状态 +// GET - 查询订单状态(并主动同步:若微信已支付但本地未标记,则更新本地订单,便于配额即时生效) func miniprogramPayGet(c *gin.Context) { orderSn := c.Query("orderSn") if orderSn == "" { @@ -336,6 +338,18 @@ func miniprogramPayGet(c *gin.Context) { switch tradeState { case "SUCCESS": status = "paid" + // 若微信已支付,主动同步到本地 orders(不等 PayNotify),便于购买次数即时生效 + db := database.DB() + var order model.Order + if err := db.Where("order_sn = ?", orderSn).First(&order).Error; err == nil && order.Status != nil && *order.Status != "paid" { + now := time.Now() + db.Model(&order).Updates(map[string]interface{}{ + "status": "paid", + "transaction_id": transactionID, + "pay_time": now, + }) + fmt.Printf("[PayGet] 主动同步订单已支付: %s\n", orderSn) + } case "CLOSED", "REVOKED", "PAYERROR": status = "failed" case "REFUND": @@ -429,6 +443,8 @@ func MiniprogramPayNotify(c *gin.Context) { if attach.ProductType == "fullbook" { db.Model(&model.User{}).Where("id = ?", buyerUserID).Update("has_full_book", true) fmt.Printf("[PayNotify] 用户已购全书: %s\n", buyerUserID) + } else if attach.ProductType == "match" { + fmt.Printf("[PayNotify] 用户购买匹配次数: %s,订单 %s\n", buyerUserID, orderSn) } else if attach.ProductType == "section" && attach.ProductID != "" { var count int64 db.Model(&model.Order{}).Where( diff --git a/soul-api/internal/handler/user.go b/soul-api/internal/handler/user.go index 50583e64..dc1e7ca1 100644 --- a/soul-api/internal/handler/user.go +++ b/soul-api/internal/handler/user.go @@ -276,6 +276,9 @@ func UserPurchaseStatus(c *gin.Context) { purchasedSections = append(purchasedSections, r.ProductID) } } + // 匹配次数配额:纯计算(订单 + match_records) + freeLimit := getFreeMatchLimit(db) + matchQuota := GetMatchQuota(db, userId, freeLimit) earnings := 0.0 if user.Earnings != nil { earnings = *user.Earnings @@ -288,8 +291,17 @@ func UserPurchaseStatus(c *gin.Context) { "hasFullBook": user.HasFullBook != nil && *user.HasFullBook, "purchasedSections": purchasedSections, "purchasedCount": len(purchasedSections), - "earnings": earnings, - "pendingEarnings": pendingEarnings, + "matchCount": matchQuota.PurchasedTotal, + "matchQuota": gin.H{ + "purchasedTotal": matchQuota.PurchasedTotal, + "purchasedUsed": matchQuota.PurchasedUsed, + "matchesUsedToday": matchQuota.MatchesUsedToday, + "freeRemainToday": matchQuota.FreeRemainToday, + "purchasedRemain": matchQuota.PurchasedRemain, + "remainToday": matchQuota.RemainToday, + }, + "earnings": earnings, + "pendingEarnings": pendingEarnings, }}) } diff --git a/soul-api/internal/model/match_record.go b/soul-api/internal/model/match_record.go new file mode 100644 index 00000000..2db72b55 --- /dev/null +++ b/soul-api/internal/model/match_record.go @@ -0,0 +1,18 @@ +package model + +import "time" + +// MatchRecord 匹配记录,每次用户成功匹配时写入 +type MatchRecord struct { + ID string `gorm:"column:id;primaryKey;size:50" json:"id"` + UserID string `gorm:"column:user_id;index;size:50;not null" json:"userId"` + MatchType string `gorm:"column:match_type;index;size:50" json:"matchType"` + Phone *string `gorm:"column:phone;size:20" json:"phone"` + WechatID *string `gorm:"column:wechat_id;size:100" json:"wechatId"` + MatchedUserID string `gorm:"column:matched_user_id;index;size:50" json:"matchedUserId"` + MatchScore *int `gorm:"column:match_score" json:"matchScore"` + Status *string `gorm:"column:status;size:20" json:"status"` + CreatedAt time.Time `gorm:"column:created_at" json:"createdAt"` +} + +func (MatchRecord) TableName() string { return "match_records" } diff --git a/soul-api/soul-api b/soul-api/soul-api index 42bfd560..004d7bd0 100644 Binary files a/soul-api/soul-api and b/soul-api/soul-api differ diff --git a/soul-api/wechat/info.log b/soul-api/wechat/info.log index 9214fa49..0ce2aed4 100644 --- a/soul-api/wechat/info.log +++ b/soul-api/wechat/info.log @@ -86,3 +86,21 @@ {"level":"debug","timestamp":"2026-02-11T15:36:10+08:00","caller":"kernel/accessToken.go:383","content":"------------------response content:HTTP/1.1 200 OK\r\nContent-Length: 174\r\nConnection: keep-alive\r\nContent-Type: application/json; encoding=utf-8\r\nDate: Wed, 11 Feb 2026 07:36:11 GMT\r\n\r\n{\"access_token\":\"101_MiWs49YphSqT8fdzbbvWznN0pNKQ99O94IqbcNCL6FoMWAZG6t0v-D4xg37xDjsyGgttZm2qfQd1pg6C4za3yiJrKv8g7s8M1n_-biSvQq-zNg6WqUzq1zTv_HQVRQaACALHG\",\"expires_in\":7200}"} {"level":"debug","timestamp":"2026-02-11T15:36:11+08:00","caller":"kernel/baseClient.go:457","content":"GET https://api.weixin.qq.com/sns/jscode2session?access_token=101_MiWs49YphSqT8fdzbbvWznN0pNKQ99O94IqbcNCL6FoMWAZG6t0v-D4xg37xDjsyGgttZm2qfQd1pg6C4za3yiJrKv8g7s8M1n_-biSvQq-zNg6WqUzq1zTv_HQVRQaACALHG&appid=wxb8bbb2b10dec74aa&grant_type=authorization_code&js_code=0a1alR000sNRQV1MJl400aJVgF3alR0q&secret=3c1fb1f63e6e052222bbcead9d07fe0c request header: { Accept:*/*} "} {"level":"debug","timestamp":"2026-02-11T15:36:11+08:00","caller":"kernel/baseClient.go:459","content":"------------------response content:HTTP/1.1 200 OK\r\nContent-Length: 82\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nDate: Wed, 11 Feb 2026 07:36:11 GMT\r\n\r\n{\"session_key\":\"XL+GLsPRGyvEHUMFyzfbZg==\",\"openid\":\"ogpTW5a9exdEmEwqZsYywvgSpSQg\"}"} +{"level":"debug","timestamp":"2026-02-11T16:20:46+08:00","caller":"kernel/accessToken.go:381","content":"GET https://api.weixin.qq.com/cgi-bin/token?appid=wxb8bbb2b10dec74aa&grant_type=client_credential&neededText=&secret=3c1fb1f63e6e052222bbcead9d07fe0c request header: { Accept:*/*} "} +{"level":"debug","timestamp":"2026-02-11T16:20:46+08:00","caller":"kernel/accessToken.go:383","content":"------------------response content:HTTP/1.1 200 OK\r\nContent-Length: 174\r\nConnection: keep-alive\r\nContent-Type: application/json; encoding=utf-8\r\nDate: Wed, 11 Feb 2026 08:20:47 GMT\r\n\r\n{\"access_token\":\"101_lk18062WH51Gpyxwf5BwvAXTDN6YbytrNdSAQ1G7-PQhmUT3FSRxP4cGbzjtYLtbJuSOenZRMn2O5-AJkY_B5nmy5Ldj5vcnTebHMdDv5iv2w3sExLP0OOe1_FkBSAbAIANEM\",\"expires_in\":7200}"} +{"level":"debug","timestamp":"2026-02-11T16:20:47+08:00","caller":"kernel/baseClient.go:457","content":"GET https://api.weixin.qq.com/sns/jscode2session?access_token=101_lk18062WH51Gpyxwf5BwvAXTDN6YbytrNdSAQ1G7-PQhmUT3FSRxP4cGbzjtYLtbJuSOenZRMn2O5-AJkY_B5nmy5Ldj5vcnTebHMdDv5iv2w3sExLP0OOe1_FkBSAbAIANEM&appid=wxb8bbb2b10dec74aa&grant_type=authorization_code&js_code=0f1M3U000LdYQV154D000I1gkx3M3U0B&secret=3c1fb1f63e6e052222bbcead9d07fe0c request header: { Accept:*/*} "} +{"level":"debug","timestamp":"2026-02-11T16:20:47+08:00","caller":"kernel/baseClient.go:459","content":"------------------response content:HTTP/1.1 200 OK\r\nContent-Length: 82\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nDate: Wed, 11 Feb 2026 08:20:47 GMT\r\n\r\n{\"session_key\":\"i8aGTGPaJoixERiDuV/inQ==\",\"openid\":\"ogpTW5a9exdEmEwqZsYywvgSpSQg\"}"} +{"level":"debug","timestamp":"2026-02-11T16:39:49+08:00","caller":"kernel/baseClient.go:457","content":"POST https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi request header: { Accept:*/*Content-Type:application/jsonAuthorization:WECHATPAY2-SHA256-RSA2048 mchid=\"1318592501\",nonce_str=\"jhAt7g06fRGod5bdlsnrFiHAb2RHZ0A1\",timestamp=\"1770799189\",serial_no=\"4A1DB62CD5C9BE0B6FC51C30621D6F99686E75C5\",signature=\"KsbmL7ZKtrjOw0jeM9k0lPUNf2Tf/aeBoxByHCsap5JekU2bkLucMJT69VRhflR18wZgWEceYoZwyitzy73eCkKNGIlF6tgywEEWysPMRGtppDykcrQh32SF8X9Gml3L+auUx7GsqJc28XgByF5tu9b1NzfVrIsxH1ZPsvz2UwnkrEBX2HDE1zRi+0s5LaxG2RMucrxWBHlMwS455s3XT8kipdGMVd5IAJn7S5mLOOu5oC6vKMGW52dFjhj3+I+mvVROOfXzB8XYVjM2B9j+47rrBeSkPMv+Gwk7W0rbJaLS/88a5G8DIXYPTD13cpaPPpC2c1iQuiJvb22emZ5ctA==\"} request body:"} +{"level":"debug","timestamp":"2026-02-11T16:39:49+08:00","caller":"kernel/baseClient.go:459","content":"------------------response content:HTTP/1.1 200 OK\r\nContent-Length: 52\r\nCache-Control: no-cache, must-revalidate\r\nConnection: keep-alive\r\nContent-Language: zh-CN\r\nContent-Type: application/json; charset=utf-8\r\nDate: Wed, 11 Feb 2026 08:39:50 GMT\r\nKeep-Alive: timeout=8\r\nRequest-Id: 08D580B1CC0610E70618D59F85AB0120F8F61428C4F202-0\r\nServer: nginx\r\nWechatpay-Nonce: 195ca7b0f0d94d96fff77025192f360a\r\nWechatpay-Serial: 5F2543BF58239A4EB68FA4433DF1438A88B34B16\r\nWechatpay-Signature: EAzms6bPJlftvkZMl7cPqplpuKY2AD5tnjppqV1ktAu5uh8e3zoSX5g2LvJmH4Bch+8Jkg/c7kN8kJGKBNEdhpA6nEW0h9CWo3Xqv7osY7xJP0VBH/or26WLoh4kgVjuKD7K+usEUIM55epJOZlrjQvHIbzaH0JzVPWLMuyLPJFJZhJv+Ha1j0NbtNmxzHN5SOD/nY0SQf6cc4kUXyP6c6XZBlNEDNvJyszrylw4Ant8yvFn6VMg88fioXZ/S7xCVLAGWwiUWQ0WAHSoixkpp+BHUYySOt2PGngkE8xrc8mcoJe7GNl/fC7lzB8H3hKuFCyJaTRno3H3OKiy2BT2Ow==\r\nWechatpay-Signature-Type: WECHATPAY2-SHA256-RSA2048\r\nWechatpay-Timestamp: 1770799190\r\nX-Content-Type-Options: nosniff\r\n\r\n{\"prepay_id\":\"wx1116395007245943fdc7aa37fd9b4c0000\"}"} +{"level":"debug","timestamp":"2026-02-11T16:44:11+08:00","caller":"kernel/baseClient.go:457","content":"POST https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi request header: { Content-Type:application/jsonAuthorization:WECHATPAY2-SHA256-RSA2048 mchid=\"1318592501\",nonce_str=\"NnyI9wSbe0fA4LPSVRIigUZVBDmmFZnx\",timestamp=\"1770799451\",serial_no=\"4A1DB62CD5C9BE0B6FC51C30621D6F99686E75C5\",signature=\"Tsh01GKm2uKgLh4aGpudgU0S7BfnH94OL9wMNtuwMxme6dOd84cBLX6VparCDqAlz4jeYK2zO119pAfRdYx0ENkPBroHUC8x1w52JP2uo9NijXpaYc+tvKtZxFqUzUwCQzOsU2C94PXQTyxzRjnc+e/Yz+7bxwReCW5LluOjIDF6lZPSVXahIUbwrgwTLkomhLOOmEwtVrypUtjQKUo251REnW/EgFfdxmromzQByAmi4i28fkFz8Ou0KihOHGcjzXjy/MA2CKlvQgmA3uDZyqID1G8uPWr78dZBRmIwRTVdH7NyKEIFqMibkHbm0hIig1RGg2CnwNPfMHs2F9lBQw==\"Accept:*/*} request body:"} +{"level":"debug","timestamp":"2026-02-11T16:44:11+08:00","caller":"kernel/baseClient.go:459","content":"------------------response content:HTTP/1.1 200 OK\r\nContent-Length: 52\r\nCache-Control: no-cache, must-revalidate\r\nConnection: keep-alive\r\nContent-Language: zh-CN\r\nContent-Type: application/json; charset=utf-8\r\nDate: Wed, 11 Feb 2026 08:44:12 GMT\r\nKeep-Alive: timeout=8\r\nRequest-Id: 08DC82B1CC0610581895B2F8AF0120F8F30A28E571-0\r\nServer: nginx\r\nWechatpay-Nonce: f49bcd1b0942e2ed44f872ff8acd2e7a\r\nWechatpay-Serial: 5F2543BF58239A4EB68FA4433DF1438A88B34B16\r\nWechatpay-Signature: o1wTiMG0gMoT4Y7dxxV8XXKmERWwYlA1XFc/FU7R07SlJcCczvA+mx8sYpuxLK0Nd+fvavGI7bvyLn7QK2kbA5+kXmE8bhzvZStavPkleWoNMdnwYRSz5QlrB0//T3o4NVkLU+g8De9jYJW7FCdBrxhib7n+Tn4CIhVygkzBWiL8CfC+dXtjOyj/OzKjlxocNsSZ7DVPargq5fvANpNPXx9j2d/oXbuU1oRZum+EK1YlHdLYU8J9PfYauIUuXOBF8RShBChI3otE4IxUan2lgmMjbQNo3PMWpV2VIQI/mGzgD+5dW1dWgPEteVgZBW43jBFv4ub9h4JHda3EAs7Rlg==\r\nWechatpay-Signature-Type: WECHATPAY2-SHA256-RSA2048\r\nWechatpay-Timestamp: 1770799452\r\nX-Content-Type-Options: nosniff\r\n\r\n{\"prepay_id\":\"wx1116441226578302ef3124977d11d20001\"}"} +{"level":"debug","timestamp":"2026-02-11T16:46:30+08:00","caller":"kernel/accessToken.go:381","content":"GET https://api.weixin.qq.com/cgi-bin/token?appid=wxb8bbb2b10dec74aa&grant_type=client_credential&neededText=&secret=3c1fb1f63e6e052222bbcead9d07fe0c request header: { Accept:*/*} "} +{"level":"debug","timestamp":"2026-02-11T16:46:30+08:00","caller":"kernel/accessToken.go:383","content":"------------------response content:HTTP/1.1 200 OK\r\nContent-Length: 174\r\nConnection: keep-alive\r\nContent-Type: application/json; encoding=utf-8\r\nDate: Wed, 11 Feb 2026 08:46:31 GMT\r\n\r\n{\"access_token\":\"101_-JXKwbzx_ltAKNx0Nvh-kvqQWBK8PgN5fjiYuTcBD3jmDSaL23BVjOL_ISi6uEPdc2BtC7kKBHRVUZu6VVVRYRcf-G4_GoktaSxLTv-YYw0RjlZbXkVrOV7aKLsXNZiAEAAQC\",\"expires_in\":7200}"} +{"level":"debug","timestamp":"2026-02-11T16:46:31+08:00","caller":"kernel/baseClient.go:457","content":"GET https://api.weixin.qq.com/sns/jscode2session?access_token=101_-JXKwbzx_ltAKNx0Nvh-kvqQWBK8PgN5fjiYuTcBD3jmDSaL23BVjOL_ISi6uEPdc2BtC7kKBHRVUZu6VVVRYRcf-G4_GoktaSxLTv-YYw0RjlZbXkVrOV7aKLsXNZiAEAAQC&appid=wxb8bbb2b10dec74aa&grant_type=authorization_code&js_code=0f17hPll2I3A9h4hTJll2pm1lE17hPlQ&secret=3c1fb1f63e6e052222bbcead9d07fe0c request header: { Accept:*/*} "} +{"level":"debug","timestamp":"2026-02-11T16:46:31+08:00","caller":"kernel/baseClient.go:459","content":"------------------response content:HTTP/1.1 200 OK\r\nContent-Length: 82\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nDate: Wed, 11 Feb 2026 08:46:31 GMT\r\n\r\n{\"session_key\":\"Y4B1/RLT06xbdfVXH1y00A==\",\"openid\":\"ogpTW5a9exdEmEwqZsYywvgSpSQg\"}"} +{"level":"debug","timestamp":"2026-02-11T16:50:45+08:00","caller":"kernel/accessToken.go:381","content":"GET https://api.weixin.qq.com/cgi-bin/token?appid=wxb8bbb2b10dec74aa&grant_type=client_credential&neededText=&secret=3c1fb1f63e6e052222bbcead9d07fe0c request header: { Accept:*/*} "} +{"level":"debug","timestamp":"2026-02-11T16:50:45+08:00","caller":"kernel/accessToken.go:383","content":"------------------response content:HTTP/1.1 200 OK\r\nContent-Length: 174\r\nConnection: keep-alive\r\nContent-Type: application/json; encoding=utf-8\r\nDate: Wed, 11 Feb 2026 08:50:46 GMT\r\n\r\n{\"access_token\":\"101_Yr_zLiETXFJ5MPdat-RxAdZxz_TaLqEmMI6COssI8tBokjwMppwVj2gzWfgvzujOnxIVWl7gFQF5R7OlqoCjUxYk_Hlak4eNxf--HGARagPByKlJnrjN6_SQ4L4PGZeACAGXQ\",\"expires_in\":7200}"} +{"level":"debug","timestamp":"2026-02-11T16:50:45+08:00","caller":"kernel/baseClient.go:457","content":"GET https://api.weixin.qq.com/sns/jscode2session?access_token=101_Yr_zLiETXFJ5MPdat-RxAdZxz_TaLqEmMI6COssI8tBokjwMppwVj2gzWfgvzujOnxIVWl7gFQF5R7OlqoCjUxYk_Hlak4eNxf--HGARagPByKlJnrjN6_SQ4L4PGZeACAGXQ&appid=wxb8bbb2b10dec74aa&grant_type=authorization_code&js_code=0d1KVG1w3BkPu634hJ3w34KXVM0KVG19&secret=3c1fb1f63e6e052222bbcead9d07fe0c request header: { Accept:*/*} "} +{"level":"debug","timestamp":"2026-02-11T16:50:45+08:00","caller":"kernel/baseClient.go:459","content":"------------------response content:HTTP/1.1 200 OK\r\nContent-Length: 82\r\nConnection: keep-alive\r\nContent-Type: text/plain\r\nDate: Wed, 11 Feb 2026 08:50:46 GMT\r\n\r\n{\"session_key\":\"aKCaNGx074vk1YgW+4KQ5A==\",\"openid\":\"ogpTW5a9exdEmEwqZsYywvgSpSQg\"}"} +{"level":"debug","timestamp":"2026-02-11T16:52:52+08:00","caller":"kernel/baseClient.go:457","content":"POST https://api.mch.weixin.qq.com/v3/pay/transactions/jsapi request header: { Content-Type:application/jsonAuthorization:WECHATPAY2-SHA256-RSA2048 mchid=\"1318592501\",nonce_str=\"6tRA1S0Oj3gSjC4PJN9Bw8aG0TiMl475\",timestamp=\"1770799971\",serial_no=\"4A1DB62CD5C9BE0B6FC51C30621D6F99686E75C5\",signature=\"A+VEHo9xo5LHllJAOIHW9agRd2CY9IyjsvjJTTVCBccRvuxSekj737zPyRSy9sTP82iRF9n94P0EPwHaqud8EaOMa2ErIDp6KJbqTYXlw+ZhoNO9VafIGKA3c/RASOU15ut5GuYx4KpYXmUUicuzhd7boKSlLI6vDQtSFQ/kT+0gTr5FZ8kqluEWtngHGJ6C78GLqajmK+1GbygMLWyTYuPTJNPMeb6+ifl7UI8glXXHpNronQEQViu8TrXAJ62o9Dxrc/QYd2XyrokrAAyNboNG0IZSJNogrdDFxwQ4zmHp2Yozq9zwpMUboU997O7dbg7+waYSLlNwnHjGZNJNmg==\"Accept:*/*} request body:"} +{"level":"debug","timestamp":"2026-02-11T16:52:52+08:00","caller":"kernel/baseClient.go:459","content":"------------------response content:HTTP/1.1 200 OK\r\nContent-Length: 52\r\nCache-Control: no-cache, must-revalidate\r\nConnection: keep-alive\r\nContent-Language: zh-CN\r\nContent-Type: application/json; charset=utf-8\r\nDate: Wed, 11 Feb 2026 08:52:52 GMT\r\nKeep-Alive: timeout=8\r\nRequest-Id: 08E486B1CC0610F80318B8CBC05520DAD417289DAA03-0\r\nServer: nginx\r\nWechatpay-Nonce: c44d544b1ecbf0d4f0ab45965b9b5979\r\nWechatpay-Serial: 5F2543BF58239A4EB68FA4433DF1438A88B34B16\r\nWechatpay-Signature: Ayy1/SGwhrYOS9JplecPKkEuS7jYKpgtxxDjVy+eGiUH1V0olSn9xzEx2HZ8LHasqGln3fhWhgkfwqddZ2rp003IJtb/R8iFn1LWoDBZw/JLFotm3Iegc9O9/sy5IxpDwOWN5Gi5KEhqxHQTta2JvSBrxnd60+GbAAd21LYzI5DHsXSc85UZZ11/MWzWwB3zvLqnpNh7PEbUyQj9+nNxeyuI59cLTDITELXa+Mrv9MpM4+raePq6VuAPqgiR2Ymf30iiggdqQjerqXWz2wIjg03Fob8XZTEnQMjYgwac6Oa4ESu7n4N/XesvopqyFsrK28chgSJqVX4SOpXIukiTnA==\r\nWechatpay-Signature-Type: WECHATPAY2-SHA256-RSA2048\r\nWechatpay-Timestamp: 1770799972\r\nX-Content-Type-Options: nosniff\r\n\r\n{\"prepay_id\":\"wx11165252691461617b158c058e434d0001\"}"} diff --git a/tmp/build-errors.log b/tmp/build-errors.log index 0efc0f31..b27e5257 100644 --- a/tmp/build-errors.log +++ b/tmp/build-errors.log @@ -1 +1 @@ -exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file +exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1exit status 1 \ No newline at end of file