first commit

This commit is contained in:
wushumin
2026-04-16 11:17:18 +08:00
commit 5b9c398e68
98 changed files with 8701 additions and 0 deletions

View File

@@ -0,0 +1,134 @@
<?php
namespace app\admin\controller;
use support\Request;
use app\common\model\AdminUser;
use Illuminate\Database\Capsule\Manager as DB;
class AdminUserController
{
public function list(Request $request)
{
$page = (int)$request->get('page', 1);
$limit = (int)$request->get('limit', 15);
$query = AdminUser::with('roles');
if ($username = $request->get('username')) {
$query->where('username', 'like', "%{$username}%");
}
$total = $query->count();
$list = $query->offset(($page - 1) * $limit)
->limit($limit)
->orderBy('id', 'desc')
->get();
return jsonResponse([
'total' => $total,
'list' => $list
]);
}
public function create(Request $request)
{
$username = trim($request->post('username', ''));
$password = $request->post('password', '');
$roleIds = $request->post('role_ids', []);
if (!$username || !$password) {
return jsonResponse(null, '用户名和密码必填', 400);
}
if (AdminUser::where('username', $username)->exists()) {
return jsonResponse(null, '用户名已存在', 400);
}
DB::beginTransaction();
try {
$admin = AdminUser::create([
'username' => $username,
'password_hash' => password_hash($password, PASSWORD_DEFAULT),
'status' => (int)$request->post('status', 1),
'is_super' => (int)$request->post('is_super', 0),
]);
if (!empty($roleIds)) {
$admin->roles()->sync($roleIds);
}
DB::commit();
return jsonResponse(null, '创建成功');
} catch (\Exception $e) {
DB::rollBack();
return jsonResponse(null, '创建失败: ' . $e->getMessage(), 500);
}
}
public function update(Request $request)
{
$id = (int)$request->post('id');
$admin = AdminUser::find($id);
if (!$admin) {
return jsonResponse(null, '用户不存在', 404);
}
$username = trim($request->post('username', ''));
if ($username && $username !== $admin->username) {
if (AdminUser::where('username', $username)->exists()) {
return jsonResponse(null, '用户名已存在', 400);
}
$admin->username = $username;
}
$password = $request->post('password');
if ($password) {
$admin->password_hash = password_hash($password, PASSWORD_DEFAULT);
}
if ($request->post('status') !== null) {
$admin->status = (int)$request->post('status');
}
if ($request->post('is_super') !== null) {
$admin->is_super = (int)$request->post('is_super');
}
$roleIds = $request->post('role_ids');
DB::beginTransaction();
try {
$admin->save();
if (is_array($roleIds)) {
$admin->roles()->sync($roleIds);
}
DB::commit();
return jsonResponse(null, '更新成功');
} catch (\Exception $e) {
DB::rollBack();
return jsonResponse(null, '更新失败: ' . $e->getMessage(), 500);
}
}
public function delete(Request $request)
{
$id = (int)$request->post('id');
if ($id === 1) {
return jsonResponse(null, '超级管理员不可删除', 403);
}
$admin = AdminUser::find($id);
if (!$admin) {
return jsonResponse(null, '用户不存在', 404);
}
DB::beginTransaction();
try {
$admin->roles()->detach();
$admin->delete();
DB::commit();
return jsonResponse(null, '删除成功');
} catch (\Exception $e) {
DB::rollBack();
return jsonResponse(null, '删除失败: ' . $e->getMessage(), 500);
}
}
}

View File

@@ -0,0 +1,64 @@
<?php
namespace app\admin\controller;
use support\Request;
use app\common\model\AdminUser;
use app\common\service\AuthService;
class AuthController
{
public function login(Request $request)
{
$username = trim((string)$request->post('username', ''));
$password = (string)$request->post('password', '');
if ($username === '' || $password === '') {
return jsonResponse(null, '参数错误', 400);
}
$admin = AdminUser::where('username', $username)->first();
if (!$admin) {
return jsonResponse(null, '账号或密码错误', 401);
}
if (intval($admin->status) !== 1) {
return jsonResponse(null, '账号已禁用', 403);
}
if (!password_verify($password, $admin->password_hash)) {
return jsonResponse(null, '账号或密码错误', 401);
}
$token = AuthService::issueAdminToken($admin);
return jsonResponse([
'token' => $token,
'admin' => $admin
], '登录成功');
}
public function me(Request $request)
{
$admin = $request->admin;
$permissions = [];
if (intval($admin->is_super) === 1) {
$permissions = ['*'];
} else {
$admin->loadMissing(['roles.permissions']);
$map = [];
foreach ($admin->roles as $role) {
foreach ($role->permissions as $permission) {
$map[$permission->code] = true;
}
}
$permissions = array_keys($map);
}
return jsonResponse([
'admin' => $admin,
'permissions' => $permissions
]);
}
public function logout(Request $request)
{
AuthService::revokeAdminToken($request->token ?? null);
return jsonResponse(null, '已退出登录');
}
}

