重构初始化

This commit is contained in:
技术老胡
2026-04-15 11:45:46 +08:00
parent 72d72d735b
commit 7612026773
381 changed files with 28287 additions and 14717 deletions

View File

@@ -1,31 +0,0 @@
<?php
namespace app\repositories;
use app\common\base\BaseRepository;
use app\models\Admin;
/**
* 管理员仓储
*/
class AdminRepository extends BaseRepository
{
public function __construct()
{
parent::__construct(new Admin());
}
/**
* 根据用户名查询
*/
public function findByUserName(string $userName): ?Admin
{
/** @var Admin|null $admin */
$admin = $this->model
->newQuery()
->where('user_name', $userName)
->first();
return $admin;
}
}

View File

@@ -1,43 +0,0 @@
<?php
namespace app\repositories;
use app\common\base\BaseRepository;
use app\models\CallbackInbox;
use Illuminate\Database\QueryException;
/**
* 回调幂等收件箱仓储
*/
class CallbackInboxRepository extends BaseRepository
{
public function __construct()
{
parent::__construct(new CallbackInbox());
}
public function findByEventKey(string $eventKey): ?CallbackInbox
{
return $this->model->newQuery()
->where('event_key', $eventKey)
->first();
}
/**
* 尝试创建幂等事件,重复时返回 false。
*/
public function createIfAbsent(array $data): bool
{
try {
$this->model->newQuery()->create($data);
return true;
} catch (QueryException $e) {
// 1062: duplicate entry
if ((int)($e->errorInfo[1] ?? 0) === 1062) {
return false;
}
throw $e;
}
}
}

View File

@@ -1,99 +0,0 @@
<?php
namespace app\repositories;
use app\common\base\BaseRepository;
use app\models\MerchantApp;
/**
* 商户应用仓储
*/
class MerchantAppRepository extends BaseRepository
{
public function __construct()
{
parent::__construct(new MerchantApp());
}
/**
* 根据AppId查询
*/
public function findByAppId(string $appId): ?MerchantApp
{
return $this->model->newQuery()
->where('app_code', $appId)
->where('status', 1)
->first();
}
/**
* 根据商户ID和应用ID查询
*/
public function findByMerchantAndApp(int $merchantId, int $appId): ?MerchantApp
{
return $this->model->newQuery()
->where('mer_id', $merchantId)
->where('id', $appId)
->where('status', 1)
->first();
}
/**
* 根据商户ID和应用IDapp_id查询
*/
public function findByMerchantAndAppId(int $merchantId, string $appId): ?MerchantApp
{
return $this->model->newQuery()
->where('mer_id', $merchantId)
->where('app_code', $appId)
->first();
}
/**
* 后台按 app_id 查询(不过滤状态)
*/
public function findAnyByAppId(string $appId): ?MerchantApp
{
return $this->model->newQuery()
->where('app_code', $appId)
->first();
}
/**
* 后台列表:支持筛选与模糊搜索
*/
public function searchPaginate(array $filters = [], int $page = 1, int $pageSize = 10)
{
$query = $this->model->newQuery();
if (!empty($filters['merchant_id'])) {
$query->where('mer_id', (int)$filters['merchant_id']);
}
if (($filters['status'] ?? '') !== '' && $filters['status'] !== null) {
$query->where('status', (int)$filters['status']);
}
if (!empty($filters['app_id'])) {
$query->where('app_code', 'like', '%' . $filters['app_id'] . '%');
}
if (!empty($filters['app_name'])) {
$query->where('app_name', 'like', '%' . $filters['app_name'] . '%');
}
if (!empty($filters['api_type'])) {
$query->where('api_type', (string)$filters['api_type']);
}
if (!empty($filters['package_code'])) {
$query->where('package_code', (string)$filters['package_code']);
}
if (($filters['notify_enabled'] ?? '') !== '' && $filters['notify_enabled'] !== null) {
$query->where('notify_enabled', (int)$filters['notify_enabled']);
}
if (!empty($filters['callback_mode'])) {
$query->where('callback_mode', (string)$filters['callback_mode']);
}
$query->orderByDesc('id');
return $query->paginate($pageSize, ['*'], 'page', $page);
}
}

View File

