fix: bug打印

This commit is contained in:
old burden 2026-04-01 15:22:16 +08:00
parent fec303b68c
commit 93ed944c14
5 changed files with 147 additions and 29 deletions

View File

@ -4,6 +4,7 @@ import com.ruoyi.ai.domain.*;
import com.ruoyi.ai.service.IAiManagerService;
import com.ruoyi.ai.service.IAiOrderService;
import com.ruoyi.ai.service.IAiTagService;
import com.ruoyi.ai.service.IByteDeptApiKeyService;
import com.ruoyi.ai.service.IByteService;
import com.ruoyi.api.request.ByteApiRequest;
import com.ruoyi.common.annotation.Anonymous;
@ -40,12 +41,10 @@ public class ByteApiController extends BaseController {
private final IAiOrderService aiOrderService;
private final IAiManagerService managerService;
private final IAiTagService aiTagService;
private final IByteDeptApiKeyService byteDeptApiKeyService;
@Value("${byteapi.callBackUrl}")
private String url;
// 火山引擎配置
@Value("${volcengine.ark.apiKey}")
private String volcApiKey;
@Value("${volcengine.ark.baseUrl}")
private String volcBaseUrl;
@Value("${volcengine.ark.callbackUrl}")
@ -105,7 +104,8 @@ public class ByteApiController extends BaseController {
byteBodyReq.setSize("2K");
byteBodyReq.setStream(false);
byteBodyReq.setWatermark(false);
ByteBodyRes byteBodyRes = byteService.promptToImg(byteBodyReq);
String arkApiKey = byteDeptApiKeyService.resolveVolcApiKey(SecurityUtils.getAiUserId());
ByteBodyRes byteBodyRes = byteService.promptToImg(byteBodyReq, arkApiKey);
List<ByteDataRes> data = byteBodyRes.getData();
ByteDataRes byteDataRes = data.get(0);
String url = byteDataRes.getUrl();
@ -184,7 +184,8 @@ public class ByteApiController extends BaseController {
byteBodyReq.setSize("2K");
byteBodyReq.setStream(false);
byteBodyReq.setWatermark(false);
ByteBodyRes byteBodyRes = byteService.promptToImg(byteBodyReq);
String arkApiKey = byteDeptApiKeyService.resolveVolcApiKey(SecurityUtils.getAiUserId());
ByteBodyRes byteBodyRes = byteService.promptToImg(byteBodyReq, arkApiKey);
List<ByteDataRes> data = byteBodyRes.getData();
ByteDataRes byteDataRes = data.get(0);
String url = byteDataRes.getUrl();
@ -295,7 +296,8 @@ public class ByteApiController extends BaseController {
byteBodyReq.setResolution("720p");
byteBodyReq.setRatio("3:4");
ByteBodyRes byteBodyRes = byteService.imgToVideo(byteBodyReq);
String arkApiKey = byteDeptApiKeyService.resolveVolcApiKey(SecurityUtils.getAiUserId());
ByteBodyRes byteBodyRes = byteService.imgToVideo(byteBodyReq, arkApiKey);
String id = byteBodyRes.getId();
if (id == null) {
aiOrderService.orderFailure(aiOrder);
@ -313,7 +315,8 @@ public class ByteApiController extends BaseController {
@GetMapping(value = "/{id}")
@ApiOperation("视频下载")
public AjaxResult getInfo(@PathVariable("id") String id) throws Exception {
ByteBodyRes byteBodyRes = byteService.uploadVideo(id);
String arkApiKey = byteDeptApiKeyService.resolveVolcApiKey(SecurityUtils.getAiUserId());
ByteBodyRes byteBodyRes = byteService.uploadVideo(id, arkApiKey);
if ("succeeded".equals(byteBodyRes.getStatus())) {
content content = byteBodyRes.getContent();
String videoUrl = content.getVideo_url();
@ -356,7 +359,8 @@ public class ByteApiController extends BaseController {
@PostMapping(value = "/{id}/cancel")
@ApiOperation("取消视频生成任务")
public AjaxResult cancelTask(@PathVariable("id") String id) throws Exception {
return byteService.cancelVideoTask(id);
String arkApiKey = byteDeptApiKeyService.resolveVolcApiKey(SecurityUtils.getAiUserId());
return byteService.cancelVideoTask(id, arkApiKey);
}
}

View File

@ -25,6 +25,7 @@ import com.ruoyi.common.utils.TencentCosUtil;
import com.ruoyi.config.PortalVideoProperties;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
@ -44,6 +45,7 @@ import java.util.stream.Collectors;
@RestController
@RequestMapping("/api/portal/video")
@RequiredArgsConstructor(onConstructor_ = @Autowired)
@Slf4j
public class PortalVideoController extends BaseController {
private final IByteService byteService;
@ -61,6 +63,13 @@ public class PortalVideoController extends BaseController {
return byteDeptApiKeyService.resolveVolcApiKey(SecurityUtils.getAiUserId());
}
private static String maskKey(String k) {
if (StringUtils.isEmpty(k)) return "";
String t = k.trim();
if (t.length() <= 8) return "***";
return t.substring(0, 4) + "***" + t.substring(t.length() - 4);
}
/** 与 ai_manager.type、portal.video.function-type 对齐,用于扣费 */
private String resolveFunctionType(PortalVideoGenRequest req) {
if (StringUtils.isNotEmpty(req.getFunctionType())) {
@ -201,6 +210,18 @@ public class PortalVideoController extends BaseController {
aiOrderService.orderSuccess(aiOrder);
return AjaxResult.success(byteBodyRes);
} catch (Exception e) {
try {
log.error(
"portal video submit failed: endpoint=/api/portal/video/{}, mode={}, req={}, byteBodyReq={}, resolvedKeyMasked={}",
mode,
mode,
OM.writeValueAsString(req),
OM.writeValueAsString(byteBodyReq),
maskKey(apiKey())
);
} catch (Exception ignored) {
log.error("portal video submit failed: endpoint=/api/portal/video/{}, mode={}", mode, mode);
}
aiOrderService.orderFailure(aiOrder);
throw new RuntimeException(e);
}

View File

@ -10,11 +10,13 @@ public interface IByteService {
* 文生图
*/
ByteBodyRes promptToImg(ByteBodyReq req) throws Exception;
ByteBodyRes promptToImg(ByteBodyReq req, String arkApiKey) throws Exception;
/**
* 图生图
*/
ByteBodyRes imgToImg(ByteBodyReq req) throws Exception;
ByteBodyRes imgToImg(ByteBodyReq req, String arkApiKey) throws Exception;
/**
* 首尾帧图生视频使用全局配置的 Ark API Key

View File

@ -8,12 +8,14 @@ import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.exception.ServiceException;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.system.service.ISysDeptService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;
@Service
@Slf4j
public class ByteDeptApiKeyServiceImpl implements IByteDeptApiKeyService {
private static final String NO_DEPT_MSG = "用户未分配部门:请在后台为门户用户设置 ai_user.dept_id关联 sys_dept.dept_id";
@ -49,12 +51,14 @@ public class ByteDeptApiKeyServiceImpl implements IByteDeptApiKeyService {
throw new ServiceException(NO_DEPT_ROW_MSG);
}
// 优先使用用户直接归属部门的 Key多数业务把用户挂在分公司 101并在该节点配 byte_api_key
Long keyDeptId = userDept.getDeptId();
String apiKey = trimKey(userDept.getByteApiKey());
if (StringUtils.isEmpty(apiKey)) {
Long fallbackDeptId = resolveSecondLevelDeptId(userDept);
if (!fallbackDeptId.equals(userDept.getDeptId())) {
SysDept keyDept = sysDeptService.selectDeptById(fallbackDeptId);
if (keyDept != null) {
keyDeptId = keyDept.getDeptId();
apiKey = trimKey(keyDept.getByteApiKey());
}
}
@ -62,12 +66,48 @@ public class ByteDeptApiKeyServiceImpl implements IByteDeptApiKeyService {
if (StringUtils.isEmpty(apiKey)) {
throw new ServiceException(NO_API_KEY_MSG);
}
log.info(
"resolveVolcApiKey ok: aiUserId={}, userDeptId={}, keyDeptId={}, keyLen={}, keyMasked={}, keyFingerprint={}",
aiUserId,
aiUser.getDeptId(),
keyDeptId,
apiKey.length(),
maskKey(apiKey),
fingerprint(apiKey)
);
redisCache.setCacheObject(cacheKey, apiKey, CACHE_HOURS, TimeUnit.HOURS);
return apiKey;
}
private static String maskKey(String key) {
if (StringUtils.isEmpty(key)) {
return "";
}
if (key.length() <= 8) {
return "***";
}
return key.substring(0, 4) + "***" + key.substring(key.length() - 4);
}
private static String fingerprint(String key) {
if (StringUtils.isEmpty(key)) {
return "";
}
return Integer.toHexString(key.hashCode());
}
private static String trimKey(String raw) {
return raw == null ? null : raw.trim();
if (raw == null) {
return null;
}
String k = raw.trim();
if (k.regionMatches(true, 0, "Bearer ", 0, 7)) {
k = k.substring(7).trim();
}
if ((k.startsWith("\"") && k.endsWith("\"")) || (k.startsWith("'") && k.endsWith("'"))) {
k = k.substring(1, k.length() - 1).trim();
}
return k;
}
/**

View File

@ -9,13 +9,42 @@ import com.ruoyi.ai.service.IByteService;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.common.utils.http.OkHttpUtils;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
import okhttp3.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class ByteService implements IByteService {
private static String normalizeArkApiKey(String rawKey) {
if (StringUtils.isBlank(rawKey)) {
return rawKey;
}
String k = rawKey.trim();
if (k.regionMatches(true, 0, "Bearer ", 0, 7)) {
k = k.substring(7).trim();
}
if ((k.startsWith("\"") && k.endsWith("\"")) || (k.startsWith("'") && k.endsWith("'"))) {
k = k.substring(1, k.length() - 1).trim();
}
return k;
}
private static String maskBearer(String authHeader) {
if (StringUtils.isBlank(authHeader)) {
return authHeader;
}
String prefix = "Bearer ";
String token = authHeader.startsWith(prefix) ? authHeader.substring(prefix.length()) : authHeader;
token = token == null ? "" : token.trim();
if (token.length() <= 8) {
return prefix + "***";
}
return prefix + token.substring(0, 4) + "***" + token.substring(token.length() - 4);
}
// private final OkHttpClient okHttpClient = OkHttpUtils.createOkHttpClient();
@ -29,28 +58,35 @@ public class ByteService implements IByteService {
@Value("${byteapi.url}")
private String API_URL;
@Value("${byteapi.apiKey}")
private String apiKey;
// 火山引擎配置
@Value("${volcengine.ark.baseUrl:https://ark.cn-beijing.volces.com}")
private String volcBaseUrl;
@Value("${volcengine.ark.apiKey}")
private String volcApiKey;
@Override
public ByteBodyRes promptToImg(ByteBodyReq req) throws Exception {
return this.imgToImg(req);
throw new Exception("promptToImg errorapiKey is required");
}
@Override
public ByteBodyRes promptToImg(ByteBodyReq req, String arkApiKey) throws Exception {
return this.imgToImg(req, arkApiKey);
}
@Override
public ByteBodyRes imgToImg(ByteBodyReq req) throws Exception {
throw new Exception("imgToImg errorapiKey is required");
}
@Override
public ByteBodyRes imgToImg(ByteBodyReq req, String arkApiKey) throws Exception {
// 1. 验证请求参数可选根据业务需求
if (StringUtils.isBlank(req.getPrompt())) {
throw new Exception("imgToImg errorprompt is null");
}
arkApiKey = normalizeArkApiKey(arkApiKey);
if (StringUtils.isBlank(arkApiKey)) {
throw new Exception("imgToImg errorapiKey is null");
}
// 2. 构建请求体JSON基于ByteBodyReq的字段
// 注意ByteBodyReq需包含与API参数对应的字段modelprompt等
@ -59,7 +95,7 @@ public class ByteService implements IByteService {
Request request = new Request.Builder()
.url(API_URL + "/images/generations")
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + apiKey)
.header("Authorization", "Bearer " + arkApiKey)
// .proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress("192.168.1.1", 8080)))
.post(RequestBody.create(
MediaType.parse("application/json"),
@ -84,7 +120,7 @@ public class ByteService implements IByteService {
@Override
public ByteBodyRes imgToVideo(ByteBodyReq req) throws Exception {
return imgToVideo(req, volcApiKey);
throw new Exception("imgToVideo errorapiKey is required");
}
@Override
@ -92,40 +128,52 @@ public class ByteService implements IByteService {
if (req == null) {
throw new Exception("imgToVideo errorreq is null");
}
arkApiKey = normalizeArkApiKey(arkApiKey);
if (StringUtils.isBlank(arkApiKey)) {
throw new Exception("imgToVideo errorapiKey is null");
}
String jsonBody = objectMapper.writeValueAsString(req);
String requestUrl = volcBaseUrl + "/api/v1/proxy/ark/contents/generations/tasks";
String authHeader = "Bearer " + arkApiKey;
Request request = new Request.Builder()
.url(volcBaseUrl + "/api/v1/proxy/ark/contents/generations/tasks")
.url(requestUrl)
.header("Content-Type", "application/json")
.header("Authorization", "Bearer " + arkApiKey)
.header("Authorization", authHeader)
.post(RequestBody.create(
MediaType.parse("application/json; charset=utf-8"),
jsonBody
))
.build();
Response response = OkHttpUtils.newCall(request).execute();
log.error(
"imgToVideo request: url={}, method=POST, headers={{Content-Type=application/json, Authorization={}}}, body={}",
requestUrl,
maskBearer(authHeader),
jsonBody
);
if (!response.isSuccessful()) {
String errorMsg = response.body() != null ? response.body().string() : "imgToVideo error";
throw new Exception("imgToVideo error" + errorMsg);
Response response = OkHttpUtils.newCall(request).execute();
int code = response.code();
String responseBody = response.body() != null ? response.body().string() : "";
log.error("imgToVideo response: code={}, body={}", code, responseBody);
if (code < 200 || code >= 300) {
throw new Exception("imgToVideo error" + (StringUtils.isNotEmpty(responseBody) ? responseBody : "imgToVideo error"));
}
if (response.body() == null) {
if (StringUtils.isEmpty(responseBody)) {
throw new Exception("imgToVideo response null");
}
String responseBody = response.body().string();
return objectMapper.readValue(responseBody, ByteBodyRes.class);
}
@Override
public ByteBodyRes uploadVideo(String id) throws Exception {
return uploadVideo(id, volcApiKey);
throw new Exception("uploadVideo errorapiKey is required");
}
@Override
@ -133,6 +181,7 @@ public class ByteService implements IByteService {
if (StringUtils.isBlank(id)) {
throw new Exception("uploadVideo errorid is null");
}
arkApiKey = normalizeArkApiKey(arkApiKey);
if (StringUtils.isBlank(arkApiKey)) {
throw new Exception("uploadVideo errorapiKey is null");
}
@ -161,7 +210,7 @@ public class ByteService implements IByteService {
@Override
public AjaxResult cancelVideoTask(String id) throws Exception {
return cancelVideoTask(id, volcApiKey);
return AjaxResult.error("API Key 无效");
}
@Override
@ -169,6 +218,7 @@ public class ByteService implements IByteService {
if (StringUtils.isBlank(id)) {
return AjaxResult.error("任务ID不能为空");
}
arkApiKey = normalizeArkApiKey(arkApiKey);
if (StringUtils.isBlank(arkApiKey)) {
return AjaxResult.error("API Key 无效");
}
@ -196,6 +246,7 @@ public class ByteService implements IByteService {
@Override
public String listVideoGenerationTasks(int pageNum, int pageSize, String arkApiKey) throws Exception {
arkApiKey = normalizeArkApiKey(arkApiKey);
if (StringUtils.isBlank(arkApiKey)) {
throw new Exception("listVideoGenerationTasks errorapiKey is null");
}