更新统一使用 PHPDoc + PSR-19 标准注释

This commit is contained in:
技术老胡
2026-04-21 08:38:59 +08:00
parent dcd58e24ce
commit 9a16a88640
252 changed files with 9218 additions and 659 deletions

View File

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

View File

@@ -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);
}
}

View File

@@ -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<string, mixed> $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<string> 支付方式代码数组,如 ['alipay', 'wechat']
* @return array 支持的支付方式编码
*/
public function getEnabledPayTypes(): array
{
@@ -127,9 +151,9 @@ abstract class BasePayment implements PayPluginInterface
}
/**
* 获取插件支持的转账方式列表
* 获取插件支持的转账方式列表
*
* @return array<string> 转账方式代码数组
* @return array 支持的转账方式编码
*/
public function getEnabledTransferTypes(): array
{
@@ -137,9 +161,9 @@ abstract class BasePayment implements PayPluginInterface
}
/**
* 获取插件配置表单结构(用于后台配置界面)
* 获取插件配置表单结构(用于后台配置界面)
*
* @return array<string, mixed> 表单字段定义数组
* @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<string, mixed> $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
}
}
}

View File

@@ -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);
}
}

View File

@@ -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)
{

View File

@@ -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<int, string> 签名类型名称表
*/
public static function signTypeMap(): array
{
return [
@@ -29,6 +61,11 @@ final class AuthConstant
];
}
/**
* 获取登录域映射。
*
* @return array<int, string> 登录域名称表
*/
public static function guardMap(): array
{
return [
@@ -37,3 +74,7 @@ final class AuthConstant
];
}
}

View File

@@ -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<int, string> 状态名称表
*/
public static function statusMap(): array
{
return [
@@ -21,6 +40,11 @@ final class CommonConstant
];
}
/**
* 获取是否名称映射。
*
* @return array<int, string> 是否名称表
*/
public static function yesNoMap(): array
{
return [
@@ -29,3 +53,7 @@ final class CommonConstant
];
}
}

View File

@@ -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<int, string> 来源名称表
*/
public static function sourceTypeMap(): array
{
return [
@@ -50,6 +101,11 @@ final class FileConstant
];
}
/**
* 获取文件可见性映射。
*
* @return array<int, string> 可见性名称表
*/
public static function visibilityMap(): array
{
return [
@@ -58,6 +114,11 @@ final class FileConstant
];
}
/**
* 获取文件场景映射。
*
* @return array<int, string> 场景名称表
*/
public static function sceneMap(): array
{
return [
@@ -68,6 +129,11 @@ final class FileConstant
];
}
/**
* 获取存储引擎映射。
*
* @return array<int, string> 存储引擎名称表
*/
public static function storageEngineMap(): array
{
return [
@@ -78,6 +144,11 @@ final class FileConstant
];
}
/**
* 获取可选存储引擎映射。
*
* @return array<int, string> 可选存储引擎名称表
*/
public static function selectableStorageEngineMap(): array
{
return [
@@ -87,6 +158,11 @@ final class FileConstant
];
}
/**
* 获取图片扩展名白名单。
*
* @return array<string, bool> 白名单集合
*/
public static function imageExtensionMap(): array
{
return [
@@ -100,6 +176,11 @@ final class FileConstant
];
}
/**
* 获取证书扩展名白名单。
*
* @return array<string, bool> 白名单集合
*/
public static function certificateExtensionMap(): array
{
return [
@@ -112,6 +193,11 @@ final class FileConstant
];
}
/**
* 获取文本扩展名白名单。
*
* @return array<string, bool> 白名单集合
*/
public static function textExtensionMap(): array
{
return [
@@ -128,8 +214,17 @@ final class FileConstant
];
}
/**
* 获取默认允许上传的扩展名。
*
* @return array<int, string> 扩展名列表
*/
public static function defaultAllowedExtensions(): array
{
return array_keys(self::imageExtensionMap() + self::certificateExtensionMap() + self::textExtensionMap());
}
}

View File