View File

@@ -0,0 +1,26 @@
<?php
namespace app\admin\controller;
use support\Request;
use app\common\model\User;
use app\common\model\Order;
use app\common\model\Report;
class DashboardController
{
public function stat(Request $request)
{
$today = date('Y-m-d');
$stat = [
'today_orders' => Order::where('created_at', '>=', $today . ' 00:00:00')->count(),
'wait_receive_orders' => Order::whereIn('status', ['shipping', 'wait_receive'])->count(),
'inspecting_orders' => Order::where('status', 'inspecting')->count(),
'today_reports' => Report::where('created_at', '>=', $today . ' 00:00:00')->count(),
'total_users' => User::count(),
'total_amount' => Order::where('status', 'finished')->sum('total_price') ?? 0
];
return jsonResponse($stat);
}
}

View File

@@ -0,0 +1,85 @@
<?php
namespace app\admin\controller;
use support\Request;
use app\common\model\Order;
class OrderController
{
public function list(Request $request)
{
$status = $request->get('status', 'all');
$page = max(1, intval($request->get('page', 1)));
$pageSize = min(50, max(1, intval($request->get('page_size', 10))));
$query = Order::query();
if ($status !== 'all') {
$query->where('status', $status);
}
if ($orderNo = $request->get('order_no')) {
$query->where('order_no', 'like', "%{$orderNo}%");
}
$total = $query->count();
$items = $query->orderBy('id', 'desc')
->offset(($page - 1) * $pageSize)
->limit($pageSize)
->get();
return jsonResponse([
'items' => $items,
'total' => $total,
'page' => $page,
'page_size' => $pageSize
]);
}
public function detail(Request $request)
{
$id = (int)$request->get('id');
$order = Order::with(['logs'])->find($id);
if (!$order) {
return jsonResponse(null, '订单不存在', 404);
}
return jsonResponse($order);
}
public function receive(Request $request)
{
$id = (int)$request->post('id');
$order = Order::find($id);
if (!$order) {
return jsonResponse(null, '订单不存在', 404);
}
try {
\app\common\service\OrderFlowService::adminReceive($order, $request->admin->id);
return jsonResponse(null, '确认收件成功,已进入鉴定状态');
} catch (\Exception $e) {
return jsonResponse(null, $e->getMessage(), 400);
}
}
public function returnShip(Request $request)
{
$id = (int)$request->post('id');
$expressCompany = trim((string)$request->post('express_company', ''));
$expressNo = trim((string)$request->post('express_no', ''));
$order = Order::find($id);
if (!$order) {
return jsonResponse(null, '订单不存在', 404);
}
try {
\app\common\service\OrderFlowService::adminReturnShip($order, $request->admin->id, $expressCompany, $expressNo);
return jsonResponse(null, '回寄信息已提交');
} catch (\Exception $e) {
return jsonResponse(null, $e->getMessage(), 400);
}
}
}

View File