@@ -1,56 +0,0 @@
<?php
namespace app\repositories;
use app\common\base\BaseRepository;
use app\models\Merchant;
/**
* 商户仓储
*/
class MerchantRepository extends BaseRepository
{
public function __construct()
{
parent::__construct(new Merchant());
}
/**
* 根据商户号查询
*/
public function findByMerchantNo(string $merchantNo): ?Merchant
{
return $this->model->newQuery()
->where('merchant_no', $merchantNo)
->first();
}
/**
* 后台列表:支持模糊搜索
*/
public function searchPaginate(array $filters = [], int $page = 1, int $pageSize = 10)
{
$query = $this->model->newQuery();
if (($filters['status'] ?? '') !== '' && $filters['status'] !== null) {
$query->where('status', (int)$filters['status']);
}
if (!empty($filters['merchant_no'])) {
$query->where('merchant_no', 'like', '%' . $filters['merchant_no'] . '%');
}
if (!empty($filters['merchant_name'])) {
$query->where('merchant_name', 'like', '%' . $filters['merchant_name'] . '%');
}
if (!empty($filters['email'])) {
$query->where('email', 'like', '%' . $filters['email'] . '%');
}
if (isset($filters['balance']) && $filters['balance'] !== '') {
$query->where('balance', (string)$filters['balance']);
}
$query->orderByDesc('id');
return $query->paginate($pageSize, ['*'], 'page', $page);
}
}

View File

@@ -1,22 +0,0 @@
<?php
namespace app\repositories;
use app\common\base\BaseRepository;
use app\models\PaymentCallbackLog;
/**
* 支付回调日志仓储
*/
class PaymentCallbackLogRepository extends BaseRepository
{
public function __construct()
{
parent::__construct(new PaymentCallbackLog());
}
public function createLog(array $data): PaymentCallbackLog
{
return $this->model->newQuery()->create($data);
}
}

View File

@@ -1,77 +0,0 @@
<?php
namespace app\repositories;
use app\common\base\BaseRepository;
use app\models\PaymentChannel;
class PaymentChannelRepository extends BaseRepository
{
public function __construct()
{
parent::__construct(new PaymentChannel());
}
public function findAvailableChannel(int $merchantId, int $merchantAppId, int $methodId): ?PaymentChannel
{
return $this->model->newQuery()
->where('mer_id', $merchantId)
->where('app_id', $merchantAppId)
->where('pay_type_id', $methodId)
->where('status', 1)
->orderBy('sort', 'asc')
->first();
}
public function findByChanCode(string $chanCode): ?PaymentChannel
{
return $this->model->newQuery()
->where('chan_code', $chanCode)
->first();
}
public function searchPaginate(array $filters = [], int $page = 1, int $pageSize = 10)
{
$query = $this->buildSearchQuery($filters);
$query->orderBy('sort', 'asc')->orderByDesc('id');
return $query->paginate($pageSize, ['*'], 'page', $page);
}
public function searchList(array $filters = [])
{
return $this->buildSearchQuery($filters)
->orderBy('sort', 'asc')
->orderByDesc('id')
->get();
}
private function buildSearchQuery(array $filters = [])
{
$query = $this->model->newQuery();
if (!empty($filters['merchant_id'])) {
$query->where('mer_id', (int)$filters['merchant_id']);
}
if (!empty($filters['merchant_app_id'])) {
$query->where('app_id', (int)$filters['merchant_app_id']);
}
if (!empty($filters['method_id'])) {
$query->where('pay_type_id', (int)$filters['method_id']);
}
if (($filters['status'] ?? '') !== '' && $filters['status'] !== null) {
$query->where('status', (int)$filters['status']);
}
if (!empty($filters['plugin_code'])) {
$query->where('plugin_code', (string)$filters['plugin_code']);
}
if (!empty($filters['chan_code'])) {
$query->where('chan_code', 'like', '%' . $filters['chan_code'] . '%');
}
if (!empty($filters['chan_name'])) {
$query->where('chan_name', 'like', '%' . $filters['chan_name'] . '%');
}
return $query;
}
}

View File

@@ -1,66 +0,0 @@
<?php
namespace app\repositories;
use app\common\base\BaseRepository;
use app\models\PaymentMethod;
/**
* 支付方式仓储
*/
class PaymentMethodRepository extends BaseRepository
{
public function __construct()
{
parent::__construct(new PaymentMethod());
}
public function getAllEnabled(): array
{
return $this->model->newQuery()
->where('status', 1)
->orderBy('sort', 'asc')
->get()
->toArray();
}
public function findByCode(string $methodCode): ?PaymentMethod
{
return $this->model->newQuery()
->where('type', $methodCode)
->where('status', 1)
->first();
}
/**
* 后台按 code 查询(不过滤状态)
*/
public function findAnyByCode(string $methodCode): ?PaymentMethod
{
return $this->model->newQuery()
->where('type', $methodCode)
->first();
}
/**
* 后台列表:支持筛选与排序
*/
public function searchPaginate(array $filters = [], int $page = 1, int $pageSize = 10)
{
$query = $this->model->newQuery();
if (($filters['status'] ?? '') !== '' && $filters['status'] !== null) {
$query->where('status', (int)$filters['status']);
}
if (!empty($filters['method_code'])) {
$query->where('type', 'like', '%' . $filters['method_code'] . '%');
}
if (!empty($filters['method_name'])) {
$query->where('name', 'like', '%' . $filters['method_name'] . '%');
}
$query->orderBy('sort', 'asc')->orderByDesc('id');
return $query->paginate($pageSize, ['*'], 'page', $page);
}
}