@@ -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<int, string> 业务类型名称表
*/
public static function bizTypeMap(): array
{
return [
@@ -34,6 +39,11 @@ final class LedgerConstant
];
}
/**
* 获取事件类型映射。
*
* @return array<int, string> 事件类型名称表
*/
public static function eventTypeMap(): array
{
return [
@@ -44,6 +54,11 @@ final class LedgerConstant
];
}
/**
* 获取流水方向映射。
*
* @return array<int, string> 方向名称表
*/
public static function directionMap(): array
{
return [
@@ -52,3 +67,7 @@ final class LedgerConstant
];
}
}

View File

@@ -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<int, string> 商户类型名称表
*/
public static function typeMap(): array
{
return [
@@ -24,6 +51,11 @@ final class MerchantConstant
];
}
/**
* 获取商户风险等级映射。
*
* @return array<int, string> 风险等级名称表
*/
public static function riskLevelMap(): array
{
return [
@@ -33,3 +65,7 @@ final class MerchantConstant
];
}
}

View File

@@ -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<int, string> 通知类型名称表
*/
public static function notifyTypeMap(): array
{
return [
@@ -33,6 +38,11 @@ final class NotifyConstant
];
}
/**
* 获取回调类型映射。
*
* @return array<int, string> 回调类型名称表
*/
public static function callbackTypeMap(): array
{
return [
@@ -41,6 +51,11 @@ final class NotifyConstant
];
}
/**
* 验证状态Map
*
* @return array<int, string> 验证状态名称表
*/
public static function verifyStatusMap(): array
{
return [
@@ -50,6 +65,11 @@ final class NotifyConstant
];
}
/**
* 处理状态Map
*
* @return array<int, string> 处理状态名称表
*/
public static function processStatusMap(): array
{
return [
@@ -59,6 +79,11 @@ final class NotifyConstant
];
}
/**
* 获取任务状态映射。
*
* @return array<int, string> 任务状态名称表
*/
public static function taskStatusMap(): array
{
return [
@@ -68,3 +93,7 @@ final class NotifyConstant
];
}
}

View File

@@ -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<int, string> 通道类型名称表
*/
public static function channelTypeMap(): array
{
return [
@@ -36,6 +57,11 @@ final class RouteConstant
];
}
/**
* 获取通道模式名称映射。
*
* @return array<int, string> 通道模式名称表
*/
public static function channelModeMap(): array
{
return [
@@ -44,6 +70,11 @@ final class RouteConstant
];
}
/**
* 获取路由模式名称映射。
*
* @return array<int, string> 路由模式名称表
*/
public static function routeModeMap(): array
{
return [
@@ -53,3 +84,7 @@ final class RouteConstant
];
}
}

View File

@@ -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<int, string> 清算周期名称表
*/
public static function settlementCycleMap(): array
{
return [
@@ -47,6 +52,11 @@ final class TradeConstant
];
}
/**
* 获取订单状态映射。
*
* @return array<int, string> 订单状态名称表
*/
public static function orderStatusMap(): array
{
return [
@@ -59,6 +69,11 @@ final class TradeConstant
];
}
/**
* 获取手续费状态映射。
*
* @return array<int, string> 手续费状态名称表
*/
public static function feeStatusMap(): array
{
return [
@@ -69,6 +84,11 @@ final class TradeConstant
];
}
/**
* 获取清算状态映射。
*
* @return array<int, string> 清算状态名称表
*/
public static function settlementStatusMap(): array
{
return [
@@ -79,6 +99,11 @@ final class TradeConstant
];
}
/**
* 获取退款状态映射。
*
* @return array<int, string> 退款状态名称表
*/
public static function refundStatusMap(): array
{
return [
@@ -90,6 +115,11 @@ final class TradeConstant
];
}
/**
* 获取可变更的订单状态列表。
*
* @return array<int, int> 状态列表
*/
public static function orderMutableStatuses(): array
{
return [
@@ -98,6 +128,11 @@ final class TradeConstant
];
}
/**
* 获取订单终态列表。
*
* @return array<int, int> 状态列表
*/
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<int, int> 状态列表
*/
public static function refundMutableStatuses(): array
{
return [
@@ -122,6 +168,11 @@ final class TradeConstant
];
}
/**
* 获取退款终态列表。
*
* @return array<int, int> 状态列表
*/
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<int, int> 状态列表
*/
public static function settlementMutableStatuses(): array
{
return [
@@ -142,6 +204,11 @@ final class TradeConstant
];
}
/**
* 获取清算终态列表。
*
* @return array<int, int> 状态列表
*/
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);
}
}