@@ -0,0 +1,94 @@
<?php
namespace app\admin\controller;
use support\Request;
use app\common\model\Permission;
class PermissionController
{
public function list(Request $request)
{
// 返回树形结构或者普通列表
$permissions = Permission::orderBy('sort', 'asc')->get();
return jsonResponse($permissions);
}
public function create(Request $request)
{
$name = trim($request->post('name', ''));
$code = trim($request->post('code', ''));
$parent_id = (int)$request->post('parent_id', 0);
$type = (int)$request->post('type', 1); // 1菜单 2按钮
$sort = (int)$request->post('sort', 0);
if (!$name || !$code) {
return jsonResponse(null, '名称和代码必填', 400);
}
if (Permission::where('code', $code)->exists()) {
return jsonResponse(null, '代码已存在', 400);
}
$permission = Permission::create([
'name' => $name,
'code' => $code,
'parent_id' => $parent_id,
'type' => $type,
'sort' => $sort,
]);
return jsonResponse($permission, '创建成功');
}
public function update(Request $request)
{
$id = (int)$request->post('id');
$permission = Permission::find($id);
if (!$permission) {
return jsonResponse(null, '权限不存在', 404);
}
$name = trim($request->post('name', ''));
$code = trim($request->post('code', ''));
if ($name) $permission->name = $name;
if ($code && $code !== $permission->code) {
if (Permission::where('code', $code)->exists()) {
return jsonResponse(null, '代码已存在', 400);
}
$permission->code = $code;
}
if ($request->post('parent_id') !== null) {
$permission->parent_id = (int)$request->post('parent_id');
}
if ($request->post('type') !== null) {
$permission->type = (int)$request->post('type');
}
if ($request->post('sort') !== null) {
$permission->sort = (int)$request->post('sort');
}
$permission->save();
return jsonResponse(null, '更新成功');
}
public function delete(Request $request)
{
$id = (int)$request->post('id');
$permission = Permission::find($id);
if (!$permission) {
return jsonResponse(null, '权限不存在', 404);
}
if (Permission::where('parent_id', $id)->exists()) {
return jsonResponse(null, '存在子权限,不可删除', 400);
}
$permission->delete();
// Remove from role_permissions
\Illuminate\Database\Capsule\Manager::table('role_permissions')->where('permission_id', $id)->delete();
return jsonResponse(null, '删除成功');
}
}

View File

@@ -0,0 +1,114 @@
<?php
namespace app\admin\controller;
use support\Request;
use app\common\model\Order;
use app\common\model\Report;
use app\common\service\OrderFlowService;
use Illuminate\Database\Capsule\Manager as DB;
class ReportController
{
public function list(Request $request)
{
$page = max(1, intval($request->get('page', 1)));
$pageSize = min(50, max(1, intval($request->get('page_size', 10))));
$query = Report::with(['order', 'inspector']);
if ($reportNo = $request->get('report_no')) {
$query->where('report_no', 'like', "%{$reportNo}%");
}
$total = $query->count();
$items = $query->orderBy('id', 'desc')
->offset(($page - 1) * $pageSize)
->limit($pageSize)
->get();
return jsonResponse([
'items' => $items,
'total' => $total,
'page' => $page,
'page_size' => $pageSize
]);
}
public function detail(Request $request)
{
$id = (int)$request->get('id');
$report = Report::with(['order.logs', 'inspector'])->find($id);
if (!$report) {
return jsonResponse(null, '报告不存在', 404);
}
return jsonResponse($report);
}
public function create(Request $request)
{
$orderId = (int)$request->post('order_id');
$conclusion = trim($request->post('conclusion', '')); // REAL, FAKE, DOUBT
$level = trim($request->post('level', ''));
$flawsJson = $request->post('flaws_json', []);
$imagesJson = $request->post('images_json', []);
if (!in_array($conclusion, ['REAL', 'FAKE', 'DOUBT'])) {
return jsonResponse(null, '鉴定结论不合法', 400);
}
if (empty($imagesJson)) {
return jsonResponse(null, '必须上传证据图片', 400);
}
$order = Order::find($orderId);
if (!$order) {
return jsonResponse(null, '订单不存在', 404);
}
if ($order->status !== 'inspecting') {
return jsonResponse(null, '订单当前状态不可出具报告', 400);
}
if (Report::where('order_id', $orderId)->exists()) {
return jsonResponse(null, '该订单已出具报告,不可重复出具', 400);
}
DB::beginTransaction();
try {
$reportNo = 'R' . date('YmdHis') . rand(1000, 9999);
$verifyCode = bin2hex(random_bytes(8)); // 16字符防伪码
$report = Report::create([
'report_no' => $reportNo,
'order_id' => $orderId,
'conclusion' => $conclusion,
'level' => $level,
'flaws_json' => $flawsJson,
'images_json' => $imagesJson,
'inspector_id' => $request->admin->id,
'verify_code' => $verifyCode
]);
// 扭转订单状态
$order->status = 'finished';
$order->save();
$conclusionMap = [
'REAL' => '正品',
'FAKE' => '仿品',
'DOUBT' => '存疑'
];
$conclusionText = $conclusionMap[$conclusion] ?? '未知';
OrderFlowService::addLog($orderId, 'report_generated', '报告已出具', "鉴定结论:{$conclusionText}", 'admin', $request->admin->id);
DB::commit();
return jsonResponse($report, '报告出具成功');
} catch (\Exception $e) {
DB::rollBack();
return jsonResponse(null, '出具报告失败: ' . $e->getMessage(), 500);
}
}
}

