// +---------------------------------------------------------------------- declare(strict_types=1); namespace app\services\driver\invoice; use app\dao\driver\invoice\InvoiceDriverDao; use crmeb\basic\BaseService; use crmeb\exceptions\AdminException; use crmeb\services\driver\DriverRegistry; /** * 发票驱动管理服务 * * 配置值存储在 config_fields JSON 的 value 属性中 * * Class InvoiceDriverServices * @package app\services\driver\invoice * @method array|object|null get($id, ?array $field = []) 获取一条数据 * @method update($id, array $data, ?string $key = null) 修改数据 */ class InvoiceDriverServices extends BaseService { /** * 初始化 * @param InvoiceDriverDao $dao */ public function __construct(InvoiceDriverDao $dao) { $this->dao = $dao; } /** * 获取驱动列表(含扫描状态) * @param array $where * @return array */ public function getList(array $where = []): array { $list = $this->dao->selectList($where, '*', 0, 0, 'sort desc,id asc')->toArray(); $scannedDrivers = $this->scanDrivers(); // 为每条记录附加驱动扫描状态 foreach ($list as &$item) { $driverKey = $item['driver_key'] ?? ''; $item['driver_exists'] = isset($scannedDrivers[$driverKey]); $item['driver_info'] = $scannedDrivers[$driverKey] ?? null; } // 附加数据库中不存在但目录中存在的驱动(未同步的) $existingKeys = array_column($list, 'driver_key'); foreach ($scannedDrivers as $key => $driver) { if (!in_array($key, $existingKeys)) { $list[] = [ 'id' => 0, 'driver_key' => $key, 'driver_name' => $driver['name'], 'icon' => $driver['icon'], 'is_builtin' => $driver['is_builtin'], 'config_fields' => $driver['config_fields'], 'sort' => $driver['sort'], 'status' => 0, 'driver_exists' => true, 'driver_info' => $driver, 'not_synced' => true, ]; } } return $list; } /** * 获取单条详情(含当前配置值) * @param int $id * @return array */ public function getDetail(int $id): array { $driver = $this->dao->get($id); if (!$driver) { throw new AdminException('发票驱动不存在'); } $data = $driver->toArray(); // 如果数据库中没有 config_fields,从 plugin.json 读取 if (empty($data['config_fields']) && $data['driver_key']) { $scannedDrivers = $this->scanDrivers(); if (isset($scannedDrivers[$data['driver_key']])) { $data['config_fields'] = $scannedDrivers[$data['driver_key']]['config_fields']; } } // 一号通驱动:账号/密钥从系统配置自动读取,标记为只读 if ($data['driver_key'] === 'yihaotong') { foreach ($data['config_fields'] as &$field) { if (in_array($field['field'], ['yihaotong_appid', 'yihaotong_appsecret'])) { $field['readonly'] = true; if ($field['field'] === 'yihaotong_appid') { $field['value'] = sys_config('yihaotong_appid', ''); } else { $field['value'] = sys_config('yihaotong_appsecret', ''); } } } } // 附加驱动扫描信息 $scannedDrivers = $this->scanDrivers(); $data['driver_info'] = $scannedDrivers[$data['driver_key']] ?? null; return $data; } /** * 同步驱动信息:扫描 driver/invoice/ 目录,将 plugin.json 信息写入数据库 * @return array ['added' => int, 'updated' => int] */ public function syncDrivers(): array { $scannedDrivers = $this->scanDrivers(); $added = 0; $updated = 0; $deleted = 0; foreach ($scannedDrivers as $driverKey => $info) { $existing = $this->dao->getByDriverKey($driverKey); if ($existing) { // 已存在:更新基本信息但保留 config_fields 中的配置值和状态 $configFields = is_array($existing->config_fields) ? $existing->config_fields : []; // 用 plugin.json 的字段定义更新,但保留已有的 value $newConfigFields = $info['config_fields']; foreach ($newConfigFields as &$newField) { $fieldName = $newField['field']; foreach ($configFields as $oldField) { if (($oldField['field'] ?? '') === $fieldName && isset($oldField['value'])) { $newField['value'] = $oldField['value']; break; } } } $this->dao->update($existing->id, [ 'driver_name' => $info['name'], 'icon' => $info['icon'], 'is_builtin' => $info['is_builtin'] ? 1 : 0, 'sort' => $info['sort'], 'config_fields' => $newConfigFields, ]); $updated++; } else { // 新增:默认禁用 $this->dao->save([ 'driver_key' => $driverKey, 'driver_name' => $info['name'], 'icon' => $info['icon'], 'is_builtin' => $info['is_builtin'] ? 1 : 0, 'sort' => $info['sort'], 'config_fields' => $info['config_fields'], 'status' => 0, ]); $added++; } } // 删除已不存在的驱动(目录已被移除) $dbKeys = $this->dao->getColumn([], 'driver_key'); foreach ($dbKeys as $key) { if (!isset($scannedDrivers[$key])) { $this->dao->delete(['driver_key' => $key]); $deleted++; } } return ['added' => $added, 'updated' => $updated, 'deleted' => $deleted]; } /** * 修改驱动状态 * @param int $id * @param int $status * @return bool */ public function updateStatus(int $id, int $status): bool { $driver = $this->dao->get($id); if (!$driver) { throw new AdminException('发票驱动不存在'); } $this->dao->update($id, ['status' => $status]); return true; } /** * 保存驱动配置 * @param int $id * @param array $data * @return bool */ public function saveConfig(int $id, array $data): bool { $driver = $this->dao->get($id); if (!$driver) { throw new AdminException('发票驱动不存在'); } // 获取当前 config_fields 定义 $configFields = is_array($driver->config_fields) ? $driver->config_fields : []; if (empty($configFields)) { $scannedDrivers = $this->scanDrivers(); $configFields = $scannedDrivers[$driver->driver_key]['config_fields'] ?? []; } // 更新配置值到 config_fields 的 value 属性 foreach ($configFields as &$field) { $fieldName = $field['field']; // 一号通驱动:账号/密钥由系统配置管理,不存储到 driver 表 if ($driver->driver_key === 'yihaotong' && in_array($fieldName, ['yihaotong_appid', 'yihaotong_appsecret'])) { continue; } if (isset($data[$fieldName])) { $field['value'] = $data[$fieldName]; } } // 配置值存储到 eb_driver 表的 config_fields JSON 中 $this->dao->update($id, [ 'config_fields' => $configFields, ]); return true; } /** * 批量排序 * @param array $sortData * @return bool */ public function setSort(array $sortData): bool { foreach ($sortData as $item) { if (isset($item['id']) && isset($item['sort'])) { $this->dao->update((int)$item['id'], ['sort' => (int)$item['sort']]); } } return true; } /** * 获取已扫描的驱动列表 * @return array */ public function getDrivers(): array { return array_values($this->scanDrivers()); } /** * 获取驱动配置字段定义 * @param string $driverKey * @return array */ public function getConfigFields(string $driverKey): array { $scannedDrivers = $this->scanDrivers(); return $scannedDrivers[$driverKey]['config_fields'] ?? []; } /** * 获取当前启用的驱动 * @return array|null */ public function getEnabledDriver(): ?array { $driver = $this->dao->getActiveDriver(); return $driver ? $driver->toArray() : null; } /** * 获取可用的驱动列表 * @return array */ public function getAvailableDrivers(): array { $list = $this->dao->getAllEnabled(); $result = []; foreach ($list as $driver) { $data = $driver->toArray(); $result[] = [ 'driver_key' => $data['driver_key'], 'name' => $data['driver_name'], 'icon' => $data['icon'], ]; } return $result; } /** * 扫描 driver/invoice/(第三方扩展 + 内置驱动 + 插件安装的驱动) * @return array */ protected function scanDrivers(): array { // 统一走驱动注册中心(读取 plugin.json 元数据,带缓存与标准键归一化) return DriverRegistry::scan('invoice'); } }