Files
lgp-admin-plus-api/app/Service/wx/WxAppService.php
LQ bcf54c2727 初始化
缺陷:主题配色需要优化整体的同风格
2026-08-14 23:21:21 +08:00

131 lines
5.4 KiB
PHP
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<?php
namespace App\Service\wx;
use App\Models\WxAppModel;
use App\Service\common\FieldEncryptService;
use App\Service\common\UtilsService;
/**
* 小程序应用凭证读取
*
* 请求头 X-App-Code或参数 app_code决定用哪个品牌的 AppID。
* 没带就用 .env 里 WX_DEFAULT_APP_CODE 兜底;都没有就取唯一启用的那条。
* 库表nl_wx_app始终是主源.env 只在「表里完全没有匹配行」时回落兜底,
* 这样单品牌部署可以不进后台配置就跑起来,双品牌共用进程仍以表 + 请求头分流为主。
*/
class WxAppService
{
private static mixed $_instance;
public static function getInstance(): null|static
{
$name = get_called_class();
if (!isset(self::$_instance[$name])) {
self::$_instance[$name] = new static();
}
return self::$_instance[$name];
}
/**
* 取当前请求对应的应用配置(含解密后的密钥)
*
* 解析顺序:
* 1. 按 code请求头 → .env 默认值)查 nl_wx_app
* 2. 查不到且 .env 配了 AppID返回仅含 app_id/name/code 的兜底配置,
* 密钥仍尝试从表里「按 app_id 匹配」的那行解密;表里也没有就抛错
* 指引运维去后台填写——密钥不能从 .env 读,安全红线
* 3. .env 也没配:直接抛错
*/
public function current(): array
{
$code = $this->currentCode();
$query = WxAppModel::where('deleted_at', 0)->where('status', 0);
$app = $code !== '' ? $query->where('code', $code)->first() : $query->first();
// 库表命中:解密密钥列后返回
if (!empty($app)) {
return $this->loadWithSecrets($app);
}
// 库表未命中,回落 .env仅 AppID/名称,不含密钥)
$envAppId = trim((string) config('nl.wx.env_app_id', ''));
if ($envAppId === '') {
// .env 没配 AppID 就没法兜底了,直接抛错
UtilsService::getInstance()->errorThrow('未配置小程序应用nl_wx_app请先在后台添加或在 .env 配 WX_APP_ID');
}
$envCode = trim((string) config('nl.wx.default_app_code', ''));
$envName = trim((string) config('nl.wx.env_app_name', ''));
// 兜底场景AppID 来自 .env但密钥仍只能从库里按 AppID 找一行来解密
// 这样老项目硬编码密钥的脏习惯不会回到 .env密钥始终加密入库
$secretRow = WxAppModel::where('app_id', $envAppId)
->where('deleted_at', 0)
->first(['app_secret', 'mch_key', 'mch_private_key']);
if (empty($secretRow) || trim((string) ($secretRow['app_secret'] ?? '')) === '') {
// 表里既没匹配行、或行里没填密钥,只能报错让运维去后台补
UtilsService::getInstance()->errorThrow(
'小程序 AppSecret 未配置:请打开侧栏「小程序 → 应用配置」,新增/编辑 AppID=' .
$envAppId .
' 的记录并填写 AppSecret密钥加密入库不要写进 .env'
);
}
$encrypt = FieldEncryptService::getInstance();
return [
'id' => 0,
'code' => $envCode,
'name' => $envName,
'app_id' => $envAppId,
'app_secret' => $encrypt->decryptFromStorage((string) $secretRow['app_secret'], true),
'mch_id' => '',
'mch_key' => $encrypt->decryptFromStorage((string) ($secretRow['mch_key'] ?? ''), true),
'mch_serial_no' => '',
'mch_private_key' => $encrypt->decryptFromStorage((string) ($secretRow['mch_private_key'] ?? ''), true),
'platform_public_key' => '',
'notify_url' => '',
'template_code' => '',
];
}
/**
* 当前请求声明的品牌标识
*
* 优先级:请求头 X-App-Code > 请求参数 app_code > .env 的 WX_DEFAULT_APP_CODE
* 全部为空时返回空串,调用方会改走「取唯一启用行」逻辑
*/
public function currentCode(): string
{
$code = (string) (request()->header('X-App-Code') ?: request()->input('app_code', ''));
$code = trim($code);
if ($code !== '') {
return $code;
}
// 请求没带品牌标识时回落 .env 默认值(单品牌部署兜底)
return trim((string) config('nl.wx.default_app_code', ''));
}
/**
* 把模型行的密钥列解密后转数组返回
*/
private function loadWithSecrets($app): array
{
$app = $app->toArray();
$raw = WxAppModel::where('id', $app['id'])->first(['app_secret', 'mch_key', 'mch_private_key']);
$encrypt = FieldEncryptService::getInstance();
$secret = $encrypt->decryptFromStorage($raw['app_secret'] ?? '', true);
if (trim((string) $secret) === '') {
UtilsService::getInstance()->errorThrow(
'小程序 AppSecret 未配置:请打开侧栏「小程序 → 应用配置」,编辑 code=' .
($app['code'] ?? '') .
'AppID=' . ($app['app_id'] ?? '') . ')并填写 AppSecret'
);
}
$app['app_secret'] = $secret;
$app['mch_key'] = $encrypt->decryptFromStorage($raw['mch_key'] ?? '', true);
$app['mch_private_key'] = $encrypt->decryptFromStorage($raw['mch_private_key'] ?? '', true);
return $app;
}
}