View File

@@ -0,0 +1,140 @@
<?php
namespace app\admin\controller;
use support\Request;
use app\common\model\Role;
use Illuminate\Database\Capsule\Manager as DB;
class RoleController
{
public function list(Request $request)
{
$page = (int)$request->get('page', 1);
$limit = (int)$request->get('limit', 15);
$query = Role::with('permissions');
if ($name = $request->get('name')) {
$query->where('name', 'like', "%{$name}%");
}
$total = $query->count();
$list = $query->offset(($page - 1) * $limit)
->limit($limit)
->orderBy('id', 'desc')
->get();
return jsonResponse([
'total' => $total,
'list' => $list
]);
}
public function all(Request $request)
{
$roles = Role::all();
return jsonResponse($roles);
}
public function create(Request $request)
{
$name = trim($request->post('name', ''));
$code = trim($request->post('code', ''));
$description = trim($request->post('description', ''));
$permissionIds = $request->post('permission_ids', []);
if (!$name || !$code) {
return jsonResponse(null, '角色名称和编码必填', 400);
}
if (Role::where('name', $name)->exists()) {
return jsonResponse(null, '角色名称已存在', 400);
}
if (Role::where('code', $code)->exists()) {
return jsonResponse(null, '角色编码已存在', 400);
}
DB::beginTransaction();
try {
$role = Role::create([
'name' => $name,
'code' => $code,
'description' => $description,
]);
if (!empty($permissionIds)) {
$role->permissions()->sync($permissionIds);
}
DB::commit();
return jsonResponse(null, '创建成功');
} catch (\Exception $e) {
DB::rollBack();
return jsonResponse(null, '创建失败: ' . $e->getMessage(), 500);
}
}
public function update(Request $request)
{
$id = (int)$request->post('id');
$role = Role::find($id);
if (!$role) {
return jsonResponse(null, '角色不存在', 404);
}
$name = trim($request->post('name', ''));
if ($name && $name !== $role->name) {
if (Role::where('name', $name)->exists()) {
return jsonResponse(null, '角色名称已存在', 400);
}
$role->name = $name;
}
$code = trim($request->post('code', ''));
if ($code && $code !== $role->code) {
if (Role::where('code', $code)->exists()) {
return jsonResponse(null, '角色编码已存在', 400);
}
$role->code = $code;
}
if ($request->post('description') !== null) {
$role->description = trim($request->post('description'));
}
$permissionIds = $request->post('permission_ids');
DB::beginTransaction();
try {
$role->save();
if (is_array($permissionIds)) {
$role->permissions()->sync($permissionIds);
}
DB::commit();
return jsonResponse(null, '更新成功');
} catch (\Exception $e) {
DB::rollBack();
return jsonResponse(null, '更新失败: ' . $e->getMessage(), 500);
}
}
public function delete(Request $request)
{
$id = (int)$request->post('id');
$role = Role::find($id);
if (!$role) {
return jsonResponse(null, '角色不存在', 404);
}
DB::beginTransaction();
try {
$role->permissions()->detach();
$role->delete();
DB::table('admin_roles')->where('role_id', $id)->delete();
DB::commit();
return jsonResponse(null, '删除成功');
} catch (\Exception $e) {
DB::rollBack();
return jsonResponse(null, '删除失败: ' . $e->getMessage(), 500);
}
}
}

View File

