This commit is contained in:
van
2026-04-05 20:47:01 +08:00
parent 43e44c8f7f
commit 8a77598c88
3 changed files with 460 additions and 113 deletions

View File

@@ -157,13 +157,27 @@ public class SocialMediaController extends BaseController
} }
/** /**
* 获取大模型接入配置API 地址、密钥等,与 Jarvis 共用 Redis * 列出多套大模型接入配置及当前激活 id
*/ */
@GetMapping("/llm-config") @GetMapping("/llm-config")
public AjaxResult getLlmConfig() public AjaxResult listLlmProfiles()
{ {
try { try {
return socialMediaService.getLlmConfig(); return socialMediaService.listLlmProfiles();
} catch (Exception e) {
logger.error("列出大模型接入配置失败", e);
return AjaxResult.error("获取失败: " + e.getMessage());
}
}
/**
* 获取单套配置(编辑)
*/
@GetMapping("/llm-config/profiles/{id}")
public AjaxResult getLlmProfile(@PathVariable("id") String id)
{
try {
return socialMediaService.getLlmProfile(id);
} catch (Exception e) { } catch (Exception e) {
logger.error("获取大模型接入配置失败", e); logger.error("获取大模型接入配置失败", e);
return AjaxResult.error("获取失败: " + e.getMessage()); return AjaxResult.error("获取失败: " + e.getMessage());
@@ -171,32 +185,92 @@ public class SocialMediaController extends BaseController
} }
/** /**
* 保存大模型接入配置 * 新增一套配置
*/ */
@Log(title = "保存大模型接入配置", businessType = BusinessType.UPDATE) @Log(title = "新增大模型接入配置", businessType = BusinessType.INSERT)
@PostMapping("/llm-config/save") @PostMapping("/llm-config/profiles")
public AjaxResult saveLlmConfig(@RequestBody Map<String, Object> request) public AjaxResult createLlmProfile(@RequestBody Map<String, Object> request)
{ {
try { try {
return socialMediaService.saveLlmConfig(request); return socialMediaService.createLlmProfile(request);
} catch (Exception e) { } catch (Exception e) {
logger.error("保存大模型接入配置失败", e); logger.error("新增大模型接入配置失败", e);
return AjaxResult.error("保存失败: " + e.getMessage()); return AjaxResult.error("保存失败: " + e.getMessage());
} }
} }
/** /**
* 清除大模型接入配置Jarvis 使用 yml 默认 Ollama * 更新一套配置
*/ */
@Log(title = "重置大模型接入配置", businessType = BusinessType.DELETE) @Log(title = "更新大模型接入配置", businessType = BusinessType.UPDATE)
@DeleteMapping("/llm-config") @PutMapping("/llm-config/profiles/{id}")
public AjaxResult resetLlmConfig() public AjaxResult updateLlmProfile(@PathVariable("id") String id, @RequestBody Map<String, Object> request)
{ {
try { try {
return socialMediaService.resetLlmConfig(); return socialMediaService.updateLlmProfile(id, request);
} catch (Exception e) { } catch (Exception e) {
logger.error("重置大模型接入配置失败", e); logger.error("更新大模型接入配置失败", e);
return AjaxResult.error("重置失败: " + e.getMessage()); return AjaxResult.error("保存失败: " + e.getMessage());
}
}
/**
* 删除一套配置
*/
@Log(title = "删除大模型接入配置", businessType = BusinessType.DELETE)
@DeleteMapping("/llm-config/profiles/{id}")
public AjaxResult deleteLlmProfile(@PathVariable("id") String id)
{
try {
return socialMediaService.deleteLlmProfile(id);
} catch (Exception e) {
logger.error("删除大模型接入配置失败", e);
return AjaxResult.error("删除失败: " + e.getMessage());
}
}
/**
* 激活指定配置为当前使用
*/
@Log(title = "激活大模型接入配置", businessType = BusinessType.UPDATE)
@PutMapping("/llm-config/active/{id}")
public AjaxResult setActiveLlmProfile(@PathVariable("id") String id)
{
try {
return socialMediaService.setActiveLlmProfile(id);
} catch (Exception e) {
logger.error("激活大模型接入配置失败", e);
return AjaxResult.error("操作失败: " + e.getMessage());
}
}
/**
* 取消激活Jarvis 使用 yml 默认 Ollama
*/
@Log(title = "取消激活大模型接入配置", businessType = BusinessType.UPDATE)
@DeleteMapping("/llm-config/active")
public AjaxResult clearActiveLlmProfile()
{
try {
return socialMediaService.clearActiveLlmProfile();
} catch (Exception e) {
logger.error("取消激活大模型接入配置失败", e);
return AjaxResult.error("操作失败: " + e.getMessage());
}
}
/**
* 清空所有套及旧版单键
*/
@Log(title = "清空大模型接入配置", businessType = BusinessType.DELETE)
@DeleteMapping("/llm-config")
public AjaxResult resetAllLlmConfig()
{
try {
return socialMediaService.resetAllLlmConfig();
} catch (Exception e) {
logger.error("清空大模型接入配置失败", e);
return AjaxResult.error("清除失败: " + e.getMessage());
} }
} }

View File

@@ -73,19 +73,28 @@ public interface ISocialMediaService
*/ */
Map<String, Object> generateXianyuWenan(String title, String remark); Map<String, Object> generateXianyuWenan(String title, String remark);
/** /** 列出多套大模型接入配置及当前激活的 id与 Jarvis 共用 Redis */
* 获取社媒大模型接入配置(与 Jarvis 共用 Redis供 OpenAI 兼容接口或 Ollama com.ruoyi.common.core.domain.AjaxResult listLlmProfiles();
*/
com.ruoyi.common.core.domain.AjaxResult getLlmConfig();
/** /** 获取单套配置(编辑用,密钥脱敏) */
* 保存社媒大模型接入配置 com.ruoyi.common.core.domain.AjaxResult getLlmProfile(String id);
*/
com.ruoyi.common.core.domain.AjaxResult saveLlmConfig(Map<String, Object> request);
/** /** 新增一套配置 */
* 清除 Redis 中的接入配置Jarvis 侧回退为 application.yml 默认 Ollama com.ruoyi.common.core.domain.AjaxResult createLlmProfile(Map<String, Object> request);
*/
com.ruoyi.common.core.domain.AjaxResult resetLlmConfig(); /** 更新一套配置 */
com.ruoyi.common.core.domain.AjaxResult updateLlmProfile(String id, Map<String, Object> request);
/** 删除一套配置 */
com.ruoyi.common.core.domain.AjaxResult deleteLlmProfile(String id);
/** 激活指定 idJarvis 调用时使用该套 */
com.ruoyi.common.core.domain.AjaxResult setActiveLlmProfile(String id);
/** 取消激活Jarvis 回退 yml 默认 Ollama */
com.ruoyi.common.core.domain.AjaxResult clearActiveLlmProfile();
/** 清空所有套及旧版单键Jarvis 回退默认 */
com.ruoyi.common.core.domain.AjaxResult resetAllLlmConfig();
} }

View File

@@ -13,8 +13,12 @@ import org.springframework.beans.factory.annotation.Value;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.UUID;
import java.util.regex.Pattern; import java.util.regex.Pattern;
import java.util.regex.PatternSyntaxException; import java.util.regex.PatternSyntaxException;
@@ -60,7 +64,9 @@ public class SocialMediaServiceImpl implements ISocialMediaService
put("xianyu:title_clean_regex", "闲鱼文案·标题/型号清洗正则\n从标题与型号备注中移除营销敏感片段须为 Java 正则,匹配到的内容会被删除"); put("xianyu:title_clean_regex", "闲鱼文案·标题/型号清洗正则\n从标题与型号备注中移除营销敏感片段须为 Java 正则,匹配到的内容会被删除");
}}; }};
/** 与 Jarvis_java SocialMediaLlmClient 使用相同 Redis 键 */ /** 多套大模型 JSON 存储,与 Jarvis_java SocialMediaLlmClient 一致 */
private static final String LLM_PROFILES_STORE_KEY = "social_media:llm:profiles_store";
/** 旧版单套 Redis 键,首次读取时迁移进 profiles_store 后删除 */
private static final String LLM_KEY_MODE = "social_media:llm:mode"; private static final String LLM_KEY_MODE = "social_media:llm:mode";
private static final String LLM_KEY_BASE_URL = "social_media:llm:base_url"; private static final String LLM_KEY_BASE_URL = "social_media:llm:base_url";
private static final String LLM_KEY_API_KEY = "social_media:llm:api_key"; private static final String LLM_KEY_API_KEY = "social_media:llm:api_key";
@@ -518,35 +524,257 @@ public class SocialMediaServiceImpl implements ISocialMediaService
} }
@Override @Override
public AjaxResult getLlmConfig() { public AjaxResult listLlmProfiles() {
try { try {
Map<String, Object> data = new HashMap<>(); Map<String, Object> data = new HashMap<>();
if (redisTemplate == null) { if (redisTemplate == null) {
data.put("redisAvailable", false); data.put("redisAvailable", false);
data.put("mode", LLM_MODE_OLLAMA); data.put("activeId", null);
data.put("baseUrl", ""); data.put("profiles", new ArrayList<>());
data.put("model", "");
data.put("hasApiKey", false);
data.put("apiKeyMasked", null);
return AjaxResult.success(data); return AjaxResult.success(data);
} }
data.put("redisAvailable", true); data.put("redisAvailable", true);
String modeRaw = StringUtils.trim(redisTemplate.opsForValue().get(LLM_KEY_MODE)); JSONObject store = loadOrMigrateLlmStore();
String mode = StringUtils.isEmpty(modeRaw) ? LLM_MODE_OLLAMA : modeRaw.toLowerCase(); JSONObject profiles = store.getJSONObject("profiles");
data.put("mode", mode); if (profiles == null) {
data.put("baseUrl", StringUtils.trim(redisTemplate.opsForValue().get(LLM_KEY_BASE_URL))); profiles = new JSONObject();
data.put("model", StringUtils.trim(redisTemplate.opsForValue().get(LLM_KEY_MODEL))); }
String rawKey = redisTemplate.opsForValue().get(LLM_KEY_API_KEY); String activeId = store.getString("activeId");
boolean hasKey = StringUtils.isNotEmpty(StringUtils.trim(rawKey)); List<Map<String, Object>> list = new ArrayList<>();
data.put("hasApiKey", hasKey); for (String id : profiles.keySet()) {
data.put("apiKeyMasked", maskLlmApiKey(rawKey)); JSONObject p = profiles.getJSONObject(id);
if (p == null) {
continue;
}
list.add(profileToSummaryMap(id, p, StringUtils.isNotEmpty(activeId) && activeId.equals(id)));
}
list.sort(Comparator.comparing(m -> String.valueOf(m.getOrDefault("name", "")), String.CASE_INSENSITIVE_ORDER));
data.put("activeId", activeId);
data.put("profiles", list);
return AjaxResult.success(data); return AjaxResult.success(data);
} catch (Exception e) { } catch (Exception e) {
log.error("读取大模型接入配置失败", e); log.error("列出大模型配置失败", e);
return AjaxResult.error("读取失败: " + e.getMessage()); return AjaxResult.error("读取失败: " + e.getMessage());
} }
} }
@Override
public AjaxResult getLlmProfile(String id) {
try {
if (redisTemplate == null) {
return AjaxResult.error("Redis未配置");
}
if (StringUtils.isEmpty(id)) {
return AjaxResult.error("配置 id 不能为空");
}
JSONObject store = loadOrMigrateLlmStore();
JSONObject profiles = store.getJSONObject("profiles");
if (profiles == null || !profiles.containsKey(id)) {
return AjaxResult.error("配置不存在");
}
JSONObject p = profiles.getJSONObject(id);
Map<String, Object> row = new HashMap<>();
row.put("id", id);
row.put("name", StringUtils.trim(p.getString("name")));
row.put("mode", normalizeLlmMode(p.getString("mode")));
row.put("baseUrl", StringUtils.trim(p.getString("baseUrl")));
row.put("model", StringUtils.trim(p.getString("model")));
String rawKey = p.getString("apiKey");
row.put("hasApiKey", StringUtils.isNotEmpty(StringUtils.trim(rawKey)));
row.put("apiKeyMasked", maskLlmApiKey(rawKey));
return AjaxResult.success(row);
} catch (Exception e) {
log.error("获取大模型配置失败", e);
return AjaxResult.error("读取失败: " + e.getMessage());
}
}
@Override
public AjaxResult createLlmProfile(Map<String, Object> request) {
try {
if (redisTemplate == null) {
return AjaxResult.error("Redis未配置无法保存");
}
AjaxResult validation = validateLlmProfilePayload(request, true);
if (validation != null) {
return validation;
}
String name = request.get("name").toString().trim();
String mode = request.get("mode").toString().trim().toLowerCase();
String baseUrl = request.get("baseUrl") == null ? "" : request.get("baseUrl").toString().trim();
String model = request.get("model") == null ? "" : request.get("model").toString().trim();
JSONObject store = loadOrMigrateLlmStore();
JSONObject profiles = store.getJSONObject("profiles");
if (profiles == null) {
profiles = new JSONObject();
store.put("profiles", profiles);
}
String id = UUID.randomUUID().toString();
JSONObject p = new JSONObject();
p.put("name", name);
p.put("mode", mode);
p.put("baseUrl", baseUrl);
p.put("model", model);
String apiKey = "";
Object apiKeyObj = request.get("apiKey");
if (apiKeyObj != null && StringUtils.isNotEmpty(apiKeyObj.toString().trim())) {
apiKey = apiKeyObj.toString().trim();
}
p.put("apiKey", apiKey);
profiles.put(id, p);
if (StringUtils.isEmpty(store.getString("activeId")) && profiles.size() == 1) {
store.put("activeId", id);
}
persistLlmStore(store);
log.info("新增大模型配置: id={}, name={}", id, name);
Map<String, Object> res = new HashMap<>();
res.put("id", id);
return AjaxResult.success(res);
} catch (Exception e) {
log.error("新增大模型配置失败", e);
return AjaxResult.error("保存失败: " + e.getMessage());
}
}
@Override
public AjaxResult updateLlmProfile(String id, Map<String, Object> request) {
try {
if (redisTemplate == null) {
return AjaxResult.error("Redis未配置无法保存");
}
if (StringUtils.isEmpty(id)) {
return AjaxResult.error("配置 id 不能为空");
}
AjaxResult validation = validateLlmProfilePayload(request, true);
if (validation != null) {
return validation;
}
JSONObject store = loadOrMigrateLlmStore();
JSONObject profiles = store.getJSONObject("profiles");
if (profiles == null || !profiles.containsKey(id)) {
return AjaxResult.error("配置不存在");
}
JSONObject p = profiles.getJSONObject(id);
p.put("name", request.get("name").toString().trim());
String mode = request.get("mode").toString().trim().toLowerCase();
p.put("mode", mode);
String baseUrl = request.get("baseUrl") == null ? "" : request.get("baseUrl").toString().trim();
String model = request.get("model") == null ? "" : request.get("model").toString().trim();
p.put("baseUrl", baseUrl);
p.put("model", model);
boolean clearApiKey = Boolean.TRUE.equals(request.get("clearApiKey"))
|| "true".equalsIgnoreCase(String.valueOf(request.get("clearApiKey")));
if (clearApiKey) {
p.put("apiKey", "");
} else {
Object apiKeyObj = request.get("apiKey");
if (apiKeyObj != null && StringUtils.isNotEmpty(apiKeyObj.toString().trim())) {
p.put("apiKey", apiKeyObj.toString().trim());
}
}
persistLlmStore(store);
log.info("更新大模型配置: id={}", id);
return AjaxResult.success("保存成功");
} catch (Exception e) {
log.error("更新大模型配置失败", e);
return AjaxResult.error("保存失败: " + e.getMessage());
}
}
@Override
public AjaxResult deleteLlmProfile(String id) {
try {
if (redisTemplate == null) {
return AjaxResult.error("Redis未配置");
}
if (StringUtils.isEmpty(id)) {
return AjaxResult.error("配置 id 不能为空");
}
JSONObject store = loadOrMigrateLlmStore();
JSONObject profiles = store.getJSONObject("profiles");
if (profiles == null || !profiles.containsKey(id)) {
return AjaxResult.error("配置不存在");
}
profiles.remove(id);
String activeId = store.getString("activeId");
if (id.equals(activeId)) {
if (profiles.isEmpty()) {
store.put("activeId", null);
} else {
store.put("activeId", profiles.keySet().iterator().next());
}
}
persistLlmStore(store);
log.info("删除大模型配置: id={}", id);
return AjaxResult.success("删除成功");
} catch (Exception e) {
log.error("删除大模型配置失败", e);
return AjaxResult.error("删除失败: " + e.getMessage());
}
}
@Override
public AjaxResult setActiveLlmProfile(String id) {
try {
if (redisTemplate == null) {
return AjaxResult.error("Redis未配置");
}
if (StringUtils.isEmpty(id)) {
return AjaxResult.error("配置 id 不能为空");
}
JSONObject store = loadOrMigrateLlmStore();
JSONObject profiles = store.getJSONObject("profiles");
if (profiles == null || !profiles.containsKey(id)) {
return AjaxResult.error("配置不存在");
}
store.put("activeId", id);
persistLlmStore(store);
log.info("已激活大模型配置: id={}", id);
return AjaxResult.success("已切换为当前使用的配置");
} catch (Exception e) {
log.error("激活大模型配置失败", e);
return AjaxResult.error("操作失败: " + e.getMessage());
}
}
@Override
public AjaxResult clearActiveLlmProfile() {
try {
if (redisTemplate == null) {
return AjaxResult.error("Redis未配置");
}
JSONObject store = loadOrMigrateLlmStore();
store.put("activeId", null);
persistLlmStore(store);
log.info("已取消激活大模型配置");
return AjaxResult.success("已取消激活Jarvis 将使用默认 Ollama");
} catch (Exception e) {
log.error("取消激活大模型配置失败", e);
return AjaxResult.error("操作失败: " + e.getMessage());
}
}
@Override
public AjaxResult resetAllLlmConfig() {
try {
if (redisTemplate == null) {
return AjaxResult.error("Redis未配置");
}
redisTemplate.delete(LLM_PROFILES_STORE_KEY);
redisTemplate.delete(LLM_KEY_MODE);
redisTemplate.delete(LLM_KEY_BASE_URL);
redisTemplate.delete(LLM_KEY_API_KEY);
redisTemplate.delete(LLM_KEY_MODEL);
log.info("已清空所有大模型接入配置");
return AjaxResult.success("已清空Jarvis 将使用默认 Ollama");
} catch (Exception e) {
log.error("清空大模型配置失败", e);
return AjaxResult.error("清除失败: " + e.getMessage());
}
}
private static String maskLlmApiKey(String secret) { private static String maskLlmApiKey(String secret) {
if (StringUtils.isEmpty(StringUtils.trim(secret))) { if (StringUtils.isEmpty(StringUtils.trim(secret))) {
return null; return null;
@@ -558,78 +786,114 @@ public class SocialMediaServiceImpl implements ISocialMediaService
return "****" + s.substring(s.length() - 4); return "****" + s.substring(s.length() - 4);
} }
@Override private static String normalizeLlmMode(String mode) {
public AjaxResult saveLlmConfig(Map<String, Object> request) { if (StringUtils.isEmpty(mode)) {
try { return LLM_MODE_OLLAMA;
if (redisTemplate == null) {
return AjaxResult.error("Redis未配置无法保存");
}
String mode = request.get("mode") == null ? "" : request.get("mode").toString().trim().toLowerCase();
if (!LLM_MODE_OLLAMA.equals(mode) && !LLM_MODE_OPENAI.equals(mode)) {
return AjaxResult.error("接入方式只能是 ollama 或 openai");
}
String baseUrl = request.get("baseUrl") == null ? "" : request.get("baseUrl").toString().trim();
String model = request.get("model") == null ? "" : request.get("model").toString().trim();
if (LLM_MODE_OPENAI.equals(mode)) {
if (StringUtils.isEmpty(baseUrl)) {
return AjaxResult.error("OpenAI 兼容模式须填写 Chat Completions 完整地址(如 https://api.openai.com/v1/chat/completions");
}
if (StringUtils.isEmpty(model)) {
return AjaxResult.error("OpenAI 兼容模式须填写模型名称");
}
}
boolean clearApiKey = Boolean.TRUE.equals(request.get("clearApiKey"))
|| "true".equalsIgnoreCase(String.valueOf(request.get("clearApiKey")));
if (clearApiKey) {
redisTemplate.delete(LLM_KEY_API_KEY);
}
Object apiKeyObj = request.get("apiKey");
if (!clearApiKey && apiKeyObj != null) {
String newKey = apiKeyObj.toString().trim();
if (StringUtils.isNotEmpty(newKey)) {
redisTemplate.opsForValue().set(LLM_KEY_API_KEY, newKey);
}
}
redisTemplate.opsForValue().set(LLM_KEY_MODE, mode);
if (StringUtils.isEmpty(baseUrl)) {
redisTemplate.delete(LLM_KEY_BASE_URL);
} else {
redisTemplate.opsForValue().set(LLM_KEY_BASE_URL, baseUrl);
}
if (StringUtils.isEmpty(model)) {
redisTemplate.delete(LLM_KEY_MODEL);
} else {
redisTemplate.opsForValue().set(LLM_KEY_MODEL, model);
}
log.info("保存大模型接入配置成功, mode={}", mode);
return AjaxResult.success("保存成功");
} catch (Exception e) {
log.error("保存大模型接入配置失败", e);
return AjaxResult.error("保存失败: " + e.getMessage());
} }
String m = mode.trim().toLowerCase();
return LLM_MODE_OPENAI.equals(m) ? LLM_MODE_OPENAI : LLM_MODE_OLLAMA;
} }
@Override /**
public AjaxResult resetLlmConfig() { * @return 校验失败时返回 error AjaxResult成功返回 null
try { */
if (redisTemplate == null) { private AjaxResult validateLlmProfilePayload(Map<String, Object> request, boolean requireName) {
return AjaxResult.error("Redis未配置"); if (request == null) {
} return AjaxResult.error("请求体不能为空");
redisTemplate.delete(LLM_KEY_MODE);
redisTemplate.delete(LLM_KEY_BASE_URL);
redisTemplate.delete(LLM_KEY_API_KEY);
redisTemplate.delete(LLM_KEY_MODEL);
log.info("已清除大模型接入 Redis 配置");
return AjaxResult.success("已恢复为 Jarvis 默认 Ollama 配置");
} catch (Exception e) {
log.error("清除大模型接入配置失败", e);
return AjaxResult.error("清除失败: " + e.getMessage());
} }
Object nameObj = request.get("name");
if (requireName && (nameObj == null || StringUtils.isEmpty(StringUtils.trim(nameObj.toString())))) {
return AjaxResult.error("请填写配置名称(用于区分多套)");
}
String mode = request.get("mode") == null ? "" : request.get("mode").toString().trim().toLowerCase();
if (!LLM_MODE_OLLAMA.equals(mode) && !LLM_MODE_OPENAI.equals(mode)) {
return AjaxResult.error("接入方式只能是 ollama 或 openai");
}
String baseUrl = request.get("baseUrl") == null ? "" : request.get("baseUrl").toString().trim();
String model = request.get("model") == null ? "" : request.get("model").toString().trim();
if (LLM_MODE_OPENAI.equals(mode)) {
if (StringUtils.isEmpty(baseUrl)) {
return AjaxResult.error("OpenAI 兼容模式须填写 Chat Completions 完整地址");
}
if (StringUtils.isEmpty(model)) {
return AjaxResult.error("OpenAI 兼容模式须填写模型名称");
}
}
return null;
}
private Map<String, Object> profileToSummaryMap(String id, JSONObject p, boolean active) {
Map<String, Object> m = new HashMap<>();
m.put("id", id);
m.put("name", StringUtils.trim(p.getString("name")));
m.put("mode", normalizeLlmMode(p.getString("mode")));
m.put("baseUrl", StringUtils.trim(p.getString("baseUrl")));
m.put("model", StringUtils.trim(p.getString("model")));
String rawKey = p.getString("apiKey");
m.put("hasApiKey", StringUtils.isNotEmpty(StringUtils.trim(rawKey)));
m.put("apiKeyMasked", maskLlmApiKey(rawKey));
m.put("active", active);
return m;
}
private void persistLlmStore(JSONObject store) {
redisTemplate.opsForValue().set(LLM_PROFILES_STORE_KEY, store.toJSONString());
}
/**
* 读取存储;若无 JSON 则尝试从旧版单键迁移并写入。
*/
private JSONObject loadOrMigrateLlmStore() {
String raw = redisTemplate.opsForValue().get(LLM_PROFILES_STORE_KEY);
if (StringUtils.isNotEmpty(raw)) {
try {
JSONObject o = JSON.parseObject(raw);
if (o != null) {
if (!o.containsKey("profiles") || o.get("profiles") == null) {
o.put("profiles", new JSONObject());
}
return o;
}
} catch (Exception e) {
log.warn("解析 LLM profiles_store 失败,将尝试迁移或重建", e);
}
}
return migrateLegacyLlmToStore();
}
private JSONObject migrateLegacyLlmToStore() {
JSONObject store = new JSONObject();
JSONObject profiles = new JSONObject();
String modeRaw = StringUtils.trim(redisTemplate.opsForValue().get(LLM_KEY_MODE));
String baseUrl = StringUtils.trim(redisTemplate.opsForValue().get(LLM_KEY_BASE_URL));
String model = StringUtils.trim(redisTemplate.opsForValue().get(LLM_KEY_MODEL));
String apiKeyRaw = redisTemplate.opsForValue().get(LLM_KEY_API_KEY);
String apiKey = StringUtils.trim(apiKeyRaw);
boolean anyLegacy = StringUtils.isNotEmpty(modeRaw) || StringUtils.isNotEmpty(baseUrl)
|| StringUtils.isNotEmpty(model) || StringUtils.isNotEmpty(apiKey);
if (!anyLegacy) {
store.put("profiles", profiles);
store.put("activeId", null);
return store;
}
String id = "migrated-" + UUID.randomUUID().toString().replace("-", "").substring(0, 12);
JSONObject p = new JSONObject();
p.put("name", "迁移自旧版单套配置");
p.put("mode", StringUtils.isEmpty(modeRaw) ? LLM_MODE_OLLAMA : modeRaw.toLowerCase());
p.put("baseUrl", baseUrl == null ? "" : baseUrl);
p.put("model", model == null ? "" : model);
p.put("apiKey", apiKey == null ? "" : apiKey);
profiles.put(id, p);
store.put("profiles", profiles);
store.put("activeId", id);
persistLlmStore(store);
redisTemplate.delete(LLM_KEY_MODE);
redisTemplate.delete(LLM_KEY_BASE_URL);
redisTemplate.delete(LLM_KEY_API_KEY);
redisTemplate.delete(LLM_KEY_MODEL);
log.info("已从旧版单键迁移大模型配置到多套存储");
return store;
} }
/** /**