View File

@@ -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<string, mixed> $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<string>
* @return array 支持的支付方式编码
*/
public function getEnabledPayTypes(): array;
/** 插件声明支持的转账方式编码 */
/**
* 获取插件声明支持的转账方式编码。
*
* @return array 支持的转账方式编码
*/
public function getEnabledTransferTypes(): array;
/**
* 插件配置结构(用于后台渲染表单/校验)
* 获取插件配置结构
*
* @return array<string, mixed>
* @return array 配置结构
*/
public function getConfigSchema(): array;
}

View File

@@ -9,91 +9,74 @@ use support\Request;
use support\Response;
/**
* 支付插件接口
* 支付动作能力接口
*
* 所有支付插件必须实现此接口,用于统一下单、订单查询、关闭、退款回调通知等核心能力
* 建议继承 BasePayment 获 HTTP 请求等通用能力,再实现本接口。
* 所有支付插件必须实现此接口,用于统一下单、订单查询、关闭、退款回调通知。
* 建议继承 `BasePayment` HTTP 请求等通用能力,再实现本接口。
*
* 异常约定:实现类在业务失败时应抛出 PaymentException便于统一处理和返回。
* 异常约定:实现类在业务失败时应抛出 `PaymentException`,便于统一处理和返回。
*/
interface PaymentInterface
{
// ==================== 订单操作 ====================
/**
* 统一下单
* 发起支付下单
*
* @param array<string, mixed> $order 订单数据,通常包含:
* - order_id: 系统支付单号,建议直接使用 pay_no
* - amount: 金额(分)
* - subject: 商品标题
* - body: 商品描述
* - callback_url: 第三方异步回调地址(回调到本系统)
* - return_url: 支付完成跳转地址
* @return array<string, mixed> 支付参数,需包含 pay_params、chan_order_no、chan_trade_no
* @throws PaymentException 下单失败、渠道异常、参数错误等
* @param array $order 订单
* @return array 下单结果
*/
public function pay(array $order): array;
/**
* 查询订单状态
* 查询订单状态
*
* @param array<string, mixed> $order 订单数据(至少含 order_id、chan_order_no
* @return array<string, mixed> 订单状态信息,通常包含:
* - status: 订单状态
* - chan_trade_no: 渠道交易号
* - pay_amount: 实付金额
* @throws PaymentException 查询失败、渠道异常等
* @param array $order 订单参数
* @return array 查询结果
*/
public function query(array $order): array;
/**
* 关闭订单
* 关闭订单
*
* @param array<string, mixed> $order 订单数据(至少含 order_id、chan_order_no
* @return array<string, mixed> 关闭结果,通常包含 success、msg
* @throws PaymentException 关闭失败、渠道异常等
* @param array $order 订单参数
* @return array 关闭结果
*/
public function close(array $order): array;
/**
* 申请退款
* 申请退款
*
* @param array<string, mixed> $order 退款数据,通常包含:
* - order_id: 原支付单号
* - chan_order_no: 渠道订单号
* - refund_amount: 退款金额(分)
* - refund_no: 退款单号
* @return array<string, mixed> 退款结果,通常包含 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<string, mixed> 解析结果,通常包含:
* - 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;
}

View File

@@ -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
]);
}
}

View File

@@ -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手机网站、appAPP 支付、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<string, mixed>
*/
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<string, mixed> 参数数组
*/
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';
}
}

View File

@@ -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<int, string> $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;
}
}

View File

@@ -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<string, mixed> $claims JWT 声明
* @param array<string, mixed> $sessionData 会话数据
* @param int|null $ttlSeconds 过期秒数
* @return array{token:string,expires_in:int,jti:string,claims:array<string, mixed>,session:array<string, mixed>} 签发结果
*/
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<string, mixed>,session:array<string, mixed>}|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<string, mixed>|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<string, mixed>|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<string, mixed> $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<string, mixed> 认证配置
* @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
));
}
}