177c3ae9b2
- 将 ProcessContext 中的 builder() 重命名为 createModifyBuilder() 并添加调用栈日志 - ProcessContextBuilder 中所有 with* 方法统一改为 set* 命名风格 - Flow 各节点及流程处理器中调用 builder() 替换为 createModifyBuilder() - 虚拟测试环境相关 ContextBuilder 中方法同步改名保持一致 - 优化流程节点中上下文修改代码调用,提升代码规范性 - 添加Config中加载自定义流程配置的占位注释及代码 - 无功能改动,纯代码风格及命名规范调整
294 lines
10 KiB
PHP
294 lines
10 KiB
PHP
<?php
|
||
|
||
namespace app\config;
|
||
|
||
|
||
|
||
class Config
|
||
{
|
||
/**
|
||
* 是否使用自定义流程配置(从Config加载)
|
||
*/
|
||
public bool $flowUseCustomProcess {
|
||
get => $this->flowUseCustomProcess;
|
||
}
|
||
|
||
/**
|
||
* 自定义流程配置键名(默认使用 'standard' 注意:此次为空,并非使用自定义配置文件里面的 'standard')
|
||
*/
|
||
public string $flowProcessConfigKey {
|
||
get => $this->flowProcessConfigKey;
|
||
}
|
||
|
||
/**
|
||
* @var int 普通日志轮转时间默认 14 天
|
||
*/
|
||
public int $logRotationTimeByDay {
|
||
get => $this->logRotationTimeByDay;
|
||
}
|
||
|
||
/**
|
||
* @var int 错误日志轮转时间默认 30 天
|
||
*/
|
||
public int $errorLogRotationTimeByDay {
|
||
get => $this->errorLogRotationTimeByDay;
|
||
}
|
||
|
||
public DatabaseConfig $database {
|
||
get => $this->database;
|
||
}
|
||
|
||
public array $customProcess {
|
||
get => $this->customProcess;
|
||
}
|
||
|
||
/**
|
||
* 存储单读卡器模式
|
||
* true=单读卡器模式(一个读卡器交替入库/出库)
|
||
* false=双读卡器模式(分别使用"内镜放入"和"内镜取出"读卡器)
|
||
*/
|
||
public bool $storageSingleReader {
|
||
get => $this->storageSingleReader;
|
||
}
|
||
|
||
/**
|
||
* 机器ID,用于分布式环境,确保唯一
|
||
*/
|
||
public string $machineId {
|
||
get => $this->machineId;
|
||
}
|
||
|
||
public bool $dbDebug {
|
||
get => $this->dbDebug;
|
||
}
|
||
|
||
/**
|
||
* 方式 类:方法
|
||
* 方式 类
|
||
* 方式 :方法
|
||
* 允许使用正则 * 作为通配符
|
||
* @var array 日志过滤器
|
||
*/
|
||
public array $logFilter {
|
||
get => $this->logFilter;
|
||
}
|
||
|
||
public int $logLevel {
|
||
get => $this->logLevel;
|
||
}
|
||
|
||
/**
|
||
* 阻断模式
|
||
*/
|
||
public bool $blockMode {
|
||
get => $this->blockMode;
|
||
}
|
||
|
||
public function setBlockMode(bool $value)
|
||
{
|
||
echo "\033[31m获取禁止修改热修改阻断模式,这只是用于测试方法\033[0m\n";
|
||
$this->blockMode = $value;
|
||
}
|
||
|
||
/**
|
||
* 人员刷卡记录缓存记录多久。然后是 0 表示不计算缓存时间,只要被使用了就删除
|
||
*/
|
||
public int $openCardRecordCacheTime = 0 {
|
||
get => $this->openCardRecordCacheTime;
|
||
}
|
||
|
||
/**
|
||
* 人员刷卡记录记录哪些读卡器的
|
||
* 设置为空则表示所有读卡器
|
||
*/
|
||
public array $openCardRecordReaders = [] {
|
||
get => $this->openCardRecordReaders;
|
||
}
|
||
|
||
/**
|
||
* 是否开启虚拟清洗机解析
|
||
*/
|
||
public bool $enableVirtualCleanerParser {
|
||
get => $this->enableVirtualCleanerParser;
|
||
}
|
||
|
||
private function __construct()
|
||
{
|
||
$this->detectCircularDependency();
|
||
$this->database = new DatabaseConfig();
|
||
// TODO 加载自定义流程配置
|
||
// 加载根目录的
|
||
$this->customProcess = require __DIR__ . '/custom_process_config.php';
|
||
$this->machineId = self::getStringEnv("MACHINE_ID", "0");
|
||
if (empty($this->machineId) || $this->machineId == '0') {
|
||
$this->machineId = $this->getMachineIdByMac();
|
||
}
|
||
$this->dbDebug = self::getBoolEnv('DB_DEBUG');
|
||
$this->flowUseCustomProcess = self::getBoolEnv('FLOW_USE_CUSTOM_PROCESS', true);
|
||
$this->flowProcessConfigKey = self::getStringEnv('FLOW_PROCESS_CONFIG_KEY', '');
|
||
$this->logRotationTimeByDay = self::getIntEnv('LOG_ROTATION_TIME_BY_DAY', 14);
|
||
$this->errorLogRotationTimeByDay = self::getIntEnv('ERROR_LOG_ROTATION_TIME_BY_DAY', 30);
|
||
$this->logFilter = self::getStringArrayEnv('LOG_FILTER', []);
|
||
$this->logLevel = match (strtoupper(self::getStringEnv('LOG_LEVEL', 'DEBUG'))) {
|
||
'INFO' => \Monolog\Logger::INFO,
|
||
'WARNING' => \Monolog\Logger::WARNING,
|
||
'ERROR' => \Monolog\Logger::ERROR,
|
||
'ALERT' => \Monolog\Logger::ALERT,
|
||
'EMERGENCY' => \Monolog\Logger::EMERGENCY,
|
||
'CRITICAL' => \Monolog\Logger::CRITICAL,
|
||
'NOTICE' => \Monolog\Logger::NOTICE,
|
||
default => \Monolog\Logger::DEBUG
|
||
};
|
||
$this->blockMode = self::getBoolEnv('BLOCK_MODE', true);
|
||
$this->openCardRecordCacheTime = self::getIntEnv('OPEN_CARD_RECORD_CACHE_TIME', 60);
|
||
$this->openCardRecordReaders = self::getStringArrayEnv('OPEN_CARD_RECORD_READERS', []);
|
||
$this->storageSingleReader = self::getBoolEnv('STORAGE_SINGLE_READER', false);
|
||
$this->enableVirtualCleanerParser = self::getBoolEnv('ENABLE_VIRTUAL_CLEANER_PARSER', false);
|
||
}
|
||
|
||
private function __clone() {}
|
||
|
||
private function detectCircularDependency(): void
|
||
{
|
||
// 获取调用栈(深度10层,足够覆盖依赖链)
|
||
$trace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS, 10);
|
||
$logRelatedPaths = [
|
||
'support/Log.php', // Log类文件
|
||
'support\Log.php', // Windows路径
|
||
'config/log.php', // 日志配置文件
|
||
'monolog' // 日志组件
|
||
];
|
||
|
||
// 遍历调用栈,检查是否包含日志相关文件
|
||
foreach ($trace as $step) {
|
||
if (isset($step['file'])) {
|
||
$file = strtolower($step['file']);
|
||
foreach ($logRelatedPaths as $path) {
|
||
if (str_contains($file, strtolower($path))) {
|
||
throw new \RuntimeException(
|
||
"循环依赖检测:Config类初始化时检测到日志组件({$step['file']})被引入\n" .
|
||
"请移除Config类中对日志的依赖,或调整日志配置加载逻辑。"
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 检查是否直接调用了 Log 类
|
||
if (isset($step['class']) && str_contains(strtolower($step['class']), 'support\log')) {
|
||
throw new \RuntimeException(
|
||
"循环依赖检测:Config类被 support\Log 类直接调用,导致循环依赖!"
|
||
);
|
||
}
|
||
}
|
||
|
||
// 检查当前文件是否主动引入了 Log 类(防止硬编码引入)
|
||
$includedFiles = get_included_files();
|
||
foreach ($includedFiles as $file) {
|
||
if (str_contains(strtolower($file), 'support/log')) {
|
||
throw new \RuntimeException(
|
||
"循环依赖检测:Config类加载时,support\Log 已被提前引入({$file}),禁止循环依赖!"
|
||
);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 获取服务器MAC地址并散列成2位数字(00-99)作为机器ID
|
||
* 兼容Linux/Windows系统,失败时降级使用IP散列
|
||
*
|
||
* @return string 2位机器ID(00-99)
|
||
*/
|
||
public function getMachineIdByMac(): string
|
||
{
|
||
try {
|
||
// 步骤1:根据系统类型执行命令获取MAC地址
|
||
$mac = '';
|
||
if (strtoupper(substr(PHP_OS, 0, 3)) === 'WIN') {
|
||
// Windows系统
|
||
$output = shell_exec('ipconfig /all');
|
||
if (preg_match('/物理地址[.:\s]+([0-9A-F]{2}[-:][0-9A-F]{2}[-:][0-9A-F]{2}[-:][0-9A-F]{2}[-:][0-9A-F]{2}[-:][0-9A-F]{2})/i', $output, $matches)) {
|
||
$mac = str_replace(['-', ':'], '', strtolower($matches[1]));
|
||
}
|
||
} else {
|
||
// Linux/Mac系统
|
||
$output = shell_exec('cat /sys/class/net/*/address 2>/dev/null || ifconfig');
|
||
if (preg_match('/([0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2}:[0-9a-f]{2})/i', $output, $matches)) {
|
||
// 过滤虚拟网卡(docker/lo等),取第一个有效物理网卡MAC
|
||
$mac = str_replace(':', '', strtolower($matches[1]));
|
||
// 排除回环地址、docker虚拟网卡等无效MAC
|
||
$invalidPrefixes = ['00:00:00', '02:42:ac', '12:34:56'];
|
||
$isInvalid = false;
|
||
foreach ($invalidPrefixes as $prefix) {
|
||
if (str_starts_with($matches[1], $prefix)) {
|
||
$isInvalid = true;
|
||
break;
|
||
}
|
||
}
|
||
if ($isInvalid) {
|
||
$mac = '';
|
||
}
|
||
}
|
||
}
|
||
|
||
// 步骤2:若MAC获取失败,降级使用服务器IP
|
||
if (empty($mac)) {
|
||
// 获取外网/内网IP(优先内网)
|
||
$ip = $_SERVER['SERVER_ADDR'] ?? gethostbyname(gethostname());
|
||
if (empty($ip) || $ip === '127.0.0.1') {
|
||
$ip = '0.0.0.0'; // 兜底
|
||
}
|
||
$mac = $ip; // 用IP替代MAC做散列
|
||
}
|
||
|
||
$hash = md5($mac); // 生成32位哈希值
|
||
$hashInt = hexdec(substr($hash, 0, 4));
|
||
$chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
||
$base = 62; // 62进制基数
|
||
$char1 = $chars[$hashInt % $base];
|
||
$char2 = $chars[(int)($hashInt / $base) % $base];
|
||
$machineId = $char2 . $char1;
|
||
|
||
// 步骤4:补零为2位(如5→05,99→99)
|
||
return str_pad($machineId, 2, '0', STR_PAD_LEFT);
|
||
} catch (\Exception $e) {
|
||
// 红色输出
|
||
echo "\033[31m获取机器ID失败:{$e->getMessage()}\033[0m\n";
|
||
return '01'; // 默认机器ID
|
||
}
|
||
}
|
||
|
||
public static function getBoolEnv($name, $default = false)
|
||
{
|
||
$value = getenv($name);
|
||
return empty($value) ? $default : filter_var($value, FILTER_VALIDATE_BOOLEAN);
|
||
}
|
||
|
||
public static function getIntEnv($name, $default = 0)
|
||
{
|
||
$value = getenv($name);
|
||
return empty($value) ? $default : (int)$value;
|
||
}
|
||
|
||
public static function getStringEnv($name, $default = null)
|
||
{
|
||
$value = getenv($name);
|
||
return empty($value) ? $default : $value;
|
||
}
|
||
|
||
private static ?Config $instance = null;
|
||
|
||
public static function getInstance(): Config
|
||
{
|
||
if (self::$instance === null) {
|
||
self::$instance = new self();
|
||
}
|
||
return self::$instance;
|
||
}
|
||
|
||
private static function getStringArrayEnv(string $string, array $array): array
|
||
{
|
||
$value = getenv($string);
|
||
return empty($value) ? $array : array_map('trim', explode(';', $value));
|
||
}
|
||
|
||
} |