Files
oil/server-tp6/app/common/service/QytConfig.php
T
2026-09-10 20:56:46 +08:00

148 lines
5.3 KiB
PHP

<?php
// +----------------------------------------------------------------------
// | 企油通配置读取
// +----------------------------------------------------------------------
// | 三层来源,从低到高:
// | 1. config/qyt.php —— 读 .env,含【密钥】与接口域名(接入方级)
// | 2. 后台「企油通对接设置」—— yoshop_store_setting.key='qyt'(接入方级默认值)
// | 3. 企业信息 —— yoshop_qyt_enterprise(企业级:执照号 / 通道 / 机构号 …)
// |
// | 分级依据见《企油通接口对接文档.md》:appId 与 RSA 密钥属于「接入方(慧明)」,
// | 一个 appId 下可挂多个企业;business_license、passageway、institution_id 是企业级。
// |
// | 敏感项(private_key、platform_public_key)只从 .env 读,任何一层都不能覆盖,
// | 数据库备份 / SQL 注入 / 后台账号失窃都拿不到签名私钥。
// +----------------------------------------------------------------------
declare (strict_types=1);
namespace app\common\service;
use app\common\enum\Setting as SettingEnum;
use app\common\model\qyt\Enterprise as EnterpriseModel;
use think\facade\Db;
class QytConfig
{
/** 常驻进程内缓存有效期(秒):后台改完最迟这么久生效 */
const TTL = 10;
/** 后台设置可覆盖的接入方级标量项 */
const OVERRIDABLE = [
'base_url', 'app_id', 'no_prefix', 'voucher_name',
'h5_base_url', 'h5_app_id', 'consume_notify_url',
];
/** @var array|null */
protected static $cache = null;
/** @var int */
protected static $cacheAt = 0;
/**
* 接入方级配置(不含任何企业信息)
*/
public static function get(): array
{
if (self::$cache !== null && (time() - self::$cacheAt) < self::TTL) {
return self::$cache;
}
$conf = (array) config('qyt');
$db = static::dbValues();
foreach (self::OVERRIDABLE as $k) {
if (isset($db[$k]) && trim((string) $db[$k]) !== '') {
$conf[$k] = trim((string) $db[$k]);
}
}
// 日志开关是布尔,0 是合法值,不能用非空判断
if (isset($db['log'])) {
$conf['log'] = (bool) $db['log'];
}
self::$cache = $conf;
self::$cacheAt = time();
return $conf;
}
/**
* 叠加企业级参数,得到某个企业实际使用的完整配置
*
* @param EnterpriseModel|array $enterprise
* @return array 额外带 enterprise_id / enterprise_name / passageway_code / require_id_card
*/
public static function forEnterprise($enterprise): array
{
$conf = self::get();
$e = $enterprise instanceof EnterpriseModel ? $enterprise->toArray() : (array) $enterprise;
$passageway = (string) ($e['passageway'] ?? 'alipay');
!isset(EnterpriseModel::PASSAGEWAY_CODE[$passageway]) && $passageway = 'alipay';
$conf['enterprise_id'] = (int) ($e['enterprise_id'] ?? 0);
$conf['enterprise_name'] = (string) ($e['company_name'] ?? '');
$conf['business_license'] = (string) ($e['business_license'] ?? '');
$conf['passageway'] = $passageway;
$conf['passageway_code'] = EnterpriseModel::PASSAGEWAY_CODE[$passageway];
$conf['passageway_name'] = EnterpriseModel::PASSAGEWAY_NAME[$passageway];
$conf['institution_id'] = (string) ($e['institution_id'] ?? '');
// 平安银行通道要求实名
$conf['require_id_card'] = $passageway === 'pingan';
// 以下留空则回落接入方级默认值
foreach (['app_id', 'no_prefix', 'voucher_name'] as $k) {
$v = trim((string) ($e[$k] ?? ''));
$v !== '' && $conf[$k] = $v;
}
return $conf;
}
/**
* 取单项,支持点号:QytConfig::item('h5_base_url')
* @param mixed $default
* @return mixed
*/
public static function item(string $path, $default = null)
{
$node = self::get();
foreach (explode('.', $path) as $seg) {
if (!is_array($node) || !array_key_exists($seg, $node)) {
return $default;
}
$node = $node[$seg];
}
return $node;
}
/** 清掉进程内缓存(单元测试 / 后台保存后使用) */
public static function flush(): void
{
self::$cache = null;
self::$cacheAt = 0;
}
/**
* 读后台设置。直接查表而不用 Setting 模型,是因为常驻命令行进程里
* BaseModel::$storeId 不会被初始化(只有 store/api 模块才赋值),
* 走模型会查不到数据。本项目为单商城部署,取 store_id 最小的一条即可。
*/
protected static function dbValues(): array
{
try {
$json = Db::name('store_setting')
->where('key', SettingEnum::QYT)
->order('store_id', 'asc')
->value('values');
if (empty($json)) {
return [];
}
$arr = json_decode((string) $json, true);
return is_array($arr) ? $arr : [];
} catch (\Throwable $e) {
// 表不存在 / 库连不上时静默回落到 .env,不能因为读设置失败就中断发放
return [];
}
}
}