feat(withdraw): DMEPA提现手续费与最小提现额度配置收敛至商城配置页面并实现内扣闭环
1. 后台商城配置收敛:在【商城配置 -> 充值提现】面板集中增加「DMEPA 数字资产提现配置」(开启开关、手续费率、最小提现额度),并自动双向同步与自愈补齐数据表行 2. 服务层内扣手续费闭环:applyMadow 增加手续费率读取、内扣手续费计算(申请金额 > 手续费校验)与实际到账持久化,驳回/失败全额原路退还 DMEPA 持仓 3. API 与前台联动:rules() 与 madow() 接口返回手续费率,withdraw.vue 实时动态计算扣除 DMEPA 枚数、手续费内扣金额与预计到账金额 4. 后台列表与导出优化:提现管理列表与导出功能支持展示换算市值、手续费与到账金额 5. 新增及更新 3 套自动化规格测试(累计 130 项断言全部通过)
这个提交包含在:
@@ -128,7 +128,7 @@ class Config extends Common
|
||||
$config->save();
|
||||
}
|
||||
} else {
|
||||
// 自动补齐缺失的提现开关配置项,确保热更新无缝落地
|
||||
// 自动补齐缺失的提现配置项,确保热更新无缝落地
|
||||
if ($code === 'shop.recharge_withdraw.withdraw.status') {
|
||||
self::create([
|
||||
'code' => $code,
|
||||
@@ -139,6 +139,45 @@ class Config extends Common
|
||||
'rule' => 'required',
|
||||
'weigh' => 50
|
||||
]);
|
||||
} elseif ($code === 'shop.recharge_withdraw.withdraw.madow_withdraw_enable') {
|
||||
self::create([
|
||||
'code' => $code,
|
||||
'parent_code' => 'shop.recharge_withdraw.withdraw',
|
||||
'name' => '开启DMEPA提现',
|
||||
'type' => 'boolean',
|
||||
'value' => (string)intval($value),
|
||||
'rule' => 'required',
|
||||
'weigh' => 45
|
||||
]);
|
||||
} elseif ($code === 'shop.recharge_withdraw.withdraw.madow_charge_rate') {
|
||||
self::create([
|
||||
'code' => $code,
|
||||
'parent_code' => 'shop.recharge_withdraw.withdraw',
|
||||
'name' => 'DMEPA提现手续费',
|
||||
'type' => 'float',
|
||||
'value' => (string)floatval($value),
|
||||
'rule' => 'required',
|
||||
'weigh' => 44
|
||||
]);
|
||||
} elseif ($code === 'shop.recharge_withdraw.withdraw.madow_min_amount') {
|
||||
self::create([
|
||||
'code' => $code,
|
||||
'parent_code' => 'shop.recharge_withdraw.withdraw',
|
||||
'name' => 'DMEPA单次最小提现金额',
|
||||
'type' => 'float',
|
||||
'value' => (string)floatval($value),
|
||||
'rule' => 'required',
|
||||
'weigh' => 43
|
||||
]);
|
||||
} elseif ($code === 'shop.commission.partner_madow_withdraw_charge_rate') {
|
||||
self::create([
|
||||
'code' => $code,
|
||||
'parent_code' => 'shop.commission',
|
||||
'name' => 'DMEPA提现手续费率',
|
||||
'type' => 'number',
|
||||
'value' => (string)floatval($value),
|
||||
'weigh' => 85
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -173,6 +173,17 @@ class Withdraw extends Common
|
||||
$valClean = trim(str_replace(['元', ' '], '', (string)$marketValue));
|
||||
$display['换算市值'] = number_format((float)$valClean, 2, '.', '') . ' 元';
|
||||
}
|
||||
|
||||
// 手续费与预计到账
|
||||
$chargeFee = $data['charge_fee'] ?? ($this->charge_fee ?? ($info['charge_fee'] ?? 0));
|
||||
$chargeRate = $data['charge_rate'] ?? ($this->charge_rate ?? ($info['charge_rate'] ?? 0));
|
||||
if (floatval($chargeFee) > 0 || floatval($chargeRate) > 0) {
|
||||
$ratePercent = bcmul((string)$chargeRate, '100', 1);
|
||||
$display['提现手续费'] = number_format(floatval($chargeFee), 2, '.', '') . ' 元 (' . $ratePercent . '%)';
|
||||
}
|
||||
$amount = $data['amount'] ?? ($this->amount ?? 0);
|
||||
$real = bcsub((string)$amount, (string)$chargeFee, 2);
|
||||
$display['预计到账'] = number_format(max(0, floatval($real)), 2, '.', '') . ' 元';
|
||||
} elseif ($isMoney) {
|
||||
$display['提现资产'] = '商城余额';
|
||||
$amount = $data['amount'] ?? ($this->amount ?? 0);
|
||||
@@ -182,7 +193,7 @@ class Withdraw extends Common
|
||||
}
|
||||
|
||||
// 填充银行卡或其它常规账户信息,并过滤英文底层键
|
||||
$excludeKeys = ['wallet_type', 'madow_amount', 'rate', '提现资产', '扣除DMEPA', '扣除dmepa', '市值汇率', '折算汇率', '换算市值'];
|
||||
$excludeKeys = ['wallet_type', 'madow_amount', 'rate', 'charge_fee', 'charge_rate', '提现资产', '扣除DMEPA', '扣除dmepa', '市值汇率', '折算汇率', '换算市值', '提现手续费', '预计到账'];
|
||||
foreach ($info as $k => $v) {
|
||||
if (!in_array($k, $excludeKeys)) {
|
||||
$display[$k] = $v;
|
||||
@@ -216,6 +227,6 @@ class Withdraw extends Common
|
||||
|
||||
public function user()
|
||||
{
|
||||
return $this->belongsTo(User::class, 'user_id', 'id')->field('id, nickname, avatar, total_consume');
|
||||
return $this->belongsTo(User::class, 'user_id', 'id')->field('id, nickname, mobile, avatar, total_consume');
|
||||
}
|
||||
}
|
||||
|
||||
在新工单中引用
屏蔽一个用户