data, $count] = [[], 0]; [$type, $field] = $this->parse($name, 'base'); if (is_array($value)) { foreach ($value as $kk => $vv) $count += $this->set("{$field}.{$kk}", $vv); return $count; } else { $this->app->cache->delete($this->table); $data = ['type' => $type, 'name' => $field, 'value' => $value]; $query = $this->app->db->name($this->table)->where(['type' => $type, 'name' => $field]); return (clone $query)->count() > 0 ? $query->update($data) : $query->insert($data); } } /** * 读取配置数据 * @param string $name * @param string $default * @return array|mixed|string * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ public function get($name = '', $default = '') { [$type, $field, $outer] = $this->parse($name, 'base'); if (empty($this->data)) $this->app->db->name($this->table)->cache($this->table)->select()->map(function ($item) { $this->data[$item['type']][$item['name']] = $item['value']; }); if (empty($name)) { return $this->data; } elseif (isset($this->data[$type])) { $group = $this->data[$type]; if ($outer !== 'raw') foreach ($group as $kk => $vo) { $group[$kk] = htmlspecialchars($vo); } return $field ? ($group[$field] ?? $default) : $group; } else { return $default; } } /** * 数据增量保存 * @param Query|string $dbQuery 数据查询对象 * @param array $data 需要保存的数据 * @param string $key 更新条件查询主键 * @param array $where 额外更新查询条件 * @return boolean|integer 失败返回 false, 成功返回主键值或 true * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ public function save($dbQuery, $data, $key = 'id', array $where = []) { $val = $data[$key] ?? null; $query = (is_string($dbQuery) ? $this->app->db->name($dbQuery) : $dbQuery)->master()->strict(false)->where($where); if (empty($where[$key])) is_string($val) && strpos($val, ',') !== false ? $query->whereIn($key, explode(',', $val)) : $query->where([$key => $val]); return is_array($info = (clone $query)->find()) && !empty($info) ? ($query->update($data) !== false ? ($info[$key] ?? true) : false) : $query->insertGetId($data); } /** * 解析缓存名称 * @param string $rule 配置名称 * @param string $type 配置类型 * @return array */ private function parse($rule, $type = 'base') { if (stripos($rule, '.') !== false) { [$type, $rule] = explode('.', $rule, 2); } [$field, $outer] = explode('|', "{$rule}|"); return [$type, $field, strtolower($outer)]; } /** * 生成最短URL地址 * @param string $url 路由地址 * @param array $vars PATH 变量 * @param boolean|string $suffix 后缀 * @param boolean|string $domain 域名 * @return string */ public function sysuri($url = '', array $vars = [], $suffix = true, $domain = false) { $location = $this->app->route->buildUrl($url, $vars)->suffix($suffix)->domain($domain)->build(); [$d1, $d2, $d3] = [$this->app->config->get('app.default_app'), $this->app->config->get('route.default_controller'), $this->app->config->get('route.default_action')]; return preg_replace('|/\.html$|', '', preg_replace(["|^/{$d1}/{$d2}/{$d3}(\.html)?$|i", "|/{$d2}/{$d3}(\.html)?$|i", "|/{$d3}(\.html)?$|i"], ['$1', '$1', '$1'], $location)); } /** * 保存数据内容 * @param string $name * @param mixed $value * @return boolean * @throws \think\db\exception\DataNotFoundException * @throws \think\db\exception\DbException * @throws \think\db\exception\ModelNotFoundException */ public function setData($name, $value) { return $this->save('SystemData', ['name' => $name, 'value' => serialize($value)], 'name'); } /** * 读取数据内容 * @param string $name * @param mixed $default * @return mixed */ public function getData($name, $default = []) { try { $value = $this->app->db->name('SystemData')->where(['name' => $name])->value('value', null); return is_null($value) ? $default : unserialize($value); } catch (\Exception $exception) { return $default; } } /** * 写入系统日志 * @param string $action * @param string $content * @return integer */ public function setOplog($action, $content) { return $this->app->db->name('SystemOplog')->insert([ 'node' => NodeService::instance()->getCurrent(), 'action' => $action, 'content' => $content, 'geoip' => $this->app->request->ip() ?: '127.0.0.1', 'username' => AdminService::instance()->getUserName() ?: '-', ]); } /** * 打印输出数据到文件 * @param mixed $data 输出的数据 * @param boolean $new 强制替换文件 * @param string|null $file 文件名称 * @return false|int */ public function putDebug($data, $new = false, $file = null) { if (is_null($file)) $file = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . date('Ymd') . '.log'; $str = (is_string($data) ? $data : ((is_array($data) || is_object($data)) ? print_r($data, true) : var_export($data, true))) . PHP_EOL; return $new ? file_put_contents($file, $str) : file_put_contents($file, $str, FILE_APPEND); } /** * 判断运行环境 * @param string $type 运行模式(dev|demo|local) * @return boolean */ public function checkRunMode($type = 'dev'): bool { $domain = $this->app->request->host(true); $isDemo = is_numeric(stripos($domain, 'thinkadmin.top')); $isLocal = in_array($domain, ['127.0.0.1', 'localhost']); if ($type === 'dev') return $isLocal || $isDemo; if ($type === 'demo') return $isDemo; if ($type === 'local') return $isLocal; return true; } /** * 判断实时运行模式 * @return boolean */ public function isDebug(): bool { return $this->getRuntime('run') !== 'product'; } /** * 设置运行环境模式 * @param null|boolean $state * @return boolean */ public function productMode($state = null): bool { if (is_null($state)) { return $this->bindRuntime(); } else { return $this->setRuntime([], $state ? 'product' : 'debug'); } } /** * 获取实时运行配置 * @param string $key * @param array $default * @return array */ public function getRuntime($key = null, $default = []) { $filename = "{$this->app->getRootPath()}runtime/config.json"; if (file_exists($filename) && is_file($filename)) { $data = json_decode(file_get_contents($filename), true); } if (empty($data) || !is_array($data)) $data = []; if (empty($data['map']) || !is_array($data['map'])) $data['map'] = []; if (empty($data['uri']) || !is_array($data['uri'])) $data['uri'] = []; if (empty($data['run']) || !is_string($data['run'])) $data['run'] = 'debug'; return is_null($key) ? $data : ($data[$key] ?? $default); } /** * 设置实时运行配置 * @param array|null $map 应用映射 * @param string|null $run 支持模式 * @param array|null $uri 域名映射 * @return boolean 是否调试模式 */ public function setRuntime(array $map = [], $run = null, array $uri = []): bool { $data = $this->getRuntime(); $data['run'] = is_string($run) ? $run : $data['run']; $data['map'] = $this->uniqueArray($data['map'], $map); $data['uri'] = $this->uniqueArray($data['uri'], $uri); $filename = "{$this->app->getRootPath()}runtime/config.json"; file_put_contents($filename, json_encode($data, JSON_UNESCAPED_UNICODE)); return $this->bindRuntime($data); } /** * 绑定应用实时配置 * @param array $data 配置数据 * @return boolean 是否调试模式 */ public function bindRuntime($data = []): bool { // 获取运行配置 if (empty($data)) $data = $this->getRuntime(); // 动态设置应用绑定 $config = ['app_map' => [], 'domain_bind' => []]; if (isset($data['map']) && is_array($data['map']) && count($data['map']) > 0) { $config['app_map'] = $this->uniqueArray($this->app->config->get('app.app_map', []), $data['map']); } if (isset($data['uri']) && is_array($data['uri']) && count($data['uri']) > 0) { $config['domain_bind'] = $this->uniqueArray($this->app->config->get('app.domain_bind', []), $data['uri']); } // 动态设置运行模式 $this->app->config->set($config, 'app'); return $this->app->debug($data['run'] !== 'product')->isDebug(); } /** * 获取唯一数组参数 * @param array ...$args * @return array */ private function uniqueArray(...$args): array { $unique = array_unique(array_reverse(array_merge(...$args))); foreach ($unique as $kk => $vv) if ($kk == $vv) unset($unique[$kk]); return $unique; } /** * 压缩发布项目 */ public function pushRuntime(): void { $type = $this->app->db->getConfig('default'); $this->app->console->call("optimize:schema", ["--connection={$type}"]); foreach (NodeService::instance()->getModules() as $module) { $path = $this->app->getRootPath() . 'runtime' . DIRECTORY_SEPARATOR . $module; file_exists($path) && is_dir($path) or mkdir($path, 0755, true); $this->app->console->call("optimize:route", [$module]); } } /** * 清理运行缓存 */ public function clearRuntime(): void { $data = $this->getRuntime(); $this->app->console->call('clear'); $this->setRuntime($data['map'], $data['run'], $data['uri']); } /** * 初始化并运行应用 * @param \think\App $app */ public function doInit(\think\App $app): void { $app->debug($this->isDebug()); ($response = $app->http->run())->send(); $app->http->end($response); } }