106 lines
3.9 KiB
PHP
106 lines
3.9 KiB
PHP
<?php
|
|
// +----------------------------------------------------------------------
|
|
// | 合作商(下单平台)余额管控
|
|
// +----------------------------------------------------------------------
|
|
// | 企油通改造后,下单按金额直传,不再有商品/库存额度概念,
|
|
// | 改由 yoshop_partners.balance 承担下单额度管控:
|
|
// | 下单时原子扣减(余额不足直接拒单),发放失败/撤单时原路回补。
|
|
// | 扣减用 UPDATE ... WHERE balance >= amount,天然防并发超扣。
|
|
// +----------------------------------------------------------------------
|
|
declare (strict_types=1);
|
|
|
|
namespace app\common\service;
|
|
|
|
use app\common\enum\partners\balanceLog\Scene as SceneEnum;
|
|
use think\facade\Db;
|
|
|
|
class PartnersBalance
|
|
{
|
|
/**
|
|
* 原子扣减合作商余额(余额不足返回 false,不做任何变更)
|
|
* @param int $partnersId 合作商ID
|
|
* @param float $amount 扣减金额(元)
|
|
* @param string $orderNo 业务单号(写入流水描述)
|
|
* @param int $orderId 关联订单ID
|
|
* @param int $storeId 商城ID
|
|
* @return int|false 成功返回流水ID(供建单后回填 order_id),余额不足返回 false
|
|
*/
|
|
public static function deduct(int $partnersId, float $amount, string $orderNo, int $orderId = 0, int $storeId = 0)
|
|
{
|
|
if ($amount <= 0) {
|
|
return false;
|
|
}
|
|
$affected = Db::name('partners')
|
|
->where('partners_id', '=', $partnersId)
|
|
->where('balance', '>=', $amount)
|
|
->update(['balance' => Db::raw('`balance` - ' . self::money($amount))]);
|
|
if (!$affected) {
|
|
return false;
|
|
}
|
|
return self::log(SceneEnum::CONSUME, $partnersId, -$amount, $orderNo, $orderId, $storeId);
|
|
}
|
|
|
|
/**
|
|
* 回填流水的关联订单ID
|
|
*/
|
|
public static function bindOrder($logId, int $orderId): void
|
|
{
|
|
if ($logId && $orderId) {
|
|
Db::name('partners_balance_log')->where('log_id', '=', (int)$logId)->update(['order_id' => $orderId]);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 回补合作商余额(发放失败 / 撤单)
|
|
*/
|
|
public static function refund(int $partnersId, float $amount, string $orderNo, int $orderId = 0, int $storeId = 0): void
|
|
{
|
|
if ($amount <= 0) {
|
|
return;
|
|
}
|
|
Db::name('partners')
|
|
->where('partners_id', '=', $partnersId)
|
|
->update(['balance' => Db::raw('`balance` + ' . self::money($amount))]);
|
|
self::log(SceneEnum::REFUND, $partnersId, $amount, $orderNo, $orderId, $storeId);
|
|
}
|
|
|
|
/**
|
|
* 查询当前余额
|
|
*/
|
|
public static function balance(int $partnersId): float
|
|
{
|
|
return (float) Db::name('partners')->where('partners_id', '=', $partnersId)->value('balance');
|
|
}
|
|
|
|
/**
|
|
* 写余额变动流水(失败不影响主流程),返回流水ID
|
|
* @return int
|
|
*/
|
|
protected static function log(int $scene, int $partnersId, float $money, string $orderNo, int $orderId, int $storeId): int
|
|
{
|
|
try {
|
|
return (int) Db::name('partners_balance_log')->insertGetId([
|
|
'partners_id' => $partnersId,
|
|
'scene' => $scene,
|
|
'money' => round($money, 2),
|
|
'describe' => vsprintf(SceneEnum::data()[$scene]['describe'], [$orderNo]),
|
|
'remark' => '',
|
|
'order_id' => $orderId,
|
|
'store_id' => $storeId,
|
|
'create_time' => time(),
|
|
]);
|
|
} catch (\Throwable $e) {
|
|
log_record(['name' => '合作商余额流水写入失败', 'partners_id' => $partnersId, 'msg' => $e->getMessage()]);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 金额安全格式化(仅允许数字与小数点,防拼接注入)
|
|
*/
|
|
protected static function money(float $amount): string
|
|
{
|
|
return number_format(round($amount, 2), 2, '.', '');
|
|
}
|
|
}
|