2 Commits

Author SHA1 Message Date
ageerle@163.com
96a1b741df fix: 兼容 Atlas 预测结果查询 404,避免前端轮询刷屏
Atlas /model/prediction/{id} 对已过期或不存在的任务返回 404
(常见于更换 API Key 后残留的旧任务)。原实现直接抛异常,前端
轮询反复触发错误提示。改为 404 时返回 status=failed 的响应,
让上层走正常失败分支落库,前端识别后终止轮询。

Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-15 11:17:20 +08:00
ageerle@163.com
e7b7d1d084 更新README.md
Co-Authored-By: Claude <noreply@anthropic.com>
2026-07-15 10:52:15 +08:00
8 changed files with 83 additions and 4 deletions

View File

@@ -45,7 +45,7 @@
| 🔧 后端服务 | [ruoyi-ai](https://github.com/ageerle/ruoyi-ai) | [ruoyi-ai](https://gitee.com/ageerle/ruoyi-ai) | [ruoyi-ai](https://gitcode.com/ageerle/ruoyi-ai) |
| 🎨 用户前端 | [ruoyi-web](https://github.com/ageerle/ruoyi-web) | [ruoyi-web](https://gitee.com/ageerle/ruoyi-web) | [ruoyi-web](https://gitcode.com/ageerle/ruoyi-web) |
| 🛠️ 管理后台 | [ruoyi-admin](https://github.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitee.com/ageerle/ruoyi-admin) | [ruoyi-admin](https://gitcode.com/ageerle/ruoyi-admin) |
| 🎬 短剧服务 | [ruoyi-drama](https://github.com/ageerle/ruoyi-drama) | [ruoyi-drama](https://gitee.com/ageerle/ruoyi-drama) | |
| 🎬 短剧平台 | [ruoyi-drama](https://github.com/ageerle/ruoyi-drama) | [ruoyi-drama](https://gitee.com/ageerle/ruoyi-drama) | [ruoyi-drama](https://gitcode.com/ageerle/ruoyi-drama) |
### 合作项目
| 项目名称 | GitHub 仓库 | Gitee 仓库
@@ -214,15 +214,15 @@ docker-compose -f docker-compose-all.yaml restart [服务名]
<img src="docs/image/sponsor/atlascloud_banner.png" alt="Atlas Cloud" width="160" height="80">
</a>
[访问Atlas Cloud官网](https://www.atlascloud.ai?ref=89F97E) · [编程计划优惠](https://www.atlascloud.ai/console/coding-plan)
[访问Atlas Cloud官网](https://www.atlascloud.ai?ref=89F97E&utm_source=github&utm_campaign=ruoyi-drama) · [编程计划优惠](https://www.atlascloud.ai/console/coding-plan)
全模态 AI 推理平台,为开发者提供统一的 AI API支持视频生成、图像生成和大语言模型。一次接入即可访问 **300+ 精选模型**
<a href="https://www.volcengine.com/activity/codingplan?utm_campaign=hw&utm_content=hw&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ageerle-ruoyi-ai">
<img src="docs/image/sponsor/huoshan.png" alt="火山引擎 CodingPlan" width="160" height="80">
</a>
[火山引擎 CodingPlan 开发者计划](https://www.volcengine.com/activity/codingplan?utm_campaign=hw&utm_content=hw&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ageerle-ruoyi-ai)
火山引擎是字节跳动旗下的云与 AI 服务平台火山方舟提供豆包大模型、DeepSeek 等多种模型的 API 接入,为开发者提供一站式 AI 开发与推理服务。
[注册即领2500万Tokens立即前往](https://www.volcengine.com/activity/ai618?utm_campaign=hw&utm_content=hw&utm_medium=devrel_tool_web&utm_source=OWO&utm_term=ageerle-ruoyi-ai)
享字节自研豆包模型+满血版开源 SOTA模型覆盖文本、VLM、图像生成全模态一站配齐Seed-2.1、Seedream-5.0、GLM-5.2、DeepSeek等。不止编程、更能解决 Agent 复杂长程任务!
## 💬 社区交流

Binary file not shown.

Before

Width:  |  Height:  |  Size: 127 KiB

After

Width:  |  Height:  |  Size: 112 KiB

View File

@@ -108,6 +108,7 @@ CREATE TABLE `chat_model` (
INSERT INTO `chat_model` VALUES (2000585866022060033, 'chat', 'deepseek-v4-flash', 'deepseek', 'deepseek-v4-flash', NULL, 'Y', 'https://api.deepseek.com', 'sk_xx', 103, 1, '2025-12-15 23:16:54', 1, '2026-03-15 19:18:48', '对话模型', 0);
INSERT INTO `chat_model` VALUES (2007528268536287233, 'vector', 'embedding-3', 'zhipu', 'embedding-3', 2048, 'N', 'https://open.bigmodel.cn', 'sk_xx', 103, 1, '2026-01-04 03:03:32', 1, '2026-03-15 19:18:51', '向量模型', 0);
INSERT INTO `chat_model` VALUES (2045071617578237953, 'rerank', 'rerank', 'zhipu', 'rerank', NULL, 'N', 'https://open.bigmodel.cn', 'sk_xx', 103, 1, '2026-04-17 17:27:24', 1, '2026-04-20 15:21:48', '重排序模型', 0);
INSERT INTO `chat_model` VALUES (2000585866022060003, 'chat', 'deepseek-ai/deepseek-v4-flash', 'atlas', 'deepseek-v4-flash', NULL, 'Y', 'https://api.atlascloud.ai/v1', 'sk_xx', 103, 1, '2025-12-15 23:16:54', 1, '2026-03-15 19:18:48', '对话模型', 0);
INSERT INTO `chat_model` VALUES (2050000000000000001, 'video', 'bytedance/seedance-2.0/text-to-video', 'atlas', 'Seedance 2.0 文生视频(字节跳动)', NULL, 'Y', 'https://api.atlascloud.ai/v1', 'sk_xx', 103, 1, '2026-06-17 18:39:20', 1, '2026-06-17 18:39:20', 'Atlas Cloud 视频模型 - Seedance 2.0', 0);
INSERT INTO `chat_model` VALUES (2060622000000000001, 'image', 'openai/gpt-image-2/text-to-image', 'atlas', 'GPT-IMAGE-2 文生图', NULL, 'Y', 'https://api.atlascloud.ai/v1', 'sk_xx', 103, 1, '2026-06-22 14:45:54', 1, '2026-06-22 14:45:54', 'Atlas Cloud 图片模型 - 文生图,支持三视图、角色设定、场景图', 0);
INSERT INTO `chat_model` VALUES (2060622000000000002, 'image', 'openai/gpt-image-2/edit', 'atlas', 'GPT-IMAGE-2 图生图编辑', NULL, 'Y', 'https://api.atlascloud.ai/v1', 'sk_xx', 103, 1, '2026-06-22 14:45:54', 1, '2026-06-22 14:45:54', 'Atlas Cloud 图片模型 - 图生图/编辑,支持基于参考图修改风格、纹理等', 0);

View File

@@ -0,0 +1,30 @@
package org.ruoyi.common.chat.domain.bo.chat;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
import lombok.EqualsAndHashCode;
import org.ruoyi.common.mybatis.core.domain.BaseEntity;
/**
* 按厂商批量更新密钥业务对象
*
* @author ageerle
* @date 2025-12-14
*/
@Data
@EqualsAndHashCode(callSuper = true)
public class ModelBatchKeyBo extends BaseEntity {
/**
* 模型供应商
*/
@NotBlank(message = "厂商编码不能为空")
private String providerCode;
/**
* 密钥
*/
@NotBlank(message = "密钥不能为空")
private String apiKey;
}

View File

@@ -73,4 +73,13 @@ public interface IChatModelService {
* @return 是否删除成功
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
* 按厂商批量更新密钥
*
* @param providerCode 厂商编码
* @param apiKey 密钥
* @return 是否更新成功
*/
Boolean updateApiKeyByProvider(String providerCode, String apiKey);
}

View File

@@ -8,6 +8,7 @@ import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.ruoyi.common.chat.service.chat.IChatModelService;
import org.ruoyi.common.chat.domain.bo.chat.ChatModelBo;
import org.ruoyi.common.chat.domain.bo.chat.ModelBatchKeyBo;
import org.ruoyi.common.chat.domain.vo.chat.ChatModelVo;
import org.ruoyi.common.core.utils.StringUtils;
import org.ruoyi.enums.ChatModeType;
@@ -121,6 +122,17 @@ public class ChatModelController extends BaseController {
return toAjax(chatModelService.updateByBo(bo));
}
/**
* 按厂商批量更新密钥
*/
@SaCheckPermission("system:model:edit")
@Log(title = "模型管理", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping("/batchKeyByProvider")
public R<Void> batchKeyByProvider(@Validated @RequestBody ModelBatchKeyBo bo) {
return toAjax(chatModelService.updateApiKeyByProvider(bo.getProviderCode(), bo.getApiKey()));
}
/**
* 删除模型管理
*

View File

@@ -10,6 +10,7 @@ import org.ruoyi.common.mybatis.core.page.TableDataInfo;
import org.ruoyi.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
@@ -149,4 +150,19 @@ public class ChatModelServiceImpl implements IChatModelService {
}
return baseMapper.deleteByIds(ids) > 0;
}
/**
* 按厂商批量更新密钥
*
* @param providerCode 厂商编码
* @param apiKey 密钥
* @return 是否更新成功
*/
@Override
public Boolean updateApiKeyByProvider(String providerCode, String apiKey) {
LambdaUpdateWrapper<ChatModel> uw = Wrappers.lambdaUpdate();
uw.set(ChatModel::getApiKey, apiKey)
.eq(ChatModel::getProviderCode, providerCode);
return baseMapper.update(null, uw) > 0;
}
}

View File

@@ -36,6 +36,17 @@ public class AtlasPredictionService {
try (Response response = okHttpClient.newCall(request).execute()) {
ResponseBody body = response.body();
String responseText = body == null ? "" : body.string();
// 404: 预测任务已过期或不存在(常见于更换 API Key 后残留的旧任务),按失败处理,
// 避免前端轮询反复触发异常提示。其余非 2xx 仍按异常抛出。
if (response.code() == 404) {
log.warn("Atlas Cloud 预测任务不存在或已过期: predictionId={}, category={}", predictionId, model.getCategory());
return MediaGenerationResponse.builder()
.type(mediaType(model.getCategory()))
.mimeType("image".equals(model.getCategory()) ? "image/png" : "video/mp4")
.status("failed")
.rawResponse(responseText)
.build();
}
if (!response.isSuccessful()) {
throw new IllegalArgumentException("Atlas Cloud生成结果查询失败: " + response.code() + " - " + responseText);
}