|
|
@@ -98,8 +98,25 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
|
|
|
*/
|
|
|
@Override
|
|
|
public Integer saveCommonRating(CommonRating commonRating) {
|
|
|
- // 1. 文本审核 + 视频审核(评价有图片和视频)
|
|
|
+ // 1. 先进行AI审核,审核通过后再保存(防止绕过审核)
|
|
|
try {
|
|
|
+ // 参数校验:必填字段不能为空
|
|
|
+ if (commonRating.getBusinessId() == null) {
|
|
|
+ log.error("保存评价失败:businessId不能为空");
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ if (commonRating.getBusinessType() == null) {
|
|
|
+ log.error("保存评价失败:businessType不能为空");
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+ if (commonRating.getUserId() == null) {
|
|
|
+ log.error("保存评价失败:userId不能为空");
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 强制设置审核状态为待审核,防止用户绕过审核
|
|
|
+ commonRating.setAuditStatus(0);
|
|
|
+
|
|
|
// 手动存评分1,2,3
|
|
|
if (StringUtils.isNotEmpty(commonRating.getOtherScore())) {
|
|
|
JSONObject parse = JSONObject.parse(commonRating.getOtherScore());
|
|
|
@@ -107,27 +124,47 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
|
|
|
commonRating.setScoreTwo(parse.getDouble("scoreTwo"));
|
|
|
commonRating.setScoreThree(parse.getDouble("scoreThree"));
|
|
|
}
|
|
|
- int i = this.save(commonRating) ? 0 : 1;
|
|
|
- // 好评发券改为仅在 AI 审核通过后执行,见 doBusinessWithType 中逻辑
|
|
|
+
|
|
|
+ // 2. 先进行文本和图片审核(同步审核,必须通过才能保存)
|
|
|
+ // 处理imageUrls可能为null的情况
|
|
|
+ String imageUrlsStr = commonRating.getImageUrls();
|
|
|
+ List<String> imageUrlList = new ArrayList<>();
|
|
|
+ if (StringUtils.isNotEmpty(imageUrlsStr)) {
|
|
|
+ imageUrlList = Arrays.asList(imageUrlsStr.split(","));
|
|
|
+ }
|
|
|
+
|
|
|
// 一次遍历完成分类,避免多次流式处理
|
|
|
- Map<String, List<String>> urlCategoryMap = StoreRenovationRequirementServiceImpl.classifyUrls(Arrays.asList(commonRating.getImageUrls().split(",")));
|
|
|
+ Map<String, List<String>> urlCategoryMap = StoreRenovationRequirementServiceImpl.classifyUrls(imageUrlList);
|
|
|
+
|
|
|
+ // 3. 文本和图片审核(同步,必须通过)
|
|
|
AiContentModerationUtil.AuditResult auditResult = new AiContentModerationUtil.AuditResult(true, "");
|
|
|
- if( StringUtils.isNotEmpty(commonRating.getContent()) || urlCategoryMap.get("image").size() > 0){
|
|
|
+ // 只要有内容或图片,就必须审核
|
|
|
+ if (StringUtils.isNotEmpty(commonRating.getContent()) || !urlCategoryMap.get("image").isEmpty()) {
|
|
|
auditResult = aiContentModerationUtil.auditContent(commonRating.getContent(), urlCategoryMap.get("image"));
|
|
|
}
|
|
|
+
|
|
|
+ // 4. 文本/图片审核不通过,直接返回,不保存
|
|
|
if (!auditResult.isPassed()) {
|
|
|
- // 审核不通过
|
|
|
- CommonRating rating = this.getById(commonRating.getId());
|
|
|
- rating.setAuditStatus(2);
|
|
|
- rating.setAuditReason(auditResult.getFailureReason());
|
|
|
- this.saveOrUpdate(rating);
|
|
|
- } else{
|
|
|
+ log.warn("评价审核不通过,不保存评论。原因:{}", auditResult.getFailureReason());
|
|
|
+ return 2; // 返回2表示文本内容异常
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 文本/图片审核通过,保存评论(状态为待审核,等待视频审核)
|
|
|
+ int i = this.save(commonRating) ? 0 : 1;
|
|
|
+ if (i != 0) {
|
|
|
+ log.error("保存评价失败");
|
|
|
+ return 1;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 6. 如果有视频,进行异步视频审核
|
|
|
+ List<String> videoUrls = urlCategoryMap.get("video");
|
|
|
+ if (!videoUrls.isEmpty()) {
|
|
|
CompletableFuture.runAsync(() -> {
|
|
|
AiVideoModerationUtil.VideoAuditResult videoAuditResult = null;
|
|
|
try {
|
|
|
// 调用审核接口,增加超时控制(避免接口挂死)
|
|
|
videoAuditResult = CompletableFuture.supplyAsync(
|
|
|
- () -> aiVideoModerationUtil.auditVideos(urlCategoryMap.get("video")),
|
|
|
+ () -> aiVideoModerationUtil.auditVideos(videoUrls),
|
|
|
commonVideoTaskExecutor
|
|
|
).get();
|
|
|
|
|
|
@@ -136,41 +173,60 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
|
|
|
// 重新查询最新的rating,避免并发覆盖
|
|
|
CommonRating rating = this.getById(commonRating.getId());
|
|
|
if (Objects.isNull(rating)) {
|
|
|
- log.error("视频审核后更新失败,rating,ID:{}", rating.getId());
|
|
|
+ log.error("视频审核后更新失败,rating ID:{}", commonRating.getId());
|
|
|
return;
|
|
|
}
|
|
|
rating.setAuditStatus(2);
|
|
|
// 失败原因
|
|
|
rating.setAuditReason(videoAuditResult.getFailureReason());
|
|
|
this.saveOrUpdate(rating);
|
|
|
- log.info("视频审核不通过,已更新状态,requirementID:{},原因:{}",
|
|
|
+ log.info("视频审核不通过,已更新状态,ratingID:{},原因:{}",
|
|
|
rating.getId(), videoAuditResult.getFailureReason());
|
|
|
} else if (Objects.nonNull(videoAuditResult) && videoAuditResult.isPassed()) {
|
|
|
- // 审核通过也更新状态(可选,根据业务需求)
|
|
|
+ // 审核通过也更新状态
|
|
|
CommonRating rating = this.getById(commonRating.getId());
|
|
|
if (Objects.nonNull(rating)) {
|
|
|
rating.setAuditStatus(1);
|
|
|
-// latestRequirement.setAuditReason(null);
|
|
|
this.saveOrUpdate(rating);
|
|
|
// 对不同的businessType进行不同的处理,
|
|
|
doBusinessWithType(commonRating);
|
|
|
- log.info("视频审核通过,已更新状态,requirementID:{}", rating.getId());
|
|
|
+ log.info("视频审核通过,已更新状态,ratingID:{}", rating.getId());
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ // 视频审核结果为空,可能是没有视频或审核服务异常
|
|
|
+ // 如果文本/图片已通过,且没有视频,则设置为审核通过
|
|
|
+ CommonRating rating = this.getById(commonRating.getId());
|
|
|
+ if (Objects.nonNull(rating)) {
|
|
|
+ rating.setAuditStatus(1);
|
|
|
+ this.saveOrUpdate(rating);
|
|
|
+ doBusinessWithType(commonRating);
|
|
|
+ log.info("无视频或视频审核结果为空,文本/图片已通过,设置为审核通过,ratingID:{}", rating.getId());
|
|
|
}
|
|
|
}
|
|
|
} catch (Exception e) {
|
|
|
- log.error("视频审核接口调用异常,commonRating:{}", commonRating.getId(), e);
|
|
|
+ log.error("视频审核接口调用异常,commonRating ID:{}", commonRating.getId(), e);
|
|
|
CommonRating rating = this.getById(commonRating.getId());
|
|
|
if (Objects.nonNull(rating)) {
|
|
|
rating.setAuditStatus(2);
|
|
|
- rating.setAuditReason("视频审核接口调用异常:" + e.getMessage()); // 实际需捕获具体异常信息
|
|
|
+ rating.setAuditReason("视频审核接口调用异常:" + e.getMessage());
|
|
|
this.saveOrUpdate(rating);
|
|
|
}
|
|
|
}
|
|
|
}, commonVideoTaskExecutor);
|
|
|
+ } else {
|
|
|
+ // 7. 没有视频,文本/图片审核已通过,直接设置为审核通过
|
|
|
+ CommonRating rating = this.getById(commonRating.getId());
|
|
|
+ if (Objects.nonNull(rating)) {
|
|
|
+ rating.setAuditStatus(1);
|
|
|
+ this.saveOrUpdate(rating);
|
|
|
+ doBusinessWithType(commonRating);
|
|
|
+ log.info("无视频,文本/图片审核已通过,设置为审核通过,ratingID:{}", rating.getId());
|
|
|
+ }
|
|
|
}
|
|
|
+
|
|
|
return i;
|
|
|
} catch (Exception e) {
|
|
|
- log.error("CommonRatingService.saveCommonRating ERROR Msg={}", e.getMessage());
|
|
|
+ log.error("CommonRatingService.saveCommonRating ERROR Msg={}", e.getMessage(), e);
|
|
|
return 1;
|
|
|
}
|
|
|
}
|
|
|
@@ -225,35 +281,73 @@ public class CommonRatingServiceImpl extends ServiceImpl<CommonRatingMapper, Com
|
|
|
webSocketProcess.sendMessage("store_" + storeUser.getPhone(), JSONObject.from(websocketVo).toJSONString());
|
|
|
}
|
|
|
// 用户好评且 AI 审核通过后:按运营活动表(评论有礼)配置的 coupon_id/voucher_id 发放,用 participation_limit 限制参与次数,发放成功后扣减库存,按优惠券/代金券发对应通知
|
|
|
+ // 支持多个活动同时生效:如果一个商家有多个正在开启的活动(如一个送优惠券,一个送代金券),都会执行
|
|
|
if (score != null && score >= 4.5 && commonRating.getUserId() != null && commonRating.getBusinessId() != null) {
|
|
|
try {
|
|
|
Date now = new Date();
|
|
|
+ // 查询所有符合条件的活动(不再限制为1个)
|
|
|
LambdaQueryWrapper<StoreOperationalActivity> activityWrapper = new LambdaQueryWrapper<>();
|
|
|
activityWrapper.eq(StoreOperationalActivity::getStoreId, businessId)
|
|
|
.eq(StoreOperationalActivity::getActivityType, "COMMENT")
|
|
|
.in(StoreOperationalActivity::getStatus, 5, 8)
|
|
|
.le(StoreOperationalActivity::getStartTime, now)
|
|
|
.ge(StoreOperationalActivity::getEndTime, now)
|
|
|
- .orderByDesc(StoreOperationalActivity::getId)
|
|
|
- .last("LIMIT 1");
|
|
|
- StoreOperationalActivity activity = storeOperationalActivityMapper.selectOne(activityWrapper);
|
|
|
- if (activity == null || (activity.getCouponId() == null && activity.getVoucherId() == null)) {
|
|
|
+ .orderByDesc(StoreOperationalActivity::getId); // 按ID降序,优先处理最新创建的活动
|
|
|
+ List<StoreOperationalActivity> activities = storeOperationalActivityMapper.selectList(activityWrapper);
|
|
|
+
|
|
|
+ if (activities == null || activities.isEmpty()) {
|
|
|
+ log.info("CommonRatingService 好评送券:未找到符合条件的活动,userId={}, storeId={}",
|
|
|
+ commonRating.getUserId(), businessId);
|
|
|
return;
|
|
|
}
|
|
|
- Integer limit = activity.getParticipationLimit();
|
|
|
- if (limit != null && limit > 0) {
|
|
|
- int passedCount = commonRatingMapper.countPassedGoodRatingsByUserAndStore(commonRating.getUserId(), businessId);
|
|
|
- if (passedCount > limit) {
|
|
|
- log.info("CommonRatingService 好评送券跳过:超过运营活动参与次数 participation_limit={}, count={}, userId={}, storeId={}",
|
|
|
- limit, passedCount, commonRating.getUserId(), businessId);
|
|
|
- return;
|
|
|
+
|
|
|
+ // 遍历所有活动,分别处理每个活动
|
|
|
+ int totalIssuedCount = 0;
|
|
|
+ for (StoreOperationalActivity activity : activities) {
|
|
|
+ // 跳过没有配置优惠券或代金券的活动
|
|
|
+ if (activity.getCouponId() == null && activity.getVoucherId() == null) {
|
|
|
+ log.info("CommonRatingService 好评送券跳过:活动未配置优惠券或代金券,activityId={}, userId={}, storeId={}",
|
|
|
+ activity.getId(), commonRating.getUserId(), businessId);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 检查参与次数限制(每个活动单独计算)
|
|
|
+ Integer limit = activity.getParticipationLimit();
|
|
|
+ if (limit != null && limit > 0) {
|
|
|
+ // 只统计活动开始时间之后的好评,活动开始前的评论不计入参与次数
|
|
|
+ int passedCount = commonRatingMapper.countPassedGoodRatingsByUserAndStore(
|
|
|
+ commonRating.getUserId(), businessId, activity.getStartTime());
|
|
|
+ if (passedCount > limit) {
|
|
|
+ log.info("CommonRatingService 好评送券跳过:超过运营活动参与次数 activityId={}, participation_limit={}, count={}, userId={}, storeId={}, activityStartTime={}",
|
|
|
+ activity.getId(), limit, passedCount, commonRating.getUserId(), businessId, activity.getStartTime());
|
|
|
+ continue; // 跳过该活动,继续处理下一个
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 发放优惠券/代金券(使用4参数版本,直接传入活动配置的券ID)
|
|
|
+ int issuedCount = lifeDiscountCouponStoreFriendService.issueCouponForGoodRating(
|
|
|
+ Integer.valueOf(Math.toIntExact(commonRating.getUserId())), businessId,
|
|
|
+ activity.getCouponId(), activity.getVoucherId());
|
|
|
+
|
|
|
+ if (issuedCount > 0) {
|
|
|
+ totalIssuedCount += issuedCount;
|
|
|
+ log.info("CommonRatingService 好评送券成功:activityId={}, 发放数量={}, userId={}, storeId={}, couponId={}, voucherId={}",
|
|
|
+ activity.getId(), issuedCount, commonRating.getUserId(), businessId,
|
|
|
+ activity.getCouponId(), activity.getVoucherId());
|
|
|
+ } else {
|
|
|
+ log.warn("CommonRatingService 好评送券失败:activityId={}, 发放数量=0, userId={}, storeId={}, couponId={}, voucherId={}",
|
|
|
+ activity.getId(), commonRating.getUserId(), businessId,
|
|
|
+ activity.getCouponId(), activity.getVoucherId());
|
|
|
}
|
|
|
}
|
|
|
-// lifeDiscountCouponStoreFriendService.issueCouponForGoodRating(
|
|
|
-// Math.toIntExact(commonRating.getUserId()), businessId,
|
|
|
-// activity.getCouponId(), activity.getVoucherId());
|
|
|
+
|
|
|
+ if (totalIssuedCount > 0) {
|
|
|
+ log.info("CommonRatingService 好评送券汇总:总发放数量={}, 活动数={}, userId={}, storeId={}",
|
|
|
+ totalIssuedCount, activities.size(), commonRating.getUserId(), businessId);
|
|
|
+ }
|
|
|
} catch (Exception ex) {
|
|
|
- log.warn("CommonRatingService 好评送券异常 userId={}, storeId={}, msg={}", commonRating.getUserId(), businessId, ex.getMessage());
|
|
|
+ log.warn("CommonRatingService 好评送券异常 userId={}, storeId={}, msg={}",
|
|
|
+ commonRating.getUserId(), businessId, ex.getMessage(), ex);
|
|
|
}
|
|
|
}
|
|
|
}
|