@@ -0,0 +1,37 @@
<?php
namespace app\admin\controller;
use support\Request;
use Webman\Http\UploadFile;
class UploadController
{
public function image(Request $request)
{
$file = $request->file('file');
if (!$file || !$file->isValid()) {
return jsonResponse(null, '未找到文件或文件无效', 400);
}
$ext = strtolower($file->getUploadExtension());
if (!in_array($ext, ['jpg', 'jpeg', 'png', 'gif', 'webp'])) {
return jsonResponse(null, '仅支持图片文件', 400);
}
$dir = public_path() . '/upload/images/' . date('Ymd');
if (!is_dir($dir)) {
mkdir($dir, 0777, true);
}
$filename = uniqid() . bin2hex(random_bytes(4)) . '.' . $ext;
$path = $dir . '/' . $filename;
$file->move($path);
$url = '/upload/images/' . date('Ymd') . '/' . $filename;
return jsonResponse([
'url' => $url,
'name' => $file->getUploadName(),
], '上传成功');
}
}

View File

@@ -0,0 +1,53 @@
<?php
namespace app\admin\controller;
use support\Request;
use app\common\model\User;
class UserController
{
public function list(Request $request)
{
$page = max(1, intval($request->get('page', 1)));
$pageSize = min(50, max(1, intval($request->get('page_size', 10))));
$query = User::query();
if ($mobile = $request->get('mobile')) {
$query->where('mobile', 'like', "%{$mobile}%");
}
if ($nickname = $request->get('nickname')) {
$query->where('nickname', 'like', "%{$nickname}%");
}
$total = $query->count();
$items = $query->orderBy('id', 'desc')
->offset(($page - 1) * $pageSize)
->limit($pageSize)
->get();
return jsonResponse([
'items' => $items,
'total' => $total,
'page' => $page,
'page_size' => $pageSize
]);
}
public function updateStatus(Request $request)
{
$id = (int)$request->post('id');
$status = (int)$request->post('status');
$user = User::find($id);
if (!$user) {
return jsonResponse(null, '用户不存在', 404);
}
$user->status = $status === 1 ? 1 : 0;
$user->save();
return jsonResponse(null, '更新状态成功');
}
}

View File

@@ -0,0 +1,136 @@
<?php
namespace app\admin\controller;
use support\Request;
use app\common\model\WechatApp;
use Illuminate\Database\Capsule\Manager as DB;
class WechatAppController
{
public function list(Request $request)
{
$page = (int)$request->get('page', 1);
$limit = (int)$request->get('limit', 15);
if ($page < 1) $page = 1;
if ($limit < 1) $limit = 15;
$query = WechatApp::query();
if (($name = trim((string)$request->get('name', ''))) !== '') {
$query->where('name', 'like', "%{$name}%");
}
if (($appId = trim((string)$request->get('app_id', ''))) !== '') {
$query->where('app_id', 'like', "%{$appId}%");
}
if (($type = trim((string)$request->get('type', ''))) !== '') {
$query->where('type', $type);
}
if ($request->get('status') !== null && $request->get('status') !== '') {
$query->where('status', (int)$request->get('status'));
}
$total = $query->count();
$list = $query->select([
'id',
'name',
'type',
'app_id',
'status',
'remark',
'created_at',
])
->selectRaw("IF(app_secret IS NULL OR app_secret = '', 0, 1) as has_secret")
->orderByDesc('id')
->offset(($page - 1) * $limit)
->limit($limit)
->get();
return jsonResponse([
'total' => $total,
'list' => $list,
]);
}
public function create(Request $request)
{
$name = trim((string)$request->post('name', ''));
$type = trim((string)$request->post('type', 'h5'));
$appId = trim((string)$request->post('app_id', ''));
$appSecret = trim((string)$request->post('app_secret', ''));
$status = (int)$request->post('status', 1);
$remark = trim((string)$request->post('remark', ''));
if ($name === '' || $appId === '') {
return jsonResponse(null, '名称和AppID必填', 400);
}
if (!in_array($type, ['h5', 'mini'], true)) {
return jsonResponse(null, '类型不合法', 400);
}
try {
$row = WechatApp::create([
'name' => $name,
'type' => $type,
'app_id' => $appId,
'app_secret' => $appSecret ?: null,
'status' => $status ? 1 : 0,
'remark' => $remark ?: null,
]);
return jsonResponse($row, '创建成功');
} catch (\Throwable $e) {
return jsonResponse(null, '创建失败: ' . $e->getMessage(), 500);
}
}
public function update(Request $request)
{
$id = (int)$request->post('id');
$row = WechatApp::find($id);
if (!$row) {
return jsonResponse(null, '应用不存在', 404);
}
$name = trim((string)$request->post('name', $row->name));
$type = trim((string)$request->post('type', $row->type));
$appId = trim((string)$request->post('app_id', $row->app_id));
$appSecret = trim((string)$request->post('app_secret', ''));
$status = (int)$request->post('status', $row->status);
$remark = trim((string)$request->post('remark', $row->remark ?? ''));
if ($name === '' || $appId === '') {
return jsonResponse(null, '名称和AppID必填', 400);
}
if (!in_array($type, ['h5', 'mini'], true)) {
return jsonResponse(null, '类型不合法', 400);
}
DB::beginTransaction();
try {
$row->name = $name;
$row->type = $type;
$row->app_id = $appId;
if ($appSecret !== '') {
$row->app_secret = $appSecret;
}
$row->status = $status ? 1 : 0;
$row->remark = $remark ?: null;
$row->save();
DB::commit();
return jsonResponse(null, '更新成功');
} catch (\Throwable $e) {
DB::rollBack();
return jsonResponse(null, '更新失败: ' . $e->getMessage(), 500);
}
}
public function delete(Request $request)
{
$id = (int)$request->post('id');
$row = WechatApp::find($id);
if (!$row) {
return jsonResponse(null, '应用不存在', 404);
}
$row->delete();
return jsonResponse(null, '删除成功');
}
}

