feat: 存储空间管理、虚拟支付接入与容量守卫
- 新增存储空间管理:会员套餐配置、用户容量/到期管理、后台支付记录查询 - 接入个人主体小程序虚拟支付:下单/发货推送/兜底查单(query_order) - 修复 adminStorageUsers 等接口鉴权、out_trade_no 长度、前端 requestVirtualPayment 参数 - 前端上传前容量预检,超容引导升级无限空间 - 我的页会员卡片展示「无限空间」与已用容量/到期时间 - 后台「存储空间管理」新增支付记录标签页
This commit is contained in:
@@ -0,0 +1,134 @@
|
||||
const { getLayout } = require('../../utils/layout');
|
||||
const { api } = require('../../utils/request');
|
||||
|
||||
function formatBytes(bytes) {
|
||||
bytes = bytes || 0;
|
||||
if (bytes < 1024 * 1024) return (bytes / 1024).toFixed(1) + ' KB';
|
||||
if (bytes < 1024 * 1024 * 1024) return (bytes / 1024 / 1024).toFixed(1) + ' MB';
|
||||
return (bytes / 1024 / 1024 / 1024).toFixed(2) + ' GB';
|
||||
}
|
||||
|
||||
Page({
|
||||
onResize: function () { this.setData(getLayout()); },
|
||||
|
||||
data: {
|
||||
loading: true,
|
||||
error: false,
|
||||
usedBytes: 0,
|
||||
usedText: '0 MB',
|
||||
freeMb: 200,
|
||||
freeText: '200 MB',
|
||||
percent: 0,
|
||||
overQuota: false,
|
||||
isVip: false,
|
||||
vipExpireText: '',
|
||||
plans: [],
|
||||
paying: false,
|
||||
},
|
||||
|
||||
onLoad: function () { this.setData(getLayout()); },
|
||||
onShow: function () { this.loadInfo(); },
|
||||
onPullDownRefresh: function () { this.loadInfo().then(function () { wx.stopPullDownRefresh(); }); },
|
||||
|
||||
loadInfo: function () {
|
||||
var self = this;
|
||||
this.setData({ loading: true, error: false });
|
||||
return api.storageInfo().then(function (d) {
|
||||
var freeBytes = d.free_bytes || d.freeBytes || 0;
|
||||
var usedBytes = d.used_bytes || d.usedBytes || 0;
|
||||
var percent = freeBytes > 0 ? Math.min(100, Math.round(usedBytes / freeBytes * 100)) : 0;
|
||||
var overQuota = usedBytes > freeBytes;
|
||||
var plans = (d.plans || []).map(function (p) {
|
||||
var cents = p.price_cents || p.priceCents || 0;
|
||||
return {
|
||||
key: p.key,
|
||||
name: p.name,
|
||||
priceText: (cents / 100).toFixed(cents % 100 === 0 ? 0 : 1),
|
||||
unit: p.key === 'year' ? '年' : '月',
|
||||
productId: p.product_id || p.productId,
|
||||
};
|
||||
});
|
||||
var vipExpireText = '';
|
||||
if (d.is_vip || d.isVip) {
|
||||
var expire = d.vip_expire_at || d.vipExpireAt ? String(d.vip_expire_at || d.vipExpireAt).slice(0, 10) : '';
|
||||
vipExpireText = expire ? ('有效期至 ' + expire) : '已开通无限空间';
|
||||
}
|
||||
self.setData({
|
||||
loading: false,
|
||||
usedBytes: usedBytes,
|
||||
usedText: formatBytes(usedBytes),
|
||||
freeMb: d.free_mb || d.freeMb || 200,
|
||||
freeText: formatBytes(freeBytes),
|
||||
percent: percent,
|
||||
overQuota: overQuota,
|
||||
isVip: !!(d.is_vip || d.isVip),
|
||||
vipExpireText: vipExpireText,
|
||||
plans: plans,
|
||||
});
|
||||
}).catch(function (e) {
|
||||
self.setData({ loading: false, error: true });
|
||||
console.error('加载存储信息失败', e);
|
||||
});
|
||||
},
|
||||
|
||||
buyPlan: function (e) {
|
||||
var key = e.currentTarget.dataset.key;
|
||||
var self = this;
|
||||
if (this.data.paying) return;
|
||||
var plan = null;
|
||||
for (var i = 0; i < this.data.plans.length; i++) {
|
||||
if (this.data.plans[i].key === key) { plan = this.data.plans[i]; break; }
|
||||
}
|
||||
if (!plan) return;
|
||||
|
||||
wx.showLoading({ title: '下单中...', mask: true });
|
||||
this.setData({ paying: true });
|
||||
api.storageCreateOrder({ plan_key: key }).then(function (order) {
|
||||
wx.hideLoading();
|
||||
if (!order || !(order.sign_data || order.signData)) {
|
||||
self.setData({ paying: false });
|
||||
wx.showToast({ title: '下单失败', icon: 'none' });
|
||||
return;
|
||||
}
|
||||
self.callVirtualPay(order);
|
||||
}).catch(function (err) {
|
||||
wx.hideLoading();
|
||||
self.setData({ paying: false });
|
||||
wx.showToast({ title: (err && err.error) || '下单失败', icon: 'none' });
|
||||
});
|
||||
},
|
||||
|
||||
callVirtualPay: function (order) {
|
||||
var self = this;
|
||||
if (!wx.requestVirtualPayment) {
|
||||
this.setData({ paying: false });
|
||||
wx.showModal({
|
||||
title: '版本过低',
|
||||
content: '请升级微信至最新版本后再试。',
|
||||
showCancel: false
|
||||
});
|
||||
return;
|
||||
}
|
||||
// wx.requestVirtualPayment 仅接受 signData / paySig / signature / mode 四个参数;
|
||||
// offerId、env、productId、goodsPrice 等均已编码在 signData JSON 字符串内,不可再平铺为顶层参数。
|
||||
wx.requestVirtualPayment({
|
||||
signData: order.sign_data || order.signData,
|
||||
paySig: order.pay_sig || order.paySig,
|
||||
signature: order.signature,
|
||||
mode: order.mode || 'short_series_goods',
|
||||
success: function () {
|
||||
self.setData({ paying: false });
|
||||
wx.showToast({ title: '支付成功', icon: 'success' });
|
||||
setTimeout(function () { self.loadInfo(); }, 1200);
|
||||
},
|
||||
fail: function (err) {
|
||||
self.setData({ paying: false });
|
||||
var msg = '支付未完成';
|
||||
var m = (err && err.errMsg) || '';
|
||||
if (m.indexOf('cancel') >= 0) msg = '已取消支付';
|
||||
else if (m.indexOf('fail') >= 0) msg = '支付失败,请稍后重试';
|
||||
wx.showToast({ title: msg, icon: 'none' });
|
||||
}
|
||||
});
|
||||
},
|
||||
});
|
||||
Reference in New Issue
Block a user