mirror of
https://gitee.com/technical-laohu/mpay_v2_webman.git
synced 2026-04-29 21:44:30 +08:00
更新统一使用 PHPDoc + PSR-19 标准注释
This commit is contained in:
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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<string, mixed>, user: array<string, mixed>, roles: array<int, string>, permissions: array<int, string>} 商户资料
|
||||
*/
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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()),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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),
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
{
|
||||
|
||||
@@ -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
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user