View File

@@ -1,34 +0,0 @@
<?php
namespace app\repositories;
use app\common\base\BaseRepository;
use app\models\PaymentNotifyTask;
/**
* 商户通知任务仓储
*/
class PaymentNotifyTaskRepository extends BaseRepository
{
public function __construct()
{
parent::__construct(new PaymentNotifyTask());
}
public function findByOrderId(string $orderId): ?PaymentNotifyTask
{
return $this->model->newQuery()
->where('order_id', $orderId)
->first();
}
public function getPendingRetryTasks(int $limit = 100): array
{
return $this->model->newQuery()
->where('status', PaymentNotifyTask::STATUS_PENDING)
->where('next_retry_at', '<=', date('Y-m-d H:i:s'))
->limit($limit)
->get()
->toArray();
}
}

View File

@@ -1,197 +0,0 @@
<?php
namespace app\repositories;
use app\common\base\BaseRepository;
use app\models\PaymentOrder;
/**
* 支付订单仓储
*/
class PaymentOrderRepository extends BaseRepository
{
public const STATUS_PENDING = 0;
public const STATUS_SUCCESS = 1;
public const STATUS_FAIL = 2;
public const STATUS_CLOSED = 3;
public function __construct()
{
parent::__construct(new PaymentOrder());
}
public function findByOrderId(string $orderId): ?PaymentOrder
{
return $this->model->newQuery()
->where('order_id', $orderId)
->first();
}
/**
* 根据商户订单号查询(幂等校验)
*/
public function findByMchNo(int $merchantId, int $merchantAppId, string $mchOrderNo): ?PaymentOrder
{
return $this->model->newQuery()
->where('merchant_id', $merchantId)
->where('merchant_app_id', $merchantAppId)
->where('mch_order_no', $mchOrderNo)
->first();
}
public function updateStatus(string $orderId, int $status, array $extra = []): bool
{
$data = array_merge(['status' => $status], $extra);
$order = $this->findByOrderId($orderId);
return $order ? $this->updateById($order->id, $data) : false;
}
public function updateChannelInfo(string $orderId, string $chanOrderNo, string $chanTradeNo = ''): bool
{
$order = $this->findByOrderId($orderId);
if (!$order) {
return false;
}
$data = ['chan_order_no' => $chanOrderNo];
if ($chanTradeNo !== '') {
$data['chan_trade_no'] = $chanTradeNo;
}
return $this->updateById($order->id, $data);
}
/**
* 后台订单列表:支持筛选与模糊搜索
*/
public function searchPaginate(array $filters = [], int $page = 1, int $pageSize = 10)
{
$query = $this->buildSearchQuery($filters);
$query->orderByDesc('id');
return $query->paginate($pageSize, ['*'], 'page', $page);
}
public function searchList(array $filters = [], int $limit = 5000)
{
return $this->buildSearchQuery($filters)
->orderByDesc('id')
->limit($limit)
->get();
}
public function aggregateByChannel(array $channelIds = [], array $filters = []): array
{
if (empty($channelIds)) {
return [];
}
$query = $this->model->newQuery()
->selectRaw(
'channel_id,
COUNT(*) AS total_orders,
SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) AS success_orders,
SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) AS pending_orders,
SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) AS fail_orders,
SUM(CASE WHEN status = ? THEN 1 ELSE 0 END) AS closed_orders,
COALESCE(SUM(amount), 0) AS total_amount,
COALESCE(SUM(CASE WHEN status = ? THEN amount ELSE 0 END), 0) AS success_amount,
SUM(CASE WHEN DATE(created_at) = CURDATE() THEN 1 ELSE 0 END) AS today_orders,
COALESCE(SUM(CASE WHEN DATE(created_at) = CURDATE() THEN amount ELSE 0 END), 0) AS today_amount,
SUM(CASE WHEN DATE(created_at) = CURDATE() AND status = ? THEN 1 ELSE 0 END) AS today_success_orders,
COALESCE(SUM(CASE WHEN DATE(created_at) = CURDATE() AND status = ? THEN amount ELSE 0 END), 0) AS today_success_amount,
MAX(created_at) AS last_order_at,
MAX(CASE WHEN status = ? THEN pay_at ELSE NULL END) AS last_success_at',
[
self::STATUS_SUCCESS,
self::STATUS_PENDING,
self::STATUS_FAIL,
self::STATUS_CLOSED,
self::STATUS_SUCCESS,
self::STATUS_SUCCESS,
self::STATUS_SUCCESS,
self::STATUS_SUCCESS,
]
)
->whereIn('channel_id', $channelIds);
if (!empty($filters['merchant_id'])) {
$query->where('merchant_id', (int)$filters['merchant_id']);
}
if (!empty($filters['merchant_app_id'])) {
$query->where('merchant_app_id', (int)$filters['merchant_app_id']);
}
if (!empty($filters['method_id'])) {
$query->where('method_id', (int)$filters['method_id']);
}
if (!empty($filters['created_from'])) {
$query->where('created_at', '>=', $filters['created_from']);
}
if (!empty($filters['created_to'])) {
$query->where('created_at', '<=', $filters['created_to']);
}
$rows = $query->groupBy('channel_id')->get();
$result = [];
foreach ($rows as $row) {
$result[(int)$row->channel_id] = $row->toArray();
}
return $result;
}
private function buildSearchQuery(array $filters = [])
{
$query = $this->model->newQuery();
if (!empty($filters['merchant_id'])) {
$query->where('merchant_id', (int)$filters['merchant_id']);
}
if (!empty($filters['merchant_app_id'])) {
$query->where('merchant_app_id', (int)$filters['merchant_app_id']);
}
if (!empty($filters['method_id'])) {
$query->where('method_id', (int)$filters['method_id']);
}
if (!empty($filters['channel_id'])) {
$query->where('channel_id', (int)$filters['channel_id']);
}
if (!empty($filters['route_policy_name'])) {
$query->whereRaw(
"JSON_UNQUOTE(JSON_EXTRACT(extra, '$.routing.policy.policy_name')) like ?",
['%' . $filters['route_policy_name'] . '%']
);
}
if (($filters['route_state'] ?? '') !== '' && $filters['route_state'] !== null) {
$routeState = (string)$filters['route_state'];
if ($routeState === 'error') {
$query->whereRaw("JSON_EXTRACT(extra, '$.route_error') IS NOT NULL");
} elseif ($routeState === 'none') {
$query->whereRaw("JSON_EXTRACT(extra, '$.route_error') IS NULL");
$query->whereRaw(
"(JSON_UNQUOTE(JSON_EXTRACT(extra, '$.routing.source')) IS NULL OR JSON_UNQUOTE(JSON_EXTRACT(extra, '$.routing.source')) = '')"
);
} else {
$query->whereRaw(
"JSON_EXTRACT(extra, '$.route_error') IS NULL AND JSON_UNQUOTE(JSON_EXTRACT(extra, '$.routing.source')) = ?",
[$routeState]
);
}
}
if (($filters['status'] ?? '') !== '' && $filters['status'] !== null) {
$query->where('status', (int)$filters['status']);
}
if (!empty($filters['order_id'])) {
$query->where('order_id', 'like', '%' . $filters['order_id'] . '%');
}
if (!empty($filters['mch_order_no'])) {
$query->where('mch_order_no', 'like', '%' . $filters['mch_order_no'] . '%');
}
if (!empty($filters['created_from'])) {
$query->where('created_at', '>=', $filters['created_from']);
}
if (!empty($filters['created_to'])) {
$query->where('created_at', '<=', $filters['created_to']);
}
return $query;
}
}

