Compare commits

...

2 Commits

4 changed files with 68 additions and 31 deletions
+1 -1
View File
@@ -1,7 +1,7 @@
{ {
"name": "宇之然AI维度", "name": "宇之然AI维度",
"appid": "wxdad62baf4ccd09e3", "appid": "wxdad62baf4ccd09e3",
"versionName": "1.3.0", "versionName": "1.4.0",
"versionCode": "100", "versionCode": "100",
"description": "AI 知识探索社区 — 从5个维度系统理解人工智能", "description": "AI 知识探索社区 — 从5个维度系统理解人工智能",
"uni-app": { "uni-app": {
+8 -2
View File
@@ -108,7 +108,7 @@
</template> </template>
<script setup> <script setup>
import { onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app' import { onShow, onShareAppMessage, onShareTimeline } from '@dcloudio/uni-app'
import { ref, computed, onMounted } from 'vue' import { ref, computed, onMounted } from 'vue'
import { useUserStore } from '@/stores/user' import { useUserStore } from '@/stores/user'
import { knowledgeApi, aiChatApi, request } from '@/utils/api' import { knowledgeApi, aiChatApi, request } from '@/utils/api'
@@ -174,9 +174,15 @@ const quotaPercent = computed(() => {
onMounted(() => { onMounted(() => {
loadStats() loadStats()
if (userStore.isLoggedIn) { if (userStore.isLoggedIn) {
loadVpayInfo()
userStore.fetchMe() userStore.fetchMe()
loadCollected() loadCollected()
}
})
// 每次进入/返回本页都刷新余额与额度(充值页支付成功后返回时余额同步)
onShow(() => {
if (userStore.isLoggedIn) {
loadVpayInfo()
loadQuota() loadQuota()
} }
}) })
+10 -2
View File
@@ -120,8 +120,16 @@ async function recharge() {
// #endif // #endif
uni.showToast({ title: '充值成功!', icon: 'success' }) uni.showToast({ title: '充值成功!', icon: 'success' })
// 刷新余额 // 刷新余额后返回"我的"(该页 onShow 会重新拉取余额与额度)
info.value = await request('GET', '/api/vpay/info', null, userStore.token) try {
info.value = await request('GET', '/api/vpay/info', null, userStore.token)
} catch (e) {}
setTimeout(() => {
uni.navigateBack({
delta: 1,
fail: () => uni.switchTab({ url: '/pages/profile/profile' }),
})
}, 1200)
} catch (e) { } catch (e) {
const msg = e.message || '支付未完成' const msg = e.message || '支付未完成'
if (msg.includes('cancel')) { if (msg.includes('cancel')) {
+49 -26
View File
@@ -48,7 +48,7 @@ module.exports = function registerWxMsgRoutes(app) {
const event = fields.Event || fields.event || '' const event = fields.Event || fields.event || ''
console.log('[wx-msg] 收到推送 Event=' + event, JSON.stringify(fields).slice(0, 500)) console.log('[wx-msg] 收到推送 Event=' + event, JSON.stringify(fields).slice(0, 500))
// 代币充值到账:改写用户星尘余额 + 订单状态 // 代币充值到账:改写用户星尘余额 + 订单状态 + 写星尘流水(明细页数据源)
if (event === 'xpay_coin_pay_notify') { if (event === 'xpay_coin_pay_notify') {
try { try {
const { User, Order, CoinLedger } = require('../models') const { User, Order, CoinLedger } = require('../models')
@@ -61,43 +61,66 @@ module.exports = function registerWxMsgRoutes(app) {
) )
console.log(`[wx-msg] 充值回调解析: order=${outTradeNo} coins=${coins} coinInfo=${JSON.stringify(coinInfo)}`) console.log(`[wx-msg] 充值回调解析: order=${outTradeNo} coins=${coins} coinInfo=${JSON.stringify(coinInfo)}`)
if (outTradeNo && coins > 0) { if (outTradeNo && coins > 0) {
// 先查订单,没有也直接给用户加币(Attach 里带了 uid) // 幂等:微信会重推,同一订单只入账一次(以流水为准)
const order = await Order.findOne({ orderNo: outTradeNo }) const dup = await CoinLedger.findOne({ orderNo: outTradeNo, type: 'recharge' })
if (order && order.status === 'pending') { if (dup) {
await Order.updateOne({ _id: order._id }, { status: 'paid', paidAt: new Date() }) console.log(`[wx-msg] 重复推送已忽略: ${outTradeNo}`)
await User.updateOne({ _id: order.userId }, { $inc: { vpayCoins: coins } })
console.log(`[wx-msg] ✓ 星尘充值到账(有订单): ${outTradeNo} +${coins} → user=${order.userId}`)
} else { } else {
// 订单可能未落库或已处理——从 Attach 里拿 uid 直接加 const order = await Order.findOne({ orderNo: outTradeNo })
try { if (order && order.status === 'refunded') {
const attach = typeof coinInfo === 'object' ? coinInfo.Attach || '' : '' console.log(`[wx-msg] 订单已退款,跳过重复入账: ${outTradeNo}`)
const uid = attach ? JSON.parse(attach).uid : '' } else {
if (uid) { // userId 优先取订单,缺失时从 Attach 兜底解析
await User.updateOne({ _id: uid }, { $inc: { vpayCoins: coins } }) let uid = order ? String(order.userId) : ''
if (order) await Order.updateOne({ _id: order._id }, { status: 'paid', paidAt: new Date() }) if (!uid) {
console.log(`[wx-msg] ✓ 星尘充值到账(从Attach): ${outTradeNo} +${coins} → user=${uid}`) try {
} else if (order) { const attach = typeof coinInfo === 'object' ? coinInfo.Attach || '' : ''
await Order.updateOne({ _id: order._id }, { status: 'paid', paidAt: new Date() }) uid = attach ? JSON.parse(attach).uid || '' : ''
await User.updateOne({ _id: order.userId }, { $inc: { vpayCoins: coins } }) } catch (e2) { console.error('[wx-msg] Attach解析失败:', e2.message) }
console.log(`[wx-msg] ✓ 星尘充值到账(订单补偿): ${outTradeNo} +${coins}`) }
if (uid) {
const u = await User.findOneAndUpdate({ _id: uid }, { $inc: { vpayCoins: coins } }, { new: true })
if (u) {
await CoinLedger.create({
openid: u.openid, userId: u._id, type: 'recharge', amount: coins,
balanceAfter: u.vpayCoins || 0, description: '星尘充值', orderNo: outTradeNo,
})
if (order && order.status === 'pending') {
await Order.updateOne({ _id: order._id }, { status: 'paid', paidAt: new Date() })
}
console.log(`[wx-msg] ✓ 星尘充值到账: ${outTradeNo} +${coins} → user=${uid} 余额=${u.vpayCoins}`)
} }
} catch (e2) { console.error('[wx-msg] Attach解析失败:', e2.message) } } else {
console.error(`[wx-msg] 充值到账失败: 找不到订单和Attach uid, order=${outTradeNo}`)
}
}
} }
} }
} catch (e) { console.error('[wx-msg] 充值到账失败:', e.message) } } catch (e) { console.error('[wx-msg] 充值到账失败:', e.message) }
} }
// 退款:扣回星尘 // 退款:扣回星尘 + 写流水(仅处理管理后台之外直接发起的退款;后台退款时订单已标 refunded,此处自动跳过)
if (event === 'xpay_refund_notify') { if (event === 'xpay_refund_notify') {
try { try {
const { User, Order, CoinLedger } = require('../models') const { User, Order, CoinLedger } = require('../models')
const outTradeNo = fields.OutTradeNo || '' const outTradeNo = fields.OutTradeNo || ''
const dup = await CoinLedger.findOne({ orderNo: outTradeNo, type: 'refund' })
const order = await Order.findOne({ orderNo: outTradeNo, status: 'paid' }) const order = await Order.findOne({ orderNo: outTradeNo, status: 'paid' })
if (order) { if (order && !dup) {
await Order.updateOne({ _id: order._id }, { status: 'refunded' }) const coins = (order.metadata || {}).coins || Math.round((order.amount || 0) * 100) || 0
const coins = (order.metadata || {}).coins || 0 if (coins > 0) {
if (coins > 0) await User.updateOne({ _id: order.userId }, { $inc: { vpayCoins: -coins } }) const u = await User.findOneAndUpdate({ _id: order.userId }, { $inc: { vpayCoins: -coins } }, { new: true })
console.log(`[wx-msg] 星尘退款: ${outTradeNo} -${coins}`) await Order.updateOne({ _id: order._id }, { status: 'refunded' })
if (u) {
await CoinLedger.create({
openid: u.openid, userId: u._id, type: 'refund', amount: -coins,
balanceAfter: u.vpayCoins || 0, description: '退款扣回', orderNo: outTradeNo,
})
}
console.log(`[wx-msg] 星尘退款: ${outTradeNo} -${coins}`)
} else {
await Order.updateOne({ _id: order._id }, { status: 'refunded' })
}
} }
} catch (e) { console.error('[wx-msg] 退款处理失败:', e.message) } } catch (e) { console.error('[wx-msg] 退款处理失败:', e.message) }
} }