1 line
12 KiB
JSON
1 line
12 KiB
JSON
{"repo": "carey-prog/open-travel-gadget", "files": {"src/main/java/org/carey/travelgadget/service/AiSettingsService.java": {"a470d2f2ae0ff164ef31292b977c40f18002bb74": {"t": 1785745686.0980055, "n": 5647, "h": "34cc02070f088c4f", "c": "package org.carey.travelgadget.service;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport jakarta.annotation.PostConstruct;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.carey.travelgadget.config.SettingsProperties;\nimport org.carey.travelgadget.config.ZhipuApiKeySupport;\nimport org.carey.travelgadget.domain.dto.ApiKeysStatusDto;\nimport org.carey.travelgadget.domain.dto.ApiKeysUpdateRequest;\nimport org.carey.travelgadget.domain.model.StoredApiKeys;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.StringUtils;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\n@Slf4j\n@Service\n@RequiredArgsConstructor\npublic class AiSettingsService {\n\n private final SettingsProperties settingsProperties;\n private final ObjectMapper objectMapper;\n private final RagService ragService;\n\n @Value(\"${travelgadget.deepseek.api-key:}\")\n private String yamlDeepseekKey;\n\n @Value(\"${spring.ai.dashscope.api-key:}\")\n private String yamlDashscopeKey;\n\n @Value(\"${zhipu.api-key:}\")\n private String yamlZhipuKey;\n\n private final StoredApiKeys stored = new StoredApiKeys();\n\n @PostConstruct\n public void loadFromFile() {\n Path path = Path.of(settingsProperties.getFile());\n if (!Files.exists(path)) {\n return;\n }\n try {\n String json = Files.readString(path);\n StoredApiKeys loaded = objectMapper.readValue(json, StoredApiKeys.class);\n mergeStored(loaded);\n log.info(\"已从 {} 加载 API Key 配置\", path.toAbsolutePath());\n } catch (IOException e) {\n log.warn(\"读取 API 配置文件失败: {}\", e.getMessage());\n }\n }\n\n public synchronized void updateKeys(ApiKeysUpdateRequest request) throws IOException {\n if (StringUtils.hasText(request.getDeepseekApiKey())) {\n stored.setDeepseekApiKey(request.getDeepseekApiKey().trim());\n }\n if (StringUtils.hasText(request.getDashscopeApiKey())) {\n stored.setDashscopeApiKey(request.getDashscopeApiKey().trim());\n }\n if (StringUtils.hasText(request.getZhipuApiKey())) {\n stored.setZhipuApiKey(request.getZhipuApiKey().trim());\n }\n persist();\n }\n\n public String resolveDeepseekKey() {\n return firstNonBlank(\n stored.getDeepseekApiKey(),\n yamlDeepseekKey,\n System.getenv(\"DEEPSEEK_API_KEY\")\n );\n }\n\n public String resolveDashscopeKey() {\n return firstNonBlank(\n stored.getDashscopeApiKey(),\n yamlDashscopeKey,\n System.getenv(\"DASHSCOPE_API_KEY\")\n );\n }\n\n public String resolveZhipuKey() {\n String key = firstNonBlank(\n stored.getZhipuApiKey(),\n yamlZhipuKey,\n System.getenv(\"ZHIPU_API_KEY\"),\n System.getenv(\"ZAI_API_KEY\")\n );\n return ZhipuApiKeySupport.isValid(key) ? key : null;\n }\n\n public boolean isDeepseekConfigured() {\n String key = resolveDeepseekKey();\n return StringUtils.hasText(key) && !key.startsWith(\"your-\");\n }\n\n public boolean isDashscopeConfigured() {\n String key = resolveDashscopeKey();\n return StringUtils.hasText(key) && !key.startsWith(\"your-\");\n }\n\n public boolean isZhipuConfigured() {\n return resolveZhipuKey() != null;\n }\n\n public ApiKeysStatusDto getStatus() {\n return ApiKeysStatusDto.builder()\n .deepseekConfigured(isDeepseekConfigured())\n .deepseekMasked(mask(resolveDeepseekKey()))\n .dashscopeConfigured(isDashscopeConfigured())\n .dashscopeMasked(mask(resolveDashscopeKey()))\n .zhipuConfigured(isZhipuConfigured())\n .zhipuMasked(mask(resolveZhipuKey()))\n .ragEnabled(isDashscopeConfigured())\n .ragKnowledgeLoaded(ragService.isKnowledgeLoaded())\n .settingsFile(Path.of(settingsProperties.getFile()).toAbsolutePath().toString())\n .note(\"默认读取 application.yml;本页保存会写入 data/api-settings.json 并覆盖。DeepSeek、智谱可热更新;DashScope 修改后需重启。\")\n .build();\n }\n\n private void persist() throws IOException {\n Path path = Path.of(settingsProperties.getFile());\n Files.createDirectories(path.getParent());\n objectMapper.writerWithDefaultPrettyPrinter().writeValue(path.toFile(), stored);\n log.info(\"API Key 已保存到 {}\", path.toAbsolutePath());\n }\n\n private void mergeStored(StoredApiKeys loaded) {\n if (StringUtils.hasText(loaded.getDeepseekApiKey())) {\n stored.setDeepseekApiKey(loaded.getDeepseekApiKey());\n }\n if (StringUtils.hasText(loaded.getDashscopeApiKey())) {\n stored.setDashscopeApiKey(loaded.getDashscopeApiKey());\n }\n if (StringUtils.hasText(loaded.getZhipuApiKey())) {\n stored.setZhipuApiKey(loaded.getZhipuApiKey());\n }\n }\n\n private String firstNonBlank(String... values) {\n for (String v : values) {\n if (StringUtils.hasText(v)) {\n return v.trim();\n }\n }\n return \"\";\n }\n\n public static String mask(String key) {\n if (!StringUtils.hasText(key)) {\n return \"未配置\";\n }\n String trimmed = key.trim();\n if (trimmed.length() <= 8) {\n return \"****\";\n }\n return trimmed.substring(0, 4) + \"****\" + trimmed.substring(trimmed.length() - 4);\n }\n}\n"}, "5e41dee662ef09c6709e9a3b8fdb20600a118442": {"t": 1785755710.589886, "n": 5647, "h": "34cc02070f088c4f", "c": "package org.carey.travelgadget.service;\n\nimport com.fasterxml.jackson.databind.ObjectMapper;\nimport jakarta.annotation.PostConstruct;\nimport lombok.RequiredArgsConstructor;\nimport lombok.extern.slf4j.Slf4j;\nimport org.carey.travelgadget.config.SettingsProperties;\nimport org.carey.travelgadget.config.ZhipuApiKeySupport;\nimport org.carey.travelgadget.domain.dto.ApiKeysStatusDto;\nimport org.carey.travelgadget.domain.dto.ApiKeysUpdateRequest;\nimport org.carey.travelgadget.domain.model.StoredApiKeys;\nimport org.springframework.beans.factory.annotation.Value;\nimport org.springframework.stereotype.Service;\nimport org.springframework.util.StringUtils;\n\nimport java.io.IOException;\nimport java.nio.file.Files;\nimport java.nio.file.Path;\n\n@Slf4j\n@Service\n@RequiredArgsConstructor\npublic class AiSettingsService {\n\n private final SettingsProperties settingsProperties;\n private final ObjectMapper objectMapper;\n private final RagService ragService;\n\n @Value(\"${travelgadget.deepseek.api-key:}\")\n private String yamlDeepseekKey;\n\n @Value(\"${spring.ai.dashscope.api-key:}\")\n private String yamlDashscopeKey;\n\n @Value(\"${zhipu.api-key:}\")\n private String yamlZhipuKey;\n\n private final StoredApiKeys stored = new StoredApiKeys();\n\n @PostConstruct\n public void loadFromFile() {\n Path path = Path.of(settingsProperties.getFile());\n if (!Files.exists(path)) {\n return;\n }\n try {\n String json = Files.readString(path);\n StoredApiKeys loaded = objectMapper.readValue(json, StoredApiKeys.class);\n mergeStored(loaded);\n log.info(\"已从 {} 加载 API Key 配置\", path.toAbsolutePath());\n } catch (IOException e) {\n log.warn(\"读取 API 配置文件失败: {}\", e.getMessage());\n }\n }\n\n public synchronized void updateKeys(ApiKeysUpdateRequest request) throws IOException {\n if (StringUtils.hasText(request.getDeepseekApiKey())) {\n stored.setDeepseekApiKey(request.getDeepseekApiKey().trim());\n }\n if (StringUtils.hasText(request.getDashscopeApiKey())) {\n stored.setDashscopeApiKey(request.getDashscopeApiKey().trim());\n }\n if (StringUtils.hasText(request.getZhipuApiKey())) {\n stored.setZhipuApiKey(request.getZhipuApiKey().trim());\n }\n persist();\n }\n\n public String resolveDeepseekKey() {\n return firstNonBlank(\n stored.getDeepseekApiKey(),\n yamlDeepseekKey,\n System.getenv(\"DEEPSEEK_API_KEY\")\n );\n }\n\n public String resolveDashscopeKey() {\n return firstNonBlank(\n stored.getDashscopeApiKey(),\n yamlDashscopeKey,\n System.getenv(\"DASHSCOPE_API_KEY\")\n );\n }\n\n public String resolveZhipuKey() {\n String key = firstNonBlank(\n stored.getZhipuApiKey(),\n yamlZhipuKey,\n System.getenv(\"ZHIPU_API_KEY\"),\n System.getenv(\"ZAI_API_KEY\")\n );\n return ZhipuApiKeySupport.isValid(key) ? key : null;\n }\n\n public boolean isDeepseekConfigured() {\n String key = resolveDeepseekKey();\n return StringUtils.hasText(key) && !key.startsWith(\"your-\");\n }\n\n public boolean isDashscopeConfigured() {\n String key = resolveDashscopeKey();\n return StringUtils.hasText(key) && !key.startsWith(\"your-\");\n }\n\n public boolean isZhipuConfigured() {\n return resolveZhipuKey() != null;\n }\n\n public ApiKeysStatusDto getStatus() {\n return ApiKeysStatusDto.builder()\n .deepseekConfigured(isDeepseekConfigured())\n .deepseekMasked(mask(resolveDeepseekKey()))\n .dashscopeConfigured(isDashscopeConfigured())\n .dashscopeMasked(mask(resolveDashscopeKey()))\n .zhipuConfigured(isZhipuConfigured())\n .zhipuMasked(mask(resolveZhipuKey()))\n .ragEnabled(isDashscopeConfigured())\n .ragKnowledgeLoaded(ragService.isKnowledgeLoaded())\n .settingsFile(Path.of(settingsProperties.getFile()).toAbsolutePath().toString())\n .note(\"默认读取 application.yml;本页保存会写入 data/api-settings.json 并覆盖。DeepSeek、智谱可热更新;DashScope 修改后需重启。\")\n .build();\n }\n\n private void persist() throws IOException {\n Path path = Path.of(settingsProperties.getFile());\n Files.createDirectories(path.getParent());\n objectMapper.writerWithDefaultPrettyPrinter().writeValue(path.toFile(), stored);\n log.info(\"API Key 已保存到 {}\", path.toAbsolutePath());\n }\n\n private void mergeStored(StoredApiKeys loaded) {\n if (StringUtils.hasText(loaded.getDeepseekApiKey())) {\n stored.setDeepseekApiKey(loaded.getDeepseekApiKey());\n }\n if (StringUtils.hasText(loaded.getDashscopeApiKey())) {\n stored.setDashscopeApiKey(loaded.getDashscopeApiKey());\n }\n if (StringUtils.hasText(loaded.getZhipuApiKey())) {\n stored.setZhipuApiKey(loaded.getZhipuApiKey());\n }\n }\n\n private String firstNonBlank(String... values) {\n for (String v : values) {\n if (StringUtils.hasText(v)) {\n return v.trim();\n }\n }\n return \"\";\n }\n\n public static String mask(String key) {\n if (!StringUtils.hasText(key)) {\n return \"未配置\";\n }\n String trimmed = key.trim();\n if (trimmed.length() <= 8) {\n return \"****\";\n }\n return trimmed.substring(0, 4) + \"****\" + trimmed.substring(trimmed.length() - 4);\n }\n}\n"}}}} |