diff --git a/app/common/base/BaseController.php b/app/common/base/BaseController.php index c840604..085a634 100644 --- a/app/common/base/BaseController.php +++ b/app/common/base/BaseController.php @@ -41,6 +41,30 @@ class BaseController ]); } + /** + * 统一分页返回结构 + * + * @param mixed $paginator Laravel/Eloquent paginator + */ + protected function page(mixed $paginator): Response + { + if (!is_object($paginator) || !method_exists($paginator, 'items')) { + return $this->success([ + 'list' => [], + 'total' => 0, + 'page' => 1, + 'size' => 10, + ]); + } + + return $this->success([ + 'list' => $paginator->items(), + 'total' => $paginator->total(), + 'page' => $paginator->currentPage(), + 'size' => $paginator->perPage(), + ]); + } + /** * 获取当前登录用户的 token 载荷 * diff --git a/app/common/base/BaseRepository.php b/app/common/base/BaseRepository.php index 88fc3ef..136f3fe 100644 --- a/app/common/base/BaseRepository.php +++ b/app/common/base/BaseRepository.php @@ -62,11 +62,8 @@ abstract class BaseRepository { $query = $this->model->newQuery(); - foreach ($where as $field => $value) { - if ($value === null || $value === '') { - continue; - } - $query->where($field, $value); + if (!empty($where)) { + $query->where($where); } return $query->paginate($pageSize, $columns, 'page', $page); diff --git a/app/http/admin/controller/MerchantAppController.php b/app/http/admin/controller/MerchantAppController.php new file mode 100644 index 0000000..6b86df5 --- /dev/null +++ b/app/http/admin/controller/MerchantAppController.php @@ -0,0 +1,177 @@ +get('page', 1); + $pageSize = (int)$request->get('page_size', 10); + + $filters = [ + 'merchant_id' => (int)$request->get('merchant_id', 0), + 'status' => $request->get('status', ''), + 'app_id' => trim((string)$request->get('app_id', '')), + 'app_name' => trim((string)$request->get('app_name', '')), + 'api_type' => trim((string)$request->get('api_type', '')), + ]; + + $paginator = $this->merchantAppRepository->searchPaginate($filters, $page, $pageSize); + return $this->page($paginator); + } + + /** + * GET /adminapi/merchant-app/detail?id=1 + */ + public function detail(Request $request) + { + $id = (int)$request->get('id', 0); + if ($id <= 0) { + return $this->fail('应用ID不能为空', 400); + } + + $row = $this->merchantAppRepository->find($id); + if (!$row) { + return $this->fail('应用不存在', 404); + } + + return $this->success($row); + } + + /** + * POST /adminapi/merchant-app/save + */ + public function save(Request $request) + { + $data = $request->post(); + $id = (int)($data['id'] ?? 0); + + $merchantId = (int)($data['merchant_id'] ?? 0); + $apiType = trim((string)($data['api_type'] ?? 'epay')); + $appId = trim((string)($data['app_id'] ?? '')); + $appName = trim((string)($data['app_name'] ?? '')); + $status = (int)($data['status'] ?? 1); + + if ($merchantId <= 0 || $appId === '' || $appName === '') { + return $this->fail('商户、应用ID、应用名称不能为空', 400); + } + + $merchant = $this->merchantRepository->find($merchantId); + if (!$merchant) { + return $this->fail('商户不存在', 404); + } + + if (!in_array($apiType, ['openapi', 'epay', 'custom', 'default'], true)) { + return $this->fail('api_type 不合法', 400); + } + + if ($id > 0) { + $row = $this->merchantAppRepository->find($id); + if (!$row) { + return $this->fail('应用不存在', 404); + } + + // app_id 变更需校验唯一 + if ($row->app_id !== $appId) { + $exists = $this->merchantAppRepository->findAnyByAppId($appId); + if ($exists) { + return $this->fail('应用ID已存在', 400); + } + } + + $update = [ + 'merchant_id' => $merchantId, + 'api_type' => $apiType, + 'app_id' => $appId, + 'app_name' => $appName, + 'status' => $status, + ]; + + // 可选:前端传入 app_secret 才更新 + if (!empty($data['app_secret'])) { + $update['app_secret'] = (string)$data['app_secret']; + } + + $this->merchantAppRepository->updateById($id, $update); + } else { + $exists = $this->merchantAppRepository->findAnyByAppId($appId); + if ($exists) { + return $this->fail('应用ID已存在', 400); + } + + $secret = !empty($data['app_secret']) ? (string)$data['app_secret'] : $this->generateSecret(); + $this->merchantAppRepository->create([ + 'merchant_id' => $merchantId, + 'api_type' => $apiType, + 'app_id' => $appId, + 'app_secret' => $secret, + 'app_name' => $appName, + 'status' => $status, + ]); + } + + return $this->success(null, '保存成功'); + } + + /** + * POST /adminapi/merchant-app/reset-secret + */ + public function resetSecret(Request $request) + { + $id = (int)$request->post('id', 0); + if ($id <= 0) { + return $this->fail('应用ID不能为空', 400); + } + + $row = $this->merchantAppRepository->find($id); + if (!$row) { + return $this->fail('应用不存在', 404); + } + + $secret = $this->generateSecret(); + $this->merchantAppRepository->updateById($id, ['app_secret' => $secret]); + + return $this->success(['app_secret' => $secret], '重置成功'); + } + + /** + * POST /adminapi/merchant-app/toggle + */ + public function toggle(Request $request) + { + $id = (int)$request->post('id', 0); + $status = $request->post('status', null); + + if ($id <= 0 || $status === null) { + return $this->fail('参数错误', 400); + } + + $ok = $this->merchantAppRepository->updateById($id, ['status' => (int)$status]); + return $ok ? $this->success(null, '操作成功') : $this->fail('操作失败', 500); + } + + private function generateSecret(): string + { + $raw = random_bytes(24); + return rtrim(strtr(base64_encode($raw), '+/', '-_'), '='); + } +} + diff --git a/app/http/admin/controller/MerchantController.php b/app/http/admin/controller/MerchantController.php new file mode 100644 index 0000000..d043ef3 --- /dev/null +++ b/app/http/admin/controller/MerchantController.php @@ -0,0 +1,116 @@ +get('page', 1); + $pageSize = (int)$request->get('page_size', 10); + + $filters = [ + 'status' => $request->get('status', ''), + 'merchant_no' => trim((string)$request->get('merchant_no', '')), + 'merchant_name' => trim((string)$request->get('merchant_name', '')), + ]; + + $paginator = $this->merchantRepository->searchPaginate($filters, $page, $pageSize); + return $this->page($paginator); + } + + /** + * GET /adminapi/merchant/detail?id=1 + */ + public function detail(Request $request) + { + $id = (int)$request->get('id', 0); + if ($id <= 0) { + return $this->fail('商户ID不能为空', 400); + } + + $row = $this->merchantRepository->find($id); + if (!$row) { + return $this->fail('商户不存在', 404); + } + + return $this->success($row); + } + + /** + * POST /adminapi/merchant/save + */ + public function save(Request $request) + { + $data = $request->post(); + $id = (int)($data['id'] ?? 0); + + $merchantNo = trim((string)($data['merchant_no'] ?? '')); + $merchantName = trim((string)($data['merchant_name'] ?? '')); + $fundsMode = trim((string)($data['funds_mode'] ?? 'direct')); + $status = (int)($data['status'] ?? 1); + + if ($merchantNo === '' || $merchantName === '') { + return $this->fail('商户号与商户名称不能为空', 400); + } + + if (!in_array($fundsMode, ['direct', 'wallet', 'hybrid'], true)) { + return $this->fail('资金模式不合法', 400); + } + + if ($id > 0) { + $this->merchantRepository->updateById($id, [ + 'merchant_no' => $merchantNo, + 'merchant_name' => $merchantName, + 'funds_mode' => $fundsMode, + 'status' => $status, + ]); + } else { + $exists = $this->merchantRepository->findByMerchantNo($merchantNo); + if ($exists) { + return $this->fail('商户号已存在', 400); + } + + $this->merchantRepository->create([ + 'merchant_no' => $merchantNo, + 'merchant_name' => $merchantName, + 'funds_mode' => $fundsMode, + 'status' => $status, + ]); + } + + return $this->success(null, '保存成功'); + } + + /** + * POST /adminapi/merchant/toggle + */ + public function toggle(Request $request) + { + $id = (int)$request->post('id', 0); + $status = $request->post('status', null); + + if ($id <= 0 || $status === null) { + return $this->fail('参数错误', 400); + } + + $ok = $this->merchantRepository->updateById($id, ['status' => (int)$status]); + return $ok ? $this->success(null, '操作成功') : $this->fail('操作失败', 500); + } +} + diff --git a/app/http/admin/controller/OrderController.php b/app/http/admin/controller/OrderController.php new file mode 100644 index 0000000..110bab1 --- /dev/null +++ b/app/http/admin/controller/OrderController.php @@ -0,0 +1,100 @@ +get('page', 1); + $pageSize = (int)$request->get('page_size', 10); + + $methodCode = trim((string)$request->get('method_code', '')); + $methodId = 0; + if ($methodCode !== '') { + $method = $this->methodRepository->findAnyByCode($methodCode); + $methodId = $method ? (int)$method->id : 0; + } + + $filters = [ + 'merchant_id' => (int)$request->get('merchant_id', 0), + 'merchant_app_id' => (int)$request->get('merchant_app_id', 0), + 'method_id' => $methodId, + 'channel_id' => (int)$request->get('channel_id', 0), + 'status' => $request->get('status', ''), + 'order_id' => trim((string)$request->get('order_id', '')), + 'mch_order_no' => trim((string)$request->get('mch_order_no', '')), + 'created_from' => trim((string)$request->get('created_from', '')), + 'created_to' => trim((string)$request->get('created_to', '')), + ]; + + $paginator = $this->orderRepository->searchPaginate($filters, $page, $pageSize); + return $this->page($paginator); + } + + /** + * GET /adminapi/order/detail?id=1 或 order_id=P... + */ + public function detail(Request $request) + { + $id = (int)$request->get('id', 0); + $orderId = trim((string)$request->get('order_id', '')); + + if ($id > 0) { + $row = $this->orderRepository->find($id); + } elseif ($orderId !== '') { + $row = $this->orderRepository->findByOrderId($orderId); + } else { + return $this->fail('参数错误', 400); + } + + if (!$row) { + return $this->fail('订单不存在', 404); + } + + return $this->success($row); + } + + /** + * POST /adminapi/order/refund + * - order_id: 系统订单号 + * - refund_amount: 退款金额 + */ + public function refund(Request $request) + { + $orderId = trim((string)$request->post('order_id', '')); + $refundAmount = (float)$request->post('refund_amount', 0); + $refundReason = trim((string)$request->post('refund_reason', '')); + + try { + $result = $this->payOrderService->refundOrder([ + 'order_id' => $orderId, + 'refund_amount' => $refundAmount, + 'refund_reason' => $refundReason, + ]); + return $this->success($result, '退款发起成功'); + } catch (\Throwable $e) { + return $this->fail($e->getMessage(), 400); + } + } +} + diff --git a/app/http/admin/controller/PayMethodController.php b/app/http/admin/controller/PayMethodController.php new file mode 100644 index 0000000..4871ecc --- /dev/null +++ b/app/http/admin/controller/PayMethodController.php @@ -0,0 +1,96 @@ +get('page', 1); + $pageSize = (int)$request->get('page_size', 10); + + $filters = [ + 'status' => $request->get('status', ''), + 'method_code' => trim((string)$request->get('method_code', '')), + 'method_name' => trim((string)$request->get('method_name', '')), + ]; + + $paginator = $this->methodRepository->searchPaginate($filters, $page, $pageSize); + return $this->page($paginator); + } + + /** + * POST /adminapi/pay-method/save + */ + public function save(Request $request) + { + $data = $request->post(); + $id = (int)($data['id'] ?? 0); + + $code = trim((string)($data['method_code'] ?? '')); + $name = trim((string)($data['method_name'] ?? '')); + $icon = trim((string)($data['icon'] ?? '')); + $sort = (int)($data['sort'] ?? 0); + $status = (int)($data['status'] ?? 1); + + if ($code === '' || $name === '') { + return $this->fail('支付方式编码与名称不能为空', 400); + } + + if ($id > 0) { + $this->methodRepository->updateById($id, [ + 'method_code' => $code, + 'method_name' => $name, + 'icon' => $icon, + 'sort' => $sort, + 'status' => $status, + ]); + } else { + $exists = $this->methodRepository->findAnyByCode($code); + if ($exists) { + return $this->fail('支付方式编码已存在', 400); + } + $this->methodRepository->create([ + 'method_code' => $code, + 'method_name' => $name, + 'icon' => $icon, + 'sort' => $sort, + 'status' => $status, + ]); + } + + return $this->success(null, '保存成功'); + } + + /** + * POST /adminapi/pay-method/toggle + */ + public function toggle(Request $request) + { + $id = (int)$request->post('id', 0); + $status = $request->post('status', null); + + if ($id <= 0 || $status === null) { + return $this->fail('参数错误', 400); + } + + $ok = $this->methodRepository->updateById($id, ['status' => (int)$status]); + return $ok ? $this->success(null, '操作成功') : $this->fail('操作失败', 500); + } +} + diff --git a/app/http/admin/controller/PayPluginController.php b/app/http/admin/controller/PayPluginController.php new file mode 100644 index 0000000..2480e07 --- /dev/null +++ b/app/http/admin/controller/PayPluginController.php @@ -0,0 +1,85 @@ +get('page', 1); + $pageSize = (int)$request->get('page_size', 10); + + $filters = [ + 'status' => $request->get('status', ''), + 'plugin_code' => trim((string)$request->get('plugin_code', '')), + 'plugin_name' => trim((string)$request->get('plugin_name', '')), + ]; + + $paginator = $this->pluginRepository->searchPaginate($filters, $page, $pageSize); + return $this->page($paginator); + } + + /** + * POST /adminapi/pay-plugin/save + */ + public function save(Request $request) + { + $data = $request->post(); + + $pluginCode = trim((string)($data['plugin_code'] ?? '')); + $pluginName = trim((string)($data['plugin_name'] ?? '')); + $className = trim((string)($data['class_name'] ?? '')); + $status = (int)($data['status'] ?? 1); + + if ($pluginCode === '' || $pluginName === '') { + return $this->fail('插件编码与名称不能为空', 400); + } + + if ($className === '') { + // 默认约定类名 + $className = 'app\\common\\payment\\' . ucfirst($pluginCode) . 'Payment'; + } + + $this->pluginRepository->upsertByCode($pluginCode, [ + 'plugin_name' => $pluginName, + 'class_name' => $className, + 'status' => $status, + ]); + + return $this->success(null, '保存成功'); + } + + /** + * POST /adminapi/pay-plugin/toggle + */ + public function toggle(Request $request) + { + $pluginCode = trim((string)$request->post('plugin_code', '')); + $status = $request->post('status', null); + + if ($pluginCode === '' || $status === null) { + return $this->fail('参数错误', 400); + } + + $ok = $this->pluginRepository->updateStatus($pluginCode, (int)$status); + return $ok ? $this->success(null, '操作成功') : $this->fail('操作失败', 500); + } +} + diff --git a/app/repositories/MerchantAppRepository.php b/app/repositories/MerchantAppRepository.php index 299bd20..3401761 100644 --- a/app/repositories/MerchantAppRepository.php +++ b/app/repositories/MerchantAppRepository.php @@ -37,5 +37,43 @@ class MerchantAppRepository extends BaseRepository ->where('status', 1) ->first(); } + + /** + * 后台按 app_id 查询(不过滤状态) + */ + public function findAnyByAppId(string $appId): ?MerchantApp + { + return $this->model->newQuery() + ->where('app_id', $appId) + ->first(); + } + + /** + * 后台列表:支持筛选与模糊搜索 + */ + public function searchPaginate(array $filters = [], int $page = 1, int $pageSize = 10) + { + $query = $this->model->newQuery(); + + if (!empty($filters['merchant_id'])) { + $query->where('merchant_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_id', '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']); + } + + $query->orderByDesc('id'); + + return $query->paginate($pageSize, ['*'], 'page', $page); + } } diff --git a/app/repositories/MerchantRepository.php b/app/repositories/MerchantRepository.php index 5a147b5..af52d46 100644 --- a/app/repositories/MerchantRepository.php +++ b/app/repositories/MerchantRepository.php @@ -24,5 +24,27 @@ class MerchantRepository extends BaseRepository ->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'] . '%'); + } + + $query->orderByDesc('id'); + + return $query->paginate($pageSize, ['*'], 'page', $page); + } } diff --git a/app/repositories/PaymentMethodRepository.php b/app/repositories/PaymentMethodRepository.php index d7bbd78..0f6bedb 100644 --- a/app/repositories/PaymentMethodRepository.php +++ b/app/repositories/PaymentMethodRepository.php @@ -31,4 +31,36 @@ class PaymentMethodRepository extends BaseRepository ->where('status', 1) ->first(); } + + /** + * 后台按 code 查询(不过滤状态) + */ + public function findAnyByCode(string $methodCode): ?PaymentMethod + { + return $this->model->newQuery() + ->where('method_code', $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('method_code', 'like', '%' . $filters['method_code'] . '%'); + } + if (!empty($filters['method_name'])) { + $query->where('method_name', 'like', '%' . $filters['method_name'] . '%'); + } + + $query->orderBy('sort', 'asc')->orderByDesc('id'); + + return $query->paginate($pageSize, ['*'], 'page', $page); + } } diff --git a/app/repositories/PaymentOrderRepository.php b/app/repositories/PaymentOrderRepository.php index 5f3e3a8..9bef819 100644 --- a/app/repositories/PaymentOrderRepository.php +++ b/app/repositories/PaymentOrderRepository.php @@ -53,4 +53,43 @@ class PaymentOrderRepository extends BaseRepository } return $this->updateById($order->id, $data); } + + /** + * 后台订单列表:支持筛选与模糊搜索 + */ + public function searchPaginate(array $filters = [], int $page = 1, int $pageSize = 10) + { + $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 (($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']); + } + + $query->orderByDesc('id'); + return $query->paginate($pageSize, ['*'], 'page', $page); + } } diff --git a/app/repositories/PaymentPluginRepository.php b/app/repositories/PaymentPluginRepository.php index 6560453..5b5e345 100644 --- a/app/repositories/PaymentPluginRepository.php +++ b/app/repositories/PaymentPluginRepository.php @@ -29,4 +29,61 @@ class PaymentPluginRepository extends BaseRepository ->where('status', 1) ->first(); } + + /** + * 后台按编码查询(不过滤状态) + */ + public function findByCode(string $pluginCode): ?PaymentPlugin + { + return $this->model->newQuery() + ->where('plugin_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('plugin_code', 'like', '%' . $filters['plugin_code'] . '%'); + } + if (!empty($filters['plugin_name'])) { + $query->where('plugin_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('plugin_code', $pluginCode) + ->update($data); + return $this->findByCode($pluginCode) ?: $row; + } + + $data['plugin_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('plugin_code', $pluginCode) + ->update(['status' => $status]); + } } diff --git a/app/routes/admin.php b/app/routes/admin.php index 81cc6b5..67667ea 100644 --- a/app/routes/admin.php +++ b/app/routes/admin.php @@ -14,6 +14,11 @@ use app\http\admin\controller\MenuController; use app\http\admin\controller\SystemController; use app\http\admin\controller\ChannelController; use app\http\admin\controller\PluginController; +use app\http\admin\controller\MerchantController; +use app\http\admin\controller\MerchantAppController; +use app\http\admin\controller\PayMethodController; +use app\http\admin\controller\PayPluginController; +use app\http\admin\controller\OrderController; use app\common\middleware\Cors; use app\http\admin\middleware\AuthMiddleware; @@ -47,5 +52,33 @@ Route::group('/adminapi', function () { Route::get('/channel/plugins', [PluginController::class, 'plugins'])->name('plugins')->setParams(['real_name' => '获取插件列表']); Route::get('/channel/plugin/config-schema', [PluginController::class, 'configSchema'])->name('configSchema')->setParams(['real_name' => '获取插件配置schema']); Route::get('/channel/plugin/products', [PluginController::class, 'products'])->name('products')->setParams(['real_name' => '获取插件产品列表']); + + // 商户管理 + Route::get('/merchant/list', [MerchantController::class, 'list'])->name('merchantList')->setParams(['real_name' => '商户列表']); + Route::get('/merchant/detail', [MerchantController::class, 'detail'])->name('merchantDetail')->setParams(['real_name' => '商户详情']); + Route::post('/merchant/save', [MerchantController::class, 'save'])->name('merchantSave')->setParams(['real_name' => '保存商户']); + Route::post('/merchant/toggle', [MerchantController::class, 'toggle'])->name('merchantToggle')->setParams(['real_name' => '启用禁用商户']); + + // 商户应用管理 + Route::get('/merchant-app/list', [MerchantAppController::class, 'list'])->name('merchantAppList')->setParams(['real_name' => '商户应用列表']); + Route::get('/merchant-app/detail', [MerchantAppController::class, 'detail'])->name('merchantAppDetail')->setParams(['real_name' => '商户应用详情']); + Route::post('/merchant-app/save', [MerchantAppController::class, 'save'])->name('merchantAppSave')->setParams(['real_name' => '保存商户应用']); + Route::post('/merchant-app/reset-secret', [MerchantAppController::class, 'resetSecret'])->name('merchantAppResetSecret')->setParams(['real_name' => '重置应用密钥']); + Route::post('/merchant-app/toggle', [MerchantAppController::class, 'toggle'])->name('merchantAppToggle')->setParams(['real_name' => '启用禁用商户应用']); + + // 支付方式管理 + Route::get('/pay-method/list', [PayMethodController::class, 'list'])->name('payMethodList')->setParams(['real_name' => '支付方式列表']); + Route::post('/pay-method/save', [PayMethodController::class, 'save'])->name('payMethodSave')->setParams(['real_name' => '保存支付方式']); + Route::post('/pay-method/toggle', [PayMethodController::class, 'toggle'])->name('payMethodToggle')->setParams(['real_name' => '启用禁用支付方式']); + + // 插件注册表管理 + Route::get('/pay-plugin/list', [PayPluginController::class, 'list'])->name('payPluginList')->setParams(['real_name' => '支付插件注册表列表']); + Route::post('/pay-plugin/save', [PayPluginController::class, 'save'])->name('payPluginSave')->setParams(['real_name' => '保存支付插件注册表']); + Route::post('/pay-plugin/toggle', [PayPluginController::class, 'toggle'])->name('payPluginToggle')->setParams(['real_name' => '启用禁用支付插件']); + + // 订单管理 + Route::get('/order/list', [OrderController::class, 'list'])->name('orderList')->setParams(['real_name' => '订单列表']); + Route::get('/order/detail', [OrderController::class, 'detail'])->name('orderDetail')->setParams(['real_name' => '订单详情']); + Route::post('/order/refund', [OrderController::class, 'refund'])->name('orderRefund')->setParams(['real_name' => '订单退款']); })->middleware([AuthMiddleware::class]); })->middleware([Cors::class]); \ No newline at end of file diff --git a/composer.json b/composer.json index a8c9ed7..9bb4650 100644 --- a/composer.json +++ b/composer.json @@ -39,7 +39,8 @@ "workerman/crontab": "^1.0", "webman/redis-queue": "^2.1", "firebase/php-jwt": "^7.0", - "webman/validation": "^2.2" + "webman/validation": "^2.2", + "illuminate/pagination": "^12.53" }, "suggest": { "ext-event": "For better performance. " diff --git a/composer.lock b/composer.lock index f6ae6c0..f171767 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c9b20b999efb47e639901ded2ade0fdb", + "content-hash": "49b81bfe43345cf72dbd33c88720b5a2", "packages": [ { "name": "brick/math", @@ -1547,6 +1547,56 @@ }, "time": "2024-07-23T16:31:01+00:00" }, + { + "name": "illuminate/pagination", + "version": "v12.53.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/pagination.git", + "reference": "87e7e3e7b02d6809b1bcd41782e1ca2c6d2a413b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/pagination/zipball/87e7e3e7b02d6809b1bcd41782e1ca2c6d2a413b", + "reference": "87e7e3e7b02d6809b1bcd41782e1ca2c6d2a413b", + "shasum": "" + }, + "require": { + "ext-filter": "*", + "illuminate/collections": "^12.0", + "illuminate/contracts": "^12.0", + "illuminate/support": "^12.0", + "php": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Pagination\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Pagination package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-11-16T14:36:17+00:00" + }, { "name": "illuminate/pipeline", "version": "v12.49.0", diff --git a/config/route.php b/config/route.php index ae8fa2b..34dd30e 100644 --- a/config/route.php +++ b/config/route.php @@ -17,6 +17,12 @@ use Webman\Route; use support\Response; use support\Request; +// 管理后台路由 +require_once base_path() . '/app/routes/admin.php'; + +// API 路由 +require_once base_path() . '/app/routes/api.php'; + // 匹配所有options路由(CORS 预检请求) Route::options('[{path:.+}]', function (Request $request){ $response = response('', 204); @@ -27,13 +33,6 @@ Route::options('[{path:.+}]', function (Request $request){ 'Access-Control-Allow-Headers' => $request->header('access-control-request-headers', '*'), ]); }); - -// 管理后台路由 -require_once base_path() . '/app/routes/admin.php'; - -// API 路由 -require_once base_path() . '/app/routes/api.php'; - /** * 关闭默认路由 */ diff --git a/database/dev_seed.sql b/database/dev_seed.sql new file mode 100644 index 0000000..0fa2304 --- /dev/null +++ b/database/dev_seed.sql @@ -0,0 +1,51 @@ +-- 开发环境初始化数据(可重复执行) +SET NAMES utf8mb4; + +-- 1) 管理员(若已有则跳过) +INSERT INTO `ma_admin` (`user_name`, `password`, `nick_name`, `status`, `created_at`) +VALUES ('admin', NULL, '超级管理员', 1, NOW()) +ON DUPLICATE KEY UPDATE + `nick_name` = VALUES(`nick_name`), + `status` = VALUES(`status`); + +-- 2) 商户 +INSERT INTO `ma_merchant` (`merchant_no`, `merchant_name`, `funds_mode`, `status`, `created_at`, `updated_at`) +VALUES ('M001', '测试商户', 'direct', 1, NOW(), NOW()) +ON DUPLICATE KEY UPDATE + `merchant_name` = VALUES(`merchant_name`), + `funds_mode` = VALUES(`funds_mode`), + `status` = VALUES(`status`), + `updated_at` = NOW(); + +-- 3) 商户应用(pid=app_id 约定:这里 app_id 使用纯数字字符串,方便易支付测试) +INSERT INTO `ma_merchant_app` (`merchant_id`, `api_type`, `app_id`, `app_secret`, `app_name`, `status`, `created_at`, `updated_at`) +SELECT m.id, 'epay', '1001', 'dev_secret_1001', '测试应用-易支付', 1, NOW(), NOW() +FROM `ma_merchant` m +WHERE m.merchant_no = 'M001' +ON DUPLICATE KEY UPDATE + `app_secret` = VALUES(`app_secret`), + `app_name` = VALUES(`app_name`), + `status` = VALUES(`status`), + `updated_at` = NOW(); + +-- 4) 支付方式 +INSERT INTO `ma_pay_method` (`method_code`, `method_name`, `icon`, `sort`, `status`, `created_at`, `updated_at`) VALUES +('alipay', '支付宝', '', 1, 1, NOW(), NOW()), +('wechat', '微信支付', '', 2, 1, NOW(), NOW()), +('unionpay','云闪付', '', 3, 1, NOW(), NOW()) +ON DUPLICATE KEY UPDATE + `method_name` = VALUES(`method_name`), + `icon` = VALUES(`icon`), + `sort` = VALUES(`sort`), + `status` = VALUES(`status`), + `updated_at` = NOW(); + +-- 5) 插件注册表(按项目约定:app\\common\\payment\\{Code}Payment) +INSERT INTO `ma_pay_plugin` (`plugin_code`, `plugin_name`, `class_name`, `status`, `created_at`, `updated_at`) +VALUES ('lakala', '拉卡拉(示例)', 'app\\\\common\\\\payment\\\\LakalaPayment', 1, NOW(), NOW()) +ON DUPLICATE KEY UPDATE + `plugin_name` = VALUES(`plugin_name`), + `class_name` = VALUES(`class_name`), + `status` = VALUES(`status`), + `updated_at` = NOW(); +