View File

@@ -1,96 +0,0 @@
<?php
namespace app\repositories;
use app\common\base\BaseRepository;
use app\models\PaymentPlugin;
/**
* 支付插件仓储
*/
class PaymentPluginRepository extends BaseRepository
{
public function __construct()
{
parent::__construct(new PaymentPlugin());
}
public function getActivePlugins()
{
return $this->model->newQuery()
->where('status', 1)
->get([
'code',
'name',
'class_name',
'pay_types',
'transfer_types',
'config_schema',
]);
}
public function findActiveByCode(string $pluginCode): ?PaymentPlugin
{
return $this->model->newQuery()
->where('code', $pluginCode)
->where('status', 1)
->first();
}
/**
* 后台按编码查询(不过滤状态)
*/
public function findByCode(string $pluginCode): ?PaymentPlugin
{
return $this->model->newQuery()
->where('code', $pluginCode)
->first();
}
/**
* 后台列表:支持筛选与模糊搜索
*/
public function searchPaginate(array $filters = [], int $page = 1, int $pageSize = 10)
{
$query = $this->model->newQuery();
if (($filters['status'] ?? '') !== '' && $filters['status'] !== null) {
$query->where('status', (int)$filters['status']);
}
if (!empty($filters['plugin_code'])) {
$query->where('code', 'like', '%' . $filters['plugin_code'] . '%');
}
if (!empty($filters['plugin_name'])) {
$query->where('name', 'like', '%' . $filters['plugin_name'] . '%');
}
$query->orderByDesc('created_at');
return $query->paginate($pageSize, ['*'], 'page', $page);
}
/**
* 后台保存:存在则更新,不存在则创建
*/
public function upsertByCode(string $pluginCode, array $data): PaymentPlugin
{
$row = $this->findByCode($pluginCode);
if ($row) {
$this->model->newQuery()
->where('code', $pluginCode)
->update($data);
return $this->findByCode($pluginCode) ?: $row;
}
$data['code'] = $pluginCode;
/** @var PaymentPlugin $created */
$created = $this->create($data);
return $created;
}
public function updateStatus(string $pluginCode, int $status): bool
{
return (bool)$this->model->newQuery()
->where('code', $pluginCode)
->update(['status' => $status]);
}
}

