@@ -27,7 +27,6 @@ import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper ;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper ;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl ;
import io.netty.util.internal.StringUtil ;
import lombok.RequiredArgsConstructor ;
import lombok.extern.slf4j.Slf4j ;
import org.apache.commons.lang3.StringUtils ;
@@ -62,6 +61,10 @@ public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements
// 请求需要顾客id, 生成的数量,风格
StylistPathEnum stylistPathEnum = StylistPathEnum . of ( requestOutfitDTO . getStylist ( ) ) ;
if ( Objects . isNull ( stylistPathEnum ) ) {
log . error ( " 未知设计师: {} " , requestOutfitDTO . getStylist ( ) ) ;
stylistPathEnum = StylistPathEnum . STYLIST_ONE ;
}
Map < String , Object > params = setRequestOutfitParams ( requestOutfitDTO , stylistPathEnum ) ;
SessionRecord sessionRecord = saveOrUpdateSession ( requestOutfitDTO . getSessionId ( ) , requestOutfitDTO . getCheckInId ( ) , null , null ) ;
@@ -101,6 +104,7 @@ public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements
String key = RedisURIConstants . outfitResultCache + requestId ;
OutfitResultVO outfitResultVO = new OutfitResultVO ( style . getId ( ) , requestId , StatusEnum . PENDING . name ( ) ) ;
outfitResultVO . setCreateTimeStamp ( System . currentTimeMillis ( ) ) ;
cacheUtil . setCache ( key , outfitResultVO , RedisURIConstants . verifyCodeTimeout ) ;
}
return requestIds ;
@@ -129,44 +133,74 @@ public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements
// 搭配完成后的回调通知处理
public void callback ( OutfitCallbackDTO callbackDTO ) {
if ( Objects . isNull ( callbackDTO . getStatus ( ) ) ) {
if ( Objects . isNull ( callbackDTO . getStatus ( ) )
| | callbackDTO . getStatus ( ) . equals ( " failed " )
| | callbackDTO . getStatus ( ) . equals ( " continue " ) ) {
return ;
}
if ( " ok " . equals ( callbackDTO . getStatus ( ) ) | | " stop " . equals ( callbackDTO . getStatus ( ) ) ) {
// 1. 判断path是否为空, 是 -> 不做任何处理
if ( StringUtil . isNullOrEmpty ( callbackDTO . getPath ( ) ) ) {
return ;
// 1. 判断path是否为空, 是 -> 不做任何处理
if ( ( " ok " . equals ( callbackDTO . getStatus ( ) )
| | " stop " . equals ( callbackDTO . getStatus ( ) ) )
& & StringUtils . isBlank ( callbackDTO . getPath ( ) ) ) {
log . error ( " 状态为ok || stop时, path为空 " ) ;
return ;
}
if ( StringUtils . isBlank ( callbackDTO . getOutfit_id ( ) ) ) {
log . error ( " 回调参数中, outfit_id为空 " ) ;
return ;
}
String requestId = callbackDTO . getOutfit_id ( ) ;
// 2. 获取outfit_id,查询数据库
String key = RedisURIConstants . outfitResultCache + requestId ;
Object outfitResult = cacheUtil . getCache ( key ) ;
if ( Objects . isNull ( outfitResult ) ) {
log . error ( " 未知搭配请求 " ) ;
return ;
}
// 3.更新path, items, 状态
// 由于数据变化较频繁, 考虑存到redis
if ( outfitResult instanceof OutfitResultVO ) {
String status ;
switch ( callbackDTO . getStatus ( ) ) {
case " ok " :
status = StatusEnum . RUNNING . name ( ) ;
( ( OutfitResultVO ) outfitResult ) . setPath ( minioUtil . getPresignedUrl ( callbackDTO . getPath ( ) , CommonConstants . MINIO_PATH_TIMEOUT ) ) ;
break ;
case " stop " :
status = StatusEnum . SUCCEEDED . name ( ) ;
( ( OutfitResultVO ) outfitResult ) . setPath ( minioUtil . getPresignedUrl ( callbackDTO . getPath ( ) , CommonConstants . MINIO_PATH_TIMEOUT ) ) ;
break ;
case " retrying " :
status = StatusEnum . PENDING . name ( ) ;
( ( OutfitResultVO ) outfitResult ) . setCreateTimeStamp ( System . currentTimeMillis ( ) ) ;
( ( OutfitResultVO ) outfitResult ) . setPath ( null ) ;
break ;
case " almost_done " :
// 此时是没有更新path的
status = StatusEnum . ALMOST_DONE . name ( ) ;
break ;
case /*"failed",*/ " retry_failed " :
status = StatusEnum . FAILED . name ( ) ;
break ;
default :
log . error ( " outfit_id为{},回调状态未知{} " , requestId , callbackDTO . getStatus ( ) ) ;
status = StatusEnum . FAILED . name ( ) ;
}
if ( StringUtil . isNullOrEmpty ( callbackDTO . g etOutfit_id ( ) ) ) {
log . error ( " 回调参数中, outfit_id为空 " ) ;
return ;
}
String requestId = callbackDTO . getOutfit_id ( ) ;
// 2. 获取outfit_id,查询数据库
String key = RedisURIConstants . outfitResultCache + requestId ;
Object outfitResult = cacheUtil . getCache ( key ) ;
if ( Objects . isNull ( outfitResult ) ) {
log . error ( " 未知搭配请求 " ) ;
return ;
}
// 3.更新path, items, 状态
// 由于数据变化较频繁, 考虑存到redis
if ( outfitResult instanceof OutfitResultVO ) {
( ( OutfitResultVO ) outfitResult ) . setPath ( minioUtil . getPresignedUrl ( callbackDTO . getPath ( ) , CommonConstants . MINIO_PATH_TIMEOUT ) ) ;
String status = " ok " . equals ( callbackDTO . getStatus ( ) ) ? StatusEnum . RUNNING . name ( ) : StatusEnum . SUCCEEDED . name ( ) ;
( ( OutfitResultVO ) outfitResult ) . setStatus ( status ) ;
cacheUtil . setCache ( key , outfitResult , RedisURIConstants . outfitResultTimeout ) ;
}
( ( OutfitResultVO ) outfitResult ) . s etStatus ( status ) ;
cacheUtil . setCache ( key , outfitResult , RedisURIConstants . outfitResultTimeout ) ;
}
// 生成结束或失败时更新数据库
if ( " stop " . equals ( callbackDTO . getStatus ( ) ) | | " failed " . equals ( callbackDTO . getStatus ( ) ) ) {
String requestId = callbackDTO . getOutfit_id ( ) ;
if ( " stop " . equals ( callbackDTO . getStatus ( ) )
/*|| "failed".equals(callbackDTO.getStatus())*/
| | " retry_failed " . equals ( callbackDTO . getStatus ( ) ) ) {
// String requestId = callbackDTO.getOutfit_id();
LambdaQueryWrapper < Style > queryWrapper = new LambdaQueryWrapper < > ( ) ;
queryWrapper . eq ( Style : : getPythonRequestId , requestId ) ;
@@ -198,6 +232,7 @@ public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements
public List < OutfitResultVO > getOutfitResult ( List < String > requestIDs ) {
ArrayList < OutfitResultVO > resultVOS = new ArrayList < > ( ) ;
ArrayList < String > reQueryIds = new ArrayList < > ( ) ;
List < String > expiredIds = new ArrayList < > ( ) ;
// 优先从redis中获取结果, 没有再从数据库中查询
for ( String requestID : requestIDs ) {
String key = RedisURIConstants . outfitResultCache + requestID ;
@@ -207,7 +242,25 @@ public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements
reQueryIds . add ( requestID ) ;
continue ;
}
resultVOS . add ( ( OutfitResultVO ) outfit ) ;
// 判断这条记录的状态是否为成功或者失败, 否, 判断这条记录的创建时间是否超过3分钟, 否, 继续往后, 是, 设置为失败并更新数据库
if ( outfit instanceof OutfitResultVO ) {
if ( ( ( ( OutfitResultVO ) outfit ) . getStatus ( ) . equals ( StatusEnum . PENDING . name ( ) )
| | ( ( OutfitResultVO ) outfit ) . getStatus ( ) . equals ( StatusEnum . RUNNING . name ( ) )
| | ( ( OutfitResultVO ) outfit ) . getStatus ( ) . equals ( StatusEnum . ALMOST_DONE . name ( ) ) )
& & isExpired ( ( ( OutfitResultVO ) outfit ) . getCreateTimeStamp ( ) ) ) {
// 设置状态为失败
( ( OutfitResultVO ) outfit ) . setStatus ( StatusEnum . FAILED . name ( ) ) ;
// 更新redis和数据库状态
expiredIds . add ( requestID ) ;
cacheUtil . setCache ( key , outfit , RedisURIConstants . outfitResultTimeout ) ;
}
resultVOS . add ( ( OutfitResultVO ) outfit ) ;
}
}
if ( ! expiredIds . isEmpty ( ) ) {
batchUpdateExpiredRecords ( expiredIds ) ;
}
if ( ! reQueryIds . isEmpty ( ) ) {
@@ -216,12 +269,13 @@ public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements
List < Style > list = list ( queryWrapper ) ;
if ( ! list . isEmpty ( ) ) {
// 在数据库中的数据,都是处于成功或失败的状态
for ( Style style : list ) {
OutfitResultVO outfitResultVO = new OutfitResultVO ( ) ;
outfitResultVO . setId ( style . getId ( ) ) ;
outfitResultVO . setRequestId ( style . getPythonRequestId ( ) ) ;
outfitResultVO . setStatus ( StatusEnum . of ( style . getGenerationStatus ( ) ) . name ( ) ) ;
if ( ! StringUtil . isNullOrEmpty ( style . getStyleImageUrl ( ) ) ) {
if ( ! StringUtils . isBlank ( style . getStyleImageUrl ( ) ) ) {
outfitResultVO . setPath ( minioUtil . getPresignedUrl ( style . getStyleImageUrl ( ) , CommonConstants . MINIO_PATH_TIMEOUT ) ) ;
}
resultVOS . add ( outfitResultVO ) ;
@@ -232,6 +286,34 @@ public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements
return resultVOS ;
}
// 3分钟的毫秒数
private static final long THREE_MINUTES_IN_MILLIS = 3 * 60 * 1000 ;
private boolean isExpired ( long createTimeMillis ) {
long currentTimeMillis = System . currentTimeMillis ( ) ;
return ( currentTimeMillis - createTimeMillis ) > = THREE_MINUTES_IN_MILLIS ;
}
private void batchUpdateExpiredRecords ( List < String > expiredIds ) {
try {
if ( CollectionUtils . isEmpty ( expiredIds ) ) {
return ;
}
// 批量更新,减少数据库压力
lambdaUpdate ( )
. in ( Style : : getPythonRequestId , expiredIds )
. set ( Style : : getGenerationStatus , StatusEnum . FAILED . getCode ( ) )
. set ( Style : : getUpdatedTime , LocalDateTime . now ( ) )
. update ( ) ;
log . info ( " 批量更新过期记录为失败状态,数量:{} " , expiredIds . size ( ) ) ;
} catch ( Exception e ) {
log . error ( " 批量更新过期记录失败 " , e ) ;
}
}
@Override
public void setFavoriteStyle ( Long styleId ) {
if ( styleId = = null ) {
@@ -291,7 +373,7 @@ public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements
return sessionRecord ;
}
public List < String > retrieveAndRegenerate ( Long tryOnEffectsId ) {
public List < String > retrieveAndRegenerate ( Long tryOnEffectsId , Long checkInId ) {
// 1. 判断id是否有效
TryOnEffect tryOnEffect = tryOnEffectMapper . selectById ( tryOnEffectsId ) ;
if ( Objects . isNull ( tryOnEffect ) ) {
@@ -302,6 +384,10 @@ public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements
log . error ( " Id 为:{} 的tryOnEffects记录,没有style_id " , tryOnEffectsId ) ;
throw new BusinessException ( " Cannot recreate outfit from past data. " ) ;
}
if ( Objects . isNull ( checkInId ) ) {
log . error ( " checkInId 为空 " ) ;
throw new BusinessException ( " 'checkInId' cannot be empty " ) ;
}
// 2. 组装参数
Style style = baseMapper . selectById ( tryOnEffect . getStyleId ( ) ) ;
@@ -316,7 +402,7 @@ public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements
log . error ( " 找不到Id 为:{} 的SessionRecord记录 " , outfitRequest . getSessionRecordId ( ) ) ;
throw new BusinessException ( " Cannot recreate outfit from past data. " ) ;
}
RequestOutfitDTO requestOutfitDTO = getRequestOutfitDTO ( outfitRequest , sessionRecord ) ;
RequestOutfitDTO requestOutfitDTO = getRequestOutfitDTO ( outfitRequest , sessionRecord , checkInId );
return requestOutfit ( requestOutfitDTO ) ;
} else {
@@ -326,10 +412,10 @@ public class StyleServiceImpl extends ServiceImpl<StyleMapper, Style> implements
}
@NotNull
private static RequestOutfitDTO getRequestOutfitDTO ( OutfitRequest outfitRequest , SessionRecord sessionRecord ) {
private static RequestOutfitDTO getRequestOutfitDTO ( OutfitRequest outfitRequest , SessionRecord sessionRecord , Long checkInId ) {
RequestOutfitDTO requestOutfitDTO = new RequestOutfitDTO ( ) ;
requestOutfitDTO . setCustomerId ( outfitRequest . getCustomerId ( ) ) ;
requestOutfitDTO . setCheckInId ( outfitRequest . getVisitRecordId ( ) ) ;
requestOutfitDTO . setCheckInId ( checkInId ) ;
requestOutfitDTO . setStylist ( outfitRequest . getStylist ( ) ) ;
requestOutfitDTO . setGender ( outfitRequest . getGender ( ) ) ;
requestOutfitDTO . setNum ( 1 ) ;