mirror of
https://gitee.com/technical-laohu/mpay_v2_webman.git
synced 2026-04-27 20:44:30 +08:00
更新统一使用 PHPDoc + PSR-19 标准注释
This commit is contained in:
@@ -17,11 +17,22 @@ use app\service\account\funds\MerchantAccountService;
|
||||
* 清算生命周期服务。
|
||||
*
|
||||
* 负责清算单创建、明细写入、入账完成和失败终态处理。
|
||||
*
|
||||
* @property SettlementOrderRepository $settlementOrderRepository 结算订单仓库
|
||||
* @property SettlementItemRepository $settlementItemRepository 结算明细仓库
|
||||
* @property PayOrderRepository $payOrderRepository 支付单仓库
|
||||
* @property MerchantAccountService $merchantAccountService 商户账户服务
|
||||
*/
|
||||
class SettlementLifecycleService extends BaseService
|
||||
{
|
||||
/**
|
||||
* 构造函数,注入对应依赖。
|
||||
* 构造方法。
|
||||
*
|
||||
* @param SettlementOrderRepository $settlementOrderRepository 结算订单仓库
|
||||
* @param SettlementItemRepository $settlementItemRepository 结算明细仓库
|
||||
* @param PayOrderRepository $payOrderRepository 支付订单仓库
|
||||
* @param MerchantAccountService $merchantAccountService 商户账户服务
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(
|
||||
protected SettlementOrderRepository $settlementOrderRepository,
|
||||
@@ -36,9 +47,10 @@ class SettlementLifecycleService extends BaseService
|
||||
*
|
||||
* 适用于平台代收链路的清算批次生成,会同时写入汇总与明细。
|
||||
*
|
||||
* @param array $input 清算单参数
|
||||
* @param array $items 清算明细列表
|
||||
* @return SettlementOrder
|
||||
* @param array $input 清算参数
|
||||
* @param array $items 清算明细
|
||||
* @return SettlementOrder 清算单记录
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function createSettlementOrder(array $input, array $items = []): SettlementOrder
|
||||
{
|
||||
@@ -47,6 +59,7 @@ class SettlementLifecycleService extends BaseService
|
||||
$settleNo = $this->generateNo('STL');
|
||||
}
|
||||
|
||||
// 清算单号天然幂等,同一批次重复触发时直接复用已有记录。
|
||||
if ($existing = $this->settlementOrderRepository->findBySettleNo($settleNo)) {
|
||||
return $existing;
|
||||
}
|
||||
@@ -62,6 +75,7 @@ class SettlementLifecycleService extends BaseService
|
||||
}
|
||||
|
||||
return $this->transactionRetry(function () use ($settleNo, $input, $items, $merchantId, $merchantGroupId, $channelId, $cycleType, $cycleKey) {
|
||||
// 先汇总主表金额,再写入主表和明细,保证批次头尾一致。
|
||||
$summary = $this->buildSummary($items, $input);
|
||||
$traceNo = trim((string) ($input['trace_no'] ?? $settleNo));
|
||||
|
||||
@@ -86,6 +100,7 @@ class SettlementLifecycleService extends BaseService
|
||||
]);
|
||||
|
||||
foreach ($items as $item) {
|
||||
// 每一笔清算明细都单独落库,方便后续对账和问题定位。
|
||||
$this->settlementItemRepository->create([
|
||||
'settle_no' => $settleNo,
|
||||
'merchant_id' => $merchantId,
|
||||
@@ -111,8 +126,10 @@ class SettlementLifecycleService extends BaseService
|
||||
*
|
||||
* 会把清算净额计入商户可提现余额,并同步标记清算单与清算明细为已完成。
|
||||
*
|
||||
* @param string $settleNo 清算单号
|
||||
* @return SettlementOrder
|
||||
* @param string $settleNo 结算单号
|
||||
* @return SettlementOrder 清算单记录
|
||||
* @throws ResourceNotFoundException
|
||||
* @throws BusinessStateException
|
||||
*/
|
||||
public function completeSettlement(string $settleNo): SettlementOrder
|
||||
{
|
||||
@@ -123,6 +140,7 @@ class SettlementLifecycleService extends BaseService
|
||||
}
|
||||
|
||||
$currentStatus = (int) $settlementOrder->status;
|
||||
// 已结算或已终态的单子直接返回,避免重复入账。
|
||||
if ($currentStatus === TradeConstant::SETTLEMENT_STATUS_SETTLED) {
|
||||
return $settlementOrder;
|
||||
}
|
||||
@@ -139,6 +157,7 @@ class SettlementLifecycleService extends BaseService
|
||||
}
|
||||
|
||||
if ((int) $settlementOrder->accounted_amount > 0) {
|
||||
// 只有净额大于 0 时才入账到商户可提现余额。
|
||||
$this->merchantAccountService->creditAvailableAmountInCurrentTransaction(
|
||||
(int) $settlementOrder->merchant_id,
|
||||
(int) $settlementOrder->accounted_amount,
|
||||
@@ -159,6 +178,7 @@ class SettlementLifecycleService extends BaseService
|
||||
|
||||
$items = $this->settlementItemRepository->listBySettleNo($settleNo);
|
||||
foreach ($items as $item) {
|
||||
// 清算明细和关联支付单状态一起同步,避免批次与订单状态不一致。
|
||||
$item->item_status = TradeConstant::SETTLEMENT_STATUS_SETTLED;
|
||||
$item->save();
|
||||
|
||||
@@ -180,9 +200,11 @@ class SettlementLifecycleService extends BaseService
|
||||
*
|
||||
* 仅用于清算批次未成功入账时的终态标记。
|
||||
*
|
||||
* @param string $settleNo 清算单号
|
||||
* @param string $settleNo 结算单号
|
||||
* @param string $reason 失败原因
|
||||
* @return SettlementOrder
|
||||
* @return SettlementOrder 清算单记录
|
||||
* @throws ResourceNotFoundException
|
||||
* @throws BusinessStateException
|
||||
*/
|
||||
public function failSettlement(string $settleNo, string $reason = ''): SettlementOrder
|
||||
{
|
||||
@@ -193,6 +215,7 @@ class SettlementLifecycleService extends BaseService
|
||||
}
|
||||
|
||||
$currentStatus = (int) $settlementOrder->status;
|
||||
// 失败态也只处理可变状态,终态直接返回。
|
||||
if ($currentStatus === TradeConstant::SETTLEMENT_STATUS_REVERSED) {
|
||||
return $settlementOrder;
|
||||
}
|
||||
@@ -213,6 +236,7 @@ class SettlementLifecycleService extends BaseService
|
||||
$settlementOrder->failed_at = $this->now();
|
||||
$extJson = (array) $settlementOrder->ext_json;
|
||||
if (trim($reason) !== '') {
|
||||
// 把失败原因同步到扩展字段,便于后台排查。
|
||||
$extJson['fail_reason'] = $reason;
|
||||
}
|
||||
$settlementOrder->ext_json = $extJson;
|
||||
@@ -230,6 +254,10 @@ class SettlementLifecycleService extends BaseService
|
||||
|
||||
/**
|
||||
* 根据清算明细构造汇总数据。
|
||||
*
|
||||
* @param array $items 清算明细
|
||||
* @param array $input 清算参数
|
||||
* @return array 汇总数据
|
||||
*/
|
||||
private function buildSummary(array $items, array $input): array
|
||||
{
|
||||
@@ -241,6 +269,7 @@ class SettlementLifecycleService extends BaseService
|
||||
$netAmount = 0;
|
||||
|
||||
foreach ($items as $item) {
|
||||
// 汇总字段都从明细逐项累加,避免依赖上游传入的批次统计值。
|
||||
$grossAmount += (int) ($item['pay_amount'] ?? 0);
|
||||
$feeAmount += (int) ($item['fee_amount'] ?? 0);
|
||||
$refundAmount += (int) ($item['refund_amount'] ?? 0);
|
||||
@@ -258,6 +287,7 @@ class SettlementLifecycleService extends BaseService
|
||||
];
|
||||
}
|
||||
|
||||
// 明细为空时,直接使用外部传入的汇总字段,兼容上游已经算好的批次数据。
|
||||
return [
|
||||
'gross_amount' => (int) ($input['gross_amount'] ?? 0),
|
||||
'fee_amount' => (int) ($input['fee_amount'] ?? 0),
|
||||
|
||||
@@ -13,11 +13,22 @@ use app\repository\payment\settlement\SettlementOrderRepository;
|
||||
|
||||
/**
|
||||
* 清算订单查询服务。
|
||||
*
|
||||
* 负责清算订单的列表、详情、时间线和关联流水装配。
|
||||
*
|
||||
* @property SettlementOrderRepository $settlementOrderRepository 结算订单仓库
|
||||
* @property SettlementItemRepository $settlementItemRepository 结算明细仓库
|
||||
* @property MerchantAccountLedgerRepository $merchantAccountLedgerRepository 商户账户流水仓库
|
||||
*/
|
||||
class SettlementOrderQueryService extends BaseService
|
||||
{
|
||||
/**
|
||||
* 构造函数,注入清算订单仓库。
|
||||
* 构造方法。
|
||||
*
|
||||
* @param SettlementOrderRepository $settlementOrderRepository 结算订单仓库
|
||||
* @param SettlementItemRepository $settlementItemRepository 结算明细仓库
|
||||
* @param MerchantAccountLedgerRepository $merchantAccountLedgerRepository 商户账户流水仓库
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(
|
||||
protected SettlementOrderRepository $settlementOrderRepository,
|
||||
@@ -28,6 +39,12 @@ class SettlementOrderQueryService extends BaseService
|
||||
|
||||
/**
|
||||
* 分页查询清算订单。
|
||||
*
|
||||
* @param array $filters 筛选条件
|
||||
* @param int $page 页码
|
||||
* @param int $pageSize 每页条数
|
||||
* @param int|null $merchantId 商户ID
|
||||
* @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果
|
||||
*/
|
||||
public function paginate(array $filters = [], int $page = 1, int $pageSize = 10, ?int $merchantId = null)
|
||||
{
|
||||
@@ -35,6 +52,7 @@ class SettlementOrderQueryService extends BaseService
|
||||
|
||||
$keyword = trim((string) ($filters['keyword'] ?? ''));
|
||||
if ($keyword !== '') {
|
||||
// 关键词同时命中清算单、追踪号、商户和通道,方便按任一线索回查批次。
|
||||
$query->where(function ($builder) use ($keyword) {
|
||||
$builder->where('s.settle_no', 'like', '%' . $keyword . '%')
|
||||
->orWhere('s.trace_no', 'like', '%' . $keyword . '%')
|
||||
@@ -69,6 +87,7 @@ class SettlementOrderQueryService extends BaseService
|
||||
->orderByDesc('s.id')
|
||||
->paginate(max(1, $pageSize), ['*'], 'page', max(1, $page));
|
||||
|
||||
// 列表页需要直接展示文本字段,所以这里统一把每一行补成可渲染结构。
|
||||
$paginator->getCollection()->transform(function ($row) {
|
||||
return $this->decorateRow($row);
|
||||
});
|
||||
@@ -78,6 +97,10 @@ class SettlementOrderQueryService extends BaseService
|
||||
|
||||
/**
|
||||
* 按清算单号查询详情。
|
||||
*
|
||||
* @param string $settleNo 清算单号
|
||||
* @param int|null $merchantId 商户ID
|
||||
* @return SettlementOrder|null 清算订单模型
|
||||
*/
|
||||
public function findBySettleNo(string $settleNo, ?int $merchantId = null): ?SettlementOrder
|
||||
{
|
||||
@@ -90,6 +113,12 @@ class SettlementOrderQueryService extends BaseService
|
||||
|
||||
/**
|
||||
* 查询清算订单详情。
|
||||
*
|
||||
* @param string $settleNo 清算单号
|
||||
* @param int|null $merchantId 商户ID
|
||||
* @return array{settlement_order: SettlementOrder, items: array, account_ledgers: \Illuminate\Support\Collection, timeline: array<int, array<string, mixed>>} 详情结构
|
||||
* @throws ValidationException
|
||||
* @throws ResourceNotFoundException
|
||||
*/
|
||||
public function detail(string $settleNo, ?int $merchantId = null): array
|
||||
{
|
||||
@@ -109,6 +138,7 @@ class SettlementOrderQueryService extends BaseService
|
||||
: collect();
|
||||
|
||||
if ($accountLedgers->isEmpty()) {
|
||||
// 清算流水优先按追踪号查,缺失时回退到清算单号兜底。
|
||||
$accountLedgers = $this->merchantAccountLedgerRepository->listByBizNo((string) $settlementOrder->settle_no);
|
||||
}
|
||||
|
||||
@@ -122,6 +152,9 @@ class SettlementOrderQueryService extends BaseService
|
||||
|
||||
/**
|
||||
* 构建时间线。
|
||||
*
|
||||
* @param SettlementOrder|null $settlementOrder 结算订单
|
||||
* @return array<int, array<string, mixed>> 清算时间线
|
||||
*/
|
||||
public function buildTimeline(?SettlementOrder $settlementOrder): array
|
||||
{
|
||||
@@ -129,6 +162,7 @@ class SettlementOrderQueryService extends BaseService
|
||||
return [];
|
||||
}
|
||||
|
||||
// 清算时间线只展示真正走到过的节点,未发生的步骤不占位。
|
||||
return array_values(array_filter([
|
||||
[
|
||||
'title' => '生成清算单',
|
||||
@@ -156,9 +190,13 @@ class SettlementOrderQueryService extends BaseService
|
||||
|
||||
/**
|
||||
* 格式化单条记录。
|
||||
*
|
||||
* @param object $row 原始查询行
|
||||
* @return object 格式化后的记录
|
||||
*/
|
||||
private function decorateRow(object $row): object
|
||||
{
|
||||
// 列表页直接要展示状态文案和金额文案,所以在查询层就把格式化字段补齐。
|
||||
$row->cycle_type_text = (string) (TradeConstant::settlementCycleMap()[(int) $row->cycle_type] ?? '未知');
|
||||
$row->status_text = (string) (TradeConstant::settlementStatusMap()[(int) $row->status] ?? '未知');
|
||||
$row->gross_amount_text = $this->formatAmount((int) $row->gross_amount);
|
||||
@@ -178,6 +216,9 @@ class SettlementOrderQueryService extends BaseService
|
||||
|
||||
/**
|
||||
* 统一构建查询。
|
||||
*
|
||||
* @param int|null $merchantId 商户ID
|
||||
* @return \Illuminate\Database\Eloquent\Builder 查询构造器
|
||||
*/
|
||||
private function baseQuery(?int $merchantId = null)
|
||||
{
|
||||
|
||||
@@ -6,14 +6,19 @@ use app\common\base\BaseService;
|
||||
use app\model\payment\SettlementOrder;
|
||||
|
||||
/**
|
||||
* 清算门面服务。
|
||||
* 清算服务。
|
||||
*
|
||||
* 对外保留原有调用契约,内部委托给清算生命周期服务。
|
||||
* @property SettlementOrderQueryService $queryService 查询服务
|
||||
* @property SettlementLifecycleService $lifecycleService 生命周期服务
|
||||
*/
|
||||
class SettlementService extends BaseService
|
||||
{
|
||||
/**
|
||||
* 构造函数,注入对应依赖。
|
||||
* 构造方法。
|
||||
*
|
||||
* @param SettlementOrderQueryService $queryService 查询服务
|
||||
* @param SettlementLifecycleService $lifecycleService 生命周期服务
|
||||
* @return void
|
||||
*/
|
||||
public function __construct(
|
||||
protected SettlementOrderQueryService $queryService,
|
||||
@@ -23,6 +28,10 @@ class SettlementService extends BaseService
|
||||
|
||||
/**
|
||||
* 创建清算单和明细。
|
||||
*
|
||||
* @param array $input 输入参数
|
||||
* @param array $items 清算明细
|
||||
* @return SettlementOrder 清算订单模型
|
||||
*/
|
||||
public function createSettlementOrder(array $input, array $items = []): SettlementOrder
|
||||
{
|
||||
@@ -31,6 +40,10 @@ class SettlementService extends BaseService
|
||||
|
||||
/**
|
||||
* 查询清算订单详情。
|
||||
*
|
||||
* @param string $settleNo 清算单号
|
||||
* @param int|null $merchantId 商户ID
|
||||
* @return array 详情结构
|
||||
*/
|
||||
public function detail(string $settleNo, ?int $merchantId = null): array
|
||||
{
|
||||
@@ -38,7 +51,10 @@ class SettlementService extends BaseService
|
||||
}
|
||||
|
||||
/**
|
||||
* 清算入账成功。
|
||||
* 标记清算入账成功。
|
||||
*
|
||||
* @param string $settleNo 清算单号
|
||||
* @return SettlementOrder 清算订单模型
|
||||
*/
|
||||
public function completeSettlement(string $settleNo): SettlementOrder
|
||||
{
|
||||
@@ -46,10 +62,16 @@ class SettlementService extends BaseService
|
||||
}
|
||||
|
||||
/**
|
||||
* 清算失败。
|
||||
* 标记清算失败。
|
||||
*
|
||||
* @param string $settleNo 清算单号
|
||||
* @param string $reason 失败原因
|
||||
* @return SettlementOrder 清算订单模型
|
||||
*/
|
||||
public function failSettlement(string $settleNo, string $reason = ''): SettlementOrder
|
||||
{
|
||||
return $this->lifecycleService->failSettlement($settleNo, $reason);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user