- 商城余额
+ 钱包余额
diff --git a/public/assets/js/backend/shopro/withdraw.js b/public/assets/js/backend/shopro/withdraw.js
index 045591d..7f718b9 100644
--- a/public/assets/js/backend/shopro/withdraw.js
+++ b/public/assets/js/backend/shopro/withdraw.js
@@ -147,8 +147,17 @@ define(['jquery', 'bootstrap', 'backend', 'table', 'form'], function ($, undefin
updatetime: 'range',
});
if (Config.save_type == 'download') {
- window.location.href = `${Config.moduleurl}/shopro/withdraw/${type}?page=${pagination.page}&list_rows=${pagination.list_rows}&order=${state.order}&sort=${state.sort}&search=${search.search}`;
- exportLoading.value = false;
+ const downloadUrl = `${Config.moduleurl}/shopro/withdraw/${type}?page=${pagination.page}&list_rows=${pagination.list_rows}&order=${state.order}&sort=${state.sort}&search=${encodeURIComponent(search.search || '')}`;
+ const iframe = document.createElement('iframe');
+ iframe.style.display = 'none';
+ iframe.src = downloadUrl;
+ document.body.appendChild(iframe);
+ setTimeout(() => {
+ if (iframe && iframe.parentNode) {
+ iframe.parentNode.removeChild(iframe);
+ }
+ exportLoading.value = false;
+ }, 3000);
} else {
Fast.api.ajax({
url: `shopro/withdraw/${type}`,
diff --git a/tests/partner/test_withdraw_export_fix_spec.js b/tests/partner/test_withdraw_export_fix_spec.js
new file mode 100644
index 0000000..6ee6bfb
--- /dev/null
+++ b/tests/partner/test_withdraw_export_fix_spec.js
@@ -0,0 +1,67 @@
+/**
+ * 提现管理导出防崩溃与 PHP 8.0 兼容性回归测试
+ */
+
+const fs = require('fs');
+const path = require('path');
+const assert = require('assert');
+
+console.log('========================================================');
+console.log(' 开始执行提现台账导出防崩溃与全链路加固测试');
+console.log('========================================================\n');
+
+// 1. 测试模型层 PHP 8.0 bcmath 防崩溃模拟
+function simulateChargeRateFormat(value, chargeRate) {
+ const rate = (value !== null && value !== undefined && value !== '') ? value : (chargeRate ?? 0);
+ const safeRate = (!isNaN(rate) && rate !== '' && rate !== null && rate !== undefined) ? String(rate) : '0';
+ return (Number(safeRate) * 100).toFixed(1) + '%';
+}
+
+function simulateRealAmount(amount, chargeFee) {
+ const safeAmount = (!isNaN(amount) && amount !== '' && amount !== null) ? Number(amount) : 0;
+ const safeFee = (!isNaN(chargeFee) && chargeFee !== '' && chargeFee !== null) ? Number(chargeFee) : 0;
+ return Math.max(0, safeAmount - safeFee);
+}
+
+// 模拟各种异常输入,确保不抛出错误
+assert.strictEqual(simulateChargeRateFormat(null, null), '0.0%');
+assert.strictEqual(simulateChargeRateFormat('', ''), '0.0%');
+assert.strictEqual(simulateChargeRateFormat(0, 0), '0.0%');
+assert.strictEqual(simulateChargeRateFormat(undefined, 0.06), '6.0%');
+assert.strictEqual(simulateRealAmount(null, null), 0);
+assert.strictEqual(simulateRealAmount('', ''), 0);
+assert.strictEqual(simulateRealAmount(100, 6), 94);
+assert.strictEqual(simulateRealAmount(10, 0.60), 9.4);
+console.log('[测试点 1] 模型层 PHP 8.0 bcmath 边界参数容错模拟断言通过');
+
+// 2. 检查 application/admin/model/shopro/Withdraw.php 包含加固逻辑
+const modelContent = fs.readFileSync(path.resolve(__dirname, '../../application/admin/model/shopro/Withdraw.php'), 'utf8');
+assert(modelContent.includes('is_numeric($rate)'), 'getChargeRateFormatAttr 包含 is_numeric 校验');
+assert(modelContent.includes('is_numeric($amount)'), 'getRealAmountAttr 包含 is_numeric 校验');
+console.log('[测试点 2] Withdraw 模型源代码加固断言通过');
+
+// 3. 检查 addons/shopro/library/Export.php 包含缓冲检查与下载退出
+const exportContent = fs.readFileSync(path.resolve(__dirname, '../../addons/shopro/library/Export.php'), 'utf8');
+assert(exportContent.includes('ob_get_level() > 0'), 'Export.php 包含 ob_get_level() 检查防 Notice');
+assert(exportContent.includes("if ($this->config['save_type'] != 'save')"), 'Export.php 包含下载模式检查');
+assert(exportContent.includes('exit;'), 'Export.php 下载完成安全退出');
+console.log('[测试点 3] Export.php 缓冲区安全清理与下载模式防污染退出断言通过');
+
+// 4. 检查 application/admin/controller/shopro/Withdraw.php 包含异常捕获、单 key 汇总、时间格式化
+const controllerContent = fs.readFileSync(path.resolve(__dirname, '../../application/admin/controller/shopro/Withdraw.php'), 'utf8');
+assert(controllerContent.includes('catch (\\Throwable $e)'), 'Withdraw.php 包含 \\Throwable 全局异常捕获');
+assert(controllerContent.includes("(new WithdrawModel)->sheepFilter()"), 'Withdraw.php 使用全新实例查询防状态污染');
+assert(controllerContent.includes("date('Y-m-d H:i:s'"), 'Withdraw.php 包含时间戳转格式化日期');
+assert(controllerContent.includes("'id' => '合计: 共 ' . $total"), 'Withdraw.php 汇总行符合 Export.php 单 key 自动合并规范');
+console.log('[测试点 4] Withdraw.php 控制器异常保护与导出规范断言通过');
+
+// 5. 检查 public/assets/js/backend/shopro/withdraw.js 采用隐藏 iframe 下载
+const jsContent = fs.readFileSync(path.resolve(__dirname, '../../public/assets/js/backend/shopro/withdraw.js'), 'utf8');
+assert(jsContent.includes("document.createElement('iframe')"), 'withdraw.js 使用隐藏 iframe 触发下载');
+assert(jsContent.includes('encodeURIComponent(search.search'), 'withdraw.js 对 search 参数进行安全 URI 编码');
+assert(!jsContent.includes('window.location.href = `${Config.moduleurl}/shopro/withdraw/${type}'), '已移除直接 window.location 避免后台页面被替换');
+console.log('[测试点 5] withdraw.js 前端静默下载与主页面安全隔离断言通过');
+
+console.log('\n========================================================');
+console.log(' 所有提现导出防崩溃加固测试全部通过![100% OK]');
+console.log('========================================================');
diff --git a/uniapp/pages/commission/components/account-info.vue b/uniapp/pages/commission/components/account-info.vue
index 21c8f7e..577886f 100644
--- a/uniapp/pages/commission/components/account-info.vue
+++ b/uniapp/pages/commission/components/account-info.vue
@@ -39,12 +39,12 @@
-
+
- 商城余额(元)
+ 钱包余额(元)
{{ state.showMoney ? userInfo?.money || '0.00' : '***' }}
diff --git a/uniapp/pages/pay/withdraw.vue b/uniapp/pages/pay/withdraw.vue
index 223dea3..5133bc3 100644
--- a/uniapp/pages/pay/withdraw.vue
+++ b/uniapp/pages/pay/withdraw.vue
@@ -21,7 +21,7 @@
可提现佣金(元)
{{ userInfo.commission || '0.00' }}
- 商城可用余额:¥{{ userInfo.money || '0.00' }}
+ 钱包可用余额:¥{{ userInfo.money || '0.00' }}
@@ -120,7 +120,7 @@
提现说明
- 💡 提示:提现金额须为100的整数倍(如100、200、1200等)。商城收益分成已全额直接入账商城余额,支持随时申请提现至银行卡/微信/支付宝,也可用于下单直接抵扣。历史存量佣金可转入商城余额消费。
+ 💡 提示:提现金额须为100的整数倍(如100、200、1200等)。商城收益分成已全额直接入账钱包余额,支持随时申请提现至银行卡/微信/支付宝,也可用于下单直接抵扣。历史存量佣金可转入钱包余额消费。
💡 提示:DMEPA 按大盘实时汇率折算为现金并内扣手续费打款,提现渠道限定为银行卡转账。审核驳回后对应 DMEPA 将全额原路退回。
@@ -402,7 +402,7 @@
return;
}
if (Number(payload.money) > Number(userInfo.value?.money || 0)) {
- sheep.$helper.toast('商城可用余额不足');
+ sheep.$helper.toast('钱包可用余额不足');
return;
}
@@ -427,7 +427,7 @@
sheep.$platform.useProvider('wechat').bind();
}
if (code === 1) {
- // 刷新用户佣金与商城余额
+ // 刷新用户佣金与钱包余额
userStore.getInfo();
if (state.walletType === 'madow') {
getMadowInfo();
diff --git a/uniapp/pages/user/wallet/commission.vue b/uniapp/pages/user/wallet/commission.vue
index 860c12d..87a6661 100644
--- a/uniapp/pages/user/wallet/commission.vue
+++ b/uniapp/pages/user/wallet/commission.vue
@@ -4,7 +4,7 @@