View File

@@ -0,0 +1,336 @@
<?php
namespace app\admin\controller;
use support\Request;
use app\common\model\WechatMerchant;
use Illuminate\Database\Capsule\Manager as DB;
use Webman\Http\UploadFile;
class WechatMerchantController
{
public function list(Request $request)
{
$page = (int)$request->get('page', 1);
$limit = (int)$request->get('limit', 15);
if ($page < 1) $page = 1;
if ($limit < 1) $limit = 15;
$query = WechatMerchant::query();
if (($name = trim((string)$request->get('name', ''))) !== '') {
$query->where('name', 'like', "%{$name}%");
}
if (($mchId = trim((string)$request->get('mch_id', ''))) !== '') {
$query->where('mch_id', 'like', "%{$mchId}%");
}
if ($request->get('status') !== null && $request->get('status') !== '') {
$query->where('status', (int)$request->get('status'));
}
$total = $query->count();
$list = $query->select([
'id',
'name',
'mode',
'mch_id',
'app_id',
'sub_mch_id',
'sub_app_id',
'service_provider',
'serial_no',
'notify_url',
'is_default',
'status',
'remark',
'apiclient_cert_path',
'apiclient_key_path',
'created_at',
])
->selectRaw("IF(api_v3_key IS NULL OR api_v3_key = '', 0, 1) as has_api_v3_key")
->selectRaw("IF((private_key_pem IS NULL OR private_key_pem = '') AND (apiclient_key_path IS NULL OR apiclient_key_path = ''), 0, 1) as has_private_key")
->selectRaw("IF(apiclient_cert_path IS NULL OR apiclient_cert_path = '', 0, 1) as has_apiclient_cert")
->orderByDesc('is_default')
->orderByDesc('id')
->offset(($page - 1) * $limit)
->limit($limit)
->get();
return jsonResponse([
'total' => $total,
'list' => $list,
]);
}
public function create(Request $request)
{
$name = trim((string)$request->post('name', ''));
$mode = trim((string)$request->post('mode', 'direct'));
$mchId = trim((string)$request->post('mch_id', ''));
$appId = trim((string)$request->post('app_id', ''));
$subMchId = trim((string)$request->post('sub_mch_id', ''));
$subAppId = trim((string)$request->post('sub_app_id', ''));
$serviceProvider = trim((string)$request->post('service_provider', ''));
$serialNo = trim((string)$request->post('serial_no', ''));
$apiV3Key = trim((string)$request->post('api_v3_key', ''));
$privateKeyPem = trim((string)$request->post('private_key_pem', ''));
$notifyUrl = trim((string)$request->post('notify_url', ''));
$remark = trim((string)$request->post('remark', ''));
$status = (int)$request->post('status', 1);
$isDefault = (int)$request->post('is_default', 0) ? 1 : 0;
if ($name === '' || $mchId === '') {
return jsonResponse(null, '名称和商户号必填', 400);
}
if (!in_array($mode, ['direct', 'service_provider', 'third_party'], true)) {
return jsonResponse(null, '商户类型不合法', 400);
}
if ($mode === 'service_provider' && $subMchId === '') {
return jsonResponse(null, '服务商模式必须填写子商户号', 400);
}
if ($this->existsConflict(0, $mode, $mchId, $subMchId, $appId, $subAppId)) {
return jsonResponse(null, '该商户配置已存在', 400);
}
DB::beginTransaction();
try {
if ($isDefault === 1) {
WechatMerchant::where('is_default', 1)->update(['is_default' => 0]);
}
$row = WechatMerchant::create([
'name' => $name,
'mode' => $mode,
'mch_id' => $mchId,
'app_id' => $appId ?: null,
'serial_no' => $serialNo ?: null,
'api_v3_key' => $apiV3Key ?: null,
'private_key_pem' => $privateKeyPem ?: null,
'notify_url' => $notifyUrl ?: null,
'sub_mch_id' => $subMchId ?: null,
'sub_app_id' => $subAppId ?: null,
'service_provider' => $serviceProvider ?: null,
'remark' => $remark ?: null,
'status' => $status ? 1 : 0,
'is_default' => $isDefault,
]);
DB::commit();
return jsonResponse($row, '创建成功');
} catch (\Throwable $e) {
DB::rollBack();
return jsonResponse(null, '创建失败: ' . $e->getMessage(), 500);
}
}
public function update(Request $request)
{
$id = (int)$request->post('id');
$row = WechatMerchant::find($id);
if (!$row) {
return jsonResponse(null, '商户号不存在', 404);
}
$name = trim((string)$request->post('name', $row->name));
$mode = trim((string)$request->post('mode', $row->mode));
$mchId = trim((string)$request->post('mch_id', $row->mch_id));
$appId = trim((string)$request->post('app_id', $row->app_id ?? ''));
$subMchId = trim((string)$request->post('sub_mch_id', $row->sub_mch_id ?? ''));
$subAppId = trim((string)$request->post('sub_app_id', $row->sub_app_id ?? ''));
$serviceProvider = trim((string)$request->post('service_provider', $row->service_provider ?? ''));
$serialNo = trim((string)$request->post('serial_no', $row->serial_no ?? ''));
$apiV3Key = trim((string)$request->post('api_v3_key', ''));
$privateKeyPem = trim((string)$request->post('private_key_pem', ''));
$notifyUrl = trim((string)$request->post('notify_url', $row->notify_url ?? ''));
$remark = trim((string)$request->post('remark', $row->remark ?? ''));
$status = (int)$request->post('status', $row->status);
$isDefault = $request->post('is_default') !== null ? ((int)$request->post('is_default') ? 1 : 0) : (int)$row->is_default;
if ($name === '' || $mchId === '') {
return jsonResponse(null, '名称和商户号必填', 400);
}
if (!in_array($mode, ['direct', 'service_provider', 'third_party'], true)) {
return jsonResponse(null, '商户类型不合法', 400);
}
if ($mode === 'service_provider' && $subMchId === '') {
return jsonResponse(null, '服务商模式必须填写子商户号', 400);
}
if ($this->existsConflict($id, $mode, $mchId, $subMchId, $appId, $subAppId)) {
return jsonResponse(null, '该商户配置已存在', 400);
}
DB::beginTransaction();
try {
if ($isDefault === 1) {
WechatMerchant::where('is_default', 1)->where('id', '<>', $id)->update(['is_default' => 0]);
}
$row->name = $name;
$row->mode = $mode;
$row->mch_id = $mchId;
$row->app_id = $appId ?: null;
$row->serial_no = $serialNo ?: null;
if ($apiV3Key !== '') {
$row->api_v3_key = $apiV3Key;
}
if ($privateKeyPem !== '') {
$row->private_key_pem = $privateKeyPem;
}
$row->notify_url = $notifyUrl ?: null;
$row->sub_mch_id = $subMchId ?: null;
$row->sub_app_id = $subAppId ?: null;
$row->service_provider = $serviceProvider ?: null;
$row->remark = $remark ?: null;
$row->status = $status ? 1 : 0;
$row->is_default = $isDefault;
$row->save();
DB::commit();
return jsonResponse(null, '更新成功');
} catch (\Throwable $e) {
DB::rollBack();
return jsonResponse(null, '更新失败: ' . $e->getMessage(), 500);
}
}
public function delete(Request $request)
{
$id = (int)$request->post('id');
$row = WechatMerchant::find($id);
if (!$row) {
return jsonResponse(null, '商户号不存在', 404);
}
if ((int)$row->is_default === 1) {
return jsonResponse(null, '默认商户号不可删除', 400);
}
$row->delete();
return jsonResponse(null, '删除成功');
}
public function uploadApiclientCert(Request $request)
{
return $this->uploadPem($request, 'apiclient_cert');
}
public function uploadApiclientKey(Request $request)
{
return $this->uploadPem($request, 'apiclient_key');
}
public function uploadApiV3Key(Request $request)
{
$id = (int)$request->post('id');
$row = WechatMerchant::find($id);
if (!$row) {
return jsonResponse(null, '商户号不存在', 404);
}
/** @var UploadFile|null $file */
$file = $request->file('file');
if (!$file || !$file->isValid()) {
return jsonResponse(null, '未找到文件或文件无效', 400);
}
$size = $file->getSize();
if ($size === false || $size > 1024) {
return jsonResponse(null, '文件过大', 400);
}
$content = file_get_contents($file->getPathname());
$content = is_string($content) ? $content : '';
$key = preg_replace('/\s+/', '', $content);
$key = is_string($key) ? trim($key) : '';
if ($key === '' || strlen($key) !== 32) {
return jsonResponse(null, 'APIv3 Key 格式不正确应为32位', 400);
}
$row->api_v3_key = $key;
$row->save();
return jsonResponse([
'id' => $row->id,
'has_api_v3_key' => 1,
], '上传成功');
}
private function uploadPem(Request $request, string $type)
{
$id = (int)$request->post('id');
$row = WechatMerchant::find($id);
if (!$row) {
return jsonResponse(null, '商户号不存在', 404);
}
/** @var UploadFile|null $file */
$file = $request->file('file');
if (!$file || !$file->isValid()) {
return jsonResponse(null, '未找到文件或文件无效', 400);
}
$ext = strtolower($file->getUploadExtension());
if ($ext !== 'pem') {
return jsonResponse(null, '仅支持 pem 文件', 400);
}
$dir = runtime_path() . '/wechatpay/merchants/' . $row->id;
if (!is_dir($dir)) {
mkdir($dir, 0700, true);
}
$filename = $type === 'apiclient_cert' ? 'apiclient_cert.pem' : 'apiclient_key.pem';
$path = $dir . '/' . $filename;
$file->move($path);
@chmod($path, 0600);
if ($type === 'apiclient_cert') {
$row->apiclient_cert_path = $path;
try {
$certPem = file_get_contents($path);
$x509 = openssl_x509_read($certPem);
if ($x509) {
$info = openssl_x509_parse($x509);
$serialHex = $info['serialNumberHex'] ?? '';
if (is_string($serialHex) && $serialHex !== '') {
$row->serial_no = $row->serial_no ?: strtoupper($serialHex);
}
}
} catch (\Throwable $e) {
}
} else {
$row->apiclient_key_path = $path;
}
$row->save();
return jsonResponse([
'id' => $row->id,
'serial_no' => $row->serial_no,
'has_apiclient_cert' => $row->apiclient_cert_path ? 1 : 0,
'has_private_key' => ($row->private_key_pem || $row->apiclient_key_path) ? 1 : 0,
], '上传成功');
}
private function existsConflict(int $id, string $mode, string $mchId, string $subMchId, string $appId, string $subAppId): bool
{
$query = WechatMerchant::query()->where('mode', $mode)->where('mch_id', $mchId);
if ($mode === 'service_provider') {
$query->where('sub_mch_id', $subMchId);
} else {
$query->where(function ($q) {
$q->whereNull('sub_mch_id')->orWhere('sub_mch_id', '');
});
}
if ($appId !== '') {
$query->where('app_id', $appId);
} else {
$query->where(function ($q) {
$q->whereNull('app_id')->orWhere('app_id', '');
});
}
if ($subAppId !== '') {
$query->where('sub_app_id', $subAppId);
} else {
$query->where(function ($q) {
$q->whereNull('sub_app_id')->orWhere('sub_app_id', '');
});
}
if ($id > 0) {
$query->where('id', '<>', $id);
}
return $query->exists();
}
}