TASK:1、to product修改任务状态字段为task 2、异步向api_generate表中添加记录 3、优化flux结果获取,避免重复获取结果时因图片过期覆盖原可用图片

This commit is contained in:
2025-07-28 15:01:16 +08:00
parent 07d60303db
commit b1d682a909
3 changed files with 195 additions and 57 deletions

View File

@@ -62,7 +62,7 @@ public class ToProductImageResult implements Serializable {
private String modelName;
private String taskStatus;
private String status;
@ApiModelProperty(value = "是否删除1:是 0:否")
@TableField

View File

@@ -41,6 +41,7 @@ import lombok.extern.slf4j.Slf4j;
import org.apache.commons.io.IOUtils;
import org.apache.http.HttpHost;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
@@ -48,6 +49,7 @@ import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.Java2DFrameConverter;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.dao.DuplicateKeyException;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
@@ -331,7 +333,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
}
ToProductImageResult toProductImageResult = toProductImageResults.get(0);
toProductImageResult.setUrl(url);
toProductImageResult.setTaskStatus("Success");
toProductImageResult.setStatus("Success");
// toProductImageResult.setResultType("ToProductImage");
toProductImageResultMapper.updateById(toProductImageResult);
@@ -359,8 +361,8 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
List<ToProductImageResult> toProductImageResults = toProductImageResultMapper.selectList(qw);
if (!CollectionUtils.isEmpty(toProductImageResults)) {
ToProductImageResult toProductImageResult = toProductImageResults.get(0);
if (StringUtil.isNullOrEmpty(toProductImageResult.getTaskStatus()) || !toProductImageResult.getTaskStatus().equals(status)){
toProductImageResult.setTaskStatus(status);
if (StringUtil.isNullOrEmpty(toProductImageResult.getStatus()) || !toProductImageResult.getStatus().equals(status)){
toProductImageResult.setStatus(status);
toProductImageResultMapper.updateById(toProductImageResult);
}
}
@@ -1064,7 +1066,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
pythonService.bright(url, toProductImageResult.getBrightenValue());
}
toProductImageResult.setUrl(url);
toProductImageResult.setTaskStatus("Success");
toProductImageResult.setStatus("Success");
// toProductImageResult.setResultType("Relight");
toProductImageResultMapper.updateById(toProductImageResult);
@@ -1462,16 +1464,19 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
// 3、生成唯一id 使用uuid,由于uuid重复的几率很小故取消对uuid重复性的校验
String taskId;
Boolean flag = false;
Boolean isRequestSuccess = false;
PoseTransformation poseTransformation = new PoseTransformation();
if (!StringUtil.isNullOrEmpty(poseTransformDTO.getModelName()) && poseTransformDTO.getModelName().equals("wx")) {
taskId = animateAnyone(poseTransformDTO, accountId);
if (!StringUtil.isNullOrEmpty(taskId)) flag = true;
if (!StringUtil.isNullOrEmpty(taskId)){
isRequestSuccess = true;
addAPIGenerateRecordAsync(taskId, Module.poseTransfer.getValue(), "wx", "Pending");
}
poseTransformation.setModelName("wx");
} else {
String uuid = UUID.randomUUID().toString();
taskId = uuid + "-" + accountId;
flag = pythonService.poseTransformation(productImage, poseId, taskId);
isRequestSuccess = pythonService.poseTransformation(productImage, poseId, taskId);
}
poseTransformation.setProjectId(projectId);
@@ -1480,7 +1485,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
poseTransformation.setProductImage(productImage);
poseTransformation.setPoseId(poseId);
poseTransformation.setIsLiked((byte) 0);
String taskStatus = flag ? "Executing" : "Fail";
String taskStatus = isRequestSuccess ? "Executing" : "Fail";
poseTransformation.setTaskStatus(taskStatus);
poseTransformation.setCreateTime(LocalDateTime.now());
poseTransformationMapper.insert(poseTransformation);
@@ -1489,7 +1494,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
toProductImageResultVO.setParentId(poseTransformDTO.getParentId());
toProductImageResultVO.setResultType(Module.poseTransfer.getValue());
toProductImageResultVO.setTaskId(taskId);
toProductImageResultVO.setTaskStatus(taskStatus);
toProductImageResultVO.setStatus(taskStatus);
toProductImageResultVO.setSourceUrl(minioUtil.getPreSignedUrl(productImage, CommonConstant.MINIO_IMAGE_EXPIRE_TIME));
toProductImageResultVO.setPoseId(poseId);
toProductImageResultVO.setModelName(poseTransformDTO.getModelName());
@@ -1508,7 +1513,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
}
if (flag) {
if (isRequestSuccess) {
// 6、添加预扣除积分到redis
creditsService.addRecordToCreditsDeduction(accountId, taskId, creditsEventsEnum);
// 6.1 添加积分扣除记录到db
@@ -2279,13 +2284,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
if (status.equals(STATUS_FAILED) || status.equals(STATUS_UNKNOWN)) {
return null;
}
String taskId = output.getStr("task_id");
/*PoseTransformation poseTransformation = new PoseTransformation(poseTransformDTO.getProjectId(),
accountId, taskId, inputImage, poseTransformDTO.getPoseId());
poseTransformation.setCreateTime(LocalDateTime.now());
poseTransformationMapper.insert(poseTransformation);*/
return taskId;
return output.getStr("task_id");
}
public void checkImage(String inputImageUrl) {
@@ -2456,6 +2455,8 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
String videoUrl = output.getStr("video_url");
String status = output.getStr("task_status");
updateTaskStatusAsync(taskId, status);
PoseTransformationVO poseTransformationVO = new PoseTransformationVO();
switch (status) {
case STATUS_SUCCESS:
@@ -2553,6 +2554,37 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
}
}
// 增强版下载方法 todo 最好不要报错
private byte[] downloadVideoOrImageWithValidation(String url) throws IOException {
CloseableHttpClient client = HttpClients.createDefault();
HttpGet request = new HttpGet(url);
try (CloseableHttpResponse response = client.execute(request)) {
// 状态码检查
if (response.getStatusLine().getStatusCode() != 200) {
throw new IOException("Invalid status: " + response.getStatusLine());
}
// 内容类型检查
org.apache.http.Header contentTypeHeader = response.getFirstHeader("Content-Type");
if (contentTypeHeader == null || !contentTypeHeader.getValue().startsWith("image/")) {
throw new IOException("Invalid content type: " +
(contentTypeHeader != null ? contentTypeHeader.getValue() : "null"));
}
// 内容长度检查
org.apache.http.Header contentLengthHeader = response.getFirstHeader("Content-Length");
if (contentLengthHeader != null) {
long length = Long.parseLong(contentLengthHeader.getValue());
if (length <= 0) {
throw new IOException("Empty content");
}
}
return IOUtils.toByteArray(response.getEntity().getContent());
}
}
public byte[] downloadWithProxy(String url) throws IOException {
// 获取系统代理设置适用于大多数VPN
// String proxyHost = System.getProperty("http.proxyHost");
@@ -2770,7 +2802,6 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
}
String resp = sendRequestUtil.sendFluxPost(fluxRequestUrl, requestBody.toString());
// JSONObject respObj = JSONUtil.parseObj(null);
JSONObject respObj = JSONUtil.parseObj(resp);
log.info("flux 发起生成请求返回结果: {}", respObj);
String taskId = respObj.getStr("id");
@@ -2783,47 +2814,98 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
String pollingUrl = respObj.getStr("polling_url");
String key = RedisUtil.FLUX_POLLING_URL + taskId;
redisUtil.addToString(key, pollingUrl, CommonConstant.GENERATE_RESULT_EXPIRE_TIME);
// 添加到api_generate表中以便之后对结果查询做补偿
addAPIGenerateRecordAsync(taskId, func.getName(), "flux", "Pending");
return taskId;
}
@Override
public String getFluxResult(String taskId, String objectName) {
// 获取轮询URL
String pollingUrl = redisUtil.getFromString(RedisUtil.FLUX_POLLING_URL + taskId);
String fluxResultRequestUrl;
// 准备请求参数
String fluxResultRequestUrl = StringUtil.isNullOrEmpty(pollingUrl)
? "https://api.bfl.ai/v1/get_result"
: pollingUrl;
HashMap<String, Object> params = new HashMap<>();
if (StringUtil.isNullOrEmpty(pollingUrl)) {
fluxResultRequestUrl = "https://api.bfl.ai/v1/get_result";
params.put("id", taskId);
} else {
fluxResultRequestUrl = pollingUrl;
}
// 发送请求并解析响应
String resp = sendRequestUtil.sendGet(fluxResultRequestUrl, params);
log.info("获取flux生成的结果为{}", resp);
JSONObject respObj = JSONUtil.parseObj(resp);
String status = respObj.getStr("status");
// 异步更新状态
updateTaskStatusAsync(taskId, status);
// 处理不同状态
switch (status) {
case "Task not found":
// 审核没过
case "Request Moderated":
// 审核没过
case "Content Moderated":
// 出错
case "Error":
return "Fail";
case "Pending":
return "Pending";
case "Request Moderated":
case "Content Moderated":
// 审核没过
return "Fail";
case "Ready":
// 已完成 获取结果
String fluxResult = JSONUtil.parseObj(respObj.getStr("result")).getStr("sample");
byte[] bytes = downloadVideoOrImage(fluxResult);
minioUtil.uploadToMinio(bytes, userBucket, objectName, "image/png");
// return minioUtil.getPreSignedUrl(userBucket + "/" + objectName, CommonConstant.MINIO_IMAGE_EXPIRE_TIME);
return userBucket + "/" + objectName;
case "Error":
// 出错
return "Fail";
return handleReadyStatus(respObj, objectName);
default:
return null;
}
}
private String handleReadyStatus(JSONObject respObj, String objectName) {
// 1. 首先检查MinIO中是否已存在该图片
if (minioUtil.doesObjectExist(userBucket, objectName)) {
return userBucket + "/" + objectName;
}
// 2. 解析响应获取结果URL和生成时间
JSONObject resultObj = JSONUtil.parseObj(respObj.getStr("result"));
String fluxResult = resultObj.getStr("sample");
double endTime = resultObj.getDouble("end_time"); // 获取任务结束时间戳
// 3. 检查图片链接是否已过期超过10分钟
long currentTime = System.currentTimeMillis() / 1000; // 当前Unix时间戳
long generateTime = (long) endTime; // 生成结束时间戳
// 图片10分钟过期保险起见保留一分钟
long tenMinutesInSeconds = 9 * 60;
if (currentTime - generateTime > tenMinutesInSeconds) {
log.warn("Flux result image has expired, generateTime: {}, currentTime: {}",
generateTime, currentTime);
return null;
}
// 4. 图片未过期下载并上传到MinIO
try {
byte[] bytes = downloadVideoOrImage(fluxResult);
minioUtil.uploadToMinio(bytes, userBucket, objectName, "image/png");
return userBucket + "/" + objectName;
} catch (Exception e) {
log.error("Failed to download or upload Flux result image", e);
return null;
}
}
@Async
public void updateTaskStatusAsync(String taskId, String status) {
try {
updateAPIGenerateStatusAsync(taskId, status);
} catch (Exception e) {
log.error("更新任务状态失败, taskId: {}, status: {}", taskId, status, e);
}
return null;
}
private GenerateResultVO getFluxResultAndSave(String taskId) {
@@ -2849,7 +2931,7 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
Boolean flag = creditsService.taskCreditsDeduction(accountId, taskId);
if (flag) creditsService.updateChangedCredits(String.valueOf(accountId), taskId);
} else if (StringUtil.isNullOrEmpty(generateDetail.getUrl())) {
// 一般来说这条线应该走不到
// 结果已经存入db,一般走不到这条线
generateDetail.setGenerateId(generate.getId());
generateDetail.setUrl(fluxResult);
generateDetail.setMd5(MD5Utils.encryptFile(
@@ -2884,4 +2966,44 @@ public class GenerateServiceImpl extends ServiceImpl<GenerateMapper, Generate> i
return null;
}
}
@Async
@Transactional
public void addAPIGenerateRecordAsync(String taskId, String function, String modelName, String status){
try {
log.info("异步执行添加");
if (!StringUtil.isNullOrEmpty(taskId) && !StringUtil.isNullOrEmpty(modelName)){
APIGenerate apiGenerate = new APIGenerate();
apiGenerate.setTaskId(taskId);
apiGenerate.setFunc(function);
apiGenerate.setModelName(modelName);
apiGenerate.setStatus(status);
apiGenerate.setRetry_count(0);
apiGenerate.setCreateTime(LocalDateTime.now());
apiGenerateMapper.insert(apiGenerate);
}
} catch (Exception e){
log.error(e.getMessage());
}
}
@Async
@Transactional
public void updateAPIGenerateStatusAsync(String taskId, String status){
log.info("异步执行修改");
QueryWrapper<APIGenerate> qw = new QueryWrapper<>();
qw.lambda().eq(APIGenerate::getTaskId, taskId);
APIGenerate apiGenerate = apiGenerateMapper.selectOne(qw);
if (Objects.nonNull(apiGenerate)){
if (apiGenerate.getStatus().equals("Ready") || apiGenerate.getStatus().equals("SUCCEEDED")) {
log.warn("当前任务 {} 状态已达Success, 不做修改", taskId);
} else {
apiGenerate.setStatus(status);
apiGenerate.setUpdateTime(LocalDateTime.now());
apiGenerateMapper.updateById(apiGenerate);
}
} else {
log.error("任务 {} 在api_generate表中找不到", taskId);
}
}
}

