From 9a16a886404ff7dcc27fc195567b825ba7d95918 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=8A=80=E6=9C=AF=E8=80=81=E8=83=A1?= <1094551889@qq.com> Date: Tue, 21 Apr 2026 08:38:59 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9B=B4=E6=96=B0=E7=BB=9F=E4=B8=80=E4=BD=BF?= =?UTF-8?q?=E7=94=A8=20PHPDoc=20+=20PSR-19=20=E6=A0=87=E5=87=86=E6=B3=A8?= =?UTF-8?q?=E9=87=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/command/EpayMapiTest.php | 240 +++++++++++++++++- app/command/MpayTest.php | 92 ++++++- app/command/SystemConfigSync.php | 20 ++ app/command/Test.php | 19 ++ app/common/base/BaseController.php | 40 ++- app/common/base/BaseModel.php | 4 + app/common/base/BasePayment.php | 79 ++++-- app/common/base/BaseRepository.php | 61 ++++- app/common/base/BaseService.php | 53 +++- app/common/constant/AuthConstant.php | 41 +++ app/common/constant/CommonConstant.php | 30 ++- app/common/constant/FileConstant.php | 95 +++++++ app/common/constant/LedgerConstant.php | 21 +- app/common/constant/MerchantConstant.php | 36 +++ app/common/constant/NotifyConstant.php | 31 ++- app/common/constant/RouteConstant.php | 37 ++- app/common/constant/TradeConstant.php | 79 +++++- app/common/interface/PayPluginInterface.php | 64 +++-- app/common/interface/PaymentInterface.php | 71 ++---- app/common/middleware/Cors.php | 6 +- app/common/payment/AlipayPayment.php | 236 ++++++++++++++++- app/common/util/FormatHelper.php | 50 ++++ app/common/util/JwtTokenManager.php | 53 +++- .../BalanceInsufficientException.php | 13 +- app/exception/BusinessStateException.php | 12 +- app/exception/ConflictException.php | 12 +- .../NotifyRetryExceededException.php | 11 +- app/exception/PaymentException.php | 12 +- app/exception/ResourceNotFoundException.php | 12 +- app/exception/ValidationException.php | 12 +- .../account/MerchantAccountController.php | 22 +- .../MerchantAccountLedgerController.php | 19 +- .../controller/file/FileRecordController.php | 79 +++++- .../MerchantApiCredentialController.php | 40 ++- .../merchant/MerchantController.php | 51 +++- .../merchant/MerchantGroupController.php | 43 +++- .../merchant/MerchantPolicyController.php | 46 ++++ .../ops/ChannelDailyStatController.php | 19 +- .../ops/ChannelNotifyLogController.php | 19 +- .../ops/PayCallbackLogController.php | 19 +- .../payment/PaymentChannelController.php | 49 +++- .../payment/PaymentPluginConfController.php | 55 ++++ .../payment/PaymentPluginController.php | 39 ++- .../PaymentPollGroupBindController.php | 46 ++++ .../PaymentPollGroupChannelController.php | 46 ++++ .../payment/PaymentPollGroupController.php | 43 +++- .../payment/PaymentTypeController.php | 33 ++- .../controller/payment/RouteController.php | 19 +- .../controller/system/AdminUserController.php | 40 ++- .../controller/system/AuthController.php | 32 ++- .../system/SystemConfigPageController.php | 36 +++ .../controller/system/SystemController.php | 25 ++ .../controller/trade/PayOrderController.php | 15 +- .../trade/RefundOrderController.php | 24 ++ .../trade/SettlementOrderController.php | 19 +- .../admin/middleware/AdminAuthMiddleware.php | 16 +- .../admin/validation/AdminUserValidator.php | 38 ++- app/http/admin/validation/AuthValidator.php | 17 ++ .../validation/ChannelDailyStatValidator.php | 17 ++ .../validation/ChannelNotifyLogValidator.php | 17 ++ .../admin/validation/FileRecordValidator.php | 21 +- .../MerchantAccountLedgerValidator.php | 17 ++ .../validation/MerchantAccountValidator.php | 17 ++ .../MerchantApiCredentialValidator.php | 39 ++- .../validation/MerchantGroupValidator.php | 40 ++- .../validation/MerchantPolicyValidator.php | 27 ++ .../admin/validation/MerchantValidator.php | 43 +++- .../validation/PayCallbackLogValidator.php | 17 ++ .../admin/validation/PayOrderValidator.php | 20 +- .../validation/PaymentChannelValidator.php | 25 +- .../validation/PaymentPluginConfValidator.php | 22 ++ .../validation/PaymentPluginValidator.php | 18 +- .../PaymentPollGroupBindValidator.php | 22 +- .../PaymentPollGroupChannelValidator.php | 22 +- .../validation/PaymentPollGroupValidator.php | 25 +- .../admin/validation/PaymentTypeValidator.php | 23 +- .../validation/RefundActionValidator.php | 17 ++ .../admin/validation/RefundOrderValidator.php | 17 ++ .../validation/RouteResolveValidator.php | 18 +- .../validation/SettlementOrderValidator.php | 18 +- .../validation/SystemConfigPageValidator.php | 20 ++ .../api/controller/adapter/EpayController.php | 23 +- .../controller/notify/NotifyController.php | 30 ++- .../api/controller/route/RouteController.php | 23 +- .../settlement/SettlementController.php | 44 +++- .../api/controller/trace/TraceController.php | 17 ++ .../api/controller/trade/PayController.php | 64 ++++- .../api/controller/trade/RefundController.php | 43 +++- app/http/api/validation/EpayValidator.php | 23 +- .../api/validation/NotifyChannelValidator.php | 17 ++ .../validation/NotifyMerchantValidator.php | 19 +- .../api/validation/PayCallbackValidator.php | 23 +- app/http/api/validation/PayCloseValidator.php | 17 ++ .../api/validation/PayPrepareValidator.php | 17 ++ .../api/validation/PayTimeoutValidator.php | 17 ++ .../api/validation/RefundActionValidator.php | 17 ++ .../api/validation/RefundCreateValidator.php | 17 ++ .../api/validation/RouteResolveValidator.php | 19 +- .../validation/SettlementActionValidator.php | 17 ++ .../validation/SettlementCreateValidator.php | 19 +- .../api/validation/TraceQueryValidator.php | 17 ++ .../controller/account/AccountController.php | 20 +- .../merchant/MerchantPortalController.php | 51 +++- .../mer/controller/system/AuthController.php | 30 ++- .../controller/system/SystemController.php | 25 ++ .../controller/trade/PayOrderController.php | 15 +- .../trade/RefundOrderController.php | 24 ++ .../mer/middleware/MerchantAuthMiddleware.php | 16 +- app/http/mer/validation/AuthValidator.php | 17 ++ app/http/mer/validation/BalanceValidator.php | 17 ++ .../validation/MerchantPortalValidator.php | 17 ++ app/http/mer/validation/PayOrderValidator.php | 20 +- .../mer/validation/RefundActionValidator.php | 17 ++ .../mer/validation/RefundOrderValidator.php | 17 ++ app/listener/SystemConfigChangedListener.php | 22 ++ app/model/admin/AdminUser.php | 22 ++ app/model/admin/ChannelDailyStat.php | 17 ++ app/model/admin/ChannelNotifyLog.php | 23 +- app/model/admin/PayCallbackLog.php | 28 +- app/model/file/FileRecord.php | 17 ++ app/model/merchant/Merchant.php | 22 ++ app/model/merchant/MerchantAccount.php | 17 ++ app/model/merchant/MerchantAccountLedger.php | 22 ++ app/model/merchant/MerchantApiCredential.php | 23 +- app/model/merchant/MerchantGroup.php | 17 ++ app/model/merchant/MerchantPolicy.php | 17 ++ app/model/payment/BizOrder.php | 17 ++ app/model/payment/NotifyTask.php | 22 ++ app/model/payment/PayOrder.php | 17 ++ app/model/payment/PaymentChannel.php | 17 ++ app/model/payment/PaymentPlugin.php | 32 +++ app/model/payment/PaymentPluginConf.php | 17 ++ app/model/payment/PaymentPollGroup.php | 17 ++ app/model/payment/PaymentPollGroupBind.php | 17 ++ app/model/payment/PaymentPollGroupChannel.php | 17 ++ app/model/payment/PaymentType.php | 17 ++ app/model/payment/RefundOrder.php | 17 ++ app/model/payment/SettlementItem.php | 17 ++ app/model/payment/SettlementOrder.php | 17 ++ app/model/system/SystemConfig.php | 32 +++ app/process/Http.php | 5 +- app/process/Monitor.php | 46 +++- .../balance/MerchantAccountRepository.php | 20 +- .../MerchantAccountLedgerRepository.php | 37 ++- app/repository/file/FileRecordRepository.php | 19 +- .../merchant/base/MerchantGroupRepository.php | 17 +- .../base/MerchantPolicyRepository.php | 14 +- .../merchant/base/MerchantRepository.php | 21 +- .../MerchantApiCredentialRepository.php | 16 +- .../ops/log/ChannelNotifyLogRepository.php | 20 +- .../ops/log/PayCallbackLogRepository.php | 14 +- .../ops/stat/ChannelDailyStatRepository.php | 15 +- .../config/PaymentChannelRepository.php | 30 ++- .../config/PaymentPluginConfRepository.php | 14 +- .../config/PaymentPluginRepository.php | 17 +- .../config/PaymentPollGroupBindRepository.php | 18 +- .../PaymentPollGroupChannelRepository.php | 18 +- .../config/PaymentPollGroupRepository.php | 12 +- .../payment/config/PaymentTypeRepository.php | 17 +- .../payment/notify/NotifyTaskRepository.php | 18 +- .../settlement/SettlementItemRepository.php | 14 +- .../settlement/SettlementOrderRepository.php | 44 +++- .../payment/trade/BizOrderRepository.php | 43 +++- .../payment/trade/PayOrderRepository.php | 53 +++- .../payment/trade/RefundOrderRepository.php | 52 +++- .../system/config/SystemConfigRepository.php | 9 +- .../system/user/AdminUserRepository.php | 14 +- app/route/admin.php | 12 +- app/route/mer.php | 6 +- .../funds/MerchantAccountCommandService.php | 176 +++++++++++++ .../funds/MerchantAccountQueryService.php | 26 ++ .../account/funds/MerchantAccountService.php | 161 +++++++++++- .../ledger/MerchantAccountLedgerService.php | 25 +- .../bootstrap/SystemBootstrapService.php | 66 +++++ app/service/file/FileRecordCommandService.php | 107 +++++++- app/service/file/FileRecordQueryService.php | 111 +++++++- app/service/file/FileRecordService.php | 69 ++++- app/service/file/StorageConfigService.php | 119 ++++++++- .../file/storage/AbstractStorageDriver.php | 104 ++++++-- app/service/file/storage/CosStorageDriver.php | 88 ++++++- .../file/storage/LocalStorageDriver.php | 68 ++++- app/service/file/storage/OssStorageDriver.php | 93 ++++++- .../file/storage/RemoteUrlStorageDriver.php | 51 +++- .../file/storage/StorageDriverInterface.php | 44 ++++ app/service/file/storage/StorageManager.php | 95 +++++++ .../merchant/MerchantCommandService.php | 110 ++++++++ .../merchant/MerchantOverviewQueryService.php | 28 +- app/service/merchant/MerchantQueryService.php | 65 +++++ app/service/merchant/MerchantService.php | 140 +++++++++- .../merchant/auth/MerchantAuthService.php | 45 ++++ .../merchant/group/MerchantGroupService.php | 36 ++- .../merchant/policy/MerchantPolicyService.php | 36 +++ .../portal/MerchantPortalBalanceService.php | 34 ++- .../MerchantPortalChannelQueryService.php | 39 ++- .../portal/MerchantPortalChannelService.php | 30 ++- ...MerchantPortalCredentialCommandService.php | 36 ++- .../MerchantPortalCredentialQueryService.php | 34 ++- .../MerchantPortalCredentialService.php | 24 +- .../portal/MerchantPortalFinanceService.php | 45 +++- .../MerchantPortalProfileCommandService.php | 29 ++- .../MerchantPortalProfileQueryService.php | 16 ++ .../portal/MerchantPortalProfileService.php | 33 ++- .../MerchantPortalRoutePreviewService.php | 102 +++++--- .../merchant/portal/MerchantPortalService.php | 96 ++++++- .../MerchantPortalSettlementService.php | 28 ++ .../portal/MerchantPortalSupportService.php | 93 ++----- .../MerchantApiCredentialQueryService.php | 29 ++- .../security/MerchantApiCredentialService.php | 93 ++++++- .../ops/log/ChannelNotifyLogService.php | 27 +- app/service/ops/log/PayCallbackLogService.php | 27 +- .../ops/stat/ChannelDailyStatService.php | 28 +- .../payment/compat/EpayCompatService.php | 238 +++++++++++++++++ .../config/PaymentChannelCommandService.php | 72 ++++++ .../config/PaymentChannelQueryService.php | 60 ++++- .../payment/config/PaymentChannelService.php | 65 ++++- .../config/PaymentPluginConfService.php | 59 ++++- .../payment/config/PaymentPluginService.php | 42 ++- .../config/PaymentPluginSyncService.php | 24 +- .../config/PaymentPollGroupBindService.php | 72 ++++++ .../config/PaymentPollGroupChannelService.php | 75 ++++++ .../config/PaymentPollGroupCommandService.php | 42 +++ .../config/PaymentPollGroupQueryService.php | 37 ++- .../config/PaymentPollGroupService.php | 53 +++- .../payment/config/PaymentTypeService.php | 40 ++- .../payment/order/PayOrderAttemptService.php | 39 ++- .../payment/order/PayOrderCallbackService.php | 36 ++- .../order/PayOrderChannelDispatchService.php | 34 ++- .../payment/order/PayOrderFeeService.php | 27 +- .../order/PayOrderLifecycleService.php | 71 +++++- .../payment/order/PayOrderQueryService.php | 46 +++- .../payment/order/PayOrderReportService.php | 17 +- app/service/payment/order/PayOrderService.php | 69 ++++- .../payment/order/RefundCreationService.php | 36 ++- .../payment/order/RefundLifecycleService.php | 62 ++++- .../payment/order/RefundQueryService.php | 59 ++++- .../payment/order/RefundReportService.php | 19 +- app/service/payment/order/RefundService.php | 58 ++++- app/service/payment/runtime/NotifyService.php | 45 +++- .../runtime/PaymentPluginFactoryService.php | 104 ++++++++ .../payment/runtime/PaymentPluginManager.php | 42 ++- .../runtime/PaymentRouteResolverService.php | 102 +++++++- .../payment/runtime/PaymentRouteService.php | 19 +- .../settlement/SettlementLifecycleService.php | 46 +++- .../SettlementOrderQueryService.php | 43 +++- .../payment/settlement/SettlementService.php | 32 ++- .../payment/trace/TradeTraceReportService.php | 34 ++- .../payment/trace/TradeTraceService.php | 63 +++++ .../system/access/AdminAuthService.php | 35 ++- .../config/SystemConfigDefinitionService.php | 63 ++++- .../system/config/SystemConfigPageService.php | 63 ++++- .../config/SystemConfigRuntimeService.php | 54 +++- app/service/system/user/AdminUserService.php | 37 ++- 252 files changed, 9218 insertions(+), 659 deletions(-) diff --git a/app/command/EpayMapiTest.php b/app/command/EpayMapiTest.php index 41135a4..498b836 100644 --- a/app/command/EpayMapiTest.php +++ b/app/command/EpayMapiTest.php @@ -28,13 +28,23 @@ use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; use support\Request; +/** + * ePay mapi 兼容层烟雾测试命令。 + * + * 用于验证真实商户、路由、插件配置和 mapi 调用是否连通,并输出落库后的订单快照。 + */ #[AsCommand('epay:mapi', '运行 ePay mapi 兼容接口烟雾测试')] class EpayMapiTest extends Command { + /** + * 配置命令参数。 + * + * @return void + */ protected function configure(): void { $this - ->setDescription('自动读取真实商户、路由和插件配置,测试 ePay mapi 是否正常调用并返回结果。') + ->setDescription('自动读取真实商户、路由和插件配置,测试 ePay mapi 是否能正常调用并返回可用结果。') ->addOption('live', null, InputOption::VALUE_NONE, '使用真实数据库并发起实际 mapi 调用') ->addOption('merchant-id', null, InputOption::VALUE_OPTIONAL, '指定商户 ID') ->addOption('merchant-no', null, InputOption::VALUE_OPTIONAL, '指定商户号') @@ -44,6 +54,13 @@ class EpayMapiTest extends Command ->addOption('out-trade-no', null, InputOption::VALUE_OPTIONAL, '商户订单号,默认自动生成'); } + /** + * 执行 ePay mapi 烟雾测试。 + * + * @param InputInterface $input 命令输入 + * @param OutputInterface $output 输出对象 + * @return int 命令退出码 + */ protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln('epay mapi 烟雾测试'); @@ -91,14 +108,15 @@ class EpayMapiTest extends Command $this->writeRouteSnapshot($output, $route); $payload = $this->buildPayload( - merchant: $merchant, - credential: $credential, - paymentType: $paymentType, - merchantOrderNo: $outTradeNo, - money: $money, - device: $device, - siteUrl: $siteUrl + $merchant, + $credential, + $paymentType, + $outTradeNo, + $money, + $device, + $siteUrl ); + /** @var EpayController $controller */ $controller = $this->resolve(EpayController::class); $response = $controller->mapi($this->buildRequest($payload)); $responseData = $this->decodeResponse($response->rawBody()); @@ -115,6 +133,11 @@ class EpayMapiTest extends Command } } + /** + * 确保烟雾测试依赖可解析。 + * + * @return void + */ private function ensureDependencies(): void { $this->resolve(EpayController::class); @@ -130,7 +153,13 @@ class EpayMapiTest extends Command } /** - * @return array{merchant:Merchant,credential:MerchantApiCredential,payment_type:PaymentType,route:array} + * 发现可用于测试的商户、凭证和路由上下文。 + * + * @param int $merchantIdOption 商户 ID 选项 + * @param string $merchantNoOption 商户编号选项 + * @param string $typeCode 支付方式编码 + * @return array 上下文数据 + * @throws RuntimeException */ private function discoverContext(int $merchantIdOption, string $merchantNoOption, string $typeCode): array { @@ -144,7 +173,7 @@ class EpayMapiTest extends Command $merchant = $this->pickMerchant($merchantIdOption, $merchantNoOption); $credential = $this->findMerchantCredential((int) $merchant->id); if (!$credential) { - throw new RuntimeException('商户未开通有效接口凭证: ' . $merchant->merchant_no); + throw new RuntimeException('商户未开通有效 API 凭证: ' . $merchant->merchant_no); } $route = $this->buildRouteSnapshot((int) $merchant->group_id, (int) $paymentType->id); @@ -160,6 +189,14 @@ class EpayMapiTest extends Command ]; } + /** + * 挑选可用商户。 + * + * @param int $merchantIdOption 商户 ID 选项 + * @param string $merchantNoOption 商户编号选项 + * @return Merchant 商户记录 + * @throws RuntimeException + */ private function pickMerchant(int $merchantIdOption, string $merchantNoOption): Merchant { /** @var MerchantRepository $merchantRepository */ @@ -195,6 +232,12 @@ class EpayMapiTest extends Command return $merchant; } + /** + * 查询商户凭证 + * + * @param int $merchantId 商户ID + * @return MerchantApiCredential|null 商户 API 凭证 + */ private function findMerchantCredential(int $merchantId): ?MerchantApiCredential { /** @var MerchantApiCredentialRepository $repository */ @@ -208,7 +251,11 @@ class EpayMapiTest extends Command } /** - * @return array{bind:mixed,poll_group:PaymentPollGroup,candidates:array>}|null + * 构建路由快照。 + * + * @param int $merchantGroupId 商户分组ID + * @param int $payTypeId 支付类型ID + * @return array|null 路由快照 */ private function buildRouteSnapshot(int $merchantGroupId, int $payTypeId): ?array { @@ -271,6 +318,18 @@ class EpayMapiTest extends Command ]; } + /** + * 构建 ePay mapi 请求载荷。 + * + * @param Merchant $merchant 商户 + * @param MerchantApiCredential $credential 商户 API 凭证 + * @param PaymentType $paymentType 支付类型 + * @param string $merchantOrderNo 商户订单号 + * @param string $money 金额 + * @param string $device 设备类型 + * @param string $siteUrl 站点地址 + * @return array 请求载荷 + */ private function buildPayload( Merchant $merchant, MerchantApiCredential $credential, @@ -299,6 +358,13 @@ class EpayMapiTest extends Command return $payload; } + /** + * 根据响应和订单快照判定测试结果。 + * + * @param array $responseData 响应数据 + * @param array $orderSnapshot 订单快照 + * @return string 判定结果 + */ private function classifyAttempt(array $responseData, array $orderSnapshot): string { $responseCode = (int) ($responseData['code'] ?? 0); @@ -312,6 +378,13 @@ class EpayMapiTest extends Command return ($payOrder && $bizOrder) ? 'pass' : 'fail'; } + /** + * 输出路由快照。 + * + * @param OutputInterface $output 输出对象 + * @param array $route 路由快照 + * @return void + */ private function writeRouteSnapshot(OutputInterface $output, array $route): void { /** @var PaymentPollGroup $pollGroup */ @@ -343,6 +416,15 @@ class EpayMapiTest extends Command } } + /** + * 输出测试结果。 + * + * @param OutputInterface $output 输出对象 + * @param array $payload 请求载荷 + * @param array $responseData 响应数据 + * @param array $orderSnapshot 订单快照 + * @return void + */ private function writeAttempt(OutputInterface $output, array $payload, array $responseData, array $orderSnapshot): void { $status = $this->classifyAttempt($responseData, $orderSnapshot); @@ -418,6 +500,12 @@ class EpayMapiTest extends Command } } + /** + * 归纳支付参数快照。 + * + * @param array $snapshot 支付参数快照 + * @return array 归纳结果 + */ private function summarizePayParamsSnapshot(array $snapshot): array { if ($snapshot === []) { @@ -458,21 +546,45 @@ class EpayMapiTest extends Command return $summary; } + /** + * 获取路由模式名称。 + * + * @param int $routeMode 路由模式 + * @return string 路由模式名称 + */ private function routeModeLabel(int $routeMode): string { return RouteConstant::routeModeMap()[$routeMode] ?? '未知'; } + /** + * 获取通道模式名称。 + * + * @param int $channelMode 通道模式 + * @return string 通道模式名称 + */ private function channelModeLabel(int $channelMode): string { return RouteConstant::channelModeMap()[$channelMode] ?? '未知'; } + /** + * 获取订单状态名称。 + * + * @param int $status 状态 + * @return string 订单状态名称 + */ private function orderStatusLabel(int $status): string { return TradeConstant::orderStatusMap()[$status] ?? '未知'; } + /** + * 生成商户订单号。 + * + * @param string $base 基础订单号 + * @return string 商户订单号 + */ private function buildMerchantOrderNo(string $base): string { $base = trim($base); @@ -483,6 +595,13 @@ class EpayMapiTest extends Command return 'EPAY-MAPI-' . FormatHelper::timestamp(time(), 'YmdHis') . random_int(1000, 9999); } + /** + * 对载荷进行 MD5 签名。 + * + * @param array $payload 请求载荷 + * @param string $key 商户密钥 + * @return string 签名结果 + */ private function signPayload(array $payload, string $key): string { $params = $payload; @@ -502,6 +621,12 @@ class EpayMapiTest extends Command return md5(implode('&', $query) . $key); } + /** + * 构建模拟请求对象。 + * + * @param array $payload 请求载荷 + * @return Request 请求对象 + */ private function buildRequest(array $payload): Request { $body = http_build_query($payload, '', '&', PHP_QUERY_RFC1738); @@ -523,6 +648,13 @@ class EpayMapiTest extends Command return new Request($rawRequest); } + /** + * 加载订单快照。 + * + * @param int $merchantId 商户ID + * @param string $merchantOrderNo 商户订单号 + * @return array 订单快照 + */ private function loadOrderSnapshot(int $merchantId, string $merchantOrderNo): array { /** @var BizOrderRepository $bizOrderRepository */ @@ -547,12 +679,24 @@ class EpayMapiTest extends Command ]; } + /** + * 解析站点地址。 + * + * @return string 站点地址 + */ private function resolveSiteUrl(): string { $siteUrl = trim((string) sys_config('site_url')); return $siteUrl !== '' ? rtrim($siteUrl, '/') : 'http://localhost:8787'; } + /** + * 归一化金额字符串。 + * + * @param string $money 金额 + * @return string 金额字符串 + * @throws RuntimeException + */ private function normalizeMoney(string $money): string { $money = trim($money); @@ -567,18 +711,36 @@ class EpayMapiTest extends Command return number_format((float) $money, 2, '.', ''); } + /** + * 归一化设备类型。 + * + * @param string $device 设备类型 + * @return string 设备类型 + */ private function normalizeDevice(string $device): string { $device = strtolower(trim($device)); return $device !== '' ? $device : 'pc'; } + /** + * 解析响应体。 + * + * @param string $body 响应体 + * @return array 解析结果 + */ private function decodeResponse(string $body): array { $decoded = json_decode($body, true); return is_array($decoded) ? $decoded : ['raw' => $body]; } + /** + * 将值转为字符串。 + * + * @param mixed $value 可转为字符串的值 + * @return string 可展示字符串 + */ private function stringifyValue(mixed $value): string { if ($value === null) { @@ -598,6 +760,13 @@ class EpayMapiTest extends Command return (string) $value; } + /** + * 限制字符串长度。 + * + * @param string $value 待截断文本 + * @param int $length 最大长度 + * @return string 截断后的字符串 + */ private function limitString(string $value, int $length): string { $value = trim($value); @@ -608,16 +777,34 @@ class EpayMapiTest extends Command return strlen($value) <= $length ? $value : substr($value, 0, $length) . '...'; } + /** + * 归一化空白字符。 + * + * @param string $value 待归一化文本 + * @return string 归一化结果 + */ private function normalizeWhitespace(string $value): string { return preg_replace('/\s+/', ' ', trim($value)) ?: ''; } + /** + * 格式化 JSON。 + * + * @param mixed $value 可编码为 JSON 的值 + * @return string JSON 文本 + */ private function formatJson(mixed $value): string { return FormatHelper::json($value); } + /** + * 格式化异常文本。 + * + * @param Throwable $e 异常 + * @return string 文本结果 + */ private function formatThrowable(\Throwable $e): string { $data = method_exists($e, 'getData') ? $e->getData() : []; @@ -626,18 +813,42 @@ class EpayMapiTest extends Command return $e::class . ': ' . $e->getMessage() . $suffix; } + /** + * 读取字符串选项。 + * + * @param InputInterface $input 命令输入 + * @param string $name 选项名称 + * @param string $default 默认值 + * @return string 选项值 + */ private function optionString(InputInterface $input, string $name, string $default = ''): string { $value = $input->getOption($name); return $value === null || $value === false ? $default : (is_string($value) ? $value : (string) $value); } + /** + * 读取整数选项。 + * + * @param InputInterface $input 命令输入 + * @param string $name 选项名称 + * @param int $default 默认值 + * @return int 选项值 + */ private function optionInt(InputInterface $input, string $name, int $default = 0): int { $value = $input->getOption($name); return is_numeric($value) ? (int) $value : $default; } + /** + * 读取布尔选项。 + * + * @param InputInterface $input 命令输入 + * @param string $name 选项名称 + * @param bool $default 默认值 + * @return bool 布尔值 + */ private function optionBool(InputInterface $input, string $name, bool $default = false): bool { $value = $input->getOption($name); @@ -650,6 +861,13 @@ class EpayMapiTest extends Command return $filtered === null ? $default : $filtered; } + /** + * 从容器中解析指定类实例。 + * + * @param string $class 类名 + * @return object 对象实例 + * @throws RuntimeException + */ private function resolve(string $class): object { try { diff --git a/app/command/MpayTest.php b/app/command/MpayTest.php index 41dd814..67a13b6 100644 --- a/app/command/MpayTest.php +++ b/app/command/MpayTest.php @@ -18,9 +18,19 @@ use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Input\InputOption; use Symfony\Component\Console\Output\OutputInterface; +/** + * MpayTest 烟雾测试命令。 + * + * 用于验证支付、退款、清结算、余额和追踪链路的核心依赖与关键流程。 + */ #[AsCommand('mpay:test', '运行支付、退款、清结算、余额和追踪烟雾测试')] class MpayTest extends Command { + /** + * 配置命令参数。 + * + * @return void + */ protected function configure(): void { $this @@ -34,6 +44,13 @@ class MpayTest extends Command ->addOption('live', null, InputOption::VALUE_NONE, '在提供测试数据时运行真实数据库检查'); } + /** + * 运行选定链路的烟雾测试。 + * + * @param InputInterface $input 命令输入 + * @param OutputInterface $output 命令输出 + * @return int 命令退出码 + */ protected function execute(InputInterface $input, OutputInterface $output): int { $cases = $this->resolveCases($input); @@ -73,7 +90,10 @@ class MpayTest extends Command } /** - * 根据命令行选项解析需要执行的测试项。 + * 根据命令行选项解析本次需要检查的链路。 + * + * @param InputInterface $input 命令输入 + * @return array 测试项列表 */ private function resolveCases(InputInterface $input): array { @@ -92,7 +112,10 @@ class MpayTest extends Command } /** - * 检查支付链路。 + * 校验支付下单与后续状态流转。 + * + * @param bool $live 是否使用真实数据 + * @return array 检查结果 */ private function checkPayment(bool $live): array { @@ -155,7 +178,10 @@ class MpayTest extends Command } /** - * 检查退款链路。 + * 校验退款创建与后续状态流转。 + * + * @param bool $live 是否使用真实数据 + * @return array 检查结果 */ private function checkRefund(bool $live): array { @@ -214,7 +240,10 @@ class MpayTest extends Command } /** - * 检查清结算链路。 + * 校验清结算单创建与入账结果。 + * + * @param bool $live 是否使用真实数据 + * @return array 检查结果 */ private function checkSettlement(bool $live): array { @@ -316,7 +345,10 @@ class MpayTest extends Command } /** - * 检查余额链路。 + * 校验商户余额快照查询。 + * + * @param bool $live 是否使用真实数据 + * @return array 检查结果 */ private function checkBalance(bool $live): array { @@ -360,7 +392,10 @@ class MpayTest extends Command } /** - * 检查统一追踪链路。 + * 校验追踪查询聚合结果。 + * + * @param bool $live 是否使用真实数据 + * @return array 检查结果 */ private function checkTrace(bool $live): array { @@ -399,6 +434,10 @@ class MpayTest extends Command /** * 从容器中解析指定类实例。 + * + * @param string $class 类名 + * @return object 对象实例 + * @throws RuntimeException */ private function resolve(string $class): object { @@ -417,6 +456,11 @@ class MpayTest extends Command /** * 检查实例是否包含指定方法。 + * + * @param object $instance 实例 + * @param string $method 方法名 + * @return void + * @throws RuntimeException */ private function ensureMethod(object $instance, string $method): void { @@ -427,6 +471,10 @@ class MpayTest extends Command /** * 读取字符串环境变量。 + * + * @param string $key 环境变量名 + * @param string $default 默认值 + * @return string 字符串值 */ private function envString(string $key, string $default = ''): string { @@ -437,6 +485,10 @@ class MpayTest extends Command /** * 读取整数环境变量。 + * + * @param string $key 环境变量名 + * @param int $default 默认值 + * @return int 整数值 */ private function envInt(string $key, int $default = 0): int { @@ -447,6 +499,10 @@ class MpayTest extends Command /** * 读取布尔环境变量。 + * + * @param string $key 环境变量名 + * @param bool $default 默认值 + * @return bool 布尔值 */ private function envBool(string $key, bool $default = false): bool { @@ -462,6 +518,10 @@ class MpayTest extends Command /** * 读取结构化环境变量。 + * + * @param string $key 环境变量名 + * @param array $default 默认值 + * @return array 结构化数据 */ private function envJson(string $key, array $default = []): array { @@ -476,6 +536,9 @@ class MpayTest extends Command /** * 生成测试编号。 + * + * @param string $prefix 前缀 + * @return string 测试编号 */ private function generateTestNo(string $prefix): string { @@ -484,6 +547,9 @@ class MpayTest extends Command /** * 将异常格式化为可读文本。 + * + * @param Throwable $e 异常 + * @return string 文本结果 */ private function formatThrowable(\Throwable $e): string { @@ -494,7 +560,13 @@ class MpayTest extends Command } /** - * 输出单个测试项的执行结果。 + * 输出单个链路的检查结果。 + * + * @param OutputInterface $output 输出对象 + * @param string $case 测试项 + * @param string $status 状态 + * @param string $message 消息 + * @return void */ private function writeResult(OutputInterface $output, string $case, string $status, string $message): void { @@ -508,3 +580,9 @@ class MpayTest extends Command } } + + + + + + diff --git a/app/command/SystemConfigSync.php b/app/command/SystemConfigSync.php index fcf8d30..61ff865 100644 --- a/app/command/SystemConfigSync.php +++ b/app/command/SystemConfigSync.php @@ -11,14 +11,31 @@ use Symfony\Component\Console\Command\Command; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +/** + * 系统配置同步命令。 + * + * 将系统配置定义中的默认值同步到数据库,并刷新运行时配置缓存。 + */ #[AsCommand('system:config-sync', '同步系统配置默认值到数据库')] class SystemConfigSync extends Command { + /** + * 配置命令说明。 + * + * @return void + */ protected function configure(): void { $this->setDescription('同步 config/system_config.php 中定义的系统配置默认值到数据库。'); } + /** + * 将系统配置定义同步到数据库并刷新运行时缓存。 + * + * @param InputInterface $input 命令输入 + * @param OutputInterface $output 命令输出 + * @return int 命令退出码 + */ protected function execute(InputInterface $input, OutputInterface $output): int { try { @@ -68,3 +85,6 @@ class SystemConfigSync extends Command } } } + + + diff --git a/app/command/Test.php b/app/command/Test.php index ad1f762..a86270f 100644 --- a/app/command/Test.php +++ b/app/command/Test.php @@ -7,16 +7,35 @@ use Symfony\Component\Console\Attribute\AsCommand; use Symfony\Component\Console\Input\InputInterface; use Symfony\Component\Console\Output\OutputInterface; +/** + * 基础测试命令。 + * + * 用于快速验证命令注册和控制台输出是否正常。 + */ #[AsCommand('test', 'test')] class Test extends Command { + /** + * 配置命令说明。 + * + * @return void + */ protected function configure(): void { } + /** + * 输出命令名以验证命令注册正常。 + * + * @param InputInterface $input 命令输入 + * @param OutputInterface $output 命令输出 + * @return int 命令退出码 + */ protected function execute(InputInterface $input, OutputInterface $output): int { $output->writeln('Hello ' . $this->getName() . ''); return self::SUCCESS; } } + + diff --git a/app/common/base/BaseController.php b/app/common/base/BaseController.php index 97426f4..cae4989 100644 --- a/app/common/base/BaseController.php +++ b/app/common/base/BaseController.php @@ -16,6 +16,11 @@ class BaseController { /** * 返回成功响应。 + * + * @param mixed $data 响应数据 + * @param string $message 响应消息 + * @param int $code 响应码 + * @return Response 响应对象 */ protected function success(mixed $data = null, string $message = '操作成功', int $code = 200): Response { @@ -28,6 +33,11 @@ class BaseController /** * 返回失败响应。 + * + * @param string $message 响应消息 + * @param int $code 响应码 + * @param mixed $data 响应数据 + * @return Response 响应对象 */ protected function fail(string $message = '操作失败', int $code = 500, mixed $data = null): Response { @@ -40,6 +50,9 @@ class BaseController /** * 返回统一分页响应。 + * + * @param mixed $paginator 分页器实例 + * @return Response 响应对象 */ protected function page(mixed $paginator): Response { @@ -63,7 +76,10 @@ class BaseController /** * 通过校验器类验证请求数据。 * - * @param class-string $validatorClass + * @param array $data 请求数据 + * @param string $validatorClass 校验器类 + * @param string|null $scene 校验场景 + * @return array 校验后的数据 */ protected function validated(array $data, string $validatorClass, ?string $scene = null): array { @@ -80,6 +96,9 @@ class BaseController /** * 获取中间件预处理后的标准化参数。 + * + * @param Request $request 请求对象 + * @return array 标准化参数 */ protected function payload(Request $request): array { @@ -95,6 +114,11 @@ class BaseController /** * 读取请求属性。 + * + * @param Request $request 请求对象 + * @param string $key 属性名 + * @param mixed $default 默认值 + * @return mixed 请求上下文值 */ protected function requestAttribute(Request $request, string $key, mixed $default = null): mixed { @@ -103,6 +127,9 @@ class BaseController /** * 获取中间件注入的当前管理员 ID。 + * + * @param Request $request 请求对象 + * @return int 当前管理员ID */ protected function currentAdminId(Request $request): int { @@ -111,6 +138,9 @@ class BaseController /** * 获取中间件注入的当前商户 ID。 + * + * @param Request $request 请求对象 + * @return int 当前商户ID */ protected function currentMerchantId(Request $request): int { @@ -119,6 +149,9 @@ class BaseController /** * 获取中间件注入的当前商户编号。 + * + * @param Request $request 请求对象 + * @return string 当前商户编号 */ protected function currentMerchantNo(Request $request): string { @@ -126,3 +159,8 @@ class BaseController } } + + + + + diff --git a/app/common/base/BaseModel.php b/app/common/base/BaseModel.php index 2a9c80a..289454b 100644 --- a/app/common/base/BaseModel.php +++ b/app/common/base/BaseModel.php @@ -43,9 +43,13 @@ class BaseModel extends Model * * 避免前端收到 ISO8601(如 2026-04-02T01:50:40.000000Z)这类不直观的时间串, * 统一改为后台常用的本地展示格式。 + * + * @param DateTimeInterface $date 时间对象 + * @return string 格式化后的日期时间 */ protected function serializeDate(DateTimeInterface $date): string { return FormatHelper::dateTime($date); } } + diff --git a/app/common/base/BasePayment.php b/app/common/base/BasePayment.php index 31037d7..0a09dea 100644 --- a/app/common/base/BasePayment.php +++ b/app/common/base/BasePayment.php @@ -14,7 +14,7 @@ use support\Log; /** * 支付插件基类(建议所有插件继承) * - * 目标:把“插件共性”集中在这里,具体渠道差异留给子类实现 `PaymentInterface`。 + * 目标:把“插件共性”集中在这里,具体渠道差异留给子类补齐支付动作能力。 * * 生命周期: * - 服务层会在每次动作前调用 `init($channelConfig)` 注入该通道配置。 @@ -48,15 +48,19 @@ abstract class BasePayment implements PayPluginInterface */ protected array $channelConfig = []; - /** HTTP 请求客户端(GuzzleHttp) */ + /** + * HTTP 请求客户端(GuzzleHttp) + * + * @var Client|null + */ private ?Client $httpClient = null; // ==================== 初始化 ==================== /** - * 初始化插件,加载通道配置并创建 HTTP 客户端 + * 初始化插件,加载通道配置并创建 HTTP 客户端。 * - * @param array $channelConfig 通道配置(商户号、密钥等) + * @param array $channelConfig 渠道配置 * @return void */ public function init(array $channelConfig): void @@ -71,11 +75,11 @@ abstract class BasePayment implements PayPluginInterface } /** - * 获取通道配置项 + * 获取通道配置项。 * - * @param string $key 配置键 - * @param mixed $default 默认值(键不存在时返回) - * @return mixed + * @param string $key 配置键 + * @param mixed $default 默认值 + * @return mixed 通道配置值 */ protected function getConfig(string $key, mixed $default = null): mixed { @@ -84,31 +88,51 @@ abstract class BasePayment implements PayPluginInterface // ==================== 插件元信息 ==================== - /** 获取插件代码(唯一标识) */ + /** + * 获取插件代码(唯一标识)。 + * + * @return string 插件代码 + */ public function getCode(): string { return $this->paymentInfo['code'] ?? ''; } - /** 获取插件名称 */ + /** + * 获取插件名称。 + * + * @return string 插件名称 + */ public function getName(): string { return $this->paymentInfo['name'] ?? ''; } - /** 获取作者名称 */ + /** + * 获取作者名称。 + * + * @return string 作者名称 + */ public function getAuthorName(): string { return $this->paymentInfo['author'] ?? ''; } - /** 获取作者链接 */ + /** + * 获取作者链接。 + * + * @return string 作者链接 + */ public function getAuthorLink(): string { return $this->paymentInfo['link'] ?? ''; } - /** 获取版本号 */ + /** + * 获取版本号。 + * + * @return string 版本号 + */ public function getVersion(): string { return $this->paymentInfo['version'] ?? ''; @@ -117,9 +141,9 @@ abstract class BasePayment implements PayPluginInterface // ==================== 能力声明 ==================== /** - * 获取插件支持的支付方式列表 + * 获取插件支持的支付方式列表。 * - * @return array 支付方式代码数组,如 ['alipay', 'wechat'] + * @return array 支持的支付方式编码 */ public function getEnabledPayTypes(): array { @@ -127,9 +151,9 @@ abstract class BasePayment implements PayPluginInterface } /** - * 获取插件支持的转账方式列表 + * 获取插件支持的转账方式列表。 * - * @return array 转账方式代码数组 + * @return array 支持的转账方式编码 */ public function getEnabledTransferTypes(): array { @@ -137,9 +161,9 @@ abstract class BasePayment implements PayPluginInterface } /** - * 获取插件配置表单结构(用于后台配置界面) + * 获取插件配置表单结构(用于后台配置界面)。 * - * @return array 表单字段定义数组 + * @return array 配置表单结构 */ public function getConfigSchema(): array { @@ -149,13 +173,13 @@ abstract class BasePayment implements PayPluginInterface // ==================== HTTP 请求 ==================== /** - * 统一 HTTP 请求(对外调用支付渠道 API) + * 统一 HTTP 请求(对外调用支付渠道 API)。 * - * @param string $method 请求方法(GET/POST/PUT/DELETE 等) - * @param string $url 请求地址 - * @param array $options Guzzle 请求选项(headers、json、form_params 等) - * @return ResponseInterface - * @throws PaymentException 未调用 init() 或渠道请求失败时 + * @param string $method 请求方法 + * @param string $url 请求地址 + * @param array $options 请求选项 + * @return ResponseInterface 响应对象 + * @throws PaymentException */ protected function request(string $method, string $url, array $options = []): ResponseInterface { @@ -171,3 +195,8 @@ abstract class BasePayment implements PayPluginInterface } } } + + + + + diff --git a/app/common/base/BaseRepository.php b/app/common/base/BaseRepository.php index 4244b06..f986da6 100644 --- a/app/common/base/BaseRepository.php +++ b/app/common/base/BaseRepository.php @@ -15,11 +15,16 @@ abstract class BaseRepository { /** * 当前仓储绑定的模型实例。 + * + * @var Model */ protected Model $model; - + /** - * 构造函数,绑定模型实例。 + * 构造方法。 + * + * @param Model $model 模型实例 + * @return void */ public function __construct(Model $model) { @@ -28,6 +33,8 @@ abstract class BaseRepository /** * 获取查询构造器。 + * + * @return \Illuminate\Database\Eloquent\Builder 查询构造器 */ public function query() { @@ -36,6 +43,10 @@ abstract class BaseRepository /** * 按主键查询记录。 + * + * @param int|string $id 主键 + * @param array $columns 字段列表 + * @return Model|null 记录或空 */ public function find(int|string $id, array $columns = ['*']): ?Model { @@ -44,6 +55,9 @@ abstract class BaseRepository /** * 新增记录。 + * + * @param array $data 新增数据 + * @return Model 新增后的模型 */ public function create(array $data): Model { @@ -52,6 +66,10 @@ abstract class BaseRepository /** * 按主键更新记录。 + * + * @param int|string $id 主键 + * @param array $data 更新数据 + * @return bool 是否更新成功 */ public function updateById(int|string $id, array $data): bool { @@ -60,6 +78,10 @@ abstract class BaseRepository /** * 按唯一键更新记录。 + * + * @param int|string $key 键值 + * @param array $data 更新数据 + * @return bool 是否更新成功 */ public function updateByKey(int|string $key, array $data): bool { @@ -68,6 +90,10 @@ abstract class BaseRepository /** * 按条件批量更新记录。 + * + * @param array $where 条件 + * @param array $data 更新数据 + * @return int 受影响行数 */ public function updateWhere(array $where, array $data): int { @@ -82,6 +108,9 @@ abstract class BaseRepository /** * 按主键删除记录。 + * + * @param int|string $id 主键 + * @return bool 是否删除成功 */ public function deleteById(int|string $id): bool { @@ -90,6 +119,9 @@ abstract class BaseRepository /** * 按条件批量删除记录。 + * + * @param array $where 条件 + * @return int 受影响行数 */ public function deleteWhere(array $where): int { @@ -104,6 +136,10 @@ abstract class BaseRepository /** * 按条件获取首条记录。 + * + * @param array $where 条件 + * @param array $columns 字段列表 + * @return Model|null 记录或空 */ public function firstBy(array $where = [], array $columns = ['*']): ?Model { @@ -118,6 +154,10 @@ abstract class BaseRepository /** * 先查后更,不存在则创建。 + * + * @param array $where 条件 + * @param array $data 更新数据 + * @return Model 记录 */ public function updateOrCreate(array $where, array $data = []): Model { @@ -156,6 +196,9 @@ abstract class BaseRepository /** * 按条件统计数量。 + * + * @param array $where 条件 + * @return int 数量 */ public function countBy(array $where = []): int { @@ -170,6 +213,9 @@ abstract class BaseRepository /** * 判断条件下是否存在记录。 + * + * @param array $where 条件 + * @return bool 是否存在 */ public function existsBy(array $where = []): bool { @@ -185,7 +231,11 @@ abstract class BaseRepository /** * 分页查询。 * - * @param array $where 条件数组,空值会被忽略 + * @param array $where 条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @param array $columns 字段列表 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 */ public function paginate(array $where = [], int $page = 1, int $pageSize = 10, array $columns = ['*']) { @@ -198,3 +248,8 @@ abstract class BaseRepository return $query->paginate($pageSize, $columns, 'page', $page); } } + + + + + diff --git a/app/common/base/BaseService.php b/app/common/base/BaseService.php index 649fa7b..58ca31a 100644 --- a/app/common/base/BaseService.php +++ b/app/common/base/BaseService.php @@ -18,6 +18,9 @@ class BaseService * * 适用于 biz_no / pay_no / refund_no / settle_no / notify_no / ledger_no 等场景。 * 默认使用时间前缀 + 随机数,保证可读性和基本唯一性。 + * + * @param string $prefix 单号前缀 + * @return string 单号 */ protected function generateNo(string $prefix = ''): string { @@ -31,6 +34,8 @@ class BaseService * 获取当前时间字符串。 * * 统一返回 `Y-m-d H:i:s` 格式,便于数据库写入和日志输出。 + * + * @return string 时间字符串 */ protected function now(): string { @@ -39,6 +44,9 @@ class BaseService /** * 金额格式化,单位为元。 + * + * @param int $amount 金额(分) + * @return string 格式化后的金额 */ protected function formatAmount(int $amount): string { @@ -47,6 +55,9 @@ class BaseService /** * 金额格式化,0 时显示不限。 + * + * @param int $amount 金额(分) + * @return string 格式化后的金额 */ protected function formatAmountOrUnlimited(int $amount): string { @@ -55,6 +66,9 @@ class BaseService /** * 次数格式化,0 时显示不限。 + * + * @param int $count 次数 + * @return string 格式化后的次数 */ protected function formatCountOrUnlimited(int $count): string { @@ -63,6 +77,9 @@ class BaseService /** * 费率格式化,单位为百分点。 + * + * @param int $basisPoints 基点值 + * @return string 格式化后的费率 */ protected function formatRate(int $basisPoints): string { @@ -71,6 +88,9 @@ class BaseService /** * 延迟格式化。 + * + * @param int $latencyMs 延迟毫秒数 + * @return string 格式化后的延迟 */ protected function formatLatency(int $latencyMs): string { @@ -79,6 +99,10 @@ class BaseService /** * 日期格式化。 + * + * @param mixed $value 日期时间值 + * @param string $emptyText 为空时显示文案 + * @return string 格式化后的日期 */ protected function formatDate(mixed $value, string $emptyText = ''): string { @@ -87,6 +111,10 @@ class BaseService /** * 日期时间格式化。 + * + * @param mixed $value 日期时间值 + * @param string $emptyText 为空时显示文案 + * @return string 格式化后的日期时间 */ protected function formatDateTime(mixed $value, string $emptyText = ''): string { @@ -95,6 +123,10 @@ class BaseService /** * JSON 文本格式化。 + * + * @param mixed $value JSON 值 + * @param string $emptyText 为空时显示文案 + * @return string 格式化后的 JSON 文本 */ protected function formatJson(mixed $value, string $emptyText = ''): string { @@ -103,6 +135,11 @@ class BaseService /** * 映射表文本转换。 + * + * @param int $value 待映射值 + * @param array $map 映射表 + * @param string $default 默认值 + * @return string 映射后的文本 */ protected function textFromMap(int $value, array $map, string $default = '未知'): string { @@ -111,6 +148,10 @@ class BaseService /** * 接口凭证明文脱敏。 + * + * @param string $credentialValue 凭证原文 + * @param bool $maskShortValue 是否对短值也进行脱敏 + * @return string 脱敏后的文本 */ protected function maskCredentialValue(string $credentialValue, bool $maskShortValue = true): string { @@ -119,6 +160,9 @@ class BaseService /** * 将模型或对象归一化成数组。 + * + * @param mixed $value 模型实例、数组或可序列化对象 + * @return array|null 归一化结果 */ protected function normalizeModel(mixed $value): ?array { @@ -130,8 +174,8 @@ class BaseService * * 适合单次数据库事务,不包含自动重试逻辑。 * - * @param callable $callback 事务体 - * @return mixed + * @param callable $callback 回调 + * @return mixed 回调原始返回值 */ protected function transaction(callable $callback) { @@ -144,6 +188,11 @@ class BaseService * 支持重试的事务封装。 * * 适合余额冻结、扣减、状态推进和幂等写入等容易发生锁冲突的场景。 + * + * @param callable $callback 回调 + * @param int $attempts 重试次数 + * @param int $sleepMs 重试间隔毫秒数 + * @return mixed 回调原始返回值 */ protected function transactionRetry(callable $callback, int $attempts = 3, int $sleepMs = 50) { diff --git a/app/common/constant/AuthConstant.php b/app/common/constant/AuthConstant.php index dda5096..9c28039 100644 --- a/app/common/constant/AuthConstant.php +++ b/app/common/constant/AuthConstant.php @@ -9,19 +9,51 @@ namespace app\common\constant; */ final class AuthConstant { + /** + * 管理员登录域。 + */ public const GUARD_ADMIN = 1; + + /** + * 商户登录域。 + */ public const GUARD_MERCHANT = 2; + /** + * JWT 签名算法。 + */ public const JWT_ALG_HS256 = 'HS256'; + /** + * 令牌禁用状态。 + */ public const TOKEN_STATUS_DISABLED = 0; + + /** + * 令牌启用状态。 + */ public const TOKEN_STATUS_ENABLED = 1; + /** + * 登录禁用状态。 + */ public const LOGIN_STATUS_DISABLED = 0; + + /** + * 登录启用状态。 + */ public const LOGIN_STATUS_ENABLED = 1; + /** + * API 签名类型:MD5。 + */ public const API_SIGN_TYPE_MD5 = 0; + /** + * 获取签名类型映射。 + * + * @return array 签名类型名称表 + */ public static function signTypeMap(): array { return [ @@ -29,6 +61,11 @@ final class AuthConstant ]; } + /** + * 获取登录域映射。 + * + * @return array 登录域名称表 + */ public static function guardMap(): array { return [ @@ -37,3 +74,7 @@ final class AuthConstant ]; } } + + + + diff --git a/app/common/constant/CommonConstant.php b/app/common/constant/CommonConstant.php index e5fd09b..ff82946 100644 --- a/app/common/constant/CommonConstant.php +++ b/app/common/constant/CommonConstant.php @@ -3,16 +3,35 @@ namespace app\common\constant; /** - * 通用状态常量。 + * 通用状态枚举。 */ final class CommonConstant { + /** + * 禁用状态。 + */ public const STATUS_DISABLED = 0; + + /** + * 启用状态。 + */ public const STATUS_ENABLED = 1; + /** + * 否。 + */ public const NO = 0; + + /** + * 是。 + */ public const YES = 1; + /** + * 获取状态名称映射。 + * + * @return array 状态名称表 + */ public static function statusMap(): array { return [ @@ -21,6 +40,11 @@ final class CommonConstant ]; } + /** + * 获取是否名称映射。 + * + * @return array 是否名称表 + */ public static function yesNoMap(): array { return [ @@ -29,3 +53,7 @@ final class CommonConstant ]; } } + + + + diff --git a/app/common/constant/FileConstant.php b/app/common/constant/FileConstant.php index 3b91cdf..4cdaf66 100644 --- a/app/common/constant/FileConstant.php +++ b/app/common/constant/FileConstant.php @@ -4,23 +4,69 @@ namespace app\common\constant; /** * 文件相关常量。 + * + * 用于描述文件来源、可见性、场景、存储引擎和文件类型白名单。 */ final class FileConstant { + /** + * 上传来源。 + */ public const SOURCE_UPLOAD = 1; + + /** + * 远程 URL 导入来源。 + */ public const SOURCE_REMOTE_URL = 2; + /** + * 公开可访问文件。 + */ public const VISIBILITY_PUBLIC = 1; + + /** + * 私有文件。 + */ public const VISIBILITY_PRIVATE = 2; + /** + * 图片场景。 + */ public const SCENE_IMAGE = 1; + + /** + * 证书场景。 + */ public const SCENE_CERTIFICATE = 2; + + /** + * 文本场景。 + */ public const SCENE_TEXT = 3; + + /** + * 其他场景。 + */ public const SCENE_OTHER = 4; + /** + * 本地存储引擎。 + */ public const STORAGE_LOCAL = 1; + + /** + * 阿里云 OSS 存储引擎。 + */ public const STORAGE_ALIYUN_OSS = 2; + + /** + * 腾讯云 COS 存储引擎。 + */ public const STORAGE_TENCENT_COS = 3; + + /** + * 远程引用存储引擎。 + */ public const STORAGE_REMOTE_URL = 4; public const CONFIG_DEFAULT_ENGINE = 'file_storage_default_engine'; @@ -42,6 +88,11 @@ final class FileConstant public const CONFIG_COS_SECRET_KEY = 'file_storage_tencent_cos_secret_key'; public const CONFIG_COS_PUBLIC_DOMAIN = 'file_storage_tencent_cos_public_domain'; + /** + * 获取文件来源映射。 + * + * @return array 来源名称表 + */ public static function sourceTypeMap(): array { return [ @@ -50,6 +101,11 @@ final class FileConstant ]; } + /** + * 获取文件可见性映射。 + * + * @return array 可见性名称表 + */ public static function visibilityMap(): array { return [ @@ -58,6 +114,11 @@ final class FileConstant ]; } + /** + * 获取文件场景映射。 + * + * @return array 场景名称表 + */ public static function sceneMap(): array { return [ @@ -68,6 +129,11 @@ final class FileConstant ]; } + /** + * 获取存储引擎映射。 + * + * @return array 存储引擎名称表 + */ public static function storageEngineMap(): array { return [ @@ -78,6 +144,11 @@ final class FileConstant ]; } + /** + * 获取可选存储引擎映射。 + * + * @return array 可选存储引擎名称表 + */ public static function selectableStorageEngineMap(): array { return [ @@ -87,6 +158,11 @@ final class FileConstant ]; } + /** + * 获取图片扩展名白名单。 + * + * @return array 白名单集合 + */ public static function imageExtensionMap(): array { return [ @@ -100,6 +176,11 @@ final class FileConstant ]; } + /** + * 获取证书扩展名白名单。 + * + * @return array 白名单集合 + */ public static function certificateExtensionMap(): array { return [ @@ -112,6 +193,11 @@ final class FileConstant ]; } + /** + * 获取文本扩展名白名单。 + * + * @return array 白名单集合 + */ public static function textExtensionMap(): array { return [ @@ -128,8 +214,17 @@ final class FileConstant ]; } + /** + * 获取默认允许上传的扩展名。 + * + * @return array 扩展名列表 + */ public static function defaultAllowedExtensions(): array { return array_keys(self::imageExtensionMap() + self::certificateExtensionMap() + self::textExtensionMap()); } } + + + + diff --git a/app/common/constant/LedgerConstant.php b/app/common/constant/LedgerConstant.php index a4783dc..17dfbcd 100644 --- a/app/common/constant/LedgerConstant.php +++ b/app/common/constant/LedgerConstant.php @@ -3,7 +3,7 @@ namespace app\common\constant; /** - * 账户流水相关枚举。 + * 账户流水枚举。 */ final class LedgerConstant { @@ -22,6 +22,11 @@ final class LedgerConstant public const DIRECTION_IN = 0; public const DIRECTION_OUT = 1; + /** + * 获取业务类型映射。 + * + * @return array 业务类型名称表 + */ public static function bizTypeMap(): array { return [ @@ -34,6 +39,11 @@ final class LedgerConstant ]; } + /** + * 获取事件类型映射。 + * + * @return array 事件类型名称表 + */ public static function eventTypeMap(): array { return [ @@ -44,6 +54,11 @@ final class LedgerConstant ]; } + /** + * 获取流水方向映射。 + * + * @return array 方向名称表 + */ public static function directionMap(): array { return [ @@ -52,3 +67,7 @@ final class LedgerConstant ]; } } + + + + diff --git a/app/common/constant/MerchantConstant.php b/app/common/constant/MerchantConstant.php index f1bd5d9..a28d005 100644 --- a/app/common/constant/MerchantConstant.php +++ b/app/common/constant/MerchantConstant.php @@ -7,14 +7,41 @@ namespace app\common\constant; */ final class MerchantConstant { + /** + * 个人商户类型。 + */ public const TYPE_PERSON = 0; + + /** + * 企业商户类型。 + */ public const TYPE_COMPANY = 1; + + /** + * 其他商户类型。 + */ public const TYPE_OTHER = 2; + /** + * 低风险等级。 + */ public const RISK_LOW = 0; + + /** + * 中风险等级。 + */ public const RISK_MEDIUM = 1; + + /** + * 高风险等级。 + */ public const RISK_HIGH = 2; + /** + * 获取商户类型映射。 + * + * @return array 商户类型名称表 + */ public static function typeMap(): array { return [ @@ -24,6 +51,11 @@ final class MerchantConstant ]; } + /** + * 获取商户风险等级映射。 + * + * @return array 风险等级名称表 + */ public static function riskLevelMap(): array { return [ @@ -33,3 +65,7 @@ final class MerchantConstant ]; } } + + + + diff --git a/app/common/constant/NotifyConstant.php b/app/common/constant/NotifyConstant.php index fac52df..02296b6 100644 --- a/app/common/constant/NotifyConstant.php +++ b/app/common/constant/NotifyConstant.php @@ -3,7 +3,7 @@ namespace app\common\constant; /** - * 通知与回调相关枚举。 + * 通知与回调枚举。 */ final class NotifyConstant { @@ -25,6 +25,11 @@ final class NotifyConstant public const TASK_STATUS_SUCCESS = 1; public const TASK_STATUS_FAILED = 2; + /** + * 获取通知类型映射。 + * + * @return array 通知类型名称表 + */ public static function notifyTypeMap(): array { return [ @@ -33,6 +38,11 @@ final class NotifyConstant ]; } + /** + * 获取回调类型映射。 + * + * @return array 回调类型名称表 + */ public static function callbackTypeMap(): array { return [ @@ -41,6 +51,11 @@ final class NotifyConstant ]; } + /** + * 验证状态Map + * + * @return array 验证状态名称表 + */ public static function verifyStatusMap(): array { return [ @@ -50,6 +65,11 @@ final class NotifyConstant ]; } + /** + * 处理状态Map + * + * @return array 处理状态名称表 + */ public static function processStatusMap(): array { return [ @@ -59,6 +79,11 @@ final class NotifyConstant ]; } + /** + * 获取任务状态映射。 + * + * @return array 任务状态名称表 + */ public static function taskStatusMap(): array { return [ @@ -68,3 +93,7 @@ final class NotifyConstant ]; } } + + + + diff --git a/app/common/constant/RouteConstant.php b/app/common/constant/RouteConstant.php index 404ce7d..d2065fb 100644 --- a/app/common/constant/RouteConstant.php +++ b/app/common/constant/RouteConstant.php @@ -3,14 +3,30 @@ namespace app\common\constant; /** - * 路由与通道编排相关枚举。 + * 路由与通道编排枚举。 + * + * 用于描述通道类型、通道模式以及轮询组的路由策略。 */ final class RouteConstant { + /** + * 平台代收通道类型。 + */ public const CHANNEL_TYPE_PLATFORM_COLLECT = 0; + + /** + * 商户自有通道类型。 + */ public const CHANNEL_TYPE_MERCHANT_SELF = 1; + /** + * 代收通道模式。 + */ public const CHANNEL_MODE_COLLECT = 0; + + /** + * 自收通道模式。 + */ public const CHANNEL_MODE_SELF = 1; /** @@ -28,6 +44,11 @@ final class RouteConstant */ public const ROUTE_MODE_FIRST_AVAILABLE = 2; + /** + * 获取通道类型名称映射。 + * + * @return array 通道类型名称表 + */ public static function channelTypeMap(): array { return [ @@ -36,6 +57,11 @@ final class RouteConstant ]; } + /** + * 获取通道模式名称映射。 + * + * @return array 通道模式名称表 + */ public static function channelModeMap(): array { return [ @@ -44,6 +70,11 @@ final class RouteConstant ]; } + /** + * 获取路由模式名称映射。 + * + * @return array 路由模式名称表 + */ public static function routeModeMap(): array { return [ @@ -53,3 +84,7 @@ final class RouteConstant ]; } } + + + + diff --git a/app/common/constant/TradeConstant.php b/app/common/constant/TradeConstant.php index a16fe0a..9a334c5 100644 --- a/app/common/constant/TradeConstant.php +++ b/app/common/constant/TradeConstant.php @@ -3,7 +3,7 @@ namespace app\common\constant; /** - * 交易、订单与结算相关枚举。 + * 交易、订单与结算状态枚举。 */ final class TradeConstant { @@ -36,6 +36,11 @@ final class TradeConstant public const REFUND_STATUS_FAILED = 3; public const REFUND_STATUS_CLOSED = 4; + /** + * 获取清算周期映射。 + * + * @return array 清算周期名称表 + */ public static function settlementCycleMap(): array { return [ @@ -47,6 +52,11 @@ final class TradeConstant ]; } + /** + * 获取订单状态映射。 + * + * @return array 订单状态名称表 + */ public static function orderStatusMap(): array { return [ @@ -59,6 +69,11 @@ final class TradeConstant ]; } + /** + * 获取手续费状态映射。 + * + * @return array 手续费状态名称表 + */ public static function feeStatusMap(): array { return [ @@ -69,6 +84,11 @@ final class TradeConstant ]; } + /** + * 获取清算状态映射。 + * + * @return array 清算状态名称表 + */ public static function settlementStatusMap(): array { return [ @@ -79,6 +99,11 @@ final class TradeConstant ]; } + /** + * 获取退款状态映射。 + * + * @return array 退款状态名称表 + */ public static function refundStatusMap(): array { return [ @@ -90,6 +115,11 @@ final class TradeConstant ]; } + /** + * 获取可变更的订单状态列表。 + * + * @return array 状态列表 + */ public static function orderMutableStatuses(): array { return [ @@ -98,6 +128,11 @@ final class TradeConstant ]; } + /** + * 获取订单终态列表。 + * + * @return array 状态列表 + */ public static function orderTerminalStatuses(): array { return [ @@ -108,11 +143,22 @@ final class TradeConstant ]; } + /** + * 判断订单是否为终态。 + * + * @param int $status 状态 + * @return bool 是否为终态 + */ public static function isOrderTerminalStatus(int $status): bool { return in_array($status, self::orderTerminalStatuses(), true); } + /** + * 获取可变更的退款状态列表。 + * + * @return array 状态列表 + */ public static function refundMutableStatuses(): array { return [ @@ -122,6 +168,11 @@ final class TradeConstant ]; } + /** + * 获取退款终态列表。 + * + * @return array 状态列表 + */ public static function refundTerminalStatuses(): array { return [ @@ -130,11 +181,22 @@ final class TradeConstant ]; } + /** + * 判断退款是否为终态。 + * + * @param int $status 状态 + * @return bool 是否为终态 + */ public static function isRefundTerminalStatus(int $status): bool { return in_array($status, self::refundTerminalStatuses(), true); } + /** + * 获取可变更的清算状态列表。 + * + * @return array 状态列表 + */ public static function settlementMutableStatuses(): array { return [ @@ -142,6 +204,11 @@ final class TradeConstant ]; } + /** + * 获取清算终态列表。 + * + * @return array 状态列表 + */ public static function settlementTerminalStatuses(): array { return [ @@ -150,8 +217,18 @@ final class TradeConstant ]; } + /** + * 判断清算是否为终态。 + * + * @param int $status 状态 + * @return bool 是否为终态 + */ public static function isSettlementTerminalStatus(int $status): bool { return in_array($status, self::settlementTerminalStatuses(), true); } } + + + + diff --git a/app/common/interface/PayPluginInterface.php b/app/common/interface/PayPluginInterface.php index cc3948c..e6149c0 100644 --- a/app/common/interface/PayPluginInterface.php +++ b/app/common/interface/PayPluginInterface.php @@ -4,58 +4,88 @@ declare(strict_types=1); namespace app\common\interface; /** - * 支付插件“基础契约”接口 + * 支付插件基础契约接口。 * * 职责边界: - * - `PayPluginInterface`:插件生命周期 + 元信息(后台可展示/可配置/可路由)。 - * - `PaymentInterface`:支付动作能力(下单/查询/关单/退款/回调)。 + * - `PayPluginInterface`:插件生命周期和元信息,用于后台展示、配置和路由。 + * - `PaymentInterface`:支付动作能力,用于下单、查询、关单、退款和回调。 * * 约定: - * - `init()` 会在每次发起支付/退款等动作前由服务层调用,用于注入该通道对应的插件配置。 - * - 元信息方法应为“纯读取”,不要依赖外部状态或数据库。 + * - `init()` 会在每次发起支付或退款前由服务层调用,用于注入该通道对应的插件配置。 + * - 元信息方法应为纯读取,不要依赖外部状态或数据库。 */ interface PayPluginInterface { /** - * 初始化插件(注入通道配置) + * 初始化插件,注入通道配置。 * * 典型来源:`ma_payment_plugin_conf.config`,并由服务层额外合并通道信息、支付方式声明等上下文。 * 插件应在这里完成:缓存配置、初始化 SDK/HTTP 客户端等。 * - * @param array $channelConfig + * @param array $channelConfig 渠道配置 + * @return void */ public function init(array $channelConfig): void; - /** 插件代码(与 ma_payment_plugin.code 对应) */ + /** + * 获取插件代码。 + * + * @return string 插件代码 + */ public function getCode(): string; - /** 插件名称(用于后台展示) */ + /** + * 获取插件名称。 + * + * @return string 插件名称 + */ public function getName(): string; - /** 插件作者名称(用于后台展示) */ + /** + * 获取作者名称。 + * + * @return string 作者名称 + */ public function getAuthorName(): string; - /** 插件作者链接(用于后台展示) */ + /** + * 获取作者链接。 + * + * @return string 作者链接 + */ public function getAuthorLink(): string; - /** 插件版本号(用于后台展示) */ + /** + * 获取版本号。 + * + * @return string 版本号 + */ public function getVersion(): string; /** - * 插件声明支持的支付方式编码 + * 获取插件声明支持的支付方式编码。 * - * @return array + * @return array 支持的支付方式编码 */ public function getEnabledPayTypes(): array; - /** 插件声明支持的转账方式编码 */ + /** + * 获取插件声明支持的转账方式编码。 + * + * @return array 支持的转账方式编码 + */ public function getEnabledTransferTypes(): array; /** - * 插件配置结构(用于后台渲染表单/校验) + * 获取插件配置结构。 * - * @return array + * @return array 配置结构 */ public function getConfigSchema(): array; } + + + + + diff --git a/app/common/interface/PaymentInterface.php b/app/common/interface/PaymentInterface.php index d871607..0b424c3 100644 --- a/app/common/interface/PaymentInterface.php +++ b/app/common/interface/PaymentInterface.php @@ -9,91 +9,74 @@ use support\Request; use support\Response; /** - * 支付插件接口 + * 支付动作能力接口。 * - * 所有支付插件必须实现此接口,用于统一下单、订单查询、关闭、退款及回调通知等核心能力。 - * 建议继承 BasePayment 获得 HTTP 请求等通用能力,再实现本接口。 + * 所有支付插件必须实现此接口,用于统一下单、订单查询、关闭、退款和回调通知。 + * 建议先继承 `BasePayment` 获取 HTTP 请求等通用能力,再实现本接口。 * - * 异常约定:实现类在业务失败时应抛出 PaymentException,便于统一处理和返回。 + * 异常约定:实现类在业务失败时应抛出 `PaymentException`,便于统一处理和返回。 */ interface PaymentInterface { // ==================== 订单操作 ==================== /** - * 统一下单 + * 发起支付下单。 * - * @param array $order 订单数据,通常包含: - * - order_id: 系统支付单号,建议直接使用 pay_no - * - amount: 金额(分) - * - subject: 商品标题 - * - body: 商品描述 - * - callback_url: 第三方异步回调地址(回调到本系统) - * - return_url: 支付完成跳转地址 - * @return array 支付参数,需包含 pay_params、chan_order_no、chan_trade_no - * @throws PaymentException 下单失败、渠道异常、参数错误等 + * @param array $order 订单参数 + * @return array 下单结果 */ public function pay(array $order): array; /** - * 查询订单状态 + * 查询订单状态。 * - * @param array $order 订单数据(至少含 order_id、chan_order_no) - * @return array 订单状态信息,通常包含: - * - status: 订单状态 - * - chan_trade_no: 渠道交易号 - * - pay_amount: 实付金额 - * @throws PaymentException 查询失败、渠道异常等 + * @param array $order 订单参数 + * @return array 查询结果 */ public function query(array $order): array; /** - * 关闭订单 + * 关闭订单。 * - * @param array $order 订单数据(至少含 order_id、chan_order_no) - * @return array 关闭结果,通常包含 success、msg - * @throws PaymentException 关闭失败、渠道异常等 + * @param array $order 订单参数 + * @return array 关闭结果 */ public function close(array $order): array; /** - * 申请退款 + * 申请退款。 * - * @param array $order 退款数据,通常包含: - * - order_id: 原支付单号 - * - chan_order_no: 渠道订单号 - * - refund_amount: 退款金额(分) - * - refund_no: 退款单号 - * @return array 退款结果,通常包含 success、chan_refund_no、msg - * @throws PaymentException 退款失败、渠道异常等 + * @param array $order 订单参数 + * @return array 退款结果 */ public function refund(array $order): array; // ==================== 异步通知 ==================== /** - * 解析并验证支付回调通知 + * 解析并验证支付回调通知。 * - * @param Request $request 支付渠道的异步通知请求(GET/POST 参数) - * @return array 解析结果,通常包含: - * - success: 是否支付成功 - * - status: 插件解析出的渠道状态文本 - * - pay_order_id: 系统支付单号 - * - chan_trade_no: 渠道交易号 - * - chan_order_no: 渠道订单号 - * - amount: 支付金额(分) - * - paid_at: 支付成功时间 - * @throws PaymentException 验签失败、数据异常等 + * @param Request $request 请求对象 + * @return array 回调结果 */ public function notify(Request $request): array; /** * 回调处理成功时返回给第三方的平台响应。 + * + * @return string|Response 响应内容 */ public function notifySuccess(): string|Response; /** * 回调处理失败时返回给第三方的平台响应。 + * + * @return string|Response 响应内容 */ public function notifyFail(): string|Response; } + + + + diff --git a/app/common/middleware/Cors.php b/app/common/middleware/Cors.php index c354229..22dfa46 100644 --- a/app/common/middleware/Cors.php +++ b/app/common/middleware/Cors.php @@ -17,7 +17,7 @@ class Cors implements MiddlewareInterface * 处理请求。 * * @param Request $request 请求对象 - * @param callable $handler 下一个中间件处理函数 + * @param callable $handler handler * @return Response 响应对象 */ public function process(Request $request, callable $handler): Response @@ -32,3 +32,7 @@ class Cors implements MiddlewareInterface ]); } } + + + + diff --git a/app/common/payment/AlipayPayment.php b/app/common/payment/AlipayPayment.php index c78a39c..9612d81 100644 --- a/app/common/payment/AlipayPayment.php +++ b/app/common/payment/AlipayPayment.php @@ -5,7 +5,9 @@ declare(strict_types=1); namespace app\common\payment; use app\common\base\BasePayment; +use app\common\constant\FileConstant; use app\common\interface\PaymentInterface; +use app\common\interface\PayPluginInterface; use app\common\util\FormatHelper; use app\exception\PaymentException; use Psr\Http\Message\ResponseInterface; @@ -15,14 +17,16 @@ use Yansongda\Pay\Pay; use Yansongda\Supports\Collection; /** - * 支付宝支付插件(基于 yansongda/pay ~3.7) + * 支付宝支付插件。 * - * 支持:web(电脑网站)、h5(手机网站)、app(APP 支付)、mini(小程序)、pos(刷卡)、scan(扫码)、transfer(转账) + * 基于 `yansongda/pay` 封装支付宝直连能力,支持网页、H5、APP、小程序、刷卡、扫码和转账。 * - * 通道配置:app_id, app_secret_cert, app_public_cert_path, alipay_public_cert_path, - * alipay_root_cert_path, mode(0正式/1沙箱) + * 通道配置:`app_id`、`app_secret_cert`、`app_public_cert_path`、`alipay_public_cert_path`、 + * `alipay_root_cert_path`、`mode`(0 正式 / 1 沙箱)。 + * + * 证书字段通过上传选择器保存 `object_key`,初始化时会自动解析成本地可读路径。 */ -class AlipayPayment extends BasePayment implements PaymentInterface +class AlipayPayment extends BasePayment implements PaymentInterface, PayPluginInterface { private const PRODUCT_WEB = 'alipay_web'; private const PRODUCT_H5 = 'alipay_h5'; @@ -56,6 +60,11 @@ class AlipayPayment extends BasePayment implements PaymentInterface 'transfer' => self::PRODUCT_TRANSFER, ]; + /** + * 插件元信息。 + * + * @var array + */ protected array $paymentInfo = [ 'code' => 'alipay', 'name' => '支付宝直连', @@ -67,9 +76,54 @@ class AlipayPayment extends BasePayment implements PaymentInterface 'config_schema' => [ ["type" => "input", "field" => "app_id", "title" => "应用ID", "value" => "", "props" => ["placeholder" => "请输入应用ID"], "validate" => [["required" => true, "message" => "应用ID不能为空"]]], ["type" => "textarea", "field" => "app_secret_cert", "title" => "应用私钥", "value" => "", "props" => ["placeholder" => "请输入应用私钥", "rows" => 4], "validate" => [["required" => true, "message" => "应用私钥不能为空"]]], - ["type" => "input", "field" => "app_public_cert_path", "title" => "应用公钥证书路径", "value" => "", "props" => ["placeholder" => "请输入应用公钥证书路径"], "validate" => [["required" => true, "message" => "应用公钥证书路径不能为空"]]], - ["type" => "input", "field" => "alipay_public_cert_path", "title" => "支付宝公钥证书路径", "value" => "", "props" => ["placeholder" => "请输入支付宝公钥证书路径"], "validate" => [["required" => true, "message" => "支付宝公钥证书路径不能为空"]]], - ["type" => "input", "field" => "alipay_root_cert_path", "title" => "支付宝根证书路径", "value" => "", "props" => ["placeholder" => "请输入支付宝根证书路径"], "validate" => [["required" => true, "message" => "支付宝根证书路径不能为空"]]], + [ + "type" => "upload", + "field" => "app_public_cert_path", + "title" => "应用公钥证书", + "value" => "", + "props" => [ + "fileUpload" => [ + "selectorType" => "file", + "scene" => FileConstant::SCENE_CERTIFICATE, + "isLocal" => true, + "isPublic" => false, + "getKey" => "object_key", + ], + ], + "validate" => [["required" => true, "message" => "应用公钥证书不能为空"]], + ], + [ + "type" => "upload", + "field" => "alipay_public_cert_path", + "title" => "支付宝公钥证书", + "value" => "", + "props" => [ + "fileUpload" => [ + "selectorType" => "file", + "scene" => FileConstant::SCENE_CERTIFICATE, + "isLocal" => true, + "isPublic" => false, + "getKey" => "object_key", + ], + ], + "validate" => [["required" => true, "message" => "支付宝公钥证书不能为空"]], + ], + [ + "type" => "upload", + "field" => "alipay_root_cert_path", + "title" => "支付宝根证书", + "value" => "", + "props" => [ + "fileUpload" => [ + "selectorType" => "file", + "scene" => FileConstant::SCENE_CERTIFICATE, + "isLocal" => true, + "isPublic" => false, + "getKey" => "object_key", + ], + ], + "validate" => [["required" => true, "message" => "支付宝根证书不能为空"]], + ], [ "type" => "checkbox", "field" => "enabled_products", @@ -90,6 +144,12 @@ class AlipayPayment extends BasePayment implements PaymentInterface ], ]; + /** + * 初始化支付宝插件。 + * + * @param array $channelConfig 渠道配置 + * @return void + */ public function init(array $channelConfig): void { parent::init($channelConfig); @@ -98,9 +158,9 @@ class AlipayPayment extends BasePayment implements PaymentInterface 'default' => [ 'app_id' => $this->getConfig('app_id', ''), 'app_secret_cert' => $this->getConfig('app_secret_cert', ''), - 'app_public_cert_path' => $this->getConfig('app_public_cert_path', ''), - 'alipay_public_cert_path' => $this->getConfig('alipay_public_cert_path', ''), - 'alipay_root_cert_path' => $this->getConfig('alipay_root_cert_path', ''), + 'app_public_cert_path' => runtime_path((string) $this->getConfig('app_public_cert_path', '')), + 'alipay_public_cert_path' => runtime_path((string) $this->getConfig('alipay_public_cert_path', '')), + 'alipay_root_cert_path' => runtime_path((string) $this->getConfig('alipay_root_cert_path', '')), 'notify_url' => $this->getConfig('notify_url', ''), 'return_url' => $this->getConfig('return_url', ''), 'mode' => (int)($this->getConfig('mode', Pay::MODE_NORMAL)), @@ -110,6 +170,14 @@ class AlipayPayment extends BasePayment implements PaymentInterface Pay::config(array_merge($config, ['_force' => true])); } + /** + * 根据订单上下文选择支付宝产品。 + * + * @param array $order 订单上下文 + * @param bool $validateEnabled 是否校验已开通产品 + * @return string 产品编码 + * @throws PaymentException + */ private function chooseProduct(array $order, bool $validateEnabled = true): string { $enabled = $this->normalizeEnabledProducts($this->channelConfig['enabled_products'] ?? self::DEFAULT_ENABLED_PRODUCTS); @@ -152,6 +220,12 @@ class AlipayPayment extends BasePayment implements PaymentInterface return in_array($prefer, $enabled, true) ? $prefer : ($enabled[0] ?? self::PRODUCT_WEB); } + /** + * 标准化已开通产品列表。 + * + * @param array|string|null $products 已开通产品配置 + * @return array 标准化后的产品编码列表 + */ private function normalizeEnabledProducts(mixed $products): array { if (is_string($products)) { @@ -176,6 +250,12 @@ class AlipayPayment extends BasePayment implements PaymentInterface return $normalized !== [] ? $normalized : self::DEFAULT_ENABLED_PRODUCTS; } + /** + * 解析显式指定的产品。 + * + * @param array $order 订单上下文 + * @return string|null 产品编码 + */ private function resolveExplicitProduct(array $order): ?string { $context = $this->collectOrderContext($order); @@ -197,6 +277,12 @@ class AlipayPayment extends BasePayment implements PaymentInterface return null; } + /** + * 归一化产品编码。 + * + * @param mixed $value 原始产品标识 + * @return string|null 标准化后的产品编码 + */ private function normalizeProductCode(mixed $value): ?string { $value = strtolower(trim((string) $value)); @@ -215,11 +301,23 @@ class AlipayPayment extends BasePayment implements PaymentInterface return null; } + /** + * 获取产品对应的动作名。 + * + * @param string $product 产品编码 + * @return string 动作名 + */ private function productAction(string $product): string { return self::PRODUCT_ACTION_MAP[$product] ?? $product; } + /** + * 合并订单上下文。 + * + * @param array $order 订单上下文 + * @return array 合并后的上下文 + */ private function collectOrderContext(array $order): array { $context = $order; @@ -236,6 +334,12 @@ class AlipayPayment extends BasePayment implements PaymentInterface return $context; } + /** + * 标准化参数包。 + * + * @param array|string|null $param 原始参数包 + * @return array 参数数组 + */ private function normalizeParamBag(mixed $param): array { if (is_array($param)) { @@ -257,6 +361,12 @@ class AlipayPayment extends BasePayment implements PaymentInterface return []; } + /** + * 构建基础下单参数。 + * + * @param array $params 原始参数 + * @return array 基础下单参数 + */ private function buildBasePayParams(array $params): array { $base = [ @@ -283,6 +393,14 @@ class AlipayPayment extends BasePayment implements PaymentInterface return $base; } + /** + * 从集合中提取首个非空值。 + * + * @param Collection $result 结果集合 + * @param array $keys 候选键 + * @param mixed $default 默认值 + * @return mixed 提取到的首个非空值 + */ private function extractCollectionValue(Collection $result, array $keys, mixed $default = ''): mixed { foreach ($keys as $key) { @@ -295,6 +413,13 @@ class AlipayPayment extends BasePayment implements PaymentInterface return $default; } + /** + * 发起支付宝下单。 + * + * @param array $order 订单上下文 + * @return array 下单结果 + * @throws PaymentException + */ public function pay(array $order): array { $orderId = (string) ($order['order_id'] ?? $order['pay_no'] ?? ''); @@ -338,6 +463,12 @@ class AlipayPayment extends BasePayment implements PaymentInterface } } + /** + * 发起网页支付。 + * + * @param array $params 基础参数 + * @return array 下单结果 + */ private function doWeb(array $params): array { $response = Pay::alipay()->web($params); @@ -358,6 +489,12 @@ class AlipayPayment extends BasePayment implements PaymentInterface ]; } + /** + * 发起 H5 支付。 + * + * @param array $params 基础参数 + * @return array 下单结果 + */ private function doH5(array $params): array { $returnUrl = $params['_return_url'] ?? $this->getConfig('return_url', ''); @@ -382,6 +519,12 @@ class AlipayPayment extends BasePayment implements PaymentInterface ]; } + /** + * 发起扫码支付。 + * + * @param array $params 基础参数 + * @return array 下单结果 + */ private function doScan(array $params): array { /** @var Collection $result */ @@ -402,6 +545,12 @@ class AlipayPayment extends BasePayment implements PaymentInterface ]; } + /** + * 发起 APP 支付。 + * + * @param array $params 基础参数 + * @return array 下单结果 + */ private function doApp(array $params): array { /** @var Collection $result */ @@ -422,6 +571,14 @@ class AlipayPayment extends BasePayment implements PaymentInterface ]; } + /** + * 发起小程序支付。 + * + * @param array $params 基础参数 + * @param array $order 订单上下文 + * @return array 下单结果 + * @throws PaymentException + */ private function doMini(array $params, array $order): array { $context = $this->collectOrderContext($order); @@ -454,6 +611,14 @@ class AlipayPayment extends BasePayment implements PaymentInterface ]; } + /** + * 发起刷卡支付。 + * + * @param array $params 基础参数 + * @param array $order 订单上下文 + * @return array 下单结果 + * @throws PaymentException + */ private function doPos(array $params, array $order): array { $context = $this->collectOrderContext($order); @@ -486,6 +651,14 @@ class AlipayPayment extends BasePayment implements PaymentInterface ]; } + /** + * 发起转账。 + * + * @param array $params 基础参数 + * @param array $order 订单上下文 + * @return array 下单结果 + * @throws PaymentException + */ private function doTransfer(array $params, array $order): array { $context = $this->collectOrderContext($order); @@ -537,6 +710,13 @@ class AlipayPayment extends BasePayment implements PaymentInterface ]; } + /** + * 查询支付宝订单状态。 + * + * @param array $order 订单上下文 + * @return array 查询结果 + * @throws PaymentException + */ public function query(array $order): array { $product = $this->chooseProduct($order, false); @@ -569,6 +749,13 @@ class AlipayPayment extends BasePayment implements PaymentInterface } } + /** + * 关闭支付宝订单。 + * + * @param array $order 订单上下文 + * @return array 关闭结果 + * @throws PaymentException + */ public function close(array $order): array { $product = $this->chooseProduct($order, false); @@ -591,6 +778,13 @@ class AlipayPayment extends BasePayment implements PaymentInterface } } + /** + * 发起支付宝退款。 + * + * @param array $order 订单上下文 + * @return array 退款结果 + * @throws PaymentException + */ public function refund(array $order): array { $product = $this->chooseProduct($order, false); @@ -637,6 +831,13 @@ class AlipayPayment extends BasePayment implements PaymentInterface } } + /** + * 解析支付宝回调通知。 + * + * @param Request $request 请求对象 + * @return array 回调结果 + * @throws PaymentException + */ public function notify(Request $request): array { $params = array_merge($request->get(), $request->post()); @@ -670,13 +871,26 @@ class AlipayPayment extends BasePayment implements PaymentInterface } } + /** + * 返回回调成功响应。 + * + * @return string|Response 响应内容 + */ public function notifySuccess(): string|Response { return 'success'; } + /** + * 返回回调失败响应。 + * + * @return string|Response 响应内容 + */ public function notifyFail(): string|Response { return 'fail'; } } + + + diff --git a/app/common/util/FormatHelper.php b/app/common/util/FormatHelper.php index fafd3fa..ded865e 100644 --- a/app/common/util/FormatHelper.php +++ b/app/common/util/FormatHelper.php @@ -15,6 +15,9 @@ class FormatHelper { /** * 金额格式化,单位为元。 + * + * @param int $amount 金额(分) + * @return string 格式化后的金额字符串 */ public static function amount(int $amount): string { @@ -23,6 +26,9 @@ class FormatHelper /** * 金额格式化,0 时显示不限。 + * + * @param int $amount 金额(分) + * @return string 格式化后的金额字符串 */ public static function amountOrUnlimited(int $amount): string { @@ -31,6 +37,9 @@ class FormatHelper /** * 次数格式化,0 时显示不限。 + * + * @param int $count 次数 + * @return string 格式化后的次数字符串 */ public static function countOrUnlimited(int $count): string { @@ -39,6 +48,9 @@ class FormatHelper /** * 费率格式化,单位为百分点。 + * + * @param int $basisPoints 基点值 + * @return string 格式化后的费率字符串 */ public static function rate(int $basisPoints): string { @@ -47,6 +59,9 @@ class FormatHelper /** * 延迟格式化。 + * + * @param int $latencyMs 延迟毫秒数 + * @return string 格式化后的延迟字符串 */ public static function latency(int $latencyMs): string { @@ -55,6 +70,10 @@ class FormatHelper /** * 日期格式化。 + * + * @param mixed $value 日期值 + * @param string $emptyText 为空时显示文案 + * @return string 格式化后的日期字符串 */ public static function date(mixed $value, string $emptyText = ''): string { @@ -63,6 +82,10 @@ class FormatHelper /** * 日期时间格式化。 + * + * @param mixed $value 日期时间值 + * @param string $emptyText 为空时显示文案 + * @return string 格式化后的日期时间字符串 */ public static function dateTime(mixed $value, string $emptyText = ''): string { @@ -71,6 +94,11 @@ class FormatHelper /** * 按时间戳格式化。 + * + * @param int $timestamp Unix 时间戳 + * @param string $pattern 输出格式 + * @param string $emptyText 为空时显示文案 + * @return string 格式化后的时间字符串 */ public static function timestamp(int $timestamp, string $pattern = 'Y-m-d H:i:s', string $emptyText = ''): string { @@ -83,6 +111,10 @@ class FormatHelper /** * JSON 文本格式化。 + * + * @param mixed $value JSON 值 + * @param string $emptyText 为空时显示文案 + * @return string 格式化后的 JSON 文本 */ public static function json(mixed $value, string $emptyText = ''): string { @@ -106,6 +138,11 @@ class FormatHelper /** * 映射表文本转换。 + * + * @param int $value 待映射值 + * @param array $map 映射表 + * @param string $default 默认值 + * @return string 映射后的文本 */ public static function textFromMap(int $value, array $map, string $default = '未知'): string { @@ -114,6 +151,10 @@ class FormatHelper /** * 接口凭证明文脱敏。 + * + * @param string $credentialValue 凭证原文 + * @param bool $maskShortValue 是否对短值也进行脱敏 + * @return string 脱敏后的文本 */ public static function maskCredentialValue(string $credentialValue, bool $maskShortValue = true): string { @@ -132,6 +173,9 @@ class FormatHelper /** * 将模型或对象归一化成数组。 + * + * @param mixed $value 模型、对象或数组 + * @return array|null 归一化后的数组 */ public static function normalizeModel(mixed $value): ?array { @@ -163,6 +207,11 @@ class FormatHelper /** * 统一格式化时间值。 + * + * @param mixed $value 时间值 + * @param string $pattern 输出格式 + * @param string $emptyText 为空时显示文案 + * @return string 格式化后的时间文本 */ private static function formatTemporalValue(mixed $value, string $pattern, string $emptyText): string { @@ -186,3 +235,4 @@ class FormatHelper return (string) $value; } } + diff --git a/app/common/util/JwtTokenManager.php b/app/common/util/JwtTokenManager.php index d38fe1e..c15dfe0 100644 --- a/app/common/util/JwtTokenManager.php +++ b/app/common/util/JwtTokenManager.php @@ -24,11 +24,11 @@ class JwtTokenManager /** * 签发 JWT,并把会话态写入 Redis。 * - * @param string $guard 认证域名称,例如 admin 或 merchant - * @param array $claims JWT 自定义声明,通常包含主体 ID 等核心身份信息 - * @param array $sessionData Redis 会话数据,通常包含用户展示信息和登录上下文 - * @param int|null $ttlSeconds 有效期,单位秒;为空时使用 guard 默认 TTL - * @return array{token:string,expires_in:int,jti:string,claims:array,session:array} + * @param string $guard 登录域 + * @param array $claims JWT 声明 + * @param array $sessionData 会话数据 + * @param int|null $ttlSeconds 过期秒数 + * @return array{token:string,expires_in:int,jti:string,claims:array,session:array} 签发结果 */ public function issue(string $guard, array $claims, array $sessionData, ?int $ttlSeconds = null): array { @@ -75,7 +75,11 @@ class JwtTokenManager * - 再通过 jti 反查 Redis 会话,确保 token 仍然有效。 * - 每次命中会刷新最近访问时间。 * - * @return array{claims:array,session:array}|null + * @param string $guard 登录域 + * @param string $token JWT 字符串 + * @param string $ip 最近访问 IP + * @param string $userAgent 用户Agent + * @return array{claims:array,session:array}|null 验证结果 */ public function verify(string $guard, string $token, string $ip = '', string $userAgent = ''): ?array { @@ -118,6 +122,10 @@ class JwtTokenManager * 通过 token 撤销登录态。 * * 适用于主动退出登录场景。 + * + * @param string $guard 登录域 + * @param string $token JWT 字符串 + * @return bool 是否已撤销 */ public function revoke(string $guard, string $token): bool { @@ -138,6 +146,10 @@ class JwtTokenManager * 通过 jti 直接撤销登录态。 * * 适用于已经掌握会话标识但没有原始 token 的补偿清理场景。 + * + * @param string $guard 登录域 + * @param string $jti 会话标识 + * @return bool 是否已撤销 */ public function revokeByJti(string $guard, string $jti): bool { @@ -152,6 +164,10 @@ class JwtTokenManager * 根据 jti 获取会话数据。 * * 返回值来自 Redis,若已过期或不存在则返回 null。 + * + * @param string $guard 登录域 + * @param string $jti 会话标识 + * @return array|null 会话数据 */ public function session(string $guard, string $jti): ?array { @@ -168,6 +184,10 @@ class JwtTokenManager * 解码并校验 JWT。 * * 只做签名、过期和 guard 校验,不处理 Redis 会话。 + * + * @param string $guard 登录域 + * @param string $token JWT 字符串 + * @return array|null JWT 载荷 */ protected function decode(string $guard, string $token): ?array { @@ -195,6 +215,12 @@ class JwtTokenManager /** * 将会话数据写入 Redis,并设置 TTL。 + * + * @param string $guard 登录域 + * @param string $jti 会话标识 + * @param array $session 会话数据 + * @param int $ttlSeconds 过期秒数 + * @return void */ protected function storeSession(string $guard, string $jti, array $session, int $ttlSeconds): void { @@ -209,6 +235,10 @@ class JwtTokenManager * 构造 Redis 会话键。 * * 最终格式由 guard 对应的 redis_prefix 加上 jti 组成。 + * + * @param string $guard 登录域 + * @param string $jti 会话标识 + * @return string Redis 会话键 */ protected function sessionKey(string $guard, string $jti): string { @@ -218,7 +248,9 @@ class JwtTokenManager /** * 获取指定认证域的配置。 * - * @throws \InvalidArgumentException 当 guard 未配置时抛出 + * @param string $guard 登录域 + * @return array 认证配置 + * @throws \InvalidArgumentException */ protected function guardConfig(string $guard): array { @@ -232,6 +264,11 @@ class JwtTokenManager /** * 校验 HS256 密钥长度,避免 firebase/php-jwt 抛出底层异常。 + * + * @param string $guard 登录域 + * @param string $secret 密钥 + * @return void + * @throws RuntimeException */ protected function assertHmacSecretLength(string $guard, string $secret): void { @@ -252,3 +289,5 @@ class JwtTokenManager )); } } + + diff --git a/app/exception/BalanceInsufficientException.php b/app/exception/BalanceInsufficientException.php index bd69f7e..fe63977 100644 --- a/app/exception/BalanceInsufficientException.php +++ b/app/exception/BalanceInsufficientException.php @@ -10,7 +10,13 @@ use Webman\Exception\BusinessException; class BalanceInsufficientException extends BusinessException { /** - * 构造函数,组装异常信息。 + * 构造方法。 + * + * @param int $merchantId 商户ID + * @param int $needAmount needAmount + * @param int $availableAmount 可用Amount + * @param array $data 数据 + * @return void */ public function __construct(int $merchantId = 0, int $needAmount = 0, int $availableAmount = 0, array $data = []) { @@ -31,3 +37,8 @@ class BalanceInsufficientException extends BusinessException } } } + + + + + diff --git a/app/exception/BusinessStateException.php b/app/exception/BusinessStateException.php index da7b0a7..c52fca1 100644 --- a/app/exception/BusinessStateException.php +++ b/app/exception/BusinessStateException.php @@ -12,7 +12,12 @@ use Webman\Exception\BusinessException; class BusinessStateException extends BusinessException { /** - * 构造函数,组装异常信息。 + * 构造方法。 + * + * @param string $message message + * @param array $data 数据 + * @param int $bizCode 业务Code + * @return void */ public function __construct(string $message = '业务状态不允许当前操作', array $data = [], int $bizCode = 40910) { @@ -23,3 +28,8 @@ class BusinessStateException extends BusinessException } } } + + + + + diff --git a/app/exception/ConflictException.php b/app/exception/ConflictException.php index 7a74372..fcf37f7 100644 --- a/app/exception/ConflictException.php +++ b/app/exception/ConflictException.php @@ -12,7 +12,12 @@ use Webman\Exception\BusinessException; class ConflictException extends BusinessException { /** - * 构造函数,组装异常信息。 + * 构造方法。 + * + * @param string $message message + * @param array $data 数据 + * @param int $bizCode 业务Code + * @return void */ public function __construct(string $message = '业务冲突', array $data = [], int $bizCode = 40900) { @@ -23,3 +28,8 @@ class ConflictException extends BusinessException } } } + + + + + diff --git a/app/exception/NotifyRetryExceededException.php b/app/exception/NotifyRetryExceededException.php index c480ad8..c50f68e 100644 --- a/app/exception/NotifyRetryExceededException.php +++ b/app/exception/NotifyRetryExceededException.php @@ -10,7 +10,11 @@ use Webman\Exception\BusinessException; class NotifyRetryExceededException extends BusinessException { /** - * 构造函数,组装异常信息。 + * 构造方法。 + * + * @param string $notifyNo 通知号 + * @param array $data 数据 + * @return void */ public function __construct(string $notifyNo = '', array $data = []) { @@ -29,3 +33,8 @@ class NotifyRetryExceededException extends BusinessException } } } + + + + + diff --git a/app/exception/PaymentException.php b/app/exception/PaymentException.php index 724d41b..48d3f68 100644 --- a/app/exception/PaymentException.php +++ b/app/exception/PaymentException.php @@ -12,7 +12,12 @@ use Webman\Exception\BusinessException; class PaymentException extends BusinessException { /** - * 构造函数,统一组装业务码与附加数据。 + * 构造方法。 + * + * @param string $message message + * @param int $bizCode 业务Code + * @param array $data 数据 + * @return void */ public function __construct(string $message = '支付渠道处理失败', int $bizCode = 40200, array $data = []) { @@ -23,3 +28,8 @@ class PaymentException extends BusinessException } } } + + + + + diff --git a/app/exception/ResourceNotFoundException.php b/app/exception/ResourceNotFoundException.php index ec226fb..803cc59 100644 --- a/app/exception/ResourceNotFoundException.php +++ b/app/exception/ResourceNotFoundException.php @@ -12,7 +12,12 @@ use Webman\Exception\BusinessException; class ResourceNotFoundException extends BusinessException { /** - * 构造函数,组装异常信息。 + * 构造方法。 + * + * @param string $message message + * @param array $data 数据 + * @param int $bizCode 业务Code + * @return void */ public function __construct(string $message = '资源不存在', array $data = [], int $bizCode = 40400) { @@ -23,3 +28,8 @@ class ResourceNotFoundException extends BusinessException } } } + + + + + diff --git a/app/exception/ValidationException.php b/app/exception/ValidationException.php index 53de349..f305a43 100644 --- a/app/exception/ValidationException.php +++ b/app/exception/ValidationException.php @@ -10,7 +10,12 @@ use Webman\Exception\BusinessException; class ValidationException extends BusinessException { /** - * 构造函数,组装异常信息。 + * 构造方法。 + * + * @param string $message message + * @param int|array $bizCodeOrData 业务CodeOr数据 + * @param array $data 数据 + * @return void */ public function __construct(string $message = '参数校验失败', int|array $bizCodeOrData = 40001, array $data = []) { @@ -28,3 +33,8 @@ class ValidationException extends BusinessException } } } + + + + + diff --git a/app/http/admin/controller/account/MerchantAccountController.php b/app/http/admin/controller/account/MerchantAccountController.php index 902a3f3..f582fed 100644 --- a/app/http/admin/controller/account/MerchantAccountController.php +++ b/app/http/admin/controller/account/MerchantAccountController.php @@ -10,11 +10,16 @@ use support\Response; /** * 商户账户控制器。 + * + * @property MerchantAccountService $merchantAccountService 商户账户服务 */ class MerchantAccountController extends BaseController { /** - * 构造函数,注入商户账户服务。 + * 构造方法。 + * + * @param MerchantAccountService $merchantAccountService 商户账户服务 + * @return void */ public function __construct( protected MerchantAccountService $merchantAccountService @@ -23,6 +28,9 @@ class MerchantAccountController extends BaseController /** * 查询商户账户列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -39,6 +47,9 @@ class MerchantAccountController extends BaseController /** * 资金中心概览。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function summary(Request $request): Response { @@ -47,6 +58,10 @@ class MerchantAccountController extends BaseController /** * 查询商户账户详情。 + * + * @param Request $request 请求对象 + * @param string $id 商户账户ID + * @return Response 响应对象 */ public function show(Request $request, string $id): Response { @@ -61,3 +76,8 @@ class MerchantAccountController extends BaseController } } + + + + + diff --git a/app/http/admin/controller/account/MerchantAccountLedgerController.php b/app/http/admin/controller/account/MerchantAccountLedgerController.php index edb6032..d32ad8f 100644 --- a/app/http/admin/controller/account/MerchantAccountLedgerController.php +++ b/app/http/admin/controller/account/MerchantAccountLedgerController.php @@ -10,11 +10,16 @@ use support\Response; /** * 商户账户流水控制器。 + * + * @property MerchantAccountLedgerService $merchantAccountLedgerService 商户账户流水服务 */ class MerchantAccountLedgerController extends BaseController { /** - * 构造函数,注入账户流水服务。 + * 构造方法。 + * + * @param MerchantAccountLedgerService $merchantAccountLedgerService 商户账户流水服务 + * @return void */ public function __construct( protected MerchantAccountLedgerService $merchantAccountLedgerService @@ -23,6 +28,9 @@ class MerchantAccountLedgerController extends BaseController /** * 查询账户流水列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -39,6 +47,10 @@ class MerchantAccountLedgerController extends BaseController /** * 查询账户流水详情。 + * + * @param Request $request 请求对象 + * @param string $id 商户账户流水ID + * @return Response 响应对象 */ public function show(Request $request, string $id): Response { @@ -52,3 +64,8 @@ class MerchantAccountLedgerController extends BaseController return $this->success($ledger); } } + + + + + diff --git a/app/http/admin/controller/file/FileRecordController.php b/app/http/admin/controller/file/FileRecordController.php index fcb653b..5093ce9 100644 --- a/app/http/admin/controller/file/FileRecordController.php +++ b/app/http/admin/controller/file/FileRecordController.php @@ -3,6 +3,7 @@ namespace app\http\admin\controller\file; use app\common\base\BaseController; +use app\exception\ValidationException; use app\http\admin\validation\FileRecordValidator; use app\service\file\FileRecordService; use Webman\Http\UploadFile; @@ -11,14 +12,28 @@ use support\Response; /** * 文件控制器。 + * + * @property FileRecordService $fileRecordService 文件记录服务 */ class FileRecordController extends BaseController { + /** + * 构造方法。 + * + * @param FileRecordService $fileRecordService 文件记录服务 + * @return void + */ public function __construct( protected FileRecordService $fileRecordService ) { } + /** + * 查询文件记录列表 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function index(Request $request): Response { $data = $this->validated($request->all(), FileRecordValidator::class, 'index'); @@ -32,11 +47,24 @@ class FileRecordController extends BaseController ); } + /** + * 获取文件记录选项 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function options(Request $request): Response { return $this->success($this->fileRecordService->options()); } + /** + * 查询文件记录详情 + * + * @param Request $request 请求对象 + * @param string $id 文件记录ID + * @return Response 响应对象 + */ public function show(Request $request, string $id): Response { $data = $this->validated(['id' => (int) $id], FileRecordValidator::class, 'show'); @@ -44,12 +72,19 @@ class FileRecordController extends BaseController return $this->success($this->fileRecordService->detail((int) $data['id'])); } + /** + * 上传文件记录 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + * @throws ValidationException + */ public function upload(Request $request): Response { $data = $this->validated(array_merge($this->payload($request), ['scene' => $request->input('scene')]), FileRecordValidator::class, 'store'); $uploadedFile = $request->file('file'); if ($uploadedFile === null) { - return $this->fail('请先选择上传文件', 400); + throw new ValidationException('请先选择上传文件'); } $createdBy = $this->currentAdminId($request); @@ -63,6 +98,10 @@ class FileRecordController extends BaseController } } + if ($items === []) { + throw new ValidationException('上传文件无效'); + } + return $this->success([ 'list' => $items, 'total' => count($items), @@ -70,12 +109,18 @@ class FileRecordController extends BaseController } if (!$uploadedFile instanceof UploadFile) { - return $this->fail('上传文件无效', 400); + throw new ValidationException('上传文件无效'); } return $this->success($this->fileRecordService->upload($uploadedFile, $data, $createdBy, $createdByName)); } + /** + * 导入远程文件记录 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function importRemote(Request $request): Response { $data = $this->validated($this->payload($request), FileRecordValidator::class, 'importRemote'); @@ -92,6 +137,13 @@ class FileRecordController extends BaseController ); } + /** + * 获取文件预览响应。 + * + * @param Request $request 请求对象 + * @param string $id 文件记录ID + * @return Response 响应对象 + */ public function preview(Request $request, string $id): Response { $data = $this->validated(['id' => (int) $id], FileRecordValidator::class, 'preview'); @@ -99,6 +151,13 @@ class FileRecordController extends BaseController return $this->fileRecordService->previewResponse((int) $data['id']); } + /** + * 获取文件下载响应。 + * + * @param Request $request 请求对象 + * @param string $id 文件记录ID + * @return Response 响应对象 + */ public function download(Request $request, string $id): Response { $data = $this->validated(['id' => (int) $id], FileRecordValidator::class, 'download'); @@ -106,13 +165,23 @@ class FileRecordController extends BaseController return $this->fileRecordService->downloadResponse((int) $data['id']); } + /** + * 删除文件记录 + * + * @param Request $request 请求对象 + * @param string $id 文件记录ID + * @return Response 响应对象 + */ public function destroy(Request $request, string $id): Response { $data = $this->validated(['id' => (int) $id], FileRecordValidator::class, 'destroy'); - if (!$this->fileRecordService->delete((int) $data['id'])) { - return $this->fail('文件不存在', 404); - } + $this->fileRecordService->delete((int) $data['id']); return $this->success(true); } } + + + + + diff --git a/app/http/admin/controller/merchant/MerchantApiCredentialController.php b/app/http/admin/controller/merchant/MerchantApiCredentialController.php index 6e4e44c..94fac1f 100644 --- a/app/http/admin/controller/merchant/MerchantApiCredentialController.php +++ b/app/http/admin/controller/merchant/MerchantApiCredentialController.php @@ -9,12 +9,17 @@ use support\Request; use support\Response; /** - * 商户接口凭证管理控制器。 + * 商户 API 凭证管理控制器。 + * + * @property MerchantApiCredentialService $merchantApiCredentialService 商户 API 凭证服务 */ class MerchantApiCredentialController extends BaseController { /** - * 构造函数,注入商户 API 凭证服务。 + * 构造方法。 + * + * @param MerchantApiCredentialService $merchantApiCredentialService 商户 API 凭证服务 + * @return void */ public function __construct( protected MerchantApiCredentialService $merchantApiCredentialService @@ -23,6 +28,9 @@ class MerchantApiCredentialController extends BaseController /** * 查询商户 API 凭证列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -39,6 +47,10 @@ class MerchantApiCredentialController extends BaseController /** * 查询商户 API 凭证详情。 + * + * @param Request $request 请求对象 + * @param string $id 商户 API 凭证ID + * @return Response 响应对象 */ public function show(Request $request, string $id): Response { @@ -46,7 +58,7 @@ class MerchantApiCredentialController extends BaseController $credential = $this->merchantApiCredentialService->findById((int) $data['id']); if (!$credential) { - return $this->fail('商户接口凭证不存在', 404); + return $this->fail('商户 API 凭证不存在', 404); } return $this->success($credential); @@ -54,6 +66,9 @@ class MerchantApiCredentialController extends BaseController /** * 新增商户 API 凭证。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function store(Request $request): Response { @@ -64,6 +79,10 @@ class MerchantApiCredentialController extends BaseController /** * 修改商户 API 凭证。 + * + * @param Request $request 请求对象 + * @param string $id 商户 API 凭证ID + * @return Response 响应对象 */ public function update(Request $request, string $id): Response { @@ -75,7 +94,7 @@ class MerchantApiCredentialController extends BaseController $credential = $this->merchantApiCredentialService->update((int) $data['id'], $data); if (!$credential) { - return $this->fail('商户接口凭证不存在', 404); + return $this->fail('商户 API 凭证不存在', 404); } return $this->success($credential); @@ -83,6 +102,10 @@ class MerchantApiCredentialController extends BaseController /** * 删除商户 API 凭证。 + * + * @param Request $request 请求对象 + * @param string $id 商户 API 凭证ID + * @return Response 响应对象 */ public function destroy(Request $request, string $id): Response { @@ -90,14 +113,19 @@ class MerchantApiCredentialController extends BaseController $credential = $this->merchantApiCredentialService->findById((int) $data['id']); if (!$credential) { - return $this->fail('商户接口凭证不存在', 404); + return $this->fail('商户 API 凭证不存在', 404); } if (!$this->merchantApiCredentialService->delete((int) $data['id'])) { - return $this->fail('商户接口凭证删除失败'); + return $this->fail('商户 API 凭证删除失败'); } return $this->success(true); } } + + + + + diff --git a/app/http/admin/controller/merchant/MerchantController.php b/app/http/admin/controller/merchant/MerchantController.php index f3d105a..93c6a9c 100644 --- a/app/http/admin/controller/merchant/MerchantController.php +++ b/app/http/admin/controller/merchant/MerchantController.php @@ -12,11 +12,16 @@ use support\Response; * 商户管理控制器。 * * 当前先提供商户列表查询,后续可继续扩展商户详情、新增、编辑等能力。 + * + * @property MerchantService $merchantService 商户服务 */ class MerchantController extends BaseController { /** - * 构造函数,注入商户服务。 + * 构造方法。 + * + * @param MerchantService $merchantService 商户服务 + * @return void */ public function __construct( protected MerchantService $merchantService @@ -27,6 +32,9 @@ class MerchantController extends BaseController * 查询商户列表。 * * 返回值里额外携带启用中的商户分组选项,方便前端一次性渲染筛选条件。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -39,6 +47,10 @@ class MerchantController extends BaseController /** * 查询商户详情。 + * + * @param Request $request 请求对象 + * @param string $id 商户ID + * @return Response 响应对象 */ public function show(Request $request, string $id): Response { @@ -54,6 +66,9 @@ class MerchantController extends BaseController /** * 新增商户。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function store(Request $request): Response { @@ -63,6 +78,10 @@ class MerchantController extends BaseController /** * 更新商户。 + * + * @param Request $request 请求对象 + * @param string $id 商户ID + * @return Response 响应对象 */ public function update(Request $request, string $id): Response { @@ -80,6 +99,10 @@ class MerchantController extends BaseController /** * 删除商户。 + * + * @param Request $request 请求对象 + * @param string $id 商户ID + * @return Response 响应对象 */ public function destroy(Request $request, string $id): Response { @@ -90,6 +113,10 @@ class MerchantController extends BaseController /** * 重置商户登录密码。 + * + * @param Request $request 请求对象 + * @param string $id 商户ID + * @return Response 响应对象 */ public function resetPassword(Request $request, string $id): Response { @@ -100,7 +127,11 @@ class MerchantController extends BaseController } /** - * 生成或重置商户接口凭证。 + * 生成或重置商户 API 凭证。 + * + * @param Request $request 请求对象 + * @param string $id 商户ID + * @return Response 响应对象 */ public function issueCredential(Request $request, string $id): Response { @@ -111,6 +142,10 @@ class MerchantController extends BaseController /** * 查询商户总览。 + * + * @param Request $request 请求对象 + * @param string $id 商户ID + * @return Response 响应对象 */ public function overview(Request $request, string $id): Response { @@ -121,6 +156,9 @@ class MerchantController extends BaseController /** * 查询商户下拉选项。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function options(Request $request): Response { @@ -129,6 +167,9 @@ class MerchantController extends BaseController /** * 远程查询商户选择项。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function selectOptions(Request $request): Response { @@ -139,3 +180,9 @@ class MerchantController extends BaseController } } + + + + + + diff --git a/app/http/admin/controller/merchant/MerchantGroupController.php b/app/http/admin/controller/merchant/MerchantGroupController.php index 157505d..3b5fe4f 100644 --- a/app/http/admin/controller/merchant/MerchantGroupController.php +++ b/app/http/admin/controller/merchant/MerchantGroupController.php @@ -12,11 +12,16 @@ use support\Response; * 商户分组管理控制器。 * * 负责商户分组的列表、详情、新增、修改和删除。 + * + * @property MerchantGroupService $merchantGroupService 商户分组服务 */ class MerchantGroupController extends BaseController { /** - * 构造函数,注入商户分组服务。 + * 构造方法。 + * + * @param MerchantGroupService $merchantGroupService 商户分组服务 + * @return void */ public function __construct( protected MerchantGroupService $merchantGroupService @@ -24,9 +29,10 @@ class MerchantGroupController extends BaseController } /** - * GET /admin/merchant-groups - * * 查询商户分组列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -42,9 +48,11 @@ class MerchantGroupController extends BaseController } /** - * GET /admin/merchant-groups/{id} - * * 查询商户分组详情。 + * + * @param Request $request 请求对象 + * @param string $id 商户分组ID + * @return Response 响应对象 */ public function show(Request $request, string $id): Response { @@ -59,9 +67,10 @@ class MerchantGroupController extends BaseController } /** - * POST /admin/merchant-groups - * * 新增商户分组。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function store(Request $request): Response { @@ -71,9 +80,11 @@ class MerchantGroupController extends BaseController } /** - * PUT /admin/merchant-groups/{id} - * * 修改商户分组。 + * + * @param Request $request 请求对象 + * @param string $id 商户分组ID + * @return Response 响应对象 */ public function update(Request $request, string $id): Response { @@ -92,9 +103,11 @@ class MerchantGroupController extends BaseController } /** - * DELETE /admin/merchant-groups/{id} - * * 删除商户分组。 + * + * @param Request $request 请求对象 + * @param string $id 商户分组ID + * @return Response 响应对象 */ public function destroy(Request $request, string $id): Response { @@ -109,6 +122,9 @@ class MerchantGroupController extends BaseController /** * 查询商户分组下拉选项。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function options(Request $request): Response { @@ -116,3 +132,8 @@ class MerchantGroupController extends BaseController } } + + + + + diff --git a/app/http/admin/controller/merchant/MerchantPolicyController.php b/app/http/admin/controller/merchant/MerchantPolicyController.php index 8e4323c..2d5ff6e 100644 --- a/app/http/admin/controller/merchant/MerchantPolicyController.php +++ b/app/http/admin/controller/merchant/MerchantPolicyController.php @@ -10,14 +10,28 @@ use support\Response; /** * 商户策略控制器。 + * + * @property MerchantPolicyService $merchantPolicyService 商户策略服务 */ class MerchantPolicyController extends BaseController { + /** + * 构造方法。 + * + * @param MerchantPolicyService $merchantPolicyService 商户策略服务 + * @return void + */ public function __construct( protected MerchantPolicyService $merchantPolicyService ) { } + /** + * 查询商户策略列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function index(Request $request): Response { $data = $this->validated($request->all(), MerchantPolicyValidator::class, 'index'); @@ -31,6 +45,13 @@ class MerchantPolicyController extends BaseController ); } + /** + * 查询商户策略详情。 + * + * @param Request $request 请求对象 + * @param string $merchantId 商户ID + * @return Response 响应对象 + */ public function show(Request $request, string $merchantId): Response { $data = $this->validated(['merchant_id' => (int) $merchantId], MerchantPolicyValidator::class, 'show'); @@ -38,6 +59,12 @@ class MerchantPolicyController extends BaseController return $this->success($this->merchantPolicyService->findByMerchantId((int) $data['merchant_id'])); } + /** + * 新增商户策略。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function store(Request $request): Response { $data = $this->validated($request->all(), MerchantPolicyValidator::class, 'store'); @@ -45,6 +72,13 @@ class MerchantPolicyController extends BaseController return $this->success($this->merchantPolicyService->saveByMerchantId((int) $data['merchant_id'], $data)); } + /** + * 更新商户策略。 + * + * @param Request $request 请求对象 + * @param string $merchantId 商户ID + * @return Response 响应对象 + */ public function update(Request $request, string $merchantId): Response { $data = $this->validated( @@ -56,6 +90,13 @@ class MerchantPolicyController extends BaseController return $this->success($this->merchantPolicyService->saveByMerchantId((int) $data['merchant_id'], $data)); } + /** + * 删除商户策略。 + * + * @param Request $request 请求对象 + * @param string $merchantId 商户ID + * @return Response 响应对象 + */ public function destroy(Request $request, string $merchantId): Response { $data = $this->validated(['merchant_id' => (int) $merchantId], MerchantPolicyValidator::class, 'show'); @@ -64,3 +105,8 @@ class MerchantPolicyController extends BaseController } } + + + + + diff --git a/app/http/admin/controller/ops/ChannelDailyStatController.php b/app/http/admin/controller/ops/ChannelDailyStatController.php index 747a564..37916ab 100644 --- a/app/http/admin/controller/ops/ChannelDailyStatController.php +++ b/app/http/admin/controller/ops/ChannelDailyStatController.php @@ -10,11 +10,16 @@ use support\Response; /** * 通道日统计控制器。 + * + * @property ChannelDailyStatService $channelDailyStatService 渠道日统计服务 */ class ChannelDailyStatController extends BaseController { /** - * 构造函数,注入通道日统计服务。 + * 构造方法。 + * + * @param ChannelDailyStatService $channelDailyStatService 渠道日统计服务 + * @return void */ public function __construct( protected ChannelDailyStatService $channelDailyStatService @@ -23,6 +28,9 @@ class ChannelDailyStatController extends BaseController /** * 查询通道日统计列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -39,6 +47,10 @@ class ChannelDailyStatController extends BaseController /** * 查询通道日统计详情。 + * + * @param Request $request 请求对象 + * @param string $id 渠道日统计ID + * @return Response 响应对象 */ public function show(Request $request, string $id): Response { @@ -52,3 +64,8 @@ class ChannelDailyStatController extends BaseController return $this->success($stat); } } + + + + + diff --git a/app/http/admin/controller/ops/ChannelNotifyLogController.php b/app/http/admin/controller/ops/ChannelNotifyLogController.php index 164a12b..a211f4c 100644 --- a/app/http/admin/controller/ops/ChannelNotifyLogController.php +++ b/app/http/admin/controller/ops/ChannelNotifyLogController.php @@ -10,11 +10,16 @@ use support\Response; /** * 渠道通知日志控制器。 + * + * @property ChannelNotifyLogService $channelNotifyLogService 渠道通知日志服务 */ class ChannelNotifyLogController extends BaseController { /** - * 构造函数,注入渠道通知日志服务。 + * 构造方法。 + * + * @param ChannelNotifyLogService $channelNotifyLogService 渠道通知日志服务 + * @return void */ public function __construct( protected ChannelNotifyLogService $channelNotifyLogService @@ -23,6 +28,9 @@ class ChannelNotifyLogController extends BaseController /** * 查询渠道通知日志列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -39,6 +47,10 @@ class ChannelNotifyLogController extends BaseController /** * 查询渠道通知日志详情。 + * + * @param Request $request 请求对象 + * @param string $id 渠道通知日志ID + * @return Response 响应对象 */ public function show(Request $request, string $id): Response { @@ -52,3 +64,8 @@ class ChannelNotifyLogController extends BaseController return $this->success($log); } } + + + + + diff --git a/app/http/admin/controller/ops/PayCallbackLogController.php b/app/http/admin/controller/ops/PayCallbackLogController.php index 3ad813f..32f40e8 100644 --- a/app/http/admin/controller/ops/PayCallbackLogController.php +++ b/app/http/admin/controller/ops/PayCallbackLogController.php @@ -10,11 +10,16 @@ use support\Response; /** * 支付回调日志控制器。 + * + * @property PayCallbackLogService $payCallbackLogService 支付回调日志服务 */ class PayCallbackLogController extends BaseController { /** - * 构造函数,注入支付回调日志服务。 + * 构造方法。 + * + * @param PayCallbackLogService $payCallbackLogService 支付回调日志服务 + * @return void */ public function __construct( protected PayCallbackLogService $payCallbackLogService @@ -23,6 +28,9 @@ class PayCallbackLogController extends BaseController /** * 查询支付回调日志列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -39,6 +47,10 @@ class PayCallbackLogController extends BaseController /** * 查询支付回调日志详情。 + * + * @param Request $request 请求对象 + * @param string $id 支付回调日志ID + * @return Response 响应对象 */ public function show(Request $request, string $id): Response { @@ -52,3 +64,8 @@ class PayCallbackLogController extends BaseController return $this->success($log); } } + + + + + diff --git a/app/http/admin/controller/payment/PaymentChannelController.php b/app/http/admin/controller/payment/PaymentChannelController.php index 2da3293..6a4dc3a 100644 --- a/app/http/admin/controller/payment/PaymentChannelController.php +++ b/app/http/admin/controller/payment/PaymentChannelController.php @@ -12,11 +12,16 @@ use support\Response; * 支付通道管理控制器。 * * 负责支付通道的列表、详情、新增、修改和删除。 + * + * @property PaymentChannelService $paymentChannelService 支付渠道服务 */ class PaymentChannelController extends BaseController { /** - * 构造函数,注入支付通道服务。 + * 构造方法。 + * + * @param PaymentChannelService $paymentChannelService 支付渠道服务 + * @return void */ public function __construct( protected PaymentChannelService $paymentChannelService @@ -24,9 +29,10 @@ class PaymentChannelController extends BaseController } /** - * GET /admin/payment-channels - * * 查询支付通道列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -42,9 +48,11 @@ class PaymentChannelController extends BaseController } /** - * GET /admin/payment-channels/{id} - * * 查询支付通道详情。 + * + * @param Request $request 请求对象 + * @param string $id 支付渠道ID + * @return Response 响应对象 */ public function show(Request $request, string $id): Response { @@ -59,9 +67,10 @@ class PaymentChannelController extends BaseController } /** - * POST /admin/payment-channels - * * 新增支付通道。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function store(Request $request): Response { @@ -71,9 +80,11 @@ class PaymentChannelController extends BaseController } /** - * PUT /admin/payment-channels/{id} - * * 修改支付通道。 + * + * @param Request $request 请求对象 + * @param string $id 支付渠道ID + * @return Response 响应对象 */ public function update(Request $request, string $id): Response { @@ -94,9 +105,11 @@ class PaymentChannelController extends BaseController } /** - * DELETE /admin/payment-channels/{id} - * * 删除支付通道。 + * + * @param Request $request 请求对象 + * @param string $id 支付渠道ID + * @return Response 响应对象 */ public function destroy(Request $request, string $id): Response { @@ -111,6 +124,9 @@ class PaymentChannelController extends BaseController /** * 查询启用中的通道选项。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function options(Request $request): Response { @@ -119,6 +135,9 @@ class PaymentChannelController extends BaseController /** * 查询路由编排场景下的通道选项。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function routeOptions(Request $request): Response { @@ -127,6 +146,9 @@ class PaymentChannelController extends BaseController /** * 远程查询支付通道选择项。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function selectOptions(Request $request): Response { @@ -136,3 +158,8 @@ class PaymentChannelController extends BaseController return $this->success($this->paymentChannelService->searchOptions($request->all(), $page, $pageSize)); } } + + + + + diff --git a/app/http/admin/controller/payment/PaymentPluginConfController.php b/app/http/admin/controller/payment/PaymentPluginConfController.php index ee4f23f..1a10262 100644 --- a/app/http/admin/controller/payment/PaymentPluginConfController.php +++ b/app/http/admin/controller/payment/PaymentPluginConfController.php @@ -12,14 +12,28 @@ use support\Response; * 支付插件配置控制器。 * * 负责插件公共配置的列表、详情、增删改和选项输出。 + * + * @property PaymentPluginConfService $paymentPluginConfService 支付插件配置服务 */ class PaymentPluginConfController extends BaseController { + /** + * 构造方法。 + * + * @param PaymentPluginConfService $paymentPluginConfService 支付插件配置服务 + * @return void + */ public function __construct( protected PaymentPluginConfService $paymentPluginConfService ) { } + /** + * 查询支付插件配置列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function index(Request $request): Response { $data = $this->validated($request->all(), PaymentPluginConfValidator::class, 'index'); @@ -29,6 +43,13 @@ class PaymentPluginConfController extends BaseController return $this->page($this->paymentPluginConfService->paginate($data, $page, $pageSize)); } + /** + * 查询支付插件配置详情。 + * + * @param Request $request 请求对象 + * @param string $id 支付插件配置ID + * @return Response 响应对象 + */ public function show(Request $request, string $id): Response { $data = $this->validated(['id' => (int) $id], PaymentPluginConfValidator::class, 'show'); @@ -41,6 +62,12 @@ class PaymentPluginConfController extends BaseController return $this->success($pluginConf); } + /** + * 新增支付插件配置。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function store(Request $request): Response { $data = $this->validated($request->all(), PaymentPluginConfValidator::class, 'store'); @@ -48,6 +75,13 @@ class PaymentPluginConfController extends BaseController return $this->success($this->paymentPluginConfService->create($data)); } + /** + * 更新支付插件配置。 + * + * @param Request $request 请求对象 + * @param string $id 支付插件配置ID + * @return Response 响应对象 + */ public function update(Request $request, string $id): Response { $data = $this->validated( @@ -64,6 +98,13 @@ class PaymentPluginConfController extends BaseController return $this->success($pluginConf); } + /** + * 删除支付插件配置。 + * + * @param Request $request 请求对象 + * @param string $id 支付插件配置ID + * @return Response 响应对象 + */ public function destroy(Request $request, string $id): Response { $data = $this->validated(['id' => (int) $id], PaymentPluginConfValidator::class, 'destroy'); @@ -75,6 +116,12 @@ class PaymentPluginConfController extends BaseController return $this->success(true); } + /** + * 获取支付插件配置选项。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function options(Request $request): Response { $data = $this->validated($request->all(), PaymentPluginConfValidator::class, 'options'); @@ -86,6 +133,9 @@ class PaymentPluginConfController extends BaseController /** * 远程查询插件配置选项。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function selectOptions(Request $request): Response { @@ -96,3 +146,8 @@ class PaymentPluginConfController extends BaseController return $this->success($this->paymentPluginConfService->searchOptions($data, $page, $pageSize)); } } + + + + + diff --git a/app/http/admin/controller/payment/PaymentPluginController.php b/app/http/admin/controller/payment/PaymentPluginController.php index d7997bc..74cd4a1 100644 --- a/app/http/admin/controller/payment/PaymentPluginController.php +++ b/app/http/admin/controller/payment/PaymentPluginController.php @@ -12,11 +12,16 @@ use support\Response; * 支付插件管理控制器。 * * 负责插件字典的列表、详情、刷新同步和状态备注维护。 + * + * @property PaymentPluginService $paymentPluginService 支付插件服务 */ class PaymentPluginController extends BaseController { /** - * 构造函数,注入支付插件服务。 + * 构造方法。 + * + * @param PaymentPluginService $paymentPluginService 支付插件服务 + * @return void */ public function __construct( protected PaymentPluginService $paymentPluginService @@ -25,6 +30,9 @@ class PaymentPluginController extends BaseController /** * 查询支付插件列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -37,6 +45,10 @@ class PaymentPluginController extends BaseController /** * 查询支付插件详情。 + * + * @param Request $request 请求对象 + * @param string $code 编码 + * @return Response 响应对象 */ public function show(Request $request, string $code): Response { @@ -52,6 +64,10 @@ class PaymentPluginController extends BaseController /** * 修改支付插件。 + * + * @param Request $request 请求对象 + * @param string $code 编码 + * @return Response 响应对象 */ public function update(Request $request, string $code): Response { @@ -73,6 +89,9 @@ class PaymentPluginController extends BaseController /** * 从插件目录刷新同步支付插件。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function refresh(Request $request): Response { @@ -81,6 +100,9 @@ class PaymentPluginController extends BaseController /** * 查询支付插件下拉选项。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function options(Request $request): Response { @@ -91,6 +113,9 @@ class PaymentPluginController extends BaseController /** * 远程查询支付插件选项。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function selectOptions(Request $request): Response { @@ -103,6 +128,9 @@ class PaymentPluginController extends BaseController /** * 查询通道配置场景下的插件选项。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function channelOptions(Request $request): Response { @@ -113,6 +141,10 @@ class PaymentPluginController extends BaseController /** * 查询插件配置结构。 + * + * @param Request $request 请求对象 + * @param string $code 编码 + * @return Response 响应对象 */ public function schema(Request $request, string $code): Response { @@ -121,3 +153,8 @@ class PaymentPluginController extends BaseController return $this->success($this->paymentPluginService->getSchema((string) $data['code'])); } } + + + + + diff --git a/app/http/admin/controller/payment/PaymentPollGroupBindController.php b/app/http/admin/controller/payment/PaymentPollGroupBindController.php index 7c017ae..95d39bd 100644 --- a/app/http/admin/controller/payment/PaymentPollGroupBindController.php +++ b/app/http/admin/controller/payment/PaymentPollGroupBindController.php @@ -10,14 +10,28 @@ use support\Response; /** * 商户分组路由绑定控制器。 + * + * @property PaymentPollGroupBindService $paymentPollGroupBindService 支付轮询分组绑定服务 */ class PaymentPollGroupBindController extends BaseController { + /** + * 构造方法。 + * + * @param PaymentPollGroupBindService $paymentPollGroupBindService 支付轮询分组绑定服务 + * @return void + */ public function __construct( protected PaymentPollGroupBindService $paymentPollGroupBindService ) { } + /** + * 查询支付轮询分组绑定列表 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function index(Request $request): Response { $data = $this->validated($request->all(), PaymentPollGroupBindValidator::class, 'index'); @@ -31,6 +45,13 @@ class PaymentPollGroupBindController extends BaseController ); } + /** + * 查询支付轮询分组绑定详情 + * + * @param Request $request 请求对象 + * @param string $id 支付轮询分组绑定ID + * @return Response 响应对象 + */ public function show(Request $request, string $id): Response { $data = $this->validated(['id' => (int) $id], PaymentPollGroupBindValidator::class, 'show'); @@ -42,6 +63,12 @@ class PaymentPollGroupBindController extends BaseController return $this->success($row); } + /** + * 新增支付轮询分组绑定 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function store(Request $request): Response { $data = $this->validated($request->all(), PaymentPollGroupBindValidator::class, 'store'); @@ -49,6 +76,13 @@ class PaymentPollGroupBindController extends BaseController return $this->success($this->paymentPollGroupBindService->create($data)); } + /** + * 更新支付轮询分组绑定 + * + * @param Request $request 请求对象 + * @param string $id 支付轮询分组绑定ID + * @return Response 响应对象 + */ public function update(Request $request, string $id): Response { $data = $this->validated( @@ -65,6 +99,13 @@ class PaymentPollGroupBindController extends BaseController return $this->success($row); } + /** + * 删除支付轮询分组绑定 + * + * @param Request $request 请求对象 + * @param string $id 支付轮询分组绑定ID + * @return Response 响应对象 + */ public function destroy(Request $request, string $id): Response { $data = $this->validated(['id' => (int) $id], PaymentPollGroupBindValidator::class, 'destroy'); @@ -75,3 +116,8 @@ class PaymentPollGroupBindController extends BaseController return $this->success(true); } } + + + + + diff --git a/app/http/admin/controller/payment/PaymentPollGroupChannelController.php b/app/http/admin/controller/payment/PaymentPollGroupChannelController.php index 6d3113f..4f7043b 100644 --- a/app/http/admin/controller/payment/PaymentPollGroupChannelController.php +++ b/app/http/admin/controller/payment/PaymentPollGroupChannelController.php @@ -10,14 +10,28 @@ use support\Response; /** * 轮询组通道编排控制器。 + * + * @property PaymentPollGroupChannelService $paymentPollGroupChannelService 支付轮询分组渠道服务 */ class PaymentPollGroupChannelController extends BaseController { + /** + * 构造方法。 + * + * @param PaymentPollGroupChannelService $paymentPollGroupChannelService 支付轮询分组渠道服务 + * @return void + */ public function __construct( protected PaymentPollGroupChannelService $paymentPollGroupChannelService ) { } + /** + * 查询支付轮询分组渠道列表 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function index(Request $request): Response { $data = $this->validated($request->all(), PaymentPollGroupChannelValidator::class, 'index'); @@ -31,6 +45,13 @@ class PaymentPollGroupChannelController extends BaseController ); } + /** + * 查询支付轮询分组渠道详情 + * + * @param Request $request 请求对象 + * @param string $id 支付轮询分组渠道ID + * @return Response 响应对象 + */ public function show(Request $request, string $id): Response { $data = $this->validated(['id' => (int) $id], PaymentPollGroupChannelValidator::class, 'show'); @@ -42,6 +63,12 @@ class PaymentPollGroupChannelController extends BaseController return $this->success($row); } + /** + * 新增支付轮询分组渠道 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function store(Request $request): Response { $data = $this->validated($request->all(), PaymentPollGroupChannelValidator::class, 'store'); @@ -49,6 +76,13 @@ class PaymentPollGroupChannelController extends BaseController return $this->success($this->paymentPollGroupChannelService->create($data)); } + /** + * 更新支付轮询分组渠道 + * + * @param Request $request 请求对象 + * @param string $id 支付轮询分组渠道ID + * @return Response 响应对象 + */ public function update(Request $request, string $id): Response { $payload = $request->all(); @@ -67,6 +101,13 @@ class PaymentPollGroupChannelController extends BaseController return $this->success($row); } + /** + * 删除支付轮询分组渠道 + * + * @param Request $request 请求对象 + * @param string $id 支付轮询分组渠道ID + * @return Response 响应对象 + */ public function destroy(Request $request, string $id): Response { $data = $this->validated(['id' => (int) $id], PaymentPollGroupChannelValidator::class, 'destroy'); @@ -77,3 +118,8 @@ class PaymentPollGroupChannelController extends BaseController return $this->success(true); } } + + + + + diff --git a/app/http/admin/controller/payment/PaymentPollGroupController.php b/app/http/admin/controller/payment/PaymentPollGroupController.php index ea9ab28..8e37940 100644 --- a/app/http/admin/controller/payment/PaymentPollGroupController.php +++ b/app/http/admin/controller/payment/PaymentPollGroupController.php @@ -12,11 +12,16 @@ use support\Response; * 支付轮询组管理控制器。 * * 负责轮询组的列表、详情、新增、修改和删除。 + * + * @property PaymentPollGroupService $paymentPollGroupService 支付轮询分组服务 */ class PaymentPollGroupController extends BaseController { /** - * 构造函数,注入轮询组服务。 + * 构造方法。 + * + * @param PaymentPollGroupService $paymentPollGroupService 支付轮询分组服务 + * @return void */ public function __construct( protected PaymentPollGroupService $paymentPollGroupService @@ -24,9 +29,10 @@ class PaymentPollGroupController extends BaseController } /** - * GET /admin/payment-poll-groups - * * 查询轮询组列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -42,9 +48,11 @@ class PaymentPollGroupController extends BaseController } /** - * GET /admin/payment-poll-groups/{id} - * * 查询轮询组详情。 + * + * @param Request $request 请求对象 + * @param string $id 支付轮询分组ID + * @return Response 响应对象 */ public function show(Request $request, string $id): Response { @@ -59,9 +67,10 @@ class PaymentPollGroupController extends BaseController } /** - * POST /admin/payment-poll-groups - * * 新增轮询组。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function store(Request $request): Response { @@ -71,9 +80,11 @@ class PaymentPollGroupController extends BaseController } /** - * PUT /admin/payment-poll-groups/{id} - * * 修改轮询组。 + * + * @param Request $request 请求对象 + * @param string $id 支付轮询分组ID + * @return Response 响应对象 */ public function update(Request $request, string $id): Response { @@ -94,9 +105,11 @@ class PaymentPollGroupController extends BaseController } /** - * DELETE /admin/payment-poll-groups/{id} - * * 删除轮询组。 + * + * @param Request $request 请求对象 + * @param string $id 支付轮询分组ID + * @return Response 响应对象 */ public function destroy(Request $request, string $id): Response { @@ -111,9 +124,17 @@ class PaymentPollGroupController extends BaseController /** * 查询轮询组下拉选项。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function options(Request $request): Response { return $this->success($this->paymentPollGroupService->enabledOptions($request->all())); } } + + + + + diff --git a/app/http/admin/controller/payment/PaymentTypeController.php b/app/http/admin/controller/payment/PaymentTypeController.php index f100838..d49f7d7 100644 --- a/app/http/admin/controller/payment/PaymentTypeController.php +++ b/app/http/admin/controller/payment/PaymentTypeController.php @@ -12,11 +12,16 @@ use support\Response; * 支付方式管理控制器。 * * 负责支付方式字典的列表、详情、新增、修改、删除和选项输出。 + * + * @property PaymentTypeService $paymentTypeService 支付类型服务 */ class PaymentTypeController extends BaseController { /** - * 构造函数,注入支付方式服务。 + * 构造方法。 + * + * @param PaymentTypeService $paymentTypeService 支付类型服务 + * @return void */ public function __construct( protected PaymentTypeService $paymentTypeService @@ -25,6 +30,9 @@ class PaymentTypeController extends BaseController /** * 查询支付方式列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -37,6 +45,10 @@ class PaymentTypeController extends BaseController /** * 查询支付方式详情。 + * + * @param Request $request 请求对象 + * @param string $id 支付类型ID + * @return Response 响应对象 */ public function show(Request $request, string $id): Response { @@ -52,6 +64,9 @@ class PaymentTypeController extends BaseController /** * 新增支付方式。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function store(Request $request): Response { @@ -62,6 +77,10 @@ class PaymentTypeController extends BaseController /** * 修改支付方式。 + * + * @param Request $request 请求对象 + * @param string $id 支付类型ID + * @return Response 响应对象 */ public function update(Request $request, string $id): Response { @@ -83,6 +102,10 @@ class PaymentTypeController extends BaseController /** * 删除支付方式。 + * + * @param Request $request 请求对象 + * @param string $id 支付类型ID + * @return Response 响应对象 */ public function destroy(Request $request, string $id): Response { @@ -97,9 +120,17 @@ class PaymentTypeController extends BaseController /** * 查询支付方式下拉选项。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function options(Request $request): Response { return $this->success($this->paymentTypeService->enabledOptions()); } } + + + + + diff --git a/app/http/admin/controller/payment/RouteController.php b/app/http/admin/controller/payment/RouteController.php index 5dac95b..8fe2aca 100644 --- a/app/http/admin/controller/payment/RouteController.php +++ b/app/http/admin/controller/payment/RouteController.php @@ -9,14 +9,19 @@ use support\Request; use support\Response; /** - * 管理后台路由预览控制器。 + * 管理后台路由解析控制器。 * * 负责按商户分组、支付方式和金额条件解析可用通道。 + * + * @property PaymentRouteService $paymentRouteService 支付路由服务 */ class RouteController extends BaseController { /** - * 构造函数,注入路由服务。 + * 构造方法。 + * + * @param PaymentRouteService $paymentRouteService 支付路由服务 + * @return void */ public function __construct( protected PaymentRouteService $paymentRouteService @@ -24,9 +29,10 @@ class RouteController extends BaseController } /** - * GET /admin/routes/resolve - * * 解析路由结果。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function resolve(Request $request): Response { @@ -41,3 +47,8 @@ class RouteController extends BaseController } } + + + + + diff --git a/app/http/admin/controller/system/AdminUserController.php b/app/http/admin/controller/system/AdminUserController.php index ccfd963..d5b55eb 100644 --- a/app/http/admin/controller/system/AdminUserController.php +++ b/app/http/admin/controller/system/AdminUserController.php @@ -12,11 +12,16 @@ use support\Response; * 管理员用户管理控制器。 * * 负责管理员账号的列表、详情、新增、修改和删除。 + * + * @property AdminUserService $adminUserService 管理用户服务 */ class AdminUserController extends BaseController { /** - * 构造函数,注入管理员用户服务。 + * 构造方法。 + * + * @param AdminUserService $adminUserService 管理用户服务 + * @return void */ public function __construct( protected AdminUserService $adminUserService @@ -24,9 +29,10 @@ class AdminUserController extends BaseController } /** - * GET /adminapi/admin-users - * * 查询管理员用户列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -42,9 +48,11 @@ class AdminUserController extends BaseController } /** - * GET /adminapi/admin-users/{id} - * * 查询管理员用户详情。 + * + * @param Request $request 请求对象 + * @param string $id 管理用户ID + * @return Response 响应对象 */ public function show(Request $request, string $id): Response { @@ -59,9 +67,10 @@ class AdminUserController extends BaseController } /** - * POST /adminapi/admin-users - * * 新增管理员用户。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function store(Request $request): Response { @@ -71,9 +80,11 @@ class AdminUserController extends BaseController } /** - * PUT /adminapi/admin-users/{id} - * * 修改管理员用户。 + * + * @param Request $request 请求对象 + * @param string $id 管理用户ID + * @return Response 响应对象 */ public function update(Request $request, string $id): Response { @@ -92,9 +103,11 @@ class AdminUserController extends BaseController } /** - * DELETE /adminapi/admin-users/{id} - * * 删除管理员用户。 + * + * @param Request $request 请求对象 + * @param string $id 管理用户ID + * @return Response 响应对象 */ public function destroy(Request $request, string $id): Response { @@ -120,3 +133,8 @@ class AdminUserController extends BaseController return $this->success(true); } } + + + + + diff --git a/app/http/admin/controller/system/AuthController.php b/app/http/admin/controller/system/AuthController.php index 8bccae2..7d27ac1 100644 --- a/app/http/admin/controller/system/AuthController.php +++ b/app/http/admin/controller/system/AuthController.php @@ -11,15 +11,31 @@ use support\Response; /** * 管理员认证控制器。 + * + * @property AdminAuthService $adminAuthService 管理认证服务 + * @property AdminUserService $adminUserService 管理用户服务 */ class AuthController extends BaseController { + /** + * 构造方法。 + * + * @param AdminAuthService $adminAuthService 管理认证服务 + * @param AdminUserService $adminUserService 管理用户服务 + * @return void + */ public function __construct( protected AdminAuthService $adminAuthService, protected AdminUserService $adminUserService ) { } + /** + * 管理员登录。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function login(Request $request): Response { $data = $this->validated($request->all(), AuthValidator::class, 'login'); @@ -32,6 +48,12 @@ class AuthController extends BaseController )); } + /** + * 管理员退出登录。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function logout(Request $request): Response { $token = trim((string) ($request->header('authorization', '') ?: $request->header('x-admin-token', ''))); @@ -47,7 +69,10 @@ class AuthController extends BaseController } /** - * 获取当前登录管理员的信息 + * 获取当前登录管理员信息。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function profile(Request $request): Response { @@ -63,3 +88,8 @@ class AuthController extends BaseController } } + + + + + diff --git a/app/http/admin/controller/system/SystemConfigPageController.php b/app/http/admin/controller/system/SystemConfigPageController.php index 1a9d5d3..4f0f561 100644 --- a/app/http/admin/controller/system/SystemConfigPageController.php +++ b/app/http/admin/controller/system/SystemConfigPageController.php @@ -8,18 +8,42 @@ use app\service\system\config\SystemConfigPageService; use support\Request; use support\Response; +/** + * 系统配置页面控制器 + * + * @property SystemConfigPageService $systemConfigPageService 系统配置页面服务 + */ class SystemConfigPageController extends BaseController { + /** + * 构造方法。 + * + * @param SystemConfigPageService $systemConfigPageService 系统配置页面服务 + * @return void + */ public function __construct( protected SystemConfigPageService $systemConfigPageService ) { } + /** + * 查询系统配置页面列表 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function index(Request $request): Response { return $this->success($this->systemConfigPageService->tabs()); } + /** + * 查询系统配置页面详情 + * + * @param Request $request 请求对象 + * @param string $groupCode 分组Code + * @return Response 响应对象 + */ public function show(Request $request, string $groupCode): Response { $data = $this->validated(['group_code' => $groupCode], SystemConfigPageValidator::class, 'show'); @@ -27,6 +51,13 @@ class SystemConfigPageController extends BaseController return $this->success($this->systemConfigPageService->detail((string) $data['group_code'])); } + /** + * 新增系统配置页面 + * + * @param Request $request 请求对象 + * @param string $groupCode 分组Code + * @return Response 响应对象 + */ public function store(Request $request, string $groupCode): Response { $data = $this->validated( @@ -40,3 +71,8 @@ class SystemConfigPageController extends BaseController ); } } + + + + + diff --git a/app/http/admin/controller/system/SystemController.php b/app/http/admin/controller/system/SystemController.php index e7fa406..1becced 100644 --- a/app/http/admin/controller/system/SystemController.php +++ b/app/http/admin/controller/system/SystemController.php @@ -9,22 +9,47 @@ use support\Response; /** * 管理后台系统数据控制器。 + * + * @property SystemBootstrapService $systemBootstrapService 系统引导服务 */ class SystemController extends BaseController { + /** + * 构造方法。 + * + * @param SystemBootstrapService $systemBootstrapService 系统引导服务 + * @return void + */ public function __construct( protected SystemBootstrapService $systemBootstrapService ) { } + /** + * 获取系统菜单树 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function menuTree(Request $request): Response { return $this->success($this->systemBootstrapService->getMenuTree('admin')); } + /** + * 获取系统字典项 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function dictItems(Request $request): Response { return $this->success($this->systemBootstrapService->getDictItems((string) $request->get('code', ''))); } } + + + + + diff --git a/app/http/admin/controller/trade/PayOrderController.php b/app/http/admin/controller/trade/PayOrderController.php index d5c43e5..84e8a56 100644 --- a/app/http/admin/controller/trade/PayOrderController.php +++ b/app/http/admin/controller/trade/PayOrderController.php @@ -12,11 +12,16 @@ use support\Response; * 支付订单管理控制器。 * * 当前先提供列表查询,后续可以继续扩展支付单详情、关闭、重试等管理操作。 + * + * @property PayOrderService $payOrderService 支付订单服务 */ class PayOrderController extends BaseController { /** - * 构造函数,注入支付订单服务。 + * 构造方法。 + * + * @param PayOrderService $payOrderService 支付订单服务 + * @return void */ public function __construct( protected PayOrderService $payOrderService @@ -25,6 +30,9 @@ class PayOrderController extends BaseController /** * 查询支付订单列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -36,3 +44,8 @@ class PayOrderController extends BaseController } } + + + + + diff --git a/app/http/admin/controller/trade/RefundOrderController.php b/app/http/admin/controller/trade/RefundOrderController.php index e1f7489..e01ba50 100644 --- a/app/http/admin/controller/trade/RefundOrderController.php +++ b/app/http/admin/controller/trade/RefundOrderController.php @@ -11,9 +11,17 @@ use support\Response; /** * 退款订单管理控制器。 + * + * @property RefundService $refundService 退款服务 */ class RefundOrderController extends BaseController { + /** + * 构造方法。 + * + * @param RefundService $refundService 退款服务 + * @return void + */ public function __construct( protected RefundService $refundService ) { @@ -21,6 +29,9 @@ class RefundOrderController extends BaseController /** * 查询退款订单列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -33,6 +44,10 @@ class RefundOrderController extends BaseController /** * 查询退款订单详情。 + * + * @param Request $request 请求对象 + * @param string $refundNo 退款单号 + * @return Response 响应对象 */ public function show(Request $request, string $refundNo): Response { @@ -41,6 +56,10 @@ class RefundOrderController extends BaseController /** * 重试退款。 + * + * @param Request $request 请求对象 + * @param string $refundNo 退款单号 + * @return Response 响应对象 */ public function retry(Request $request, string $refundNo): Response { @@ -54,3 +73,8 @@ class RefundOrderController extends BaseController } } + + + + + diff --git a/app/http/admin/controller/trade/SettlementOrderController.php b/app/http/admin/controller/trade/SettlementOrderController.php index d48fe4d..d462155 100644 --- a/app/http/admin/controller/trade/SettlementOrderController.php +++ b/app/http/admin/controller/trade/SettlementOrderController.php @@ -11,11 +11,16 @@ use support\Response; /** * 清算订单控制器。 + * + * @property SettlementOrderQueryService $settlementOrderQueryService 结算订单查询服务 */ class SettlementOrderController extends BaseController { /** - * 构造函数,注入清算订单服务。 + * 构造方法。 + * + * @param SettlementOrderQueryService $settlementOrderQueryService 结算订单查询服务 + * @return void */ public function __construct( protected SettlementOrderQueryService $settlementOrderQueryService @@ -24,6 +29,9 @@ class SettlementOrderController extends BaseController /** * 查询清算订单列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -40,6 +48,10 @@ class SettlementOrderController extends BaseController /** * 查询清算订单详情。 + * + * @param Request $request 请求对象 + * @param string $settleNo 结算单号 + * @return Response 响应对象 */ public function show(Request $request, string $settleNo): Response { @@ -51,3 +63,8 @@ class SettlementOrderController extends BaseController } } } + + + + + diff --git a/app/http/admin/middleware/AdminAuthMiddleware.php b/app/http/admin/middleware/AdminAuthMiddleware.php index 851e69d..7a8af82 100644 --- a/app/http/admin/middleware/AdminAuthMiddleware.php +++ b/app/http/admin/middleware/AdminAuthMiddleware.php @@ -12,11 +12,16 @@ use Webman\MiddlewareInterface; * 管理员认证中间件。 * * 负责读取管理员 token,并把管理员身份写入请求上下文。 + * + * @property AdminAuthService $adminAuthService 管理认证服务 */ class AdminAuthMiddleware implements MiddlewareInterface { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param AdminAuthService $adminAuthService 管理认证服务 + * @return void */ public function __construct( protected AdminAuthService $adminAuthService @@ -25,6 +30,10 @@ class AdminAuthMiddleware implements MiddlewareInterface /** * 处理请求。 + * + * @param Request $request 请求对象 + * @param callable $handler handler + * @return Response 响应对象 */ public function process(Request $request, callable $handler): Response { @@ -60,3 +69,8 @@ class AdminAuthMiddleware implements MiddlewareInterface return $handler($request); } } + + + + + diff --git a/app/http/admin/validation/AdminUserValidator.php b/app/http/admin/validation/AdminUserValidator.php index ff3128a..d9520b3 100644 --- a/app/http/admin/validation/AdminUserValidator.php +++ b/app/http/admin/validation/AdminUserValidator.php @@ -9,6 +9,11 @@ use support\validation\Validator; */ class AdminUserValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'sometimes|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -24,6 +29,11 @@ class AdminUserValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '管理员ID', 'keyword' => '关键词', @@ -33,12 +43,17 @@ class AdminUserValidator extends Validator 'mobile' => '手机号', 'email' => '邮箱', 'is_super' => '超级管理员', - 'status' => '状态', + 'status' => '管理员状态', 'remark' => '备注', 'page' => '页码', 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'status', 'is_super', 'page', 'page_size'], 'store' => ['username', 'password', 'real_name', 'mobile', 'email', 'is_super', 'status', 'remark'], @@ -47,6 +62,11 @@ class AdminUserValidator extends Validator 'destroy' => ['id'], ]; + /** + * 配置新增管理员场景规则。 + * + * @return static 校验器实例 + */ public function sceneStore(): static { return $this->appendRules([ @@ -58,6 +78,11 @@ class AdminUserValidator extends Validator ]); } + /** + * 配置更新管理员场景规则。 + * + * @return static 校验器实例 + */ public function sceneUpdate(): static { return $this->appendRules([ @@ -69,6 +94,11 @@ class AdminUserValidator extends Validator ]); } + /** + * 配置管理员详情场景规则。 + * + * @return static 校验器实例 + */ public function sceneShow(): static { return $this->appendRules([ @@ -76,8 +106,14 @@ class AdminUserValidator extends Validator ]); } + /** + * 配置删除管理员场景规则。 + * + * @return static 校验器实例 + */ public function sceneDestroy(): static { return $this->sceneShow(); } } + diff --git a/app/http/admin/validation/AuthValidator.php b/app/http/admin/validation/AuthValidator.php index 7662921..31c6056 100644 --- a/app/http/admin/validation/AuthValidator.php +++ b/app/http/admin/validation/AuthValidator.php @@ -11,17 +11,34 @@ use support\validation\Validator; */ class AuthValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'username' => 'required|string|min:1|max:32', 'password' => 'required|string|min:6|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'username' => '用户名', 'password' => '密码', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'login' => ['username', 'password'], ]; } + + diff --git a/app/http/admin/validation/ChannelDailyStatValidator.php b/app/http/admin/validation/ChannelDailyStatValidator.php index 4a9a883..2b5e844 100644 --- a/app/http/admin/validation/ChannelDailyStatValidator.php +++ b/app/http/admin/validation/ChannelDailyStatValidator.php @@ -9,6 +9,11 @@ use support\validation\Validator; */ class ChannelDailyStatValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'required|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -19,6 +24,11 @@ class ChannelDailyStatValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '统计ID', 'keyword' => '关键词', @@ -29,8 +39,15 @@ class ChannelDailyStatValidator extends Validator 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'merchant_id', 'channel_id', 'stat_date', 'page', 'page_size'], 'show' => ['id'], ]; } + + diff --git a/app/http/admin/validation/ChannelNotifyLogValidator.php b/app/http/admin/validation/ChannelNotifyLogValidator.php index 094efc6..ae70861 100644 --- a/app/http/admin/validation/ChannelNotifyLogValidator.php +++ b/app/http/admin/validation/ChannelNotifyLogValidator.php @@ -9,6 +9,11 @@ use support\validation\Validator; */ class ChannelNotifyLogValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'required|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -21,6 +26,11 @@ class ChannelNotifyLogValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '日志ID', 'keyword' => '关键词', @@ -33,8 +43,15 @@ class ChannelNotifyLogValidator extends Validator 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'merchant_id', 'channel_id', 'notify_type', 'verify_status', 'process_status', 'page', 'page_size'], 'show' => ['id'], ]; } + + diff --git a/app/http/admin/validation/FileRecordValidator.php b/app/http/admin/validation/FileRecordValidator.php index 23da680..ff69592 100644 --- a/app/http/admin/validation/FileRecordValidator.php +++ b/app/http/admin/validation/FileRecordValidator.php @@ -9,6 +9,11 @@ use support\validation\Validator; */ class FileRecordValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'sometimes|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -21,6 +26,11 @@ class FileRecordValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '文件ID', 'keyword' => '关键字', @@ -33,13 +43,20 @@ class FileRecordValidator extends Validator 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'scene', 'source_type', 'visibility', 'storage_engine', 'page', 'page_size'], 'show' => ['id'], 'destroy' => ['id'], 'preview' => ['id'], 'download' => ['id'], - 'store' => ['scene', 'visibility'], - 'importRemote' => ['remote_url', 'scene', 'visibility'], + 'store' => ['scene', 'visibility', 'storage_engine'], + 'importRemote' => ['remote_url', 'scene', 'visibility', 'storage_engine'], ]; } + + diff --git a/app/http/admin/validation/MerchantAccountLedgerValidator.php b/app/http/admin/validation/MerchantAccountLedgerValidator.php index bd416de..304986d 100644 --- a/app/http/admin/validation/MerchantAccountLedgerValidator.php +++ b/app/http/admin/validation/MerchantAccountLedgerValidator.php @@ -9,6 +9,11 @@ use support\validation\Validator; */ class MerchantAccountLedgerValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'required|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -20,6 +25,11 @@ class MerchantAccountLedgerValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '流水ID', 'keyword' => '关键词', @@ -31,8 +41,15 @@ class MerchantAccountLedgerValidator extends Validator 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'merchant_id', 'biz_type', 'event_type', 'direction', 'page', 'page_size'], 'show' => ['id'], ]; } + + diff --git a/app/http/admin/validation/MerchantAccountValidator.php b/app/http/admin/validation/MerchantAccountValidator.php index 112fe11..113715d 100644 --- a/app/http/admin/validation/MerchantAccountValidator.php +++ b/app/http/admin/validation/MerchantAccountValidator.php @@ -9,6 +9,11 @@ use support\validation\Validator; */ class MerchantAccountValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'required|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -17,6 +22,11 @@ class MerchantAccountValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '账户ID', 'keyword' => '关键词', @@ -25,8 +35,15 @@ class MerchantAccountValidator extends Validator 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'merchant_id', 'page', 'page_size'], 'show' => ['id'], ]; } + + diff --git a/app/http/admin/validation/MerchantApiCredentialValidator.php b/app/http/admin/validation/MerchantApiCredentialValidator.php index c12d1ca..7e69e95 100644 --- a/app/http/admin/validation/MerchantApiCredentialValidator.php +++ b/app/http/admin/validation/MerchantApiCredentialValidator.php @@ -5,10 +5,15 @@ namespace app\http\admin\validation; use support\validation\Validator; /** - * 商户接口凭证参数校验器。 + * 商户 API 凭证参数校验器。 */ class MerchantApiCredentialValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'sometimes|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -20,17 +25,27 @@ class MerchantApiCredentialValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '凭证ID', 'keyword' => '关键词', 'merchant_id' => '所属商户', 'sign_type' => '签名类型', 'api_key' => '接口凭证值', - 'status' => '状态', + 'status' => '接口凭证状态', 'page' => '页码', 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'merchant_id', 'status', 'page', 'page_size'], 'store' => ['merchant_id', 'sign_type', 'api_key', 'status'], @@ -39,6 +54,11 @@ class MerchantApiCredentialValidator extends Validator 'destroy' => ['id'], ]; + /** + * 配置新增接口凭证场景规则。 + * + * @return static 校验器实例 + */ public function sceneStore(): static { return $this->appendRules([ @@ -48,6 +68,11 @@ class MerchantApiCredentialValidator extends Validator ]); } + /** + * 配置更新接口凭证场景规则。 + * + * @return static 校验器实例 + */ public function sceneUpdate(): static { return $this->appendRules([ @@ -57,6 +82,11 @@ class MerchantApiCredentialValidator extends Validator ]); } + /** + * 配置接口凭证详情场景规则。 + * + * @return static 校验器实例 + */ public function sceneShow(): static { return $this->appendRules([ @@ -64,6 +94,11 @@ class MerchantApiCredentialValidator extends Validator ]); } + /** + * 配置删除接口凭证场景规则。 + * + * @return static 校验器实例 + */ public function sceneDestroy(): static { return $this->sceneShow(); diff --git a/app/http/admin/validation/MerchantGroupValidator.php b/app/http/admin/validation/MerchantGroupValidator.php index 7caa5b2..f4926cd 100644 --- a/app/http/admin/validation/MerchantGroupValidator.php +++ b/app/http/admin/validation/MerchantGroupValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class MerchantGroupValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'sometimes|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -21,16 +26,26 @@ class MerchantGroupValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '分组ID', 'keyword' => '关键字', 'group_name' => '分组名称', - 'status' => '状态', + 'status' => '分组状态', 'remark' => '备注', 'page' => '页码', 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'group_name', 'status', 'page', 'page_size'], 'store' => ['group_name', 'status', 'remark'], @@ -39,6 +54,11 @@ class MerchantGroupValidator extends Validator 'destroy' => ['id'], ]; + /** + * 配置新增商户分组场景规则。 + * + * @return static 校验器实例 + */ public function sceneStore(): static { return $this->appendRules([ @@ -47,6 +67,11 @@ class MerchantGroupValidator extends Validator ]); } + /** + * 配置更新商户分组场景规则。 + * + * @return static 校验器实例 + */ public function sceneUpdate(): static { return $this->appendRules([ @@ -56,6 +81,11 @@ class MerchantGroupValidator extends Validator ]); } + /** + * 配置商户分组详情场景规则。 + * + * @return static 校验器实例 + */ public function sceneShow(): static { return $this->appendRules([ @@ -63,8 +93,16 @@ class MerchantGroupValidator extends Validator ]); } + /** + * 配置删除商户分组场景规则。 + * + * @return static 校验器实例 + */ public function sceneDestroy(): static { return $this->sceneShow(); } } + + + diff --git a/app/http/admin/validation/MerchantPolicyValidator.php b/app/http/admin/validation/MerchantPolicyValidator.php index bf524bd..178601d 100644 --- a/app/http/admin/validation/MerchantPolicyValidator.php +++ b/app/http/admin/validation/MerchantPolicyValidator.php @@ -9,6 +9,11 @@ use support\validation\Validator; */ class MerchantPolicyValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'sometimes|integer|min:1', 'merchant_id' => 'sometimes|integer|min:1|exists:ma_merchant,id', @@ -27,6 +32,11 @@ class MerchantPolicyValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '策略ID', 'merchant_id' => '所属商户', @@ -45,6 +55,11 @@ class MerchantPolicyValidator extends Validator 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'merchant_id', 'group_id', 'has_policy', 'settlement_cycle_override', 'auto_payout', 'page', 'page_size'], 'show' => ['merchant_id'], @@ -72,6 +87,11 @@ class MerchantPolicyValidator extends Validator ], ]; + /** + * 配置新增商户策略场景规则。 + * + * @return static 校验器实例 + */ public function sceneStore(): static { return $this->appendRules([ @@ -82,8 +102,15 @@ class MerchantPolicyValidator extends Validator ]); } + /** + * 配置更新商户策略场景规则。 + * + * @return static 校验器实例 + */ public function sceneUpdate(): static { return $this->sceneStore(); } } + + diff --git a/app/http/admin/validation/MerchantValidator.php b/app/http/admin/validation/MerchantValidator.php index 5f9e4cf..18dba98 100644 --- a/app/http/admin/validation/MerchantValidator.php +++ b/app/http/admin/validation/MerchantValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class MerchantValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'sometimes|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -35,11 +40,16 @@ class MerchantValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '商户ID', 'keyword' => '关键字', 'group_id' => '商户分组', - 'status' => '状态', + 'status' => '商户状态', 'merchant_type' => '商户类型', 'merchant_no' => '商户号', 'merchant_name' => '商户名称', @@ -59,6 +69,11 @@ class MerchantValidator extends Validator 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'group_id', 'status', 'merchant_type', 'risk_level', 'page', 'page_size'], 'show' => ['id'], @@ -101,6 +116,11 @@ class MerchantValidator extends Validator 'destroy' => ['id'], ]; + /** + * 配置新增商户场景规则。 + * + * @return static 校验器实例 + */ public function sceneStore(): static { return $this->appendRules([ @@ -114,6 +134,11 @@ class MerchantValidator extends Validator ]); } + /** + * 配置更新商户场景规则。 + * + * @return static 校验器实例 + */ public function sceneUpdate(): static { return $this->appendRules([ @@ -128,6 +153,11 @@ class MerchantValidator extends Validator ]); } + /** + * 配置更新商户状态场景规则。 + * + * @return static 校验器实例 + */ public function sceneUpdateStatus(): static { return $this->appendRules([ @@ -136,6 +166,11 @@ class MerchantValidator extends Validator ]); } + /** + * 配置重置密码场景规则。 + * + * @return static 校验器实例 + */ public function sceneResetPassword(): static { return $this->appendRules([ @@ -145,6 +180,11 @@ class MerchantValidator extends Validator ]); } + /** + * 配置删除商户场景规则。 + * + * @return static 校验器实例 + */ public function sceneDestroy(): static { return $this->appendRules([ @@ -152,3 +192,4 @@ class MerchantValidator extends Validator ]); } } + diff --git a/app/http/admin/validation/PayCallbackLogValidator.php b/app/http/admin/validation/PayCallbackLogValidator.php index 5110263..cc554c3 100644 --- a/app/http/admin/validation/PayCallbackLogValidator.php +++ b/app/http/admin/validation/PayCallbackLogValidator.php @@ -9,6 +9,11 @@ use support\validation\Validator; */ class PayCallbackLogValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'required|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -21,6 +26,11 @@ class PayCallbackLogValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '日志ID', 'keyword' => '关键词', @@ -33,8 +43,15 @@ class PayCallbackLogValidator extends Validator 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'merchant_id', 'channel_id', 'callback_type', 'verify_status', 'process_status', 'page', 'page_size'], 'show' => ['id'], ]; } + + diff --git a/app/http/admin/validation/PayOrderValidator.php b/app/http/admin/validation/PayOrderValidator.php index 5cfbcfd..8178f03 100644 --- a/app/http/admin/validation/PayOrderValidator.php +++ b/app/http/admin/validation/PayOrderValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class PayOrderValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'keyword' => 'sometimes|string|max:128', 'merchant_id' => 'sometimes|integer|min:1', @@ -22,18 +27,29 @@ class PayOrderValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'keyword' => '关键字', 'merchant_id' => '商户ID', 'pay_type_id' => '支付方式', - 'status' => '状态', + 'status' => '支付单状态', 'channel_mode' => '通道模式', - 'callback_status' => '回调状态', + 'callback_status' => '回调处理状态', 'page' => '页码', 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'merchant_id', 'pay_type_id', 'status', 'channel_mode', 'callback_status', 'page', 'page_size'], ]; } + diff --git a/app/http/admin/validation/PaymentChannelValidator.php b/app/http/admin/validation/PaymentChannelValidator.php index 0118881..9284cf8 100644 --- a/app/http/admin/validation/PaymentChannelValidator.php +++ b/app/http/admin/validation/PaymentChannelValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class PaymentChannelValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'sometimes|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -33,6 +38,11 @@ class PaymentChannelValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '通道ID', 'keyword' => '关键字', @@ -49,12 +59,17 @@ class PaymentChannelValidator extends Validator 'min_amount' => '最小金额', 'max_amount' => '最大金额', 'remark' => '备注', - 'status' => '状态', + 'status' => '通道状态', 'sort_no' => '排序', 'page' => '页码', 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'merchant_id', 'pay_type_id', 'plugin_code', 'channel_mode', 'status', 'page', 'page_size'], 'store' => ['merchant_id', 'name', 'split_rate_bp', 'cost_rate_bp', 'channel_mode', 'pay_type_id', 'plugin_code', 'api_config_id', 'daily_limit_amount', 'daily_limit_count', 'min_amount', 'max_amount', 'remark', 'status', 'sort_no'], @@ -64,6 +79,11 @@ class PaymentChannelValidator extends Validator 'destroy' => ['id'], ]; + /** + * 根据场景返回支付通道校验规则。 + * + * @return array 校验规则 + */ public function rules(): array { $rules = parent::rules(); @@ -101,3 +121,6 @@ class PaymentChannelValidator extends Validator }; } } + + + diff --git a/app/http/admin/validation/PaymentPluginConfValidator.php b/app/http/admin/validation/PaymentPluginConfValidator.php index 21438ba..31a6a75 100644 --- a/app/http/admin/validation/PaymentPluginConfValidator.php +++ b/app/http/admin/validation/PaymentPluginConfValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class PaymentPluginConfValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'sometimes|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -24,6 +29,11 @@ class PaymentPluginConfValidator extends Validator 'ids' => 'sometimes|array', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '配置ID', 'keyword' => '关键字', @@ -37,6 +47,11 @@ class PaymentPluginConfValidator extends Validator 'ids' => '配置ID集合', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'plugin_code', 'page', 'page_size'], 'store' => ['plugin_code', 'config', 'settlement_cycle_type', 'settlement_cutoff_time', 'remark'], @@ -47,6 +62,11 @@ class PaymentPluginConfValidator extends Validator 'selectOptions' => ['keyword', 'plugin_code', 'page', 'page_size', 'ids'], ]; + /** + * 根据场景返回支付插件配置校验规则。 + * + * @return array 校验规则 + */ public function rules(): array { $rules = parent::rules(); @@ -66,3 +86,5 @@ class PaymentPluginConfValidator extends Validator }; } } + + diff --git a/app/http/admin/validation/PaymentPluginValidator.php b/app/http/admin/validation/PaymentPluginValidator.php index d7995e8..ab0a826 100644 --- a/app/http/admin/validation/PaymentPluginValidator.php +++ b/app/http/admin/validation/PaymentPluginValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class PaymentPluginValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'code' => 'sometimes|string|alpha_dash|min:2|max:32', 'status' => 'sometimes|integer|in:0,1', @@ -23,10 +28,15 @@ class PaymentPluginValidator extends Validator 'ids' => 'sometimes|array', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'code' => '插件编码', 'name' => '插件名称', - 'status' => '状态', + 'status' => '插件状态', 'remark' => '备注', 'keyword' => '关键字', 'page' => '页码', @@ -35,6 +45,11 @@ class PaymentPluginValidator extends Validator 'ids' => '插件编码集合', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'code', 'name', 'status', 'page', 'page_size'], 'update' => ['code', 'status', 'remark'], @@ -43,3 +58,4 @@ class PaymentPluginValidator extends Validator 'selectOptions' => ['keyword', 'page', 'page_size', 'pay_type_code', 'ids'], ]; } + diff --git a/app/http/admin/validation/PaymentPollGroupBindValidator.php b/app/http/admin/validation/PaymentPollGroupBindValidator.php index 61de2c5..f01e695 100644 --- a/app/http/admin/validation/PaymentPollGroupBindValidator.php +++ b/app/http/admin/validation/PaymentPollGroupBindValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class PaymentPollGroupBindValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'sometimes|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -23,18 +28,28 @@ class PaymentPollGroupBindValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '绑定ID', 'keyword' => '关键字', 'merchant_group_id' => '商户分组', 'pay_type_id' => '支付方式', 'poll_group_id' => '轮询组', - 'status' => '状态', + 'status' => '分组绑定状态', 'remark' => '备注', 'page' => '页码', 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'merchant_group_id', 'pay_type_id', 'poll_group_id', 'status', 'page', 'page_size'], 'store' => ['merchant_group_id', 'pay_type_id', 'poll_group_id', 'status', 'remark'], @@ -43,6 +58,11 @@ class PaymentPollGroupBindValidator extends Validator 'destroy' => ['id'], ]; + /** + * 根据场景返回轮询组绑定校验规则。 + * + * @return array 校验规则 + */ public function rules(): array { $rules = parent::rules(); diff --git a/app/http/admin/validation/PaymentPollGroupChannelValidator.php b/app/http/admin/validation/PaymentPollGroupChannelValidator.php index 8b436d8..c898765 100644 --- a/app/http/admin/validation/PaymentPollGroupChannelValidator.php +++ b/app/http/admin/validation/PaymentPollGroupChannelValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class PaymentPollGroupChannelValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'sometimes|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -25,12 +30,17 @@ class PaymentPollGroupChannelValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '编排ID', 'keyword' => '关键字', 'poll_group_id' => '轮询组', 'channel_id' => '支付通道', - 'status' => '状态', + 'status' => '通道编排状态', 'sort_no' => '排序', 'weight' => '权重', 'is_default' => '默认通道', @@ -39,6 +49,11 @@ class PaymentPollGroupChannelValidator extends Validator 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'poll_group_id', 'channel_id', 'status', 'page', 'page_size'], 'store' => ['poll_group_id', 'channel_id', 'sort_no', 'weight', 'is_default', 'status', 'remark'], @@ -48,6 +63,11 @@ class PaymentPollGroupChannelValidator extends Validator 'destroy' => ['id'], ]; + /** + * 根据场景返回轮询组通道校验规则。 + * + * @return array 校验规则 + */ public function rules(): array { $rules = parent::rules(); diff --git a/app/http/admin/validation/PaymentPollGroupValidator.php b/app/http/admin/validation/PaymentPollGroupValidator.php index 2e5ca70..39d2308 100644 --- a/app/http/admin/validation/PaymentPollGroupValidator.php +++ b/app/http/admin/validation/PaymentPollGroupValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class PaymentPollGroupValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'sometimes|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -23,18 +28,28 @@ class PaymentPollGroupValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '轮询组ID', 'keyword' => '关键字', 'group_name' => '轮询组名称', 'pay_type_id' => '支付方式', 'route_mode' => '路由模式', - 'status' => '状态', + 'status' => '轮询组状态', 'remark' => '备注', 'page' => '页码', 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'group_name', 'pay_type_id', 'route_mode', 'status', 'page', 'page_size'], 'store' => ['group_name', 'pay_type_id', 'route_mode', 'status', 'remark'], @@ -44,6 +59,11 @@ class PaymentPollGroupValidator extends Validator 'destroy' => ['id'], ]; + /** + * 根据场景返回支付轮询组校验规则。 + * + * @return array 校验规则 + */ public function rules(): array { $rules = parent::rules(); @@ -73,3 +93,6 @@ class PaymentPollGroupValidator extends Validator }; } } + + + diff --git a/app/http/admin/validation/PaymentTypeValidator.php b/app/http/admin/validation/PaymentTypeValidator.php index 709c665..6bc4648 100644 --- a/app/http/admin/validation/PaymentTypeValidator.php +++ b/app/http/admin/validation/PaymentTypeValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class PaymentTypeValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'id' => 'sometimes|integer|min:1', 'keyword' => 'sometimes|string|max:128', @@ -24,6 +29,11 @@ class PaymentTypeValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'id' => '支付方式ID', 'keyword' => '关键字', @@ -31,12 +41,17 @@ class PaymentTypeValidator extends Validator 'name' => '支付方式名称', 'icon' => '图标', 'sort_no' => '排序', - 'status' => '状态', + 'status' => '支付方式状态', 'remark' => '备注', 'page' => '页码', 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'code', 'name', 'status', 'page', 'page_size'], 'store' => ['code', 'name', 'icon', 'sort_no', 'status', 'remark'], @@ -46,6 +61,11 @@ class PaymentTypeValidator extends Validator 'destroy' => ['id'], ]; + /** + * 根据场景返回支付类型校验规则。 + * + * @return array 校验规则 + */ public function rules(): array { $rules = parent::rules(); @@ -73,3 +93,4 @@ class PaymentTypeValidator extends Validator }; } } + diff --git a/app/http/admin/validation/RefundActionValidator.php b/app/http/admin/validation/RefundActionValidator.php index f73aae3..648a48a 100644 --- a/app/http/admin/validation/RefundActionValidator.php +++ b/app/http/admin/validation/RefundActionValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class RefundActionValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'refund_no' => 'required|string|max:64', 'processing_at' => 'sometimes|date_format:Y-m-d H:i:s', @@ -19,6 +24,11 @@ class RefundActionValidator extends Validator 'channel_refund_no' => 'sometimes|string|max:64', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'refund_no' => '退款单号', 'processing_at' => '处理时间', @@ -27,6 +37,11 @@ class RefundActionValidator extends Validator 'channel_refund_no' => '渠道退款单号', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'retry' => ['refund_no', 'processing_at'], 'mark_fail' => ['refund_no', 'failed_at', 'last_error'], @@ -34,3 +49,5 @@ class RefundActionValidator extends Validator 'mark_success' => ['refund_no', 'channel_refund_no'], ]; } + + diff --git a/app/http/admin/validation/RefundOrderValidator.php b/app/http/admin/validation/RefundOrderValidator.php index a676959..f444770 100644 --- a/app/http/admin/validation/RefundOrderValidator.php +++ b/app/http/admin/validation/RefundOrderValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class RefundOrderValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'keyword' => 'sometimes|string|max:128', 'merchant_id' => 'sometimes|integer|min:1', @@ -21,6 +26,11 @@ class RefundOrderValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'keyword' => '关键字', 'merchant_id' => '商户ID', @@ -31,7 +41,14 @@ class RefundOrderValidator extends Validator 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'merchant_id', 'pay_type_id', 'status', 'channel_mode', 'page', 'page_size'], ]; } + + diff --git a/app/http/admin/validation/RouteResolveValidator.php b/app/http/admin/validation/RouteResolveValidator.php index abf7d76..a6e4d49 100644 --- a/app/http/admin/validation/RouteResolveValidator.php +++ b/app/http/admin/validation/RouteResolveValidator.php @@ -7,10 +7,15 @@ use support\validation\Validator; /** * 路由解析参数校验器。 * - * 仅供管理后台预览路由使用。 + * 用于校验管理后台路由解析所需参数。 */ class RouteResolveValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'merchant_group_id' => 'required|integer|min:1', 'pay_type_id' => 'required|integer|min:1', @@ -20,6 +25,11 @@ class RouteResolveValidator extends Validator 'stat_date' => 'sometimes|date_format:Y-m-d', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'merchant_group_id' => '商户分组', 'pay_type_id' => '支付方式', @@ -29,7 +39,13 @@ class RouteResolveValidator extends Validator 'stat_date' => '统计日期', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'resolve' => ['merchant_group_id', 'pay_type_id', 'pay_amount', 'pay_type_code', 'channel_mode', 'stat_date'], ]; } + diff --git a/app/http/admin/validation/SettlementOrderValidator.php b/app/http/admin/validation/SettlementOrderValidator.php index e008dc2..623253b 100644 --- a/app/http/admin/validation/SettlementOrderValidator.php +++ b/app/http/admin/validation/SettlementOrderValidator.php @@ -9,6 +9,11 @@ use support\validation\Validator; */ class SettlementOrderValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'settle_no' => 'required|string|max:32', 'keyword' => 'sometimes|string|max:128', @@ -20,19 +25,30 @@ class SettlementOrderValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'settle_no' => '清算单号', 'keyword' => '关键词', 'merchant_id' => '所属商户', 'channel_id' => '所属通道', - 'status' => '状态', + 'status' => '清算单状态', 'cycle_type' => '结算周期类型', 'page' => '页码', 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'merchant_id', 'channel_id', 'status', 'cycle_type', 'page', 'page_size'], 'show' => ['settle_no'], ]; } + diff --git a/app/http/admin/validation/SystemConfigPageValidator.php b/app/http/admin/validation/SystemConfigPageValidator.php index 3f79f9f..ae5802d 100644 --- a/app/http/admin/validation/SystemConfigPageValidator.php +++ b/app/http/admin/validation/SystemConfigPageValidator.php @@ -4,20 +4,40 @@ namespace app\http\admin\validation; use support\validation\Validator; +/** + * 系统配置页面校验器 + */ class SystemConfigPageValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'group_code' => 'required|string|min:1|max:50|regex:/^[a-z0-9_]+$/', 'values' => 'required|array', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'group_code' => '配置分组', 'values' => '配置值', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'show' => ['group_code'], 'store' => ['group_code', 'values'], ]; } + + diff --git a/app/http/api/controller/adapter/EpayController.php b/app/http/api/controller/adapter/EpayController.php index 57a041f..282b7a0 100644 --- a/app/http/api/controller/adapter/EpayController.php +++ b/app/http/api/controller/adapter/EpayController.php @@ -12,12 +12,17 @@ use support\Response; /** * Epay 协议兼容控制器。 * - * 负责 submit.php、mapi.php 和 api.php 的入口场景校验与结果分发。 + * 负责兼容入口场景的校验与结果分发。 + * + * @property EpayCompatService $epayCompatService Epay 兼容服务 */ class EpayController extends BaseController { /** - * 构造函数,注入兼容服务。 + * 构造方法。 + * + * @param EpayCompatService $epayCompatService Epay 兼容服务 + * @return void */ public function __construct( protected EpayCompatService $epayCompatService @@ -25,6 +30,9 @@ class EpayController extends BaseController /** * 页面跳转支付入口。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function submit(Request $request): Response { @@ -47,6 +55,9 @@ class EpayController extends BaseController /** * API 接口支付入口。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function mapi(Request $request): Response { @@ -69,6 +80,9 @@ class EpayController extends BaseController /** * 标准 API 接口入口。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function api(Request $request): Response { @@ -98,3 +112,8 @@ class EpayController extends BaseController } } } + + + + + diff --git a/app/http/api/controller/notify/NotifyController.php b/app/http/api/controller/notify/NotifyController.php index 52a4338..9d59460 100644 --- a/app/http/api/controller/notify/NotifyController.php +++ b/app/http/api/controller/notify/NotifyController.php @@ -10,14 +10,19 @@ use support\Request; use support\Response; /** - * 通知与回调记录控制器。 + * 通知记录控制器。 * - * 负责渠道通知日志和商户通知任务的接入。 + * 负责渠道通知日志和商户通知任务的接入,不承担真实业务回调处理。 + * + * @property NotifyService $notifyService 通知服务 */ class NotifyController extends BaseController { /** - * 构造函数,注入通知服务。 + * 构造方法。 + * + * @param NotifyService $notifyService 通知服务 + * @return void */ public function __construct( protected NotifyService $notifyService @@ -25,9 +30,12 @@ class NotifyController extends BaseController } /** - * POST /api/notify/channel - * * 记录渠道通知日志。 + * + * 用于保存外部渠道发来的通知原文,便于后续排查和审计。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function channel(Request $request): Response { @@ -37,9 +45,12 @@ class NotifyController extends BaseController } /** - * POST /api/notify/merchant - * * 创建商户通知任务。 + * + * 由支付或结算完成后触发,把通知任务交给异步通知链路处理。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function merchant(Request $request): Response { @@ -49,3 +60,8 @@ class NotifyController extends BaseController } } + + + + + diff --git a/app/http/api/controller/route/RouteController.php b/app/http/api/controller/route/RouteController.php index 84c3a0a..4f7e461 100644 --- a/app/http/api/controller/route/RouteController.php +++ b/app/http/api/controller/route/RouteController.php @@ -9,14 +9,19 @@ use support\Request; use support\Response; /** - * 路由预览控制器。 + * 路由解析控制器。 * - * 用于返回指定商户分组、支付方式和金额条件下的路由解析结果。 + * 用于根据商户分组、支付方式和金额条件返回路由候选与最终选中通道。 + * + * @property PaymentRouteService $paymentRouteService 支付路由服务 */ class RouteController extends BaseController { /** - * 构造函数,注入路由服务。 + * 构造方法。 + * + * @param PaymentRouteService $paymentRouteService 支付路由服务 + * @return void */ public function __construct( protected PaymentRouteService $paymentRouteService @@ -24,9 +29,12 @@ class RouteController extends BaseController } /** - * GET /api/routes/resolve - * * 解析支付路由。 + * + * 这个接口会返回当前条件下的候选通道和最终命中的通道信息,通常用于下单前查看结果。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function resolve(Request $request): Response { @@ -41,3 +49,8 @@ class RouteController extends BaseController } } + + + + + diff --git a/app/http/api/controller/settlement/SettlementController.php b/app/http/api/controller/settlement/SettlementController.php index 6ddc3d7..2450331 100644 --- a/app/http/api/controller/settlement/SettlementController.php +++ b/app/http/api/controller/settlement/SettlementController.php @@ -13,12 +13,17 @@ use support\Response; /** * 清算接口控制器。 * - * 负责清算单创建、查询和清算状态推进。 + * 负责清算单创建、查询和清算终态推进。 + * + * @property SettlementService $settlementService 结算服务 */ class SettlementController extends BaseController { /** - * 构造函数,注入清算相关依赖。 + * 构造方法。 + * + * @param SettlementService $settlementService 结算服务 + * @return void */ public function __construct( protected SettlementService $settlementService, @@ -26,7 +31,12 @@ class SettlementController extends BaseController } /** - * 创建清结算单。 + * 创建清算单。 + * + * 会把传入的清算明细和汇总一起交给清算生命周期服务落库。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function create(Request $request): Response { @@ -37,7 +47,13 @@ class SettlementController extends BaseController } /** - * 查询清结算单详情。 + * 查询清算单详情。 + * + * 用于查看批次金额、状态和关联支付单明细。 + * + * @param Request $request 请求对象 + * @param string $settleNo 结算单号 + * @return Response 响应对象 */ public function show(Request $request, string $settleNo): Response { @@ -49,7 +65,13 @@ class SettlementController extends BaseController } /** - * 标记清结算成功。 + * 标记清算成功。 + * + * 会触发商户余额入账,并同步清算单、清算明细和关联支付单状态。 + * + * @param Request $request 请求对象 + * @param string $settleNo 结算单号 + * @return Response 响应对象 */ public function complete(Request $request, string $settleNo): Response { @@ -63,7 +85,13 @@ class SettlementController extends BaseController } /** - * 标记清结算失败。 + * 标记清算失败。 + * + * 仅在清算批次未成功入账时使用,用于把批次推进到失败终态并保留原因。 + * + * @param Request $request 请求对象 + * @param string $settleNo 结算单号 + * @return Response 响应对象 */ public function failSettlement(Request $request, string $settleNo): Response { @@ -76,3 +104,7 @@ class SettlementController extends BaseController return $this->success($this->settlementService->failSettlement($settleNo, (string) ($data['reason'] ?? ''))); } } + + + + diff --git a/app/http/api/controller/trace/TraceController.php b/app/http/api/controller/trace/TraceController.php index 9377d06..c89d221 100644 --- a/app/http/api/controller/trace/TraceController.php +++ b/app/http/api/controller/trace/TraceController.php @@ -10,9 +10,17 @@ use support\Response; /** * 统一追踪查询控制器。 + * + * @property TradeTraceService $tradeTraceService 交易追踪服务 */ class TraceController extends BaseController { + /** + * 构造方法。 + * + * @param TradeTraceService $tradeTraceService 交易追踪服务 + * @return void + */ public function __construct( protected TradeTraceService $tradeTraceService ) { @@ -20,6 +28,10 @@ class TraceController extends BaseController /** * 查询指定追踪号对应的完整交易链路。 + * + * @param Request $request 请求对象 + * @param string $traceNo 追踪号 + * @return Response 响应对象 */ public function show(Request $request, string $traceNo): Response { @@ -37,3 +49,8 @@ class TraceController extends BaseController return $this->success($result); } } + + + + + diff --git a/app/http/api/controller/trade/PayController.php b/app/http/api/controller/trade/PayController.php index 3d240ec..08e981c 100644 --- a/app/http/api/controller/trade/PayController.php +++ b/app/http/api/controller/trade/PayController.php @@ -15,14 +15,23 @@ use support\Request; use support\Response; /** - * 支付接口控制器。 + * 收银台支付接口控制器。 * - * 负责支付预下单、支付查询、支付关闭和渠道回调入口。 + * 负责支付预下单、支付单查询、支付关闭、超时收口和渠道回调入口。 + * + * @property PayOrderService $payOrderService 支付订单服务 + * @property MerchantApiCredentialService $merchantApiCredentialService 商户 API 凭证服务 + * @property PaymentTypeService $paymentTypeService 支付方式服务 */ class PayController extends BaseController { /** - * 构造函数,注入支付单相关依赖。 + * 构造方法。 + * + * @param PayOrderService $payOrderService 支付订单服务 + * @param MerchantApiCredentialService $merchantApiCredentialService 商户 API 凭证服务 + * @param PaymentTypeService $paymentTypeService 支付类型服务 + * @return void */ public function __construct( protected PayOrderService $payOrderService, @@ -32,7 +41,12 @@ class PayController extends BaseController } /** - * 支付预下单。 + * 创建支付预下单并返回支付尝试结果。 + * + * 先对外部支付参数完成验签和归一化,再交给支付单尝试服务选择路由并创建支付单。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function prepare(Request $request): Response { @@ -47,6 +61,12 @@ class PayController extends BaseController /** * 查询支付单详情。 + * + * 用于前端轮询支付结果或展示支付单当前状态。 + * + * @param Request $request 请求对象 + * @param string $payNo 支付单号 + * @return Response 响应对象 */ public function show(Request $request, string $payNo): Response { @@ -59,6 +79,12 @@ class PayController extends BaseController /** * 关闭支付单。 + * + * 仅对尚未完成的支付单生效,通常由业务系统在用户主动取消时调用。 + * + * @param Request $request 请求对象 + * @param string $payNo 支付单号 + * @return Response 响应对象 */ public function close(Request $request, string $payNo): Response { @@ -73,6 +99,12 @@ class PayController extends BaseController /** * 标记支付单超时。 + * + * 用于订单到达超时时间后的状态收口,后续由生命周期服务统一处理手续费释放和订单同步。 + * + * @param Request $request 请求对象 + * @param string $payNo 支付单号 + * @return Response 响应对象 */ public function timeout(Request $request, string $payNo): Response { @@ -86,7 +118,14 @@ class PayController extends BaseController } /** - * 处理渠道回调。 + * 处理支付回调。 + * + * 当路径里携带 `payNo` 时,进入第三方插件回调链路; + * 当未携带 `payNo` 时,按平台统一回调载荷进入渠道回调处理。 + * + * @param Request $request 请求对象 + * @param string $payNo 支付单号 + * @return Response|string 字符串或响应对象 */ public function callback(Request $request, string $payNo = ''): Response|string { @@ -103,6 +142,13 @@ class PayController extends BaseController * 归一化外部支付下单参数并完成签名校验。 * * 这层逻辑保留在控制器内,避免中间件承担业务验签职责。 + * 同时把外部字段映射为系统内部支付单入参,并将回调基址写入扩展信息。 + * + * @param Request $request 请求对象 + * @param array $payload 请求载荷 + * @return array 支付下单参数 + * @throws \app\exception\ResourceNotFoundException + * @throws \app\exception\ValidationException */ private function normalizePreparePayload(Request $request, array $payload): array { @@ -113,6 +159,7 @@ class PayController extends BaseController $typeCode = (string) $paymentType->code; $money = (string) ($payload['money'] ?? '0'); + // 外部协议按“元”传金额,系统内部统一转成“分”存储和计算。 $amount = (int) round(((float) $money) * 100); return [ @@ -130,9 +177,16 @@ class PayController extends BaseController 'clientip' => (string) ($payload['clientip'] ?? ''), 'device' => (string) ($payload['device'] ?? ''), 'sign_type' => (string) ($payload['sign_type'] ?? 'MD5'), + // 回调基址会被插件和支付单后续流程复用。 'channel_callback_base_url' => (string) sys_config('site_url') . '/api/pay', ], ]; } } + + + + + + diff --git a/app/http/api/controller/trade/RefundController.php b/app/http/api/controller/trade/RefundController.php index 86d4b17..7cc1e69 100644 --- a/app/http/api/controller/trade/RefundController.php +++ b/app/http/api/controller/trade/RefundController.php @@ -13,12 +13,17 @@ use support\Response; /** * 退款接口控制器。 * - * 负责退款单创建与退款单查询。 + * 负责退款单创建、查询和终态推进入口。 + * + * @property RefundService $refundService 退款服务 */ class RefundController extends BaseController { /** - * 构造函数,注入退款相关依赖。 + * 构造方法。 + * + * @param RefundService $refundService 退款服务 + * @return void */ public function __construct( protected RefundService $refundService, @@ -27,6 +32,11 @@ class RefundController extends BaseController /** * 创建退款单。 + * + * 以原支付单为基准创建退款申请,后续由退款生命周期服务推进处理中、成功或失败状态。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function create(Request $request): Response { @@ -37,6 +47,12 @@ class RefundController extends BaseController /** * 查询退款单详情。 + * + * 用于退款进度展示和后台对账。 + * + * @param Request $request 请求对象 + * @param string $refundNo 退款单号 + * @return Response 响应对象 */ public function show(Request $request, string $refundNo): Response { @@ -49,6 +65,12 @@ class RefundController extends BaseController /** * 标记退款处理中。 + * + * 由渠道侧或任务调度侧在退款请求已经受理后调用,用于推进退款状态机。 + * + * @param Request $request 请求对象 + * @param string $refundNo 退款单号 + * @return Response 响应对象 */ public function processing(Request $request, string $refundNo): Response { @@ -63,6 +85,12 @@ class RefundController extends BaseController /** * 退款重试。 + * + * 仅用于失败态退款单重新发起处理,保持退款链路幂等和可恢复。 + * + * @param Request $request 请求对象 + * @param string $refundNo 退款单号 + * @return Response 响应对象 */ public function retry(Request $request, string $refundNo): Response { @@ -77,6 +105,12 @@ class RefundController extends BaseController /** * 标记退款失败。 + * + * 用于把退款单推进到终态失败,并记录失败原因供运营和对账排查。 + * + * @param Request $request 请求对象 + * @param string $refundNo 退款单号 + * @return Response 响应对象 */ public function markFail(Request $request, string $refundNo): Response { @@ -91,3 +125,8 @@ class RefundController extends BaseController } + + + + + diff --git a/app/http/api/validation/EpayValidator.php b/app/http/api/validation/EpayValidator.php index a9245cd..2c3c864 100644 --- a/app/http/api/validation/EpayValidator.php +++ b/app/http/api/validation/EpayValidator.php @@ -7,10 +7,15 @@ use support\validation\Validator; /** * ePay 兼容层请求校验器。 * - * 根据 submit、mapi、api.php 的不同入口场景做分场景校验,不依赖隐藏标记字段。 + * 用于校验兼容层不同入口的请求参数。 */ class EpayValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'act' => 'required|string|in:query,settle,order,orders,refund', 'pid' => 'required|integer|gt:0', @@ -33,6 +38,11 @@ class EpayValidator extends Validator 'page' => 'sometimes|integer|gt:0', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'act' => '操作类型', 'pid' => '商户ID', @@ -55,6 +65,11 @@ class EpayValidator extends Validator 'page' => '页码', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'submit' => ['pid', 'type', 'out_trade_no', 'notify_url', 'return_url', 'name', 'money', 'sign', 'sign_type', 'param'], 'mapi' => ['pid', 'type', 'out_trade_no', 'notify_url', 'return_url', 'name', 'money', 'clientip', 'device', 'sign', 'sign_type', 'param'], @@ -67,6 +82,11 @@ class EpayValidator extends Validator 'refund_out_trade_no' => ['act', 'pid', 'key', 'out_trade_no', 'money', 'refund_no', 'reason'], ]; + /** + * 根据场景返回 ePay 兼容层校验规则。 + * + * @return array 校验规则 + */ public function rules(): array { $rules = parent::rules(); @@ -111,3 +131,4 @@ class EpayValidator extends Validator }; } } + diff --git a/app/http/api/validation/NotifyChannelValidator.php b/app/http/api/validation/NotifyChannelValidator.php index f96ed6f..cb143fb 100644 --- a/app/http/api/validation/NotifyChannelValidator.php +++ b/app/http/api/validation/NotifyChannelValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class NotifyChannelValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'notify_no' => 'sometimes|string|min:1|max:64', 'channel_id' => 'required|integer|min:1|exists:ma_payment_channel,id', @@ -27,6 +32,11 @@ class NotifyChannelValidator extends Validator 'last_error' => 'nullable|string|max:255', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'notify_no' => '通知单号', 'channel_id' => '通道ID', @@ -43,7 +53,14 @@ class NotifyChannelValidator extends Validator 'last_error' => '最后错误', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'store' => ['notify_no', 'channel_id', 'notify_type', 'biz_no', 'pay_no', 'channel_request_no', 'channel_trade_no', 'raw_payload', 'verify_status', 'process_status', 'retry_count', 'next_retry_at', 'last_error'], ]; } + + diff --git a/app/http/api/validation/NotifyMerchantValidator.php b/app/http/api/validation/NotifyMerchantValidator.php index 66bb4ed..f5ff844 100644 --- a/app/http/api/validation/NotifyMerchantValidator.php +++ b/app/http/api/validation/NotifyMerchantValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class NotifyMerchantValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'notify_no' => 'sometimes|string|min:1|max:64', 'merchant_id' => 'required|integer|min:1|exists:ma_merchant,id', @@ -26,6 +31,11 @@ class NotifyMerchantValidator extends Validator 'last_response' => 'nullable|string|max:255', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'notify_no' => '通知单号', 'merchant_id' => '商户ID', @@ -34,14 +44,21 @@ class NotifyMerchantValidator extends Validator 'pay_no' => '支付单号', 'notify_url' => '通知地址', 'notify_data' => '通知内容', - 'status' => '状态', + 'status' => '通知任务状态', 'retry_count' => '重试次数', 'next_retry_at' => '下次重试时间', 'last_notify_at' => '最后通知时间', 'last_response' => '最后响应', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'store' => ['notify_no', 'merchant_id', 'merchant_group_id', 'biz_no', 'pay_no', 'notify_url', 'notify_data', 'status', 'retry_count', 'next_retry_at', 'last_notify_at', 'last_response'], ]; } + + diff --git a/app/http/api/validation/PayCallbackValidator.php b/app/http/api/validation/PayCallbackValidator.php index b484892..c6e0599 100644 --- a/app/http/api/validation/PayCallbackValidator.php +++ b/app/http/api/validation/PayCallbackValidator.php @@ -7,10 +7,15 @@ use support\validation\Validator; /** * 支付回调参数校验器。 * - * 用于校验渠道回调和主动查单回传参数。 + * 用于校验渠道回调和回调模拟入参。 */ class PayCallbackValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'pay_no' => 'required|string|min:1|max:64|exists:ma_pay_order,pay_no', 'success' => 'required|boolean', @@ -29,15 +34,20 @@ class PayCallbackValidator extends Validator 'ext_json' => 'nullable|array', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'pay_no' => '支付单号', - 'success' => '是否成功', + 'success' => '支付是否成功', 'channel_id' => '通道ID', 'callback_type' => '回调类型', 'request_data' => '原始回调数据', 'verify_status' => '验签状态', 'process_status' => '处理状态', - 'process_result' => '处理结果', + 'process_result' => '处理详情', 'channel_trade_no' => '渠道交易号', 'channel_order_no' => '渠道订单号', 'fee_actual_amount' => '实际手续费', @@ -47,7 +57,14 @@ class PayCallbackValidator extends Validator 'ext_json' => '扩展信息', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'callback' => ['pay_no', 'success', 'channel_id', 'callback_type', 'request_data', 'verify_status', 'process_status', 'process_result', 'channel_trade_no', 'channel_order_no', 'fee_actual_amount', 'paid_at', 'channel_error_code', 'channel_error_msg', 'ext_json'], ]; } + + diff --git a/app/http/api/validation/PayCloseValidator.php b/app/http/api/validation/PayCloseValidator.php index 81a02da..7b2dc36 100644 --- a/app/http/api/validation/PayCloseValidator.php +++ b/app/http/api/validation/PayCloseValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class PayCloseValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'pay_no' => 'required|string|min:1|max:64|exists:ma_pay_order,pay_no', 'reason' => 'nullable|string|max:255', @@ -18,6 +23,11 @@ class PayCloseValidator extends Validator 'ext_json' => 'nullable|array', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'pay_no' => '支付单号', 'reason' => '关闭原因', @@ -25,7 +35,14 @@ class PayCloseValidator extends Validator 'ext_json' => '扩展信息', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'close' => ['pay_no', 'reason', 'closed_at', 'ext_json'], ]; } + + diff --git a/app/http/api/validation/PayPrepareValidator.php b/app/http/api/validation/PayPrepareValidator.php index 24ac9fb..10300bd 100644 --- a/app/http/api/validation/PayPrepareValidator.php +++ b/app/http/api/validation/PayPrepareValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class PayPrepareValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'merchant_id' => 'required|integer|min:1|exists:ma_merchant,id', 'merchant_order_no' => 'required|string|min:1|max:64', @@ -21,6 +26,11 @@ class PayPrepareValidator extends Validator 'ext_json' => 'nullable|array', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'merchant_id' => '商户ID', 'merchant_order_no' => '商户订单号', @@ -31,7 +41,14 @@ class PayPrepareValidator extends Validator 'ext_json' => '扩展信息', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'prepare' => ['merchant_id', 'merchant_order_no', 'pay_type_id', 'pay_amount', 'subject', 'body', 'ext_json'], ]; } + + diff --git a/app/http/api/validation/PayTimeoutValidator.php b/app/http/api/validation/PayTimeoutValidator.php index 99f50b3..7133446 100644 --- a/app/http/api/validation/PayTimeoutValidator.php +++ b/app/http/api/validation/PayTimeoutValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class PayTimeoutValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'pay_no' => 'required|string|min:1|max:64|exists:ma_pay_order,pay_no', 'reason' => 'nullable|string|max:255', @@ -18,6 +23,11 @@ class PayTimeoutValidator extends Validator 'ext_json' => 'nullable|array', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'pay_no' => '支付单号', 'reason' => '超时原因', @@ -25,7 +35,14 @@ class PayTimeoutValidator extends Validator 'ext_json' => '扩展信息', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'timeout' => ['pay_no', 'reason', 'timeout_at', 'ext_json'], ]; } + + diff --git a/app/http/api/validation/RefundActionValidator.php b/app/http/api/validation/RefundActionValidator.php index 8e550c0..03ec037 100644 --- a/app/http/api/validation/RefundActionValidator.php +++ b/app/http/api/validation/RefundActionValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class RefundActionValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'refund_no' => 'required|string|min:1|max:64|exists:ma_refund_order,refund_no', 'reason' => 'nullable|string|max:255', @@ -20,6 +25,11 @@ class RefundActionValidator extends Validator 'ext_json' => 'nullable|array', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'refund_no' => '退款单号', 'reason' => '原因', @@ -29,9 +39,16 @@ class RefundActionValidator extends Validator 'ext_json' => '扩展信息', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'processing' => ['refund_no', 'reason', 'last_error', 'processing_at', 'ext_json'], 'retry' => ['refund_no', 'reason', 'last_error', 'processing_at', 'ext_json'], 'fail' => ['refund_no', 'reason', 'last_error', 'failed_at', 'ext_json'], ]; } + + diff --git a/app/http/api/validation/RefundCreateValidator.php b/app/http/api/validation/RefundCreateValidator.php index a8d408b..c587a4c 100644 --- a/app/http/api/validation/RefundCreateValidator.php +++ b/app/http/api/validation/RefundCreateValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class RefundCreateValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'pay_no' => 'required|string|min:1|max:64|exists:ma_pay_order,pay_no', 'merchant_refund_no' => 'sometimes|string|min:1|max:64', @@ -19,6 +24,11 @@ class RefundCreateValidator extends Validator 'ext_json' => 'nullable|array', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'pay_no' => '支付单号', 'merchant_refund_no' => '商户退款单号', @@ -27,7 +37,14 @@ class RefundCreateValidator extends Validator 'ext_json' => '扩展信息', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'store' => ['pay_no', 'merchant_refund_no', 'refund_amount', 'reason', 'ext_json'], ]; } + + diff --git a/app/http/api/validation/RouteResolveValidator.php b/app/http/api/validation/RouteResolveValidator.php index b82d2fc..1a75fdc 100644 --- a/app/http/api/validation/RouteResolveValidator.php +++ b/app/http/api/validation/RouteResolveValidator.php @@ -7,10 +7,15 @@ use support\validation\Validator; /** * 路由解析参数校验器。 * - * 用于校验路由预览所需参数。 + * 用于校验路由解析所需参数。 */ class RouteResolveValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'merchant_group_id' => 'required|integer|min:1|exists:ma_merchant_group,id', 'pay_type_id' => 'required|integer|min:1|exists:ma_payment_type,id', @@ -18,6 +23,11 @@ class RouteResolveValidator extends Validator 'stat_date' => 'nullable|date_format:Y-m-d', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'merchant_group_id' => '商户分组ID', 'pay_type_id' => '支付方式', @@ -25,7 +35,14 @@ class RouteResolveValidator extends Validator 'stat_date' => '统计日期', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'resolve' => ['merchant_group_id', 'pay_type_id', 'pay_amount', 'stat_date'], ]; } + + diff --git a/app/http/api/validation/SettlementActionValidator.php b/app/http/api/validation/SettlementActionValidator.php index 562a3be..1a2dd1f 100644 --- a/app/http/api/validation/SettlementActionValidator.php +++ b/app/http/api/validation/SettlementActionValidator.php @@ -11,20 +11,37 @@ use support\validation\Validator; */ class SettlementActionValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'settle_no' => 'required|string|min:1|max:64|exists:ma_settlement_order,settle_no', 'reason' => 'nullable|string|max:255', 'ext_json' => 'nullable|array', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'settle_no' => '清算单号', 'reason' => '原因', 'ext_json' => '扩展信息', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'complete' => ['settle_no'], 'fail' => ['settle_no', 'reason', 'ext_json'], ]; } + + diff --git a/app/http/api/validation/SettlementCreateValidator.php b/app/http/api/validation/SettlementCreateValidator.php index f5d130d..69619ca 100644 --- a/app/http/api/validation/SettlementCreateValidator.php +++ b/app/http/api/validation/SettlementCreateValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class SettlementCreateValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'settle_no' => 'sometimes|string|min:1|max:64', 'merchant_id' => 'required|integer|min:1|exists:ma_merchant,id', @@ -38,6 +43,11 @@ class SettlementCreateValidator extends Validator 'items.*.item_status' => 'sometimes|integer|min:0', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'settle_no' => '清算单号', 'merchant_id' => '商户ID', @@ -45,7 +55,7 @@ class SettlementCreateValidator extends Validator 'channel_id' => '通道ID', 'cycle_type' => '结算周期类型', 'cycle_key' => '结算周期键', - 'status' => '状态', + 'status' => '清算单状态', 'generated_at' => '生成时间', 'accounted_amount' => '入账金额', 'gross_amount' => '交易总额', @@ -57,7 +67,14 @@ class SettlementCreateValidator extends Validator 'items' => '清算明细', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'store' => ['settle_no', 'merchant_id', 'merchant_group_id', 'channel_id', 'cycle_type', 'cycle_key', 'status', 'generated_at', 'accounted_amount', 'gross_amount', 'fee_amount', 'refund_amount', 'fee_reverse_amount', 'net_amount', 'ext_json', 'items', 'items.*.pay_no', 'items.*.refund_no', 'items.*.pay_amount', 'items.*.fee_amount', 'items.*.refund_amount', 'items.*.fee_reverse_amount', 'items.*.net_amount', 'items.*.item_status'], ]; } + + diff --git a/app/http/api/validation/TraceQueryValidator.php b/app/http/api/validation/TraceQueryValidator.php index 8e99f83..4a2e4c5 100644 --- a/app/http/api/validation/TraceQueryValidator.php +++ b/app/http/api/validation/TraceQueryValidator.php @@ -9,15 +9,32 @@ use support\validation\Validator; */ class TraceQueryValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'trace_no' => 'required|string|min:1|max:64', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'trace_no' => '追踪号', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'show' => ['trace_no'], ]; } + + diff --git a/app/http/mer/controller/account/AccountController.php b/app/http/mer/controller/account/AccountController.php index 1a47cfb..09c5125 100644 --- a/app/http/mer/controller/account/AccountController.php +++ b/app/http/mer/controller/account/AccountController.php @@ -13,11 +13,18 @@ use support\Response; * 商户账户控制器。 * * 负责商户余额查询等账户类接口。 + * + * @property MerchantService $merchantService 商户服务 + * @property MerchantAccountService $merchantAccountService 商户账户服务 */ class AccountController extends BaseController { /** - * 构造函数,注入商户与账户服务。 + * 构造方法。 + * + * @param MerchantService $merchantService 商户服务 + * @param MerchantAccountService $merchantAccountService 商户账户服务 + * @return void */ public function __construct( protected MerchantService $merchantService, @@ -26,9 +33,11 @@ class AccountController extends BaseController } /** - * GET /mer/merchant/{merchantNo}/balance - * * 查询商户余额。 + * + * @param Request $request 请求对象 + * @param string $merchantNo 商户号 + * @return Response 响应对象 */ public function balance(Request $request, string $merchantNo): Response { @@ -45,3 +54,8 @@ class AccountController extends BaseController } } + + + + + diff --git a/app/http/mer/controller/merchant/MerchantPortalController.php b/app/http/mer/controller/merchant/MerchantPortalController.php index 5da8914..e8771d3 100644 --- a/app/http/mer/controller/merchant/MerchantPortalController.php +++ b/app/http/mer/controller/merchant/MerchantPortalController.php @@ -12,9 +12,17 @@ use support\Response; * 商户后台基础页面控制器。 * * 统一承接当前商户可见的资料、通道、路由、凭证、清算和资金页面数据。 + * + * @property MerchantPortalService $merchantPortalService 商户门户服务 */ class MerchantPortalController extends BaseController { + /** + * 构造方法。 + * + * @param MerchantPortalService $merchantPortalService 商户门户服务 + * @return void + */ public function __construct( protected MerchantPortalService $merchantPortalService ) { @@ -22,6 +30,9 @@ class MerchantPortalController extends BaseController /** * 当前商户资料。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function profile(Request $request): Response { @@ -35,6 +46,9 @@ class MerchantPortalController extends BaseController /** * 更新当前商户资料。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function updateProfile(Request $request): Response { @@ -50,6 +64,9 @@ class MerchantPortalController extends BaseController /** * 修改当前商户登录密码。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function changePassword(Request $request): Response { @@ -65,6 +82,9 @@ class MerchantPortalController extends BaseController /** * 我的通道列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function myChannels(Request $request): Response { @@ -81,7 +101,10 @@ class MerchantPortalController extends BaseController } /** - * 路由预览。 + * 获取路由解析结果。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function routePreview(Request $request): Response { @@ -99,7 +122,10 @@ class MerchantPortalController extends BaseController } /** - * 当前商户接口凭证。 + * 当前商户 API 凭证。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function apiCredential(Request $request): Response { @@ -113,6 +139,9 @@ class MerchantPortalController extends BaseController /** * 生成或重置接口凭证。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function issueCredential(Request $request): Response { @@ -126,6 +155,9 @@ class MerchantPortalController extends BaseController /** * 当前商户的清算记录列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function settlementRecords(Request $request): Response { @@ -143,6 +175,10 @@ class MerchantPortalController extends BaseController /** * 当前商户的清算记录详情。 + * + * @param Request $request 请求对象 + * @param string $settleNo 结算单号 + * @return Response 响应对象 */ public function settlementRecordShow(Request $request, string $settleNo): Response { @@ -161,6 +197,9 @@ class MerchantPortalController extends BaseController /** * 当前商户可提现余额快照。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function withdrawableBalance(Request $request): Response { @@ -174,6 +213,9 @@ class MerchantPortalController extends BaseController /** * 当前商户资金流水列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function balanceFlows(Request $request): Response { @@ -189,3 +231,8 @@ class MerchantPortalController extends BaseController return $this->success($this->merchantPortalService->balanceFlows($payload, $merchantId, $page, $pageSize)); } } + + + + + diff --git a/app/http/mer/controller/system/AuthController.php b/app/http/mer/controller/system/AuthController.php index fc1b922..5f977c4 100644 --- a/app/http/mer/controller/system/AuthController.php +++ b/app/http/mer/controller/system/AuthController.php @@ -10,14 +10,28 @@ use support\Response; /** * 商户认证控制器。 + * + * @property MerchantAuthService $merchantAuthService 商户认证服务 */ class AuthController extends BaseController { + /** + * 构造方法。 + * + * @param MerchantAuthService $merchantAuthService 商户认证服务 + * @return void + */ public function __construct( protected MerchantAuthService $merchantAuthService ) { } + /** + * 商户登录。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function login(Request $request): Response { $data = $this->validated($request->all(), AuthValidator::class, 'login'); @@ -30,6 +44,12 @@ class AuthController extends BaseController )); } + /** + * 商户退出登录。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function logout(Request $request): Response { $token = trim((string) ($request->header('authorization', '') ?: $request->header('x-merchant-token', ''))); @@ -45,7 +65,10 @@ class AuthController extends BaseController } /** - * 获取当前登录商户的信息 + * 获取当前登录商户信息。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function profile(Request $request): Response { @@ -59,3 +82,8 @@ class AuthController extends BaseController } } + + + + + diff --git a/app/http/mer/controller/system/SystemController.php b/app/http/mer/controller/system/SystemController.php index 6d1f399..7afe310 100644 --- a/app/http/mer/controller/system/SystemController.php +++ b/app/http/mer/controller/system/SystemController.php @@ -9,22 +9,47 @@ use support\Response; /** * 商户后台系统数据控制器。 + * + * @property SystemBootstrapService $systemBootstrapService 系统引导服务 */ class SystemController extends BaseController { + /** + * 构造方法。 + * + * @param SystemBootstrapService $systemBootstrapService 系统引导服务 + * @return void + */ public function __construct( protected SystemBootstrapService $systemBootstrapService ) { } + /** + * 获取系统菜单树 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function menuTree(Request $request): Response { return $this->success($this->systemBootstrapService->getMenuTree('merchant')); } + /** + * 获取系统字典项 + * + * @param Request $request 请求对象 + * @return Response 响应对象 + */ public function dictItems(Request $request): Response { return $this->success($this->systemBootstrapService->getDictItems((string) $request->get('code', ''))); } } + + + + + diff --git a/app/http/mer/controller/trade/PayOrderController.php b/app/http/mer/controller/trade/PayOrderController.php index 7053e60..6669aac 100644 --- a/app/http/mer/controller/trade/PayOrderController.php +++ b/app/http/mer/controller/trade/PayOrderController.php @@ -12,11 +12,16 @@ use support\Response; * 商户后台支付订单控制器。 * * 商户后台只能看到当前登录商户自己的支付订单。 + * + * @property PayOrderService $payOrderService 支付订单服务 */ class PayOrderController extends BaseController { /** - * 构造函数,注入支付订单服务。 + * 构造方法。 + * + * @param PayOrderService $payOrderService 支付订单服务 + * @return void */ public function __construct( protected PayOrderService $payOrderService @@ -25,6 +30,9 @@ class PayOrderController extends BaseController /** * 查询当前商户的支付订单列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -42,3 +50,8 @@ class PayOrderController extends BaseController } } + + + + + diff --git a/app/http/mer/controller/trade/RefundOrderController.php b/app/http/mer/controller/trade/RefundOrderController.php index d82c236..8638886 100644 --- a/app/http/mer/controller/trade/RefundOrderController.php +++ b/app/http/mer/controller/trade/RefundOrderController.php @@ -11,9 +11,17 @@ use support\Response; /** * 商户后台退款订单控制器。 + * + * @property RefundService $refundService 退款服务 */ class RefundOrderController extends BaseController { + /** + * 构造方法。 + * + * @param RefundService $refundService 退款服务 + * @return void + */ public function __construct( protected RefundService $refundService ) { @@ -21,6 +29,9 @@ class RefundOrderController extends BaseController /** * 查询当前商户的退款订单列表。 + * + * @param Request $request 请求对象 + * @return Response 响应对象 */ public function index(Request $request): Response { @@ -39,6 +50,10 @@ class RefundOrderController extends BaseController /** * 查询当前商户的退款订单详情。 + * + * @param Request $request 请求对象 + * @param string $refundNo 退款单号 + * @return Response 响应对象 */ public function show(Request $request, string $refundNo): Response { @@ -52,6 +67,10 @@ class RefundOrderController extends BaseController /** * 重试当前商户的退款单。 + * + * @param Request $request 请求对象 + * @param string $refundNo 退款单号 + * @return Response 响应对象 */ public function retry(Request $request, string $refundNo): Response { @@ -70,3 +89,8 @@ class RefundOrderController extends BaseController } } + + + + + diff --git a/app/http/mer/middleware/MerchantAuthMiddleware.php b/app/http/mer/middleware/MerchantAuthMiddleware.php index ff47285..e3658de 100644 --- a/app/http/mer/middleware/MerchantAuthMiddleware.php +++ b/app/http/mer/middleware/MerchantAuthMiddleware.php @@ -12,11 +12,16 @@ use Webman\MiddlewareInterface; * 商户认证中间件。 * * 负责读取商户 token,并把商户身份写入请求上下文。 + * + * @property MerchantAuthService $merchantAuthService 商户认证服务 */ class MerchantAuthMiddleware implements MiddlewareInterface { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param MerchantAuthService $merchantAuthService 商户认证服务 + * @return void */ public function __construct( protected MerchantAuthService $merchantAuthService @@ -25,6 +30,10 @@ class MerchantAuthMiddleware implements MiddlewareInterface /** * 处理请求。 + * + * @param Request $request 请求对象 + * @param callable $handler handler + * @return Response 响应对象 */ public function process(Request $request, callable $handler): Response { @@ -61,3 +70,8 @@ class MerchantAuthMiddleware implements MiddlewareInterface } } + + + + + diff --git a/app/http/mer/validation/AuthValidator.php b/app/http/mer/validation/AuthValidator.php index d234876..64961da 100644 --- a/app/http/mer/validation/AuthValidator.php +++ b/app/http/mer/validation/AuthValidator.php @@ -9,17 +9,34 @@ use support\validation\Validator; */ class AuthValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'merchant_no' => 'required|string|min:1|max:32', 'password' => 'required|string|min:6|max:32', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'merchant_no' => '商户号', 'password' => '登录密码', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'login' => ['merchant_no', 'password'], ]; } + + diff --git a/app/http/mer/validation/BalanceValidator.php b/app/http/mer/validation/BalanceValidator.php index bae0730..fc991d3 100644 --- a/app/http/mer/validation/BalanceValidator.php +++ b/app/http/mer/validation/BalanceValidator.php @@ -11,15 +11,32 @@ use support\validation\Validator; */ class BalanceValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'merchant_no' => 'required|string|min:1|max:64', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'merchant_no' => '商户号', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'show' => ['merchant_no'], ]; } + + diff --git a/app/http/mer/validation/MerchantPortalValidator.php b/app/http/mer/validation/MerchantPortalValidator.php index 9a70d7c..096192e 100644 --- a/app/http/mer/validation/MerchantPortalValidator.php +++ b/app/http/mer/validation/MerchantPortalValidator.php @@ -9,6 +9,11 @@ use support\validation\Validator; */ class MerchantPortalValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'merchant_short_name' => 'sometimes|string|max:64', 'contact_name' => 'sometimes|string|max:64', @@ -26,6 +31,11 @@ class MerchantPortalValidator extends Validator 'stat_date' => 'sometimes|date', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'merchant_short_name' => '商户简称', 'contact_name' => '联系人', @@ -43,6 +53,11 @@ class MerchantPortalValidator extends Validator 'stat_date' => '统计日期', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'profileUpdate' => [ 'merchant_short_name', @@ -58,3 +73,5 @@ class MerchantPortalValidator extends Validator 'routePreview' => ['pay_type_id', 'pay_amount', 'stat_date'], ]; } + + diff --git a/app/http/mer/validation/PayOrderValidator.php b/app/http/mer/validation/PayOrderValidator.php index f6955e3..513e93e 100644 --- a/app/http/mer/validation/PayOrderValidator.php +++ b/app/http/mer/validation/PayOrderValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class PayOrderValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'keyword' => 'sometimes|string|max:128', 'merchant_id' => 'sometimes|integer|min:1', @@ -22,18 +27,29 @@ class PayOrderValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'keyword' => '关键字', 'merchant_id' => '商户ID', 'pay_type_id' => '支付方式', - 'status' => '状态', + 'status' => '支付单状态', 'channel_mode' => '通道模式', - 'callback_status' => '回调状态', + 'callback_status' => '回调处理状态', 'page' => '页码', 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'merchant_id', 'pay_type_id', 'status', 'channel_mode', 'callback_status', 'page', 'page_size'], ]; } + diff --git a/app/http/mer/validation/RefundActionValidator.php b/app/http/mer/validation/RefundActionValidator.php index 75157f3..f50b3eb 100644 --- a/app/http/mer/validation/RefundActionValidator.php +++ b/app/http/mer/validation/RefundActionValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class RefundActionValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'refund_no' => 'required|string|max:64', 'processing_at' => 'sometimes|date_format:Y-m-d H:i:s', @@ -19,6 +24,11 @@ class RefundActionValidator extends Validator 'channel_refund_no' => 'sometimes|string|max:64', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'refund_no' => '退款单号', 'processing_at' => '处理时间', @@ -27,6 +37,11 @@ class RefundActionValidator extends Validator 'channel_refund_no' => '渠道退款单号', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'retry' => ['refund_no', 'processing_at'], 'mark_fail' => ['refund_no', 'failed_at', 'last_error'], @@ -34,3 +49,5 @@ class RefundActionValidator extends Validator 'mark_success' => ['refund_no', 'channel_refund_no'], ]; } + + diff --git a/app/http/mer/validation/RefundOrderValidator.php b/app/http/mer/validation/RefundOrderValidator.php index b69ca9e..3f97db1 100644 --- a/app/http/mer/validation/RefundOrderValidator.php +++ b/app/http/mer/validation/RefundOrderValidator.php @@ -11,6 +11,11 @@ use support\validation\Validator; */ class RefundOrderValidator extends Validator { + /** + * 校验规则 + * + * @var array + */ protected array $rules = [ 'keyword' => 'sometimes|string|max:128', 'merchant_id' => 'sometimes|integer|min:1', @@ -21,6 +26,11 @@ class RefundOrderValidator extends Validator 'page_size' => 'sometimes|integer|min:1|max:100', ]; + /** + * 字段别名 + * + * @var array + */ protected array $attributes = [ 'keyword' => '关键字', 'merchant_id' => '商户ID', @@ -31,7 +41,14 @@ class RefundOrderValidator extends Validator 'page_size' => '每页条数', ]; + /** + * 校验场景 + * + * @var array + */ protected array $scenes = [ 'index' => ['keyword', 'merchant_id', 'pay_type_id', 'status', 'channel_mode', 'page', 'page_size'], ]; } + + diff --git a/app/listener/SystemConfigChangedListener.php b/app/listener/SystemConfigChangedListener.php index 33e00a1..bc3e27e 100644 --- a/app/listener/SystemConfigChangedListener.php +++ b/app/listener/SystemConfigChangedListener.php @@ -4,15 +4,37 @@ namespace app\listener; use app\service\system\config\SystemConfigRuntimeService; +/** + * 系统配置变更监听器。 + * + * @property SystemConfigRuntimeService $systemConfigRuntimeService 系统配置运行时服务 + */ class SystemConfigChangedListener { + /** + * 构造方法。 + * + * @param SystemConfigRuntimeService $systemConfigRuntimeService 系统配置运行时服务 + * @return void + */ public function __construct( protected SystemConfigRuntimeService $systemConfigRuntimeService ) { } + /** + * 刷新系统配置运行时缓存。 + * + * @param array $payload 请求载荷 + * @param string $eventName 事件名称 + * @return void + */ public function refreshRuntimeCache(array $payload = [], string $eventName = ''): void { $this->systemConfigRuntimeService->refresh(); } } + + + + diff --git a/app/model/admin/AdminUser.php b/app/model/admin/AdminUser.php index 9d0facb..5a3237d 100644 --- a/app/model/admin/AdminUser.php +++ b/app/model/admin/AdminUser.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class AdminUser extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_admin_user'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'username', 'password_hash', @@ -25,10 +35,20 @@ class AdminUser extends BaseModel 'remark', ]; + /** + * 隐藏字段 + * + * @var mixed + */ protected $hidden = [ 'password_hash', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'is_super' => 'integer', 'status' => 'integer', @@ -39,3 +59,5 @@ class AdminUser extends BaseModel } + + diff --git a/app/model/admin/ChannelDailyStat.php b/app/model/admin/ChannelDailyStat.php index e0f3e7b..aa08fef 100644 --- a/app/model/admin/ChannelDailyStat.php +++ b/app/model/admin/ChannelDailyStat.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class ChannelDailyStat extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_channel_daily_stat'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'merchant_id', 'merchant_group_id', @@ -27,6 +37,11 @@ class ChannelDailyStat extends BaseModel 'health_score', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'merchant_id' => 'integer', 'merchant_group_id' => 'integer', @@ -46,3 +61,5 @@ class ChannelDailyStat extends BaseModel } + + diff --git a/app/model/admin/ChannelNotifyLog.php b/app/model/admin/ChannelNotifyLog.php index 9100f7a..4ee1747 100644 --- a/app/model/admin/ChannelNotifyLog.php +++ b/app/model/admin/ChannelNotifyLog.php @@ -6,12 +6,22 @@ use app\common\base\BaseModel; /** * 渠道通知日志模型。 - * 用于记录异步通知、查单请求和去重处理结果。 + * 用于记录异步通知、查单请求和去重状态。 */ class ChannelNotifyLog extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_channel_notify_log'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'notify_no', 'channel_id', @@ -28,10 +38,20 @@ class ChannelNotifyLog extends BaseModel 'last_error', ]; + /** + * 隐藏字段 + * + * @var mixed + */ protected $hidden = [ 'raw_payload', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'channel_id' => 'integer', 'notify_type' => 'integer', @@ -45,3 +65,4 @@ class ChannelNotifyLog extends BaseModel } + diff --git a/app/model/admin/PayCallbackLog.php b/app/model/admin/PayCallbackLog.php index d7e332b..a2c235d 100644 --- a/app/model/admin/PayCallbackLog.php +++ b/app/model/admin/PayCallbackLog.php @@ -6,14 +6,29 @@ use app\common\base\BaseModel; /** * 支付回调日志模型。 - * 用于记录同步和异步回调原始报文和处理结果。 + * 用于记录同步和异步回调原始报文和处理状态。 */ class PayCallbackLog extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_pay_callback_log'; + /** + * 是否自动维护时间戳 + * + * @var mixed + */ public $timestamps = false; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'pay_no', 'channel_id', @@ -24,11 +39,21 @@ class PayCallbackLog extends BaseModel 'process_result', ]; + /** + * 隐藏字段 + * + * @var mixed + */ protected $hidden = [ 'request_data', 'process_result', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'channel_id' => 'integer', 'callback_type' => 'integer', @@ -39,3 +64,4 @@ class PayCallbackLog extends BaseModel } + diff --git a/app/model/file/FileRecord.php b/app/model/file/FileRecord.php index ade3f1f..728d5b1 100644 --- a/app/model/file/FileRecord.php +++ b/app/model/file/FileRecord.php @@ -9,8 +9,18 @@ use app\common\base\BaseModel; */ class FileRecord extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_file_asset'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'scene', 'source_type', @@ -30,6 +40,11 @@ class FileRecord extends BaseModel 'remark', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'scene' => 'integer', 'source_type' => 'integer', @@ -41,3 +56,5 @@ class FileRecord extends BaseModel 'updated_at' => 'datetime', ]; } + + diff --git a/app/model/merchant/Merchant.php b/app/model/merchant/Merchant.php index aaf1262..c21fbe9 100644 --- a/app/model/merchant/Merchant.php +++ b/app/model/merchant/Merchant.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class Merchant extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_merchant'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'merchant_no', 'password_hash', @@ -34,10 +44,20 @@ class Merchant extends BaseModel 'remark', ]; + /** + * 隐藏字段 + * + * @var mixed + */ protected $hidden = [ 'password_hash', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'merchant_type' => 'integer', 'group_id' => 'integer', @@ -49,3 +69,5 @@ class Merchant extends BaseModel 'updated_at' => 'datetime', ]; } + + diff --git a/app/model/merchant/MerchantAccount.php b/app/model/merchant/MerchantAccount.php index 63bfcc3..38c9fe1 100644 --- a/app/model/merchant/MerchantAccount.php +++ b/app/model/merchant/MerchantAccount.php @@ -10,14 +10,29 @@ use app\common\base\BaseModel; */ class MerchantAccount extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_merchant_account'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'merchant_id', 'available_balance', 'frozen_balance', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'merchant_id' => 'integer', 'available_balance' => 'integer', @@ -26,3 +41,5 @@ class MerchantAccount extends BaseModel 'updated_at' => 'datetime', ]; } + + diff --git a/app/model/merchant/MerchantAccountLedger.php b/app/model/merchant/MerchantAccountLedger.php index 640724c..ca78721 100644 --- a/app/model/merchant/MerchantAccountLedger.php +++ b/app/model/merchant/MerchantAccountLedger.php @@ -10,10 +10,25 @@ use app\common\base\BaseModel; */ class MerchantAccountLedger extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_merchant_account_ledger'; + /** + * 是否自动维护时间戳 + * + * @var mixed + */ public $timestamps = false; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'ledger_no', 'merchant_id', @@ -32,6 +47,11 @@ class MerchantAccountLedger extends BaseModel 'ext_json', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'merchant_id' => 'integer', 'biz_type' => 'integer', @@ -48,3 +68,5 @@ class MerchantAccountLedger extends BaseModel } + + diff --git a/app/model/merchant/MerchantApiCredential.php b/app/model/merchant/MerchantApiCredential.php index 215d9be..5f8839e 100644 --- a/app/model/merchant/MerchantApiCredential.php +++ b/app/model/merchant/MerchantApiCredential.php @@ -6,12 +6,22 @@ use app\common\base\BaseModel; /** * 商户对外接口凭证模型。 - * 保存商户接口凭证、签名类型、启用状态和最近使用时间。 + * 保存商户 API 凭证、签名类型、启用状态和最近使用时间。 */ class MerchantApiCredential extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_merchant_api_credential'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'merchant_id', 'sign_type', @@ -20,10 +30,20 @@ class MerchantApiCredential extends BaseModel 'last_used_at', ]; + /** + * 隐藏字段 + * + * @var mixed + */ protected $hidden = [ 'api_key', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'merchant_id' => 'integer', 'sign_type' => 'integer', @@ -33,3 +53,4 @@ class MerchantApiCredential extends BaseModel 'updated_at' => 'datetime', ]; } + diff --git a/app/model/merchant/MerchantGroup.php b/app/model/merchant/MerchantGroup.php index 4b6ff9d..481d494 100644 --- a/app/model/merchant/MerchantGroup.php +++ b/app/model/merchant/MerchantGroup.php @@ -10,14 +10,29 @@ use app\common\base\BaseModel; */ class MerchantGroup extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_merchant_group'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'group_name', 'status', 'remark', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'status' => 'integer', 'created_at' => 'datetime', @@ -25,3 +40,5 @@ class MerchantGroup extends BaseModel ]; } + + diff --git a/app/model/merchant/MerchantPolicy.php b/app/model/merchant/MerchantPolicy.php index f72a6ab..3ee290c 100644 --- a/app/model/merchant/MerchantPolicy.php +++ b/app/model/merchant/MerchantPolicy.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class MerchantPolicy extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_merchant_policy'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'merchant_id', 'settlement_cycle_override', @@ -24,6 +34,11 @@ class MerchantPolicy extends BaseModel 'remark', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'merchant_id' => 'integer', 'settlement_cycle_override' => 'integer', @@ -39,3 +54,5 @@ class MerchantPolicy extends BaseModel } + + diff --git a/app/model/payment/BizOrder.php b/app/model/payment/BizOrder.php index 6725811..1137d72 100644 --- a/app/model/payment/BizOrder.php +++ b/app/model/payment/BizOrder.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class BizOrder extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_biz_order'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'biz_no', 'trace_no', @@ -35,6 +45,11 @@ class BizOrder extends BaseModel 'ext_json', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'merchant_id' => 'integer', 'merchant_group_id' => 'integer', @@ -56,3 +71,5 @@ class BizOrder extends BaseModel } + + diff --git a/app/model/payment/NotifyTask.php b/app/model/payment/NotifyTask.php index 101a9d5..895f51f 100644 --- a/app/model/payment/NotifyTask.php +++ b/app/model/payment/NotifyTask.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class NotifyTask extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_notify_task'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'notify_no', 'merchant_id', @@ -27,11 +37,21 @@ class NotifyTask extends BaseModel 'last_response', ]; + /** + * 隐藏字段 + * + * @var mixed + */ protected $hidden = [ 'notify_data', 'last_response', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'merchant_id' => 'integer', 'merchant_group_id' => 'integer', @@ -45,3 +65,5 @@ class NotifyTask extends BaseModel } + + diff --git a/app/model/payment/PayOrder.php b/app/model/payment/PayOrder.php index 4580279..45ef8b2 100644 --- a/app/model/payment/PayOrder.php +++ b/app/model/payment/PayOrder.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class PayOrder extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_pay_order'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'pay_no', 'biz_no', @@ -49,6 +59,11 @@ class PayOrder extends BaseModel 'ext_json', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'merchant_id' => 'integer', 'merchant_group_id' => 'integer', @@ -81,3 +96,5 @@ class PayOrder extends BaseModel } + + diff --git a/app/model/payment/PaymentChannel.php b/app/model/payment/PaymentChannel.php index f712a1a..905c286 100644 --- a/app/model/payment/PaymentChannel.php +++ b/app/model/payment/PaymentChannel.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class PaymentChannel extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_payment_channel'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'merchant_id', 'name', @@ -30,6 +40,11 @@ class PaymentChannel extends BaseModel 'sort_no', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'merchant_id' => 'integer', 'split_rate_bp' => 'integer', @@ -49,3 +64,5 @@ class PaymentChannel extends BaseModel } + + diff --git a/app/model/payment/PaymentPlugin.php b/app/model/payment/PaymentPlugin.php index da0cf79..5fff4fe 100644 --- a/app/model/payment/PaymentPlugin.php +++ b/app/model/payment/PaymentPlugin.php @@ -10,14 +10,39 @@ use app\common\base\BaseModel; */ class PaymentPlugin extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_payment_plugin'; + /** + * 主键字段名 + * + * @var mixed + */ protected $primaryKey = 'code'; + /** + * incrementing + * + * @var mixed + */ public $incrementing = false; + /** + * key类型 + * + * @var mixed + */ protected $keyType = 'string'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'code', 'name', @@ -32,6 +57,11 @@ class PaymentPlugin extends BaseModel 'remark', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'config_schema' => 'array', 'pay_types' => 'array', @@ -43,3 +73,5 @@ class PaymentPlugin extends BaseModel } + + diff --git a/app/model/payment/PaymentPluginConf.php b/app/model/payment/PaymentPluginConf.php index 1d26da8..7cd82aa 100644 --- a/app/model/payment/PaymentPluginConf.php +++ b/app/model/payment/PaymentPluginConf.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class PaymentPluginConf extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_payment_plugin_conf'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'plugin_code', 'config', @@ -20,6 +30,11 @@ class PaymentPluginConf extends BaseModel 'remark', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'config' => 'array', 'settlement_cycle_type' => 'integer', @@ -29,3 +44,5 @@ class PaymentPluginConf extends BaseModel } + + diff --git a/app/model/payment/PaymentPollGroup.php b/app/model/payment/PaymentPollGroup.php index 2fcf4aa..9b8f7ea 100644 --- a/app/model/payment/PaymentPollGroup.php +++ b/app/model/payment/PaymentPollGroup.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class PaymentPollGroup extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_payment_poll_group'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'group_name', 'pay_type_id', @@ -20,6 +30,11 @@ class PaymentPollGroup extends BaseModel 'remark', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'pay_type_id' => 'integer', 'route_mode' => 'integer', @@ -29,3 +44,5 @@ class PaymentPollGroup extends BaseModel ]; } + + diff --git a/app/model/payment/PaymentPollGroupBind.php b/app/model/payment/PaymentPollGroupBind.php index d355186..4168f92 100644 --- a/app/model/payment/PaymentPollGroupBind.php +++ b/app/model/payment/PaymentPollGroupBind.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class PaymentPollGroupBind extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_payment_poll_group_bind'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'merchant_group_id', 'pay_type_id', @@ -20,6 +30,11 @@ class PaymentPollGroupBind extends BaseModel 'remark', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'merchant_group_id' => 'integer', 'pay_type_id' => 'integer', @@ -31,3 +46,5 @@ class PaymentPollGroupBind extends BaseModel } + + diff --git a/app/model/payment/PaymentPollGroupChannel.php b/app/model/payment/PaymentPollGroupChannel.php index 6e5a919..cfdd506 100644 --- a/app/model/payment/PaymentPollGroupChannel.php +++ b/app/model/payment/PaymentPollGroupChannel.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class PaymentPollGroupChannel extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_payment_poll_group_channel'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'poll_group_id', 'channel_id', @@ -22,6 +32,11 @@ class PaymentPollGroupChannel extends BaseModel 'remark', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'poll_group_id' => 'integer', 'channel_id' => 'integer', @@ -35,3 +50,5 @@ class PaymentPollGroupChannel extends BaseModel } + + diff --git a/app/model/payment/PaymentType.php b/app/model/payment/PaymentType.php index fb3eee0..f893ff7 100644 --- a/app/model/payment/PaymentType.php +++ b/app/model/payment/PaymentType.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class PaymentType extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_payment_type'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'code', 'name', @@ -21,6 +31,11 @@ class PaymentType extends BaseModel 'remark', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'sort_no' => 'integer', 'status' => 'integer', @@ -30,3 +45,5 @@ class PaymentType extends BaseModel } + + diff --git a/app/model/payment/RefundOrder.php b/app/model/payment/RefundOrder.php index c20598d..5edc8c9 100644 --- a/app/model/payment/RefundOrder.php +++ b/app/model/payment/RefundOrder.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class RefundOrder extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_refund_order'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'refund_no', 'merchant_id', @@ -36,6 +46,11 @@ class RefundOrder extends BaseModel 'ext_json', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'merchant_id' => 'integer', 'merchant_group_id' => 'integer', @@ -55,3 +70,5 @@ class RefundOrder extends BaseModel } + + diff --git a/app/model/payment/SettlementItem.php b/app/model/payment/SettlementItem.php index 61465b9..6342ef3 100644 --- a/app/model/payment/SettlementItem.php +++ b/app/model/payment/SettlementItem.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class SettlementItem extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_settlement_item'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'settle_no', 'merchant_id', @@ -27,6 +37,11 @@ class SettlementItem extends BaseModel 'item_status', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'merchant_id' => 'integer', 'merchant_group_id' => 'integer', @@ -43,3 +58,5 @@ class SettlementItem extends BaseModel } + + diff --git a/app/model/payment/SettlementOrder.php b/app/model/payment/SettlementOrder.php index ed28c58..d2c50e3 100644 --- a/app/model/payment/SettlementOrder.php +++ b/app/model/payment/SettlementOrder.php @@ -10,8 +10,18 @@ use app\common\base\BaseModel; */ class SettlementOrder extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_settlement_order'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'settle_no', 'trace_no', @@ -35,6 +45,11 @@ class SettlementOrder extends BaseModel 'ext_json', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'merchant_id' => 'integer', 'merchant_group_id' => 'integer', @@ -58,3 +73,5 @@ class SettlementOrder extends BaseModel } + + diff --git a/app/model/system/SystemConfig.php b/app/model/system/SystemConfig.php index c36c412..9fa8182 100644 --- a/app/model/system/SystemConfig.php +++ b/app/model/system/SystemConfig.php @@ -10,23 +10,55 @@ use app\common\base\BaseModel; */ class SystemConfig extends BaseModel { + /** + * 数据表名 + * + * @var mixed + */ protected $table = 'ma_system_config'; + /** + * 主键字段名 + * + * @var mixed + */ protected $primaryKey = 'config_key'; + /** + * incrementing + * + * @var mixed + */ public $incrementing = false; + /** + * key类型 + * + * @var mixed + */ protected $keyType = 'string'; + /** + * 可批量赋值字段 + * + * @var mixed + */ protected $fillable = [ 'config_key', 'group_code', 'config_value', ]; + /** + * 字段类型转换配置 + * + * @var mixed + */ protected $casts = [ 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; } + + diff --git a/app/process/Http.php b/app/process/Http.php index f462c3a..a17f3b0 100644 --- a/app/process/Http.php +++ b/app/process/Http.php @@ -4,7 +4,10 @@ namespace app\process; use Webman\App; +/** + * HTTP类 + */ class Http extends App { -} \ No newline at end of file +} diff --git a/app/process/Monitor.php b/app/process/Monitor.php index 25180e9..a56926c 100644 --- a/app/process/Monitor.php +++ b/app/process/Monitor.php @@ -15,32 +15,44 @@ use Workerman\Timer; use Workerman\Worker; /** - * 文件监控器。 + * Webman 文件监控进程。 + * + * 负责监听指定目录或文件的变更,并在需要时触发自动重载或内存回收。 */ class Monitor { /** * 监控路径列表。 + * + * @var array */ protected array $paths = []; /** * 监控扩展名列表。 + * + * @var array */ protected array $extensions = []; /** * 已加载文件列表。 + * + * @var array */ protected array $loadedFiles = []; /** * 父进程 ID。 + * + * @var int */ protected int $ppid = 0; /** * 暂停监控。 + * + * @return void */ public static function pause(): void { @@ -49,6 +61,8 @@ class Monitor /** * 恢复监控。 + * + * @return void */ public static function resume(): void { @@ -60,6 +74,8 @@ class Monitor /** * 判断监控是否已暂停。 + * + * @return bool 是否已暂停 */ public static function isPaused(): bool { @@ -69,6 +85,8 @@ class Monitor /** * 锁文件路径。 + * + * @return string 锁文件路径 */ protected static function lockFile(): string { @@ -77,6 +95,11 @@ class Monitor /** * 构造文件监控器。 + * + * @param mixed $monitorDir 监控目录或文件路径 + * @param mixed $monitorExtensions 监控文件扩展名 + * @param array $options 选项参数 + * @return void */ public function __construct($monitorDir, $monitorExtensions, array $options = []) { @@ -112,6 +135,9 @@ class Monitor /** * 检查指定路径是否有文件变化。 + * + * @param mixed $monitorDir 监控目录或文件路径 + * @return bool 是否发生变化 */ public function checkFilesChange($monitorDir): bool { @@ -125,9 +151,13 @@ class Monitor return false; } $iterator = [new SplFileInfo($monitorDir)]; + /** @var RecursiveDirectoryIterator $dirIterator */ } else { + /** @var RecursiveIteratorIterator $iterator */ // 递归遍历目录 + /** @var RecursiveDirectoryIterator $dirIterator */ $dirIterator = new RecursiveDirectoryIterator($monitorDir, FilesystemIterator::SKIP_DOTS | FilesystemIterator::FOLLOW_SYMLINKS); + /** @var RecursiveIteratorIterator $iterator */ $iterator = new RecursiveIteratorIterator($dirIterator); } $count = 0; @@ -171,7 +201,9 @@ class Monitor } /** - * @return int + * 获取主进程 PID。 + * + * @return int 主进程 PID */ public function getMasterPid(): int { @@ -195,6 +227,8 @@ class Monitor /** * 检查所有监控路径是否有变化。 + * + * @return bool 是否发生变化 */ public function checkAllFilesChange(): bool { @@ -211,6 +245,9 @@ class Monitor /** * 检查子进程内存占用。 + * + * @param mixed $memoryLimit 内存限制阈值(MB) + * @return void */ public function checkMemory($memoryLimit): void { @@ -246,6 +283,9 @@ class Monitor /** * 计算内存限制值。 + * + * @param mixed $memoryLimit 原始内存限制配置 + * @return int 内存限制值(MB) */ protected function getMemoryLimit($memoryLimit): int { @@ -284,3 +324,5 @@ class Monitor } } + + diff --git a/app/repository/account/balance/MerchantAccountRepository.php b/app/repository/account/balance/MerchantAccountRepository.php index 352c523..4dee518 100644 --- a/app/repository/account/balance/MerchantAccountRepository.php +++ b/app/repository/account/balance/MerchantAccountRepository.php @@ -7,11 +7,15 @@ use app\model\merchant\MerchantAccount; /** * 商户余额账户仓库。 + * + * 封装商户余额账户的单条查询、加锁查询和存在性统计。 */ class MerchantAccountRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class MerchantAccountRepository extends BaseRepository /** * 根据商户 ID 查询余额账户。 + * + * @param int $merchantId 商户ID + * @param array $columns 字段列表 + * @return MerchantAccount|null 账户记录 */ public function findByMerchantId(int $merchantId, array $columns = ['*']) { @@ -30,6 +38,10 @@ class MerchantAccountRepository extends BaseRepository /** * 根据商户 ID 加锁查询余额账户。 + * + * @param int $merchantId 商户ID + * @param array $columns 字段列表 + * @return MerchantAccount|null 账户记录 */ public function findForUpdateByMerchantId(int $merchantId, array $columns = ['*']) { @@ -41,6 +53,9 @@ class MerchantAccountRepository extends BaseRepository /** * 统计商户是否存在资金账户。 + * + * @param int $merchantId 商户ID + * @return int 账户数量 */ public function countByMerchantId(int $merchantId): int { @@ -50,3 +65,6 @@ class MerchantAccountRepository extends BaseRepository } } + + + diff --git a/app/repository/account/ledger/MerchantAccountLedgerRepository.php b/app/repository/account/ledger/MerchantAccountLedgerRepository.php index f48debd..f879673 100644 --- a/app/repository/account/ledger/MerchantAccountLedgerRepository.php +++ b/app/repository/account/ledger/MerchantAccountLedgerRepository.php @@ -7,11 +7,15 @@ use app\model\merchant\MerchantAccountLedger; /** * 商户余额流水仓库。 + * + * 封装幂等键、追踪号、业务单号和商户维度的流水查询。 */ class MerchantAccountLedgerRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class MerchantAccountLedgerRepository extends BaseRepository /** * 根据幂等键查询流水记录。 + * + * @param string $idempotencyKey 幂等键 + * @param array $columns 字段列表 + * @return MerchantAccountLedger|null 流水记录 */ public function findByIdempotencyKey(string $idempotencyKey, array $columns = ['*']) { @@ -30,6 +38,10 @@ class MerchantAccountLedgerRepository extends BaseRepository /** * 根据追踪号查询流水记录。 + * + * @param string $traceNo 追踪号 + * @param array $columns 字段列表 + * @return MerchantAccountLedger|null 流水记录 */ public function findByTraceNo(string $traceNo, array $columns = ['*']) { @@ -41,6 +53,10 @@ class MerchantAccountLedgerRepository extends BaseRepository /** * 查询指定追踪号的流水列表。 + * + * @param string $traceNo 追踪号 + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 流水列表 */ public function listByTraceNo(string $traceNo, array $columns = ['*']) { @@ -50,11 +66,12 @@ class MerchantAccountLedgerRepository extends BaseRepository ->get($columns); } - /** - * 查询商户指定业务类型和业务单号的流水列表。 - */ /** * 查询指定业务单号的流水列表。 + * + * @param string $bizNo 业务单号 + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 流水列表 */ public function listByBizNo(string $bizNo, array $columns = ['*']) { @@ -64,6 +81,15 @@ class MerchantAccountLedgerRepository extends BaseRepository ->get($columns); } + /** + * 按商户、业务类型和业务单号查询流水列表。 + * + * @param int $merchantId 商户ID + * @param int $bizType 业务类型 + * @param string $bizNo 业务单号 + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 流水列表 + */ public function listByMerchantAndBiz(int $merchantId, int $bizType, string $bizNo, array $columns = ['*']) { return $this->model->newQuery() @@ -76,3 +102,6 @@ class MerchantAccountLedgerRepository extends BaseRepository } + + + diff --git a/app/repository/file/FileRecordRepository.php b/app/repository/file/FileRecordRepository.php index d1b5197..479b21e 100644 --- a/app/repository/file/FileRecordRepository.php +++ b/app/repository/file/FileRecordRepository.php @@ -6,15 +6,29 @@ use app\common\base\BaseRepository; use app\model\file\FileRecord; /** - * 文件仓储。 + * 文件记录仓储。 + * + * 封装文件资产表的基础查询方法。 */ class FileRecordRepository extends BaseRepository { + /** + * 构造方法。 + * + * @return void + */ public function __construct() { parent::__construct(new FileRecord()); } + /** + * 按 ID 查询文件记录。 + * + * @param int $id 文件记录ID + * @param array $columns 字段列表 + * @return FileRecord|null 文件记录 + */ public function findById(int $id, array $columns = ['*']): ?FileRecord { $model = $this->find($id, $columns); @@ -22,3 +36,6 @@ class FileRecordRepository extends BaseRepository return $model instanceof FileRecord ? $model : null; } } + + + diff --git a/app/repository/merchant/base/MerchantGroupRepository.php b/app/repository/merchant/base/MerchantGroupRepository.php index ee325dd..3381a95 100644 --- a/app/repository/merchant/base/MerchantGroupRepository.php +++ b/app/repository/merchant/base/MerchantGroupRepository.php @@ -7,11 +7,15 @@ use app\model\merchant\MerchantGroup; /** * 商户分组仓库。 + * + * 封装商户分组启用列表和唯一性检查。 */ class MerchantGroupRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,9 @@ class MerchantGroupRepository extends BaseRepository /** * 获取所有启用的商户分组。 + * + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 启用分组列表 */ public function enabledList(array $columns = ['*']) { @@ -31,6 +38,10 @@ class MerchantGroupRepository extends BaseRepository /** * 判断分组名称是否已存在。 + * + * @param string $groupName 分组名称 + * @param int $ignoreId 需要排除的记录ID + * @return bool 是否存在 */ public function existsByGroupName(string $groupName, int $ignoreId = 0): bool { @@ -45,3 +56,7 @@ class MerchantGroupRepository extends BaseRepository } } + + + + diff --git a/app/repository/merchant/base/MerchantPolicyRepository.php b/app/repository/merchant/base/MerchantPolicyRepository.php index 45de936..b839f4e 100644 --- a/app/repository/merchant/base/MerchantPolicyRepository.php +++ b/app/repository/merchant/base/MerchantPolicyRepository.php @@ -7,11 +7,15 @@ use app\model\merchant\MerchantPolicy; /** * 商户策略仓库。 + * + * 封装商户策略的基础查询。 */ class MerchantPolicyRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class MerchantPolicyRepository extends BaseRepository /** * 根据商户 ID 查询商户策略。 + * + * @param int $merchantId 商户ID + * @param array $columns 字段列表 + * @return MerchantPolicy|null 策略记录 */ public function findByMerchantId(int $merchantId, array $columns = ['*']) { @@ -30,3 +38,7 @@ class MerchantPolicyRepository extends BaseRepository } + + + + diff --git a/app/repository/merchant/base/MerchantRepository.php b/app/repository/merchant/base/MerchantRepository.php index 3a98407..20e6c74 100644 --- a/app/repository/merchant/base/MerchantRepository.php +++ b/app/repository/merchant/base/MerchantRepository.php @@ -6,12 +6,16 @@ use app\common\base\BaseRepository; use app\model\merchant\Merchant; /** - * 商户仓库。 + * 商户基础查询仓库。 + * + * 封装按商户号、启用状态等基础条件读取商户记录的方法。 */ class MerchantRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class MerchantRepository extends BaseRepository /** * 根据商户编号查询商户。 + * + * @param string $merchantNo 商户号 + * @param array $columns 字段列表 + * @return Merchant|null 商户记录 */ public function findByMerchantNo(string $merchantNo, array $columns = ['*']): ?Merchant { @@ -30,6 +38,9 @@ class MerchantRepository extends BaseRepository /** * 获取所有启用的商户。 + * + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 启用商户列表 */ public function enabledList(array $columns = ['*']) { @@ -41,3 +52,9 @@ class MerchantRepository extends BaseRepository } + + + + + + diff --git a/app/repository/merchant/credential/MerchantApiCredentialRepository.php b/app/repository/merchant/credential/MerchantApiCredentialRepository.php index 8c869fa..dab649c 100644 --- a/app/repository/merchant/credential/MerchantApiCredentialRepository.php +++ b/app/repository/merchant/credential/MerchantApiCredentialRepository.php @@ -7,11 +7,15 @@ use app\model\merchant\MerchantApiCredential; /** * 商户 API 凭证仓库。 + * + * 封装商户 API 凭证的单条查询与存在性统计。 */ class MerchantApiCredentialRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class MerchantApiCredentialRepository extends BaseRepository /** * 根据商户 ID 查询 API 凭证。 + * + * @param int $merchantId 商户ID + * @param array $columns 字段列表 + * @return MerchantApiCredential|null 凭证记录 */ public function findByMerchantId(int $merchantId, array $columns = ['*']): ?MerchantApiCredential { @@ -30,6 +38,9 @@ class MerchantApiCredentialRepository extends BaseRepository /** * 统计商户是否已开通 API 凭证。 + * + * @param int $merchantId 商户ID + * @return int 凭证数量 */ public function countByMerchantId(int $merchantId): int { @@ -39,3 +50,6 @@ class MerchantApiCredentialRepository extends BaseRepository } } + + + diff --git a/app/repository/ops/log/ChannelNotifyLogRepository.php b/app/repository/ops/log/ChannelNotifyLogRepository.php index 25c9143..fe6c64a 100644 --- a/app/repository/ops/log/ChannelNotifyLogRepository.php +++ b/app/repository/ops/log/ChannelNotifyLogRepository.php @@ -7,11 +7,15 @@ use app\model\admin\ChannelNotifyLog; /** * 渠道通知日志仓库。 + * + * 封装通知单号查询与重复通知识别。 */ class ChannelNotifyLogRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class ChannelNotifyLogRepository extends BaseRepository /** * 根据通知单号查询渠道通知日志。 + * + * @param string $notifyNo 通知号 + * @param array $columns 字段列表 + * @return ChannelNotifyLog|null 日志记录 */ public function findByNotifyNo(string $notifyNo, array $columns = ['*']) { @@ -30,6 +38,12 @@ class ChannelNotifyLogRepository extends BaseRepository /** * 根据渠道、通知类型和业务单号查询重复通知记录。 + * + * @param int $channelId 渠道ID + * @param int $notifyType 通知类型 + * @param string $bizNo 业务单号 + * @param array $columns 字段列表 + * @return ChannelNotifyLog|null 日志记录 */ public function findDuplicate(int $channelId, int $notifyType, string $bizNo, array $columns = ['*']) { @@ -42,3 +56,7 @@ class ChannelNotifyLogRepository extends BaseRepository } + + + + diff --git a/app/repository/ops/log/PayCallbackLogRepository.php b/app/repository/ops/log/PayCallbackLogRepository.php index dacc66c..363caf6 100644 --- a/app/repository/ops/log/PayCallbackLogRepository.php +++ b/app/repository/ops/log/PayCallbackLogRepository.php @@ -7,11 +7,15 @@ use app\model\admin\PayCallbackLog; /** * 支付回调日志仓库。 + * + * 封装按支付单号查询回调日志列表。 */ class PayCallbackLogRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class PayCallbackLogRepository extends BaseRepository /** * 根据支付单号查询回调日志列表。 + * + * @param string $payNo 支付单号 + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 回调日志列表 */ public function listByPayNo(string $payNo, array $columns = ['*']) { @@ -31,3 +39,7 @@ class PayCallbackLogRepository extends BaseRepository } + + + + diff --git a/app/repository/ops/stat/ChannelDailyStatRepository.php b/app/repository/ops/stat/ChannelDailyStatRepository.php index 4255e85..c1aabbf 100644 --- a/app/repository/ops/stat/ChannelDailyStatRepository.php +++ b/app/repository/ops/stat/ChannelDailyStatRepository.php @@ -7,11 +7,15 @@ use app\model\admin\ChannelDailyStat; /** * 通道日统计仓库。 + * + * 封装按通道和日期读取日统计记录。 */ class ChannelDailyStatRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,11 @@ class ChannelDailyStatRepository extends BaseRepository /** * 根据通道和日期查询统计记录。 + * + * @param int $channelId 渠道ID + * @param string $statDate 统计日期 + * @param array $columns 字段列表 + * @return ChannelDailyStat|null 统计记录 */ public function findByChannelAndDate(int $channelId, string $statDate, array $columns = ['*']) { @@ -31,3 +40,7 @@ class ChannelDailyStatRepository extends BaseRepository } + + + + diff --git a/app/repository/payment/config/PaymentChannelRepository.php b/app/repository/payment/config/PaymentChannelRepository.php index c51db94..9f8856a 100644 --- a/app/repository/payment/config/PaymentChannelRepository.php +++ b/app/repository/payment/config/PaymentChannelRepository.php @@ -6,12 +6,16 @@ use app\common\base\BaseRepository; use app\model\payment\PaymentChannel; /** - * 支付通道仓库。 + * 支付通道基础查询仓库。 + * + * 提供商户通道的启用列表、单条查询和统计概览等基础读方法。 */ class PaymentChannelRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class PaymentChannelRepository extends BaseRepository /** * 查询指定商户启用的支付通道。 + * + * @param int $merchantId 商户ID + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 启用通道列表 */ public function enabledByMerchantId(int $merchantId, array $columns = ['*']) { @@ -32,6 +40,11 @@ class PaymentChannelRepository extends BaseRepository /** * 根据商户 ID 和通道 ID 查询通道。 + * + * @param int $merchantId 商户ID + * @param int $channelId 渠道ID + * @param array $columns 字段列表 + * @return PaymentChannel|null 通道记录 */ public function findByMerchantAndId(int $merchantId, int $channelId, array $columns = ['*']) { @@ -43,6 +56,10 @@ class PaymentChannelRepository extends BaseRepository /** * 判断通道名称是否已存在。 + * + * @param string $name 通道名称 + * @param int $ignoreId 需要排除的记录ID + * @return bool 是否存在 */ public function existsByName(string $name, int $ignoreId = 0): bool { @@ -58,6 +75,9 @@ class PaymentChannelRepository extends BaseRepository /** * 统计商户名下的支付通道概览。 + * + * @param int $merchantId 商户ID + * @return object{total_count:int, enabled_count:int, self_count:int} 通道统计概览 */ public function summaryByMerchantId(int $merchantId): object { @@ -71,6 +91,9 @@ class PaymentChannelRepository extends BaseRepository /** * 统计商户下的支付通道数量。 + * + * @param int $merchantId 商户ID + * @return int 通道数量 */ public function countByMerchantId(int $merchantId): int { @@ -79,3 +102,6 @@ class PaymentChannelRepository extends BaseRepository ->count(); } } + + + diff --git a/app/repository/payment/config/PaymentPluginConfRepository.php b/app/repository/payment/config/PaymentPluginConfRepository.php index 50f6f1d..85de946 100644 --- a/app/repository/payment/config/PaymentPluginConfRepository.php +++ b/app/repository/payment/config/PaymentPluginConfRepository.php @@ -7,11 +7,15 @@ use app\model\payment\PaymentPluginConf; /** * 支付插件配置仓库。 + * + * 封装按插件编码读取最新配置的查询方法。 */ class PaymentPluginConfRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class PaymentPluginConfRepository extends BaseRepository /** * 根据插件编码查询插件配置。 + * + * @param string $pluginCode 插件编码 + * @param array $columns 字段列表 + * @return PaymentPluginConf|null 插件配置记录 */ public function findByPluginCode(string $pluginCode, array $columns = ['*']) { @@ -31,3 +39,7 @@ class PaymentPluginConfRepository extends BaseRepository } + + + + diff --git a/app/repository/payment/config/PaymentPluginRepository.php b/app/repository/payment/config/PaymentPluginRepository.php index 8e74629..80dae2a 100644 --- a/app/repository/payment/config/PaymentPluginRepository.php +++ b/app/repository/payment/config/PaymentPluginRepository.php @@ -7,11 +7,15 @@ use app\model\payment\PaymentPlugin; /** * 支付插件仓库。 + * + * 封装支付插件字典的查询与启用列表读取。 */ class PaymentPluginRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class PaymentPluginRepository extends BaseRepository /** * 根据插件编码查询支付插件。 + * + * @param string $code 插件编码 + * @param array $columns 字段列表 + * @return PaymentPlugin|null 插件记录 */ public function findByCode(string $code, array $columns = ['*']): ?PaymentPlugin { @@ -30,6 +38,9 @@ class PaymentPluginRepository extends BaseRepository /** * 获取所有启用的支付插件。 + * + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 启用插件列表 */ public function enabledList(array $columns = ['*']) { @@ -41,3 +52,7 @@ class PaymentPluginRepository extends BaseRepository } + + + + diff --git a/app/repository/payment/config/PaymentPollGroupBindRepository.php b/app/repository/payment/config/PaymentPollGroupBindRepository.php index 4ea63f0..5474269 100644 --- a/app/repository/payment/config/PaymentPollGroupBindRepository.php +++ b/app/repository/payment/config/PaymentPollGroupBindRepository.php @@ -7,11 +7,15 @@ use app\model\payment\PaymentPollGroupBind; /** * 商户分组与轮询组绑定仓库。 + * + * 封装路由绑定的启用记录与编排展示查询。 */ class PaymentPollGroupBindRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,11 @@ class PaymentPollGroupBindRepository extends BaseRepository /** * 根据商户分组和支付方式查询启用的绑定关系。 + * + * @param int $merchantGroupId 商户分组ID + * @param int $payTypeId 支付类型ID + * @param array $columns 字段列表 + * @return PaymentPollGroupBind|null 绑定记录 */ public function findActiveByMerchantGroupAndPayType(int $merchantGroupId, int $payTypeId, array $columns = ['*']) { @@ -32,6 +41,9 @@ class PaymentPollGroupBindRepository extends BaseRepository /** * 查询商户分组下的路由绑定概览。 + * + * @param int $merchantGroupId 商户分组ID + * @return \Illuminate\Database\Eloquent\Collection 绑定概览列表 */ public function listSummaryByMerchantGroupId(int $merchantGroupId) { @@ -55,3 +67,7 @@ class PaymentPollGroupBindRepository extends BaseRepository } } + + + + diff --git a/app/repository/payment/config/PaymentPollGroupChannelRepository.php b/app/repository/payment/config/PaymentPollGroupChannelRepository.php index db2d99c..5d133aa 100644 --- a/app/repository/payment/config/PaymentPollGroupChannelRepository.php +++ b/app/repository/payment/config/PaymentPollGroupChannelRepository.php @@ -7,11 +7,15 @@ use app\model\payment\PaymentPollGroupChannel; /** * 轮询组与通道编排仓库。 + * + * 封装轮询组下通道编排、默认通道清理等查询与更新。 */ class PaymentPollGroupChannelRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class PaymentPollGroupChannelRepository extends BaseRepository /** * 查询轮询组下的通道编排列表。 + * + * @param int $pollGroupId 轮询分组ID + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 编排列表 */ public function listByPollGroupId(int $pollGroupId, array $columns = ['*']) { @@ -32,6 +40,10 @@ class PaymentPollGroupChannelRepository extends BaseRepository /** * 清空轮询组下其他默认通道标记。 + * + * @param int $pollGroupId 轮询分组ID + * @param int $ignoreId 需要保留默认标记的记录ID + * @return int 受影响行数 */ public function clearDefaultExcept(int $pollGroupId, int $ignoreId = 0): int { @@ -47,3 +59,7 @@ class PaymentPollGroupChannelRepository extends BaseRepository } } + + + + diff --git a/app/repository/payment/config/PaymentPollGroupRepository.php b/app/repository/payment/config/PaymentPollGroupRepository.php index 69c012d..cafbf2e 100644 --- a/app/repository/payment/config/PaymentPollGroupRepository.php +++ b/app/repository/payment/config/PaymentPollGroupRepository.php @@ -11,7 +11,9 @@ use app\model\payment\PaymentPollGroup; class PaymentPollGroupRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +22,10 @@ class PaymentPollGroupRepository extends BaseRepository /** * 判断轮询组名称是否已存在。 + * + * @param string $groupName 轮询组名称 + * @param int $ignoreId 需要排除的记录ID + * @return bool 是否存在 */ public function existsByGroupName(string $groupName, int $ignoreId = 0): bool { @@ -33,3 +39,7 @@ class PaymentPollGroupRepository extends BaseRepository return $query->exists(); } } + + + + diff --git a/app/repository/payment/config/PaymentTypeRepository.php b/app/repository/payment/config/PaymentTypeRepository.php index 306eb39..f19dca6 100644 --- a/app/repository/payment/config/PaymentTypeRepository.php +++ b/app/repository/payment/config/PaymentTypeRepository.php @@ -7,11 +7,15 @@ use app\model\payment\PaymentType; /** * 支付方式字典仓库。 + * + * 封装支付方式启用列表和按编码查询方法。 */ class PaymentTypeRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,9 @@ class PaymentTypeRepository extends BaseRepository /** * 获取所有启用的支付方式。 + * + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 启用支付方式列表 */ public function enabledList(array $columns = ['*']) { @@ -31,6 +38,10 @@ class PaymentTypeRepository extends BaseRepository /** * 根据支付方式编码查询字典。 + * + * @param string $code 支付方式编码 + * @param array $columns 字段列表 + * @return PaymentType|null 支付方式记录 */ public function findByCode(string $code, array $columns = ['*']): ?PaymentType { @@ -41,3 +52,7 @@ class PaymentTypeRepository extends BaseRepository } + + + + diff --git a/app/repository/payment/notify/NotifyTaskRepository.php b/app/repository/payment/notify/NotifyTaskRepository.php index 47ee71e..b55e43b 100644 --- a/app/repository/payment/notify/NotifyTaskRepository.php +++ b/app/repository/payment/notify/NotifyTaskRepository.php @@ -7,11 +7,15 @@ use app\model\payment\NotifyTask; /** * 商户通知任务仓库。 + * + * 封装通知单号查询和可重试任务列表。 */ class NotifyTaskRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class NotifyTaskRepository extends BaseRepository /** * 根据通知单号查询通知任务。 + * + * @param string $notifyNo 通知号 + * @param array $columns 字段列表 + * @return NotifyTask|null 通知任务记录 */ public function findByNotifyNo(string $notifyNo, array $columns = ['*']) { @@ -30,6 +38,10 @@ class NotifyTaskRepository extends BaseRepository /** * 查询可重试的通知任务列表。 + * + * @param int $status 状态 + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 可重试任务列表 */ public function listRetryable(int $status, array $columns = ['*']) { @@ -41,3 +53,7 @@ class NotifyTaskRepository extends BaseRepository } + + + + diff --git a/app/repository/payment/settlement/SettlementItemRepository.php b/app/repository/payment/settlement/SettlementItemRepository.php index 5ee8456..21eae1e 100644 --- a/app/repository/payment/settlement/SettlementItemRepository.php +++ b/app/repository/payment/settlement/SettlementItemRepository.php @@ -7,11 +7,15 @@ use app\model\payment\SettlementItem; /** * 清算明细仓库。 + * + * 封装清算单下的明细列表查询。 */ class SettlementItemRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class SettlementItemRepository extends BaseRepository /** * 查询指定清算单下的明细列表。 + * + * @param string $settleNo 结算单号 + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 清算明细列表 */ public function listBySettleNo(string $settleNo, array $columns = ['*']) { @@ -31,3 +39,7 @@ class SettlementItemRepository extends BaseRepository } + + + + diff --git a/app/repository/payment/settlement/SettlementOrderRepository.php b/app/repository/payment/settlement/SettlementOrderRepository.php index abb3862..99fa60f 100644 --- a/app/repository/payment/settlement/SettlementOrderRepository.php +++ b/app/repository/payment/settlement/SettlementOrderRepository.php @@ -7,11 +7,15 @@ use app\model\payment\SettlementOrder; /** * 清算单仓库。 + * + * 封装清算单号、追踪号、清算周期和最近列表查询。 */ class SettlementOrderRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class SettlementOrderRepository extends BaseRepository /** * 根据清算单号查询清算单。 + * + * @param string $settleNo 结算单号 + * @param array $columns 字段列表 + * @return SettlementOrder|null 清算单记录 */ public function findBySettleNo(string $settleNo, array $columns = ['*']) { @@ -30,6 +38,10 @@ class SettlementOrderRepository extends BaseRepository /** * 根据追踪号查询清算单。 + * + * @param string $traceNo 追踪号 + * @param array $columns 字段列表 + * @return SettlementOrder|null 清算单记录 */ public function findByTraceNo(string $traceNo, array $columns = ['*']) { @@ -40,6 +52,10 @@ class SettlementOrderRepository extends BaseRepository /** * 根据追踪号查询清结算单列表。 + * + * @param string $traceNo 追踪号 + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 清算单列表 */ public function listByTraceNo(string $traceNo, array $columns = ['*']) { @@ -51,6 +67,13 @@ class SettlementOrderRepository extends BaseRepository /** * 根据商户、通道和清算周期查询清算单。 + * + * @param int $merchantId 商户ID + * @param int $channelId 渠道ID + * @param int $cycleType 周期类型 + * @param string $cycleKey 周期标识 + * @param array $columns 字段列表 + * @return SettlementOrder|null 清算单记录 */ public function findByCycle(int $merchantId, int $channelId, int $cycleType, string $cycleKey, array $columns = ['*']) { @@ -64,6 +87,10 @@ class SettlementOrderRepository extends BaseRepository /** * 根据清算单号加锁查询清算单。 + * + * @param string $settleNo 结算单号 + * @param array $columns 字段列表 + * @return SettlementOrder|null 清算单记录 */ public function findForUpdateBySettleNo(string $settleNo, array $columns = ['*']) { @@ -75,6 +102,10 @@ class SettlementOrderRepository extends BaseRepository /** * 根据追踪号加锁查询清算单。 + * + * @param string $traceNo 追踪号 + * @param array $columns 字段列表 + * @return SettlementOrder|null 清算单记录 */ public function findForUpdateByTraceNo(string $traceNo, array $columns = ['*']) { @@ -86,6 +117,10 @@ class SettlementOrderRepository extends BaseRepository /** * 查询商户最近清算单列表,用于总览展示。 + * + * @param int $merchantId 商户ID + * @param int $limit 限制条数 + * @return \Illuminate\Database\Eloquent\Collection 最近清算单列表 */ public function recentByMerchantId(int $merchantId, int $limit = 5) { @@ -104,6 +139,9 @@ class SettlementOrderRepository extends BaseRepository /** * 统计商户下的清算单数量。 + * + * @param int $merchantId 商户ID + * @return int 清算单数量 */ public function countByMerchantId(int $merchantId): int { @@ -112,3 +150,7 @@ class SettlementOrderRepository extends BaseRepository ->count(); } } + + + + diff --git a/app/repository/payment/trade/BizOrderRepository.php b/app/repository/payment/trade/BizOrderRepository.php index ff1036f..5ef39de 100644 --- a/app/repository/payment/trade/BizOrderRepository.php +++ b/app/repository/payment/trade/BizOrderRepository.php @@ -7,11 +7,15 @@ use app\model\payment\BizOrder; /** * 业务订单仓库。 + * + * 封装业务单号、追踪号、商户订单号和加锁查询方法。 */ class BizOrderRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class BizOrderRepository extends BaseRepository /** * 根据业务单号查询业务订单。 + * + * @param string $bizNo 业务单号 + * @param array $columns 字段列表 + * @return BizOrder|null 业务订单记录 */ public function findByBizNo(string $bizNo, array $columns = ['*']) { @@ -30,6 +38,10 @@ class BizOrderRepository extends BaseRepository /** * 根据追踪号查询业务订单。 + * + * @param string $traceNo 追踪号 + * @param array $columns 字段列表 + * @return BizOrder|null 业务订单记录 */ public function findByTraceNo(string $traceNo, array $columns = ['*']) { @@ -40,6 +52,11 @@ class BizOrderRepository extends BaseRepository /** * 根据商户 ID 和商户订单号查询业务订单。 + * + * @param int $merchantId 商户ID + * @param string $merchantOrderNo 商户订单号 + * @param array $columns 字段列表 + * @return BizOrder|null 业务订单记录 */ public function findByMerchantAndOrderNo(int $merchantId, string $merchantOrderNo, array $columns = ['*']) { @@ -51,6 +68,10 @@ class BizOrderRepository extends BaseRepository /** * 根据业务单号查询当前有效的业务订单。 + * + * @param string $bizNo 业务单号 + * @param array $columns 字段列表 + * @return BizOrder|null 有效业务订单记录 */ public function findActiveByBizNo(string $bizNo, array $columns = ['*']) { @@ -62,6 +83,10 @@ class BizOrderRepository extends BaseRepository /** * 根据业务单号加锁查询业务订单。 + * + * @param string $bizNo 业务单号 + * @param array $columns 字段列表 + * @return BizOrder|null 业务订单记录 */ public function findForUpdateByBizNo(string $bizNo, array $columns = ['*']) { @@ -73,6 +98,10 @@ class BizOrderRepository extends BaseRepository /** * 根据追踪号加锁查询业务订单。 + * + * @param string $traceNo 追踪号 + * @param array $columns 字段列表 + * @return BizOrder|null 业务订单记录 */ public function findForUpdateByTraceNo(string $traceNo, array $columns = ['*']) { @@ -84,6 +113,11 @@ class BizOrderRepository extends BaseRepository /** * 根据商户 ID 和商户订单号加锁查询业务订单。 + * + * @param int $merchantId 商户ID + * @param string $merchantOrderNo 商户订单号 + * @param array $columns 字段列表 + * @return BizOrder|null 业务订单记录 */ public function findForUpdateByMerchantAndOrderNo(int $merchantId, string $merchantOrderNo, array $columns = ['*']) { @@ -96,6 +130,9 @@ class BizOrderRepository extends BaseRepository /** * 统计商户下的业务订单数量。 + * + * @param int $merchantId 商户ID + * @return int 整数结果 */ public function countByMerchantId(int $merchantId): int { @@ -105,3 +142,7 @@ class BizOrderRepository extends BaseRepository } } + + + + diff --git a/app/repository/payment/trade/PayOrderRepository.php b/app/repository/payment/trade/PayOrderRepository.php index c162b64..820de73 100644 --- a/app/repository/payment/trade/PayOrderRepository.php +++ b/app/repository/payment/trade/PayOrderRepository.php @@ -6,12 +6,16 @@ use app\common\base\BaseRepository; use app\model\payment\PayOrder; /** - * 支付单仓库。 + * 支付单基础查询仓库。 + * + * 封装支付单号、业务单号、追踪号和商户请求号等常用查询方法。 */ class PayOrderRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class PayOrderRepository extends BaseRepository /** * 根据支付单号查询支付单。 + * + * @param string $payNo 支付单号 + * @param array $columns 字段列表 + * @return PayOrder|null 支付单记录 */ public function findByPayNo(string $payNo, array $columns = ['*']) { @@ -30,6 +38,10 @@ class PayOrderRepository extends BaseRepository /** * 根据追踪号查询支付单。 + * + * @param string $traceNo 追踪号 + * @param array $columns 字段列表 + * @return PayOrder|null 支付单记录 */ public function findByTraceNo(string $traceNo, array $columns = ['*']) { @@ -40,6 +52,10 @@ class PayOrderRepository extends BaseRepository /** * 根据追踪号查询支付单列表。 + * + * @param string $traceNo 追踪号 + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 支付单列表 */ public function listByTraceNo(string $traceNo, array $columns = ['*']) { @@ -52,6 +68,10 @@ class PayOrderRepository extends BaseRepository /** * 根据业务单号查询支付单列表。 + * + * @param string $bizNo 业务单号 + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 支付单列表 */ public function listByBizNo(string $bizNo, array $columns = ['*']) { @@ -64,6 +84,10 @@ class PayOrderRepository extends BaseRepository /** * 根据业务单号查询最新支付单。 + * + * @param string $bizNo 业务单号 + * @param array $columns 字段列表 + * @return PayOrder|null 支付单记录 */ public function findLatestByBizNo(string $bizNo, array $columns = ['*']) { @@ -75,6 +99,11 @@ class PayOrderRepository extends BaseRepository /** * 根据商户和渠道请求号查询支付单。 + * + * @param int $merchantId 商户ID + * @param string $channelRequestNo 渠道Request号 + * @param array $columns 字段列表 + * @return PayOrder|null 支付单记录 */ public function findByChannelRequestNo(int $merchantId, string $channelRequestNo, array $columns = ['*']) { @@ -86,6 +115,10 @@ class PayOrderRepository extends BaseRepository /** * 根据支付单号加锁查询支付单。 + * + * @param string $payNo 支付单号 + * @param array $columns 字段列表 + * @return PayOrder|null 支付单记录 */ public function findForUpdateByPayNo(string $payNo, array $columns = ['*']) { @@ -97,6 +130,10 @@ class PayOrderRepository extends BaseRepository /** * 根据追踪号加锁查询支付单。 + * + * @param string $traceNo 追踪号 + * @param array $columns 字段列表 + * @return PayOrder|null 支付单记录 */ public function findForUpdateByTraceNo(string $traceNo, array $columns = ['*']) { @@ -108,6 +145,10 @@ class PayOrderRepository extends BaseRepository /** * 根据业务单号加锁查询最新支付单。 + * + * @param string $bizNo 业务单号 + * @param array $columns 字段列表 + * @return PayOrder|null 支付单记录 */ public function findLatestForUpdateByBizNo(string $bizNo, array $columns = ['*']) { @@ -120,6 +161,10 @@ class PayOrderRepository extends BaseRepository /** * 查询商户最近支付单列表,用于总览展示。 + * + * @param int $merchantId 商户ID + * @param int $limit 限制条数 + * @return \Illuminate\Database\Eloquent\Collection 最近支付单列表 */ public function recentByMerchantId(int $merchantId, int $limit = 5) { @@ -141,3 +186,7 @@ class PayOrderRepository extends BaseRepository } } + + + + diff --git a/app/repository/payment/trade/RefundOrderRepository.php b/app/repository/payment/trade/RefundOrderRepository.php index 0eeac47..cbfd426 100644 --- a/app/repository/payment/trade/RefundOrderRepository.php +++ b/app/repository/payment/trade/RefundOrderRepository.php @@ -6,12 +6,16 @@ use app\common\base\BaseRepository; use app\model\payment\RefundOrder; /** - * 退款单仓库。 + * 退款单基础查询仓库。 + * + * 封装退款单号、业务单号、追踪号、支付单号和商户退款号等常用查询方法。 */ class RefundOrderRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class RefundOrderRepository extends BaseRepository /** * 根据退款单号查询退款单。 + * + * @param string $refundNo 退款单号 + * @param array $columns 字段列表 + * @return RefundOrder|null 退款单记录 */ public function findByRefundNo(string $refundNo, array $columns = ['*']) { @@ -30,6 +38,10 @@ class RefundOrderRepository extends BaseRepository /** * 根据追踪号查询退款单。 + * + * @param string $traceNo 追踪号 + * @param array $columns 字段列表 + * @return RefundOrder|null 退款单记录 */ public function findByTraceNo(string $traceNo, array $columns = ['*']) { @@ -40,6 +52,10 @@ class RefundOrderRepository extends BaseRepository /** * 根据追踪号查询退款单列表。 + * + * @param string $traceNo 追踪号 + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 退款单列表 */ public function listByTraceNo(string $traceNo, array $columns = ['*']) { @@ -51,6 +67,10 @@ class RefundOrderRepository extends BaseRepository /** * 根据业务单号查询退款单列表。 + * + * @param string $bizNo 业务单号 + * @param array $columns 字段列表 + * @return \Illuminate\Database\Eloquent\Collection 退款单列表 */ public function listByBizNo(string $bizNo, array $columns = ['*']) { @@ -62,6 +82,11 @@ class RefundOrderRepository extends BaseRepository /** * 根据商户退款单号查询退款单。 + * + * @param int $merchantId 商户ID + * @param string $merchantRefundNo 商户退款号 + * @param array $columns 字段列表 + * @return RefundOrder|null 退款单记录 */ public function findByMerchantRefundNo(int $merchantId, string $merchantRefundNo, array $columns = ['*']) { @@ -73,6 +98,10 @@ class RefundOrderRepository extends BaseRepository /** * 根据支付单号查询退款单。 + * + * @param string $payNo 支付单号 + * @param array $columns 字段列表 + * @return RefundOrder|null 退款单记录 */ public function findByPayNo(string $payNo, array $columns = ['*']) { @@ -83,6 +112,10 @@ class RefundOrderRepository extends BaseRepository /** * 根据退款单号加锁查询退款单。 + * + * @param string $refundNo 退款单号 + * @param array $columns 字段列表 + * @return RefundOrder|null 退款单记录 */ public function findForUpdateByRefundNo(string $refundNo, array $columns = ['*']) { @@ -94,6 +127,10 @@ class RefundOrderRepository extends BaseRepository /** * 根据追踪号加锁查询退款单。 + * + * @param string $traceNo 追踪号 + * @param array $columns 字段列表 + * @return RefundOrder|null 退款单记录 */ public function findForUpdateByTraceNo(string $traceNo, array $columns = ['*']) { @@ -105,6 +142,10 @@ class RefundOrderRepository extends BaseRepository /** * 根据支付单号加锁查询退款单。 + * + * @param string $payNo 支付单号 + * @param array $columns 字段列表 + * @return RefundOrder|null 退款单记录 */ public function findForUpdateByPayNo(string $payNo, array $columns = ['*']) { @@ -116,6 +157,9 @@ class RefundOrderRepository extends BaseRepository /** * 统计商户下的退款订单数量。 + * + * @param int $merchantId 商户ID + * @return int 退款订单数量 */ public function countByMerchantId(int $merchantId): int { @@ -125,3 +169,7 @@ class RefundOrderRepository extends BaseRepository } } + + + + diff --git a/app/repository/system/config/SystemConfigRepository.php b/app/repository/system/config/SystemConfigRepository.php index f979ede..a704021 100644 --- a/app/repository/system/config/SystemConfigRepository.php +++ b/app/repository/system/config/SystemConfigRepository.php @@ -11,10 +11,17 @@ use app\model\system\SystemConfig; class SystemConfigRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { parent::__construct(new SystemConfig()); } } + + + + + diff --git a/app/repository/system/user/AdminUserRepository.php b/app/repository/system/user/AdminUserRepository.php index c25fbe8..9529953 100644 --- a/app/repository/system/user/AdminUserRepository.php +++ b/app/repository/system/user/AdminUserRepository.php @@ -7,11 +7,15 @@ use app\model\admin\AdminUser; /** * 管理员账号仓库。 + * + * 封装管理员用户名查询等基础读方法。 */ class AdminUserRepository extends BaseRepository { /** - * 构造函数,注入对应模型。 + * 构造方法。 + * + * @return void */ public function __construct() { @@ -20,6 +24,10 @@ class AdminUserRepository extends BaseRepository /** * 根据用户名查询管理员。 + * + * @param string $username 用户名 + * @param array $columns 字段列表 + * @return AdminUser|null 管理员记录 */ public function findByUsername(string $username, array $columns = ['*']): ?AdminUser { @@ -30,3 +38,7 @@ class AdminUserRepository extends BaseRepository } + + + + diff --git a/app/route/admin.php b/app/route/admin.php index 4425dcf..947e340 100644 --- a/app/route/admin.php +++ b/app/route/admin.php @@ -49,7 +49,7 @@ Route::group('/adminapi', function () { Route::put('/merchants/{id}', [MerchantController::class, 'update'])->name('adminApiMerchantsUpdate')->setParams(['real_name' => '更新商户']); Route::delete('/merchants/{id}', [MerchantController::class, 'destroy'])->name('adminApiMerchantsDestroy')->setParams(['real_name' => '删除商户']); Route::post('/merchants/{id}/reset-password', [MerchantController::class, 'resetPassword'])->name('adminApiMerchantsResetPassword')->setParams(['real_name' => '重置商户密码']); - Route::post('/merchants/{id}/issue-credential', [MerchantController::class, 'issueCredential'])->name('adminApiMerchantsIssueCredential')->setParams(['real_name' => '生成或重置接口凭证']); + Route::post('/merchants/{id}/issue-credential', [MerchantController::class, 'issueCredential'])->name('adminApiMerchantsIssueCredential')->setParams(['real_name' => '生成或重置商户 API 凭证']); Route::get('/admin-users', [AdminUserController::class, 'index'])->name('adminApiAdminUsersIndex')->setParams(['real_name' => '管理员列表']); Route::get('/admin-users/{id}', [AdminUserController::class, 'show'])->name('adminApiAdminUsersShow')->setParams(['real_name' => '管理员详情']); @@ -58,11 +58,11 @@ Route::group('/adminapi', function () { Route::delete('/admin-users/{id}', [AdminUserController::class, 'destroy'])->name('adminApiAdminUsersDestroy')->setParams(['real_name' => '删除管理员']); - Route::get('/merchant-api-credentials', [MerchantApiCredentialController::class, 'index'])->name('adminApiMerchantApiCredentialsIndex')->setParams(['real_name' => '商户接口凭证列表']); - Route::get('/merchant-api-credentials/{id}', [MerchantApiCredentialController::class, 'show'])->name('adminApiMerchantApiCredentialsShow')->setParams(['real_name' => '商户接口凭证详情']); - Route::post('/merchant-api-credentials', [MerchantApiCredentialController::class, 'store'])->name('adminApiMerchantApiCredentialsStore')->setParams(['real_name' => '开通商户接口凭证']); - Route::put('/merchant-api-credentials/{id}', [MerchantApiCredentialController::class, 'update'])->name('adminApiMerchantApiCredentialsUpdate')->setParams(['real_name' => '更新商户接口凭证']); - Route::delete('/merchant-api-credentials/{id}', [MerchantApiCredentialController::class, 'destroy'])->name('adminApiMerchantApiCredentialsDestroy')->setParams(['real_name' => '删除商户接口凭证']); + Route::get('/merchant-api-credentials', [MerchantApiCredentialController::class, 'index'])->name('adminApiMerchantApiCredentialsIndex')->setParams(['real_name' => '商户 API 凭证列表']); + Route::get('/merchant-api-credentials/{id}', [MerchantApiCredentialController::class, 'show'])->name('adminApiMerchantApiCredentialsShow')->setParams(['real_name' => '商户 API 凭证详情']); + Route::post('/merchant-api-credentials', [MerchantApiCredentialController::class, 'store'])->name('adminApiMerchantApiCredentialsStore')->setParams(['real_name' => '开通商户 API 凭证']); + Route::put('/merchant-api-credentials/{id}', [MerchantApiCredentialController::class, 'update'])->name('adminApiMerchantApiCredentialsUpdate')->setParams(['real_name' => '更新商户 API 凭证']); + Route::delete('/merchant-api-credentials/{id}', [MerchantApiCredentialController::class, 'destroy'])->name('adminApiMerchantApiCredentialsDestroy')->setParams(['real_name' => '删除商户 API 凭证']); Route::get('/merchant-groups', [MerchantGroupController::class, 'index'])->name('adminApiMerchantGroupsIndex')->setParams(['real_name' => '商户分组列表']); Route::get('/merchant-groups/options', [MerchantGroupController::class, 'options'])->name('adminApiMerchantGroupsOptions')->setParams(['real_name' => '商户分组选项']); diff --git a/app/route/mer.php b/app/route/mer.php index 7a7a487..d30c5f1 100644 --- a/app/route/mer.php +++ b/app/route/mer.php @@ -23,9 +23,9 @@ Route::group('/merapi', function () { Route::put('/merchant/profile', [MerchantPortalController::class, 'updateProfile'])->name('merchantApiPortalProfileUpdate')->setParams(['real_name' => '更新商户资料']); Route::post('/merchant/change-password', [MerchantPortalController::class, 'changePassword'])->name('merchantApiPortalChangePassword')->setParams(['real_name' => '修改登录密码']); Route::get('/my-channels', [MerchantPortalController::class, 'myChannels'])->name('merchantApiPortalMyChannels')->setParams(['real_name' => '我的通道']); - Route::get('/route-preview', [MerchantPortalController::class, 'routePreview'])->name('merchantApiPortalRoutePreview')->setParams(['real_name' => '路由预览']); - Route::get('/api-credential', [MerchantPortalController::class, 'apiCredential'])->name('merchantApiPortalCredential')->setParams(['real_name' => '接口凭证']); - Route::post('/api-credential/issue-credential', [MerchantPortalController::class, 'issueCredential'])->name('merchantApiPortalIssueCredential')->setParams(['real_name' => '生成或重置接口凭证']); + Route::get('/route-preview', [MerchantPortalController::class, 'routePreview'])->name('merchantApiPortalRoutePreview')->setParams(['real_name' => '路由解析']); + Route::get('/api-credential', [MerchantPortalController::class, 'apiCredential'])->name('merchantApiPortalCredential')->setParams(['real_name' => '商户 API 凭证']); + Route::post('/api-credential/issue-credential', [MerchantPortalController::class, 'issueCredential'])->name('merchantApiPortalIssueCredential')->setParams(['real_name' => '生成或重置商户 API 凭证']); Route::get('/settlement-records', [MerchantPortalController::class, 'settlementRecords'])->name('merchantApiPortalSettlementRecords')->setParams(['real_name' => '清算记录']); Route::get('/settlement-records/{settleNo}', [MerchantPortalController::class, 'settlementRecordShow'])->name('merchantApiPortalSettlementRecordShow')->setParams(['real_name' => '清算记录详情']); Route::get('/withdrawable-balance', [MerchantPortalController::class, 'withdrawableBalance'])->name('merchantApiPortalWithdrawableBalance')->setParams(['real_name' => '可提现余额']); diff --git a/app/service/account/funds/MerchantAccountCommandService.php b/app/service/account/funds/MerchantAccountCommandService.php index c132b64..b028993 100644 --- a/app/service/account/funds/MerchantAccountCommandService.php +++ b/app/service/account/funds/MerchantAccountCommandService.php @@ -16,9 +16,19 @@ use app\repository\account\ledger\MerchantAccountLedgerRepository; * 商户账户命令服务。 * * 只负责账户创建、冻结、扣减、释放和入账等资金变更。 + * + * @property MerchantAccountRepository $accountRepository 账户仓库 + * @property MerchantAccountLedgerRepository $ledgerRepository 流水仓库 */ class MerchantAccountCommandService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantAccountRepository $accountRepository 账户仓库 + * @param MerchantAccountLedgerRepository $ledgerRepository 流水仓库 + * @return void + */ public function __construct( protected MerchantAccountRepository $accountRepository, protected MerchantAccountLedgerRepository $ledgerRepository @@ -27,6 +37,9 @@ class MerchantAccountCommandService extends BaseService /** * 获取或创建商户账户。 + * + * @param int $merchantId 商户ID + * @return MerchantAccount 账户记录 */ public function ensureAccount(int $merchantId): MerchantAccount { @@ -37,6 +50,10 @@ class MerchantAccountCommandService extends BaseService /** * 在当前事务中获取或创建商户账户。 + * + * @param int $merchantId 商户ID + * @return MerchantAccount 账户记录 + * @throws ValidationException */ public function ensureAccountInCurrentTransaction(int $merchantId): MerchantAccount { @@ -59,6 +76,17 @@ class MerchantAccountCommandService extends BaseService return $account; } + /** + * 冻结可用余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function freezeAmount(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->transactionRetry(function () use ($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo) { @@ -66,6 +94,19 @@ class MerchantAccountCommandService extends BaseService }); } + /** + * 在当前事务中冻结可用余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + * @throws ValidationException + * @throws BalanceInsufficientException + */ public function freezeAmountInCurrentTransaction(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { $this->assertPositiveAmount($amount); @@ -108,6 +149,17 @@ class MerchantAccountCommandService extends BaseService ]); } + /** + * 扣减冻结余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function deductFrozenAmount(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->transactionRetry(function () use ($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo) { @@ -115,6 +167,18 @@ class MerchantAccountCommandService extends BaseService }); } + /** + * 在当前事务中扣减冻结余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + * @throws ValidationException + */ public function deductFrozenAmountInCurrentTransaction(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { $this->assertPositiveAmount($amount); @@ -160,6 +224,17 @@ class MerchantAccountCommandService extends BaseService ]); } + /** + * 释放冻结余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function releaseFrozenAmount(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->transactionRetry(function () use ($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo) { @@ -167,6 +242,18 @@ class MerchantAccountCommandService extends BaseService }); } + /** + * 在当前事务中释放冻结余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + * @throws ValidationException + */ public function releaseFrozenAmountInCurrentTransaction(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { $this->assertPositiveAmount($amount); @@ -213,6 +300,17 @@ class MerchantAccountCommandService extends BaseService ]); } + /** + * 增加可用余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function creditAvailableAmount(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->transactionRetry(function () use ($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo) { @@ -220,6 +318,18 @@ class MerchantAccountCommandService extends BaseService }); } + /** + * 在当前事务中增加可用余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + * @throws ValidationException + */ public function creditAvailableAmountInCurrentTransaction(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { $this->assertPositiveAmount($amount); @@ -257,6 +367,17 @@ class MerchantAccountCommandService extends BaseService ]); } + /** + * 扣减可用余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function debitAvailableAmount(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->transactionRetry(function () use ($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo) { @@ -264,6 +385,19 @@ class MerchantAccountCommandService extends BaseService }); } + /** + * 在当前事务中扣减可用余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + * @throws ValidationException + * @throws BalanceInsufficientException + */ public function debitAvailableAmountInCurrentTransaction(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { $this->assertPositiveAmount($amount); @@ -305,6 +439,12 @@ class MerchantAccountCommandService extends BaseService ]); } + /** + * 创建账户流水。 + * + * @param array $data 流水数据 + * @return MerchantAccountLedger 流水记录 + */ private function createLedger(array $data): MerchantAccountLedger { $data['ledger_no'] = $data['ledger_no'] ?? $this->generateNo('LG'); @@ -314,11 +454,24 @@ class MerchantAccountCommandService extends BaseService return $this->ledgerRepository->create($data); } + /** + * 按幂等键查询流水。 + * + * @param string $idempotencyKey 幂等键 + * @return MerchantAccountLedger|null 流水记录 + */ private function findLedgerByIdempotencyKey(string $idempotencyKey): ?MerchantAccountLedger { return $this->ledgerRepository->findByIdempotencyKey($idempotencyKey); } + /** + * 校验金额必须大于 0。 + * + * @param int $amount 金额(分) + * @return void + * @throws ValidationException + */ private function assertPositiveAmount(int $amount): void { if ($amount <= 0) { @@ -326,6 +479,17 @@ class MerchantAccountCommandService extends BaseService } } + /** + * 校验幂等流水与当前请求一致。 + * + * @param MerchantAccountLedger $ledger 流水 + * @param int $bizType 业务类型 + * @param string $bizNo 业务单号 + * @param int $amount 金额(分) + * @param int $direction 流向 + * @return void + * @throws ConflictException + */ private function assertLedgerMatch(MerchantAccountLedger $ledger, int $bizType, string $bizNo, int $amount, int $direction): void { if ((int) $ledger->biz_type !== $bizType || (int) $ledger->amount !== $amount || (string) $ledger->biz_no !== $bizNo || (int) $ledger->direction !== $direction) { @@ -337,6 +501,13 @@ class MerchantAccountCommandService extends BaseService } } + /** + * 归一化追踪号。 + * + * @param string $traceNo 追踪号 + * @param string $bizNo 业务单号 + * @return string 追踪号 + */ private function normalizeTraceNo(string $traceNo, string $bizNo): string { $traceNo = trim($traceNo); @@ -347,3 +518,8 @@ class MerchantAccountCommandService extends BaseService return $bizNo; } } + + + + + diff --git a/app/service/account/funds/MerchantAccountQueryService.php b/app/service/account/funds/MerchantAccountQueryService.php index 8a4cd3a..a215917 100644 --- a/app/service/account/funds/MerchantAccountQueryService.php +++ b/app/service/account/funds/MerchantAccountQueryService.php @@ -11,9 +11,19 @@ use app\repository\account\ledger\MerchantAccountLedgerRepository; * 商户账户查询服务。 * * 只负责账户列表、概览和快照查询,不承载资金变更逻辑。 + * + * @property MerchantAccountRepository $accountRepository 账户仓库 + * @property MerchantAccountLedgerRepository $ledgerRepository 流水仓库 */ class MerchantAccountQueryService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantAccountRepository $accountRepository 账户仓库 + * @param MerchantAccountLedgerRepository $ledgerRepository 流水仓库 + * @return void + */ public function __construct( protected MerchantAccountRepository $accountRepository, protected MerchantAccountLedgerRepository $ledgerRepository @@ -22,6 +32,11 @@ class MerchantAccountQueryService extends BaseService /** * 分页查询商户账户。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -73,6 +88,8 @@ class MerchantAccountQueryService extends BaseService /** * 资金中心概览。 + * + * @return array 概览数据 */ public function summary(): array { @@ -103,6 +120,9 @@ class MerchantAccountQueryService extends BaseService * 获取商户余额快照。 * * 用于后台展示和接口返回,不修改任何账户数据。 + * + * @param int $merchantId 商户ID + * @return array 快照数据 */ public function getBalanceSnapshot(int $merchantId): array { @@ -125,6 +145,9 @@ class MerchantAccountQueryService extends BaseService /** * 查询商户账户详情。 + * + * @param int $id 商户账户查询ID + * @return MerchantAccount|null 账户记录 */ public function findById(int $id): ?MerchantAccount { @@ -158,3 +181,6 @@ class MerchantAccountQueryService extends BaseService } } + + + diff --git a/app/service/account/funds/MerchantAccountService.php b/app/service/account/funds/MerchantAccountService.php index 8abf84e..f1f9fc2 100644 --- a/app/service/account/funds/MerchantAccountService.php +++ b/app/service/account/funds/MerchantAccountService.php @@ -7,95 +7,252 @@ use app\model\merchant\MerchantAccount; use app\model\merchant\MerchantAccountLedger; /** - * 商户余额门面服务。 + * 商户余额服务。 * - * 对外保留原有调用契约,内部委托给查询和命令两个子服务。 + * @property MerchantAccountQueryService $queryService 查询服务 + * @property MerchantAccountCommandService $commandService 命令服务 */ class MerchantAccountService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantAccountQueryService $queryService 查询服务 + * @param MerchantAccountCommandService $commandService 命令服务 + * @return void + */ public function __construct( protected MerchantAccountQueryService $queryService, protected MerchantAccountCommandService $commandService ) { } + /** + * 分页查询商户账户列表。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 + */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { return $this->queryService->paginate($filters, $page, $pageSize); } + /** + * 获取商户账户总览。 + * + * @return array 总览数据 + */ public function summary(): array { return $this->queryService->summary(); } + /** + * 获取或创建商户账户。 + * + * @param int $merchantId 商户ID + * @return MerchantAccount 账户记录 + */ public function ensureAccount(int $merchantId): MerchantAccount { return $this->commandService->ensureAccount($merchantId); } + /** + * 在当前事务中获取或创建商户账户。 + * + * @param int $merchantId 商户ID + * @return MerchantAccount 账户记录 + */ public function ensureAccountInCurrentTransaction(int $merchantId): MerchantAccount { return $this->commandService->ensureAccountInCurrentTransaction($merchantId); } + /** + * 冻结可用余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function freezeAmount(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->commandService->freezeAmount($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo); } + /** + * 在当前事务中冻结可用余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function freezeAmountInCurrentTransaction(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->commandService->freezeAmountInCurrentTransaction($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo); } + /** + * 扣减冻结余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function deductFrozenAmount(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->commandService->deductFrozenAmount($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo); } + /** + * 在当前事务中扣减冻结余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function deductFrozenAmountInCurrentTransaction(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->commandService->deductFrozenAmountInCurrentTransaction($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo); } + /** + * 释放冻结余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function releaseFrozenAmount(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->commandService->releaseFrozenAmount($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo); } + /** + * 在当前事务中释放冻结余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function releaseFrozenAmountInCurrentTransaction(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->commandService->releaseFrozenAmountInCurrentTransaction($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo); } + /** + * 增加可用余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function creditAvailableAmount(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->commandService->creditAvailableAmount($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo); } + /** + * 在当前事务中增加可用余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function creditAvailableAmountInCurrentTransaction(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->commandService->creditAvailableAmountInCurrentTransaction($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo); } + /** + * 扣减可用余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function debitAvailableAmount(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->commandService->debitAvailableAmount($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo); } + /** + * 在当前事务中扣减可用余额。 + * + * @param int $merchantId 商户ID + * @param int $amount 金额(分) + * @param string $bizNo 业务单号 + * @param string $idempotencyKey 幂等键 + * @param array $extJson 扩展字段 + * @param string $traceNo 追踪号 + * @return MerchantAccountLedger 流水记录 + */ public function debitAvailableAmountInCurrentTransaction(int $merchantId, int $amount, string $bizNo, string $idempotencyKey, array $extJson = [], string $traceNo = ''): MerchantAccountLedger { return $this->commandService->debitAvailableAmountInCurrentTransaction($merchantId, $amount, $bizNo, $idempotencyKey, $extJson, $traceNo); } + /** + * 获取余额快照。 + * + * @param int $merchantId 商户ID + * @return array 快照数据 + */ public function getBalanceSnapshot(int $merchantId): array { return $this->queryService->getBalanceSnapshot($merchantId); } + /** + * 按ID查询商户账户。 + * + * @param int $id 商户账户ID + * @return MerchantAccount|null 账户记录 + */ public function findById(int $id): ?MerchantAccount { return $this->queryService->findById($id); } } + + diff --git a/app/service/account/ledger/MerchantAccountLedgerService.php b/app/service/account/ledger/MerchantAccountLedgerService.php index ddff10c..5d44f55 100644 --- a/app/service/account/ledger/MerchantAccountLedgerService.php +++ b/app/service/account/ledger/MerchantAccountLedgerService.php @@ -9,11 +9,18 @@ use app\repository\account\ledger\MerchantAccountLedgerRepository; /** * 商户账户流水查询服务。 + * + * 负责商户账户流水的列表、详情和展示字段装配。 + * + * @property MerchantAccountLedgerRepository $merchantAccountLedgerRepository 商户账户流水仓库 */ class MerchantAccountLedgerService extends BaseService { /** - * 构造函数,注入流水仓库。 + * 构造方法。 + * + * @param MerchantAccountLedgerRepository $merchantAccountLedgerRepository 商户账户流水仓库 + * @return void */ public function __construct( protected MerchantAccountLedgerRepository $merchantAccountLedgerRepository @@ -22,6 +29,11 @@ class MerchantAccountLedgerService extends BaseService /** * 分页查询账户流水。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -72,6 +84,9 @@ class MerchantAccountLedgerService extends BaseService /** * 查询流水详情。 + * + * @param int $id 商户账户流水ID + * @return MerchantAccountLedger|null 流水模型 */ public function findById(int $id): ?MerchantAccountLedger { @@ -84,6 +99,9 @@ class MerchantAccountLedgerService extends BaseService /** * 格式化记录。 + * + * @param object $row 原始查询行 + * @return object 格式化后的记录 */ private function decorateRow(object $row): object { @@ -103,6 +121,8 @@ class MerchantAccountLedgerService extends BaseService /** * 构建查询。 + * + * @return \Illuminate\Database\Eloquent\Builder 查询构造器 */ private function baseQuery() { @@ -136,3 +156,6 @@ class MerchantAccountLedgerService extends BaseService } } + + + diff --git a/app/service/bootstrap/SystemBootstrapService.php b/app/service/bootstrap/SystemBootstrapService.php index 58215a5..5e02436 100644 --- a/app/service/bootstrap/SystemBootstrapService.php +++ b/app/service/bootstrap/SystemBootstrapService.php @@ -4,8 +4,19 @@ namespace app\service\bootstrap; use app\common\base\BaseService; +/** + * 系统引导服务。 + * + * 用于提供前端启动时需要的菜单树和字典项数据。 + */ class SystemBootstrapService extends BaseService { + /** + * 获取指定面板的菜单树。 + * + * @param string $panel 面板标识,通常为 `admin` 或 `merchant` + * @return array 菜单树 + */ public function getMenuTree(string $panel): array { $roles = $panel === 'merchant' ? ['common'] : ['admin']; @@ -14,6 +25,14 @@ class SystemBootstrapService extends BaseService return $this->normalizeRedirects($this->buildTree($nodes)); } + /** + * 获取字典项。 + * + * 支持一次获取全部字典,也支持按逗号分隔的 code 过滤。 + * + * @param string|null $code 字典编码 + * @return array 字典数据 + */ public function getDictItems(?string $code = null): array { $items = $this->dictItems(); @@ -34,16 +53,33 @@ class SystemBootstrapService extends BaseService return array_values(array_intersect_key($items, array_flip($codes))); } + /** + * 获取面板菜单配置原始节点。 + * + * @param string $panel 面板标识 + * @return array 原始节点 + */ protected function menuNodes(string $panel): array { return (array) config("menu.$panel", config('menu.admin', [])); } + /** + * 获取系统字典原始配置。 + * + * @return array 原始字典配置 + */ protected function dictItems(): array { return $this->normalizeDictItems((array) config('dict', [])); } + /** + * 将系统字典配置标准化为 code 索引结构。 + * + * @param array $items 原始配置 + * @return array 标准化后的字典项 + */ protected function normalizeDictItems(array $items): array { $normalized = []; @@ -80,6 +116,13 @@ class SystemBootstrapService extends BaseService return $normalized; } + /** + * 按角色过滤菜单节点。 + * + * @param array $nodes 菜单节点 + * @param array $roles 角色集合 + * @return array 过滤后的节点 + */ protected function filterByRoles(array $nodes, array $roles): array { return array_values(array_filter($nodes, function (array $node) use ($roles): bool { @@ -96,6 +139,12 @@ class SystemBootstrapService extends BaseService })); } + /** + * 将扁平菜单节点构造成树。 + * + * @param array $nodes 菜单节点 + * @return array 树结构 + */ protected function buildTree(array $nodes): array { $grouped = []; @@ -127,6 +176,12 @@ class SystemBootstrapService extends BaseService return $build('0'); } + /** + * 为有子节点的菜单补充默认重定向路径。 + * + * @param array $tree 菜单树 + * @return array 处理后的菜单树 + */ protected function normalizeRedirects(array $tree): array { foreach ($tree as &$node) { @@ -142,6 +197,12 @@ class SystemBootstrapService extends BaseService return $tree; } + /** + * 获取首个可渲染路径。 + * + * @param array $nodes 菜单节点 + * @return string|null 路径 + */ protected function firstRenderablePath(array $nodes): ?string { foreach ($nodes as $node) { @@ -155,3 +216,8 @@ class SystemBootstrapService extends BaseService } } + + + + + diff --git a/app/service/file/FileRecordCommandService.php b/app/service/file/FileRecordCommandService.php index 344ce43..d677938 100644 --- a/app/service/file/FileRecordCommandService.php +++ b/app/service/file/FileRecordCommandService.php @@ -4,6 +4,7 @@ namespace app\service\file; use app\common\base\BaseService; use app\exception\BusinessStateException; +use app\exception\ResourceNotFoundException; use app\exception\ValidationException; use app\repository\file\FileRecordRepository; use app\service\file\storage\StorageManager; @@ -12,9 +13,25 @@ use Webman\Http\UploadFile; /** * 文件命令服务。 + * + * 负责上传、远程导入和删除文件,并负责把文件内容同步到存储驱动和数据库。 + * + * @property FileRecordRepository $fileRecordRepository 文件记录仓库 + * @property FileRecordQueryService $fileRecordQueryService 文件记录查询服务 + * @property StorageManager $storageManager 存储管理器 + * @property StorageConfigService $storageConfigService 存储配置服务 */ class FileRecordCommandService extends BaseService { + /** + * 构造方法。 + * + * @param FileRecordRepository $fileRecordRepository 文件记录仓库 + * @param FileRecordQueryService $fileRecordQueryService 文件记录查询服务 + * @param StorageManager $storageManager 存储管理器 + * @param StorageConfigService $storageConfigService 存储配置服务 + * @return void + */ public function __construct( protected FileRecordRepository $fileRecordRepository, protected FileRecordQueryService $fileRecordQueryService, @@ -23,6 +40,15 @@ class FileRecordCommandService extends BaseService ) { } + /** + * 上传文件并创建记录。 + * + * @param UploadFile $file 上传文件 + * @param array $data 文件参数 + * @param int $createdBy 创建人ID + * @param string $createdByName 创建人名称 + * @return array 文件记录 + */ public function upload(UploadFile $file, array $data, int $createdBy = 0, string $createdByName = ''): array { $this->assertFileUpload($file); @@ -31,7 +57,7 @@ class FileRecordCommandService extends BaseService try { $scene = $this->storageConfigService->normalizeScene($data['scene'] ?? null, (string) $file->getUploadName(), (string) $file->getUploadMimeType()); $visibility = $this->storageConfigService->normalizeVisibility($data['visibility'] ?? null, $scene); - $engine = $this->storageConfigService->defaultEngine(); + $engine = $this->resolveStorageEngine($data); $result = $this->storageManager->storeFromPath( $sourcePath, @@ -62,7 +88,10 @@ class FileRecordCommandService extends BaseService 'created_by_name' => $createdByName, ]); } catch (\Throwable $e) { - $this->storageManager->delete($result); + try { + $this->storageManager->delete($result); + } catch (\Throwable) { + } throw $e; } @@ -74,18 +103,28 @@ class FileRecordCommandService extends BaseService } } + /** + * 导入远程文件并创建记录。 + * + * @param string $remoteUrl 远程地址 + * @param array $data 文件参数 + * @param int $createdBy 创建人ID + * @param string $createdByName 创建人名称 + * @return array 文件记录 + * @throws ValidationException + */ public function importRemote(string $remoteUrl, array $data, int $createdBy = 0, string $createdByName = ''): array { $remoteUrl = trim($remoteUrl); if ($remoteUrl === '') { - throw new ValidationException('远程图片地址不能为空'); + throw new ValidationException('远程文件地址不能为空'); } $download = $this->downloadRemoteFile($remoteUrl, (int) ($data['scene'] ?? 0)); try { $scene = $this->storageConfigService->normalizeScene($data['scene'] ?? null, $download['name'], $download['mime_type']); $visibility = $this->storageConfigService->normalizeVisibility($data['visibility'] ?? null, $scene); - $engine = $this->storageConfigService->defaultEngine(); + $engine = $this->resolveStorageEngine($data); $result = $this->storageManager->storeFromPath( $download['path'], @@ -116,7 +155,10 @@ class FileRecordCommandService extends BaseService 'created_by_name' => $createdByName, ]); } catch (\Throwable $e) { - $this->storageManager->delete($result); + try { + $this->storageManager->delete($result); + } catch (\Throwable) { + } throw $e; } @@ -128,18 +170,38 @@ class FileRecordCommandService extends BaseService } } + /** + * 删除文件记录。 + * + * @param int $id 文件记录命令ID + * @return bool 是否删除成功 + * @throws ResourceNotFoundException + * @throws BusinessStateException + */ public function delete(int $id): bool { $asset = $this->fileRecordRepository->findById($id); if (!$asset) { - return false; + throw new ResourceNotFoundException('文件不存在', ['id' => $id]); } $this->storageManager->delete($this->fileRecordQueryService->formatModel($asset)); - return $this->fileRecordRepository->deleteById($id); + if (!$this->fileRecordRepository->deleteById($id)) { + throw new BusinessStateException('文件删除失败'); + } + + return true; } + /** + * 校验上传文件是否合法。 + * + * @param UploadFile $file 文件 + * @return void + * @throws ValidationException + * @throws BusinessStateException + */ private function assertFileUpload(UploadFile $file): void { if (!$file->isValid()) { @@ -162,10 +224,19 @@ class FileRecordCommandService extends BaseService } } + /** + * 下载远程文件到临时文件。 + * + * @param string $remoteUrl 远程地址 + * @param int $scene 场景 + * @return array 下载结果 + * @throws ValidationException + * @throws BusinessStateException + */ private function downloadRemoteFile(string $remoteUrl, int $scene = 0): array { if (!filter_var($remoteUrl, FILTER_VALIDATE_URL)) { - throw new ValidationException('远程图片地址格式不正确'); + throw new ValidationException('远程文件地址格式不正确'); } $scheme = strtolower((string) parse_url($remoteUrl, PHP_URL_SCHEME)); @@ -175,7 +246,7 @@ class FileRecordCommandService extends BaseService $host = (string) parse_url($remoteUrl, PHP_URL_HOST); if ($host === '') { - throw new ValidationException('远程图片地址格式不正确'); + throw new ValidationException('远程文件地址格式不正确'); } if (filter_var($host, FILTER_VALIDATE_IP) && Request::isIntranetIp($host)) { @@ -278,4 +349,22 @@ class FileRecordCommandService extends BaseService 'scene' => $scene, ]; } + + /** + * 解析存储Engine + * + * @param array $data 数据 + * @return int 整数结果 + */ + private function resolveStorageEngine(array $data): int + { + if (!array_key_exists('storage_engine', $data) || $data['storage_engine'] === null || $data['storage_engine'] === '') { + return $this->storageConfigService->defaultEngine(); + } + + return $this->storageConfigService->normalizeEngine($data['storage_engine']); + } } + + + diff --git a/app/service/file/FileRecordQueryService.php b/app/service/file/FileRecordQueryService.php index 8c44c75..d192c7f 100644 --- a/app/service/file/FileRecordQueryService.php +++ b/app/service/file/FileRecordQueryService.php @@ -10,15 +10,35 @@ use app\service\file\storage\StorageManager; /** * 文件查询服务。 + * + * 负责文件记录的分页、详情、选项和展示数据格式化。 + * + * @property FileRecordRepository $fileRecordRepository 文件记录仓库 + * @property StorageManager $storageManager 存储管理器 */ class FileRecordQueryService extends BaseService { + /** + * 构造方法。 + * + * @param FileRecordRepository $fileRecordRepository 文件记录仓库 + * @param StorageManager $storageManager 存储管理器 + * @return void + */ public function __construct( protected FileRecordRepository $fileRecordRepository, protected StorageManager $storageManager ) { } + /** + * 分页查询文件记录。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 + */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { $query = $this->fileRecordRepository->query()->from('ma_file_asset as f'); @@ -50,6 +70,13 @@ class FileRecordQueryService extends BaseService return $paginator; } + /** + * 查询文件记录详情。 + * + * @param int $id 文件记录查询ID + * @return array 文件详情 + * @throws ResourceNotFoundException + */ public function detail(int $id): array { $asset = $this->fileRecordRepository->findById($id); @@ -60,7 +87,13 @@ class FileRecordQueryService extends BaseService return $this->formatModel($asset); } - public function formatModel(mixed $asset): array + /** + * 将文件记录格式化为前端展示结构。 + * + * @param array|object|null $asset 文件记录 + * @return array 展示数据 + */ + public function formatModel(array|object|null $asset): array { $id = (int) $this->field($asset, 'id', 0); $scene = (int) $this->field($asset, 'scene', FileConstant::SCENE_OTHER); @@ -68,10 +101,19 @@ class FileRecordQueryService extends BaseService $storageEngine = (int) $this->field($asset, 'storage_engine', FileConstant::STORAGE_LOCAL); $sourceType = (int) $this->field($asset, 'source_type', FileConstant::SOURCE_UPLOAD); $size = (int) $this->field($asset, 'size', 0); - $publicUrl = (string) $this->field($asset, 'url', ''); - $previewUrl = $publicUrl !== '' ? $publicUrl : $this->storageManager->temporaryUrl($this->normalizeAsset($asset)); - if ($previewUrl === '' && $id > 0) { - $previewUrl = '/adminapi/file-asset/' . $id . '/preview'; + $mimeType = strtolower((string) $this->field($asset, 'mime_type', '')); + $fileExt = strtolower((string) $this->field($asset, 'file_ext', '')); + $normalizedAsset = $this->normalizeAsset($asset); + $publicUrl = $visibility === FileConstant::VISIBILITY_PUBLIC + ? $this->storageManager->publicUrl($normalizedAsset) + : ''; + $previewable = $this->isPreviewable($scene, $mimeType, $fileExt); + $previewUrl = ''; + if ($previewable) { + $previewUrl = $publicUrl !== '' ? $publicUrl : $this->storageManager->temporaryUrl($normalizedAsset); + if ($previewUrl === '' && $id > 0) { + $previewUrl = '/adminapi/file-asset/' . $id . '/preview'; + } } return [ @@ -93,10 +135,11 @@ class FileRecordQueryService extends BaseService 'md5' => (string) $this->field($asset, 'md5', ''), 'object_key' => (string) $this->field($asset, 'object_key', ''), 'source_url' => (string) $this->field($asset, 'source_url', ''), - 'url' => $previewUrl, + 'url' => $publicUrl, 'public_url' => $publicUrl, 'preview_url' => $previewUrl, 'download_url' => $id > 0 ? '/adminapi/file-asset/' . $id . '/download' : '', + 'previewable' => $previewable, 'created_by' => (int) $this->field($asset, 'created_by', 0), 'created_by_name' => (string) $this->field($asset, 'created_by_name', ''), 'remark' => (string) $this->field($asset, 'remark', ''), @@ -106,6 +149,11 @@ class FileRecordQueryService extends BaseService ]; } + /** + * 获取文件记录选项。 + * + * @return array> 选项数据 + */ public function options(): array { return [ @@ -117,6 +165,12 @@ class FileRecordQueryService extends BaseService ]; } + /** + * 格式化文件大小。 + * + * @param int $size 文件大小(字节) + * @return string 格式化后的大小 + */ private function formatSize(int $size): string { if ($size <= 0) { @@ -134,6 +188,12 @@ class FileRecordQueryService extends BaseService return $index === 0 ? (string) (int) $value . ' ' . $units[$index] : number_format($value, 2) . ' ' . $units[$index]; } + /** + * 将映射表转换为前端选项。 + * + * @param array $map 映射表 + * @return array 选项列表 + */ private function toOptions(array $map): array { $options = []; @@ -147,7 +207,15 @@ class FileRecordQueryService extends BaseService return $options; } - private function field(mixed $asset, string $key, mixed $default = null): mixed + /** + * 从数组或对象中读取字段值。 + * + * @param array|object|null $asset 文件记录数据 + * @param string $key 字段名 + * @param mixed $default 默认值 + * @return mixed 文件字段值 + */ + private function field(array|object|null $asset, string $key, mixed $default = null): mixed { if (is_array($asset)) { return $asset[$key] ?? $default; @@ -160,7 +228,13 @@ class FileRecordQueryService extends BaseService return $default; } - private function normalizeAsset(mixed $asset): array + /** + * 归一化文件记录。 + * + * @param array|object|null $asset 原始记录 + * @return array 标准化记录 + */ + private function normalizeAsset(array|object|null $asset): array { return $this->field($asset, 'id', null) === null ? [] : [ 'id' => (int) $this->field($asset, 'id', 0), @@ -173,4 +247,25 @@ class FileRecordQueryService extends BaseService 'mime_type' => (string) $this->field($asset, 'mime_type', ''), ]; } + + /** + * 判断文件是否支持预览。 + * + * @param int $scene 场景 + * @param string $mimeType MIME 类型 + * @param string $fileExt 文件扩展名 + * @return bool 是否支持预览 + */ + private function isPreviewable(int $scene, string $mimeType, string $fileExt): bool + { + if ($scene === FileConstant::SCENE_IMAGE || str_starts_with($mimeType, 'image/')) { + return true; + } + + if ($scene === FileConstant::SCENE_TEXT || str_starts_with($mimeType, 'text/')) { + return true; + } + + return in_array($fileExt, ['pem', 'crt', 'cer', 'key'], true); + } } diff --git a/app/service/file/FileRecordService.php b/app/service/file/FileRecordService.php index 39861fa..0e6fc41 100644 --- a/app/service/file/FileRecordService.php +++ b/app/service/file/FileRecordService.php @@ -7,10 +7,22 @@ use app\service\file\storage\StorageManager; use Webman\Http\UploadFile; /** - * 文件门面服务。 + * 文件记录服务。 + * + * @property FileRecordQueryService $queryService 查询服务 + * @property FileRecordCommandService $commandService 命令服务 + * @property StorageManager $storageManager 存储管理器 */ class FileRecordService extends BaseService { + /** + * 构造方法。 + * + * @param FileRecordQueryService $queryService 查询服务 + * @param FileRecordCommandService $commandService 命令服务 + * @param StorageManager $storageManager 存储管理器 + * @return void + */ public function __construct( protected FileRecordQueryService $queryService, protected FileRecordCommandService $commandService, @@ -18,36 +30,85 @@ class FileRecordService extends BaseService ) { } + /** + * 分页查询文件记录。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 + */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { return $this->queryService->paginate($filters, $page, $pageSize); } + /** + * 查询文件记录详情。 + * + * @param int $id 文件记录ID + * @return array 文件详情 + */ public function detail(int $id): array { return $this->queryService->detail($id); } + /** + * 获取文件记录选项。 + * + * @return array 选项数据 + */ public function options(): array { return $this->queryService->options(); } + /** + * 上传文件并创建记录。 + * + * @param UploadFile $file 上传文件 + * @param array $data 文件参数 + * @param int $createdBy 创建人ID + * @param string $createdByName 创建人名称 + * @return array 文件记录 + */ public function upload(UploadFile $file, array $data, int $createdBy = 0, string $createdByName = ''): array { return $this->commandService->upload($file, $data, $createdBy, $createdByName); } + /** + * 导入远程文件并创建记录。 + * + * @param string $remoteUrl 远程地址 + * @param array $data 文件参数 + * @param int $createdBy 创建人ID + * @param string $createdByName 创建人名称 + * @return array 文件记录 + */ public function importRemote(string $remoteUrl, array $data, int $createdBy = 0, string $createdByName = ''): array { return $this->commandService->importRemote($remoteUrl, $data, $createdBy, $createdByName); } + /** + * 删除文件记录。 + * + * @param int $id 文件记录ID + * @return bool 是否删除成功 + */ public function delete(int $id): bool { return $this->commandService->delete($id); } + /** + * 获取文件预览响应。 + * + * @param int $id 文件记录ID + * @return \support\Response 响应对象 + */ public function previewResponse(int $id) { $asset = $this->queryService->detail($id); @@ -55,6 +116,12 @@ class FileRecordService extends BaseService return $this->storageManager->previewResponse($asset); } + /** + * 获取文件下载响应。 + * + * @param int $id 文件记录ID + * @return \support\Response 响应对象 + */ public function downloadResponse(int $id) { $asset = $this->queryService->detail($id); diff --git a/app/service/file/StorageConfigService.php b/app/service/file/StorageConfigService.php index 79a92fa..40e4210 100644 --- a/app/service/file/StorageConfigService.php +++ b/app/service/file/StorageConfigService.php @@ -4,32 +4,51 @@ namespace app\service\file; use app\common\base\BaseService; use app\common\constant\FileConstant; +use app\exception\BusinessStateException; /** * 文件存储配置服务。 + * + * 负责读取系统配置并统一整理文件场景、可见性、扩展名和存储引擎相关规则。 */ class StorageConfigService extends BaseService { + /** + * 获取默认存储引擎。 + * + * @return int 存储引擎 + */ public function defaultEngine(): int { return $this->normalizeSelectableEngine((int) sys_config(FileConstant::CONFIG_DEFAULT_ENGINE, FileConstant::STORAGE_LOCAL)); } + /** + * 获取本地公开访问基址。 + * + * @return string 基础地址 + * @throws BusinessStateException + */ public function localPublicBaseUrl(): string { - $baseUrl = trim((string) sys_config(FileConstant::CONFIG_LOCAL_PUBLIC_BASE_URL, '')); - if ($baseUrl !== '') { - return rtrim($baseUrl, '/'); - } - $siteUrl = trim((string) sys_config('site_url', '')); if ($siteUrl !== '') { + $baseUrl = trim((string) sys_config(FileConstant::CONFIG_LOCAL_PUBLIC_BASE_URL, '')); + if ($baseUrl !== '') { + return rtrim($baseUrl, '/'); + } + return rtrim($siteUrl, '/'); } - return ''; + throw new BusinessStateException('请先在系统配置中设置站点 URL'); } + /** + * 获取本地公开目录。 + * + * @return string 目录 + */ public function localPublicDir(): string { $dir = trim((string) sys_config(FileConstant::CONFIG_LOCAL_PUBLIC_DIR, 'storage/uploads'), "/ \t\n\r\0\x0B"); @@ -37,6 +56,11 @@ class StorageConfigService extends BaseService return $dir !== '' ? $dir : 'storage/uploads'; } + /** + * 获取本地私有目录。 + * + * @return string 目录 + */ public function localPrivateDir(): string { $dir = trim((string) sys_config(FileConstant::CONFIG_LOCAL_PRIVATE_DIR, 'storage/private'), "/ \t\n\r\0\x0B"); @@ -44,6 +68,11 @@ class StorageConfigService extends BaseService return $dir !== '' ? $dir : 'storage/private'; } + /** + * 获取上传大小上限。 + * + * @return int 字节数 + */ public function uploadMaxSizeBytes(): int { $mb = max(1, (int) sys_config(FileConstant::CONFIG_UPLOAD_MAX_SIZE_MB, 20)); @@ -51,6 +80,11 @@ class StorageConfigService extends BaseService return $mb * 1024 * 1024; } + /** + * 获取远程下载大小上限。 + * + * @return int 字节数 + */ public function remoteDownloadLimitBytes(): int { $mb = max(1, (int) sys_config(FileConstant::CONFIG_REMOTE_DOWNLOAD_LIMIT_MB, 10)); @@ -58,6 +92,11 @@ class StorageConfigService extends BaseService return $mb * 1024 * 1024; } + /** + * 获取允许上传的扩展名列表。 + * + * @return array 允许的扩展名列表 + */ public function allowedExtensions(): array { $raw = trim((string) sys_config(FileConstant::CONFIG_ALLOWED_EXTENSIONS, implode(',', FileConstant::defaultAllowedExtensions()))); @@ -70,6 +109,11 @@ class StorageConfigService extends BaseService return array_values(array_unique($extensions)); } + /** + * 获取阿里云 OSS 配置。 + * + * @return array OSS 配置 + */ public function ossConfig(): array { return [ @@ -82,6 +126,11 @@ class StorageConfigService extends BaseService ]; } + /** + * 获取腾讯云 COS 配置。 + * + * @return array COS 配置 + */ public function cosConfig(): array { return [ @@ -93,6 +142,14 @@ class StorageConfigService extends BaseService ]; } + /** + * 归一化文件场景。 + * + * @param int|string|null $scene 场景 + * @param string $originalName 原始文件名 + * @param string $mimeType MIME 类型 + * @return int 场景值 + */ public function normalizeScene(int|string|null $scene = null, string $originalName = '', string $mimeType = ''): int { $scene = (int) $scene; @@ -122,6 +179,13 @@ class StorageConfigService extends BaseService return FileConstant::SCENE_OTHER; } + /** + * 归一化文件可见性。 + * + * @param int|string|null $visibility 可见性 + * @param int $scene 场景 + * @return int 可见性值 + */ public function normalizeVisibility(int|string|null $visibility = null, int $scene = FileConstant::SCENE_OTHER): int { $visibility = (int) $visibility; @@ -134,6 +198,12 @@ class StorageConfigService extends BaseService : FileConstant::VISIBILITY_PRIVATE; } + /** + * 归一化存储引擎。 + * + * @param int|string|null $engine 存储引擎 + * @return int 存储引擎值 + */ public function normalizeEngine(int|string|null $engine = null): int { $engine = (int) $engine; @@ -141,6 +211,12 @@ class StorageConfigService extends BaseService return $this->normalizeSelectableEngine($engine); } + /** + * 获取场景对应的目录名。 + * + * @param int $scene 场景 + * @return string 目录名 + */ public function sceneFolder(int $scene): string { return match ($scene) { @@ -151,6 +227,14 @@ class StorageConfigService extends BaseService }; } + /** + * 构建对象键。 + * + * @param int $scene 场景 + * @param int $visibility 可见性 + * @param string $extension 扩展名 + * @return string 对象键 + */ public function buildObjectKey(int $scene, int $visibility, string $extension): string { $extension = strtolower(trim($extension, ". \t\n\r\0\x0B")); @@ -168,6 +252,13 @@ class StorageConfigService extends BaseService return trim($rootDir . '/' . $this->sceneFolder($scene) . '/' . $timestampPath . '/' . $name, '/'); } + /** + * 构建本地绝对路径。 + * + * @param int $visibility 可见性 + * @param string $objectKey 对象键 + * @return string 绝对路径 + */ public function buildLocalAbsolutePath(int $visibility, string $objectKey): string { $root = $visibility === FileConstant::VISIBILITY_PUBLIC @@ -178,6 +269,12 @@ class StorageConfigService extends BaseService return rtrim($root, DIRECTORY_SEPARATOR) . DIRECTORY_SEPARATOR . str_replace('/', DIRECTORY_SEPARATOR, $relativePath); } + /** + * 构建本地公开访问 URL。 + * + * @param string $objectKey 对象键 + * @return string 访问 URL + */ public function buildLocalPublicUrl(string $objectKey): string { $path = trim(str_replace('\\', '/', $objectKey), '/'); @@ -190,6 +287,12 @@ class StorageConfigService extends BaseService return '/' . $path; } + /** + * 归一化可选存储引擎。 + * + * @param int $engine 存储引擎 + * @return int 存储引擎值 + */ private function normalizeSelectableEngine(int $engine): int { return match ($engine) { @@ -200,3 +303,7 @@ class StorageConfigService extends BaseService }; } } + + + + diff --git a/app/service/file/storage/AbstractStorageDriver.php b/app/service/file/storage/AbstractStorageDriver.php index 61a1199..262fbba 100644 --- a/app/service/file/storage/AbstractStorageDriver.php +++ b/app/service/file/storage/AbstractStorageDriver.php @@ -3,57 +3,69 @@ namespace app\service\file\storage; use app\common\constant\FileConstant; +use app\exception\ResourceNotFoundException; use app\service\file\StorageConfigService; use support\Response; /** * 文件存储驱动抽象基类。 + * + * 提供文件存储驱动公共能力。 + * + * @property-read StorageConfigService $storageConfigService 存储配置服务 */ abstract class AbstractStorageDriver implements StorageDriverInterface { + /** + * 注入存储配置服务。 + * + * @param StorageConfigService $storageConfigService 存储配置服务 + */ public function __construct( protected StorageConfigService $storageConfigService ) { } + /** + * 从资产数组中读取指定字段。 + * + * @param array $asset 文件资产数据 + * @param string $key 字段名 + * @param mixed $default 默认值 + * @return mixed 资产字段值 + */ protected function assetValue(array $asset, string $key, mixed $default = null): mixed { return $asset[$key] ?? $default; } + /** + * 解析本地存储文件的绝对路径。 + * + * @param array $asset 文件资产数据 + * @return string 绝对路径 + */ protected function resolveLocalAbsolutePath(array $asset): string { $objectKey = trim((string) $this->assetValue($asset, 'object_key', '')); + if ($objectKey === '') { + return ''; + } + $visibility = (int) $this->assetValue($asset, 'visibility', FileConstant::VISIBILITY_PRIVATE); - $candidate = ''; - if ($objectKey !== '') { - $candidate = $this->storageConfigService->buildLocalAbsolutePath($visibility, $objectKey); - if ($candidate !== '' && is_file($candidate)) { - return $candidate; - } - } - - foreach (['url', 'public_url'] as $field) { - $url = trim((string) $this->assetValue($asset, $field, '')); - if ($url === '') { - continue; - } - - $parsedPath = (string) parse_url($url, PHP_URL_PATH); - if ($parsedPath === '') { - continue; - } - - $candidate = public_path() . DIRECTORY_SEPARATOR . ltrim($parsedPath, '/'); - if (is_file($candidate)) { - return $candidate; - } - } - - return $candidate; + return $this->storageConfigService->buildLocalAbsolutePath($visibility, $objectKey); } + /** + * 构造字符串响应。 + * + * @param string $body 响应内容 + * @param string $mimeType MIME 类型 + * @param int $status HTTP 状态码 + * @param array $headers 额外响应头 + * @return Response 响应对象 + */ protected function bodyResponse(string $body, string $mimeType = 'application/octet-stream', int $status = 200, array $headers = []): Response { $responseHeaders = array_merge([ @@ -63,6 +75,14 @@ abstract class AbstractStorageDriver implements StorageDriverInterface return response($body, $status, $responseHeaders); } + /** + * 构造文件下载响应。 + * + * @param string $body 响应内容 + * @param string $downloadName 下载文件名 + * @param string $mimeType MIME 类型 + * @return Response 响应对象 + */ protected function downloadBodyResponse(string $body, string $downloadName, string $mimeType = 'application/octet-stream'): Response { $response = $this->bodyResponse($body, $mimeType, 200, [ @@ -72,6 +92,14 @@ abstract class AbstractStorageDriver implements StorageDriverInterface return $response; } + /** + * 根据本地路径构造预览或下载响应。 + * + * @param string $path 本地文件路径 + * @param string $downloadName 下载文件名 + * @param bool $attachment 是否下载附件 + * @return Response 响应对象 + */ protected function responseFromPath(string $path, string $downloadName = '', bool $attachment = false): Response { if ($attachment) { @@ -81,26 +109,46 @@ abstract class AbstractStorageDriver implements StorageDriverInterface return response()->file($path); } + /** + * 构造本地文件预览响应。 + * + * @param array $asset 文件资产数据 + * @return Response 响应对象 + * @throws ResourceNotFoundException + */ protected function localPreviewResponse(array $asset): Response { $path = $this->resolveLocalAbsolutePath($asset); if ($path === '' || !is_file($path)) { - return response('文件不存在', 404); + throw new ResourceNotFoundException('文件不存在'); } return $this->responseFromPath($path); } + /** + * 构造本地文件下载响应。 + * + * @param array $asset 文件资产数据 + * @return Response 响应对象 + * @throws ResourceNotFoundException + */ protected function localDownloadResponse(array $asset): Response { $path = $this->resolveLocalAbsolutePath($asset); if ($path === '' || !is_file($path)) { - return response('文件不存在', 404); + throw new ResourceNotFoundException('文件不存在'); } return $this->responseFromPath($path, (string) $this->assetValue($asset, 'original_name', basename($path)), true); } + /** + * 根据文件场景返回目录前缀。 + * + * @param int $scene 文件场景 + * @return string 目录前缀 + */ protected function scenePrefix(int $scene): string { return match ($scene) { diff --git a/app/service/file/storage/CosStorageDriver.php b/app/service/file/storage/CosStorageDriver.php index b9e537c..aa004ba 100644 --- a/app/service/file/storage/CosStorageDriver.php +++ b/app/service/file/storage/CosStorageDriver.php @@ -4,20 +4,36 @@ namespace app\service\file\storage; use app\common\constant\FileConstant; use app\exception\BusinessStateException; +use app\exception\ResourceNotFoundException; use Qcloud\Cos\Client as CosClient; use support\Response; use Throwable; /** * 腾讯云 COS 文件存储驱动。 + * + * 负责对象上传、删除、公开地址生成和对象内容响应。 */ class CosStorageDriver extends AbstractStorageDriver { + /** + * 获取 COS 存储引擎标识。 + * + * @return int 存储引擎常量 + */ public function engine(): int { return FileConstant::STORAGE_TENCENT_COS; } + /** + * 将本地临时文件上传到 COS。 + * + * @param string $sourcePath 待上传文件路径 + * @param array $context 上传上下文,包含 object_key、visibility 等信息 + * @return array 上传后的资产数据 + * @throws BusinessStateException + */ public function storeFromPath(string $sourcePath, array $context): array { if (!is_file($sourcePath)) { @@ -33,6 +49,7 @@ class CosStorageDriver extends AbstractStorageDriver $client = $this->client($config); $objectKey = (string) ($context['object_key'] ?? ''); + $visibility = (int) ($context['visibility'] ?? FileConstant::VISIBILITY_PRIVATE); $client->putObject([ 'Bucket' => (string) $config['bucket'], 'Key' => $objectKey, @@ -40,23 +57,30 @@ class CosStorageDriver extends AbstractStorageDriver ]); $publicUrl = $this->publicUrl([ + 'visibility' => $visibility, 'object_key' => $objectKey, ]); - $visibility = (int) ($context['visibility'] ?? FileConstant::VISIBILITY_PRIVATE); return [ 'storage_engine' => $this->engine(), 'object_key' => $objectKey, 'url' => $visibility === FileConstant::VISIBILITY_PUBLIC ? $publicUrl : '', - 'public_url' => $publicUrl, + 'public_url' => $visibility === FileConstant::VISIBILITY_PUBLIC ? $publicUrl : '', ]; } + /** + * 删除 COS 对象。 + * + * @param array $asset 文件资产数据 + * @return bool 是否删除成功 + * @throws BusinessStateException + */ public function delete(array $asset): bool { $config = $this->storageConfigService->cosConfig(); if (trim((string) ($config['bucket'] ?? '')) === '') { - return false; + throw new BusinessStateException('腾讯云 COS 存储配置未完整'); } $objectKey = (string) ($asset['object_key'] ?? ''); @@ -73,23 +97,41 @@ class CosStorageDriver extends AbstractStorageDriver return true; } + /** + * 构造 COS 文件预览响应。 + * + * @param array $asset 文件资产数据 + * @return Response 响应对象 + */ public function previewResponse(array $asset): Response { - $url = $this->publicUrl($asset); - if ($url !== '') { - return redirect($url); - } - return $this->responseFromObject($asset, false); } + /** + * 构造 COS 文件下载响应。 + * + * @param array $asset 文件资产数据 + * @return Response 响应对象 + */ public function downloadResponse(array $asset): Response { return $this->responseFromObject($asset, true); } + /** + * 获取 COS 公开访问地址。 + * + * @param array $asset 文件资产数据 + * @return string 公共 URL + */ public function publicUrl(array $asset): string { + $visibility = (int) ($asset['visibility'] ?? FileConstant::VISIBILITY_PRIVATE); + if ($visibility !== FileConstant::VISIBILITY_PUBLIC) { + return ''; + } + $publicUrl = trim((string) ($asset['url'] ?? $asset['public_url'] ?? '')); if ($publicUrl !== '') { return $publicUrl; @@ -115,11 +157,17 @@ class CosStorageDriver extends AbstractStorageDriver return 'https://' . $bucket . '.cos.' . $region . '.myqcloud.com/' . ltrim($objectKey, '/'); } + /** + * 获取 COS 临时访问地址。 + * + * @param array $asset 文件资产数据 + * @return string 临时 URL + */ public function temporaryUrl(array $asset): string { $config = $this->storageConfigService->cosConfig(); if (trim((string) ($config['bucket'] ?? '')) === '' || trim((string) ($config['region'] ?? '')) === '') { - return $this->publicUrl($asset); + return ''; } try { @@ -134,10 +182,16 @@ class CosStorageDriver extends AbstractStorageDriver $objectKey ); } catch (Throwable) { - return $this->publicUrl($asset); + return ''; } } + /** + * 创建 COS 客户端。 + * + * @param array $config 存储配置 + * @return CosClient COS 客户端 + */ private function client(array $config): CosClient { return new CosClient([ @@ -149,13 +203,21 @@ class CosStorageDriver extends AbstractStorageDriver ]); } + /** + * 根据 COS 对象内容构造预览或下载响应。 + * + * @param array $asset 文件资产数据 + * @param bool $attachment 是否下载附件 + * @return Response 响应对象 + * @throws ResourceNotFoundException + */ private function responseFromObject(array $asset, bool $attachment): Response { $config = $this->storageConfigService->cosConfig(); $bucket = trim((string) ($config['bucket'] ?? '')); $objectKey = (string) ($asset['object_key'] ?? ''); if ($bucket === '' || $objectKey === '') { - return response('文件不存在', 404); + throw new ResourceNotFoundException('文件不存在'); } try { @@ -171,7 +233,7 @@ class CosStorageDriver extends AbstractStorageDriver } elseif (is_object($result) && method_exists($result, '__toString')) { $body = (string) $result; } elseif (is_array($result)) { - $body = (string) ($result['Body'] ?? $result['body'] ?? ''); + $body = (string) ($result['Body'] ?? $result['body'] ?? ''); } $mimeType = (string) ($asset['mime_type'] ?? 'application/octet-stream'); @@ -182,7 +244,7 @@ class CosStorageDriver extends AbstractStorageDriver return $this->bodyResponse($body, $mimeType); } catch (Throwable) { - return response('文件不存在', 404); + throw new ResourceNotFoundException('文件不存在'); } } } diff --git a/app/service/file/storage/LocalStorageDriver.php b/app/service/file/storage/LocalStorageDriver.php index 8cc8530..c4fab04 100644 --- a/app/service/file/storage/LocalStorageDriver.php +++ b/app/service/file/storage/LocalStorageDriver.php @@ -8,14 +8,29 @@ use support\Response; /** * 本地文件存储驱动。 + * + * 负责本地文件存储和响应构造。 */ class LocalStorageDriver extends AbstractStorageDriver { + /** + * 获取本地存储引擎标识。 + * + * @return int 存储引擎常量 + */ public function engine(): int { return FileConstant::STORAGE_LOCAL; } + /** + * 将临时文件写入本地存储目录。 + * + * @param string $sourcePath 待上传文件路径 + * @param array $context 上传上下文,包含 object_key、visibility、public_url 等信息 + * @return array 上传后的资产数据 + * @throws BusinessStateException + */ public function storeFromPath(string $sourcePath, array $context): array { if (!is_file($sourcePath)) { @@ -51,6 +66,13 @@ class LocalStorageDriver extends AbstractStorageDriver ]; } + /** + * 删除本地文件。 + * + * @param array $asset 文件资产数据 + * @return bool 是否删除成功 + * @throws BusinessStateException + */ public function delete(array $asset): bool { $path = $this->resolveLocalAbsolutePath($asset); @@ -58,26 +80,43 @@ class LocalStorageDriver extends AbstractStorageDriver return true; } - return @unlink($path); + if (@unlink($path)) { + return true; + } + + throw new BusinessStateException('本地文件删除失败'); } + /** + * 构造本地文件预览响应。 + * + * @param array $asset 文件资产数据 + * @return Response 响应对象 + */ public function previewResponse(array $asset): Response { return $this->localPreviewResponse($asset); } + /** + * 构造本地文件下载响应。 + * + * @param array $asset 文件资产数据 + * @return Response 响应对象 + */ public function downloadResponse(array $asset): Response { return $this->localDownloadResponse($asset); } + /** + * 获取本地公开访问地址。 + * + * @param array $asset 文件资产数据 + * @return string 公共 URL + */ public function publicUrl(array $asset): string { - $url = trim((string) ($asset['url'] ?? $asset['public_url'] ?? '')); - if ($url !== '') { - return $url; - } - $visibility = (int) ($asset['visibility'] ?? FileConstant::VISIBILITY_PRIVATE); if ($visibility !== FileConstant::VISIBILITY_PUBLIC) { return ''; @@ -91,13 +130,14 @@ class LocalStorageDriver extends AbstractStorageDriver return $this->storageConfigService->buildLocalPublicUrl($objectKey); } + /** + * 获取本地临时访问地址。 + * + * @param array $asset 文件资产数据 + * @return string 临时 URL + */ public function temporaryUrl(array $asset): string { - $url = trim((string) ($asset['url'] ?? $asset['public_url'] ?? '')); - if ($url !== '') { - return $url; - } - $visibility = (int) ($asset['visibility'] ?? FileConstant::VISIBILITY_PRIVATE); if ($visibility === FileConstant::VISIBILITY_PUBLIC) { return $this->publicUrl($asset); @@ -108,6 +148,12 @@ class LocalStorageDriver extends AbstractStorageDriver return $id > 0 ? '/adminapi/file-asset/' . $id . '/preview' : ''; } + /** + * 确保目标目录存在。 + * + * @param string $directory 目录路径 + * @throws BusinessStateException + */ private function ensureDirectory(string $directory): void { if (is_dir($directory)) { diff --git a/app/service/file/storage/OssStorageDriver.php b/app/service/file/storage/OssStorageDriver.php index caa1baf..253f6ef 100644 --- a/app/service/file/storage/OssStorageDriver.php +++ b/app/service/file/storage/OssStorageDriver.php @@ -4,20 +4,36 @@ namespace app\service\file\storage; use app\common\constant\FileConstant; use app\exception\BusinessStateException; +use app\exception\ResourceNotFoundException; use AlibabaCloud\Oss\V2 as Oss; use support\Response; use Throwable; /** * 阿里云 OSS 文件存储驱动。 + * + * 负责对象上传、删除、公开地址生成和预签名访问。 */ class OssStorageDriver extends AbstractStorageDriver { + /** + * 获取 OSS 存储引擎标识。 + * + * @return int 存储引擎常量 + */ public function engine(): int { return FileConstant::STORAGE_ALIYUN_OSS; } + /** + * 将本地临时文件上传到 OSS。 + * + * @param string $sourcePath 待上传文件路径 + * @param array $context 上传上下文,包含 object_key、visibility 等信息 + * @return array 上传后的资产数据 + * @throws BusinessStateException + */ public function storeFromPath(string $sourcePath, array $context): array { if (!is_file($sourcePath)) { @@ -33,6 +49,9 @@ class OssStorageDriver extends AbstractStorageDriver $client = $this->client($config); $objectKey = (string) ($context['object_key'] ?? ''); + $visibility = (int) ($context['visibility'] ?? FileConstant::VISIBILITY_PRIVATE); + + /** @var Oss\Models\PutObjectRequest $request */ $request = new Oss\Models\PutObjectRequest( bucket: (string) $config['bucket'], key: $objectKey @@ -42,23 +61,30 @@ class OssStorageDriver extends AbstractStorageDriver $client->putObject($request); $publicUrl = $this->publicUrl([ + 'visibility' => $visibility, 'object_key' => $objectKey, ]); - $visibility = (int) ($context['visibility'] ?? FileConstant::VISIBILITY_PRIVATE); return [ 'storage_engine' => $this->engine(), 'object_key' => $objectKey, 'url' => $visibility === FileConstant::VISIBILITY_PUBLIC ? $publicUrl : '', - 'public_url' => $publicUrl, + 'public_url' => $visibility === FileConstant::VISIBILITY_PUBLIC ? $publicUrl : '', ]; } + /** + * 删除 OSS 对象。 + * + * @param array $asset 文件资产数据 + * @return bool 是否删除成功 + * @throws BusinessStateException + */ public function delete(array $asset): bool { $config = $this->storageConfigService->ossConfig(); if (trim((string) ($config['bucket'] ?? '')) === '') { - return false; + throw new BusinessStateException('阿里云 OSS 存储配置未完整'); } $objectKey = (string) ($asset['object_key'] ?? ''); @@ -67,6 +93,8 @@ class OssStorageDriver extends AbstractStorageDriver } $client = $this->client($config); + + /** @var Oss\Models\DeleteObjectRequest $request */ $request = new Oss\Models\DeleteObjectRequest( bucket: (string) $config['bucket'], key: $objectKey @@ -76,23 +104,41 @@ class OssStorageDriver extends AbstractStorageDriver return true; } + /** + * 构造 OSS 文件预览响应。 + * + * @param array $asset 文件资产数据 + * @return Response 响应对象 + */ public function previewResponse(array $asset): Response { - $url = $this->publicUrl($asset); - if ($url !== '') { - return redirect($url); - } - return $this->responseFromObject($asset, false); } + /** + * 构造 OSS 文件下载响应。 + * + * @param array $asset 文件资产数据 + * @return Response 响应对象 + */ public function downloadResponse(array $asset): Response { return $this->responseFromObject($asset, true); } + /** + * 获取 OSS 公开访问地址。 + * + * @param array $asset 文件资产数据 + * @return string 公共 URL + */ public function publicUrl(array $asset): string { + $visibility = (int) ($asset['visibility'] ?? FileConstant::VISIBILITY_PRIVATE); + if ($visibility !== FileConstant::VISIBILITY_PUBLIC) { + return ''; + } + $publicUrl = trim((string) ($asset['url'] ?? $asset['public_url'] ?? '')); if ($publicUrl !== '') { return $publicUrl; @@ -120,11 +166,17 @@ class OssStorageDriver extends AbstractStorageDriver return 'https://' . $bucket . '.' . ltrim($endpoint, '/') . '/' . ltrim($objectKey, '/'); } + /** + * 获取 OSS 预签名访问地址。 + * + * @param array $asset 文件资产数据 + * @return string 临时 URL + */ public function temporaryUrl(array $asset): string { $config = $this->storageConfigService->ossConfig(); if (trim((string) ($config['bucket'] ?? '')) === '' || trim((string) ($config['region'] ?? '')) === '') { - return $this->publicUrl($asset); + return ''; } try { @@ -134,6 +186,7 @@ class OssStorageDriver extends AbstractStorageDriver return ''; } + /** @var Oss\Models\GetObjectRequest $request */ $request = new Oss\Models\GetObjectRequest( bucket: (string) $config['bucket'], key: $objectKey @@ -142,12 +195,19 @@ class OssStorageDriver extends AbstractStorageDriver return (string) ($result->url ?? ''); } catch (Throwable) { - return $this->publicUrl($asset); + return ''; } } + /** + * 创建 OSS 客户端。 + * + * @param array $config 存储配置 + * @return Oss\Client OSS 客户端 + */ private function client(array $config): Oss\Client { + /** @var Oss\Credentials\StaticCredentialsProvider $provider */ $provider = new Oss\Credentials\StaticCredentialsProvider( accessKeyId: (string) $config['access_key_id'], accessKeySecret: (string) $config['access_key_secret'] @@ -165,17 +225,26 @@ class OssStorageDriver extends AbstractStorageDriver return new Oss\Client($cfg); } + /** + * 根据 OSS 对象内容构造预览或下载响应。 + * + * @param array $asset 文件资产数据 + * @param bool $attachment 是否下载附件 + * @return Response 响应对象 + * @throws ResourceNotFoundException + */ private function responseFromObject(array $asset, bool $attachment): Response { $config = $this->storageConfigService->ossConfig(); $bucket = trim((string) ($config['bucket'] ?? '')); $objectKey = (string) ($asset['object_key'] ?? ''); if ($bucket === '' || $objectKey === '') { - return response('文件不存在', 404); + throw new ResourceNotFoundException('文件不存在'); } try { $client = $this->client($config); + /** @var Oss\Models\GetObjectRequest $request */ $request = new Oss\Models\GetObjectRequest( bucket: $bucket, key: $objectKey @@ -190,7 +259,7 @@ class OssStorageDriver extends AbstractStorageDriver return $this->bodyResponse($body, $mimeType); } catch (Throwable) { - return response('文件不存在', 404); + throw new ResourceNotFoundException('文件不存在'); } } } diff --git a/app/service/file/storage/RemoteUrlStorageDriver.php b/app/service/file/storage/RemoteUrlStorageDriver.php index af0aef5..18627f8 100644 --- a/app/service/file/storage/RemoteUrlStorageDriver.php +++ b/app/service/file/storage/RemoteUrlStorageDriver.php @@ -4,48 +4,95 @@ namespace app\service\file\storage; use app\common\constant\FileConstant; use app\exception\BusinessStateException; +use app\exception\ResourceNotFoundException; use support\Response; /** - * 远程引用驱动。 + * 远程引用文件存储驱动。 + * + * 仅保存原始远程 URL,不做本地落盘或对象存储复制。 */ class RemoteUrlStorageDriver extends AbstractStorageDriver { + /** + * 获取远程引用引擎标识。 + * + * @return int 存储引擎常量 + */ public function engine(): int { return FileConstant::STORAGE_REMOTE_URL; } + /** + * 远程引用模式不支持直接上传。 + * + * @param string $sourcePath 待上传文件路径 + * @param array $context 上传上下文 + * @return array 上传后的资产数据 + * @throws BusinessStateException + */ public function storeFromPath(string $sourcePath, array $context): array { throw new BusinessStateException('远程引用模式不支持直接上传,请先下载后再入库'); } + /** + * 远程引用模式不需要真正删除对象。 + * + * @param array $asset 文件资产数据 + * @return bool 是否删除成功 + */ public function delete(array $asset): bool { return true; } + /** + * 直接跳转到源站地址进行预览。 + * + * @param array $asset 文件资产数据 + * @return Response 响应对象 + * @throws ResourceNotFoundException + */ public function previewResponse(array $asset): Response { $url = (string) ($asset['source_url'] ?? $asset['url'] ?? ''); if ($url === '') { - return response('文件不存在', 404); + throw new ResourceNotFoundException('文件不存在'); } return redirect($url); } + /** + * 远程引用文件的下载行为与预览保持一致。 + * + * @param array $asset 文件资产数据 + * @return Response 响应对象 + */ public function downloadResponse(array $asset): Response { return $this->previewResponse($asset); } + /** + * 获取原始远程地址。 + * + * @param array $asset 文件资产数据 + * @return string 远程 URL + */ public function publicUrl(array $asset): string { return (string) ($asset['source_url'] ?? $asset['url'] ?? ''); } + /** + * 获取原始远程地址。 + * + * @param array $asset 文件资产数据 + * @return string 远程 URL + */ public function temporaryUrl(array $asset): string { return (string) ($asset['source_url'] ?? $asset['url'] ?? ''); diff --git a/app/service/file/storage/StorageDriverInterface.php b/app/service/file/storage/StorageDriverInterface.php index 6cdbf28..f86be4c 100644 --- a/app/service/file/storage/StorageDriverInterface.php +++ b/app/service/file/storage/StorageDriverInterface.php @@ -6,20 +6,64 @@ use support\Response; /** * 文件存储驱动接口。 + * + * 统一定义文件存储驱动能力。 */ interface StorageDriverInterface { + /** + * 获取存储引擎标识。 + * + * @return int 存储引擎常量 + */ public function engine(): int; + /** + * 将本地临时文件写入存储后端。 + * + * @param string $sourcePath 待上传的本地临时文件路径 + * @param array $context 上传上下文,通常包含 object_key、visibility 等信息 + * @return array 上传后的资产数据 + */ public function storeFromPath(string $sourcePath, array $context): array; + /** + * 删除指定文件资产。 + * + * @param array $asset 文件资产数据 + * @return bool 是否删除成功 + */ public function delete(array $asset): bool; + /** + * 构造文件预览响应。 + * + * @param array $asset 文件资产数据 + * @return Response 响应对象 + */ public function previewResponse(array $asset): Response; + /** + * 构造文件下载响应。 + * + * @param array $asset 文件资产数据 + * @return Response 响应对象 + */ public function downloadResponse(array $asset): Response; + /** + * 获取公开访问地址。 + * + * @param array $asset 文件资产数据 + * @return string 公开 URL + */ public function publicUrl(array $asset): string; + /** + * 获取临时访问地址。 + * + * @param array $asset 文件资产数据 + * @return string 临时 URL + */ public function temporaryUrl(array $asset): string; } diff --git a/app/service/file/storage/StorageManager.php b/app/service/file/storage/StorageManager.php index 166ccbd..49c50a8 100644 --- a/app/service/file/storage/StorageManager.php +++ b/app/service/file/storage/StorageManager.php @@ -8,9 +8,27 @@ use support\Response; /** * 文件存储驱动管理器。 + * + * 负责根据存储引擎分发文件操作。 + * + * @property StorageConfigService $storageConfigService 存储配置服务 + * @property LocalStorageDriver $localStorageDriver 本地存储驱动 + * @property OssStorageDriver $ossStorageDriver oss存储驱动 + * @property CosStorageDriver $cosStorageDriver cos存储驱动 + * @property RemoteUrlStorageDriver $remoteUrlStorageDriver remoteUrl存储驱动 */ class StorageManager { + /** + * 构造方法。 + * + * @param StorageConfigService $storageConfigService 存储配置服务 + * @param LocalStorageDriver $localStorageDriver 本地存储驱动 + * @param OssStorageDriver $ossStorageDriver oss存储驱动 + * @param CosStorageDriver $cosStorageDriver cos存储驱动 + * @param RemoteUrlStorageDriver $remoteUrlStorageDriver remoteUrl存储驱动 + * @return void + */ public function __construct( protected StorageConfigService $storageConfigService, protected LocalStorageDriver $localStorageDriver, @@ -20,6 +38,18 @@ class StorageManager ) { } + /** + * 构建存储上下文。 + * + * @param string $sourcePath 源文件路径 + * @param string $originalName 原始文件名 + * @param int|null $scene 场景 + * @param int|null $visibility 可见性 + * @param int|null $engine 存储引擎 + * @param string|null $sourceUrl 源地址 + * @param string $sourceType 来源类型 + * @return array 上下文数据 + */ public function buildContext( string $sourcePath, string $originalName, @@ -58,6 +88,18 @@ class StorageManager ]; } + /** + * 从文件路径保存文件。 + * + * @param string $sourcePath 源文件路径 + * @param string $originalName 原始文件名 + * @param int|null $scene 场景 + * @param int|null $visibility 可见性 + * @param int|null $engine 存储引擎 + * @param string|null $sourceUrl 源地址 + * @param string $sourceType 来源类型 + * @return array 保存结果 + */ public function storeFromPath( string $sourcePath, string $originalName, @@ -73,36 +115,72 @@ class StorageManager return array_merge($context, $driver->storeFromPath($sourcePath, $context)); } + /** + * 删除存储对象。 + * + * @param array $asset 文件记录 + * @return bool 是否删除成功 + */ public function delete(array $asset): bool { return $this->resolveDriver((int) ($asset['storage_engine'] ?? FileConstant::STORAGE_LOCAL)) ->delete($asset); } + /** + * 获取预览响应。 + * + * @param array $asset 文件记录 + * @return Response 响应对象 + */ public function previewResponse(array $asset): Response { return $this->resolveDriver((int) ($asset['storage_engine'] ?? FileConstant::STORAGE_LOCAL)) ->previewResponse($asset); } + /** + * 获取下载响应。 + * + * @param array $asset 文件记录 + * @return Response 响应对象 + */ public function downloadResponse(array $asset): Response { return $this->resolveDriver((int) ($asset['storage_engine'] ?? FileConstant::STORAGE_LOCAL)) ->downloadResponse($asset); } + /** + * 获取公开访问 URL。 + * + * @param array $asset 文件记录 + * @return string 访问 URL + */ public function publicUrl(array $asset): string { return $this->resolveDriver((int) ($asset['storage_engine'] ?? FileConstant::STORAGE_LOCAL)) ->publicUrl($asset); } + /** + * 获取临时访问 URL。 + * + * @param array $asset 文件记录 + * @return string 访问 URL + */ public function temporaryUrl(array $asset): string { return $this->resolveDriver((int) ($asset['storage_engine'] ?? FileConstant::STORAGE_LOCAL)) ->temporaryUrl($asset); } + /** + * 解析对应的存储驱动。 + * + * @param int $engine 存储引擎 + * @return StorageDriverInterface 存储驱动 + */ public function resolveDriver(int $engine): StorageDriverInterface { return match ($engine) { @@ -114,6 +192,14 @@ class StorageManager }; } + /** + * 按存储引擎构建公开访问 URL。 + * + * @param int $engine 存储引擎 + * @param int $visibility 可见性 + * @param string $objectKey 对象键 + * @return string 访问 URL + */ private function buildPublicUrlByEngine(int $engine, int $visibility, string $objectKey): string { if ($engine === FileConstant::STORAGE_LOCAL && $visibility === FileConstant::VISIBILITY_PUBLIC) { @@ -123,6 +209,13 @@ class StorageManager return ''; } + /** + * 估算 MIME 类型。 + * + * @param string $sourcePath 源文件路径 + * @param string $originalName 原始文件名 + * @return string MIME 类型 + */ private function guessMimeType(string $sourcePath, string $originalName): string { $mimeType = ''; @@ -156,3 +249,5 @@ class StorageManager }; } } + + diff --git a/app/service/merchant/MerchantCommandService.php b/app/service/merchant/MerchantCommandService.php index 50cc1d5..391f7eb 100644 --- a/app/service/merchant/MerchantCommandService.php +++ b/app/service/merchant/MerchantCommandService.php @@ -24,9 +24,37 @@ use app\service\merchant\security\MerchantApiCredentialService; * 商户命令服务。 * * 负责商户创建、更新、删除、密码和登录元数据这类写操作。 + * + * @property MerchantRepository $merchantRepository 商户仓库 + * @property MerchantGroupRepository $merchantGroupRepository 商户分组仓库 + * @property MerchantQueryService $merchantQueryService 商户查询服务 + * @property MerchantApiCredentialService $merchantApiCredentialService 商户 API 凭证服务 + * @property MerchantAccountRepository $merchantAccountRepository 商户账户仓库 + * @property MerchantApiCredentialRepository $merchantApiCredentialRepository 商户 API 凭证仓库 + * @property PaymentChannelRepository $paymentChannelRepository 支付渠道仓库 + * @property BizOrderRepository $bizOrderRepository 业务订单仓库 + * @property RefundOrderRepository $refundOrderRepository 退款单仓库 + * @property SettlementOrderRepository $settlementOrderRepository 结算订单仓库 + * @property MerchantAccountService $merchantAccountService 商户账户服务 */ class MerchantCommandService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantRepository $merchantRepository 商户仓库 + * @param MerchantGroupRepository $merchantGroupRepository 商户分组仓库 + * @param MerchantQueryService $merchantQueryService 商户查询服务 + * @param MerchantApiCredentialService $merchantApiCredentialService 商户 API 凭证服务 + * @param MerchantAccountRepository $merchantAccountRepository 商户账户仓库 + * @param MerchantApiCredentialRepository $merchantApiCredentialRepository 商户 API 凭证仓库 + * @param PaymentChannelRepository $paymentChannelRepository 支付渠道仓库 + * @param BizOrderRepository $bizOrderRepository 业务订单仓库 + * @param RefundOrderRepository $refundOrderRepository 退款单仓库 + * @param SettlementOrderRepository $settlementOrderRepository 结算订单仓库 + * @param MerchantAccountService $merchantAccountService 商户账户服务 + * @return void + */ public function __construct( protected MerchantRepository $merchantRepository, protected MerchantGroupRepository $merchantGroupRepository, @@ -42,6 +70,13 @@ class MerchantCommandService extends BaseService ) { } + /** + * 创建商户。 + * + * @param array $data 商户数据 + * @return Merchant 商户模型 + * @throws ValidationException + */ public function create(array $data): Merchant { return $this->transaction(function () use ($data) { @@ -96,6 +131,13 @@ class MerchantCommandService extends BaseService }); } + /** + * 更新商户。 + * + * @param int $merchantId 商户ID + * @param array $data 商户数据 + * @return Merchant|null 商户模型 + */ public function update(int $merchantId, array $data): ?Merchant { $merchant = $this->merchantRepository->find($merchantId); @@ -132,6 +174,14 @@ class MerchantCommandService extends BaseService return $this->merchantRepository->find($merchantId); } + /** + * 删除商户命令 + * + * @param int $merchantId 商户ID + * @return bool 是否删除成功 + * @throws ResourceNotFoundException + * @throws BusinessStateException + */ public function delete(int $merchantId): bool { $merchant = $this->merchantRepository->find($merchantId); @@ -160,6 +210,14 @@ class MerchantCommandService extends BaseService return $this->merchantRepository->deleteById($merchantId); } + /** + * 重置商户密码。 + * + * @param int $merchantId 商户ID + * @param string $password 新密码 + * @return Merchant 商户模型 + * @throws ResourceNotFoundException + */ public function resetPassword(int $merchantId, string $password): Merchant { $merchant = $this->merchantRepository->find($merchantId); @@ -175,11 +233,25 @@ class MerchantCommandService extends BaseService return $this->merchantRepository->find($merchantId); } + /** + * 校验商户命令密码 + * + * @param Merchant $merchant 商户 + * @param string $password 密码 + * @return bool 是否密码匹配 + */ public function verifyPassword(Merchant $merchant, string $password): bool { return $password !== '' && password_verify($password, (string) $merchant->password_hash); } + /** + * 更新商户命令登录信息 + * + * @param int $merchantId 商户ID + * @param string $ip ip + * @return void + */ public function touchLoginMeta(int $merchantId, string $ip = ''): void { $this->merchantRepository->updateById($merchantId, [ @@ -188,6 +260,13 @@ class MerchantCommandService extends BaseService ]); } + /** + * 生成或重置商户 API 凭证。 + * + * @param int $merchantId 商户ID + * @return array 凭证数据 + * @throws ResourceNotFoundException + */ public function issueCredential(int $merchantId): array { $merchant = $this->merchantQueryService->findById($merchantId); @@ -205,6 +284,14 @@ class MerchantCommandService extends BaseService ]; } + /** + * 根据商户号查询已启用商户。 + * + * @param string $merchantNo 商户号 + * @return Merchant 商户模型 + * @throws ResourceNotFoundException + * @throws BusinessStateException + */ public function findEnabledMerchantByNo(string $merchantNo): Merchant { $merchant = $this->merchantRepository->findByMerchantNo($merchantNo); @@ -220,6 +307,14 @@ class MerchantCommandService extends BaseService return $merchant; } + /** + * 校验商户是否启用。 + * + * @param int $merchantId 商户ID + * @return Merchant 商户模型 + * @throws ResourceNotFoundException + * @throws BusinessStateException + */ public function ensureMerchantEnabled(int $merchantId): Merchant { $merchant = $this->merchantRepository->find($merchantId); @@ -235,6 +330,14 @@ class MerchantCommandService extends BaseService return $merchant; } + /** + * 校验商户分组是否启用。 + * + * @param int $groupId 分组ID + * @return MerchantGroup 商户分组模型 + * @throws ResourceNotFoundException + * @throws BusinessStateException + */ public function ensureMerchantGroupEnabled(int $groupId): MerchantGroup { $group = $this->merchantGroupRepository->find($groupId); @@ -250,6 +353,11 @@ class MerchantCommandService extends BaseService return $group; } + /** + * 生成商户No + * + * @return string 商户号 + */ private function generateMerchantNo(): string { do { @@ -261,6 +369,8 @@ class MerchantCommandService extends BaseService /** * 生成商户初始临时密码。 + * + * @return string 临时密码 */ private function generateTemporaryPassword(): string { diff --git a/app/service/merchant/MerchantOverviewQueryService.php b/app/service/merchant/MerchantOverviewQueryService.php index 65153d2..cb49b15 100644 --- a/app/service/merchant/MerchantOverviewQueryService.php +++ b/app/service/merchant/MerchantOverviewQueryService.php @@ -16,10 +16,29 @@ use app\repository\payment\trade\PayOrderRepository; /** * 商户总览查询服务。 * - * 负责商户资料、接口凭证、资金、路由、通道和最近交易的总览拼装。 + * 负责拼装商户资料、接口凭证、资金、路由、通道以及最近交易和清结算的总览数据。 + * + * @property MerchantQueryService $merchantQueryService 商户查询服务 + * @property MerchantAccountRepository $merchantAccountRepository 商户账户仓库 + * @property MerchantApiCredentialRepository $merchantApiCredentialRepository 商户 API 凭证仓库 + * @property PaymentChannelRepository $paymentChannelRepository 支付渠道仓库 + * @property PaymentPollGroupBindRepository $paymentPollGroupBindRepository 支付轮询分组绑定仓库 + * @property PayOrderRepository $payOrderRepository 支付订单仓库 + * @property SettlementOrderRepository $settlementOrderRepository 清结算订单仓库 */ class MerchantOverviewQueryService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantQueryService $merchantQueryService 商户查询服务 + * @param MerchantAccountRepository $merchantAccountRepository 商户账户仓库 + * @param MerchantApiCredentialRepository $merchantApiCredentialRepository 商户 API 凭证仓库 + * @param PaymentChannelRepository $paymentChannelRepository 支付渠道仓库 + * @param PaymentPollGroupBindRepository $paymentPollGroupBindRepository 支付轮询分组绑定仓库 + * @param PayOrderRepository $payOrderRepository 支付订单仓库 + * @param SettlementOrderRepository $settlementOrderRepository 清结算订单仓库 + */ public function __construct( protected MerchantQueryService $merchantQueryService, protected MerchantAccountRepository $merchantAccountRepository, @@ -33,6 +52,10 @@ class MerchantOverviewQueryService extends BaseService /** * 查询商户总览。 + * + * @param int $merchantId 商户ID + * @return array 总览数据 + * @throws ResourceNotFoundException */ public function overview(int $merchantId): array { @@ -128,3 +151,6 @@ class MerchantOverviewQueryService extends BaseService ]; } } + + + diff --git a/app/service/merchant/MerchantQueryService.php b/app/service/merchant/MerchantQueryService.php index 25b7f0c..56e34c3 100644 --- a/app/service/merchant/MerchantQueryService.php +++ b/app/service/merchant/MerchantQueryService.php @@ -14,9 +14,21 @@ use app\repository\merchant\base\MerchantRepository; * 商户查询服务。 * * 负责商户列表、详情和总览这类只读查询。 + * + * @property MerchantRepository $merchantRepository 商户仓库 + * @property MerchantGroupRepository $merchantGroupRepository 商户分组仓库 + * @property MerchantPolicyRepository $merchantPolicyRepository 商户策略仓库 */ class MerchantQueryService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantRepository $merchantRepository 商户仓库 + * @param MerchantGroupRepository $merchantGroupRepository 商户分组仓库 + * @param MerchantPolicyRepository $merchantPolicyRepository 商户策略仓库 + * @return void + */ public function __construct( protected MerchantRepository $merchantRepository, protected MerchantGroupRepository $merchantGroupRepository, @@ -24,6 +36,14 @@ class MerchantQueryService extends BaseService ) { } + /** + * 分页查询商户列表。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页对象 + */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { $query = $this->merchantRepository->query() @@ -94,6 +114,14 @@ class MerchantQueryService extends BaseService ->paginate(max(1, $pageSize), ['*'], 'page', max(1, $page)); } + /** + * 分页查询商户列表并附带分组选项。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array 页面数据 + */ public function paginateWithGroupOptions(array $filters = [], int $page = 1, int $pageSize = 10): array { $paginator = $this->paginate($filters, $page, $pageSize); @@ -107,6 +135,11 @@ class MerchantQueryService extends BaseService ]; } + /** + * 获取启用商户下拉选项。 + * + * @return array 商户选项列表 + */ public function enabledOptions(): array { return $this->merchantRepository->enabledList(['id', 'merchant_no', 'merchant_name']) @@ -120,6 +153,11 @@ class MerchantQueryService extends BaseService ->all(); } + /** + * 获取启用商户分组选项。 + * + * @return array 分组选项列表 + */ public function enabledGroupOptions(): array { return $this->merchantGroupRepository->enabledList(['id', 'group_name']) @@ -133,6 +171,14 @@ class MerchantQueryService extends BaseService ->all(); } + /** + * 搜索启用商户下拉选项。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array 搜索结果 + */ public function searchOptions(array $filters = [], int $page = 1, int $pageSize = 20): array { $query = $this->merchantRepository->query() @@ -181,6 +227,12 @@ class MerchantQueryService extends BaseService ]; } + /** + * 按 ID 查询商户详情。 + * + * @param int $merchantId 商户ID + * @return object|null 商户详情对象 + */ public function findById(int $merchantId): ?object { return $this->merchantRepository->query() @@ -217,11 +269,23 @@ class MerchantQueryService extends BaseService ->first(); } + /** + * 查询商户策略。 + * + * @param int $merchantId 商户ID + * @return MerchantPolicy|null 商户策略模型 + */ public function findPolicy(int $merchantId): ?MerchantPolicy { return $this->merchantPolicyRepository->findByMerchantId($merchantId); } + /** + * 规范化商户 ID 列表。 + * + * @param array|string|int $ids 原始 ID 值 + * @return array 正整数 ID 列表 + */ private function normalizeIds(array|string|int $ids): array { if (is_string($ids)) { @@ -233,3 +297,4 @@ class MerchantQueryService extends BaseService return array_values(array_filter(array_map(static fn ($id) => (int) $id, $ids), static fn ($id) => $id > 0)); } } + diff --git a/app/service/merchant/MerchantService.php b/app/service/merchant/MerchantService.php index f70c6c2..df2319f 100644 --- a/app/service/merchant/MerchantService.php +++ b/app/service/merchant/MerchantService.php @@ -8,12 +8,22 @@ use app\model\merchant\MerchantGroup; use app\model\merchant\MerchantPolicy; /** - * 商户基础服务门面。 + * 商户服务。 * - * 仅保留现有控制器和其他服务依赖的统一入口。 + * @property MerchantQueryService $queryService 查询服务 + * @property MerchantCommandService $commandService 命令服务 + * @property MerchantOverviewQueryService $overviewQueryService 总览查询服务 */ class MerchantService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantQueryService $queryService 查询服务 + * @param MerchantCommandService $commandService 命令服务 + * @param MerchantOverviewQueryService $overviewQueryService 总览查询服务 + * @return void + */ public function __construct( protected MerchantQueryService $queryService, protected MerchantCommandService $commandService, @@ -21,36 +31,83 @@ class MerchantService extends BaseService ) { } + /** + * 分页查询商户列表。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 + */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { return $this->queryService->paginate($filters, $page, $pageSize); } + /** + * 分页查询商户列表并附带分组选项。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array 页面数据 + */ public function paginateWithGroupOptions(array $filters = [], int $page = 1, int $pageSize = 10): array { return $this->queryService->paginateWithGroupOptions($filters, $page, $pageSize); } + /** + * 获取启用商户下拉选项。 + * + * @return array 商户选项列表 + */ public function enabledOptions(): array { return $this->queryService->enabledOptions(); } + /** + * 搜索启用商户下拉选项。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array 搜索结果 + */ public function searchOptions(array $filters = [], int $page = 1, int $pageSize = 20): array { return $this->queryService->searchOptions($filters, $page, $pageSize); } + /** + * 按 ID 查询商户详情。 + * + * @param int $merchantId 商户ID + * @return object|null 商户详情 + */ public function findById(int $merchantId): ?object { return $this->queryService->findById($merchantId); } + /** + * 创建商户。 + * + * @param array $data 商户数据 + * @return Merchant 商户模型 + */ public function create(array $data): Merchant { return $this->commandService->create($data); } + /** + * 创建商户并返回详情。 + * + * @param array $data 商户数据 + * @return object|null 商户详情 + */ public function createWithDetail(array $data): ?object { $merchant = $this->create($data); @@ -62,11 +119,25 @@ class MerchantService extends BaseService return $detail ?? $merchant; } + /** + * 更新商户。 + * + * @param int $merchantId 商户ID + * @param array $data 商户数据 + * @return Merchant|null 商户模型 + */ public function update(int $merchantId, array $data): ?Merchant { return $this->commandService->update($merchantId, $data); } + /** + * 更新商户并返回详情。 + * + * @param int $merchantId 商户ID + * @param array $data 商户数据 + * @return object|null 商户详情 + */ public function updateWithDetail(int $merchantId, array $data): ?object { $merchant = $this->update($merchantId, $data); @@ -77,53 +148,118 @@ class MerchantService extends BaseService return $this->findById($merchantId); } + /** + * 删除商户。 + * + * @param int $merchantId 商户ID + * @return bool 是否删除成功 + */ public function delete(int $merchantId): bool { return $this->commandService->delete($merchantId); } + /** + * 重置商户密码。 + * + * @param int $merchantId 商户ID + * @param string $password 新密码 + * @return Merchant 商户模型 + */ public function resetPassword(int $merchantId, string $password): Merchant { return $this->commandService->resetPassword($merchantId, $password); } + /** + * 校验商户密码。 + * + * @param Merchant $merchant 商户 + * @param string $password 密码 + * @return bool 是否匹配 + */ public function verifyPassword(Merchant $merchant, string $password): bool { return $this->commandService->verifyPassword($merchant, $password); } + /** + * 更新商户登录信息。 + * + * @param int $merchantId 商户ID + * @param string $ip 登录 IP + * @return void + */ public function touchLoginMeta(int $merchantId, string $ip = ''): void { $this->commandService->touchLoginMeta($merchantId, $ip); } + /** + * 生成或重置商户 API 凭证。 + * + * @param int $merchantId 商户ID + * @return array 凭证数据 + */ public function issueCredential(int $merchantId): array { return $this->commandService->issueCredential($merchantId); } + /** + * 获取商户总览。 + * + * @param int $merchantId 商户ID + * @return array 总览数据 + */ public function overview(int $merchantId): array { return $this->overviewQueryService->overview($merchantId); } + /** + * 根据商户号查询已启用商户。 + * + * @param string $merchantNo 商户号 + * @return Merchant 商户模型 + */ public function findEnabledMerchantByNo(string $merchantNo): Merchant { return $this->commandService->findEnabledMerchantByNo($merchantNo); } + /** + * 校验商户是否启用。 + * + * @param int $merchantId 商户ID + * @return Merchant 商户模型 + */ public function ensureMerchantEnabled(int $merchantId): Merchant { return $this->commandService->ensureMerchantEnabled($merchantId); } + /** + * 校验商户分组是否启用。 + * + * @param int $groupId 分组ID + * @return MerchantGroup 商户分组模型 + */ public function ensureMerchantGroupEnabled(int $groupId): MerchantGroup { return $this->commandService->ensureMerchantGroupEnabled($groupId); } + /** + * 查询商户策略。 + * + * @param int $merchantId 商户ID + * @return MerchantPolicy|null 商户策略模型 + */ public function findPolicy(int $merchantId): ?MerchantPolicy { return $this->queryService->findPolicy($merchantId); } } + + diff --git a/app/service/merchant/auth/MerchantAuthService.php b/app/service/merchant/auth/MerchantAuthService.php index 9efe5d4..d3c8904 100644 --- a/app/service/merchant/auth/MerchantAuthService.php +++ b/app/service/merchant/auth/MerchantAuthService.php @@ -13,9 +13,23 @@ use app\service\merchant\portal\MerchantPortalSupportService; /** * 商户认证服务。 + * + * @property MerchantRepository $merchantRepository 商户仓库 + * @property MerchantApiCredentialRepository $merchantApiCredentialRepository 商户 API 凭证仓库 + * @property MerchantPortalSupportService $merchantPortalSupportService 商户门户支持服务 + * @property JwtTokenManager $jwtTokenManager jwtToken管理器 */ class MerchantAuthService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantRepository $merchantRepository 商户仓库 + * @param MerchantApiCredentialRepository $merchantApiCredentialRepository 商户 API 凭证仓库 + * @param MerchantPortalSupportService $merchantPortalSupportService 商户门户支持服务 + * @param JwtTokenManager $jwtTokenManager jwtToken管理器 + * @return void + */ public function __construct( protected MerchantRepository $merchantRepository, protected MerchantApiCredentialRepository $merchantApiCredentialRepository, @@ -26,6 +40,10 @@ class MerchantAuthService extends BaseService /** * 获取当前登录商户的资料。 + * + * @param int $merchantId 商户ID + * @param string $merchantNo 商户号 + * @return array{merchant_id: int, merchant_no: string, merchant: array, user: array, roles: array, permissions: array} 商户资料 */ public function profile(int $merchantId, string $merchantNo = ''): array { @@ -78,6 +96,11 @@ class MerchantAuthService extends BaseService /** * 校验商户登录 token,并返回商户与登录态信息。 + * + * @param string $token 登录令牌 + * @param string $ip 请求 IP + * @param string $userAgent 用户代理 + * @return array{merchant: Merchant, credential: \app\model\merchant\MerchantApiCredential|null}|null 登录态 */ public function authenticateToken(string $token, string $ip = '', string $userAgent = ''): ?array { @@ -107,6 +130,13 @@ class MerchantAuthService extends BaseService /** * 校验商户登录凭证并签发 JWT。 + * + * @param string $merchantNo 商户号 + * @param string $password 密码 + * @param string $ip 请求 IP + * @param string $userAgent 用户代理 + * @return array{token: string, expires_in: int, merchant: Merchant, credential: array{status: int, sign_type: int, last_used_at: mixed}|null} 登录结果 + * @throws ValidationException */ public function authenticateCredentials(string $merchantNo, string $password, string $ip = '', string $userAgent = ''): array { @@ -136,6 +166,9 @@ class MerchantAuthService extends BaseService /** * 撤销当前商户登录 token。 + * + * @param string $token 登录令牌 + * @return bool 是否撤销成功 */ public function revokeToken(string $token): bool { @@ -144,6 +177,13 @@ class MerchantAuthService extends BaseService /** * 签发新的商户登录 token。 + * + * @param int $merchantId 商户ID + * @param int $ttlSeconds 过期秒数 + * @param string $ip 请求 IP + * @param string $userAgent 用户代理 + * @return array{token: string, expires_in: int, merchant: Merchant, credential: array{status: int, sign_type: int, last_used_at: mixed}|null} 登录结果 + * @throws ValidationException */ public function issueToken(int $merchantId, int $ttlSeconds = 86400, string $ip = '', string $userAgent = ''): array { @@ -179,3 +219,8 @@ class MerchantAuthService extends BaseService } } + + + + + diff --git a/app/service/merchant/group/MerchantGroupService.php b/app/service/merchant/group/MerchantGroupService.php index 0e58b42..f60274e 100644 --- a/app/service/merchant/group/MerchantGroupService.php +++ b/app/service/merchant/group/MerchantGroupService.php @@ -9,11 +9,15 @@ use app\repository\merchant\base\MerchantGroupRepository; /** * 商户分组管理服务。 + * + * @property MerchantGroupRepository $merchantGroupRepository 商户分组仓库 */ class MerchantGroupService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param MerchantGroupRepository $merchantGroupRepository 商户分组仓库 */ public function __construct( protected MerchantGroupRepository $merchantGroupRepository @@ -24,6 +28,8 @@ class MerchantGroupService extends BaseService * 获取启用中的商户分组选项。 * * 前端筛选框直接使用 `label / value` 结构即可。 + * + * @return array 启用分组选项 */ public function enabledOptions(): array { @@ -40,6 +46,11 @@ class MerchantGroupService extends BaseService /** * 分页查询商户分组。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页对象 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -66,6 +77,9 @@ class MerchantGroupService extends BaseService /** * 根据 ID 查询商户分组。 + * + * @param int $id 商户分组ID + * @return MerchantGroup|null 商户分组模型 */ public function findById(int $id): ?MerchantGroup { @@ -74,6 +88,9 @@ class MerchantGroupService extends BaseService /** * 新增商户分组。 + * + * @param array $data 分组数据 + * @return MerchantGroup 商户分组模型 */ public function create(array $data): MerchantGroup { @@ -83,6 +100,10 @@ class MerchantGroupService extends BaseService /** * 更新商户分组。 + * + * @param int $id 商户分组ID + * @param array $data 分组数据 + * @return MerchantGroup|null 商户分组模型 */ public function update(int $id, array $data): ?MerchantGroup { @@ -96,6 +117,9 @@ class MerchantGroupService extends BaseService /** * 删除商户分组。 + * + * @param int $id 商户分组ID + * @return bool 是否删除成功 */ public function delete(int $id): bool { @@ -104,6 +128,11 @@ class MerchantGroupService extends BaseService /** * 校验商户分组名称唯一。 + * + * @param string $groupName 分组名称 + * @param int $ignoreId 忽略的分组ID + * @return void + * @throws ValidationException */ private function assertGroupNameUnique(string $groupName, int $ignoreId = 0): void { @@ -118,3 +147,8 @@ class MerchantGroupService extends BaseService } } + + + + + diff --git a/app/service/merchant/policy/MerchantPolicyService.php b/app/service/merchant/policy/MerchantPolicyService.php index cba1d70..3a93e02 100644 --- a/app/service/merchant/policy/MerchantPolicyService.php +++ b/app/service/merchant/policy/MerchantPolicyService.php @@ -10,9 +10,18 @@ use app\repository\merchant\base\MerchantRepository; /** * 商户策略服务。 + * + * @property MerchantPolicyRepository $merchantPolicyRepository 商户策略仓库 + * @property MerchantRepository $merchantRepository 商户仓库 */ class MerchantPolicyService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantPolicyRepository $merchantPolicyRepository 商户策略仓库 + * @param MerchantRepository $merchantRepository 商户仓库 + */ public function __construct( protected MerchantPolicyRepository $merchantPolicyRepository, protected MerchantRepository $merchantRepository @@ -21,6 +30,11 @@ class MerchantPolicyService extends BaseService /** * 分页查询商户策略列表。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页对象 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -106,6 +120,9 @@ class MerchantPolicyService extends BaseService /** * 查询单个商户的策略。 + * + * @param int $merchantId 商户ID + * @return MerchantPolicy|null 商户策略模型 */ public function findByMerchantId(int $merchantId): ?MerchantPolicy { @@ -114,6 +131,11 @@ class MerchantPolicyService extends BaseService /** * 保存商户策略。 + * + * @param int $merchantId 商户ID + * @param array $data 策略数据 + * @return MerchantPolicy 商户策略模型 + * @throws ResourceNotFoundException */ public function saveByMerchantId(int $merchantId, array $data): MerchantPolicy { @@ -139,12 +161,21 @@ class MerchantPolicyService extends BaseService /** * 删除商户策略。 + * + * @param int $merchantId 商户ID + * @return bool 是否删除成功 */ public function deleteByMerchantId(int $merchantId): bool { return $this->merchantPolicyRepository->deleteWhere(['merchant_id' => $merchantId]) > 0; } + /** + * 将结算周期值转换为文本。 + * + * @param int $value 结算周期值 + * @return string 结算周期文本 + */ private function settlementCycleText(int $value): string { return match ($value) { @@ -159,3 +190,8 @@ class MerchantPolicyService extends BaseService } + + + + + diff --git a/app/service/merchant/portal/MerchantPortalBalanceService.php b/app/service/merchant/portal/MerchantPortalBalanceService.php index c81ed97..c5646f3 100644 --- a/app/service/merchant/portal/MerchantPortalBalanceService.php +++ b/app/service/merchant/portal/MerchantPortalBalanceService.php @@ -8,9 +8,22 @@ use app\service\account\ledger\MerchantAccountLedgerService; /** * 商户门户余额服务。 + * + * 负责余额快照、可提现余额和资金流水页面的数据拼装。 + * + * @property MerchantPortalSupportService $supportService 支持服务 + * @property MerchantAccountService $merchantAccountService 商户账户服务 + * @property MerchantAccountLedgerService $merchantAccountLedgerService 商户账户流水服务 */ class MerchantPortalBalanceService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantPortalSupportService $supportService 支持服务 + * @param MerchantAccountService $merchantAccountService 商户账户服务 + * @param MerchantAccountLedgerService $merchantAccountLedgerService 商户账户流水服务 + */ public function __construct( protected MerchantPortalSupportService $supportService, protected MerchantAccountService $merchantAccountService, @@ -18,13 +31,19 @@ class MerchantPortalBalanceService extends BaseService ) { } + /** + * 查询商户门户可提现余额。 + * + * @param int $merchantId 商户ID + * @return array 余额摘要 + */ public function withdrawableBalance(int $merchantId): array { $merchant = $this->supportService->merchantSummary($merchantId); $snapshot = $this->merchantAccountService->getBalanceSnapshot($merchantId); - $snapshot['available_balance_text'] = $this->supportService->formatAmount((int) ($snapshot['available_balance'] ?? 0)); - $snapshot['frozen_balance_text'] = $this->supportService->formatAmount((int) ($snapshot['frozen_balance'] ?? 0)); + $snapshot['available_balance_text'] = $this->formatAmount((int) ($snapshot['available_balance'] ?? 0)); + $snapshot['frozen_balance_text'] = $this->formatAmount((int) ($snapshot['frozen_balance'] ?? 0)); $snapshot['withdrawable_balance_text'] = $snapshot['available_balance_text']; return [ @@ -33,6 +52,15 @@ class MerchantPortalBalanceService extends BaseService ]; } + /** + * 查询商户门户资金流水。 + * + * @param array $filters 筛选条件 + * @param int $merchantId 商户ID + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array 流水列表数据 + */ public function balanceFlows(array $filters, int $merchantId, int $page, int $pageSize): array { $filters['merchant_id'] = $merchantId; @@ -48,3 +76,5 @@ class MerchantPortalBalanceService extends BaseService ]; } } + + diff --git a/app/service/merchant/portal/MerchantPortalChannelQueryService.php b/app/service/merchant/portal/MerchantPortalChannelQueryService.php index 8511598..28856b5 100644 --- a/app/service/merchant/portal/MerchantPortalChannelQueryService.php +++ b/app/service/merchant/portal/MerchantPortalChannelQueryService.php @@ -11,9 +11,18 @@ use app\repository\payment\config\PaymentChannelRepository; * 商户门户通道查询服务。 * * 负责商户通道列表查询和通道行格式化。 + * + * @property MerchantPortalSupportService $supportService 支持服务 + * @property PaymentChannelRepository $paymentChannelRepository 支付渠道仓库 */ class MerchantPortalChannelQueryService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantPortalSupportService $supportService 支持服务 + * @param PaymentChannelRepository $paymentChannelRepository 支付渠道仓库 + */ public function __construct( protected MerchantPortalSupportService $supportService, protected PaymentChannelRepository $paymentChannelRepository @@ -22,6 +31,12 @@ class MerchantPortalChannelQueryService extends BaseService /** * 查询当前商户的通道列表。 + * + * @param array $filters 筛选条件 + * @param int $merchantId 商户ID + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array 通道列表数据 */ public function myChannels(array $filters, int $merchantId, int $page, int $pageSize): array { @@ -95,19 +110,27 @@ class MerchantPortalChannelQueryService extends BaseService ]; } + /** + * 为通道行补充文本字段。 + * + * @param object $row 通道行 + * @return object 处理后的通道行 + */ private function decorateChannelRow(object $row): object { $row->channel_mode_text = (string) (RouteConstant::channelModeMap()[(int) $row->channel_mode] ?? '未知'); $row->status_text = (string) (CommonConstant::statusMap()[(int) $row->status] ?? '未知'); - $row->split_rate_text = $this->supportService->formatRate((int) $row->split_rate_bp); - $row->cost_rate_text = $this->supportService->formatRate((int) $row->cost_rate_bp); - $row->daily_limit_amount_text = $this->supportService->formatAmountOrUnlimited((int) $row->daily_limit_amount); - $row->daily_limit_count_text = $this->supportService->formatCountOrUnlimited((int) $row->daily_limit_count); - $row->min_amount_text = $this->supportService->formatAmountOrUnlimited((int) $row->min_amount); - $row->max_amount_text = $this->supportService->formatAmountOrUnlimited((int) $row->max_amount); - $row->created_at_text = $this->supportService->formatDateTime($row->created_at ?? null); - $row->updated_at_text = $this->supportService->formatDateTime($row->updated_at ?? null); + $row->split_rate_text = $this->formatRate((int) $row->split_rate_bp); + $row->cost_rate_text = $this->formatRate((int) $row->cost_rate_bp); + $row->daily_limit_amount_text = $this->formatAmountOrUnlimited((int) $row->daily_limit_amount); + $row->daily_limit_count_text = $this->formatCountOrUnlimited((int) $row->daily_limit_count); + $row->min_amount_text = $this->formatAmountOrUnlimited((int) $row->min_amount); + $row->max_amount_text = $this->formatAmountOrUnlimited((int) $row->max_amount); + $row->created_at_text = $this->formatDateTime($row->created_at ?? null); + $row->updated_at_text = $this->formatDateTime($row->updated_at ?? null); return $row; } } + + diff --git a/app/service/merchant/portal/MerchantPortalChannelService.php b/app/service/merchant/portal/MerchantPortalChannelService.php index 73fbb1f..0f6e1cd 100644 --- a/app/service/merchant/portal/MerchantPortalChannelService.php +++ b/app/service/merchant/portal/MerchantPortalChannelService.php @@ -5,25 +5,51 @@ namespace app\service\merchant\portal; use app\common\base\BaseService; /** - * 商户门户通道门面服务。 + * 商户门户通道服务。 * - * 对外保留原有调用契约,内部委托给通道查询和路由预览子服务。 + * @property MerchantPortalChannelQueryService $queryService 查询服务 + * @property MerchantPortalRoutePreviewService $routePreviewService 路由解析服务 */ class MerchantPortalChannelService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantPortalChannelQueryService $queryService 查询服务 + * @param MerchantPortalRoutePreviewService $routePreviewService 路由解析服务 + */ public function __construct( protected MerchantPortalChannelQueryService $queryService, protected MerchantPortalRoutePreviewService $routePreviewService ) { } + /** + * 查询当前商户已开通的渠道。 + * + * @param array $filters 筛选条件 + * @param int $merchantId 商户ID + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array 渠道列表数据 + */ public function myChannels(array $filters, int $merchantId, int $page, int $pageSize): array { return $this->queryService->myChannels($filters, $merchantId, $page, $pageSize); } + /** + * 获取商户渠道路由解析结果。 + * + * @param int $merchantId 商户ID + * @param int $payTypeId 支付类型ID + * @param int $payAmount 支付金额 + * @param string $statDate 统计日期 + * @return array 路由解析数据 + */ public function routePreview(int $merchantId, int $payTypeId, int $payAmount, string $statDate = ''): array { return $this->routePreviewService->routePreview($merchantId, $payTypeId, $payAmount, $statDate); } } + diff --git a/app/service/merchant/portal/MerchantPortalCredentialCommandService.php b/app/service/merchant/portal/MerchantPortalCredentialCommandService.php index e4a4cbb..4ffeff7 100644 --- a/app/service/merchant/portal/MerchantPortalCredentialCommandService.php +++ b/app/service/merchant/portal/MerchantPortalCredentialCommandService.php @@ -7,10 +7,21 @@ use app\repository\merchant\credential\MerchantApiCredentialRepository; use app\service\merchant\security\MerchantApiCredentialService; /** - * 商户门户接口凭证命令服务。 + * 商户门户 API 凭证命令服务。 + * + * @property MerchantPortalSupportService $supportService 支持服务 + * @property MerchantApiCredentialRepository $merchantApiCredentialRepository 商户 API 凭证仓库 + * @property MerchantApiCredentialService $merchantApiCredentialService 商户 API 凭证服务 */ class MerchantPortalCredentialCommandService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantPortalSupportService $supportService 支持服务 + * @param MerchantApiCredentialRepository $merchantApiCredentialRepository 商户 API 凭证仓库 + * @param MerchantApiCredentialService $merchantApiCredentialService 商户 API 凭证服务 + */ public function __construct( protected MerchantPortalSupportService $supportService, protected MerchantApiCredentialRepository $merchantApiCredentialRepository, @@ -18,10 +29,17 @@ class MerchantPortalCredentialCommandService extends BaseService ) { } + /** + * 生成或重置商户 API 凭证。 + * + * @param int $merchantId 商户ID + * @return array 凭证数据 + */ public function issueCredential(int $merchantId): array { $merchant = $this->supportService->merchantSummary($merchantId); $credentialValue = $this->merchantApiCredentialService->issueCredential($merchantId); + // 凭证明文只在发放当次返回一次,随后再查库只拿脱敏后的展示结构。 $credential = $this->merchantApiCredentialRepository->findByMerchantId($merchantId); return [ @@ -31,6 +49,13 @@ class MerchantPortalCredentialCommandService extends BaseService ]; } + /** + * 格式化接口凭证展示数据。 + * + * @param \app\model\merchant\MerchantApiCredential $credential 凭证 + * @param array $merchant 商户摘要 + * @return array 展示数据 + */ private function formatCredential(\app\model\merchant\MerchantApiCredential $credential, array $merchant): array { $signType = (int) $credential->sign_type; @@ -42,12 +67,13 @@ class MerchantPortalCredentialCommandService extends BaseService 'merchant_name' => (string) ($merchant['merchant_name'] ?? ''), 'sign_type' => $signType, 'sign_type_text' => $this->supportService->signTypeText($signType), - 'api_key_preview' => $this->supportService->maskCredentialValue((string) $credential->api_key), + // 展示页只保留脱敏后的 key 片段,避免明文凭证再次暴露。 + 'api_key_preview' => $this->maskCredentialValue((string) $credential->api_key), 'status' => (int) $credential->status, 'status_text' => (string) ($credential->status ? '启用' : '禁用'), - 'last_used_at' => $this->supportService->formatDateTime($credential->last_used_at ?? null), - 'created_at' => $this->supportService->formatDateTime($credential->created_at ?? null), - 'updated_at' => $this->supportService->formatDateTime($credential->updated_at ?? null), + 'last_used_at' => $this->formatDateTime($credential->last_used_at ?? null), + 'created_at' => $this->formatDateTime($credential->created_at ?? null), + 'updated_at' => $this->formatDateTime($credential->updated_at ?? null), ]; } } diff --git a/app/service/merchant/portal/MerchantPortalCredentialQueryService.php b/app/service/merchant/portal/MerchantPortalCredentialQueryService.php index f84d12d..a58cf60 100644 --- a/app/service/merchant/portal/MerchantPortalCredentialQueryService.php +++ b/app/service/merchant/portal/MerchantPortalCredentialQueryService.php @@ -8,16 +8,31 @@ use app\model\merchant\MerchantApiCredential; use app\repository\merchant\credential\MerchantApiCredentialRepository; /** - * 商户门户接口凭证查询服务。 + * 商户门户 API 凭证查询服务。 + * + * @property MerchantPortalSupportService $supportService 支持服务 + * @property MerchantApiCredentialRepository $merchantApiCredentialRepository 商户 API 凭证仓库 */ class MerchantPortalCredentialQueryService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantPortalSupportService $supportService 支持服务 + * @param MerchantApiCredentialRepository $merchantApiCredentialRepository 商户 API 凭证仓库 + */ public function __construct( protected MerchantPortalSupportService $supportService, protected MerchantApiCredentialRepository $merchantApiCredentialRepository ) { } + /** + * 查询商户 API 凭证详情。 + * + * @param int $merchantId 商户ID + * @return array 凭证详情 + */ public function apiCredential(int $merchantId): array { $merchant = $this->supportService->merchantSummary($merchantId); @@ -30,6 +45,13 @@ class MerchantPortalCredentialQueryService extends BaseService ]; } + /** + * 格式化接口凭证展示数据。 + * + * @param MerchantApiCredential $credential 凭证 + * @param array $merchant 商户摘要 + * @return array 展示数据 + */ private function formatCredential(MerchantApiCredential $credential, array $merchant): array { $signType = (int) $credential->sign_type; @@ -42,12 +64,14 @@ class MerchantPortalCredentialQueryService extends BaseService 'merchant_name' => (string) ($merchant['merchant_name'] ?? ''), 'sign_type' => $signType, 'sign_type_text' => $this->supportService->signTypeText($signType), - 'api_key_preview' => $this->supportService->maskCredentialValue((string) $credential->api_key), + 'api_key_preview' => $this->maskCredentialValue((string) $credential->api_key), 'status' => $status, 'status_text' => (string) (CommonConstant::statusMap()[$status] ?? '未知'), - 'last_used_at' => $this->supportService->formatDateTime($credential->last_used_at ?? null), - 'created_at' => $this->supportService->formatDateTime($credential->created_at ?? null), - 'updated_at' => $this->supportService->formatDateTime($credential->updated_at ?? null), + 'last_used_at' => $this->formatDateTime($credential->last_used_at ?? null), + 'created_at' => $this->formatDateTime($credential->created_at ?? null), + 'updated_at' => $this->formatDateTime($credential->updated_at ?? null), ]; } } + + diff --git a/app/service/merchant/portal/MerchantPortalCredentialService.php b/app/service/merchant/portal/MerchantPortalCredentialService.php index 2b14ba9..1c96877 100644 --- a/app/service/merchant/portal/MerchantPortalCredentialService.php +++ b/app/service/merchant/portal/MerchantPortalCredentialService.php @@ -5,23 +5,45 @@ namespace app\service\merchant\portal; use app\common\base\BaseService; /** - * 商户门户接口凭证门面服务。 + * 商户门户 API 凭证服务。 + * + * @property MerchantPortalCredentialQueryService $queryService 查询服务 + * @property MerchantPortalCredentialCommandService $commandService 命令服务 */ class MerchantPortalCredentialService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantPortalCredentialQueryService $queryService 查询服务 + * @param MerchantPortalCredentialCommandService $commandService 命令服务 + */ public function __construct( protected MerchantPortalCredentialQueryService $queryService, protected MerchantPortalCredentialCommandService $commandService ) { } + /** + * 查询商户 API 凭证。 + * + * @param int $merchantId 商户ID + * @return array 凭证数据 + */ public function apiCredential(int $merchantId): array { return $this->queryService->apiCredential($merchantId); } + /** + * 生成或重置商户 API 凭证。 + * + * @param int $merchantId 商户ID + * @return array 凭证数据 + */ public function issueCredential(int $merchantId): array { return $this->commandService->issueCredential($merchantId); } } + diff --git a/app/service/merchant/portal/MerchantPortalFinanceService.php b/app/service/merchant/portal/MerchantPortalFinanceService.php index e7bbb48..1a783bc 100644 --- a/app/service/merchant/portal/MerchantPortalFinanceService.php +++ b/app/service/merchant/portal/MerchantPortalFinanceService.php @@ -5,35 +5,76 @@ namespace app\service\merchant\portal; use app\common\base\BaseService; /** - * 商户门户资金与清算门面服务。 + * 商户门户资金与清算服务。 * - * 对外保留原有调用契约,内部委托给清算与余额子服务。 + * @property MerchantPortalSettlementService $settlementService 结算服务 + * @property MerchantPortalBalanceService $balanceService 余额服务 */ class MerchantPortalFinanceService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantPortalSettlementService $settlementService 结算服务 + * @param MerchantPortalBalanceService $balanceService 余额服务 + */ public function __construct( protected MerchantPortalSettlementService $settlementService, protected MerchantPortalBalanceService $balanceService ) { } + /** + * 查询商户结算记录。 + * + * @param array $filters 筛选条件 + * @param int $merchantId 商户ID + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array 结算记录列表 + */ public function settlementRecords(array $filters, int $merchantId, int $page, int $pageSize): array { return $this->settlementService->settlementRecords($filters, $merchantId, $page, $pageSize); } + /** + * 查询商户结算记录详情。 + * + * @param string $settleNo 结算单号 + * @param int $merchantId 商户ID + * @return array|null 结算详情 + */ public function settlementRecordDetail(string $settleNo, int $merchantId): ?array { return $this->settlementService->settlementRecordDetail($settleNo, $merchantId); } + /** + * 查询商户可提现余额。 + * + * @param int $merchantId 商户ID + * @return array 余额数据 + */ public function withdrawableBalance(int $merchantId): array { return $this->balanceService->withdrawableBalance($merchantId); } + /** + * 查询商户资金流水。 + * + * @param array $filters 筛选条件 + * @param int $merchantId 商户ID + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array 流水列表 + */ public function balanceFlows(array $filters, int $merchantId, int $page, int $pageSize): array { return $this->balanceService->balanceFlows($filters, $merchantId, $page, $pageSize); } } + + + diff --git a/app/service/merchant/portal/MerchantPortalProfileCommandService.php b/app/service/merchant/portal/MerchantPortalProfileCommandService.php index c006b3d..9c27288 100644 --- a/app/service/merchant/portal/MerchantPortalProfileCommandService.php +++ b/app/service/merchant/portal/MerchantPortalProfileCommandService.php @@ -9,15 +9,32 @@ use app\repository\merchant\base\MerchantRepository; /** * 商户门户资料命令服务。 + * + * @property MerchantPortalSupportService $supportService 支持服务 + * @property MerchantRepository $merchantRepository 商户仓库 */ class MerchantPortalProfileCommandService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantPortalSupportService $supportService 支持服务 + * @param MerchantRepository $merchantRepository 商户仓库 + */ public function __construct( protected MerchantPortalSupportService $supportService, protected MerchantRepository $merchantRepository ) { } + /** + * 更新商户门户资料。 + * + * @param int $merchantId 商户ID + * @param array $data 资料数据 + * @return array 更新后的资料数据 + * @throws ResourceNotFoundException + */ public function updateProfile(int $merchantId, array $data): array { $merchant = $this->merchantRepository->find($merchantId); @@ -42,6 +59,15 @@ class MerchantPortalProfileCommandService extends BaseService ]; } + /** + * 修改商户门户密码。 + * + * @param int $merchantId 商户ID + * @param array $data 密码数据 + * @return array 密码修改结果 + * @throws ResourceNotFoundException + * @throws ValidationException + */ public function changePassword(int $merchantId, array $data): array { $merchant = $this->merchantRepository->find($merchantId); @@ -63,7 +89,8 @@ class MerchantPortalProfileCommandService extends BaseService return [ 'updated' => true, - 'password_updated_at' => $this->supportService->formatDateTime($this->now()), + 'password_updated_at' => $this->formatDateTime($this->now()), ]; } } + diff --git a/app/service/merchant/portal/MerchantPortalProfileQueryService.php b/app/service/merchant/portal/MerchantPortalProfileQueryService.php index 4ad5e88..941008a 100644 --- a/app/service/merchant/portal/MerchantPortalProfileQueryService.php +++ b/app/service/merchant/portal/MerchantPortalProfileQueryService.php @@ -6,14 +6,27 @@ use app\common\base\BaseService; /** * 商户门户资料查询服务。 + * + * @property MerchantPortalSupportService $supportService 支持服务 */ class MerchantPortalProfileQueryService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantPortalSupportService $supportService 支持服务 + */ public function __construct( protected MerchantPortalSupportService $supportService ) { } + /** + * 查询商户门户资料页数据。 + * + * @param int $merchantId 商户ID + * @return array 页面数据 + */ public function profile(int $merchantId): array { return [ @@ -22,3 +35,6 @@ class MerchantPortalProfileQueryService extends BaseService ]; } } + + + diff --git a/app/service/merchant/portal/MerchantPortalProfileService.php b/app/service/merchant/portal/MerchantPortalProfileService.php index c2c9883..52e0ccf 100644 --- a/app/service/merchant/portal/MerchantPortalProfileService.php +++ b/app/service/merchant/portal/MerchantPortalProfileService.php @@ -5,28 +5,59 @@ namespace app\service\merchant\portal; use app\common\base\BaseService; /** - * 商户门户资料门面服务。 + * 商户门户资料服务。 + * + * @property MerchantPortalProfileQueryService $queryService 查询服务 + * @property MerchantPortalProfileCommandService $commandService 命令服务 */ class MerchantPortalProfileService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantPortalProfileQueryService $queryService 查询服务 + * @param MerchantPortalProfileCommandService $commandService 命令服务 + */ public function __construct( protected MerchantPortalProfileQueryService $queryService, protected MerchantPortalProfileCommandService $commandService ) { } + /** + * 查询商户门户资料。 + * + * @param int $merchantId 商户ID + * @return array 资料数据 + */ public function profile(int $merchantId): array { return $this->queryService->profile($merchantId); } + /** + * 更新商户门户资料。 + * + * @param int $merchantId 商户ID + * @param array $data 资料数据 + * @return array 更新后的资料数据 + */ public function updateProfile(int $merchantId, array $data): array { return $this->commandService->updateProfile($merchantId, $data); } + /** + * 修改商户门户密码。 + * + * @param int $merchantId 商户ID + * @param array $data 密码数据 + * @return array 密码修改结果 + */ public function changePassword(int $merchantId, array $data): array { return $this->commandService->changePassword($merchantId, $data); } } + + diff --git a/app/service/merchant/portal/MerchantPortalRoutePreviewService.php b/app/service/merchant/portal/MerchantPortalRoutePreviewService.php index 93a6610..9d0b1b9 100644 --- a/app/service/merchant/portal/MerchantPortalRoutePreviewService.php +++ b/app/service/merchant/portal/MerchantPortalRoutePreviewService.php @@ -10,10 +10,21 @@ use app\service\payment\runtime\PaymentRouteService; use Throwable; /** - * 商户门户路由预览服务。 + * 商户门户路由解析服务。 + * + * 负责根据商户分组、支付方式和金额解析路由结果。 + * + * @property MerchantPortalSupportService $supportService 支持服务 + * @property PaymentRouteService $paymentRouteService 支付路由服务 */ class MerchantPortalRoutePreviewService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantPortalSupportService $supportService 支持服务 + * @param PaymentRouteService $paymentRouteService 支付路由服务 + */ public function __construct( protected MerchantPortalSupportService $supportService, protected PaymentRouteService $paymentRouteService @@ -21,22 +32,30 @@ class MerchantPortalRoutePreviewService extends BaseService } /** - * 预览当前商户的路由选择结果。 + * 获取当前商户的路由解析结果。 + * + * @param int $merchantId 商户ID + * @param int $payTypeId 支付类型ID + * @param int $payAmount 支付金额 + * @param string $statDate 统计日期 + * @return array 路由解析数据 */ public function routePreview(int $merchantId, int $payTypeId, int $payAmount, string $statDate = ''): array { + // 先拿商户摘要,后面即使路由解析失败,也还能返回基础商户信息给前端展示。 $merchant = $this->supportService->merchantSummary($merchantId); $statDate = trim($statDate) !== '' ? trim($statDate) : FormatHelper::timestamp(time(), 'Y-m-d'); + // 先组一个可直接渲染的基础响应结构,失败时只需要改 reason 和可用状态。 $response = [ 'merchant' => $merchant, 'pay_types' => $this->supportService->enabledPayTypeOptions(), 'pay_type_id' => $payTypeId, 'pay_amount' => $payAmount, - 'pay_amount_text' => $this->supportService->formatAmount($payAmount), + 'pay_amount_text' => $this->formatAmount($payAmount), 'stat_date' => $statDate, 'available' => false, - 'reason' => '请选择支付方式和金额后预览路由', + 'reason' => '请选择支付方式和金额后解析路由', 'merchant_group_id' => (int) ($merchant['merchant_group_id'] ?? 0), 'merchant_group_name' => (string) ($merchant['merchant_group_name'] ?? ''), 'bind' => null, @@ -50,11 +69,12 @@ class MerchantPortalRoutePreviewService extends BaseService } if ((int) $merchant['merchant_group_id'] <= 0) { - $response['reason'] = '当前商户未配置商户分组,无法预览路由'; + $response['reason'] = '当前商户未配置商户分组,无法解析路由'; return $response; } try { + // 只有基础条件满足时,才进入完整的路由解析流程。 $resolved = $this->paymentRouteService->resolveByMerchantGroup( (int) $merchant['merchant_group_id'], $payTypeId, @@ -63,7 +83,8 @@ class MerchantPortalRoutePreviewService extends BaseService ); $response['available'] = true; - $response['reason'] = '路由预览成功'; + $response['reason'] = '路由解析成功'; + // 把模型和仓库返回的对象统一整理成前端可直接展示的数组结构。 $response['bind'] = $this->normalizeBind($resolved['bind'] ?? null); $response['poll_group'] = $this->normalizePollGroup($resolved['poll_group'] ?? null); $response['selected_channel'] = $this->normalizePreviewCandidate($resolved['selected_channel'] ?? null); @@ -73,15 +94,22 @@ class MerchantPortalRoutePreviewService extends BaseService (array) ($resolved['candidates'] ?? []) )); } catch (Throwable $e) { - $response['reason'] = $e->getMessage() !== '' ? $e->getMessage() : '路由预览失败'; + // 解析异常只影响路由结果,不影响基础信息展示,因此这里只回填失败原因。 + $response['reason'] = $e->getMessage() !== '' ? $e->getMessage() : '路由解析失败'; } return $response; } - private function normalizeBind(mixed $bind): ?array + /** + * 标准化商户分组与支付方式绑定数据。 + * + * @param array|object|null $bind 绑定数据 + * @return array|null 标准化结果 + */ + private function normalizeBind(array|object|null $bind): ?array { - $data = $this->supportService->normalizeModel($bind); + $data = $this->normalizeModel($bind); if ($data === null) { return null; } @@ -95,14 +123,20 @@ class MerchantPortalRoutePreviewService extends BaseService 'status' => $status, 'status_text' => (string) (CommonConstant::statusMap()[$status] ?? '未知'), 'remark' => (string) ($data['remark'] ?? ''), - 'created_at' => $this->supportService->formatDateTime($data['created_at'] ?? null), - 'updated_at' => $this->supportService->formatDateTime($data['updated_at'] ?? null), + 'created_at' => $this->formatDateTime($data['created_at'] ?? null), + 'updated_at' => $this->formatDateTime($data['updated_at'] ?? null), ]; } - private function normalizePollGroup(mixed $pollGroup): ?array + /** + * 标准化轮询分组数据。 + * + * @param array|object|null $pollGroup 轮询分组 + * @return array|null 标准化结果 + */ + private function normalizePollGroup(array|object|null $pollGroup): ?array { - $data = $this->supportService->normalizeModel($pollGroup); + $data = $this->normalizeModel($pollGroup); if ($data === null) { return null; } @@ -119,21 +153,28 @@ class MerchantPortalRoutePreviewService extends BaseService 'status' => $status, 'status_text' => (string) (CommonConstant::statusMap()[$status] ?? '未知'), 'remark' => (string) ($data['remark'] ?? ''), - 'created_at' => $this->supportService->formatDateTime($data['created_at'] ?? null), - 'updated_at' => $this->supportService->formatDateTime($data['updated_at'] ?? null), + 'created_at' => $this->formatDateTime($data['created_at'] ?? null), + 'updated_at' => $this->formatDateTime($data['updated_at'] ?? null), ]; } - private function normalizePreviewCandidate(mixed $candidate): ?array + /** + * 标准化路由候选数据。 + * + * @param array|object|null $candidate 候选数据 + * @return array|null 标准化结果 + */ + private function normalizePreviewCandidate(array|object|null $candidate): ?array { - $data = is_array($candidate) ? $candidate : $this->supportService->normalizeModel($candidate); + $data = is_array($candidate) ? $candidate : $this->normalizeModel($candidate); if ($data === null) { return null; } - $channel = $this->supportService->normalizeModel($data['channel'] ?? null) ?? []; - $pollGroupChannel = $this->supportService->normalizeModel($data['poll_group_channel'] ?? null) ?? []; - $dailyStat = $this->supportService->normalizeModel($data['daily_stat'] ?? null) ?? []; + // 一个候选项会同时带出通道、轮询关系和日统计三层数据,后面统一整理成展示结构。 + $channel = $this->normalizeModel($data['channel'] ?? null) ?? []; + $pollGroupChannel = $this->normalizeModel($data['poll_group_channel'] ?? null) ?? []; + $dailyStat = $this->normalizeModel($data['daily_stat'] ?? null) ?? []; $channelMode = (int) ($channel['channel_mode'] ?? 0); $status = (int) ($channel['status'] ?? 0); @@ -152,27 +193,28 @@ class MerchantPortalRoutePreviewService extends BaseService 'sort_no' => (int) ($pollGroupChannel['sort_no'] ?? 0), 'weight' => (int) ($pollGroupChannel['weight'] ?? 0), 'is_default' => (int) ($pollGroupChannel['is_default'] ?? 0), + // 统计指标同时返回原始值和格式化文本,前端可以直接展示而不再二次换算。 'health_score' => (int) ($dailyStat['health_score'] ?? 0), 'health_score_text' => (string) ($dailyStat['health_score'] ?? 0), 'success_rate_bp' => (int) ($dailyStat['success_rate_bp'] ?? 0), - 'success_rate_text' => $this->supportService->formatRate((int) ($dailyStat['success_rate_bp'] ?? 0)), + 'success_rate_text' => $this->formatRate((int) ($dailyStat['success_rate_bp'] ?? 0)), 'avg_latency_ms' => (int) ($dailyStat['avg_latency_ms'] ?? 0), - 'avg_latency_text' => $this->supportService->formatLatency((int) ($dailyStat['avg_latency_ms'] ?? 0)), + 'avg_latency_text' => $this->formatLatency((int) ($dailyStat['avg_latency_ms'] ?? 0)), 'split_rate_bp' => (int) ($channel['split_rate_bp'] ?? 0), - 'split_rate_text' => $this->supportService->formatRate((int) ($channel['split_rate_bp'] ?? 0)), + 'split_rate_text' => $this->formatRate((int) ($channel['split_rate_bp'] ?? 0)), 'cost_rate_bp' => (int) ($channel['cost_rate_bp'] ?? 0), - 'cost_rate_text' => $this->supportService->formatRate((int) ($channel['cost_rate_bp'] ?? 0)), + 'cost_rate_text' => $this->formatRate((int) ($channel['cost_rate_bp'] ?? 0)), 'daily_limit_amount' => (int) ($channel['daily_limit_amount'] ?? 0), - 'daily_limit_amount_text' => $this->supportService->formatAmountOrUnlimited((int) ($channel['daily_limit_amount'] ?? 0)), + 'daily_limit_amount_text' => $this->formatAmountOrUnlimited((int) ($channel['daily_limit_amount'] ?? 0)), 'daily_limit_count' => (int) ($channel['daily_limit_count'] ?? 0), - 'daily_limit_count_text' => $this->supportService->formatCountOrUnlimited((int) ($channel['daily_limit_count'] ?? 0)), + 'daily_limit_count_text' => $this->formatCountOrUnlimited((int) ($channel['daily_limit_count'] ?? 0)), 'min_amount' => (int) ($channel['min_amount'] ?? 0), - 'min_amount_text' => $this->supportService->formatAmountOrUnlimited((int) ($channel['min_amount'] ?? 0)), + 'min_amount_text' => $this->formatAmountOrUnlimited((int) ($channel['min_amount'] ?? 0)), 'max_amount' => (int) ($channel['max_amount'] ?? 0), - 'max_amount_text' => $this->supportService->formatAmountOrUnlimited((int) ($channel['max_amount'] ?? 0)), + 'max_amount_text' => $this->formatAmountOrUnlimited((int) ($channel['max_amount'] ?? 0)), 'remark' => (string) ($channel['remark'] ?? ''), - 'created_at' => $this->supportService->formatDateTime($channel['created_at'] ?? null), - 'updated_at' => $this->supportService->formatDateTime($channel['updated_at'] ?? null), + 'created_at' => $this->formatDateTime($channel['created_at'] ?? null), + 'updated_at' => $this->formatDateTime($channel['updated_at'] ?? null), ]; } } diff --git a/app/service/merchant/portal/MerchantPortalService.php b/app/service/merchant/portal/MerchantPortalService.php index ae6215c..731d660 100644 --- a/app/service/merchant/portal/MerchantPortalService.php +++ b/app/service/merchant/portal/MerchantPortalService.php @@ -5,12 +5,23 @@ namespace app\service\merchant\portal; use app\common\base\BaseService; /** - * 商户后台基础页面服务门面。 + * 商户后台门户服务。 * - * 仅保留控制器依赖的统一入口,具体能力拆到资料、通道、凭证和资金子服务。 + * @property MerchantPortalProfileService $profileService 资料服务 + * @property MerchantPortalChannelService $channelService 渠道服务 + * @property MerchantPortalCredentialService $credentialService 凭证服务 + * @property MerchantPortalFinanceService $financeService 财务服务 */ class MerchantPortalService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantPortalProfileService $profileService 资料服务 + * @param MerchantPortalChannelService $channelService 渠道服务 + * @param MerchantPortalCredentialService $credentialService 凭证服务 + * @param MerchantPortalFinanceService $financeService 财务服务 + */ public function __construct( protected MerchantPortalProfileService $profileService, protected MerchantPortalChannelService $channelService, @@ -19,56 +30,137 @@ class MerchantPortalService extends BaseService ) { } + /** + * 查询商户门户资料。 + * + * @param int $merchantId 商户ID + * @return array 资料数据 + */ public function profile(int $merchantId): array { return $this->profileService->profile($merchantId); } + /** + * 更新商户门户资料。 + * + * @param int $merchantId 商户ID + * @param array $data 资料数据 + * @return array 更新后的资料数据 + */ public function updateProfile(int $merchantId, array $data): array { return $this->profileService->updateProfile($merchantId, $data); } + /** + * 修改商户门户密码。 + * + * @param int $merchantId 商户ID + * @param array $data 密码数据 + * @return array 密码修改结果 + */ public function changePassword(int $merchantId, array $data): array { return $this->profileService->changePassword($merchantId, $data); } + /** + * 查询当前商户已开通的渠道。 + * + * @param array $filters 筛选条件 + * @param int $merchantId 商户ID + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array 渠道列表 + */ public function myChannels(array $filters, int $merchantId, int $page, int $pageSize): array { return $this->channelService->myChannels($filters, $merchantId, $page, $pageSize); } + /** + * 获取商户路由解析结果。 + * + * @param int $merchantId 商户ID + * @param int $payTypeId 支付类型ID + * @param int $payAmount 支付金额 + * @param string $statDate 统计日期 + * @return array 路由解析数据 + */ public function routePreview(int $merchantId, int $payTypeId, int $payAmount, string $statDate = ''): array { return $this->channelService->routePreview($merchantId, $payTypeId, $payAmount, $statDate); } + /** + * 查询商户 API 凭证。 + * + * @param int $merchantId 商户ID + * @return array 凭证数据 + */ public function apiCredential(int $merchantId): array { return $this->credentialService->apiCredential($merchantId); } + /** + * 生成或重置商户门户接口凭证。 + * + * @param int $merchantId 商户ID + * @return array 凭证数据 + */ public function issueCredential(int $merchantId): array { return $this->credentialService->issueCredential($merchantId); } + /** + * 查询商户结算记录。 + * + * @param array $filters 筛选条件 + * @param int $merchantId 商户ID + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array 结算记录列表 + */ public function settlementRecords(array $filters, int $merchantId, int $page, int $pageSize): array { return $this->financeService->settlementRecords($filters, $merchantId, $page, $pageSize); } + /** + * 查询商户结算记录详情。 + * + * @param string $settleNo 结算单号 + * @param int $merchantId 商户ID + * @return array|null 结算详情 + */ public function settlementRecordDetail(string $settleNo, int $merchantId): ?array { return $this->financeService->settlementRecordDetail($settleNo, $merchantId); } + /** + * 查询商户可提现余额。 + * + * @param int $merchantId 商户ID + * @return array 余额数据 + */ public function withdrawableBalance(int $merchantId): array { return $this->financeService->withdrawableBalance($merchantId); } + /** + * 查询商户资金流水。 + * + * @param array $filters 筛选条件 + * @param int $merchantId 商户ID + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array 流水列表 + */ public function balanceFlows(array $filters, int $merchantId, int $page, int $pageSize): array { return $this->financeService->balanceFlows($filters, $merchantId, $page, $pageSize); diff --git a/app/service/merchant/portal/MerchantPortalSettlementService.php b/app/service/merchant/portal/MerchantPortalSettlementService.php index 04c721b..92d7b3b 100644 --- a/app/service/merchant/portal/MerchantPortalSettlementService.php +++ b/app/service/merchant/portal/MerchantPortalSettlementService.php @@ -8,15 +8,33 @@ use app\service\payment\settlement\SettlementOrderQueryService; /** * 商户门户清算服务。 + * + * @property MerchantPortalSupportService $supportService 支持服务 + * @property SettlementOrderQueryService $settlementOrderQueryService 结算订单查询服务 */ class MerchantPortalSettlementService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantPortalSupportService $supportService 支持服务 + * @param SettlementOrderQueryService $settlementOrderQueryService 结算订单查询服务 + */ public function __construct( protected MerchantPortalSupportService $supportService, protected SettlementOrderQueryService $settlementOrderQueryService ) { } + /** + * 查询商户结算记录。 + * + * @param array $filters 筛选条件 + * @param int $merchantId 商户ID + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array 结算记录列表 + */ public function settlementRecords(array $filters, int $merchantId, int $page, int $pageSize): array { $paginator = $this->settlementOrderQueryService->paginate($filters, $page, $pageSize, $merchantId); @@ -30,6 +48,13 @@ class MerchantPortalSettlementService extends BaseService ]; } + /** + * 查询商户结算记录详情。 + * + * @param string $settleNo 结算单号 + * @param int $merchantId 商户ID + * @return array|null 结算详情 + */ public function settlementRecordDetail(string $settleNo, int $merchantId): ?array { try { @@ -45,3 +70,6 @@ class MerchantPortalSettlementService extends BaseService ]; } } + + + diff --git a/app/service/merchant/portal/MerchantPortalSupportService.php b/app/service/merchant/portal/MerchantPortalSupportService.php index 041b2a4..f7cc762 100644 --- a/app/service/merchant/portal/MerchantPortalSupportService.php +++ b/app/service/merchant/portal/MerchantPortalSupportService.php @@ -10,12 +10,23 @@ use app\service\merchant\MerchantService; use app\service\payment\config\PaymentTypeService; /** - * 商户门户公共支持服务。 + * 商户门户支持服务。 * - * 统一承接商户门户里复用的商户摘要、支付方式和通用格式化能力。 + * 统一承接商户门户里复用的商户摘要、支付方式和展示整理能力。 + * + * @property MerchantService $merchantService 商户服务 + * @property MerchantRepository $merchantRepository 商户仓库 + * @property PaymentTypeService $paymentTypeService 支付类型服务 */ class MerchantPortalSupportService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantService $merchantService 商户服务 + * @param MerchantRepository $merchantRepository 商户仓库 + * @param PaymentTypeService $paymentTypeService 支付类型服务 + */ public function __construct( protected MerchantService $merchantService, protected MerchantRepository $merchantRepository, @@ -25,6 +36,10 @@ class MerchantPortalSupportService extends BaseService /** * 当前商户基础资料摘要。 + * + * @param int $merchantId 商户ID + * @return array 商户摘要 + * @throws ResourceNotFoundException */ public function merchantSummary(int $merchantId): array { @@ -106,7 +121,9 @@ class MerchantPortalSupportService extends BaseService } /** - * 启用的支付方式选项。 + * 获取启用的支付方式选项。 + * + * @return array 支付方式选项 */ public function enabledPayTypeOptions(): array { @@ -115,6 +132,9 @@ class MerchantPortalSupportService extends BaseService /** * 根据支付方式 ID 获取名称。 + * + * @param int $payTypeId 支付类型ID + * @return string 支付方式名称 */ public function paymentTypeName(int $payTypeId): string { @@ -127,72 +147,11 @@ class MerchantPortalSupportService extends BaseService return $payTypeId > 0 ? '未知' : ''; } - /** - * 格式化金额,单位为元。 - */ - public function formatAmount(int $amount): string - { - return parent::formatAmount($amount); - } - - /** - * 格式化金额,0 时显示不限。 - */ - public function formatAmountOrUnlimited(int $amount): string - { - return parent::formatAmountOrUnlimited($amount); - } - - /** - * 格式化次数,0 时显示不限。 - */ - public function formatCountOrUnlimited(int $count): string - { - return parent::formatCountOrUnlimited($count); - } - - /** - * 格式化费率,单位为百分点。 - */ - public function formatRate(int $basisPoints): string - { - return parent::formatRate($basisPoints); - } - - /** - * 格式化延迟。 - */ - public function formatLatency(int $latencyMs): string - { - return parent::formatLatency($latencyMs); - } - - /** - * 格式化日期时间。 - */ - public function formatDateTime(mixed $value, string $emptyText = ''): string - { - return parent::formatDateTime($value, $emptyText); - } - - /** - * 归一化模型对象,兼容模型和数组。 - */ - public function normalizeModel(mixed $value): ?array - { - return parent::normalizeModel($value); - } - - /** - * 隐藏接口凭证明文。 - */ - public function maskCredentialValue(string $credentialValue, bool $maskShortValue = true): string - { - return parent::maskCredentialValue($credentialValue, $maskShortValue); - } - /** * 签名类型文案。 + * + * @param int $signType 签名类型 + * @return string 签名类型文本 */ public function signTypeText(int $signType): string { diff --git a/app/service/merchant/security/MerchantApiCredentialQueryService.php b/app/service/merchant/security/MerchantApiCredentialQueryService.php index c8a46b0..8bd6723 100644 --- a/app/service/merchant/security/MerchantApiCredentialQueryService.php +++ b/app/service/merchant/security/MerchantApiCredentialQueryService.php @@ -8,14 +8,18 @@ use app\model\merchant\MerchantApiCredential; use app\repository\merchant\credential\MerchantApiCredentialRepository; /** - * 商户接口凭证查询服务。 + * 商户 API 凭证查询服务。 * * 负责凭证列表和详情展示,不承载验签和写入逻辑。 + * + * @property MerchantApiCredentialRepository $merchantApiCredentialRepository 商户 API 凭证仓库 */ class MerchantApiCredentialQueryService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param MerchantApiCredentialRepository $merchantApiCredentialRepository 商户 API 凭证仓库 */ public function __construct( protected MerchantApiCredentialRepository $merchantApiCredentialRepository @@ -23,7 +27,12 @@ class MerchantApiCredentialQueryService extends BaseService } /** - * 分页查询商户接口凭证。 + * 分页查询商户 API 凭证。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页对象 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -63,7 +72,10 @@ class MerchantApiCredentialQueryService extends BaseService } /** - * 查询商户接口凭证详情。 + * 查询商户 API 凭证详情。 + * + * @param int $id 商户 API 凭证ID + * @return MerchantApiCredential|null 凭证模型 */ public function findById(int $id): ?MerchantApiCredential { @@ -73,6 +85,9 @@ class MerchantApiCredentialQueryService extends BaseService /** * 查询商户对应的接口凭证详情。 + * + * @param int $merchantId 商户ID + * @return MerchantApiCredential|null 凭证模型 */ public function findByMerchantId(int $merchantId): ?MerchantApiCredential { @@ -82,6 +97,9 @@ class MerchantApiCredentialQueryService extends BaseService /** * 统一构造查询对象。 + * + * @param bool $maskCredentialValue 是否脱敏接口凭证 + * @return \Illuminate\Database\Eloquent\Builder 查询构造器 */ private function baseQuery(bool $maskCredentialValue = false) { @@ -112,6 +130,9 @@ class MerchantApiCredentialQueryService extends BaseService /** * 给详情行补充展示字段。 + * + * @param object|null $row 原始记录对象 + * @return MerchantApiCredential|null 凭证模型 */ private function decorateRow(mixed $row): ?MerchantApiCredential { diff --git a/app/service/merchant/security/MerchantApiCredentialService.php b/app/service/merchant/security/MerchantApiCredentialService.php index 951602e..9d4229a 100644 --- a/app/service/merchant/security/MerchantApiCredentialService.php +++ b/app/service/merchant/security/MerchantApiCredentialService.php @@ -15,12 +15,20 @@ use app\repository\merchant\base\MerchantRepository; /** * 商户对外接口凭证与签名校验服务。 * - * 负责外部支付接口的签名验证、接口凭证发放和最近使用时间更新。 + * 负责商户外部接口签名校验、接口凭证发放和最近使用时间更新。 + * + * @property MerchantRepository $merchantRepository 商户仓库 + * @property MerchantApiCredentialRepository $merchantApiCredentialRepository 商户 API 凭证仓库 + * @property MerchantApiCredentialQueryService $merchantApiCredentialQueryService 商户 API 凭证查询服务 */ class MerchantApiCredentialService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param MerchantRepository $merchantRepository 商户仓库 + * @param MerchantApiCredentialRepository $merchantApiCredentialRepository 商户 API 凭证仓库 + * @param MerchantApiCredentialQueryService $merchantApiCredentialQueryService 商户 API 凭证查询服务 */ public function __construct( protected MerchantRepository $merchantRepository, @@ -30,7 +38,12 @@ class MerchantApiCredentialService extends BaseService } /** - * 分页查询商户接口凭证。 + * 分页查询商户 API 凭证。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页对象 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -40,7 +53,12 @@ class MerchantApiCredentialService extends BaseService /** * 校验外部支付接口的 MD5 签名。 * - * @return array{merchant:\app\model\merchant\Merchant,credential:\app\model\merchant\MerchantApiCredential} + * 会先校验商户和接口凭证是否存在,再按签名规则计算并比对请求签名。 + * + * @param array $payload 请求载荷 + * @return array{merchant: Merchant, credential: MerchantApiCredential} 校验通过后的商户和凭证数据 + * @throws ValidationException + * @throws ResourceNotFoundException */ public function verifyMd5Sign(array $payload): array { @@ -66,15 +84,17 @@ class MerchantApiCredentialService extends BaseService /** @var MerchantApiCredential|null $credential */ $credential = $this->merchantApiCredentialRepository->findByMerchantId($merchantId); if (!$credential || (int) $credential->status !== AuthConstant::LOGIN_STATUS_ENABLED) { - throw new ValidationException('商户接口凭证未开通'); + throw new ValidationException('商户 API 凭证未开通'); } if ($providedKey !== '' && !hash_equals((string) $credential->api_key, $providedKey)) { - throw new ValidationException('商户接口凭证错误'); + throw new ValidationException('商户 API 凭证错误'); } + // 签名字段本身不参与原文拼接,只保留业务参数。 $params = $payload; unset($params['sign'], $params['sign_type'], $params['key']); + // 过滤空值并按键名排序,保证不同参数顺序下得到同一签名串。 foreach ($params as $paramKey => $paramValue) { if ($paramValue === '' || $paramValue === null) { unset($params[$paramKey]); @@ -84,12 +104,14 @@ class MerchantApiCredentialService extends BaseService $key = (string) $credential->api_key; $query = []; + // 旧版 ePay 采用 `a=1&b=2` 再拼接 key 的方式验签,这里保持兼容。 foreach ($params as $paramKey => $paramValue) { $query[] = $paramKey . '=' . $paramValue; } $base = implode('&', $query) . $key; $expected = md5($base); + // 使用常量时间比较,避免签名对比被时序差异放大。 if (!hash_equals(strtolower($expected), strtolower($sign))) { throw new ValidationException('签名验证失败'); } @@ -107,6 +129,10 @@ class MerchantApiCredentialService extends BaseService * 为商户生成并保存一份新的接口凭证。 * * 返回值是明文接口凭证值,只会在调用时完整出现一次,后续仅保存脱敏展示。 + * + * @param int $merchantId 商户ID + * @return string 新接口凭证 + * @throws ResourceNotFoundException */ public function issueCredential(int $merchantId): string { @@ -130,7 +156,10 @@ class MerchantApiCredentialService extends BaseService } /** - * 查询商户接口凭证详情。 + * 查询商户 API 凭证详情。 + * + * @param int $id 商户 API 凭证ID + * @return MerchantApiCredential|null 凭证模型 */ public function findById(int $id): ?MerchantApiCredential { @@ -139,6 +168,9 @@ class MerchantApiCredentialService extends BaseService /** * 查询商户对应的接口凭证详情。 + * + * @param int $merchantId 商户ID + * @return MerchantApiCredential|null 凭证模型 */ public function findByMerchantId(int $merchantId): ?MerchantApiCredential { @@ -146,7 +178,13 @@ class MerchantApiCredentialService extends BaseService } /** - * 新增或更新商户接口凭证。 + * 新增或更新商户 API 凭证。 + * + * 如果商户已有凭证,则转为更新;否则创建新记录。 + * + * @param array $data 凭证数据 + * @return MerchantApiCredential 凭证模型 + * @throws ResourceNotFoundException */ public function create(array $data): MerchantApiCredential { @@ -158,6 +196,7 @@ class MerchantApiCredentialService extends BaseService $current = $this->merchantApiCredentialRepository->findByMerchantId($merchantId); if ($current) { + // 同一商户只保留一份凭证,已有记录时优先走更新,避免重复创建。 $updated = $this->update((int) $current->id, $data); if ($updated) { return $updated; @@ -168,7 +207,11 @@ class MerchantApiCredentialService extends BaseService } /** - * 修改商户接口凭证。 + * 修改商户 API 凭证。 + * + * @param int $id 商户 API 凭证ID + * @param array $data 凭证数据 + * @return MerchantApiCredential|null 凭证模型 */ public function update(int $id, array $data): ?MerchantApiCredential { @@ -186,7 +229,10 @@ class MerchantApiCredentialService extends BaseService } /** - * 删除商户接口凭证。 + * 删除商户 API 凭证。 + * + * @param int $id 商户 API 凭证ID + * @return bool 是否删除成功 */ public function delete(int $id): bool { @@ -196,9 +242,13 @@ class MerchantApiCredentialService extends BaseService /** * 使用商户 ID 和接口凭证直接进行身份校验。 * - * 该方法用于兼容 epay 风格的查询接口,不涉及签名串验签。 + * 该方法用于兼容 ePay 风格的查询接口,不涉及签名串验签。 * - * @return array{merchant:\app\model\merchant\Merchant,credential:\app\model\merchant\MerchantApiCredential} + * @param int $merchantId 商户ID + * @param string $key 接口凭证 + * @return array{merchant: Merchant, credential: MerchantApiCredential} 商户和凭证数据 + * @throws ValidationException + * @throws ResourceNotFoundException */ public function authenticateByKey(int $merchantId, string $key): array { @@ -215,11 +265,12 @@ class MerchantApiCredentialService extends BaseService /** @var MerchantApiCredential|null $credential */ $credential = $this->merchantApiCredentialRepository->findByMerchantId($merchantId); if (!$credential || (int) $credential->status !== AuthConstant::LOGIN_STATUS_ENABLED) { - throw new ValidationException('商户接口凭证未开通'); + throw new ValidationException('商户 API 凭证未开通'); } + // 同样使用常量时间比较,避免明文 key 对比暴露额外信息。 if (!hash_equals((string) $credential->api_key, $key)) { - throw new ValidationException('商户接口凭证错误'); + throw new ValidationException('商户 API 凭证错误'); } $credential->last_used_at = $this->now(); @@ -233,9 +284,15 @@ class MerchantApiCredentialService extends BaseService /** * 整理写入字段。 + * + * @param array $data 凭证数据 + * @param bool $isUpdate 是否更新 + * @param MerchantApiCredential|null $current 当前凭证 + * @return array{merchant_id: int, sign_type: int, status: int, api_key?: string} 标准化后的写入数据 */ private function normalizePayload(array $data, bool $isUpdate, ?MerchantApiCredential $current = null): array { + // 更新场景下以现有记录的 merchant_id 为准,避免把凭证误挂到别的商户。 $merchantId = (int) ($current?->merchant_id ?? ($data['merchant_id'] ?? 0)); $payload = [ 'merchant_id' => $merchantId, @@ -247,6 +304,7 @@ class MerchantApiCredentialService extends BaseService if ($apiKey !== '') { $payload['api_key'] = $apiKey; } elseif (!$isUpdate) { + // 新增凭证时如果前端没有传入明文 key,就自动补一份随机值。 $payload['api_key'] = $this->generateCredentialValue(); } @@ -255,6 +313,8 @@ class MerchantApiCredentialService extends BaseService /** * 生成新的接口凭证值。 + * + * @return string 接口凭证 */ private function generateCredentialValue(): string { @@ -263,3 +323,8 @@ class MerchantApiCredentialService extends BaseService } + + + + + diff --git a/app/service/ops/log/ChannelNotifyLogService.php b/app/service/ops/log/ChannelNotifyLogService.php index c57ab21..d6ad580 100644 --- a/app/service/ops/log/ChannelNotifyLogService.php +++ b/app/service/ops/log/ChannelNotifyLogService.php @@ -9,11 +9,18 @@ use app\repository\ops\log\ChannelNotifyLogRepository; /** * 渠道通知日志查询服务。 + * + * 负责查询渠道通知记录、补充展示字段和还原通知处理状态。 + * + * @property ChannelNotifyLogRepository $channelNotifyLogRepository 渠道通知日志仓库 */ class ChannelNotifyLogService extends BaseService { /** - * 构造函数,注入渠道通知日志仓库。 + * 构造方法。 + * + * @param ChannelNotifyLogRepository $channelNotifyLogRepository 渠道通知日志仓库 + * @return void */ public function __construct( protected ChannelNotifyLogRepository $channelNotifyLogRepository @@ -22,6 +29,11 @@ class ChannelNotifyLogService extends BaseService /** * 分页查询渠道通知日志。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -84,7 +96,10 @@ class ChannelNotifyLogService extends BaseService } /** - * 按 ID 查询详情。 + * 按 ID 查询渠道通知日志详情。 + * + * @param int $id 渠道通知日志ID + * @return ChannelNotifyLog|null 日志模型 */ public function findById(int $id): ?ChannelNotifyLog { @@ -97,6 +112,9 @@ class ChannelNotifyLogService extends BaseService /** * 格式化单条记录。 + * + * @param object $row 查询结果对象 + * @return object 格式化后的对象 */ private function decorateRow(object $row): object { @@ -113,6 +131,8 @@ class ChannelNotifyLogService extends BaseService /** * 构建基础查询。 + * + * @return \Illuminate\Database\Eloquent\Builder 查询构造器 */ private function baseQuery() { @@ -152,3 +172,6 @@ class ChannelNotifyLogService extends BaseService } } + + + diff --git a/app/service/ops/log/PayCallbackLogService.php b/app/service/ops/log/PayCallbackLogService.php index 5d85901..c32ced5 100644 --- a/app/service/ops/log/PayCallbackLogService.php +++ b/app/service/ops/log/PayCallbackLogService.php @@ -9,11 +9,18 @@ use app\repository\ops\log\PayCallbackLogRepository; /** * 支付回调日志查询服务。 + * + * 负责查询支付回调记录、补充展示字段和还原回调处理状态。 + * + * @property PayCallbackLogRepository $payCallbackLogRepository 支付回调日志仓库 */ class PayCallbackLogService extends BaseService { /** - * 构造函数,注入支付回调日志仓库。 + * 构造方法。 + * + * @param PayCallbackLogRepository $payCallbackLogRepository 支付回调日志仓库 + * @return void */ public function __construct( protected PayCallbackLogRepository $payCallbackLogRepository @@ -22,6 +29,11 @@ class PayCallbackLogService extends BaseService /** * 分页查询支付回调日志。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -79,7 +91,10 @@ class PayCallbackLogService extends BaseService } /** - * 按 ID 查询详情。 + * 按 ID 查询支付回调日志详情。 + * + * @param int $id 支付回调日志ID + * @return PayCallbackLog|null 日志模型 */ public function findById(int $id): ?PayCallbackLog { @@ -92,6 +107,9 @@ class PayCallbackLogService extends BaseService /** * 格式化单条记录。 + * + * @param object $row 查询结果对象 + * @return object 格式化后的对象 */ private function decorateRow(object $row): object { @@ -107,6 +125,8 @@ class PayCallbackLogService extends BaseService /** * 构建基础查询。 + * + * @return \Illuminate\Database\Eloquent\Builder 查询构造器 */ private function baseQuery() { @@ -139,3 +159,6 @@ class PayCallbackLogService extends BaseService } } + + + diff --git a/app/service/ops/stat/ChannelDailyStatService.php b/app/service/ops/stat/ChannelDailyStatService.php index 3d85f8d..cc1c337 100644 --- a/app/service/ops/stat/ChannelDailyStatService.php +++ b/app/service/ops/stat/ChannelDailyStatService.php @@ -8,11 +8,18 @@ use app\repository\ops\stat\ChannelDailyStatRepository; /** * 通道日统计查询服务。 + * + * 负责渠道日统计列表、详情和展示字段补充。 + * + * @property ChannelDailyStatRepository $channelDailyStatRepository 渠道日统计仓库 */ class ChannelDailyStatService extends BaseService { /** - * 构造函数,注入通道日统计仓库。 + * 构造方法。 + * + * @param ChannelDailyStatRepository $channelDailyStatRepository 渠道日统计仓库 + * @return void */ public function __construct( protected ChannelDailyStatRepository $channelDailyStatRepository @@ -21,6 +28,11 @@ class ChannelDailyStatService extends BaseService /** * 分页查询通道日统计。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -67,7 +79,10 @@ class ChannelDailyStatService extends BaseService } /** - * 按 ID 查询详情。 + * 按 ID 查询渠道日统计详情。 + * + * @param int $id 渠道日统计ID + * @return ChannelDailyStat|null 统计模型 */ public function findById(int $id): ?ChannelDailyStat { @@ -80,6 +95,9 @@ class ChannelDailyStatService extends BaseService /** * 格式化单条统计记录。 + * + * @param object $row 查询结果对象 + * @return object 格式化后的对象 */ private function decorateRow(object $row): object { @@ -96,6 +114,8 @@ class ChannelDailyStatService extends BaseService /** * 构建基础查询。 + * + * @return \Illuminate\Database\Eloquent\Builder 查询构造器 */ private function baseQuery() { @@ -130,3 +150,7 @@ class ChannelDailyStatService extends BaseService } } + + + + diff --git a/app/service/payment/compat/EpayCompatService.php b/app/service/payment/compat/EpayCompatService.php index 4ce9940..36b5f69 100644 --- a/app/service/payment/compat/EpayCompatService.php +++ b/app/service/payment/compat/EpayCompatService.php @@ -22,10 +22,39 @@ use support\Request; use support\Response; use Throwable; +/** + * 旧版 Epay 协议兼容服务。 + * + * 负责将旧协议请求转换为当前支付、退款和查询流程。 + * + * @property MerchantApiCredentialService $merchantApiCredentialService 商户 API 凭证服务 + * @property PaymentTypeService $paymentTypeService 支付类型服务 + * @property PayOrderService $payOrderService 支付订单服务 + * @property PaymentPluginManager $paymentPluginManager 支付插件管理器 + * @property MerchantAccountRepository $merchantAccountRepository 商户账户仓库 + * @property BizOrderRepository $bizOrderRepository 业务订单仓库 + * @property PayOrderRepository $payOrderRepository 支付单仓库 + * @property SettlementOrderRepository $settlementOrderRepository 结算订单仓库 + * @property RefundService $refundService 退款服务 + */ class EpayCompatService extends BaseService { private const API_ACTIONS = ['query', 'settle', 'order', 'orders', 'refund']; + /** + * 构造方法。 + * + * @param MerchantApiCredentialService $merchantApiCredentialService 商户 API 凭证服务 + * @param PaymentTypeService $paymentTypeService 支付类型服务 + * @param PayOrderService $payOrderService 支付订单服务 + * @param PaymentPluginManager $paymentPluginManager 支付插件管理器 + * @param MerchantAccountRepository $merchantAccountRepository 商户账户仓库 + * @param BizOrderRepository $bizOrderRepository 业务订单仓库 + * @param PayOrderRepository $payOrderRepository 支付订单仓库 + * @param SettlementOrderRepository $settlementOrderRepository 结算订单仓库 + * @param RefundService $refundService 退款服务 + * @return void + */ public function __construct( protected MerchantApiCredentialService $merchantApiCredentialService, protected PaymentTypeService $paymentTypeService, @@ -39,6 +68,14 @@ class EpayCompatService extends BaseService ) { } + /** + * 处理页面跳转支付入口。 + * + * @param array $payload 请求载荷 + * @param Request $request 请求对象 + * @return Response 跳转响应或错误 JSON + * @throws ValidationException + */ public function submit(array $payload, Request $request): Response { try { @@ -58,6 +95,13 @@ class EpayCompatService extends BaseService } } + /** + * 处理 API 支付入口。 + * + * @param array $payload 请求载荷 + * @param Request $request 请求对象 + * @return array ePay 风格响应 + */ public function mapi(array $payload, Request $request): array { try { @@ -68,6 +112,14 @@ class EpayCompatService extends BaseService } } + /** + * 处理旧版兼容入口。 + * + * 支持 `query`、`settle`、`order`、`orders` 和 `refund` 五种操作。 + * + * @param array $payload 请求载荷 + * @return array ePay 风格响应 + */ public function api(array $payload): array { $act = strtolower(trim((string) ($payload['act'] ?? ''))); @@ -84,6 +136,12 @@ class EpayCompatService extends BaseService }; } + /** + * 查询商户信息,对应 `act=query`。 + * + * @param array $payload 请求载荷 + * @return array ePay 风格响应 + */ public function queryMerchantInfo(array $payload): array { try { @@ -93,6 +151,7 @@ class EpayCompatService extends BaseService $merchant = $auth['merchant']; $credential = $auth['credential']; $account = $this->merchantAccountRepository->findByMerchantId($merchantId); + // 旧协议会同时返回总单量、今日单量和昨日单量,便于上游直接做商户概览。 $todayDate = FormatHelper::timestamp(time(), 'Y-m-d'); $lastDayDate = FormatHelper::timestamp(strtotime('-1 day'), 'Y-m-d'); $totalOrders = (int) $this->payOrderRepository->query()->where('merchant_id', $merchantId)->count(); @@ -117,6 +176,12 @@ class EpayCompatService extends BaseService } } + /** + * 查询结算记录列表,对应 `act=settle`。 + * + * @param array $payload 请求载荷 + * @return array ePay 风格响应 + */ public function querySettlementList(array $payload): array { try { @@ -125,6 +190,7 @@ class EpayCompatService extends BaseService $this->merchantApiCredentialService->authenticateByKey($merchantId, $key); $rows = $this->settlementOrderRepository->query()->where('merchant_id', $merchantId)->orderByDesc('id')->get(); + // 旧协议列表只需要基础字段和金额文本,这里直接整理成可展示数组。 return [ 'code' => 1, 'msg' => '查询结算记录成功!', @@ -147,6 +213,12 @@ class EpayCompatService extends BaseService } } + /** + * 查询单个订单,对应 `act=order`。 + * + * @param array $payload 请求载荷 + * @return array ePay 风格响应 + */ public function queryOrder(array $payload): array { try { @@ -158,18 +230,26 @@ class EpayCompatService extends BaseService return ['code' => 0, 'msg' => '订单不存在']; } + // 旧协议查询单号时,要把支付单和业务单合并成同一份响应结构。 return ['code' => 1, 'msg' => '查询订单号成功!'] + $this->formatEpayOrderRow($context['pay_order'], $context['biz_order']); } catch (Throwable $e) { return ['code' => 0, 'msg' => $this->normalizeErrorMessage($e, '查询失败')]; } } + /** + * 批量查询订单,对应 `act=orders`。 + * + * @param array $payload 请求载荷 + * @return array ePay 风格响应 + */ public function queryOrders(array $payload): array { try { $merchantId = (int) ($payload['pid'] ?? 0); $key = trim((string) ($payload['key'] ?? '')); $this->merchantApiCredentialService->authenticateByKey($merchantId, $key); + // 旧接口默认只允许一次拉少量订单,这里沿用上限 50 的兼容口径。 $limit = min(50, max(1, (int) ($payload['limit'] ?? 20))); $page = max(1, (int) ($payload['page'] ?? 1)); $paginator = $this->payOrderRepository->query()->where('merchant_id', $merchantId)->orderByDesc('id')->paginate($limit, ['*'], 'page', $page); @@ -177,6 +257,7 @@ class EpayCompatService extends BaseService return [ 'code' => 1, 'msg' => '查询结算记录成功!', + // 批量查询和单条查询共用同一套格式化器,避免字段口径不一致。 'data' => array_map(function ($row): array { return $this->formatEpayOrderRow($row, $this->bizOrderRepository->findByBizNo((string) $row->biz_no)); }, $paginator->items()), @@ -186,12 +267,19 @@ class EpayCompatService extends BaseService } } + /** + * 提交退款申请,对应 `act=refund`。 + * + * @param array $payload 请求载荷 + * @return array ePay 风格响应 + */ public function createRefund(array $payload): array { try { $merchantId = (int) ($payload['pid'] ?? 0); $key = trim((string) ($payload['key'] ?? '')); $this->merchantApiCredentialService->authenticateByKey($merchantId, $key); + // 先确认退款目标单据归属当前商户,避免旧协议拿着别人的单号误发退款。 $context = $this->resolvePayOrderContext($merchantId, $payload); if (!$context) { return ['code' => 1, 'msg' => '订单不存在']; @@ -213,6 +301,7 @@ class EpayCompatService extends BaseService ]); $plugin = $this->paymentPluginManager->createByPayOrder($payOrder, true); + // 不同插件返回的退款结果字段不完全一致,这里仍按旧协议的退款参数重新组织一次。 $pluginResult = $plugin->refund([ 'order_id' => (string) $payOrder->pay_no, 'pay_no' => (string) $payOrder->pay_no, @@ -227,6 +316,7 @@ class EpayCompatService extends BaseService ]); if (!$this->isPluginSuccess($pluginResult)) { + // 渠道明确失败时,先把退款单推进失败态,再把旧协议响应收口成失败文案。 $this->refundService->markRefundFailed((string) $refundOrder->refund_no, [ 'failed_at' => $this->now(), 'last_error' => (string) ($pluginResult['msg'] ?? $pluginResult['message'] ?? '退款失败'), @@ -249,8 +339,18 @@ class EpayCompatService extends BaseService } } + /** + * 预处理支付提交请求。 + * + * 这里负责把旧协议载荷转换为当前支付单创建所需的数据结构。 + * + * @param array $payload 请求载荷 + * @param Request $request 请求对象 + * @return array 预处理数据 + */ private function prepareSubmitAttempt(array $payload, Request $request): array { + // 先把旧协议载荷转换成当前系统的统一入参,再交给支付单主流程处理。 $normalized = $this->normalizeSubmitPayload($payload, $request); $result = $this->payOrderService->preparePayAttempt($normalized); $payOrder = $result['pay_order']; @@ -265,8 +365,19 @@ class EpayCompatService extends BaseService ]; } + /** + * 归一化提交支付参数。 + * + * 这里会完成签名校验、金额转分、支付方式解析,并把旧协议字段写入扩展信息。 + * + * @param array $payload 请求载荷 + * @param Request $request 请求对象 + * @return array 当前支付单创建参数 + * @throws ValidationException + */ private function normalizeSubmitPayload(array $payload, Request $request): array { + // 提交入口也必须先验签,避免旧协议请求绕过统一的身份校验。 $this->merchantApiCredentialService->verifyMd5Sign($payload); $typeCode = trim((string) ($payload['type'] ?? '')); $merchantOrderNo = trim((string) ($payload['out_trade_no'] ?? '')); @@ -296,6 +407,7 @@ class EpayCompatService extends BaseService 'submitted_type' => $typeCode, 'submit_mode' => $typeCode === '' ? 'cashier' : 'direct', 'request_method' => strtoupper((string) ($_SERVER['REQUEST_METHOD'] ?? 'POST')), + // 原始请求快照保留在扩展字段里,方便后续排查旧协议参数差异。 'request_snapshot' => $this->normalizeRequestSnapshot($payload), 'channel_callback_base_url' => (string) sys_config('site_url') . '/api/pay', ]; @@ -311,6 +423,15 @@ class EpayCompatService extends BaseService ]; } + /** + * 解析提交支付方式。 + * + * 空支付方式时,沿用当前系统默认启用支付方式;显式传值时必须是启用中的支付方式。 + * + * @param string $typeCode 支付方式编码 + * @return PaymentType 支付方式模型 + * @throws ValidationException + */ private function resolveSubmitPaymentType(string $typeCode): PaymentType { $typeCode = trim($typeCode); @@ -326,6 +447,14 @@ class EpayCompatService extends BaseService return $paymentType; } + /** + * 构建旧版 MAPI 返回结构。 + * + * 根据当前支付尝试结果,输出 payurl、qrcode 或 urlscheme 等旧协议字段。 + * + * @param array $attempt 支付尝试结果 + * @return array ePay 风格响应 + */ private function buildMapiResponse(array $attempt): array { /** @var PayOrder $payOrder */ @@ -336,6 +465,7 @@ class EpayCompatService extends BaseService $response = ['code' => 1, 'msg' => '提交成功', 'trade_no' => $payNo]; $type = (string) ($payParams['type'] ?? ''); + // 不同插件返回的支付承载形态不同,这里按旧协议常见字段逐个兼容。 if ($type === 'qrcode') { $qrcode = $this->stringifyValue($payParams['qrcode_url'] ?? $payParams['qrcode_data'] ?? ''); if ($qrcode !== '') { @@ -364,6 +494,7 @@ class EpayCompatService extends BaseService } if ($type === 'form' && $this->stringifyValue($payParams['html'] ?? '') !== '') { + // 表单类承载本身会把页面内容交给插件,这里仍然只回传收银台入口。 $response['payurl'] = $cashierUrl; return $response; } @@ -385,6 +516,13 @@ class EpayCompatService extends BaseService return $response; } + /** + * 将当前支付单格式化为旧版订单查询结构。 + * + * @param PayOrder $payOrder 支付订单 + * @param BizOrder|null $bizOrder 业务订单 + * @return array 旧版订单结构 + */ private function formatEpayOrderRow(PayOrder $payOrder, ?BizOrder $bizOrder = null): array { $bizOrder ??= $this->bizOrderRepository->findByBizNo((string) $payOrder->biz_no); @@ -406,6 +544,15 @@ class EpayCompatService extends BaseService ]; } + /** + * 解析支付订单上下文。 + * + * 优先按 `trade_no` 查找,其次按 `out_trade_no` 回退,并校验订单归属当前商户。 + * + * @param int $merchantId 商户ID + * @param array $payload 请求载荷 + * @return array{pay_order: PayOrder, biz_order: BizOrder|null}|null 上下文 + */ private function resolvePayOrderContext(int $merchantId, array $payload): ?array { $payNo = trim((string) ($payload['trade_no'] ?? '')); @@ -414,6 +561,7 @@ class EpayCompatService extends BaseService $bizOrder = null; if ($payNo !== '') { + // 旧协议如果传了 trade_no,就优先按支付单号定位,命中率最高。 $payOrder = $this->payOrderRepository->findByPayNo($payNo); if ($payOrder) { $bizOrder = $this->bizOrderRepository->findByBizNo((string) $payOrder->biz_no); @@ -421,12 +569,15 @@ class EpayCompatService extends BaseService } if (!$payOrder && $merchantOrderNo !== '') { + // 没有 trade_no 时,再按商户单号反查业务单和最新支付单。 $bizOrder = $this->bizOrderRepository->findByMerchantAndOrderNo($merchantId, $merchantOrderNo); if ($bizOrder) { + // 旧协议经常只传商户单号,这里拿业务单找到最新一笔支付单。 $payOrder = $this->payOrderRepository->findLatestByBizNo((string) $bizOrder->biz_no); } } + // 旧协议有时会传到别家商户的单号,这里必须再次校验归属,避免跨商户读取。 if (!$payOrder || (int) $payOrder->merchant_id !== $merchantId) { return null; } @@ -438,13 +589,26 @@ class EpayCompatService extends BaseService return ['pay_order' => $payOrder, 'biz_order' => $bizOrder]; } + /** + * 根据支付方式 ID 解析支付方式编码。 + * + * @param int $payTypeId 支付方式ID + * @return string 支付方式编码 + */ private function resolvePaymentTypeCode(int $payTypeId): string { return $this->paymentTypeService->resolveCodeById($payTypeId); } + /** + * 解析商户结算类型。 + * + * @param object $merchant 商户对象 + * @return int 结算类型编码 + */ private function resolveMerchantSettlementType(mixed $merchant): int { + // 旧 Epay 协议里结算类型是约定好的整数,这里用账户信息做一个兼容性映射。 $bankName = strtolower(trim((string) ($merchant->settlement_bank_name ?? ''))); $accountName = strtolower(trim((string) ($merchant->settlement_account_name ?? ''))); $accountNo = strtolower(trim((string) ($merchant->settlement_account_no ?? ''))); @@ -468,6 +632,12 @@ class EpayCompatService extends BaseService return 4; } + /** + * 将元金额转成分。 + * + * @param string $money 金额字符串 + * @return int 金额分值,非法时返回 0 + */ private function parseMoneyToAmount(string $money): int { $money = trim($money); @@ -475,9 +645,19 @@ class EpayCompatService extends BaseService return 0; } + // 旧协议金额按“元”传入,内部统一转成“分”处理。 return (int) round(((float) $money) * 100); } + /** + * 解析客户端 IP。 + * + * 优先使用旧协议中的 `clientip`,缺省时回退到请求真实 IP。 + * + * @param array $payload 请求载荷 + * @param Request $request 请求对象 + * @return string 客户端 IP + */ private function resolveClientIp(array $payload, Request $request): string { $clientIp = trim((string) ($payload['clientip'] ?? '')); @@ -485,15 +665,29 @@ class EpayCompatService extends BaseService return $clientIp; } + // 旧请求没传 clientip 时,退回到框架识别的真实 IP。 return trim((string) $request->getRealIp()); } + /** + * 归一化设备类型。 + * + * @param string $device 设备编码 + * @return string 归一化后的设备编码 + */ private function normalizeDeviceCode(string $device): string { $device = strtolower(trim($device)); + // 没传设备类型时默认按 pc 处理,兼容旧接口的页面跳转场景。 return $device !== '' ? $device : 'pc'; } + /** + * 归一化旧协议扩展参数。 + * + * @param array|object|bool|float|int|string|null $value 扩展参数 + * @return array|string|null 归一化后的值 + */ private function normalizePayloadValue(mixed $value): mixed { if ($value === null) { @@ -512,30 +706,67 @@ class EpayCompatService extends BaseService return is_scalar($value) ? (string) $value : null; } + /** + * 生成请求快照。 + * + * 快照会移除敏感签名字段,便于落库排障。 + * + * @param array $payload 请求载荷 + * @return array 请求快照 + */ private function normalizeRequestSnapshot(array $payload): array { $snapshot = $payload; + // 签名字段和内部 submit_mode 不参与快照展示,避免误导排障。 unset($snapshot['sign'], $snapshot['key']); unset($snapshot['submit_mode']); return $snapshot; } + /** + * 构建收银台跳转地址。 + * + * @param string $payNo 支付单号 + * @return string 收银台 URL + */ private function buildCashierUrl(string $payNo): string { return (string) sys_config('site_url') . '/pay/' . rawurlencode($payNo) . '/payment'; } + /** + * 规范化异常提示。 + * + * @param Throwable $e 异常对象 + * @param string $fallback 默认文案 + * @return string 错误提示 + */ private function normalizeErrorMessage(Throwable $e, string $fallback): string { $message = trim((string) $e->getMessage()); return $message !== '' ? $message : $fallback; } + /** + * 判断插件返回的 success 标记。 + * + * 如果插件未显式返回 `success`,则默认视为成功。 + * + * @param array $pluginResult 插件结果 + * @return bool 插件是否通过 + */ private function isPluginSuccess(array $pluginResult): bool { return !array_key_exists('success', $pluginResult) || (bool) $pluginResult['success']; } + /** + * 解析退款渠道单号。 + * + * @param array $pluginResult 插件结果 + * @param string $default 默认值 + * @return string 渠道退款单号 + */ private function resolveRefundChannelNo(array $pluginResult, string $default = ''): string { foreach (['chan_refund_no', 'refund_no', 'trade_no', 'out_request_no'] as $key) { @@ -550,6 +781,12 @@ class EpayCompatService extends BaseService return $default; } + /** + * 将任意值规范化为字符串。 + * + * @param array|object|bool|float|int|string|null $value 待转换值 + * @return string 规范化后的字符串 + */ private function stringifyValue(mixed $value): string { if ($value === null) { @@ -562,6 +799,7 @@ class EpayCompatService extends BaseService return (string) $value; } if (is_array($value) || is_object($value)) { + // 复杂结构直接 JSON 化,保证旧协议回显时仍然可读。 $json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); return $json !== false ? $json : ''; } diff --git a/app/service/payment/config/PaymentChannelCommandService.php b/app/service/payment/config/PaymentChannelCommandService.php index 91daef8..5bc968d 100644 --- a/app/service/payment/config/PaymentChannelCommandService.php +++ b/app/service/payment/config/PaymentChannelCommandService.php @@ -13,9 +13,25 @@ use app\repository\payment\config\PaymentTypeRepository; /** * 支付通道命令服务。 + * + * 负责支付通道的新增、修改、删除以及写入前的商户、插件和支付方式约束校验。 + * + * @property MerchantRepository $merchantRepository 商户仓库 + * @property PaymentChannelRepository $paymentChannelRepository 支付渠道仓库 + * @property PaymentPluginRepository $paymentPluginRepository 支付插件仓库 + * @property PaymentTypeRepository $paymentTypeRepository 支付类型仓库 */ class PaymentChannelCommandService extends BaseService { + /** + * 构造方法。 + * + * @param MerchantRepository $merchantRepository 商户仓库 + * @param PaymentChannelRepository $paymentChannelRepository 支付渠道仓库 + * @param PaymentPluginRepository $paymentPluginRepository 支付插件仓库 + * @param PaymentTypeRepository $paymentTypeRepository 支付类型仓库 + * @return void + */ public function __construct( protected MerchantRepository $merchantRepository, protected PaymentChannelRepository $paymentChannelRepository, @@ -24,13 +40,27 @@ class PaymentChannelCommandService extends BaseService ) { } + /** + * 按 ID 查询支付通道。 + * + * @param int $id 支付通道ID + * @return PaymentChannel|null 支付通道模型 + */ public function findById(int $id): ?PaymentChannel { return $this->paymentChannelRepository->find($id); } + /** + * 新增支付通道。 + * + * @param array $data 写入数据 + * @return PaymentChannel 新增后的支付通道模型 + * @throws PaymentException + */ public function create(array $data): PaymentChannel { + // 新增通道前先校验名称、商户归属和插件支付方式兼容性。 $this->assertChannelNameUnique((string) ($data['name'] ?? '')); $this->assertMerchantExists($data); $this->assertPluginSupportsPayType($data); @@ -38,8 +68,17 @@ class PaymentChannelCommandService extends BaseService return $this->paymentChannelRepository->create($data); } + /** + * 更新支付通道。 + * + * @param int $id 支付通道ID + * @param array $data 写入数据 + * @return PaymentChannel|null 更新后的支付通道模型 + * @throws PaymentException + */ public function update(int $id, array $data): ?PaymentChannel { + // 更新通道时同样要先拦住冲突配置,避免保存后才发现路由不可用。 $this->assertChannelNameUnique((string) ($data['name'] ?? ''), $id); $this->assertMerchantExists($data); $this->assertPluginSupportsPayType($data); @@ -51,11 +90,24 @@ class PaymentChannelCommandService extends BaseService return $this->paymentChannelRepository->find($id); } + /** + * 删除支付通道。 + * + * @param int $id 支付通道ID + * @return bool 是否删除成功 + */ public function delete(int $id): bool { return $this->paymentChannelRepository->deleteById($id); } + /** + * 校验通道所属商户是否存在。 + * + * @param array $data 写入数据 + * @return void + * @throws PaymentException + */ private function assertMerchantExists(array $data): void { if (!array_key_exists('merchant_id', $data)) { @@ -63,6 +115,7 @@ class PaymentChannelCommandService extends BaseService } $merchantId = (int) $data['merchant_id']; + // merchant_id 为空或为 0 时通常表示通道草稿,这里不强制拦截。 if ($merchantId === 0) { return; } @@ -74,11 +127,19 @@ class PaymentChannelCommandService extends BaseService } } + /** + * 校验支付插件是否支持当前支付方式。 + * + * @param array $data 写入数据 + * @return void + * @throws PaymentException + */ private function assertPluginSupportsPayType(array $data): void { $pluginCode = trim((string) ($data['plugin_code'] ?? '')); $payTypeId = (int) ($data['pay_type_id'] ?? 0); + // 草稿态允许只填一半字段,只有插件和支付方式都明确时才做交叉校验。 if ($pluginCode === '' || $payTypeId <= 0) { return; } @@ -90,6 +151,7 @@ class PaymentChannelCommandService extends BaseService return; } + // 插件支持的支付方式可能来自 JSON 配置,先统一压成编码列表再比对。 $payTypes = is_array($plugin->pay_types) ? $plugin->pay_types : []; $payTypeCodes = array_values(array_filter(array_map(static fn ($item) => trim((string) $item), $payTypes))); $payTypeCode = trim((string) $paymentType->code); @@ -102,6 +164,14 @@ class PaymentChannelCommandService extends BaseService } } + /** + * 校验通道名称唯一。 + * + * @param string $name 通道名称 + * @param int $ignoreId 排除的通道ID + * @return void + * @throws PaymentException + */ private function assertChannelNameUnique(string $name, int $ignoreId = 0): void { $name = trim($name); @@ -117,3 +187,5 @@ class PaymentChannelCommandService extends BaseService } } } + + diff --git a/app/service/payment/config/PaymentChannelQueryService.php b/app/service/payment/config/PaymentChannelQueryService.php index 2bb0ce7..c4fa881 100644 --- a/app/service/payment/config/PaymentChannelQueryService.php +++ b/app/service/payment/config/PaymentChannelQueryService.php @@ -8,15 +8,30 @@ use app\model\payment\PaymentChannel; use app\repository\payment\config\PaymentChannelRepository; /** - * 支付通道查询服务。 + * 支付通道查询与选项拼装服务。 + * + * 负责支付通道列表、详情、下拉选项和路由候选数据的查询拼装。 + * + * @property PaymentChannelRepository $paymentChannelRepository 支付渠道仓库 */ class PaymentChannelQueryService extends BaseService { + /** + * 构造方法。 + * + * @param PaymentChannelRepository $paymentChannelRepository 支付渠道仓库 + * @return void + */ public function __construct( protected PaymentChannelRepository $paymentChannelRepository ) { } + /** + * 获取启用支付通道选项。 + * + * @return array 启用通道选项 + */ public function enabledOptions(): array { return $this->paymentChannelRepository->query() @@ -38,6 +53,14 @@ class PaymentChannelQueryService extends BaseService ->all(); } + /** + * 搜索支付通道选项。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array{list: array, total: int, page: int, size: int} 通道搜索结果 + */ public function searchOptions(array $filters = [], int $page = 1, int $pageSize = 20): array { $query = $this->paymentChannelRepository->query() @@ -59,12 +82,15 @@ class PaymentChannelQueryService extends BaseService $ids = $this->normalizeIds($filters['ids'] ?? []); if (!empty($ids)) { + // 显式传 ID 时,直接按 ID 集合返回,避免再叠加其他筛选条件影响回显。 $query->whereIn('c.id', $ids); } else { + // 选择器默认只给启用通道,避免把已停用的历史数据混进后台下拉框。 $query->where('c.status', CommonConstant::STATUS_ENABLED); $keyword = trim((string) ($filters['keyword'] ?? '')); if ($keyword !== '') { + // 关键词同时支持通道、插件和商户维度搜索,方便后台快速定位路由节点。 $query->where(function ($builder) use ($keyword) { $builder->where('c.name', 'like', '%' . $keyword . '%') ->orWhere('c.plugin_code', 'like', '%' . $keyword . '%') @@ -87,6 +113,7 @@ class PaymentChannelQueryService extends BaseService $excludeIds = $this->normalizeIds($filters['exclude_ids'] ?? []); if (!empty($excludeIds)) { + // 编排时经常要排除当前已选项,这里提供反选列表避免重复挂载同一通道。 $query->whereNotIn('c.id', $excludeIds); } } @@ -119,6 +146,12 @@ class PaymentChannelQueryService extends BaseService ]; } + /** + * 获取支付通道路由候选选项。 + * + * @param array $filters 筛选条件 + * @return array 路由候选选项 + */ public function routeOptions(array $filters = []): array { $query = $this->paymentChannelRepository->query() @@ -140,6 +173,7 @@ class PaymentChannelQueryService extends BaseService } if (array_key_exists('merchant_id', $filters) && $filters['merchant_id'] !== '') { + // 路由预览/编排时会按商户分组筛选通道,这里直接用商户 ID 限定范围。 $query->where('c.merchant_id', (int) $filters['merchant_id']); } @@ -162,6 +196,14 @@ class PaymentChannelQueryService extends BaseService ->all(); } + /** + * 分页查询支付通道。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 + */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { $query = $this->paymentChannelRepository->query() @@ -175,6 +217,7 @@ class PaymentChannelQueryService extends BaseService $keyword = trim((string) ($filters['keyword'] ?? '')); if ($keyword !== '') { + // 列表页的搜索同时覆盖通道名、插件编码和商户信息,便于运营一次性查到整条链路。 $query->where(function ($builder) use ($keyword) { $builder->where('c.name', 'like', '%' . $keyword . '%') ->orWhere('c.plugin_code', 'like', '%' . $keyword . '%') @@ -210,11 +253,23 @@ class PaymentChannelQueryService extends BaseService ->paginate(max(1, $pageSize), ['*'], 'page', max(1, $page)); } + /** + * 按 ID 查询支付通道。 + * + * @param int $id 支付通道ID + * @return PaymentChannel|null 支付通道模型 + */ public function findById(int $id): ?PaymentChannel { return $this->paymentChannelRepository->find($id); } + /** + * 归一化 ID 列表。 + * + * @param array|string|int $ids 通道ID或ID列表 + * @return array ID 列表 + */ private function normalizeIds(array|string|int $ids): array { if (is_string($ids)) { @@ -223,6 +278,9 @@ class PaymentChannelQueryService extends BaseService $ids = [$ids]; } + // 下拉/搜索参数有时是字符串、有时是数组,统一压成正整数列表后再查询。 return array_values(array_filter(array_map(static fn ($id) => (int) $id, $ids), static fn ($id) => $id > 0)); } } + + diff --git a/app/service/payment/config/PaymentChannelService.php b/app/service/payment/config/PaymentChannelService.php index ae647b4..5c40d9a 100644 --- a/app/service/payment/config/PaymentChannelService.php +++ b/app/service/payment/config/PaymentChannelService.php @@ -6,53 +6,116 @@ use app\common\base\BaseService; use app\model\payment\PaymentChannel; /** - * 支付通道门面服务。 + * 支付通道服务。 + * + * @property PaymentChannelQueryService $queryService 查询服务 + * @property PaymentChannelCommandService $commandService 命令服务 */ class PaymentChannelService extends BaseService { + /** + * 构造方法。 + * + * @param PaymentChannelQueryService $queryService 查询服务 + * @param PaymentChannelCommandService $commandService 命令服务 + * @return void + */ public function __construct( protected PaymentChannelQueryService $queryService, protected PaymentChannelCommandService $commandService ) { } + /** + * 获取启用支付通道选项。 + * + * @return array 启用通道选项 + */ public function enabledOptions(): array { return $this->queryService->enabledOptions(); } + /** + * 搜索支付通道选项。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array{list: array>, total: int, page: int, size: int} 通道搜索结果 + */ public function searchOptions(array $filters = [], int $page = 1, int $pageSize = 20): array { return $this->queryService->searchOptions($filters, $page, $pageSize); } + /** + * 获取支付渠道路由选项。 + * + * @param array $filters 筛选条件 + * @return array> 路由候选选项 + */ public function routeOptions(array $filters = []): array { return $this->queryService->routeOptions($filters); } + /** + * 分页查询支付通道。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 + */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { return $this->queryService->paginate($filters, $page, $pageSize); } + /** + * 按 ID 查询支付通道。 + * + * @param int $id 支付通道ID + * @return PaymentChannel|null 支付通道模型 + */ public function findById(int $id): ?PaymentChannel { return $this->queryService->findById($id); } + /** + * 新增支付通道。 + * + * @param array $data 写入数据 + * @return PaymentChannel 新增后的支付通道模型 + */ public function create(array $data): PaymentChannel { return $this->commandService->create($data); } + /** + * 更新支付通道。 + * + * @param int $id 支付通道ID + * @param array $data 写入数据 + * @return PaymentChannel|null 更新后的支付通道模型 + */ public function update(int $id, array $data): ?PaymentChannel { return $this->commandService->update($id, $data); } + /** + * 删除支付通道。 + * + * @param int $id 支付通道ID + * @return bool 是否删除成功 + */ public function delete(int $id): bool { return $this->commandService->delete($id); } } + diff --git a/app/service/payment/config/PaymentPluginConfService.php b/app/service/payment/config/PaymentPluginConfService.php index 9bb2142..ef1a07c 100644 --- a/app/service/payment/config/PaymentPluginConfService.php +++ b/app/service/payment/config/PaymentPluginConfService.php @@ -11,10 +11,20 @@ use app\repository\payment\config\PaymentPluginRepository; /** * 支付插件配置服务。 * - * 负责插件公共配置的增删改查和下拉选项输出。 + * 负责支付插件公共配置的增删改查、下拉选项输出以及插件存在性校验。 + * + * @property PaymentPluginConfRepository $paymentPluginConfRepository 支付插件配置仓库 + * @property PaymentPluginRepository $paymentPluginRepository 支付插件仓库 */ class PaymentPluginConfService extends BaseService { + /** + * 构造方法。 + * + * @param PaymentPluginConfRepository $paymentPluginConfRepository 支付插件配置仓库 + * @param PaymentPluginRepository $paymentPluginRepository 支付插件仓库 + * @return void + */ public function __construct( protected PaymentPluginConfRepository $paymentPluginConfRepository, protected PaymentPluginRepository $paymentPluginRepository @@ -23,6 +33,11 @@ class PaymentPluginConfService extends BaseService /** * 分页查询插件配置。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -43,6 +58,7 @@ class PaymentPluginConfService extends BaseService $keyword = trim((string) ($filters['keyword'] ?? '')); if ($keyword !== '') { + // 列表页关键词同时覆盖插件编码、备注和插件名称,方便后台快速定位配置记录。 $query->where(function ($builder) use ($keyword) { $builder->where('c.plugin_code', 'like', '%' . $keyword . '%') ->orWhere('c.remark', 'like', '%' . $keyword . '%') @@ -62,6 +78,9 @@ class PaymentPluginConfService extends BaseService /** * 按 ID 查询插件配置。 + * + * @param int $id 支付插件配置ID + * @return PaymentPluginConf|null 插件配置模型 */ public function findById(int $id): ?PaymentPluginConf { @@ -70,6 +89,10 @@ class PaymentPluginConfService extends BaseService /** * 新增插件配置。 + * + * @param array $data 写入数据 + * @return PaymentPluginConf 新增后的插件配置模型 + * @throws PaymentException */ public function create(array $data): PaymentPluginConf { @@ -81,6 +104,11 @@ class PaymentPluginConfService extends BaseService /** * 修改插件配置。 + * + * @param int $id 支付插件配置ID + * @param array $data 写入数据 + * @return PaymentPluginConf|null 更新后的插件配置模型 + * @throws PaymentException */ public function update(int $id, array $data): ?PaymentPluginConf { @@ -96,6 +124,9 @@ class PaymentPluginConfService extends BaseService /** * 删除插件配置。 + * + * @param int $id 支付插件配置ID + * @return bool 是否删除成功 */ public function delete(int $id): bool { @@ -104,6 +135,9 @@ class PaymentPluginConfService extends BaseService /** * 查询插件配置下拉选项。 + * + * @param string|null $pluginCode 插件编码 + * @return array 配置选项 */ public function options(?string $pluginCode = null): array { @@ -120,6 +154,7 @@ class PaymentPluginConfService extends BaseService ->orderByDesc('c.id'); if ($pluginCode !== '') { + // 如果前端已经明确指定插件编码,就只回这个插件下的配置选项。 $query->where('c.plugin_code', $pluginCode); } @@ -138,7 +173,12 @@ class PaymentPluginConfService extends BaseService } /** - * 远程查询插件配置选择项。 + * 搜索插件配置选择项。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array{list: array, total: int, page: int, size: int} 配置搜索结果 */ public function searchOptions(array $filters = [], int $page = 1, int $pageSize = 20): array { @@ -154,15 +194,18 @@ class PaymentPluginConfService extends BaseService $ids = $filters['ids'] ?? []; if (is_array($ids) && $ids !== []) { + // 显式传 ID 时优先按配置主键回显,避免关键词过滤把已选项漏掉。 $query->whereIn('c.id', array_map('intval', $ids)); } else { $pluginCode = trim((string) ($filters['plugin_code'] ?? '')); if ($pluginCode !== '') { + // 插件编码是配置项的一级过滤条件,先收窄到单个插件。 $query->where('c.plugin_code', $pluginCode); } $keyword = trim((string) ($filters['keyword'] ?? '')); if ($keyword !== '') { + // 数字关键词既可以按配置 ID 查,也可以按编码或备注查。 $query->where(function ($builder) use ($keyword) { $builder->where('c.plugin_code', 'like', '%' . $keyword . '%') ->orWhere('p.name', 'like', '%' . $keyword . '%') @@ -197,13 +240,18 @@ class PaymentPluginConfService extends BaseService } /** - * 标准化写入数据。 + * 标准化插件配置写入数据。 + * + * @param array $data 写入数据 + * @return array 标准化后的数据 */ private function normalizePayload(array $data): array { return [ 'plugin_code' => trim((string) ($data['plugin_code'] ?? '')), + // 配置内容统一按数组保存,外部传入非数组时直接回退为空数组。 'config' => is_array($data['config'] ?? null) ? $data['config'] : [], + // 默认结算周期按日配置,截止时间默认按当天 23:59:59 收口。 'settlement_cycle_type' => (int) ($data['settlement_cycle_type'] ?? 1), 'settlement_cutoff_time' => trim((string) ($data['settlement_cutoff_time'] ?? '23:59:59')) ?: '23:59:59', 'remark' => trim((string) ($data['remark'] ?? '')), @@ -212,6 +260,10 @@ class PaymentPluginConfService extends BaseService /** * 校验插件是否存在。 + * + * @param string $pluginCode 插件编码 + * @return void + * @throws PaymentException */ private function assertPluginExists(string $pluginCode): void { @@ -219,6 +271,7 @@ class PaymentPluginConfService extends BaseService throw new PaymentException('插件编码不能为空', 40230); } + // 插件配置必须挂到已存在的插件定义上,避免配置和实际实现脱节。 if (!$this->paymentPluginRepository->findByCode($pluginCode)) { throw new PaymentException('支付插件不存在', 40231, [ 'plugin_code' => $pluginCode, diff --git a/app/service/payment/config/PaymentPluginService.php b/app/service/payment/config/PaymentPluginService.php index 5e9acf1..e831a99 100644 --- a/app/service/payment/config/PaymentPluginService.php +++ b/app/service/payment/config/PaymentPluginService.php @@ -11,11 +11,18 @@ use app\repository\payment\config\PaymentPluginRepository; * 支付插件管理服务。 * * 负责插件目录同步、插件列表查询,以及 JSON 字段写入前的归一化。 + * + * @property PaymentPluginRepository $paymentPluginRepository 支付插件仓库 + * @property PaymentPluginSyncService $paymentPluginSyncService 支付插件同步服务 */ class PaymentPluginService extends BaseService { /** - * 构造函数,注入支付插件仓库。 + * 构造方法。 + * + * @param PaymentPluginRepository $paymentPluginRepository 支付插件仓库 + * @param PaymentPluginSyncService $paymentPluginSyncService 支付插件同步服务 + * @return void */ public function __construct( protected PaymentPluginRepository $paymentPluginRepository, @@ -25,6 +32,11 @@ class PaymentPluginService extends BaseService /** * 分页查询支付插件。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -60,6 +72,8 @@ class PaymentPluginService extends BaseService /** * 查询启用中的支付插件选项。 + * + * @return array 启用插件选项 */ public function enabledOptions(): array { @@ -77,7 +91,12 @@ class PaymentPluginService extends BaseService } /** - * 远程查询支付插件选择项。 + * 搜索支付插件选择项。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return array{list: array}>, total: int, page: int, size: int} 插件搜索结果 */ public function searchOptions(array $filters = [], int $page = 1, int $pageSize = 20): array { @@ -88,6 +107,7 @@ class PaymentPluginService extends BaseService $ids = $filters['ids'] ?? []; if (is_array($ids) && $ids !== []) { + // 显式传 ID 时优先按编码集合回显,避免关键词过滤把手工选择项漏掉。 $query->whereIn('code', array_values(array_filter(array_map('strval', $ids)))); } else { $keyword = trim((string) ($filters['keyword'] ?? '')); @@ -100,6 +120,7 @@ class PaymentPluginService extends BaseService $payTypeCode = trim((string) ($filters['pay_type_code'] ?? '')); if ($payTypeCode !== '') { + // 如果前端按支付方式筛选,就只保留 pay_types 中包含该编码的插件。 $query->whereJsonContains('pay_types', $payTypeCode); } } @@ -124,9 +145,12 @@ class PaymentPluginService extends BaseService /** * 查询通道配置场景使用的支付插件选项。 + * + * @return array}> 通道配置选项 */ public function channelOptions(): array { + // 通道配置场景只需要启用中的插件,并且要带上支付方式集合供前端联动展示。 return $this->paymentPluginRepository->enabledList([ 'code', 'name', @@ -147,6 +171,9 @@ class PaymentPluginService extends BaseService /** * 按插件编码查询插件。 + * + * @param string $code 插件编码 + * @return PaymentPlugin|null 插件模型 */ public function findByCode(string $code): ?PaymentPlugin { @@ -156,7 +183,9 @@ class PaymentPluginService extends BaseService /** * 查询插件配置结构。 * - * @return array + * @param string $code 插件编码 + * @return array{config_schema: array} 配置结构 + * @throws PaymentException */ public function getSchema(string $code): array { @@ -174,10 +203,15 @@ class PaymentPluginService extends BaseService /** * 更新支付插件。 + * + * @param string $code 插件编码 + * @param array $data 写入数据 + * @return PaymentPlugin|null 更新后的插件模型 */ public function update(string $code, array $data): ?PaymentPlugin { $payload = []; + // 插件元信息由文件同步维护,后台这里只允许调整状态和备注,避免人工改动覆盖同步结果。 if (array_key_exists('status', $data)) { $payload['status'] = (int) $data['status']; } @@ -199,6 +233,8 @@ class PaymentPluginService extends BaseService /** * 从插件目录刷新并同步支付插件定义。 + * + * @return array{count: int, plugins: array} 同步结果 */ public function refreshFromClasses(): array { diff --git a/app/service/payment/config/PaymentPluginSyncService.php b/app/service/payment/config/PaymentPluginSyncService.php index 0feda03..758c91e 100644 --- a/app/service/payment/config/PaymentPluginSyncService.php +++ b/app/service/payment/config/PaymentPluginSyncService.php @@ -12,26 +12,40 @@ use app\repository\payment\config\PaymentPluginRepository; /** * 支付插件同步服务。 * - * 负责扫描插件目录、实例化插件类并同步数据库定义。 + * 负责扫描插件目录、实例化插件类并同步数据库中的插件定义。 + * + * @property PaymentPluginRepository $paymentPluginRepository 支付插件仓库 */ class PaymentPluginSyncService extends BaseService { + /** + * 构造方法。 + * + * @param PaymentPluginRepository $paymentPluginRepository 支付插件仓库 + * @return void + */ public function __construct( protected PaymentPluginRepository $paymentPluginRepository ) {} /** * 从插件目录刷新并同步支付插件定义。 + * + * @return array{count: int, plugins: array} 同步结果 + * @throws PaymentException */ public function refreshFromClasses(): array { $directory = base_path() . DIRECTORY_SEPARATOR . 'app' . DIRECTORY_SEPARATOR . 'common' . DIRECTORY_SEPARATOR . 'payment'; + // 扫描固定目录下的插件类文件,每个文件都可能对应一个可同步的插件定义。 $files = glob($directory . DIRECTORY_SEPARATOR . '*.php') ?: []; + // 以插件 code 为键去重,避免同一个插件被多个类重复注册。 $rows = []; foreach ($files as $file) { $shortClassName = pathinfo($file, PATHINFO_FILENAME); $className = 'app\\common\\payment\\' . $shortClassName; + // 先实例化插件,再从实例上读取元信息作为同步源。 $plugin = $this->instantiatePlugin($className); if (!$plugin) { continue; @@ -62,6 +76,7 @@ class PaymentPluginSyncService extends BaseService ]; } + // 先固定排序,再和数据库现有记录逐条对比,保证同步过程稳定可复现。 ksort($rows); $existing = $this->paymentPluginRepository->query() @@ -79,6 +94,7 @@ class PaymentPluginSyncService extends BaseService ]); if ($current) { + // 已存在的插件只覆盖元信息,不改动人工维护的状态和备注。 $current->fill($payload); $current->save(); unset($existing[$code]); @@ -88,6 +104,7 @@ class PaymentPluginSyncService extends BaseService $this->paymentPluginRepository->create($payload); } + // 数据库里还残留、但文件中已不存在的插件,直接删除避免配置漂移。 foreach ($existing as $plugin) { $plugin->delete(); } @@ -105,6 +122,9 @@ class PaymentPluginSyncService extends BaseService /** * 实例化插件类并过滤非支付插件类。 + * + * @param string $className 插件类名 + * @return null|(PaymentInterface&PayPluginInterface) 支付插件实例 */ private function instantiatePlugin(string $className): null|(PaymentInterface & PayPluginInterface) { @@ -120,3 +140,5 @@ class PaymentPluginSyncService extends BaseService return $instance; } } + + diff --git a/app/service/payment/config/PaymentPollGroupBindService.php b/app/service/payment/config/PaymentPollGroupBindService.php index 3de8062..4d6ee7f 100644 --- a/app/service/payment/config/PaymentPollGroupBindService.php +++ b/app/service/payment/config/PaymentPollGroupBindService.php @@ -11,9 +11,23 @@ use app\repository\payment\config\PaymentPollGroupRepository; /** * 商户分组路由绑定服务。 + * + * 负责把商户分组和支付方式绑定到指定轮询组,并校验轮询组与支付方式的匹配关系。 + * + * @property PaymentPollGroupBindRepository $paymentPollGroupBindRepository 支付轮询分组绑定仓库 + * @property MerchantGroupRepository $merchantGroupRepository 商户分组仓库 + * @property PaymentPollGroupRepository $paymentPollGroupRepository 支付轮询分组仓库 */ class PaymentPollGroupBindService extends BaseService { + /** + * 构造方法。 + * + * @param PaymentPollGroupBindRepository $paymentPollGroupBindRepository 支付轮询分组绑定仓库 + * @param MerchantGroupRepository $merchantGroupRepository 商户分组仓库 + * @param PaymentPollGroupRepository $paymentPollGroupRepository 支付轮询分组仓库 + * @return void + */ public function __construct( protected PaymentPollGroupBindRepository $paymentPollGroupBindRepository, protected MerchantGroupRepository $merchantGroupRepository, @@ -23,6 +37,11 @@ class PaymentPollGroupBindService extends BaseService /** * 分页查询商户分组路由绑定。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -76,11 +95,24 @@ class PaymentPollGroupBindService extends BaseService ->paginate(max(1, $pageSize), ['*'], 'page', max(1, $page)); } + /** + * 按 ID 查询路由绑定。 + * + * @param int $id 绑定ID + * @return PaymentPollGroupBind|null 绑定模型 + */ public function findById(int $id): ?PaymentPollGroupBind { return $this->paymentPollGroupBindRepository->find($id); } + /** + * 创建路由绑定。 + * + * @param array $data 写入数据 + * @return PaymentPollGroupBind 新增后的绑定模型 + * @throws PaymentException + */ public function create(array $data): PaymentPollGroupBind { $this->assertBindingUnique((int) $data['merchant_group_id'], (int) $data['pay_type_id']); @@ -89,6 +121,14 @@ class PaymentPollGroupBindService extends BaseService return $this->paymentPollGroupBindRepository->create($this->normalizePayload($data)); } + /** + * 更新路由绑定。 + * + * @param int $id 绑定ID + * @param array $data 写入数据 + * @return PaymentPollGroupBind|null 更新后的绑定模型 + * @throws PaymentException + */ public function update(int $id, array $data): ?PaymentPollGroupBind { $current = $this->paymentPollGroupBindRepository->find($id); @@ -96,6 +136,7 @@ class PaymentPollGroupBindService extends BaseService return null; } + // 更新时要以现有记录为底,把未传的分组和支付方式补齐后再做唯一性校验。 $merchantGroupId = (int) ($data['merchant_group_id'] ?? $current->merchant_group_id); $payTypeId = (int) ($data['pay_type_id'] ?? $current->pay_type_id); $this->assertBindingUnique($merchantGroupId, $payTypeId, $id); @@ -108,11 +149,23 @@ class PaymentPollGroupBindService extends BaseService return $this->paymentPollGroupBindRepository->find($id); } + /** + * 删除路由绑定。 + * + * @param int $id 绑定ID + * @return bool 是否删除成功 + */ public function delete(int $id): bool { return $this->paymentPollGroupBindRepository->deleteById($id); } + /** + * 标准化路由绑定写入数据。 + * + * @param array $data 写入数据 + * @return array 标准化后的数据 + */ private function normalizePayload(array $data): array { return [ @@ -124,6 +177,15 @@ class PaymentPollGroupBindService extends BaseService ]; } + /** + * 校验商户分组与支付方式的绑定唯一性。 + * + * @param int $merchantGroupId 商户分组ID + * @param int $payTypeId 支付方式ID + * @param int $ignoreId 排除的绑定ID + * @return void + * @throws PaymentException + */ private function assertBindingUnique(int $merchantGroupId, int $payTypeId, int $ignoreId = 0): void { $query = $this->paymentPollGroupBindRepository->query() @@ -142,11 +204,19 @@ class PaymentPollGroupBindService extends BaseService } } + /** + * 校验轮询组与支付方式是否一致。 + * + * @param array $data 写入数据 + * @return void + * @throws PaymentException + */ private function assertPollGroupMatchesPayType(array $data): void { $pollGroupId = (int) ($data['poll_group_id'] ?? 0); $payTypeId = (int) ($data['pay_type_id'] ?? 0); + // 轮询组和支付方式必须保持一致;轮询组缺失时交给上层必填校验处理。 $pollGroup = $this->paymentPollGroupRepository->find($pollGroupId); if (!$pollGroup) { return; @@ -160,3 +230,5 @@ class PaymentPollGroupBindService extends BaseService } } } + + diff --git a/app/service/payment/config/PaymentPollGroupChannelService.php b/app/service/payment/config/PaymentPollGroupChannelService.php index 556301f..c00a3df 100644 --- a/app/service/payment/config/PaymentPollGroupChannelService.php +++ b/app/service/payment/config/PaymentPollGroupChannelService.php @@ -11,9 +11,23 @@ use app\repository\payment\config\PaymentPollGroupRepository; /** * 轮询组通道编排服务。 + * + * 负责维护轮询组内通道的顺序、权重、默认通道以及支付方式一致性。 + * + * @property PaymentPollGroupChannelRepository $paymentPollGroupChannelRepository 支付轮询分组渠道仓库 + * @property PaymentPollGroupRepository $paymentPollGroupRepository 支付轮询分组仓库 + * @property PaymentChannelRepository $paymentChannelRepository 支付渠道仓库 */ class PaymentPollGroupChannelService extends BaseService { + /** + * 构造方法。 + * + * @param PaymentPollGroupChannelRepository $paymentPollGroupChannelRepository 支付轮询分组渠道仓库 + * @param PaymentPollGroupRepository $paymentPollGroupRepository 支付轮询分组仓库 + * @param PaymentChannelRepository $paymentChannelRepository 支付渠道仓库 + * @return void + */ public function __construct( protected PaymentPollGroupChannelRepository $paymentPollGroupChannelRepository, protected PaymentPollGroupRepository $paymentPollGroupRepository, @@ -23,6 +37,11 @@ class PaymentPollGroupChannelService extends BaseService /** * 分页查询轮询组通道编排。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -79,11 +98,24 @@ class PaymentPollGroupChannelService extends BaseService ->paginate(max(1, $pageSize), ['*'], 'page', max(1, $page)); } + /** + * 按 ID 查询轮询组通道编排。 + * + * @param int $id 编排ID + * @return PaymentPollGroupChannel|null 编排模型 + */ public function findById(int $id): ?PaymentPollGroupChannel { return $this->paymentPollGroupChannelRepository->find($id); } + /** + * 创建轮询组通道编排。 + * + * @param array $data 写入数据 + * @return PaymentPollGroupChannel 新增后的编排模型 + * @throws PaymentException + */ public function create(array $data): PaymentPollGroupChannel { $this->assertPairUnique((int) $data['poll_group_id'], (int) $data['channel_id']); @@ -91,6 +123,7 @@ class PaymentPollGroupChannelService extends BaseService $payload = $this->normalizePayload($data); return $this->transaction(function () use ($payload) { + // 一个轮询组只能有一个默认通道,新增默认项前先清理掉其他默认标记。 if ((int) ($payload['is_default'] ?? 0) === 1) { $this->paymentPollGroupChannelRepository->clearDefaultExcept((int) $payload['poll_group_id']); } @@ -99,6 +132,14 @@ class PaymentPollGroupChannelService extends BaseService }); } + /** + * 更新轮询组通道编排。 + * + * @param int $id 编排ID + * @param array $data 写入数据 + * @return PaymentPollGroupChannel|null 更新后的编排模型 + * @throws PaymentException + */ public function update(int $id, array $data): ?PaymentPollGroupChannel { $current = $this->paymentPollGroupChannelRepository->find($id); @@ -114,6 +155,7 @@ class PaymentPollGroupChannelService extends BaseService $payload = $this->normalizePayload($data); return $this->transaction(function () use ($id, $payload) { + // 更新成默认通道时,同样先把本轮询组的其他默认项清空。 if ((int) ($payload['is_default'] ?? 0) === 1) { $this->paymentPollGroupChannelRepository->clearDefaultExcept((int) $payload['poll_group_id'], $id); } @@ -126,17 +168,30 @@ class PaymentPollGroupChannelService extends BaseService }); } + /** + * 删除轮询组通道编排。 + * + * @param int $id 编排ID + * @return bool 是否删除成功 + */ public function delete(int $id): bool { return $this->paymentPollGroupChannelRepository->deleteById($id); } + /** + * 标准化编排写入数据。 + * + * @param array $data 写入数据 + * @return array 标准化后的数据 + */ private function normalizePayload(array $data): array { return [ 'poll_group_id' => (int) $data['poll_group_id'], 'channel_id' => (int) $data['channel_id'], 'sort_no' => (int) ($data['sort_no'] ?? 0), + // 权重至少为 1,避免轮询时出现 0 权重通道导致随机分配失真。 'weight' => max(1, (int) ($data['weight'] ?? 100)), 'is_default' => (int) ($data['is_default'] ?? 0), 'status' => (int) ($data['status'] ?? 1), @@ -144,6 +199,15 @@ class PaymentPollGroupChannelService extends BaseService ]; } + /** + * 校验轮询组与通道的组合唯一性。 + * + * @param int $pollGroupId 轮询组ID + * @param int $channelId 通道ID + * @param int $ignoreId 排除的编排ID + * @return void + * @throws PaymentException + */ private function assertPairUnique(int $pollGroupId, int $channelId, int $ignoreId = 0): void { $query = $this->paymentPollGroupChannelRepository->query() @@ -162,6 +226,13 @@ class PaymentPollGroupChannelService extends BaseService } } + /** + * 校验通道支付方式与轮询组支付方式一致。 + * + * @param array $data 写入数据 + * @return void + * @throws PaymentException + */ private function assertChannelMatchesPollGroup(array $data): void { $pollGroupId = (int) ($data['poll_group_id'] ?? 0); @@ -174,6 +245,7 @@ class PaymentPollGroupChannelService extends BaseService return; } + // 轮询组和通道必须属于同一支付方式,否则排序再正确也会在运行时被路由规则拦下。 if ((int) $pollGroup->pay_type_id !== (int) $channel->pay_type_id) { throw new PaymentException('轮询组与支付通道的支付方式不一致', 40231, [ 'poll_group_id' => $pollGroupId, @@ -182,3 +254,6 @@ class PaymentPollGroupChannelService extends BaseService } } } + + + diff --git a/app/service/payment/config/PaymentPollGroupCommandService.php b/app/service/payment/config/PaymentPollGroupCommandService.php index 28c27ba..03e8b5c 100644 --- a/app/service/payment/config/PaymentPollGroupCommandService.php +++ b/app/service/payment/config/PaymentPollGroupCommandService.php @@ -9,22 +9,47 @@ use app\repository\payment\config\PaymentPollGroupRepository; /** * 支付轮询组命令服务。 + * + * @property PaymentPollGroupRepository $paymentPollGroupRepository 支付轮询分组仓库 */ class PaymentPollGroupCommandService extends BaseService { + /** + * 构造方法。 + * + * @param PaymentPollGroupRepository $paymentPollGroupRepository 支付轮询分组仓库 + * @return void + */ public function __construct( protected PaymentPollGroupRepository $paymentPollGroupRepository ) { } + /** + * 创建支付轮询组。 + * + * @param array $data 写入数据 + * @return PaymentPollGroup 新增后的轮询组模型 + * @throws PaymentException + */ public function create(array $data): PaymentPollGroup { + // 新增前先确保轮询组名称不冲突,避免后台同时出现两个同名配置。 $this->assertGroupNameUnique((string) ($data['group_name'] ?? '')); return $this->paymentPollGroupRepository->create($data); } + /** + * 更新支付轮询组。 + * + * @param int $id 轮询组ID + * @param array $data 写入数据 + * @return PaymentPollGroup|null 更新后的轮询组模型 + * @throws PaymentException + */ public function update(int $id, array $data): ?PaymentPollGroup { + // 更新时同样要排除自身后再做唯一性判断,防止修改回原名时误报冲突。 $this->assertGroupNameUnique((string) ($data['group_name'] ?? ''), $id); if (!$this->paymentPollGroupRepository->updateById($id, $data)) { return null; @@ -33,11 +58,25 @@ class PaymentPollGroupCommandService extends BaseService return $this->paymentPollGroupRepository->find($id); } + /** + * 删除支付轮询组。 + * + * @param int $id 轮询组ID + * @return bool 是否删除成功 + */ public function delete(int $id): bool { return $this->paymentPollGroupRepository->deleteById($id); } + /** + * 校验轮询组名称唯一。 + * + * @param string $groupName 轮询组名称 + * @param int $ignoreId 排除的轮询组ID + * @return void + * @throws PaymentException + */ private function assertGroupNameUnique(string $groupName, int $ignoreId = 0): void { $groupName = trim($groupName); @@ -53,3 +92,6 @@ class PaymentPollGroupCommandService extends BaseService } } } + + + diff --git a/app/service/payment/config/PaymentPollGroupQueryService.php b/app/service/payment/config/PaymentPollGroupQueryService.php index b21f5b5..245f76c 100644 --- a/app/service/payment/config/PaymentPollGroupQueryService.php +++ b/app/service/payment/config/PaymentPollGroupQueryService.php @@ -7,21 +7,40 @@ use app\model\payment\PaymentPollGroup; use app\repository\payment\config\PaymentPollGroupRepository; /** - * 支付轮询组查询服务。 + * 支付轮询组查询与选项拼装服务。 + * + * 负责轮询组列表、详情和启用选项输出。 + * + * @property PaymentPollGroupRepository $paymentPollGroupRepository 支付轮询分组仓库 */ class PaymentPollGroupQueryService extends BaseService { + /** + * 构造方法。 + * + * @param PaymentPollGroupRepository $paymentPollGroupRepository 支付轮询分组仓库 + * @return void + */ public function __construct( protected PaymentPollGroupRepository $paymentPollGroupRepository ) { } + /** + * 分页查询支付轮询组。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 + */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { $query = $this->paymentPollGroupRepository->query(); $keyword = trim((string) ($filters['keyword'] ?? '')); if ($keyword !== '') { + // 轮询组列表只按组名搜索,避免把支付方式或路由模式混进模糊搜索结果里。 $query->where('group_name', 'like', '%' . $keyword . '%'); } @@ -47,12 +66,19 @@ class PaymentPollGroupQueryService extends BaseService ->paginate(max(1, $pageSize), ['*'], 'page', max(1, $page)); } + /** + * 获取启用支付轮询组选项。 + * + * @param array $filters 筛选条件 + * @return array 启用轮询组选项 + */ public function enabledOptions(array $filters = []): array { $query = $this->paymentPollGroupRepository->query() ->where('status', 1); if (($payTypeId = (int) ($filters['pay_type_id'] ?? 0)) > 0) { + // 轮询组选项通常要跟支付方式联动,因此启用项会先按支付方式收窄。 $query->where('pay_type_id', $payTypeId); } @@ -72,8 +98,17 @@ class PaymentPollGroupQueryService extends BaseService ->all(); } + /** + * 按 ID 查询轮询组。 + * + * @param int $id 轮询组ID + * @return PaymentPollGroup|null 轮询组模型 + */ public function findById(int $id): ?PaymentPollGroup { return $this->paymentPollGroupRepository->find($id); } } + + + diff --git a/app/service/payment/config/PaymentPollGroupService.php b/app/service/payment/config/PaymentPollGroupService.php index e5849ed..07c7888 100644 --- a/app/service/payment/config/PaymentPollGroupService.php +++ b/app/service/payment/config/PaymentPollGroupService.php @@ -6,43 +6,94 @@ use app\common\base\BaseService; use app\model\payment\PaymentPollGroup; /** - * 支付轮询组门面服务。 + * 支付轮询组服务。 + * + * @property PaymentPollGroupQueryService $queryService 查询服务 + * @property PaymentPollGroupCommandService $commandService 命令服务 */ class PaymentPollGroupService extends BaseService { + /** + * 构造方法。 + * + * @param PaymentPollGroupQueryService $queryService 查询服务 + * @param PaymentPollGroupCommandService $commandService 命令服务 + * @return void + */ public function __construct( protected PaymentPollGroupQueryService $queryService, protected PaymentPollGroupCommandService $commandService ) { } + /** + * 分页查询支付轮询组。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 + */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { return $this->queryService->paginate($filters, $page, $pageSize); } + /** + * 获取启用支付轮询组选项。 + * + * @param array $filters 筛选条件 + * @return array> 启用轮询组选项 + */ public function enabledOptions(array $filters = []): array { return $this->queryService->enabledOptions($filters); } + /** + * 按 ID 查询支付轮询组。 + * + * @param int $id 轮询组ID + * @return PaymentPollGroup|null 轮询组模型 + */ public function findById(int $id): ?PaymentPollGroup { return $this->queryService->findById($id); } + /** + * 新增支付轮询组。 + * + * @param array $data 写入数据 + * @return PaymentPollGroup 新增后的轮询组模型 + */ public function create(array $data): PaymentPollGroup { return $this->commandService->create($data); } + /** + * 更新支付轮询组。 + * + * @param int $id 轮询组ID + * @param array $data 写入数据 + * @return PaymentPollGroup|null 更新后的轮询组模型 + */ public function update(int $id, array $data): ?PaymentPollGroup { return $this->commandService->update($id, $data); } + /** + * 删除支付轮询组。 + * + * @param int $id 轮询组ID + * @return bool 是否删除成功 + */ public function delete(int $id): bool { return $this->commandService->delete($id); } } + + diff --git a/app/service/payment/config/PaymentTypeService.php b/app/service/payment/config/PaymentTypeService.php index 3f31c52..8faf0da 100644 --- a/app/service/payment/config/PaymentTypeService.php +++ b/app/service/payment/config/PaymentTypeService.php @@ -11,11 +11,16 @@ use app\repository\payment\config\PaymentTypeRepository; * 支付方式字典服务。 * * 负责支付方式的基础列表查询、新增、修改、删除和下拉选项输出。 + * + * @property PaymentTypeRepository $paymentTypeRepository 支付类型仓库 */ class PaymentTypeService extends BaseService { /** - * 构造函数,注入支付方式仓库。 + * 构造方法。 + * + * @param PaymentTypeRepository $paymentTypeRepository 支付类型仓库 + * @return void */ public function __construct( protected PaymentTypeRepository $paymentTypeRepository @@ -24,6 +29,11 @@ class PaymentTypeService extends BaseService /** * 分页查询支付方式。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -59,6 +69,8 @@ class PaymentTypeService extends BaseService /** * 查询启用中的支付方式选项。 + * + * @return array 启用支付方式选项 */ public function enabledOptions(): array { @@ -76,6 +88,10 @@ class PaymentTypeService extends BaseService /** * 解析启用中的支付方式,优先按编码匹配,未命中则取首个启用项。 + * + * @param string $code 支付方式编码 + * @return PaymentType 支付方式模型 + * @throws ValidationException */ public function resolveEnabledType(string $code = ''): PaymentType { @@ -87,6 +103,7 @@ class PaymentTypeService extends BaseService } } + // 没有传编码或编码不可用时,直接回退到系统当前首个启用支付方式。 $paymentType = $this->paymentTypeRepository->enabledList()->first(); if (!$paymentType) { throw new ValidationException('未配置可用支付方式'); @@ -97,6 +114,9 @@ class PaymentTypeService extends BaseService /** * 根据支付方式编码查询字典。 + * + * @param string $code 支付方式编码 + * @return PaymentType|null 支付方式模型 */ public function findByCode(string $code): ?PaymentType { @@ -105,6 +125,9 @@ class PaymentTypeService extends BaseService /** * 根据支付方式 ID 解析支付方式编码。 + * + * @param int $id 支付方式ID + * @return string 支付方式编码 */ public function resolveCodeById(int $id): string { @@ -114,6 +137,9 @@ class PaymentTypeService extends BaseService /** * 按 ID 查询支付方式。 + * + * @param int $id 支付方式ID + * @return PaymentType|null 支付方式模型 */ public function findById(int $id): ?PaymentType { @@ -122,6 +148,9 @@ class PaymentTypeService extends BaseService /** * 新增支付方式。 + * + * @param array $data 写入数据 + * @return PaymentType 新增后的支付方式模型 */ public function create(array $data): PaymentType { @@ -130,6 +159,10 @@ class PaymentTypeService extends BaseService /** * 更新支付方式。 + * + * @param int $id 支付方式ID + * @param array $data 写入数据 + * @return PaymentType|null 更新后的支付方式模型 */ public function update(int $id, array $data): ?PaymentType { @@ -142,9 +175,14 @@ class PaymentTypeService extends BaseService /** * 删除支付方式。 + * + * @param int $id 支付方式ID + * @return bool 是否删除成功 */ public function delete(int $id): bool { return $this->paymentTypeRepository->deleteById($id); } } + + diff --git a/app/service/payment/order/PayOrderAttemptService.php b/app/service/payment/order/PayOrderAttemptService.php index 6ef8228..6d3e256 100644 --- a/app/service/payment/order/PayOrderAttemptService.php +++ b/app/service/payment/order/PayOrderAttemptService.php @@ -23,11 +23,27 @@ use app\service\payment\runtime\PaymentRouteService; * 支付单发起服务。 * * 负责支付单预创建、通道路由选择、第三方装单和首轮状态落库。 + * + * @property MerchantService $merchantService 商户服务 + * @property PaymentRouteService $paymentRouteService 支付路由服务 + * @property MerchantAccountService $merchantAccountService 商户账户服务 + * @property BizOrderRepository $bizOrderRepository 业务订单仓库 + * @property PayOrderRepository $payOrderRepository 支付单仓库 + * @property PaymentTypeRepository $paymentTypeRepository 支付类型仓库 + * @property PayOrderChannelDispatchService $payOrderChannelDispatchService 支付单渠道派发服务 */ class PayOrderAttemptService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param MerchantService $merchantService 商户服务 + * @param PaymentRouteService $paymentRouteService 支付路由服务 + * @param MerchantAccountService $merchantAccountService 商户账户服务 + * @param BizOrderRepository $bizOrderRepository 业务订单仓库 + * @param PayOrderRepository $payOrderRepository 支付订单仓库 + * @param PaymentTypeRepository $paymentTypeRepository 支付类型仓库 + * @param PayOrderChannelDispatchService $payOrderChannelDispatchService 支付单渠道派发服务 */ public function __construct( protected MerchantService $merchantService, @@ -45,8 +61,11 @@ class PayOrderAttemptService extends BaseService * * 该方法会完成商户、支付方式、路由、通道限额、串行尝试和自有通道手续费预占的完整预检查。 * - * @param array $input 支付请求参数 - * @return array{merchant:mixed,biz_order:mixed,pay_order:mixed,route:array,payment_result:array,pay_params:array} + * @param array $input 支付预创建参数 + * @return array 发起结果 + * @throws ValidationException + * @throws BusinessStateException + * @throws ConflictException */ public function preparePayAttempt(array $input): array { @@ -59,6 +78,7 @@ class PayOrderAttemptService extends BaseService throw new ValidationException('支付入参不完整'); } + // 先校验商户和支付方式是否可用,避免进入事务后才发现前置条件不满足。 $merchant = $this->merchantService->ensureMerchantEnabled($merchantId); $merchantGroupId = (int) $merchant->group_id; if ($merchantGroupId <= 0) { @@ -72,6 +92,7 @@ class PayOrderAttemptService extends BaseService throw new BusinessStateException('支付方式不支持', ['pay_type_id' => $payTypeId]); } + // 根据商户分组、支付金额和请求参数选择可用通道。 $route = $this->paymentRouteService->resolveByMerchantGroup($merchantGroupId, $payTypeId, $payAmount, $input); $selected = $route['selected_channel']; /** @var PaymentChannel $channel */ @@ -93,10 +114,12 @@ class PayOrderAttemptService extends BaseService $payNo, $channelRequestNo ) { + // 在事务中完成业务单和支付单的原子创建,保证幂等与状态一致。 $existingBizOrder = $this->bizOrderRepository->findForUpdateByMerchantAndOrderNo($merchantId, $merchantOrderNo); $bizTraceNo = ''; if ($existingBizOrder) { + // 同一商户订单号只能复用原业务单,且金额必须完全一致。 if ((int) $existingBizOrder->order_amount !== $payAmount) { throw new ValidationException('同一商户订单号金额不一致', [ 'merchant_id' => $merchantId, @@ -128,6 +151,7 @@ class PayOrderAttemptService extends BaseService $bizOrder = $existingBizOrder; $bizTraceNo = trim((string) ($bizOrder->trace_no ?? '')); if ($bizTraceNo === '') { + // 旧单如果没有 trace_no,就补成业务单号,方便后续串起来查。 $bizTraceNo = (string) $bizOrder->biz_no; $bizOrder->trace_no = $bizTraceNo; } @@ -155,9 +179,11 @@ class PayOrderAttemptService extends BaseService $feeRateBp = (int) $channel->cost_rate_bp; $splitRateBp = (int) $channel->split_rate_bp ?: 10000; + // 手续费和分账费率都按快照落库,后续配置变化不会影响这笔单的口径。 $feeEstimated = $this->calculateAmountByBp($payAmount, $feeRateBp); if ((int) $channel->channel_mode === RouteConstant::CHANNEL_MODE_SELF && $feeEstimated > 0) { + // 自有通道先冻结预估手续费,避免后续余额不足。 $this->merchantAccountService->freezeAmountInCurrentTransaction( $merchantId, $feeEstimated, @@ -214,6 +240,7 @@ class PayOrderAttemptService extends BaseService $bizOrder->merchant_group_id = $merchantGroupId; $bizOrder->poll_group_id = (int) $route['poll_group']->id; if ($bizTraceNo !== '' && (string) ($bizOrder->trace_no ?? '') === '') { + // 把追踪号回写到业务单上,后续查单和对账能串到同一条链路。 $bizOrder->trace_no = $bizTraceNo; } $bizOrder->save(); @@ -233,6 +260,7 @@ class PayOrderAttemptService extends BaseService /** @var \app\model\payment\PaymentChannel $channel */ $channel = $prepared['route']['selected_channel']['channel']; + // 支付单落库后立即拉起渠道订单,补全渠道返回的单号和参数快照。 $channelDispatchResult = $this->payOrderChannelDispatchService->dispatch($payOrder, $bizOrder, $channel); $prepared['pay_order'] = $channelDispatchResult['pay_order']; @@ -244,6 +272,10 @@ class PayOrderAttemptService extends BaseService /** * 计算手续费金额。 + * + * @param int $amount 金额(分) + * @param int $bp 费率基点,`10000` 表示 100% + * @return int 手续费金额(分) */ private function calculateAmountByBp(int $amount, int $bp): int { @@ -251,6 +283,7 @@ class PayOrderAttemptService extends BaseService return 0; } + // 基点换算统一向下取整,避免手续费计算时出现超扣。 return (int) floor($amount * $bp / 10000); } } diff --git a/app/service/payment/order/PayOrderCallbackService.php b/app/service/payment/order/PayOrderCallbackService.php index 826e1fb..baf98df 100644 --- a/app/service/payment/order/PayOrderCallbackService.php +++ b/app/service/payment/order/PayOrderCallbackService.php @@ -17,11 +17,21 @@ use support\Response; * 支付单回调服务。 * * 负责渠道回调日志记录、插件回调解析和支付状态分发。 + * + * @property NotifyService $notifyService 通知服务 + * @property PaymentPluginManager $paymentPluginManager 支付插件管理器 + * @property PayOrderRepository $payOrderRepository 支付单仓库 + * @property PayOrderLifecycleService $payOrderLifecycleService 支付单生命周期服务 */ class PayOrderCallbackService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param NotifyService $notifyService 通知服务 + * @param PaymentPluginManager $paymentPluginManager 支付插件管理器 + * @param PayOrderRepository $payOrderRepository 支付单仓库 + * @param PayOrderLifecycleService $payOrderLifecycleService 支付单生命周期服务 */ public function __construct( protected NotifyService $notifyService, @@ -32,7 +42,11 @@ class PayOrderCallbackService extends BaseService } /** - * 处理渠道回调。 + * 处理渠道回调载荷并推进支付状态。 + * + * @param array $input 回调载荷 + * @return PayOrder 支付订单模型 + * @throws \InvalidArgumentException */ public function handleChannelCallback(array $input): PayOrder { @@ -41,6 +55,7 @@ class PayOrderCallbackService extends BaseService throw new \InvalidArgumentException('pay_no 不能为空'); } + // 先落回调日志,后续无论成功还是失败,都可以从统一表里排查。 $this->notifyService->recordPayCallback([ 'pay_no' => $payNo, 'channel_id' => (int) ($input['channel_id'] ?? 0), @@ -52,6 +67,7 @@ class PayOrderCallbackService extends BaseService ]); $success = (bool) ($input['success'] ?? false); + // 回调链路只根据插件/渠道给出的结果收口支付单状态。 if ($success) { return $this->payOrderLifecycleService->markPaySuccess($payNo, $input); } @@ -61,9 +77,17 @@ class PayOrderCallbackService extends BaseService /** * 按支付单号处理真实第三方回调。 + * + * 该方法先定位支付单,再由插件解析原始请求,最后统一交给生命周期服务推进状态。 + * + * @param string $payNo 支付单号 + * @param Request $request 请求对象 + * @return string|Response 插件要求返回的响应内容 + * @throws ResourceNotFoundException */ public function handlePluginCallback(string $payNo, Request $request): string|Response { + // 回调必须能定位到具体支付单,找不到就直接终止。 $payOrder = $this->payOrderRepository->findByPayNo($payNo); if (!$payOrder) { throw new ResourceNotFoundException('支付单不存在', ['pay_no' => $payNo]); @@ -72,12 +96,16 @@ class PayOrderCallbackService extends BaseService $plugin = $this->paymentPluginManager->createByPayOrder($payOrder, true); try { + // 由插件自行解析请求并返回统一结构,控制器层不直接判断渠道格式。 $result = $plugin->notify($request); $status = (string) ($result['status'] ?? ''); + // 老插件可能只返回 success / paid / failed 这类状态字符串,这里统一折算成布尔结果。 $success = array_key_exists('success', $result) ? (bool) $result['success'] : in_array($status, ['success', 'paid'], true); + // 将插件返回值归一化为生命周期服务可消费的回调载荷。 + /** @var array $callbackPayload */ $callbackPayload = [ 'pay_no' => $payNo, 'success' => $success, @@ -97,14 +125,17 @@ class PayOrderCallbackService extends BaseService 'notify_status' => $status, ], ]; + // 部分渠道会返回实际手续费,补充进回调载荷,便于后续清算和对账。 if (isset($result['fee_actual_amount'])) { $callbackPayload['fee_actual_amount'] = (int) $result['fee_actual_amount']; } + // 回调成功后统一交给生命周期服务落库,避免状态推进分散在不同分支里。 $this->handleChannelCallback($callbackPayload); return $success ? $plugin->notifySuccess() : $plugin->notifyFail(); } catch (PaymentException $e) { + // 插件已明确返回业务失败时,记录失败日志并按失败响应收口。 $this->notifyService->recordPayCallback([ 'pay_no' => $payNo, 'channel_id' => (int) $payOrder->channel_id, @@ -120,6 +151,7 @@ class PayOrderCallbackService extends BaseService return $plugin->notifyFail(); } catch (\Throwable $e) { + // 非业务异常同样记为失败,避免渠道重复推送造成状态抖动。 $this->notifyService->recordPayCallback([ 'pay_no' => $payNo, 'channel_id' => (int) $payOrder->channel_id, diff --git a/app/service/payment/order/PayOrderChannelDispatchService.php b/app/service/payment/order/PayOrderChannelDispatchService.php index 2ec81c1..d5571d8 100644 --- a/app/service/payment/order/PayOrderChannelDispatchService.php +++ b/app/service/payment/order/PayOrderChannelDispatchService.php @@ -18,11 +18,21 @@ use Throwable; * 支付渠道单据拉起服务。 * * 负责调用第三方插件、写回渠道订单号,并在失败时推进支付失败状态。 + * + * @property PaymentPluginManager $paymentPluginManager 支付插件管理器 + * @property PaymentTypeRepository $paymentTypeRepository 支付类型仓库 + * @property PayOrderRepository $payOrderRepository 支付单仓库 + * @property PayOrderLifecycleService $payOrderLifecycleService 支付单生命周期服务 */ class PayOrderChannelDispatchService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param PaymentPluginManager $paymentPluginManager 支付插件管理器 + * @param PaymentTypeRepository $paymentTypeRepository 支付类型仓库 + * @param PayOrderRepository $payOrderRepository 支付单仓库 + * @param PayOrderLifecycleService $payOrderLifecycleService 支付单生命周期服务 */ public function __construct( protected PaymentPluginManager $paymentPluginManager, @@ -35,20 +45,28 @@ class PayOrderChannelDispatchService extends BaseService /** * 拉起第三方支付单并回写渠道响应。 * - * @return array{pay_order:PayOrder,payment_result:array,pay_params:array} + * @param PayOrder $payOrder 支付订单 + * @param BizOrder $bizOrder 业务订单 + * @param PaymentChannel $channel 渠道 + * @return array 拉起结果 + * @throws ResourceNotFoundException + * @throws PaymentException */ public function dispatch(PayOrder $payOrder, BizOrder $bizOrder, PaymentChannel $channel): array { try { + // 先构造支付插件实例,由插件完成具体渠道下单。 $plugin = $this->paymentPluginManager->createByChannel($channel, (int) $payOrder->pay_type_id); /** @var PaymentType|null $paymentType */ $paymentType = $this->paymentTypeRepository->find((int) $payOrder->pay_type_id); $extJson = (array) ($payOrder->ext_json ?? []); + // 下单回调基址由支付单提前写入,这里拼出具体支付单回调地址交给插件使用。 $callbackBaseUrl = trim((string) ($extJson['channel_callback_base_url'] ?? '')); $callbackUrl = $callbackBaseUrl === '' ? '' : rtrim($callbackBaseUrl, '/') . '/' . $payOrder->pay_no . '/callback'; + // 插件下单参数里同时带业务单号、支付单号和扩展信息,方便渠道侧回调后能反查同一笔单。 $channelResult = $plugin->pay([ 'pay_no' => (string) $payOrder->pay_no, 'order_id' => (string) $payOrder->pay_no, @@ -69,6 +87,7 @@ class PayOrderChannelDispatchService extends BaseService ]); $payOrder = $this->transactionRetry(function () use ($payOrder, $channelResult) { + // 回写渠道订单号和支付参数快照,便于后续查询和回调排障。 $latest = $this->payOrderRepository->findForUpdateByPayNo((string) $payOrder->pay_no); if (!$latest) { throw new ResourceNotFoundException('支付单不存在', ['pay_no' => (string) $payOrder->pay_no]); @@ -87,6 +106,7 @@ class PayOrderChannelDispatchService extends BaseService return $latest->refresh(); }); } catch (PaymentException $e) { + // 插件层异常统一收口为支付失败,避免订单长时间停留在处理中。 $this->payOrderLifecycleService->markPayFailed((string) $payOrder->pay_no, [ 'channel_error_msg' => $e->getMessage(), 'channel_error_code' => (string) $e->getCode(), @@ -97,6 +117,7 @@ class PayOrderChannelDispatchService extends BaseService throw $e; } catch (Throwable $e) { + // 非业务异常同样收口为失败态,并保留原始错误信息。 $this->payOrderLifecycleService->markPayFailed((string) $payOrder->pay_no, [ 'channel_error_msg' => $e->getMessage(), 'channel_error_code' => 'PLUGIN_CREATE_ORDER_ERROR', @@ -117,6 +138,9 @@ class PayOrderChannelDispatchService extends BaseService /** * 归一化支付参数快照,便于后续页面渲染和排障。 + * + * @param array|object|null $payParams 支付参数数组或对象 + * @return array 参数快照 */ private function normalizePayParamsSnapshot(mixed $payParams): array { @@ -125,6 +149,7 @@ class PayOrderChannelDispatchService extends BaseService } if (is_object($payParams) && method_exists($payParams, 'toArray')) { + // 有些插件会返回对象,这里统一转成数组,方便后续落库和页面回显。 $data = $payParams->toArray(); return is_array($data) ? $data : []; } @@ -132,3 +157,8 @@ class PayOrderChannelDispatchService extends BaseService return []; } } + + + + + diff --git a/app/service/payment/order/PayOrderFeeService.php b/app/service/payment/order/PayOrderFeeService.php index 5675306..edcc223 100644 --- a/app/service/payment/order/PayOrderFeeService.php +++ b/app/service/payment/order/PayOrderFeeService.php @@ -12,11 +12,16 @@ use app\service\account\funds\MerchantAccountService; * 支付单手续费处理服务。 * * 负责支付成功时的手续费结算,以及终态时的冻结手续费释放。 + * + * @property MerchantAccountService $merchantAccountService 商户账户服务 */ class PayOrderFeeService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param MerchantAccountService $merchantAccountService 商户账户服务 + * @return void */ public function __construct( protected MerchantAccountService $merchantAccountService @@ -25,6 +30,12 @@ class PayOrderFeeService extends BaseService /** * 处理支付成功后的手续费结算。 + * + * @param PayOrder $payOrder 支付订单 + * @param int $actualFee actual手续费 + * @param string $payNo 支付单号 + * @param string $traceNo 追踪号 + * @return void */ public function settleSuccessFee(PayOrder $payOrder, int $actualFee, string $payNo, string $traceNo): void { @@ -34,6 +45,7 @@ class PayOrderFeeService extends BaseService $estimated = (int) $payOrder->fee_estimated_amount; if ($actualFee > $estimated) { + // 实际手续费高于预估值时,先扣掉预冻结部分,再把差额从可用余额里补扣。 if ($estimated > 0) { $this->merchantAccountService->deductFrozenAmountInCurrentTransaction( (int) $payOrder->merchant_id, @@ -66,6 +78,7 @@ class PayOrderFeeService extends BaseService } if ($actualFee < $estimated) { + // 实际手续费低于预估值时,先按实际值扣减冻结金额,再把多冻结部分释放回可用余额。 if ($actualFee > 0) { $this->merchantAccountService->deductFrozenAmountInCurrentTransaction( (int) $payOrder->merchant_id, @@ -98,6 +111,7 @@ class PayOrderFeeService extends BaseService } if ($actualFee > 0) { + // 实际值和预估值一致时,直接把冻结金额一次性扣减掉即可。 $this->merchantAccountService->deductFrozenAmountInCurrentTransaction( (int) $payOrder->merchant_id, $actualFee, @@ -114,6 +128,12 @@ class PayOrderFeeService extends BaseService /** * 释放支付单已冻结的手续费。 + * + * @param PayOrder $payOrder 支付订单 + * @param string $payNo 支付单号 + * @param string $traceNo 追踪号 + * @param string $remark 备注 + * @return void */ public function releaseFrozenFeeIfNeeded(PayOrder $payOrder, string $payNo, string $traceNo, string $remark): void { @@ -121,6 +141,7 @@ class PayOrderFeeService extends BaseService return; } + // 只有真正处于冻结态的手续费才需要释放,已经扣减或已释放的单子直接跳过。 if ((int) $payOrder->fee_status !== TradeConstant::FEE_STATUS_FROZEN) { return; } @@ -138,3 +159,7 @@ class PayOrderFeeService extends BaseService ); } } + + + + diff --git a/app/service/payment/order/PayOrderLifecycleService.php b/app/service/payment/order/PayOrderLifecycleService.php index 68700cd..9d8e65c 100644 --- a/app/service/payment/order/PayOrderLifecycleService.php +++ b/app/service/payment/order/PayOrderLifecycleService.php @@ -16,11 +16,19 @@ use app\repository\payment\trade\PayOrderRepository; * 支付单生命周期服务。 * * 负责支付单状态推进、关闭、超时和手续费处理。 + * + * @property PayOrderFeeService $payOrderFeeService 支付单手续费服务 + * @property BizOrderRepository $bizOrderRepository 业务订单仓库 + * @property PayOrderRepository $payOrderRepository 支付单仓库 */ class PayOrderLifecycleService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param PayOrderFeeService $payOrderFeeService 支付单手续费服务 + * @param BizOrderRepository $bizOrderRepository 业务订单仓库 + * @param PayOrderRepository $payOrderRepository 支付订单仓库 */ public function __construct( protected PayOrderFeeService $payOrderFeeService, @@ -35,8 +43,8 @@ class PayOrderLifecycleService extends BaseService * 用于支付回调或主动查单成功后的状态推进;自有通道在这里完成手续费正式扣减。 * * @param string $payNo 支付单号 - * @param array $input 回调或查单入参 - * @return PayOrder + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 */ public function markPaySuccess(string $payNo, array $input = []): PayOrder { @@ -51,8 +59,10 @@ class PayOrderLifecycleService extends BaseService * 该方法只处理状态推进和资金动作,不负责外部通道请求。 * * @param string $payNo 支付单号 - * @param array $input 回调或查单入参 - * @return PayOrder + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 + * @throws ResourceNotFoundException + * @throws BusinessStateException */ public function markPaySuccessInCurrentTransaction(string $payNo, array $input = []): PayOrder { @@ -77,16 +87,19 @@ class PayOrderLifecycleService extends BaseService ]); } + // 成功态优先使用插件回传的实际手续费,没有则沿用预估值。 $actualFee = array_key_exists('fee_actual_amount', $input) ? (int) $input['fee_actual_amount'] : (int) $payOrder->fee_estimated_amount; $traceNo = (string) ($payOrder->trace_no ?: $payOrder->biz_no); + // 成功后正式结算手续费,避免自有通道只冻结不扣减。 $this->payOrderFeeService->settleSuccessFee($payOrder, $actualFee, $payNo, $traceNo); $payOrder->status = TradeConstant::ORDER_STATUS_SUCCESS; $payOrder->paid_at = $input['paid_at'] ?? $this->now(); $payOrder->fee_actual_amount = $actualFee; + // 平台代收和自有通道的手续费、结算状态规则不同,这里统一收口。 $payOrder->fee_status = (int) $payOrder->channel_type === RouteConstant::CHANNEL_MODE_SELF ? TradeConstant::FEE_STATUS_DEDUCTED : TradeConstant::FEE_STATUS_NONE; @@ -102,6 +115,7 @@ class PayOrderLifecycleService extends BaseService $payOrder->ext_json = array_merge((array) $payOrder->ext_json, $input['ext_json'] ?? []); $payOrder->save(); + // 业务单状态也要一起收口,保证支付单和业务单一致。 $this->syncBizOrderAfterSuccess($payOrder, $traceNo); return $payOrder->refresh(); @@ -109,6 +123,10 @@ class PayOrderLifecycleService extends BaseService /** * 标记支付失败。 + * + * @param string $payNo 支付单号 + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 */ public function markPayFailed(string $payNo, array $input = []): PayOrder { @@ -119,6 +137,12 @@ class PayOrderLifecycleService extends BaseService /** * 在当前事务中标记支付失败。 + * + * @param string $payNo 支付单号 + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 + * @throws ResourceNotFoundException + * @throws BusinessStateException */ public function markPayFailedInCurrentTransaction(string $payNo, array $input = []): PayOrder { @@ -144,6 +168,7 @@ class PayOrderLifecycleService extends BaseService } $traceNo = (string) ($payOrder->trace_no ?: $payOrder->biz_no); + // 失败时只释放需要冻结的手续费,避免重复扣减或重复释放。 $this->payOrderFeeService->releaseFrozenFeeIfNeeded($payOrder, $payNo, $traceNo, '支付失败释放手续费'); $payOrder->status = TradeConstant::ORDER_STATUS_FAILED; @@ -159,6 +184,7 @@ class PayOrderLifecycleService extends BaseService $payOrder->ext_json = array_merge((array) $payOrder->ext_json, $input['ext_json'] ?? []); $payOrder->save(); + // 支付单进入终态后,同步回业务单,避免上游只能依赖支付单判断结果。 $this->syncBizOrderAfterTerminalStatus($payOrder, $payNo, $traceNo, TradeConstant::ORDER_STATUS_FAILED, 'failed_at'); return $payOrder->refresh(); @@ -166,6 +192,10 @@ class PayOrderLifecycleService extends BaseService /** * 关闭支付单。 + * + * @param string $payNo 支付单号 + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 */ public function closePayOrder(string $payNo, array $input = []): PayOrder { @@ -176,6 +206,12 @@ class PayOrderLifecycleService extends BaseService /** * 在当前事务中关闭支付单。 + * + * @param string $payNo 支付单号 + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 + * @throws ResourceNotFoundException + * @throws BusinessStateException */ public function closePayOrderInCurrentTransaction(string $payNo, array $input = []): PayOrder { @@ -201,6 +237,7 @@ class PayOrderLifecycleService extends BaseService } $traceNo = (string) ($payOrder->trace_no ?: $payOrder->biz_no); + // 关闭单据时同样要处理冻结手续费,防止资金一直占用。 $this->payOrderFeeService->releaseFrozenFeeIfNeeded($payOrder, $payNo, $traceNo, '支付关闭释放手续费'); $payOrder->status = TradeConstant::ORDER_STATUS_CLOSED; @@ -217,6 +254,7 @@ class PayOrderLifecycleService extends BaseService $payOrder->ext_json = array_merge($extJson, $input['ext_json'] ?? []); $payOrder->save(); + // 关闭态也要同步给业务单,避免后续继续拉起支付。 $this->syncBizOrderAfterTerminalStatus($payOrder, $payNo, $traceNo, TradeConstant::ORDER_STATUS_CLOSED, 'closed_at'); return $payOrder->refresh(); @@ -224,6 +262,10 @@ class PayOrderLifecycleService extends BaseService /** * 标记支付超时。 + * + * @param string $payNo 支付单号 + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 */ public function timeoutPayOrder(string $payNo, array $input = []): PayOrder { @@ -234,6 +276,12 @@ class PayOrderLifecycleService extends BaseService /** * 在当前事务中标记支付超时。 + * + * @param string $payNo 支付单号 + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 + * @throws ResourceNotFoundException + * @throws BusinessStateException */ public function timeoutPayOrderInCurrentTransaction(string $payNo, array $input = []): PayOrder { @@ -259,6 +307,7 @@ class PayOrderLifecycleService extends BaseService } $traceNo = (string) ($payOrder->trace_no ?: $payOrder->biz_no); + // 超时单同样释放冻结手续费,确保后续可以重新发起支付。 $this->payOrderFeeService->releaseFrozenFeeIfNeeded($payOrder, $payNo, $traceNo, '支付超时释放手续费'); $payOrder->status = TradeConstant::ORDER_STATUS_TIMEOUT; @@ -282,6 +331,10 @@ class PayOrderLifecycleService extends BaseService /** * 同步支付成功后的业务单状态。 + * + * @param PayOrder $payOrder 支付订单 + * @param string $traceNo 追踪号 + * @return void */ private function syncBizOrderAfterSuccess(PayOrder $payOrder, string $traceNo): void { @@ -302,11 +355,19 @@ class PayOrderLifecycleService extends BaseService /** * 同步支付终态后的业务单状态。 + * + * @param PayOrder $payOrder 支付订单 + * @param string $payNo 支付单号 + * @param string $traceNo 追踪号 + * @param int $status 状态 + * @param string $timestampField 时间字段名 + * @return void */ private function syncBizOrderAfterTerminalStatus(PayOrder $payOrder, string $payNo, string $traceNo, int $status, string $timestampField): void { $bizOrder = $this->bizOrderRepository->findForUpdateByBizNo((string) $payOrder->biz_no); if (!$bizOrder || (string) $bizOrder->active_pay_no !== $payNo) { + // 只有当前生效的支付单才允许回写业务单,避免旧重试单覆盖新单状态。 return; } diff --git a/app/service/payment/order/PayOrderQueryService.php b/app/service/payment/order/PayOrderQueryService.php index d74ce3d..914d02b 100644 --- a/app/service/payment/order/PayOrderQueryService.php +++ b/app/service/payment/order/PayOrderQueryService.php @@ -14,14 +14,27 @@ use app\repository\payment\trade\BizOrderRepository; use app\repository\payment\trade\PayOrderRepository; /** - * 支付单查询服务。 + * 支付单查询与展示拼装服务。 * - * 只负责支付单列表类查询与展示格式化,不承载状态推进逻辑。 + * 负责支付单列表、详情和筛选辅助数据的查询,不承载状态推进逻辑。 + * + * @property PayOrderRepository $payOrderRepository 支付单仓库 + * @property BizOrderRepository $bizOrderRepository 业务订单仓库 + * @property MerchantAccountLedgerRepository $merchantAccountLedgerRepository 商户账户流水仓库 + * @property PaymentTypeRepository $paymentTypeRepository 支付类型仓库 + * @property PayOrderReportService $payOrderReportService 支付单报表服务 */ class PayOrderQueryService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param PayOrderRepository $payOrderRepository 支付订单仓库 + * @param BizOrderRepository $bizOrderRepository 业务订单仓库 + * @param MerchantAccountLedgerRepository $merchantAccountLedgerRepository 商户账户流水仓库 + * @param PaymentTypeRepository $paymentTypeRepository 支付类型仓库 + * @param PayOrderReportService $payOrderReportService 支付单报表服务 + * @return void */ public function __construct( protected PayOrderRepository $payOrderRepository, @@ -36,12 +49,13 @@ class PayOrderQueryService extends BaseService * 分页查询支付订单列表。 * * 后台和商户后台共用同一套查询逻辑,商户侧会额外限制当前商户 ID。 + * 返回值会同时带上支付方式选项,方便列表页直接渲染筛选器。 * - * @param array $filters 查询条件 + * @param array $filters 筛选条件 * @param int $page 页码 * @param int $pageSize 每页条数 - * @param int|null $merchantId 商户侧强制限定的商户 ID - * @return array{list:array,total:int,page:int,size:int,pay_types:array} + * @param int|null $merchantId 商户ID + * @return array{list: array>, total: int, page: int, size: int, pay_types: array} 支付订单列表结构 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10, ?int $merchantId = null): array { @@ -122,6 +136,7 @@ class PayOrderQueryService extends BaseService $keyword = trim((string) ($filters['keyword'] ?? '')); if ($keyword !== '') { + // 关键词同时命中支付单、业务单、商户、通道和支付方式,方便后台一把搜全链路。 $query->where(function ($builder) use ($keyword) { $builder->where('po.pay_no', 'like', '%' . $keyword . '%') ->orWhere('po.biz_no', 'like', '%' . $keyword . '%') @@ -181,9 +196,13 @@ class PayOrderQueryService extends BaseService /** * 查询支付订单详情。 * + * 返回支付单、业务单、时间线和资金流水,供管理后台与商户后台共用。 + * * @param string $payNo 支付单号 - * @param int|null $merchantId 商户侧强制限定的商户 ID - * @return array{pay_order:mixed,biz_order:mixed,timeline:array,account_ledgers:mixed} + * @param int|null $merchantId 商户ID + * @return array{pay_order: PayOrder, biz_order: \app\model\payment\BizOrder|null, timeline: array>, account_ledgers: \Illuminate\Support\Collection} 支付详情结构 + * @throws ValidationException + * @throws ResourceNotFoundException */ public function detail(string $payNo, ?int $merchantId = null): array { @@ -198,6 +217,7 @@ class PayOrderQueryService extends BaseService } if ($merchantId !== null && $merchantId > 0 && (int) $payOrder->merchant_id !== $merchantId) { + // 商户后台只允许看自己的单,归属不匹配时直接按不存在处理。 throw new ResourceNotFoundException('支付单不存在', ['pay_no' => $payNo]); } @@ -215,6 +235,11 @@ class PayOrderQueryService extends BaseService /** * 加载支付相关资金流水。 + * + * 优先按追踪号查询,追踪号为空时回退到业务单号,避免漏掉关联流水。 + * + * @param PayOrder $payOrder 支付订单 + * @return \Illuminate\Support\Collection 支付相关资金流水集合 */ private function loadPayLedgers(PayOrder $payOrder) { @@ -224,7 +249,8 @@ class PayOrderQueryService extends BaseService : collect(); if ($ledgers->isEmpty()) { - $ledgers = $this->merchantAccountLedgerRepository->listByBizNo((string) $payOrder->pay_no); + // 追踪号没有命中时,回到业务单号继续兜底,避免早期单据漏掉资金流水。 + $ledgers = $this->merchantAccountLedgerRepository->listByBizNo((string) $payOrder->biz_no); } return $ledgers; @@ -232,6 +258,8 @@ class PayOrderQueryService extends BaseService /** * 返回启用的支付方式选项,供列表筛选使用。 + * + * @return array 支付方式选项 */ private function payTypeOptions(): array { diff --git a/app/service/payment/order/PayOrderReportService.php b/app/service/payment/order/PayOrderReportService.php index c2b85f4..2e51b66 100644 --- a/app/service/payment/order/PayOrderReportService.php +++ b/app/service/payment/order/PayOrderReportService.php @@ -11,12 +11,17 @@ use app\model\payment\PayOrder; /** * 支付单结果组装服务。 * - * 负责支付单列表和详情页的展示字段格式化。 + * 负责支付单列表、详情页和时间线的展示字段格式化。 */ class PayOrderReportService extends BaseService { /** * 格式化支付订单行,统一输出前端需要的中文字段。 + * + * 该方法只做展示层字段补齐,不修改原始业务语义。 + * + * @param array $row 原始查询行 + * @return array 格式化后的支付单行 */ public function formatPayOrderRow(array $row): array { @@ -58,11 +63,17 @@ class PayOrderReportService extends BaseService /** * 构造支付时间线。 + * + * 按创建、成功、关闭、失败、超时的顺序输出,方便前端直接渲染状态流转。 + * + * @param PayOrder $payOrder 支付订单 + * @return array> 支付时间线 */ public function buildPayTimeline(PayOrder $payOrder): array { $extJson = (array) ($payOrder->ext_json ?? []); + // 只保留真实发生过的节点,未触发的状态直接过滤掉,避免时间线里出现空占位。 return array_values(array_filter([ [ 'status' => 'created', @@ -75,11 +86,13 @@ class PayOrderReportService extends BaseService $payOrder->closed_at ? [ 'status' => 'closed', 'at' => $this->formatDateTime($payOrder->closed_at, '—'), + // 关闭原因优先取扩展信息里的记录,便于展示人工关单或自动关单原因。 'reason' => (string) ($extJson['close_reason'] ?? ''), ] : null, $payOrder->failed_at ? [ 'status' => 'failed', 'at' => $this->formatDateTime($payOrder->failed_at, '—'), + // 失败原因先看渠道返回,再回落到扩展信息里保存的统一原因字段。 'reason' => (string) ($payOrder->channel_error_msg ?: ($extJson['reason'] ?? '')), ] : null, $payOrder->timeout_at ? [ @@ -90,3 +103,5 @@ class PayOrderReportService extends BaseService ])); } } + + diff --git a/app/service/payment/order/PayOrderService.php b/app/service/payment/order/PayOrderService.php index 40648cb..5ce02a7 100644 --- a/app/service/payment/order/PayOrderService.php +++ b/app/service/payment/order/PayOrderService.php @@ -8,14 +8,22 @@ use support\Request; use support\Response; /** - * 支付单门面服务。 + * 支付单服务。 * - * 对外保留原有调用契约,内部委托给查询、发起、生命周期和回调四个子服务。 + * @property PayOrderQueryService $queryService 查询服务 + * @property PayOrderAttemptService $attemptService 发起服务 + * @property PayOrderLifecycleService $lifecycleService 生命周期服务 + * @property PayOrderCallbackService $callbackService 回调服务 */ class PayOrderService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param PayOrderQueryService $queryService 查询服务 + * @param PayOrderAttemptService $attemptService 发起服务 + * @param PayOrderLifecycleService $lifecycleService 生命周期服务 + * @param PayOrderCallbackService $callbackService 回调服务 */ public function __construct( protected PayOrderQueryService $queryService, @@ -27,6 +35,12 @@ class PayOrderService extends BaseService /** * 分页查询支付订单列表。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @param int|null $merchantId 商户ID + * @return array 分页数据 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10, ?int $merchantId = null): array { @@ -35,6 +49,10 @@ class PayOrderService extends BaseService /** * 查询支付订单详情。 + * + * @param string $payNo 支付单号 + * @param int|null $merchantId 商户ID + * @return array 订单详情 */ public function detail(string $payNo, ?int $merchantId = null): array { @@ -43,6 +61,9 @@ class PayOrderService extends BaseService /** * 预创建支付尝试。 + * + * @param array $input 下单数据 + * @return array 发起结果 */ public function preparePayAttempt(array $input): array { @@ -51,6 +72,10 @@ class PayOrderService extends BaseService /** * 标记支付成功。 + * + * @param string $payNo 支付单号 + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 */ public function markPaySuccess(string $payNo, array $input = []): PayOrder { @@ -59,6 +84,10 @@ class PayOrderService extends BaseService /** * 在当前事务中标记支付成功。 + * + * @param string $payNo 支付单号 + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 */ public function markPaySuccessInCurrentTransaction(string $payNo, array $input = []): PayOrder { @@ -67,6 +96,10 @@ class PayOrderService extends BaseService /** * 标记支付失败。 + * + * @param string $payNo 支付单号 + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 */ public function markPayFailed(string $payNo, array $input = []): PayOrder { @@ -75,6 +108,10 @@ class PayOrderService extends BaseService /** * 在当前事务中标记支付失败。 + * + * @param string $payNo 支付单号 + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 */ public function markPayFailedInCurrentTransaction(string $payNo, array $input = []): PayOrder { @@ -83,6 +120,10 @@ class PayOrderService extends BaseService /** * 关闭支付单。 + * + * @param string $payNo 支付单号 + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 */ public function closePayOrder(string $payNo, array $input = []): PayOrder { @@ -91,6 +132,10 @@ class PayOrderService extends BaseService /** * 在当前事务中关闭支付单。 + * + * @param string $payNo 支付单号 + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 */ public function closePayOrderInCurrentTransaction(string $payNo, array $input = []): PayOrder { @@ -99,6 +144,10 @@ class PayOrderService extends BaseService /** * 标记支付超时。 + * + * @param string $payNo 支付单号 + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 */ public function timeoutPayOrder(string $payNo, array $input = []): PayOrder { @@ -107,6 +156,10 @@ class PayOrderService extends BaseService /** * 在当前事务中标记支付超时。 + * + * @param string $payNo 支付单号 + * @param array $input 状态数据 + * @return PayOrder 支付订单模型 */ public function timeoutPayOrderInCurrentTransaction(string $payNo, array $input = []): PayOrder { @@ -115,6 +168,9 @@ class PayOrderService extends BaseService /** * 处理渠道回调。 + * + * @param array $input 回调数据 + * @return PayOrder 支付订单模型 */ public function handleChannelCallback(array $input): PayOrder { @@ -123,9 +179,16 @@ class PayOrderService extends BaseService /** * 按支付单号处理真实第三方回调。 + * + * @param string $payNo 支付单号 + * @param Request $request 请求对象 + * @return string|Response 字符串或响应对象 */ public function handlePluginCallback(string $payNo, Request $request): string|Response { return $this->callbackService->handlePluginCallback($payNo, $request); } } + + + diff --git a/app/service/payment/order/RefundCreationService.php b/app/service/payment/order/RefundCreationService.php index 20e7473..fe564f5 100644 --- a/app/service/payment/order/RefundCreationService.php +++ b/app/service/payment/order/RefundCreationService.php @@ -17,11 +17,18 @@ use app\repository\payment\trade\RefundOrderRepository; * 退款单创建服务。 * * 负责退款单创建和幂等校验,不承载状态推进逻辑。 + * + * @property PayOrderRepository $payOrderRepository 支付单仓库 + * @property RefundOrderRepository $refundOrderRepository 退款单仓库 */ class RefundCreationService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param PayOrderRepository $payOrderRepository 支付订单仓库 + * @param RefundOrderRepository $refundOrderRepository 退款单仓库 + * @return void */ public function __construct( protected PayOrderRepository $payOrderRepository, @@ -34,8 +41,12 @@ class RefundCreationService extends BaseService * * 当前仅支持整单全额退款,且同一支付单只能创建一张退款单。 * - * @param array $input 退款请求参数 - * @return RefundOrder + * @param array $input 退款参数 + * @return RefundOrder 退款单记录 + * @throws ValidationException + * @throws ResourceNotFoundException + * @throws BusinessStateException + * @throws ConflictException */ public function createRefund(array $input): RefundOrder { @@ -44,11 +55,14 @@ class RefundCreationService extends BaseService throw new ValidationException('pay_no 不能为空'); } + // 退款必须先锁定原支付单,确保状态和金额都满足退款前置条件。 + /** @var \app\model\payment\PayOrder|null $payOrder */ $payOrder = $this->payOrderRepository->findByPayNo($payNo); if (!$payOrder) { throw new ResourceNotFoundException('支付单不存在', ['pay_no' => $payNo]); } + // 只有已支付订单才允许发起退款,其他状态直接拒绝。 if ((int) $payOrder->status !== TradeConstant::ORDER_STATUS_SUCCESS) { throw new BusinessStateException('订单状态不允许退款', [ 'pay_no' => $payNo, @@ -64,8 +78,11 @@ class RefundCreationService extends BaseService throw new BusinessStateException('当前仅支持整单全额退款'); } + // 业务系统若传了商户退款单号,就优先按商户幂等键查重。 $merchantRefundNo = trim((string) ($input['merchant_refund_no'] ?? '')); if ($merchantRefundNo !== '') { + // 商户退款单号是第一层幂等键,优先用它判断是否重复提交。 + /** @var RefundOrder|null $existingByMerchantNo */ $existingByMerchantNo = $this->refundOrderRepository->findByMerchantRefundNo((int) $payOrder->merchant_id, $merchantRefundNo); if ($existingByMerchantNo) { if ((string) $existingByMerchantNo->pay_no !== $payNo || (int) $existingByMerchantNo->refund_amount !== $refundAmount) { @@ -80,7 +97,10 @@ class RefundCreationService extends BaseService } } - if ($existingByPayNo = $this->refundOrderRepository->findByPayNo($payNo)) { + // 没有商户退款单号时,用支付单号兜底,避免同一支付单重复创建退款单。 + /** @var RefundOrder|null $existingByPayNo */ + $existingByPayNo = $this->refundOrderRepository->findByPayNo($payNo); + if ($existingByPayNo) { if ($merchantRefundNo !== '' && (string) $existingByPayNo->merchant_refund_no !== $merchantRefundNo) { throw new ConflictException('重复退款', ['pay_no' => $payNo]); } @@ -90,6 +110,12 @@ class RefundCreationService extends BaseService $traceNo = (string) ($payOrder->trace_no ?: $payOrder->biz_no); + // 退款单落库时同步追踪号、渠道单号和反向手续费,方便后续退款推进与对账。 + /** @var int $feeReverseAmount */ + $feeReverseAmount = ((int) $payOrder->channel_type === RouteConstant::CHANNEL_MODE_COLLECT) + ? (int) $payOrder->fee_actual_amount + : 0; + // 代收场景下,退款需要把实际手续费作为反向金额记录下来,后续成功态才能正确冲正余额。 return $this->refundOrderRepository->create([ 'refund_no' => $this->generateNo('RFD'), 'merchant_id' => (int) $payOrder->merchant_id, @@ -100,7 +126,7 @@ class RefundCreationService extends BaseService 'merchant_refund_no' => $merchantRefundNo !== '' ? $merchantRefundNo : $this->generateNo('MRF'), 'channel_id' => (int) $payOrder->channel_id, 'refund_amount' => $refundAmount, - 'fee_reverse_amount' => (int) $payOrder->channel_type === RouteConstant::CHANNEL_MODE_COLLECT ? (int) $payOrder->fee_actual_amount : 0, + 'fee_reverse_amount' => $feeReverseAmount, 'status' => TradeConstant::REFUND_STATUS_CREATED, 'channel_request_no' => $this->generateNo('RQR'), 'reason' => (string) ($input['reason'] ?? ''), diff --git a/app/service/payment/order/RefundLifecycleService.php b/app/service/payment/order/RefundLifecycleService.php index 1587ba5..5da1c84 100644 --- a/app/service/payment/order/RefundLifecycleService.php +++ b/app/service/payment/order/RefundLifecycleService.php @@ -17,11 +17,22 @@ use app\service\account\funds\MerchantAccountService; * 退款单生命周期服务。 * * 负责退款单创建、处理中、成功、失败和重试等状态推进。 + * + * @property PayOrderRepository $payOrderRepository 支付单仓库 + * @property BizOrderRepository $bizOrderRepository 业务订单仓库 + * @property RefundOrderRepository $refundOrderRepository 退款单仓库 + * @property MerchantAccountService $merchantAccountService 商户账户服务 */ class RefundLifecycleService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param PayOrderRepository $payOrderRepository 支付订单仓库 + * @param BizOrderRepository $bizOrderRepository 业务订单仓库 + * @param RefundOrderRepository $refundOrderRepository 退款单仓库 + * @param MerchantAccountService $merchantAccountService 商户账户服务 + * @return void */ public function __construct( protected PayOrderRepository $payOrderRepository, @@ -33,6 +44,12 @@ class RefundLifecycleService extends BaseService /** * 标记退款处理中。 + * + * 由渠道受理后推进到处理中态,幂等地处理重复请求。 + * + * @param string $refundNo 退款单号 + * @param array $input 输入参数 + * @return RefundOrder 退款单模型 */ public function markRefundProcessing(string $refundNo, array $input = []): RefundOrder { @@ -43,6 +60,12 @@ class RefundLifecycleService extends BaseService /** * 退款重试。 + * + * 仅允许失败态退款单重新推进到处理中。 + * + * @param string $refundNo 退款单号 + * @param array $input 输入参数 + * @return RefundOrder 退款单模型 */ public function retryRefund(string $refundNo, array $input = []): RefundOrder { @@ -53,6 +76,13 @@ class RefundLifecycleService extends BaseService /** * 在当前事务中标记退款处理中或重试。 + * + * @param string $refundNo 退款单号 + * @param array $input 输入参数 + * @param bool $isRetry 是否来自重试流程 + * @return RefundOrder 退款单模型 + * @throws ResourceNotFoundException + * @throws BusinessStateException */ public function markRefundProcessingInCurrentTransaction(string $refundNo, array $input = [], bool $isRetry = false): RefundOrder { @@ -77,6 +107,7 @@ class RefundLifecycleService extends BaseService ]); } + // 退款失败后再重试时,只有失败态才允许重新推进到处理中。 if ($currentStatus === TradeConstant::REFUND_STATUS_FAILED && !$isRetry) { return $refundOrder; } @@ -92,6 +123,7 @@ class RefundLifecycleService extends BaseService } $refundOrder->last_error = (string) ($input['last_error'] ?? $refundOrder->last_error ?? ''); if ($isRetry) { + // 重试时生成新的渠道请求号,避免和上一轮失败请求混在一起。 $refundOrder->retry_count = (int) $refundOrder->retry_count + 1; $refundOrder->channel_request_no = $this->generateNo('RQR'); } @@ -99,6 +131,7 @@ class RefundLifecycleService extends BaseService $extJson = (array) $refundOrder->ext_json; $reason = trim((string) ($input['reason'] ?? '')); if ($reason !== '') { + // 把处理/重试原因单独保留到扩展字段里,便于后台排查。 $extJson[$isRetry ? 'retry_reason' : 'processing_reason'] = $reason; } $refundOrder->ext_json = array_merge($extJson, $input['ext_json'] ?? []); @@ -113,8 +146,8 @@ class RefundLifecycleService extends BaseService * 成功后会推进退款单状态,并在平台代收场景下做余额冲减或结算逆向处理。 * * @param string $refundNo 退款单号 - * @param array $input 回调或查单入参 - * @return RefundOrder + * @param array $input 输入参数 + * @return RefundOrder 退款单模型 */ public function markRefundSuccess(string $refundNo, array $input = []): RefundOrder { @@ -127,8 +160,10 @@ class RefundLifecycleService extends BaseService * 在当前事务中标记退款成功。 * * @param string $refundNo 退款单号 - * @param array $input 回调或查单入参 - * @return RefundOrder + * @param array $input 输入参数 + * @return RefundOrder 退款单模型 + * @throws ResourceNotFoundException + * @throws BusinessStateException */ public function markRefundSuccessInCurrentTransaction(string $refundNo, array $input = []): RefundOrder { @@ -150,6 +185,7 @@ class RefundLifecycleService extends BaseService return $refundOrder; } + // 先锁定原支付单,避免退款推进时原单状态被并发修改。 $payOrder = $this->payOrderRepository->findForUpdateByPayNo((string) $refundOrder->pay_no); if (!$payOrder || (int) $payOrder->status !== TradeConstant::ORDER_STATUS_SUCCESS) { throw new BusinessStateException('原支付单状态不允许退款', [ @@ -160,6 +196,7 @@ class RefundLifecycleService extends BaseService $traceNo = (string) ($refundOrder->trace_no ?: $refundOrder->biz_no); if ((int) $payOrder->channel_type === RouteConstant::CHANNEL_MODE_COLLECT) { + // 平台代收退款在已结算时,需要同步冲减商户可提现余额,口径按实收净额处理。 $reverseAmount = max(0, (int) $payOrder->pay_amount - (int) $payOrder->fee_actual_amount); if ((int) $payOrder->settlement_status === TradeConstant::SETTLEMENT_STATUS_SETTLED && $reverseAmount > 0) { $this->merchantAccountService->debitAvailableAmountInCurrentTransaction( @@ -175,10 +212,12 @@ class RefundLifecycleService extends BaseService ); } + // 已结算的代收单被退款后,状态要回写成 reversed,表示结算已被抵消。 $payOrder->settlement_status = TradeConstant::SETTLEMENT_STATUS_REVERSED; $payOrder->save(); } + // 退款成功后,退款单和业务单都要同步收口到成功态。 $refundOrder->status = TradeConstant::REFUND_STATUS_SUCCESS; $refundOrder->succeeded_at = $input['succeeded_at'] ?? $this->now(); $refundOrder->channel_refund_no = (string) ($input['channel_refund_no'] ?? $refundOrder->channel_refund_no ?? ''); @@ -188,6 +227,7 @@ class RefundLifecycleService extends BaseService $bizOrder = $this->bizOrderRepository->findForUpdateByBizNo((string) $refundOrder->biz_no); if ($bizOrder) { + // 业务单的退款金额直接收口到原支付金额,避免后续展示和统计再做推导。 $bizOrder->refund_amount = (int) $bizOrder->order_amount; if (empty($bizOrder->trace_no)) { $bizOrder->trace_no = $traceNo; @@ -200,6 +240,10 @@ class RefundLifecycleService extends BaseService /** * 退款失败。 + * + * @param string $refundNo 退款单号 + * @param array $input 输入参数 + * @return RefundOrder 退款单模型 */ public function markRefundFailed(string $refundNo, array $input = []): RefundOrder { @@ -210,6 +254,12 @@ class RefundLifecycleService extends BaseService /** * 在当前事务中标记退款失败。 + * + * @param string $refundNo 退款单号 + * @param array $input 输入参数 + * @return RefundOrder 退款单模型 + * @throws ResourceNotFoundException + * @throws BusinessStateException */ public function markRefundFailedInCurrentTransaction(string $refundNo, array $input = []): RefundOrder { @@ -234,6 +284,7 @@ class RefundLifecycleService extends BaseService ]); } + // 失败状态只更新失败信息,不再改动原支付单和业务单。 $refundOrder->status = TradeConstant::REFUND_STATUS_FAILED; $refundOrder->failed_at = $input['failed_at'] ?? $this->now(); $refundOrder->channel_refund_no = (string) ($input['channel_refund_no'] ?? $refundOrder->channel_refund_no ?? ''); @@ -241,6 +292,7 @@ class RefundLifecycleService extends BaseService $extJson = (array) $refundOrder->ext_json; $reason = trim((string) ($input['reason'] ?? '')); if ($reason !== '') { + // 失败原因也放进扩展字段,方便后台对比渠道返回和内部处理结果。 $extJson['fail_reason'] = $reason; } $refundOrder->ext_json = array_merge($extJson, $input['ext_json'] ?? []); diff --git a/app/service/payment/order/RefundQueryService.php b/app/service/payment/order/RefundQueryService.php index 4e995ae..bdf8760 100644 --- a/app/service/payment/order/RefundQueryService.php +++ b/app/service/payment/order/RefundQueryService.php @@ -10,14 +10,25 @@ use app\repository\payment\config\PaymentTypeRepository; use app\repository\payment\trade\RefundOrderRepository; /** - * 退款单查询服务。 + * 退款单查询与展示拼装服务。 * - * 只负责退款列表、详情和数据查询,不承载退款状态推进逻辑。 + * 负责退款列表、详情和展示辅助数据查询,不承载退款状态推进逻辑。 + * + * @property RefundOrderRepository $refundOrderRepository 退款单仓库 + * @property MerchantAccountLedgerRepository $merchantAccountLedgerRepository 商户账户流水仓库 + * @property PaymentTypeRepository $paymentTypeRepository 支付类型仓库 + * @property RefundReportService $refundReportService 退款报表服务 */ class RefundQueryService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param RefundOrderRepository $refundOrderRepository 退款单仓库 + * @param MerchantAccountLedgerRepository $merchantAccountLedgerRepository 商户账户流水仓库 + * @param PaymentTypeRepository $paymentTypeRepository 支付类型仓库 + * @param RefundReportService $refundReportService 退款报表服务 + * @return void */ public function __construct( protected RefundOrderRepository $refundOrderRepository, @@ -30,11 +41,13 @@ class RefundQueryService extends BaseService /** * 分页查询退款订单列表。 * - * @param array $filters 查询条件 + * 返回列表、总数、分页信息和支付方式选项,供后台和商户后台直接复用。 + * + * @param array $filters 筛选条件 * @param int $page 页码 * @param int $pageSize 每页条数 - * @param int|null $merchantId 商户侧强制限定的商户 ID - * @return array{list:array,total:int,page:int,size:int,pay_types:array} + * @param int|null $merchantId 商户ID + * @return array{list: array>, total: int, page: int, size: int, pay_types: array} 退款订单列表结构 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10, ?int $merchantId = null): array { @@ -42,6 +55,7 @@ class RefundQueryService extends BaseService $keyword = trim((string) ($filters['keyword'] ?? '')); if ($keyword !== '') { + // 关键词同时命中退款单、支付单、业务单、商户和通道,方便后台按任一线索快速定位。 $query->where(function ($builder) use ($keyword) { $builder->where('ro.refund_no', 'like', '%' . $keyword . '%') ->orWhere('ro.pay_no', 'like', '%' . $keyword . '%') @@ -83,6 +97,7 @@ class RefundQueryService extends BaseService ->orderByDesc('ro.id') ->paginate(max(1, $pageSize), ['*'], 'page', max(1, $page)); + // 列表页需要直接显示文案和金额格式,所以在查询层统一做一次格式化。 $list = []; foreach ($paginator->items() as $item) { $list[] = $this->refundReportService->formatRefundOrderRow((array) $item); @@ -100,9 +115,13 @@ class RefundQueryService extends BaseService /** * 查询退款订单详情。 * + * 返回退款单、时间线和资金流水,供列表钻取和详情页展示。 + * * @param string $refundNo 退款单号 - * @param int|null $merchantId 商户侧强制限定的商户 ID - * @return array{refund_order:array,timeline:array,account_ledgers:array} + * @param int|null $merchantId 商户ID + * @return array{refund_order: array, timeline: array>, account_ledgers: array>} 退款详情结构 + * @throws ValidationException + * @throws ResourceNotFoundException */ public function detail(string $refundNo, ?int $merchantId = null): array { @@ -118,6 +137,7 @@ class RefundQueryService extends BaseService } $refundOrder = $this->refundReportService->formatRefundOrderRow((array) $row); + // 详情页把原始行再转成展示数组,便于前端直接渲染各类状态和金额字段。 $timeline = $this->refundReportService->buildRefundTimeline($row); $accountLedgers = $this->loadRefundLedgers($row); @@ -130,6 +150,11 @@ class RefundQueryService extends BaseService /** * 按退款单号查询退款单,可按商户限制。 + * + * @param string $refundNo 退款单号 + * @param int|null $merchantId 商户ID + * @return \app\model\payment\RefundOrder|null 退款单模型 + * @throws ValidationException */ public function findByRefundNo(string $refundNo, ?int $merchantId = null): ?\app\model\payment\RefundOrder { @@ -157,9 +182,13 @@ class RefundQueryService extends BaseService /** * 构建退款订单基础查询,列表与详情共用。 + * + * @param int|null $merchantId 商户ID + * @return \Illuminate\Database\Eloquent\Builder 查询构造器 */ private function buildRefundOrderQuery(?int $merchantId = null) { + // 退款单详情需要同时展示支付、业务、商户和通道信息,所以一次性把相关表都 join 进来。 $query = $this->refundOrderRepository->query() ->from('ma_refund_order as ro') ->leftJoin('ma_pay_order as po', 'po.pay_no', '=', 'ro.pay_no') @@ -226,8 +255,13 @@ class RefundQueryService extends BaseService /** * 加载退款相关资金流水。 + * + * 按追踪号、业务单号、退款单号依次回退查找,尽量把相关流水补齐。 + * + * @param object|null $refundOrder 退款订单或查询行 + * @return array> 退款流水展示结构 */ - private function loadRefundLedgers(mixed $refundOrder): array + private function loadRefundLedgers(object|null $refundOrder): array { $traceNo = trim((string) ($refundOrder->trace_no ?? '')); $bizNo = trim((string) ($refundOrder->biz_no ?? '')); @@ -239,10 +273,12 @@ class RefundQueryService extends BaseService } if (empty($ledgers) && $bizNo !== '') { + // 退款流水优先按追踪号查,查不到再回到业务单号兜底。 $ledgers = $this->collectionToArray($this->merchantAccountLedgerRepository->listByBizNo($bizNo)); } if (empty($ledgers) && $refundNo !== '') { + // 最后再用退款单号补查,尽量避免详情页缺少资金流水。 $ledgers = $this->collectionToArray($this->merchantAccountLedgerRepository->listByBizNo($refundNo)); } @@ -256,6 +292,9 @@ class RefundQueryService extends BaseService /** * 将查询结果转换成普通数组。 + * + * @param iterable $items 查询结果 + * @return array 查询结果列表 */ private function collectionToArray(iterable $items): array { @@ -269,6 +308,8 @@ class RefundQueryService extends BaseService /** * 返回启用的支付方式选项,供筛选使用。 + * + * @return array 支付方式选项 */ private function payTypeOptions(): array { diff --git a/app/service/payment/order/RefundReportService.php b/app/service/payment/order/RefundReportService.php index f8537a9..7d1fc0d 100644 --- a/app/service/payment/order/RefundReportService.php +++ b/app/service/payment/order/RefundReportService.php @@ -10,12 +10,15 @@ use app\common\constant\TradeConstant; /** * 退款单结果组装服务。 * - * 负责退款详情页和列表页的展示字段格式化。 + * 负责退款列表、详情页和资金流水的展示字段格式化。 */ class RefundReportService extends BaseService { /** * 格式化退款订单行,统一输出前端展示字段。 + * + * @param array $row 原始查询行 + * @return array 格式化后的退款单行 */ public function formatRefundOrderRow(array $row): array { @@ -48,11 +51,17 @@ class RefundReportService extends BaseService /** * 构造退款时间线。 + * + * 依次输出创建、处理中、成功和失败节点,便于前端直接展示进度。 + * + * @param object|null $refundOrder 退款订单或查询行 + * @return array> 退款时间线 */ - public function buildRefundTimeline(mixed $refundOrder): array + public function buildRefundTimeline(object|null $refundOrder): array { $extJson = (array) ($refundOrder->ext_json ?? []); + // 退款时间线同样只展示已经发生的节点,并尽量用扩展信息补全原因字段。 return array_values(array_filter([ [ 'status' => 'created', @@ -64,6 +73,7 @@ class RefundReportService extends BaseService 'label' => '退款处理中', 'at' => $this->formatDateTime($refundOrder->processing_at, '—'), 'retry_count' => (int) ($refundOrder->retry_count ?? 0), + // 处理中原因优先按重试原因、处理中原因、最后错误的顺序回退。 'reason' => (string) ($extJson['retry_reason'] ?? $extJson['processing_reason'] ?? $refundOrder->last_error ?? ''), ] : null, $refundOrder->succeeded_at ? [ @@ -75,6 +85,7 @@ class RefundReportService extends BaseService 'status' => 'failed', 'label' => '退款失败', 'at' => $this->formatDateTime($refundOrder->failed_at, '—'), + // 失败原因先看最后错误,再回退到扩展信息和退款单原始原因。 'reason' => (string) ($refundOrder->last_error ?: ($extJson['fail_reason'] ?? $refundOrder->reason ?? '')), ] : null, ])); @@ -82,6 +93,9 @@ class RefundReportService extends BaseService /** * 格式化退款相关资金流水。 + * + * @param array $row 原始查询行 + * @return array 格式化后的流水行 */ public function formatLedgerRow(array $row): array { @@ -98,3 +112,4 @@ class RefundReportService extends BaseService return $row; } } + diff --git a/app/service/payment/order/RefundService.php b/app/service/payment/order/RefundService.php index 999eb4a..d4dfde6 100644 --- a/app/service/payment/order/RefundService.php +++ b/app/service/payment/order/RefundService.php @@ -6,14 +6,21 @@ use app\common\base\BaseService; use app\model\payment\RefundOrder; /** - * 退款单门面服务。 + * 退款单服务。 * - * 对外保留原有调用契约,内部委托给查询、创建和生命周期三个子服务。 + * @property RefundQueryService $queryService 查询服务 + * @property RefundCreationService $creationService 创建服务 + * @property RefundLifecycleService $lifecycleService 生命周期服务 */ class RefundService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param RefundQueryService $queryService 查询服务 + * @param RefundCreationService $creationService 创建服务 + * @param RefundLifecycleService $lifecycleService 生命周期服务 + * @return void */ public function __construct( protected RefundQueryService $queryService, @@ -24,6 +31,12 @@ class RefundService extends BaseService /** * 分页查询退款订单列表。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @param int|null $merchantId 商户ID + * @return array{list: array>, total: int, page: int, size: int, pay_types: array} 退款列表结构 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10, ?int $merchantId = null): array { @@ -32,6 +45,10 @@ class RefundService extends BaseService /** * 查询退款订单详情。 + * + * @param string $refundNo 退款单号 + * @param int|null $merchantId 商户ID + * @return array{refund_order: array, timeline: array>, account_ledgers: array>} 退款详情结构 */ public function detail(string $refundNo, ?int $merchantId = null): array { @@ -40,6 +57,9 @@ class RefundService extends BaseService /** * 创建退款单。 + * + * @param array $input 输入参数 + * @return RefundOrder 退款单模型 */ public function createRefund(array $input): RefundOrder { @@ -48,6 +68,10 @@ class RefundService extends BaseService /** * 标记退款处理中。 + * + * @param string $refundNo 退款单号 + * @param array $input 输入参数 + * @return RefundOrder 退款单模型 */ public function markRefundProcessing(string $refundNo, array $input = []): RefundOrder { @@ -56,10 +80,17 @@ class RefundService extends BaseService /** * 退款重试。 + * + * @param string $refundNo 退款单号 + * @param array $input 输入参数 + * @param int|null $merchantId 商户ID + * @return RefundOrder 退款单模型 + * @throws app\exception\ResourceNotFoundException */ public function retryRefund(string $refundNo, array $input = [], ?int $merchantId = null): RefundOrder { if ($merchantId !== null && $merchantId > 0) { + // 商户后台重试前先确认退款单归属,避免跨商户误操作。 $refundOrder = $this->queryService->findByRefundNo($refundNo, $merchantId); if (!$refundOrder) { throw new \app\exception\ResourceNotFoundException('退款单不存在', ['refund_no' => $refundNo]); @@ -71,6 +102,11 @@ class RefundService extends BaseService /** * 在当前事务中标记退款处理中或重试。 + * + * @param string $refundNo 退款单号 + * @param array $input 输入参数 + * @param bool $isRetry 是否来自重试流程 + * @return RefundOrder 退款单模型 */ public function markRefundProcessingInCurrentTransaction(string $refundNo, array $input = [], bool $isRetry = false): RefundOrder { @@ -79,6 +115,10 @@ class RefundService extends BaseService /** * 退款成功。 + * + * @param string $refundNo 退款单号 + * @param array $input 输入参数 + * @return RefundOrder 退款单模型 */ public function markRefundSuccess(string $refundNo, array $input = []): RefundOrder { @@ -87,6 +127,10 @@ class RefundService extends BaseService /** * 在当前事务中标记退款成功。 + * + * @param string $refundNo 退款单号 + * @param array $input 输入参数 + * @return RefundOrder 退款单模型 */ public function markRefundSuccessInCurrentTransaction(string $refundNo, array $input = []): RefundOrder { @@ -95,6 +139,10 @@ class RefundService extends BaseService /** * 退款失败。 + * + * @param string $refundNo 退款单号 + * @param array $input 输入参数 + * @return RefundOrder 退款单模型 */ public function markRefundFailed(string $refundNo, array $input = []): RefundOrder { @@ -103,6 +151,10 @@ class RefundService extends BaseService /** * 在当前事务中标记退款失败。 + * + * @param string $refundNo 退款单号 + * @param array $input 输入参数 + * @return RefundOrder 退款单模型 */ public function markRefundFailedInCurrentTransaction(string $refundNo, array $input = []): RefundOrder { diff --git a/app/service/payment/runtime/NotifyService.php b/app/service/payment/runtime/NotifyService.php index d2fc4aa..57fb41c 100644 --- a/app/service/payment/runtime/NotifyService.php +++ b/app/service/payment/runtime/NotifyService.php @@ -16,11 +16,20 @@ use app\repository\ops\log\PayCallbackLogRepository; * 通知服务。 * * 负责渠道通知日志、支付回调日志和商户通知任务的统一管理,核心目标是去重、留痕和可重试。 + * + * @property ChannelNotifyLogRepository $channelNotifyLogRepository 渠道通知日志仓库 + * @property PayCallbackLogRepository $payCallbackLogRepository 支付回调日志仓库 + * @property NotifyTaskRepository $notifyTaskRepository 通知任务仓库 */ class NotifyService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param ChannelNotifyLogRepository $channelNotifyLogRepository 渠道通知日志仓库 + * @param PayCallbackLogRepository $payCallbackLogRepository 支付回调日志仓库 + * @param NotifyTaskRepository $notifyTaskRepository 通知任务仓库 + * @return void */ public function __construct( protected ChannelNotifyLogRepository $channelNotifyLogRepository, @@ -33,6 +42,10 @@ class NotifyService extends BaseService * 记录渠道通知日志。 * * 同一通道、通知类型和业务单号只保留一条重复记录。 + * + * @param array $input 通知数据 + * @return ChannelNotifyLog 渠道通知日志 + * @throws InvalidArgumentException */ public function recordChannelNotify(array $input): ChannelNotifyLog { @@ -44,6 +57,7 @@ class NotifyService extends BaseService throw new \InvalidArgumentException('渠道通知入参不完整'); } + // 同一业务单如果已经记录过相同类型的通知,就直接复用旧日志,避免重复落库。 if ($duplicate = $this->channelNotifyLogRepository->findDuplicate($channelId, $notifyType, $bizNo)) { return $duplicate; } @@ -69,6 +83,10 @@ class NotifyService extends BaseService * 记录支付回调日志。 * * 以支付单号 + 回调类型作为去重依据。 + * + * @param array $input 回调数据 + * @return PayCallbackLog 支付回调日志 + * @throws InvalidArgumentException */ public function recordPayCallback(array $input): PayCallbackLog { @@ -80,6 +98,7 @@ class NotifyService extends BaseService $callbackType = (int) ($input['callback_type'] ?? NotifyConstant::CALLBACK_TYPE_ASYNC); $logs = $this->payCallbackLogRepository->listByPayNo($payNo); foreach ($logs as $log) { + // 同一支付单的同一类型回调只保留一条,后续重复请求直接返回已有日志。 if ((int) $log->callback_type === $callbackType) { return $log; } @@ -100,6 +119,9 @@ class NotifyService extends BaseService * 创建商户通知任务。 * * 通常用于支付成功、退款成功或清算完成后的商户异步通知。 + * + * @param array $input 通知任务数据 + * @return NotifyTask 通知任务 */ public function enqueueMerchantNotify(array $input): NotifyTask { @@ -123,6 +145,11 @@ class NotifyService extends BaseService * 标记商户通知成功。 * * 成功后会刷新最后通知时间和响应内容。 + * + * @param string $notifyNo 通知号 + * @param array $input 附加数据 + * @return NotifyTask 通知任务 + * @throws InvalidArgumentException */ public function markTaskSuccess(string $notifyNo, array $input = []): NotifyTask { @@ -143,6 +170,11 @@ class NotifyService extends BaseService * 标记商户通知失败并计算下次重试时间。 * * 失败后会累计重试次数,并根据退避策略生成下一次重试时间。 + * + * @param string $notifyNo 通知号 + * @param array $input 附加数据 + * @return NotifyTask 通知任务 + * @throws InvalidArgumentException */ public function markTaskFailed(string $notifyNo, array $input = []): NotifyTask { @@ -151,6 +183,7 @@ class NotifyService extends BaseService throw new \InvalidArgumentException('通知任务不存在'); } + // 每次失败都累计一次重试,并根据新的次数重新计算下一次触发时间。 $retryCount = (int) $task->retry_count + 1; $task->status = NotifyConstant::TASK_STATUS_FAILED; $task->retry_count = $retryCount; @@ -164,6 +197,8 @@ class NotifyService extends BaseService /** * 获取待重试任务。 + * + * @return iterable 待重试任务集合 */ public function listRetryableTasks(): iterable { @@ -174,6 +209,9 @@ class NotifyService extends BaseService * 根据重试次数计算下次重试时间。 * * 使用简单的指数退避思路控制重试频率。 + * + * @param int $retryCount 重试次数 + * @return string 下次重试时间 */ private function nextRetryAt(int $retryCount): string { @@ -189,3 +227,8 @@ class NotifyService extends BaseService } } + + + + + diff --git a/app/service/payment/runtime/PaymentPluginFactoryService.php b/app/service/payment/runtime/PaymentPluginFactoryService.php index b361711..fecd982 100644 --- a/app/service/payment/runtime/PaymentPluginFactoryService.php +++ b/app/service/payment/runtime/PaymentPluginFactoryService.php @@ -18,9 +18,23 @@ use app\repository\payment\config\PaymentTypeRepository; * 支付插件工厂服务。 * * 负责解析插件定义、装配配置并实例化插件。 + * + * @property PaymentPluginRepository $paymentPluginRepository 支付插件仓库 + * @property PaymentPluginConfRepository $paymentPluginConfRepository 支付插件配置仓库 + * @property PaymentChannelRepository $paymentChannelRepository 支付渠道仓库 + * @property PaymentTypeRepository $paymentTypeRepository 支付类型仓库 */ class PaymentPluginFactoryService extends BaseService { + /** + * 构造方法。 + * + * @param PaymentPluginRepository $paymentPluginRepository 支付插件仓库 + * @param PaymentPluginConfRepository $paymentPluginConfRepository 支付插件配置仓库 + * @param PaymentChannelRepository $paymentChannelRepository 支付渠道仓库 + * @param PaymentTypeRepository $paymentTypeRepository 支付类型仓库 + * @return void + */ public function __construct( protected PaymentPluginRepository $paymentPluginRepository, protected PaymentPluginConfRepository $paymentPluginConfRepository, @@ -28,6 +42,15 @@ class PaymentPluginFactoryService extends BaseService protected PaymentTypeRepository $paymentTypeRepository ) {} + /** + * 根据渠道创建支付插件实例。 + * + * @param PaymentChannel|int $channel 渠道对象或渠道ID + * @param int|null $payTypeId 支付类型ID + * @param bool $allowDisabled 是否允许已禁用插件 + * @return PaymentInterface&PayPluginInterface 插件实例 + * @throws PaymentException + */ public function createByChannel(PaymentChannel|int $channel, ?int $payTypeId = null, bool $allowDisabled = false): PaymentInterface & PayPluginInterface { $channelModel = $channel instanceof PaymentChannel @@ -39,6 +62,7 @@ class PaymentPluginFactoryService extends BaseService } $plugin = $this->resolvePlugin((string) $channelModel->plugin_code, $allowDisabled); + // 如果外部没有额外指定支付方式,就沿用通道自身绑定的支付方式,确保插件校验口径一致。 $payTypeCode = $this->resolvePayTypeCode((int) ($payTypeId ?: $channelModel->pay_type_id)); if (!$allowDisabled && !$this->pluginSupportsPayType($plugin, $payTypeCode)) { throw new PaymentException('支付插件不支持当前支付方式', 40210, [ @@ -54,13 +78,30 @@ class PaymentPluginFactoryService extends BaseService return $instance; } + /** + * 根据支付订单创建支付插件实例。 + * + * @param PayOrder $payOrder 支付订单 + * @param bool $allowDisabled 是否允许已禁用插件 + * @return PaymentInterface&PayPluginInterface 插件实例 + */ public function createByPayOrder(PayOrder $payOrder, bool $allowDisabled = true): PaymentInterface { + // 支付单已经带了渠道和支付方式快照,这里直接复用渠道工厂逻辑,避免两套实例化口径分叉。 return $this->createByChannel((int) $payOrder->channel_id, (int) $payOrder->pay_type_id, $allowDisabled); } + /** + * 校验渠道是否支持指定支付方式。 + * + * @param PaymentChannel $channel 渠道 + * @param int $payTypeId 支付类型ID + * @return void + * @throws PaymentException + */ public function ensureChannelSupportsPayType(PaymentChannel $channel, int $payTypeId): void { + // 只做能力校验,不实例化插件,便于后台在保存配置前先拦住不兼容组合。 $plugin = $this->resolvePlugin((string) $channel->plugin_code, false); $payTypeCode = $this->resolvePayTypeCode($payTypeId); @@ -73,6 +114,13 @@ class PaymentPluginFactoryService extends BaseService } } + /** + * 获取插件支持的支付方式编码。 + * + * @param string $pluginCode 插件编码 + * @param bool $allowDisabled 是否允许已禁用插件 + * @return array 支付方式编码列表 + */ public function pluginPayTypes(string $pluginCode, bool $allowDisabled = false): array { $plugin = $this->resolvePlugin($pluginCode, $allowDisabled); @@ -80,12 +128,21 @@ class PaymentPluginFactoryService extends BaseService return $this->normalizeCodes($plugin->pay_types ?? []); } + /** + * 组装渠道初始化配置。 + * + * @param PaymentChannel $channel 渠道 + * @param PaymentPlugin $plugin 插件 + * @return array 初始化配置 + * @throws PaymentException + */ private function buildChannelConfig(PaymentChannel $channel, PaymentPlugin $plugin): array { $config = []; $configId = (int) $channel->api_config_id; if ($configId > 0) { + // 渠道绑定了配置时,先把配置表里的内容作为插件初始化基础数据。 $pluginConf = $this->paymentPluginConfRepository->find($configId); if (!$pluginConf) { throw new PaymentException('支付插件配置不存在', 40403, [ @@ -103,10 +160,12 @@ class PaymentPluginFactoryService extends BaseService } $config = (array) ($pluginConf->config ?? []); + // 结算周期信息属于配置层,插件可以直接读取,不必再去查数据库。 $config['settlement_cycle_type'] = (int) ($pluginConf->settlement_cycle_type ?? 1); $config['settlement_cutoff_time'] = (string) ($pluginConf->settlement_cutoff_time ?? '23:59:59'); } + // 以下字段是所有插件都通用的运行时上下文。 $config['plugin_code'] = (string) $plugin->code; $config['plugin_name'] = (string) $plugin->name; $config['channel_id'] = (int) $channel->id; @@ -120,6 +179,13 @@ class PaymentPluginFactoryService extends BaseService return $config; } + /** + * 实例化支付插件。 + * + * @param string $className 类名 + * @return PaymentInterface&PayPluginInterface 插件实例 + * @throws PaymentException + */ private function instantiatePlugin(string $className): PaymentInterface & PayPluginInterface { $className = $this->resolvePluginClassName($className); @@ -131,7 +197,9 @@ class PaymentPluginFactoryService extends BaseService throw new PaymentException('支付插件实现类不存在', 40404, ['class_name' => $className]); } + // 通过容器实例化插件,便于插件内部继续使用依赖注入。 $instance = container_make($className, []); + // 插件必须同时实现动作接口和元信息接口,否则工厂无法正常调用和展示。 if (!$instance instanceof PaymentInterface || !$instance instanceof PayPluginInterface) { throw new PaymentException('支付插件必须同时实现 PaymentInterface 与 PayPluginInterface', 40213, ['class_name' => $className]); } @@ -139,6 +207,12 @@ class PaymentPluginFactoryService extends BaseService return $instance; } + /** + * 规范化插件类名。 + * + * @param string $className 类名 + * @return string 完整类名 + */ private function resolvePluginClassName(string $className): string { $className = trim($className); @@ -153,6 +227,14 @@ class PaymentPluginFactoryService extends BaseService return 'app\\common\\payment\\' . $className; } + /** + * 根据编码解析支付插件。 + * + * @param string $pluginCode 插件编码 + * @param bool $allowDisabled 是否允许已禁用插件 + * @return PaymentPlugin 插件模型 + * @throws PaymentException + */ private function resolvePlugin(string $pluginCode, bool $allowDisabled): PaymentPlugin { /** @var PaymentPlugin|null $plugin */ @@ -168,6 +250,13 @@ class PaymentPluginFactoryService extends BaseService return $plugin; } + /** + * 根据支付类型 ID 解析支付方式编码。 + * + * @param int $payTypeId 支付类型ID + * @return string 支付方式编码 + * @throws PaymentException + */ private function resolvePayTypeCode(int $payTypeId): string { $paymentType = $this->paymentTypeRepository->find($payTypeId); @@ -178,6 +267,13 @@ class PaymentPluginFactoryService extends BaseService return trim((string) $paymentType->code); } + /** + * 判断插件是否支持指定支付方式。 + * + * @param PaymentPlugin $plugin 插件 + * @param string $payTypeCode 支付方式编码 + * @return bool 是否支持 + */ private function pluginSupportsPayType(PaymentPlugin $plugin, string $payTypeCode): bool { $payTypeCode = trim($payTypeCode); @@ -188,6 +284,14 @@ class PaymentPluginFactoryService extends BaseService return in_array($payTypeCode, $this->normalizeCodes($plugin->pay_types ?? []), true); } + /** + * 规范化编码列表。 + * + * 支持数组和 JSON 字符串两种输入形式,输出去重后的纯字符串数组。 + * + * @param array|string|null $codes 原始编码集合 + * @return array 编码列表 + */ private function normalizeCodes(mixed $codes): array { if (is_string($codes)) { diff --git a/app/service/payment/runtime/PaymentPluginManager.php b/app/service/payment/runtime/PaymentPluginManager.php index 83e35d7..6893b6e 100644 --- a/app/service/payment/runtime/PaymentPluginManager.php +++ b/app/service/payment/runtime/PaymentPluginManager.php @@ -9,34 +9,72 @@ use app\model\payment\PayOrder; use app\model\payment\PaymentChannel; /** - * 支付插件门面服务。 + * 支付插件服务。 * - * 对外保留原有调用契约,内部委托给插件工厂服务。 + * @property PaymentPluginFactoryService $factoryService 插件工厂服务 */ class PaymentPluginManager extends BaseService { + /** + * 构造方法。 + * + * @param PaymentPluginFactoryService $factoryService 插件工厂服务 + * @return void + */ public function __construct( protected PaymentPluginFactoryService $factoryService ) { } + /** + * 根据渠道创建支付插件实例。 + * + * @param PaymentChannel|int $channel 渠道对象或渠道ID + * @param int|null $payTypeId 支付类型ID + * @param bool $allowDisabled 是否允许已禁用插件 + * @return PaymentInterface&PayPluginInterface 插件实例 + */ public function createByChannel(PaymentChannel|int $channel, ?int $payTypeId = null, bool $allowDisabled = false): PaymentInterface & PayPluginInterface { return $this->factoryService->createByChannel($channel, $payTypeId, $allowDisabled); } + /** + * 根据支付订单创建支付插件实例。 + * + * @param PayOrder $payOrder 支付订单 + * @param bool $allowDisabled 是否允许已禁用插件 + * @return PaymentInterface&PayPluginInterface 插件实例 + */ public function createByPayOrder(PayOrder $payOrder, bool $allowDisabled = true): PaymentInterface & PayPluginInterface { return $this->factoryService->createByPayOrder($payOrder, $allowDisabled); } + /** + * 校验渠道是否支持指定支付方式。 + * + * @param PaymentChannel $channel 渠道 + * @param int $payTypeId 支付类型ID + * @return void + */ public function ensureChannelSupportsPayType(PaymentChannel $channel, int $payTypeId): void { $this->factoryService->ensureChannelSupportsPayType($channel, $payTypeId); } + /** + * 获取插件支持的支付方式编码。 + * + * @param string $pluginCode 插件编码 + * @param bool $allowDisabled 是否允许已禁用插件 + * @return array 支付方式编码列表 + */ public function pluginPayTypes(string $pluginCode, bool $allowDisabled = false): array { return $this->factoryService->pluginPayTypes($pluginCode, $allowDisabled); } } + + + diff --git a/app/service/payment/runtime/PaymentRouteResolverService.php b/app/service/payment/runtime/PaymentRouteResolverService.php index f9266a8..dbe3609 100644 --- a/app/service/payment/runtime/PaymentRouteResolverService.php +++ b/app/service/payment/runtime/PaymentRouteResolverService.php @@ -23,10 +23,30 @@ use support\Redis; /** * 支付路由解析服务。 * - * 负责商户分组 -> 轮询组 -> 支付通道的编排与选择。 + * 负责商户分组、轮询组、支付类型和支付通道之间的筛选、排序与最终选择。 + * + * @property PaymentPollGroupBindRepository $bindRepository 绑定仓库 + * @property PaymentPollGroupRepository $pollGroupRepository 轮询分组仓库 + * @property PaymentPollGroupChannelRepository $pollGroupChannelRepository 轮询分组渠道仓库 + * @property PaymentChannelRepository $channelRepository 渠道仓库 + * @property ChannelDailyStatRepository $channelDailyStatRepository 渠道日统计仓库 + * @property PaymentPluginRepository $paymentPluginRepository 支付插件仓库 + * @property PaymentTypeRepository $paymentTypeRepository 支付类型仓库 */ class PaymentRouteResolverService extends BaseService { + /** + * 构造方法。 + * + * @param PaymentPollGroupBindRepository $bindRepository 绑定仓库 + * @param PaymentPollGroupRepository $pollGroupRepository 轮询分组仓库 + * @param PaymentPollGroupChannelRepository $pollGroupChannelRepository 轮询分组渠道仓库 + * @param PaymentChannelRepository $channelRepository 渠道仓库 + * @param ChannelDailyStatRepository $channelDailyStatRepository 渠道日统计仓库 + * @param PaymentPluginRepository $paymentPluginRepository 支付插件仓库 + * @param PaymentTypeRepository $paymentTypeRepository 支付类型仓库 + * @return void + */ public function __construct( protected PaymentPollGroupBindRepository $bindRepository, protected PaymentPollGroupRepository $pollGroupRepository, @@ -41,7 +61,17 @@ class PaymentRouteResolverService extends BaseService /** * 按商户分组和支付方式解析路由。 * - * @return array{bind:mixed,poll_group:mixed,candidates:array,selected_channel:array} + * 先读取有效的商户分组绑定和轮询组,再按支付类型、插件支持、金额区间和日限额过滤候选通道, + * 最后依据轮询组策略选出实际使用的通道。 + * + * @param int $merchantGroupId 商户分组ID + * @param int $payTypeId 支付类型ID + * @param int $payAmount 支付金额(分) + * @param array $context 路由上下文,支持传入 `stat_date` 等辅助参数 + * @return array 路由解析结果 + * @throws ValidationException + * @throws ResourceNotFoundException + * @throws BusinessStateException */ public function resolveByMerchantGroup(int $merchantGroupId, int $payTypeId, int $payAmount, array $context = []): array { @@ -74,7 +104,9 @@ class PaymentRouteResolverService extends BaseService ]); } + // 先拿到轮询组下的编排记录,再去批量加载通道、插件和统计数据,避免逐条查库。 $channelIds = $candidateRows->pluck('channel_id')->all(); + // 先一次性拉出通道和插件信息,避免候选过滤过程中频繁查库。 $channels = $this->channelRepository->query() ->whereIn('id', $channelIds) ->where('status', CommonConstant::STATUS_ENABLED) @@ -83,6 +115,7 @@ class PaymentRouteResolverService extends BaseService $pluginCodes = $channels->pluck('plugin_code')->filter()->unique()->values()->all(); $plugins = []; if (!empty($pluginCodes)) { + // 通道会复用同一个插件实现,插件信息也按编码批量加载一次即可。 $plugins = $this->paymentPluginRepository->query() ->whereIn('code', $pluginCodes) ->get() @@ -92,6 +125,7 @@ class PaymentRouteResolverService extends BaseService $paymentType = $this->paymentTypeRepository->find($payTypeId); $payTypeCode = trim((string) ($paymentType->code ?? '')); + // 默认统计日期取当天,路由预览时也可以由外部显式传入历史日期。 $statDate = $context['stat_date'] ?? FormatHelper::timestamp(time(), 'Y-m-d'); $payAmount = (int) $payAmount; $eligible = []; @@ -105,30 +139,36 @@ class PaymentRouteResolverService extends BaseService continue; } + // 先按支付方式收口,避免插件和通道配置不一致时误选。 if ((int) $channel->pay_type_id !== $payTypeId) { continue; } + /** @var \app\model\payment\PaymentPlugin|null $plugin */ $plugin = $plugins[(string) $channel->plugin_code] ?? null; if (!$plugin || (int) $plugin->status !== CommonConstant::STATUS_ENABLED) { continue; } + // 通道还必须被插件明确支持,才允许进入候选集。 $pluginPayTypes = is_array($plugin->pay_types) ? $plugin->pay_types : []; $pluginPayTypes = array_values(array_filter(array_map(static fn ($item) => trim((string) $item), $pluginPayTypes))); if ($payTypeCode === '' || !in_array($payTypeCode, $pluginPayTypes, true)) { continue; } + // 金额区间不匹配的通道直接过滤掉。 if (!$this->isAmountAllowed($channel, $payAmount)) { continue; } + // 日限额和日成功笔数也要同时校验,防止选中已接近上限的通道。 $stat = $this->channelDailyStatRepository->findByChannelAndDate($channelId, $statDate); if (!$this->isDailyLimitAllowed($channel, $payAmount, $statDate, $stat)) { continue; } + // 保留排序和择优所需的权重、默认标记和统计指标。 $eligible[] = [ 'channel' => $channel, 'poll_group_channel' => $row, @@ -143,6 +183,7 @@ class PaymentRouteResolverService extends BaseService } if (empty($eligible)) { + // 所有候选都被过滤后,直接判定通道不可用。 throw new BusinessStateException('支付通道不可用', [ 'poll_group_id' => (int) $pollGroup->id, 'merchant_group_id' => $merchantGroupId, @@ -150,10 +191,12 @@ class PaymentRouteResolverService extends BaseService ]); } + // 按路由模式进行排序,然后再选出最终通道。 $routeMode = (int) $pollGroup->route_mode; $ordered = $this->sortCandidates($eligible, $routeMode); $selected = $this->selectChannel($ordered, $routeMode, (int) $pollGroup->id); + // 返回绑定、轮询组、候选集和最终选中项,供路由预览和实际支付共用。 return [ 'bind' => $bind, 'poll_group' => $pollGroup, @@ -162,6 +205,13 @@ class PaymentRouteResolverService extends BaseService ]; } + /** + * 判断通道是否满足金额区间。 + * + * @param PaymentChannel $channel 渠道 + * @param int $payAmount 支付金额(分) + * @return bool 是否可用 + */ private function isAmountAllowed(PaymentChannel $channel, int $payAmount): bool { if ((int) $channel->min_amount > 0 && $payAmount < (int) $channel->min_amount) { @@ -175,6 +225,15 @@ class PaymentRouteResolverService extends BaseService return true; } + /** + * 判断通道是否满足日限额和日成功笔数。 + * + * @param PaymentChannel $channel 渠道 + * @param int $payAmount 支付金额(分) + * @param string $statDate 统计日期 + * @param object|null $stat 当日统计数据 + * @return bool 是否可用 + */ private function isDailyLimitAllowed(PaymentChannel $channel, int $payAmount, string $statDate, ?object $stat = null): bool { if ((int) $channel->daily_limit_amount <= 0 && (int) $channel->daily_limit_count <= 0) { @@ -196,9 +255,17 @@ class PaymentRouteResolverService extends BaseService return true; } + /** + * 按路由模式整理候选通道顺序。 + * + * @param array $candidates 候选通道列表 + * @param int $routeMode 路由模式 + * @return array 排序后的候选列表 + */ private function sortCandidates(array $candidates, int $routeMode): array { usort($candidates, function (array $left, array $right) use ($routeMode) { + // 第一可用模式下先把默认通道排到前面,其余模式再按排序号和主键做稳定排序。 if ( $routeMode === RouteConstant::ROUTE_MODE_FIRST_AVAILABLE && (int) $left['is_default'] !== (int) $right['is_default'] @@ -216,6 +283,14 @@ class PaymentRouteResolverService extends BaseService return $candidates; } + /** + * 根据路由模式选择最终通道。 + * + * @param array $candidates 候选通道列表 + * @param int $routeMode 路由模式 + * @param int $pollGroupId 轮询分组ID + * @return array 选中的通道候选 + */ private function selectChannel(array $candidates, int $routeMode, int $pollGroupId): array { if (count($candidates) === 1) { @@ -230,6 +305,12 @@ class PaymentRouteResolverService extends BaseService }; } + /** + * 按权重随机选择通道。 + * + * @param array $candidates 候选通道列表 + * @return array 选中的通道候选 + */ private function selectWeightedChannel(array $candidates): array { $totalWeight = array_sum(array_map(static fn (array $item) => max(1, (int) $item['weight']), $candidates)); @@ -245,6 +326,13 @@ class PaymentRouteResolverService extends BaseService return $candidates[0]; } + /** + * 按轮询游标顺序选择通道。 + * + * @param array $candidates 候选通道列表 + * @param int $pollGroupId 轮询分组ID + * @return array 选中的通道候选 + */ private function selectSequentialChannel(array $candidates, int $pollGroupId): array { if ($pollGroupId <= 0) { @@ -252,17 +340,27 @@ class PaymentRouteResolverService extends BaseService } try { + // 用 Redis 维护跨进程共享的轮询游标,避免每个 PHP 进程各选各的。 $cursorKey = sprintf('payment:route:round_robin:%d', $pollGroupId); $cursor = (int) Redis::incr($cursorKey); + // 游标保留一个较长的生命周期,避免 Redis 清理后轮询顺序完全丢失。 Redis::expire($cursorKey, 30 * 86400); + // Redis 自增从 1 开始,这里转成 0 基索引后再对候选集取模。 $index = max(0, ($cursor - 1) % count($candidates)); return $candidates[$index] ?? $candidates[0]; } catch (\Throwable) { + // Redis 不可用时降级成首个候选,保证路由还能继续往下走。 return $candidates[0]; } } + /** + * 优先返回默认通道,否则返回首个候选。 + * + * @param array $candidates 候选通道列表 + * @return array 选中的通道候选 + */ private function selectDefaultChannel(array $candidates): array { foreach ($candidates as $candidate) { diff --git a/app/service/payment/runtime/PaymentRouteService.php b/app/service/payment/runtime/PaymentRouteService.php index 08027c3..cf4a321 100644 --- a/app/service/payment/runtime/PaymentRouteService.php +++ b/app/service/payment/runtime/PaymentRouteService.php @@ -5,12 +5,18 @@ namespace app\service\payment\runtime; use app\common\base\BaseService; /** - * 支付路由门面服务。 + * 支付路由服务。 * - * 对外保留原有调用契约,内部委托给路由解析服务。 + * @property PaymentRouteResolverService $resolverService 路由解析服务 */ class PaymentRouteService extends BaseService { + /** + * 构造方法。 + * + * @param PaymentRouteResolverService $resolverService 路由解析服务 + * @return void + */ public function __construct( protected PaymentRouteResolverService $resolverService ) { @@ -18,9 +24,18 @@ class PaymentRouteService extends BaseService /** * 按商户分组和支付方式解析路由。 + * + * @param int $merchantGroupId 商户分组ID + * @param int $payTypeId 支付类型ID + * @param int $payAmount 支付金额(分) + * @param array $context 路由上下文,例如统计日期、额外筛选条件 + * @return array 路由解析结果 */ public function resolveByMerchantGroup(int $merchantGroupId, int $payTypeId, int $payAmount, array $context = []): array { return $this->resolverService->resolveByMerchantGroup($merchantGroupId, $payTypeId, $payAmount, $context); } } + + + diff --git a/app/service/payment/settlement/SettlementLifecycleService.php b/app/service/payment/settlement/SettlementLifecycleService.php index 9b318c4..8bef5b1 100644 --- a/app/service/payment/settlement/SettlementLifecycleService.php +++ b/app/service/payment/settlement/SettlementLifecycleService.php @@ -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), diff --git a/app/service/payment/settlement/SettlementOrderQueryService.php b/app/service/payment/settlement/SettlementOrderQueryService.php index 8fc3efe..46a73f8 100644 --- a/app/service/payment/settlement/SettlementOrderQueryService.php +++ b/app/service/payment/settlement/SettlementOrderQueryService.php @@ -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>} 详情结构 + * @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> 清算时间线 */ 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) { diff --git a/app/service/payment/settlement/SettlementService.php b/app/service/payment/settlement/SettlementService.php index a4330ee..dd80575 100644 --- a/app/service/payment/settlement/SettlementService.php +++ b/app/service/payment/settlement/SettlementService.php @@ -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); } } + + diff --git a/app/service/payment/trace/TradeTraceReportService.php b/app/service/payment/trace/TradeTraceReportService.php index dedc75f..f85c538 100644 --- a/app/service/payment/trace/TradeTraceReportService.php +++ b/app/service/payment/trace/TradeTraceReportService.php @@ -17,6 +17,14 @@ class TradeTraceReportService extends BaseService { /** * 汇总追踪统计数据。 + * + * @param BizOrder|null $bizOrder 业务订单 + * @param array $payOrders 支付订单列表 + * @param array $refundOrders 退款订单列表 + * @param array $settlementOrders 清算订单列表 + * @param array $accountLedgers 账户流水列表 + * @param array $payCallbacks 支付回调列表 + * @return array 汇总统计 */ public function buildSummary(?BizOrder $bizOrder, array $payOrders, array $refundOrders, array $settlementOrders, array $accountLedgers, array $payCallbacks): array { @@ -36,6 +44,14 @@ class TradeTraceReportService extends BaseService /** * 根据关联记录组装追踪时间线。 + * + * @param BizOrder|null $bizOrder 业务订单 + * @param array $payOrders 支付订单列表 + * @param array $refundOrders 退款订单列表 + * @param array $settlementOrders 清算订单列表 + * @param array $accountLedgers 账户流水列表 + * @param array $payCallbacks 支付回调列表 + * @return array> 时间线事件 */ public function buildTimeline(?BizOrder $bizOrder, array $payOrders, array $refundOrders, array $settlementOrders, array $accountLedgers, array $payCallbacks): array { @@ -213,8 +229,17 @@ class TradeTraceReportService extends BaseService /** * 追加一条时间线事件。 + * + * @param array> $events 事件列表 + * @param int $sortOrder 当前排序号 + * @param string $type 事件类型 + * @param string $sourceNo 事件来源单号 + * @param string $status 事件状态 + * @param \DateTimeInterface|int|string|float|null $at 事件时间 + * @param array $payload 事件载荷 + * @return void */ - private function pushEvent(array &$events, int &$sortOrder, string $type, string $sourceNo, string $status, mixed $at, array $payload = []): void + private function pushEvent(array &$events, int &$sortOrder, string $type, string $sourceNo, string $status, \DateTimeInterface|int|string|float|null $at, array $payload = []): void { $atText = $this->formatDateTime($at); if ($atText === '') { @@ -235,6 +260,10 @@ class TradeTraceReportService extends BaseService /** * 汇总模型列表中的数值字段。 + * + * @param array $items 模型列表 + * @param string $field 字段名 + * @return int 汇总值 */ private function sumBy(array $items, string $field): int { @@ -246,3 +275,6 @@ class TradeTraceReportService extends BaseService return $total; } } + + + diff --git a/app/service/payment/trace/TradeTraceService.php b/app/service/payment/trace/TradeTraceService.php index e2ea5d1..8d4a98f 100644 --- a/app/service/payment/trace/TradeTraceService.php +++ b/app/service/payment/trace/TradeTraceService.php @@ -15,9 +15,29 @@ use app\repository\payment\settlement\SettlementOrderRepository; /** * 跨域交易追踪查询服务。 + * + * @property TradeTraceReportService $tradeTraceReportService 交易追踪报表服务 + * @property BizOrderRepository $bizOrderRepository 业务订单仓库 + * @property PayOrderRepository $payOrderRepository 支付单仓库 + * @property RefundOrderRepository $refundOrderRepository 退款单仓库 + * @property SettlementOrderRepository $settlementOrderRepository 结算订单仓库 + * @property MerchantAccountLedgerRepository $merchantAccountLedgerRepository 商户账户流水仓库 + * @property PayCallbackLogRepository $payCallbackLogRepository 支付回调日志仓库 */ class TradeTraceService extends BaseService { + /** + * 构造方法。 + * + * @param TradeTraceReportService $tradeTraceReportService 交易追踪报表服务 + * @param BizOrderRepository $bizOrderRepository 业务订单仓库 + * @param PayOrderRepository $payOrderRepository 支付订单仓库 + * @param RefundOrderRepository $refundOrderRepository 退款单仓库 + * @param SettlementOrderRepository $settlementOrderRepository 结算订单仓库 + * @param MerchantAccountLedgerRepository $merchantAccountLedgerRepository 商户账户流水仓库 + * @param PayCallbackLogRepository $payCallbackLogRepository 支付回调日志仓库 + * @return void + */ public function __construct( protected TradeTraceReportService $tradeTraceReportService, protected BizOrderRepository $bizOrderRepository, @@ -31,6 +51,10 @@ class TradeTraceService extends BaseService /** * 根据追踪号查询完整交易链路。 + * + * @param string $traceNo 追踪号 + * @return array{trace_no: string, resolved_trace_no: string, matched_by: string, biz_order: BizOrder|null, pay_orders: array, refund_orders: array, settlement_orders: array, account_ledgers: array, pay_callbacks: array, summary: array, timeline: array} 追踪结果 + * @throws ValidationException */ public function queryByTraceNo(string $traceNo): array { @@ -40,6 +64,7 @@ class TradeTraceService extends BaseService } $matchedBy = 'trace_no'; + // 先按追踪号找,找不到再用业务单号兜底,尽量把同一条链路串起来。 $bizOrder = $this->bizOrderRepository->findByTraceNo($traceNo); if (!$bizOrder) { $bizOrder = $this->bizOrderRepository->findByBizNo($traceNo); @@ -58,6 +83,7 @@ class TradeTraceService extends BaseService $settlementOrders = $this->loadSettlementOrders($resolvedTraceNo); if (!$bizOrder) { + // 如果主单没直接查到,就从支付单或退款单反推业务单,保证追踪页尽量有完整链路。 $bizOrder = $this->deriveBizOrder($payOrders, $refundOrders); if ($bizOrder) { $matchedBy = $matchedBy === 'trace_no' ? 'derived' : $matchedBy; @@ -102,9 +128,14 @@ class TradeTraceService extends BaseService /** * 加载支付单列表。 + * + * @param string $traceNo 追踪号 + * @param BizOrder|null $bizOrder 业务订单 + * @return array 支付订单列表 */ private function loadPayOrders(string $traceNo, ?BizOrder $bizOrder): array { + // 优先按 trace_no 查,缺失时再回到 biz_no,兼容早期单据没有完整追踪号的情况。 $items = $this->collectionToArray($this->payOrderRepository->listByTraceNo($traceNo)); if (!empty($items)) { return $items; @@ -119,9 +150,14 @@ class TradeTraceService extends BaseService /** * 加载退款单列表。 + * + * @param string $traceNo 追踪号 + * @param BizOrder|null $bizOrder 业务订单 + * @return array 退款订单列表 */ private function loadRefundOrders(string $traceNo, ?BizOrder $bizOrder): array { + // 退款单同样先按追踪号查,再用业务单号兜底。 $items = $this->collectionToArray($this->refundOrderRepository->listByTraceNo($traceNo)); if (!empty($items)) { return $items; @@ -136,6 +172,9 @@ class TradeTraceService extends BaseService /** * 加载清结算单列表。 + * + * @param string $traceNo 追踪号 + * @return array 清算订单列表 */ private function loadSettlementOrders(string $traceNo): array { @@ -144,11 +183,15 @@ class TradeTraceService extends BaseService /** * 加载支付回调日志列表。 + * + * @param array $payOrders 支付订单列表 + * @return array> 支付回调列表 */ private function loadPayCallbacks(array $payOrders): array { $callbacks = []; foreach ($payOrders as $payOrder) { + // 同一追踪号下可能有多次回调记录,这里把每笔支付单的回调都收进来统一展示。 foreach ($this->payCallbackLogRepository->listByPayNo((string) $payOrder->pay_no) as $callback) { $callbacks[] = [ 'id' => (int) ($callback->id ?? 0), @@ -168,6 +211,7 @@ class TradeTraceService extends BaseService } usort($callbacks, static function ($left, $right): int { + // 新的回调日志排在前面,时间线页面直接从近到远看。 return ($right['id'] ?? 0) <=> ($left['id'] ?? 0); }); @@ -176,18 +220,27 @@ class TradeTraceService extends BaseService /** * 加载资金流水列表。 + * + * @param string $traceNo 追踪号 + * @param BizOrder|null $bizOrder 业务订单 + * @param array $payOrders 支付订单列表 + * @param array $refundOrders 退款订单列表 + * @param array $settlementOrders 清算订单列表 + * @return array 资金流水列表 */ private function loadLedgers(string $traceNo, ?BizOrder $bizOrder, array $payOrders, array $refundOrders, array $settlementOrders): array { $ledgers = []; $seen = []; + // 先合并 trace_no 命中的流水,再补查相关业务单号下的流水并去重。 foreach ($this->collectionToArray($this->merchantAccountLedgerRepository->listByTraceNo($traceNo)) as $ledger) { $seen[(string) $ledger->ledger_no] = true; $ledgers[] = $ledger; } $bizNos = []; + // 资金流水有时挂在业务单号,有时挂在支付单号、退款单号或清算单号上,这里一并纳入兜底查询。 if ($bizOrder) { $bizNos[] = (string) $bizOrder->biz_no; } @@ -208,6 +261,7 @@ class TradeTraceService extends BaseService foreach ($bizNos as $bizNo) { foreach ($this->collectionToArray($this->merchantAccountLedgerRepository->listByBizNo($bizNo)) as $ledger) { $ledgerNo = (string) ($ledger->ledger_no ?? ''); + // 同一笔流水可能同时被 trace_no 和 biz_no 命中,这里只保留一份。 if ($ledgerNo !== '' && isset($seen[$ledgerNo])) { continue; } @@ -227,10 +281,15 @@ class TradeTraceService extends BaseService /** * 从支付单或退款单反推出业务单。 + * + * @param array $payOrders 支付订单列表 + * @param array $refundOrders 退款订单列表 + * @return BizOrder|null 业务订单模型 */ private function deriveBizOrder(array $payOrders, array $refundOrders): ?BizOrder { if (!empty($payOrders)) { + // 先从支付单反推业务单,支付单通常比退款单更早、更稳定。 $bizNo = (string) ($payOrders[0]->biz_no ?? ''); if ($bizNo !== '') { $bizOrder = $this->bizOrderRepository->findByBizNo($bizNo); @@ -241,6 +300,7 @@ class TradeTraceService extends BaseService } if (!empty($refundOrders)) { + // 没有支付单时,再用退款单反推业务单作为兜底。 $bizNo = (string) ($refundOrders[0]->biz_no ?? ''); if ($bizNo !== '') { $bizOrder = $this->bizOrderRepository->findByBizNo($bizNo); @@ -255,6 +315,9 @@ class TradeTraceService extends BaseService /** * 将可迭代对象转换为普通数组。 + * + * @param iterable $items 可迭代对象 + * @return array 普通数组 */ private function collectionToArray(iterable $items): array { diff --git a/app/service/system/access/AdminAuthService.php b/app/service/system/access/AdminAuthService.php index fa97bf2..b19f6c4 100644 --- a/app/service/system/access/AdminAuthService.php +++ b/app/service/system/access/AdminAuthService.php @@ -14,11 +14,18 @@ use app\repository\system\user\AdminUserRepository; * 管理员认证服务。 * * 负责管理员账号校验、JWT 签发、登录态校验和主动注销。 + * + * @property AdminUserRepository $adminUserRepository 管理用户仓库 + * @property JwtTokenManager $jwtTokenManager jwtToken管理器 */ class AdminAuthService extends BaseService { /** - * 构造函数,注入对应依赖。 + * 构造方法。 + * + * @param AdminUserRepository $adminUserRepository 管理用户仓库 + * @param JwtTokenManager $jwtTokenManager jwtToken管理器 + * @return void */ public function __construct( protected AdminUserRepository $adminUserRepository, @@ -28,6 +35,11 @@ class AdminAuthService extends BaseService /** * 校验中间件传入的管理员登录 token。 + * + * @param string $token 登录令牌 + * @param string $ip 请求 IP + * @param string $userAgent 用户代理 + * @return AdminUser|null 管理员模型 */ public function authenticateToken(string $token, string $ip = '', string $userAgent = ''): ?AdminUser { @@ -52,6 +64,13 @@ class AdminAuthService extends BaseService /** * 校验管理员账号密码并签发 JWT。 + * + * @param string $username 管理员账号 + * @param string $password 密码 + * @param string $ip 请求 IP + * @param string $userAgent 用户代理 + * @return array{token: string, expires_in: int, admin: AdminUser} 登录结果 + * @throws ValidationException */ public function authenticateCredentials(string $username, string $password, string $ip = '', string $userAgent = ''): array { @@ -73,6 +92,9 @@ class AdminAuthService extends BaseService /** * 撤销当前管理员登录 token。 + * + * @param string $token 登录令牌 + * @return bool 是否撤销成功 */ public function revokeToken(string $token): bool { @@ -81,6 +103,13 @@ class AdminAuthService extends BaseService /** * 签发新的管理员登录 token。 + * + * @param int $adminId 管理员ID + * @param int $ttlSeconds 过期秒数 + * @param string $ip 请求 IP + * @param string $userAgent 用户代理 + * @return array{token: string, expires_in: int, admin: AdminUser} 登录结果 + * @throws ValidationException */ public function issueToken(int $adminId, int $ttlSeconds = 86400, string $ip = '', string $userAgent = ''): array { @@ -111,3 +140,7 @@ class AdminAuthService extends BaseService ]; } } + + + + diff --git a/app/service/system/config/SystemConfigDefinitionService.php b/app/service/system/config/SystemConfigDefinitionService.php index dce73c2..4b755fe 100644 --- a/app/service/system/config/SystemConfigDefinitionService.php +++ b/app/service/system/config/SystemConfigDefinitionService.php @@ -5,20 +5,35 @@ namespace app\service\system\config; use app\common\base\BaseService; use RuntimeException; +/** + * 系统配置定义解析服务。 + * + * 负责读取 `system_config` 配置并标准化为标签页、规则和默认值结构。 + */ class SystemConfigDefinitionService extends BaseService { protected const VIRTUAL_FIELD_PREFIX = '__'; /** * 已解析的标签页缓存。 + * + * @var array|null */ protected ?array $tabCache = null; /** * 标签页键到定义的缓存。 + * + * @var array|null */ protected ?array $tabMapCache = null; + /** + * 获取全部系统配置标签页。 + * + * @return array 标签页列表 + * @throws RuntimeException + */ public function tabs(): array { if ($this->tabCache !== null) { @@ -82,6 +97,12 @@ class SystemConfigDefinitionService extends BaseService return $this->tabCache; } + /** + * 根据分组代码获取标签页定义。 + * + * @param string $groupCode 分组代码 + * @return array|null 标签页定义 + */ public function tab(string $groupCode): ?array { $groupCode = strtolower(trim($groupCode)); @@ -94,6 +115,13 @@ class SystemConfigDefinitionService extends BaseService return $this->tabMapCache[$groupCode] ?? null; } + /** + * 使用当前值回填标签页规则。 + * + * @param array $tab 标签页定义 + * @param array $values 当前值映射 + * @return array 回填后的规则列表 + */ public function hydrateRules(array $tab, array $values): array { $rules = []; @@ -116,6 +144,13 @@ class SystemConfigDefinitionService extends BaseService return $rules; } + /** + * 从标签页规则中提取表单提交数据。 + * + * @param array $tab 标签页定义 + * @param array $values 当前值映射 + * @return array 表单数据 + */ public function extractFormData(array $tab, array $values): array { $data = []; @@ -135,6 +170,12 @@ class SystemConfigDefinitionService extends BaseService return $data; } + /** + * 生成必填字段校验消息。 + * + * @param array $tab 标签页定义 + * @return array 字段到错误消息的映射 + */ public function requiredFieldMessages(array $tab): array { $messages = []; @@ -163,6 +204,13 @@ class SystemConfigDefinitionService extends BaseService return $messages; } + /** + * 标准化单个标签页定义。 + * + * @param string $groupCode 分组代码 + * @param array $definition 原始定义 + * @return array|null 标准化后的标签页 + */ private function normalizeTab(string $groupCode, array $definition): ?array { $key = strtolower(trim((string) ($definition['key'] ?? $groupCode))); @@ -191,7 +239,13 @@ class SystemConfigDefinitionService extends BaseService ]; } - private function normalizeRule(mixed $rule): ?array + /** + * 标准化单个配置项定义。 + * + * @param array|object|null $rule 原始规则 + * @return array|null 标准化后的规则 + */ + private function normalizeRule(array|object|null $rule): ?array { if (!is_array($rule)) { return null; @@ -235,8 +289,15 @@ class SystemConfigDefinitionService extends BaseService return $normalized; } + /** + * 判断是否为虚拟字段。 + * + * @param string $field 字段名 + * @return bool 是否为虚拟字段 + */ private function isVirtualField(string $field): bool { return str_starts_with($field, self::VIRTUAL_FIELD_PREFIX); } } + diff --git a/app/service/system/config/SystemConfigPageService.php b/app/service/system/config/SystemConfigPageService.php index 1d95e3f..3d818dd 100644 --- a/app/service/system/config/SystemConfigPageService.php +++ b/app/service/system/config/SystemConfigPageService.php @@ -7,14 +7,34 @@ use app\exception\ValidationException; use app\repository\system\config\SystemConfigRepository; use Webman\Event\Event; +/** + * 系统配置页面服务。 + * + * 负责把配置定义和数据库中的配置值组装成页面所需的数据结构。 + * 典型流程是先读取定义,再回填当前值,最后生成表单页数据。 + * + * @property SystemConfigRepository $systemConfigRepository 系统配置仓库 + * @property SystemConfigDefinitionService $systemConfigDefinitionService 系统配置定义解析服务 + */ class SystemConfigPageService extends BaseService { + /** + * 构造方法。 + * + * @param SystemConfigRepository $systemConfigRepository 系统配置仓库 + * @param SystemConfigDefinitionService $systemConfigDefinitionService 系统配置定义解析服务 + */ public function __construct( protected SystemConfigRepository $systemConfigRepository, protected SystemConfigDefinitionService $systemConfigDefinitionService ) { } + /** + * 获取系统配置页面标签页列表。 + * + * @return array 页面所需标签页数据 + */ public function tabs(): array { $tabs = []; @@ -41,6 +61,13 @@ class SystemConfigPageService extends BaseService ]; } + /** + * 查询系统配置页面详情。 + * + * @param string $groupCode 分组代码 + * @return array 页面详情 + * @throws ValidationException + */ public function detail(string $groupCode): array { $tab = $this->systemConfigDefinitionService->tab($groupCode); @@ -80,6 +107,14 @@ class SystemConfigPageService extends BaseService return $tab; } + /** + * 保存系统配置页面。 + * + * @param string $groupCode 分组代码 + * @param array $values 提交值 + * @return array 保存后的页面详情 + * @throws ValidationException + */ public function save(string $groupCode, array $values): array { $tab = $this->systemConfigDefinitionService->tab($groupCode); @@ -119,6 +154,14 @@ class SystemConfigPageService extends BaseService return $this->detail((string) $tab['key']); } + /** + * 校验必填配置项。 + * + * @param array $tab 标签页定义 + * @param array $values 配置值 + * @return void + * @throws ValidationException + */ protected function validateRequiredValues(array $tab, array $values): void { $messages = $this->systemConfigDefinitionService->requiredFieldMessages($tab); @@ -131,7 +174,13 @@ class SystemConfigPageService extends BaseService } } - protected function isEmptyValue(mixed $value): bool + /** + * 判断配置值是否为空。 + * + * @param array|object|bool|float|int|string|null $value 配置值 + * @return bool 是否为空 + */ + protected function isEmptyValue(array|object|bool|float|int|string|null $value): bool { if (is_array($value)) { return $value === []; @@ -144,7 +193,14 @@ class SystemConfigPageService extends BaseService return $value === null || $value === ''; } - protected function stringifyValue(mixed $value): string + /** + * 将配置值转换为可保存字符串。 + * + * @param array|object|bool|float|int|string|null $value 配置值 + * @return string 可保存字符串 + * @throws ValidationException + */ + protected function stringifyValue(array|object|bool|float|int|string|null $value): string { if (is_bool($value)) { return $value ? '1' : '0'; @@ -156,5 +212,6 @@ class SystemConfigPageService extends BaseService return (string) $value; } - } + + diff --git a/app/service/system/config/SystemConfigRuntimeService.php b/app/service/system/config/SystemConfigRuntimeService.php index 7490c89..294660d 100644 --- a/app/service/system/config/SystemConfigRuntimeService.php +++ b/app/service/system/config/SystemConfigRuntimeService.php @@ -7,16 +7,37 @@ use app\repository\system\config\SystemConfigRepository; use support\Cache; use Throwable; +/** + * 系统配置运行时服务。 + * + * 负责读取、缓存和刷新当前进程可直接使用的系统配置值。 + * 读取结果以配置键到字符串值的映射形式提供给业务层。 + * + * @property SystemConfigRepository $systemConfigRepository 系统配置仓库 + * @property SystemConfigDefinitionService $systemConfigDefinitionService 系统配置定义解析服务 + */ class SystemConfigRuntimeService extends BaseService { protected const CACHE_KEY = 'system_config:all'; + /** + * 构造方法。 + * + * @param SystemConfigRepository $systemConfigRepository 系统配置仓库 + * @param SystemConfigDefinitionService $systemConfigDefinitionService 系统配置定义解析服务 + */ public function __construct( protected SystemConfigRepository $systemConfigRepository, protected SystemConfigDefinitionService $systemConfigDefinitionService ) { } + /** + * 获取全部系统配置运行时值。 + * + * @param bool $refresh 是否强制刷新 + * @return array 系统配置值映射 + */ public function all(bool $refresh = false): array { if (!$refresh) { @@ -29,7 +50,15 @@ class SystemConfigRuntimeService extends BaseService return $this->refresh(); } - public function get(string $configKey, mixed $default = '', bool $refresh = false): string + /** + * 根据配置键获取运行时值。 + * + * @param string $configKey 配置键 + * @param string|int|float|bool|null $default 默认值 + * @param bool $refresh 是否强制刷新 + * @return string 配置值字符串 + */ + public function get(string $configKey, string|int|float|bool|null $default = '', bool $refresh = false): string { $configKey = strtolower(trim($configKey)); if ($configKey === '') { @@ -41,6 +70,11 @@ class SystemConfigRuntimeService extends BaseService return (string) ($values[$configKey] ?? $default); } + /** + * 刷新系统配置运行时缓存。 + * + * @return array 最新配置值映射 + */ public function refresh(): array { $values = $this->buildValueMap(); @@ -49,6 +83,11 @@ class SystemConfigRuntimeService extends BaseService return $values; } + /** + * 构建配置值映射。 + * + * @return array 配置值映射 + */ protected function buildValueMap(): array { $values = []; @@ -102,6 +141,11 @@ class SystemConfigRuntimeService extends BaseService return $values; } + /** + * 读取运行时缓存。 + * + * @return array|null 缓存值 + */ protected function readCache(): ?array { try { @@ -113,6 +157,12 @@ class SystemConfigRuntimeService extends BaseService return is_array($raw) ? $raw : null; } + /** + * 写入运行时缓存。 + * + * @param array $values 配置值映射 + * @return void + */ protected function writeCache(array $values): void { try { @@ -122,3 +172,5 @@ class SystemConfigRuntimeService extends BaseService } } } + + diff --git a/app/service/system/user/AdminUserService.php b/app/service/system/user/AdminUserService.php index e0f4939..9ec7de8 100644 --- a/app/service/system/user/AdminUserService.php +++ b/app/service/system/user/AdminUserService.php @@ -12,11 +12,16 @@ use app\repository\system\user\AdminUserRepository; * 管理员用户管理服务。 * * 负责管理员账号的列表查询、新增、修改和删除,以及密码字段的统一处理。 + * + * @property AdminUserRepository $adminUserRepository 管理用户仓库 */ class AdminUserService extends BaseService { /** - * 构造函数,注入管理员用户仓库。 + * 构造方法。 + * + * @param AdminUserRepository $adminUserRepository 管理用户仓库 + * @return void */ public function __construct( protected AdminUserRepository $adminUserRepository @@ -25,6 +30,11 @@ class AdminUserService extends BaseService /** * 分页查询管理员用户。 + * + * @param array $filters 筛选条件 + * @param int $page 页码 + * @param int $pageSize 每页条数 + * @return \Illuminate\Contracts\Pagination\LengthAwarePaginator 分页结果 */ public function paginate(array $filters = [], int $page = 1, int $pageSize = 10) { @@ -80,6 +90,9 @@ class AdminUserService extends BaseService /** * 根据 ID 查询管理员用户。 + * + * @param int $id 管理员用户ID + * @return AdminUser|null 管理员模型 */ public function findById(int $id): ?AdminUser { @@ -88,6 +101,9 @@ class AdminUserService extends BaseService /** * 新增管理员用户。 + * + * @param array $data 写入数据 + * @return AdminUser 新增后的管理员模型 */ public function create(array $data): AdminUser { @@ -96,6 +112,10 @@ class AdminUserService extends BaseService /** * 修改管理员用户。 + * + * @param int $id 管理员用户ID + * @param array $data 写入数据 + * @return AdminUser|null 更新后的管理员模型 */ public function update(int $id, array $data): ?AdminUser { @@ -113,6 +133,9 @@ class AdminUserService extends BaseService /** * 删除管理员用户。 + * + * @param int $id 管理员用户ID + * @return bool 是否删除成功 */ public function delete(int $id): bool { @@ -121,6 +144,11 @@ class AdminUserService extends BaseService /** * 当前管理员资料。 + * + * @param int $adminId 管理员ID + * @param string $adminUsername 管理员账号 + * @return array 当前用户资料 + * @throws ResourceNotFoundException */ public function profile(int $adminId, string $adminUsername = ''): array { @@ -170,6 +198,10 @@ class AdminUserService extends BaseService /** * 统一整理写入字段,并处理密码哈希。 + * + * @param array $data 写入数据 + * @param bool $isUpdate 是否更新 + * @return array 标准化后的数据 */ private function normalizePayload(array $data, bool $isUpdate): array { @@ -194,3 +226,6 @@ class AdminUserService extends BaseService } } + + +