|
|
@@ -6,6 +6,7 @@ use App\Models\Agent\Agent;
|
|
|
use App\Models\Agent\AgentBalanceLog;
|
|
|
use App\Models\Agent\AgentProfitCommissionProfile;
|
|
|
use App\Models\Agent\AgentProfitEntry;
|
|
|
+use App\Models\Agent\AgentRechargeLock;
|
|
|
use App\Models\Agent\AgentSetting;
|
|
|
use Illuminate\Support\Facades\DB;
|
|
|
|
|
|
@@ -17,6 +18,9 @@ use Illuminate\Support\Facades\DB;
|
|
|
* - 盈亏为负时等于从保证金扣除,之后的盈利先补足保证金;
|
|
|
* - 只有超出保证金本金的部分可以提现。
|
|
|
* realtime_balance = balance + 尚未入账的实时分成,是判断「实时保证金」的内部字段,不对代理展示。
|
|
|
+ *
|
|
|
+ * 入股代理:充值计入余额,可以立即购买股份;但充值后 N 天(入股配置 equity_recharge_lock_days,默认 7)内不能提现,
|
|
|
+ * 记录在 agent_recharge_locks。购买股份优先消耗锁定中的充值。可提现 = 余额 − 锁定中的金额。
|
|
|
*/
|
|
|
class AgentWalletService
|
|
|
{
|
|
|
@@ -85,11 +89,65 @@ class AgentWalletService
|
|
|
'idempotency_key' => $idempotencyKey, 'remark' => $remark,
|
|
|
'reference_type' => $referenceType, 'reference_id' => $referenceId,
|
|
|
]);
|
|
|
+ if ($type === 'recharge' && $agent->isEquity()) {
|
|
|
+ $this->lockRecharge($agent, $log, $amount, $referenceType, $referenceId);
|
|
|
+ } elseif ($type === 'equity_purchase') {
|
|
|
+ $this->consumeLocks((int) $agent->id, $amount);
|
|
|
+ }
|
|
|
$this->evaluateActivation($agent);
|
|
|
return $log;
|
|
|
});
|
|
|
}
|
|
|
|
|
|
+ /** 入股代理的充值锁定 N 天。调用方已在事务内锁住代理。 */
|
|
|
+ private function lockRecharge(Agent $agent, AgentBalanceLog $log, string $amount, string $referenceType, ?int $referenceId): void
|
|
|
+ {
|
|
|
+ $days = (int) AgentSetting::current()->equity_recharge_lock_days;
|
|
|
+ if ($days <= 0) return;
|
|
|
+ AgentRechargeLock::query()->create([
|
|
|
+ 'agent_id' => $agent->id, 'balance_log_id' => $log->id,
|
|
|
+ 'reference_type' => $referenceType, 'reference_id' => $referenceId,
|
|
|
+ 'amount' => $amount, 'remaining' => $amount, 'unlock_at' => now()->addDays($days),
|
|
|
+ ]);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 购买股份先用锁定中的充值(先到期的先用),用完再用可提现余额。 */
|
|
|
+ private function consumeLocks(int $agentId, string $amount): void
|
|
|
+ {
|
|
|
+ $left = bcadd($amount, '0', 4);
|
|
|
+ $locks = AgentRechargeLock::query()->where('agent_id', $agentId)->where('unlock_at', '>', now())
|
|
|
+ ->where('remaining', '>', 0)->orderBy('unlock_at')->orderBy('id')->lockForUpdate()->get();
|
|
|
+ foreach ($locks as $lock) {
|
|
|
+ if (bccomp($left, '0', 4) <= 0) break;
|
|
|
+ $take = bccomp((string) $lock->remaining, $left, 4) < 0 ? bcadd((string) $lock->remaining, '0', 4) : $left;
|
|
|
+ $lock->remaining = bcsub((string) $lock->remaining, $take, 4);
|
|
|
+ $lock->save();
|
|
|
+ $left = bcsub($left, $take, 4);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 入股代理仍在锁定期内的充值金额。 */
|
|
|
+ public function lockedAmount(Agent $agent): string
|
|
|
+ {
|
|
|
+ if (!$agent->isEquity()) return '0.0000';
|
|
|
+ $sum = AgentRechargeLock::query()->where('agent_id', $agent->id)->where('unlock_at', '>', now())->sum('remaining');
|
|
|
+ return bcadd((string) $sum, '0', 4);
|
|
|
+ }
|
|
|
+
|
|
|
+ /** 入股代理锁定中的充值明细(按到期时间),给资金页展示「X 月 X 日后可提现」。 */
|
|
|
+ public function activeLocks(Agent $agent): array
|
|
|
+ {
|
|
|
+ if (!$agent->isEquity()) return [];
|
|
|
+ return AgentRechargeLock::query()->where('agent_id', $agent->id)->where('unlock_at', '>', now())
|
|
|
+ ->where('remaining', '>', 0)->orderBy('unlock_at')->limit(100)
|
|
|
+ ->get(['id', 'amount', 'remaining', 'unlock_at', 'created_at'])
|
|
|
+ ->map(fn (AgentRechargeLock $lock) => [
|
|
|
+ 'amount' => bcadd((string) $lock->amount, '0', 4), 'remaining' => bcadd((string) $lock->remaining, '0', 4),
|
|
|
+ 'recharged_at' => $lock->created_at ? \Carbon\Carbon::parse($lock->created_at)->format('Y-m-d H:i:s') : null,
|
|
|
+ 'unlock_at' => \Carbon\Carbon::parse($lock->unlock_at)->format('Y-m-d H:i:s'),
|
|
|
+ ])->all();
|
|
|
+ }
|
|
|
+
|
|
|
/** 每日入账合营分成:实时余额在注单结算时已计入,这里只改 balance。调用方需已在事务内锁住代理。 */
|
|
|
public function creditProfitShare(Agent $agent, string $signed, string $idempotencyKey, int $recordId, string $remark): ?AgentBalanceLog
|
|
|
{
|
|
|
@@ -180,6 +238,11 @@ class AgentWalletService
|
|
|
];
|
|
|
} else {
|
|
|
$data['equity_percent'] = bcadd((string) $agent->equity_percent, '0', 4);
|
|
|
+ // 锁定金额最多展示到当前余额(人工扣款等可能让余额低于锁定合计)
|
|
|
+ $locked = $this->lockedAmount($agent);
|
|
|
+ $shown = bccomp($locked, $balance, 4) > 0 ? $balance : $locked;
|
|
|
+ $data['locked_amount'] = bccomp($shown, '0', 4) > 0 ? $shown : '0.0000';
|
|
|
+ $data['recharge_lock_days'] = (int) AgentSetting::current()->equity_recharge_lock_days;
|
|
|
}
|
|
|
return $data;
|
|
|
}
|
|
|
@@ -191,7 +254,9 @@ class AgentWalletService
|
|
|
{
|
|
|
$balance = bcadd((string) $agent->balance, '0', 4);
|
|
|
if (!$agent->isJoint()) {
|
|
|
- return bccomp($balance, '0', 4) > 0 ? $balance : '0.0000';
|
|
|
+ // 入股:锁定期内的充值不能提现
|
|
|
+ $free = bcsub($balance, $this->lockedAmount($agent), 4);
|
|
|
+ return bccomp($free, '0', 4) > 0 ? $free : '0.0000';
|
|
|
}
|
|
|
$available = bcsub(bcadd($balance, $this->pendingFloor($agent), 4), (string) $agent->margin_amount, 4);
|
|
|
return bccomp($available, '0', 4) > 0 ? $available : '0.0000';
|