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
}
}
+
+
+