View File

@@ -1,157 +0,0 @@
<?php
namespace app\repositories;
use app\common\base\BaseRepository;
use app\models\SystemConfig;
use support\Cache;
use Webman\Event\Event;
/**
* 系统配置仓储
*/
class SystemConfigRepository extends BaseRepository
{
/**
* 缓存键:全部系统配置
*/
private const CACHE_KEY_ALL_CONFIG = 'system_config_all';
public function __construct()
{
parent::__construct(new SystemConfig());
}
/**
* 从数据库加载所有配置到缓存
*
* @return array<string, string>
*/
protected function loadAllToCache(): array
{
// 优先从 webman/cache 获取
$cached = Cache::get(self::CACHE_KEY_ALL_CONFIG);
if (is_array($cached)) {
return $cached;
}
// 缓存不存在时从数据库加载
$configs = $this->model
->newQuery()
->get(['config_key', 'config_value']);
$result = [];
foreach ($configs as $config) {
$result[$config->config_key] = $config->config_value;
}
// 写入缓存(不过期,除非显式清理)
Cache::set(self::CACHE_KEY_ALL_CONFIG, $result);
return $result;
}
/**
* 清空缓存(供事件调用)
*/
public static function clearCache(): void
{
Cache::delete(self::CACHE_KEY_ALL_CONFIG);
}
/**
* 重新从数据库加载缓存(供事件调用)
*/
public function reloadCache(): void
{
Cache::delete(self::CACHE_KEY_ALL_CONFIG);
$this->loadAllToCache();
}
/**
* 根据配置键名查询配置值
*
* @param string $configKey
* @return string|null
*/
public function getValueByKey(string $configKey): ?string
{
$all = $this->loadAllToCache();
return $all[$configKey] ?? null;
}
/**
* 根据配置键名数组批量查询配置
*
* @param array $configKeys
* @return array 返回 ['config_key' => 'config_value'] 格式的数组
*/
public function getValuesByKeys(array $configKeys): array
{
if (empty($configKeys)) {
return [];
}
$all = $this->loadAllToCache();
$result = [];
foreach ($configKeys as $key) {
if (array_key_exists($key, $all)) {
$result[$key] = $all[$key];
}
}
return $result;
}
/**
* 根据配置键名更新或创建配置
*
* @param string $configKey
* @param string $configValue
* @return bool
*/
public function updateOrCreate(string $configKey, string $configValue): bool
{
$this->model
->newQuery()
->updateOrCreate(
['config_key' => $configKey],
['config_value' => $configValue]
);
// 通过事件通知重新加载缓存
Event::emit('system.config.updated', null);
return true;
}
/**
* 批量更新或创建配置
*
* @param array $configs 格式:['config_key' => 'config_value']
* @return bool
*/
public function batchUpdateOrCreate(array $configs): bool
{
if (empty($configs)) {
return true;
}
foreach ($configs as $configKey => $configValue) {
$this->model
->newQuery()
->updateOrCreate(
['config_key' => $configKey],
['config_value' => $configValue]
);
}
// 批量更新后只触发一次事件,通知重新加载缓存
Event::emit('system.config.updated', null);
return true;
}
}