PaymentChannelLinkService.php 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. <?php
  2. namespace App\Services;
  3. use App\Models\RechargeChannel;
  4. use App\Models\RechargeChannelGroup;
  5. use Illuminate\Database\Eloquent\Collection as EloquentCollection;
  6. use Illuminate\Database\Eloquent\Model;
  7. use Illuminate\Support\Collection;
  8. use RuntimeException;
  9. use App\Services\Payment\PaymentProviderCatalog;
  10. use App\Services\Payment\PaymentProviderService;
  11. class PaymentChannelLinkService
  12. {
  13. public function validate(int $channelId, array $groupIds, int $expectedDataType, bool $lockChannel = false): array
  14. {
  15. $query = RechargeChannel::query();
  16. if ($lockChannel) $query->lockForUpdate();
  17. $channel = $query->find($channelId);
  18. if (!$channel) throw new RuntimeException('充值通道不存在');
  19. if ((int)$channel->data_type !== $expectedDataType) {
  20. throw new RuntimeException($expectedDataType === 2 ? '请选择提现通道' : '请选择充值通道');
  21. }
  22. $identity = PaymentProviderCatalog::channelIdentity($channel->toArray());
  23. if ($identity) PaymentProviderService::assertEnabled($identity['provider_code']);
  24. $groupIds = array_values(array_unique(array_filter(array_map('intval', $groupIds))));
  25. if (!$groupIds) throw new RuntimeException('至少选择一个通道组合');
  26. $groupQuery = RechargeChannelGroup::query()->whereIn('id', $groupIds)->orderBy('id');
  27. if ($lockChannel) $groupQuery->lockForUpdate();
  28. $groups = $groupQuery->get();
  29. if ($groups->count() !== count($groupIds)) throw new RuntimeException('部分通道组合不存在');
  30. $field = $this->groupField($expectedDataType);
  31. foreach ($groups as $group) {
  32. if (!in_array((string)$channel->type, (array)$group->{$field}, true)) {
  33. throw new RuntimeException("通道组合“{$group->name}”没有包含通道“{$channel->name}”");
  34. }
  35. }
  36. return [$channel, $groupIds];
  37. }
  38. public function formatMany($models): Collection
  39. {
  40. $models = $models instanceof Collection ? $models : collect($models);
  41. if ($models instanceof EloquentCollection) $models->loadMissing('rechargeChannel');
  42. $groupIds = $models->flatMap(fn ($model) => (array)($model->recharge_channel_group_ids ?? []))
  43. ->map(fn ($id) => (int)$id)->filter()->unique()->values();
  44. $groups = RechargeChannelGroup::query()->whereIn('id', $groupIds)->get()->keyBy('id');
  45. return $models->map(fn (Model $model) => $this->formatOne($model, $groups));
  46. }
  47. public function formatOne(Model $model, ?Collection $groups = null): array
  48. {
  49. $model->loadMissing('rechargeChannel');
  50. $groups ??= RechargeChannelGroup::query()
  51. ->whereIn('id', (array)($model->recharge_channel_group_ids ?? []))->get()->keyBy('id');
  52. $channel = $model->rechargeChannel;
  53. $assignedIds = array_values(array_unique(array_filter(array_map(
  54. 'intval',
  55. (array)($model->recharge_channel_group_ids ?? [])
  56. ))));
  57. $effectiveIds = [];
  58. $unavailableIds = [];
  59. $groupRows = [];
  60. foreach ($assignedIds as $groupId) {
  61. $group = $groups->get($groupId);
  62. $effective = false;
  63. if ($group && $channel) {
  64. $effective = in_array(
  65. (string)$channel->type,
  66. (array)$group->{$this->groupField((int)$channel->data_type)},
  67. true
  68. );
  69. }
  70. if ($effective) {
  71. $effectiveIds[] = $groupId;
  72. } else {
  73. $unavailableIds[] = $groupId;
  74. }
  75. $groupRows[] = [
  76. 'id' => $groupId,
  77. 'name' => (string)($group->name ?? ''),
  78. 'effective' => $effective ? 1 : 0,
  79. ];
  80. }
  81. $data = $model->toArray();
  82. $identity = $channel ? PaymentProviderCatalog::channelIdentity($channel->toArray()) : null;
  83. if (array_key_exists('payment_company', $data)) {
  84. $data['payment_company_label'] = PaymentProviderCatalog::label((string)$data['payment_company']);
  85. $data['payment_method_label'] = $identity && $identity['method_code'] === $data['payment_method']
  86. ? $identity['method_label'] : (string)$data['payment_method'];
  87. }
  88. if (array_key_exists('provider_name', $data)) {
  89. $data['provider_label'] = PaymentProviderCatalog::label((string)$data['provider_name']);
  90. $data['collection_method_label'] = $identity && $identity['method_code'] === $data['collection_method']
  91. ? $identity['method_label'] : (string)$data['collection_method'];
  92. }
  93. if (array_key_exists('payment_company', $data) || array_key_exists('provider_name', $data)) {
  94. $data['credential_source'] = 'environment';
  95. $data['merchant_config_applied'] = false;
  96. }
  97. $data['recharge_channel_group_ids'] = $assignedIds;
  98. $data['effective_group_ids'] = $effectiveIds;
  99. $data['unavailable_group_ids'] = $unavailableIds;
  100. $data['groups'] = $groupRows;
  101. $data['recharge_channel'] = $channel ? [
  102. 'id' => (int)$channel->id,
  103. 'data_type' => (int)$channel->data_type,
  104. 'name' => (string)$channel->name,
  105. 'key' => (string)$channel->key,
  106. 'type' => (string)$channel->type,
  107. 'rate' => number_format((float)$channel->rate, 4, '.', ''),
  108. 'fee_rate' => bcmul((string)$channel->rate, '100', 4),
  109. 'min_amount' => $channel->min === null ? null : number_format((float)$channel->min, 2, '.', ''),
  110. 'max_amount' => $channel->max === null ? null : number_format((float)$channel->max, 2, '.', ''),
  111. 'fixed_amounts' => $channel->fixed ?: [],
  112. 'sort' => (int)$channel->sort,
  113. 'status' => (int)$channel->status,
  114. ] : null;
  115. $data['fee_rate'] = $channel ? bcmul((string)$channel->rate, '100', 4) : null;
  116. $data['min_amount'] = $channel?->min;
  117. $data['max_amount'] = $channel?->max;
  118. $data['sort'] = $channel ? (int)$channel->sort : null;
  119. $data['config_status'] = (int)$model->status;
  120. $data['channel_status'] = $channel ? (int)$channel->status : 0;
  121. $companyStatus = $identity ? (int)(PaymentProviderService::statuses()[$identity['provider_code']] ?? 0) : null;
  122. $data['company_status'] = $companyStatus;
  123. $data['runtime_channel_available'] = $channel
  124. && (int)$channel->status === 1 && $companyStatus !== 0 && count($effectiveIds) > 0 ? 1 : 0;
  125. $data['effective_status'] = (int)$model->status === 1
  126. && $data['runtime_channel_available'] === 1 ? 1 : 0;
  127. if (array_key_exists('amount', $data)) {
  128. $data['actual_amount'] = $channel
  129. ? bcsub((string)$model->amount, bcmul((string)$model->amount, (string)$channel->rate, 6), 2)
  130. : null;
  131. }
  132. $data['channel_fields_readonly'] = [
  133. 'recharge_channel.name', 'recharge_channel.key', 'recharge_channel.type',
  134. 'fee_rate', 'min_amount', 'max_amount', 'recharge_channel.fixed_amounts',
  135. 'sort', 'channel_status',
  136. ];
  137. unset($data['level_scope']);
  138. return $data;
  139. }
  140. private function groupField(int $dataType): string
  141. {
  142. return match ($dataType) {
  143. 2 => 'withdraw_type',
  144. 3 => 'activity_type',
  145. default => 'recharge_type',
  146. };
  147. }
  148. }