mirror of
https://gitee.com/technical-laohu/mpay_v2_webman.git
synced 2026-05-17 06:20:25 +08:00
feat: 完善支付通道和收款监听链路
新增 ChannelNotifyPayloadInterface 等支付插件通知契约,规范 pay_no 定位和插件返回校验。 新增微信、支付宝、收钱吧、Postar 个人收款插件适配,支持余额识别与备注识别。 新增 receipt-watcher 后端进程、Redis 队列 job 和平台事件监听,覆盖收款流水通知、商户通知、退款派发、转账派发与清算完成。 补齐个人收款监听相关系统配置、仓储、服务费冻结明细、订单后台操作和通道测试能力。 重构支付单创建、回调、费用、风控、结算和通道统计链路,统一状态流转与幂等处理。
This commit is contained in:
73
app/queue/support/AbstractQueueJob.php
Normal file
73
app/queue/support/AbstractQueueJob.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
namespace app\queue\support;
|
||||
|
||||
use app\common\interface\QueueJobInterface;
|
||||
use RuntimeException;
|
||||
use support\Log;
|
||||
use Throwable;
|
||||
|
||||
/**
|
||||
* 队列任务基类。
|
||||
*
|
||||
* 提供消息字段校验、布尔值解析和统一失败日志,具体业务 Job 只需要实现 handle。
|
||||
*/
|
||||
abstract class AbstractQueueJob implements QueueJobInterface
|
||||
{
|
||||
/**
|
||||
* 默认消费失败处理。
|
||||
*
|
||||
* @param Throwable $exception 异常
|
||||
* @param array<string, mixed> $package 原始队列包
|
||||
* @return void
|
||||
*/
|
||||
public function failed(Throwable $exception, array $package): void
|
||||
{
|
||||
Log::warning(sprintf(
|
||||
'[%s] 消费失败 queue=%s attempts=%s error=%s',
|
||||
$this->logName(),
|
||||
(string) ($package['queue'] ?? ''),
|
||||
(string) ($package['attempts'] ?? ''),
|
||||
$exception->getMessage()
|
||||
));
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取必填字符串字段。
|
||||
*
|
||||
* @param array<string, mixed> $data 队列消息
|
||||
* @param string $key 字段名
|
||||
* @param string $label 字段显示名
|
||||
* @return string 字段值
|
||||
*/
|
||||
protected function requireString(array $data, string $key, string $label = ''): string
|
||||
{
|
||||
$value = trim((string) ($data[$key] ?? ''));
|
||||
if ($value === '') {
|
||||
throw new RuntimeException(($label !== '' ? $label : $key) . ' 不能为空');
|
||||
}
|
||||
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* 解析布尔字段。
|
||||
*
|
||||
* @param mixed $value 字段值
|
||||
* @return bool 布尔结果
|
||||
*/
|
||||
protected function boolValue(mixed $value): bool
|
||||
{
|
||||
return filter_var($value, FILTER_VALIDATE_BOOL);
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取日志名称。
|
||||
*
|
||||
* @return string 日志名称
|
||||
*/
|
||||
protected function logName(): string
|
||||
{
|
||||
return static::class;
|
||||
}
|
||||
}
|
||||
82
app/queue/support/AbstractRedisConsumer.php
Normal file
82
app/queue/support/AbstractRedisConsumer.php
Normal file
@@ -0,0 +1,82 @@
|
||||
<?php
|
||||
|
||||
namespace app\queue\support;
|
||||
|
||||
use app\common\interface\QueueJobInterface;
|
||||
use RuntimeException;
|
||||
use support\Log;
|
||||
use Throwable;
|
||||
use Webman\RedisQueue\Consumer;
|
||||
|
||||
/**
|
||||
* Redis 队列消费者基类。
|
||||
*
|
||||
* 统一把 webman/redis-queue 的 Consumer 协议适配到业务 Job,具体消费者只需要声明
|
||||
* 队列名和 Job 类名。
|
||||
*/
|
||||
abstract class AbstractRedisConsumer implements Consumer
|
||||
{
|
||||
/**
|
||||
* Redis 队列连接名。
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
public $connection = 'default';
|
||||
|
||||
/**
|
||||
* 获取任务类名。
|
||||
*
|
||||
* @return class-string<QueueJobInterface> 任务类名
|
||||
*/
|
||||
abstract protected function jobClass(): string;
|
||||
|
||||
/**
|
||||
* 消费队列消息。
|
||||
*
|
||||
* @param mixed $data 队列消息
|
||||
* @return void
|
||||
*/
|
||||
public function consume($data): void
|
||||
{
|
||||
$this->job()->handle(is_array($data) ? $data : []);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理消费失败。
|
||||
*
|
||||
* @param Throwable $exception 异常
|
||||
* @param array<string, mixed> $package 原始队列包
|
||||
* @return void
|
||||
*/
|
||||
public function onConsumeFailure(Throwable $exception, array $package): void
|
||||
{
|
||||
try {
|
||||
$this->job()->failed($exception, $package);
|
||||
} catch (Throwable $failureException) {
|
||||
Log::warning(sprintf(
|
||||
'[QueueConsumer] 失败处理异常 job=%s queue=%s error=%s failure_error=%s',
|
||||
$this->jobClass(),
|
||||
(string) ($package['queue'] ?? ''),
|
||||
$exception->getMessage(),
|
||||
$failureException->getMessage()
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 从容器中获取任务实例。
|
||||
*
|
||||
* Job 不保存单次消费的可变状态,使用 container_get 复用实例,避免每条消息重复构造依赖。
|
||||
*
|
||||
* @return QueueJobInterface 任务实例
|
||||
*/
|
||||
private function job(): QueueJobInterface
|
||||
{
|
||||
$job = container_get($this->jobClass());
|
||||
if (!$job instanceof QueueJobInterface) {
|
||||
throw new RuntimeException('队列任务必须实现 QueueJobInterface:' . $this->jobClass());
|
||||
}
|
||||
|
||||
return $job;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user