View File

@@ -490,7 +490,7 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
}
toProductImageResult.setImageStrength(toProductImageDTO.getImageStrength());
toProductImageResult.setResultType(CollectionType.TO_PRODUCT_IMAGE.getValue());
toProductImageResult.setTaskStatus("Pending");
toProductImageResult.setStatus("Pending");
toProductImageResultMapper.insert(toProductImageResult);
// toProductImageResult.setUrl(minioUtil.getPresignedUrl(toProductImageResult.getUrl(), 24 * 60));
// 先判断是否需要默认like
@@ -545,7 +545,7 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
}
toProductImageResult.setImageStrength(toProductImageDTO.getImageStrength());
toProductImageResult.setResultType(CollectionType.TO_PRODUCT_IMAGE.getValue());
toProductImageResult.setTaskStatus("Pending");
toProductImageResult.setStatus("Pending");
toProductImageResultMapper.insert(toProductImageResult);
// toProductImageResult.setUrl(minioUtil.getPresignedUrl(toProductImageResult.getUrl(), 24 * 60));
// 先判断是否需要默认like
@@ -724,14 +724,20 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
if (Objects.isNull(project)){
throw new BusinessException("unknown project");
}
String objectName = project.getAccountId() + "/product_image/" + taskId + ".png";
String fluxResult = generateService.getFluxResult(taskId, objectName);
String fluxResult;
if (toProductImageResult.getStatus().equals("Success") && !StringUtil.isNullOrEmpty(toProductImageResult.getUrl())){
fluxResult = toProductImageResult.getUrl();
}else {
String objectName = project.getAccountId() + "/product_image/" + taskId + ".png";
fluxResult = generateService.getFluxResult(taskId, objectName);
}
if (StringUtil.isNullOrEmpty(fluxResult)){
toProductImageResult.setTaskStatus("Fail");
toProductImageResult.setStatus("Fail");
toProductImageResultMapper.updateById(toProductImageResult);
results.add(new MagicToolResultVO(taskId, "Fail"));
} else if (fluxResult.equals("Fail") || fluxResult.equals("Pending")) {
toProductImageResult.setTaskStatus(fluxResult);
toProductImageResult.setStatus(fluxResult);
toProductImageResultMapper.updateById(toProductImageResult);
results.add(new MagicToolResultVO(taskId, fluxResult));
} else {
@@ -789,9 +795,13 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
}
private MagicToolResultVO processFluxResult(String fluxImgMinioPath, ToProductImageResult toProductImageResult, String taskId, String prompt){
toProductImageResult.setTaskStatus("Success");
toProductImageResult.setUrl(fluxImgMinioPath);
toProductImageResultMapper.updateById(toProductImageResult);
// todo 需要先判断当前任务状态是否已为 Success 是 -> 不用修改,直接处理回参
if (!toProductImageResult.getStatus().equals("Success")
&& !StringUtil.isNullOrEmpty(toProductImageResult.getUrl())){
toProductImageResult.setStatus("Success");
toProductImageResult.setUrl(fluxImgMinioPath);
toProductImageResultMapper.updateById(toProductImageResult);
}
MagicToolResultVO magicToolResultVO = CopyUtil.copyObject(toProductImageResult, MagicToolResultVO.class);
magicToolResultVO.setTaskId(taskId);
@@ -1127,7 +1137,7 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
}
toProductImageResult.setDirection(toProductImageDTO.getDirection());
toProductImageResult.setResultType(CollectionType.RELIGHT.getValue());
toProductImageResult.setTaskStatus("Pending");
toProductImageResult.setStatus("Pending");
toProductImageResultMapper.insert(toProductImageResult);
// toProductImageResult.setUrl(minioUtil.getPresignedUrl(toProductImageResult.getUrl(), 24 * 60));
// 先判断是否需要默认like
@@ -1172,7 +1182,7 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
}
toProductImageResult.setDirection(toProductImageDTO.getDirection());
toProductImageResult.setResultType(CollectionType.RELIGHT.getValue());
toProductImageResult.setTaskStatus("Pending");
toProductImageResult.setStatus("Pending");
toProductImageResultMapper.insert(toProductImageResult);
// toProductImageResult.setUrl(minioUtil.getPresignedUrl(toProductImageResult.getUrl(), 24 * 60));
// 先判断是否需要默认like
@@ -1223,15 +1233,20 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
if (Objects.isNull(project)){
throw new BusinessException("unknown project");
}
String objectName = project.getAccountId() + "/product_image/" + taskId + ".png";
String fluxResult = generateService.getFluxResult(taskId, objectName);
String fluxResult;
if (toProductImageResult.getStatus().equals("Success") && !StringUtil.isNullOrEmpty(toProductImageResult.getUrl())){
fluxResult = toProductImageResult.getUrl();
}else {
String objectName = project.getAccountId() + "/product_image/" + taskId + ".png";
fluxResult = generateService.getFluxResult(taskId, objectName);
}
if (StringUtil.isNullOrEmpty(fluxResult)){
toProductImageResult.setTaskStatus("Fail");
toProductImageResult.setStatus("Fail");
toProductImageResultMapper.updateById(toProductImageResult);
results.add(new MagicToolResultVO(taskId, "Fail"));
} else if (fluxResult.equals("Fail") || fluxResult.equals("Pending")) {
toProductImageResult.setTaskStatus(fluxResult);
toProductImageResult.setStatus(fluxResult);
toProductImageResultMapper.updateById(toProductImageResult);
results.add(new MagicToolResultVO(taskId, fluxResult));
} else {
@@ -1923,7 +1938,7 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
for (CollectionSort userLikeSort : childSortList) {
if (userLikeSort.getRelationType().equals(CollectionType.TO_PRODUCT_IMAGE.getValue())) {
ToProductImageResult toProductImageResult = toProductImageResultMapper.selectById(userLikeSort.getRelationId());
if (isGenerateTaskFailed(toProductImageResult.getTaskStatus(), toProductImageResult.getCreateTime())){
if (isGenerateTaskFailed(toProductImageResult.getStatus(), toProductImageResult.getCreateTime())){
continue;
}
toProductImageResult.setUrl(getMinioUrl(toProductImageResult.getUrl()));
@@ -1954,7 +1969,7 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
childList.add(toProductImageResultVO);
} else if (userLikeSort.getRelationType().equals(CollectionType.RELIGHT.getValue())) {
ToProductImageResult toProductImageResult = toProductImageResultMapper.selectById(userLikeSort.getRelationId());
if (isGenerateTaskFailed(toProductImageResult.getTaskStatus(), toProductImageResult.getCreateTime())){
if (isGenerateTaskFailed(toProductImageResult.getStatus(), toProductImageResult.getCreateTime())){
continue;
}
toProductImageResult.setUrl(getMinioUrl(toProductImageResult.getUrl()));
@@ -2004,6 +2019,7 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
poseTransformationVO.setParentId(userLikeSort.getParentId());
poseTransformationVO.setModelName(item.getModelName());
poseTransformationVO.setPoseId(item.getPoseId());
poseTransformationVO.setStatus(item.getTaskStatus());
childList.add(poseTransformationVO);
}
}
@@ -2857,7 +2873,7 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
ToProductImageResult toProductImageResult = toProductImageResultMapper.selectOne(qw);
if (Objects.nonNull(toProductImageResult)) {
toProductImageResult.setUrl(url);
toProductImageResult.setTaskStatus("Success");
toProductImageResult.setStatus("Success");
toProductImageResultMapper.updateById(toProductImageResult);
String taskIdBatch = toProductImageResult.getTaskIdBatch();
log.info("toProductImage云生成 batchTaskId:" + taskIdBatch + " 完成数+1");
@@ -2896,7 +2912,7 @@ public class UserLikeGroupServiceImpl extends ServiceImpl<UserLikeGroupMapper, U
}
toProductImageResult.setUrl(url);
// toProductImageResult.setResultType("Relight");
toProductImageResult.setTaskStatus("Success");
toProductImageResult.setStatus("Success");
toProductImageResultMapper.updateById(toProductImageResult);
String taskIdBatch = toProductImageResult.getTaskIdBatch();