From 7d0f99d2e5c337b47cdd02964a30c06751dad80b Mon Sep 17 00:00:00 2001 From: zhx Date: Tue, 21 Jul 2026 14:36:46 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=B1=95=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../aslan/AslanGiftChallengeServiceImpl.java | 107 +++++++++++++++++- 1 file changed, 101 insertions(+), 6 deletions(-) diff --git a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/aslan/AslanGiftChallengeServiceImpl.java b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/aslan/AslanGiftChallengeServiceImpl.java index db875f8e..8cdaf028 100644 --- a/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/aslan/AslanGiftChallengeServiceImpl.java +++ b/rc-service/rc-service-other/other-application/src/main/java/com/red/circle/other/app/service/activity/aslan/AslanGiftChallengeServiceImpl.java @@ -19,6 +19,7 @@ import com.red.circle.other.infra.common.activity.send.SendRewardSingle; import com.red.circle.other.infra.database.rds.dao.activity.aslan.AslanGiftChallengeMapper; import com.red.circle.other.infra.database.rds.entity.activity.PropsActivityRewardConfig; import com.red.circle.other.infra.database.rds.entity.activity.PropsActivityRewardGroup; +import com.red.circle.other.infra.database.rds.entity.badge.BadgePictureConfig; import com.red.circle.other.infra.database.rds.model.activity.aslan.AslanGiftChallengeRows.ActivityRow; import com.red.circle.other.infra.database.rds.model.activity.aslan.AslanGiftChallengeRows.DeliveryItemRow; import com.red.circle.other.infra.database.rds.model.activity.aslan.AslanGiftChallengeRows.GiftLedgerRow; @@ -30,6 +31,7 @@ import com.red.circle.other.infra.database.rds.model.activity.aslan.AslanGiftCha import com.red.circle.other.infra.database.rds.model.activity.aslan.AslanGiftChallengeRows.UserScoreRow; import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRewardConfigService; import com.red.circle.other.infra.database.rds.service.activity.PropsActivityRewardGroupService; +import com.red.circle.other.infra.database.rds.service.badge.BadgePictureConfigService; import com.red.circle.other.inner.endpoint.material.props.PropsActivityRewardConfigClient; import com.red.circle.other.inner.endpoint.user.user.UserProfileClient; import com.red.circle.other.inner.enums.material.GiftCurrencyType; @@ -120,6 +122,7 @@ public class AslanGiftChallengeServiceImpl implements AslanGiftChallengeService private final PropsActivityRewardConfigService rewardConfigService; private final PropsActivityRewardGroupService rewardGroupService; private final PropsActivityRewardConfigClient rewardConfigClient; + private final BadgePictureConfigService badgePictureConfigService; private final UserProfileClient userProfileClient; private final UserProfileGateway userProfileGateway; @@ -1014,7 +1017,8 @@ public class AslanGiftChallengeServiceImpl implements AslanGiftChallengeService // 展示字段获取失败不影响核心发放快照;type/content/quantity 等实际发放字段始终直接 // 来自同一事务读取的本地奖励配置,确保同一期所有用户收到完全一致的资源版本。 - Map> displayDetails = loadRewardDetails(groupIds); + Map> displayDetails = loadRewardDetails( + activity.getSysOrigin(), groupIds); Map displayByConfigId = displayDetails.values().stream() .filter(Objects::nonNull).flatMap(List::stream).filter(Objects::nonNull) .filter(item -> item.getId() != null) @@ -1638,21 +1642,33 @@ public class AslanGiftChallengeServiceImpl implements AslanGiftChallengeService if (!snapshots.isEmpty()) { // 活动一旦启用就只展示冻结模板;某组快照缺失时返回空而不是回退实时资源组,避免 // 页面展示与实际 delivery item 在配置异常时悄悄采用两个版本。 - return snapshots.stream().filter(item -> groupIds.contains(item.getResourceGroupId())) + Map> items = snapshots.stream() + .filter(item -> groupIds.contains(item.getResourceGroupId())) .collect(Collectors.groupingBy(RewardSnapshotRow::getResourceGroupId, Collectors.mapping(this::toRewardItem, Collectors.toList()))); + // 老活动启用时 BADGE 的 cover/sourceUrl 尚未被写入快照。这里仅按冻结 content + // 补展示图片,不修改 type/content/quantity,确保现网可见性与历史发奖事实解耦。 + enrichBadgePictures(activity.getSysOrigin(), items.values().stream() + .filter(Objects::nonNull).flatMap(List::stream).toList()); + return items; } - return loadRewardDetails(groupIds).entrySet().stream().collect(Collectors.toMap( - Map.Entry::getKey, entry -> toRewardItems(entry.getValue()))); + return loadRewardDetails(activity.getSysOrigin(), groupIds).entrySet().stream() + .collect(Collectors.toMap( + Map.Entry::getKey, entry -> toRewardItems(entry.getValue()))); } - private Map> loadRewardDetails(Set groupIds) { + private Map> loadRewardDetails(String sysOrigin, + Set groupIds) { if (groupIds == null || groupIds.isEmpty()) { return Collections.emptyMap(); } try { - return Optional.ofNullable(rewardConfigClient.mapByGroupIds(groupIds).getBody()) + Map> details = Optional.ofNullable( + rewardConfigClient.mapByGroupIds(groupIds).getBody()) .orElse(Collections.emptyMap()); + enrichBadgePictureDetails(sysOrigin, details.values().stream() + .filter(Objects::nonNull).flatMap(List::stream).toList()); + return details; } catch (Exception exception) { // 奖励展示服务短暂失败不应让活动详情、积分和任务状态整体不可用。 log.warn("Load Aslan Gift Challenge reward details failed, groupIds={}", groupIds, @@ -1661,6 +1677,85 @@ public class AslanGiftChallengeServiceImpl implements AslanGiftChallengeService } } + private void enrichBadgePictureDetails(String sysOrigin, + List items) { + // detail 是高频读接口:只为缺封面的徽章收集 ID,并通过一次 IN 查询批量补图; + // 已完整冻结的活动不会额外访问徽章图片表。 + Map pictures = loadBadgePictures(sysOrigin, items.stream() + .filter(Objects::nonNull) + .filter(item -> isBadgeReward(item.getType()) + && StringUtils.isBlank(item.getCover())) + .map(PropsActivityRewardConfigInfoDTO::getContent).toList()); + items.stream().filter(Objects::nonNull) + .filter(item -> isBadgeReward(item.getType()) + && StringUtils.isBlank(item.getCover())) + .forEach(item -> { + BadgePictureConfig picture = pictures.get(parseRewardResourceId(item.getContent())); + if (picture == null) { + return; + } + if (StringUtils.isBlank(item.getCover())) { + item.setCover(picture.getSelectUrl()); + } + if (StringUtils.isBlank(item.getSourceUrl())) { + item.setSourceUrl(picture.getAnimationUrl()); + } + }); + } + + private void enrichBadgePictures(String sysOrigin, List items) { + Map pictures = loadBadgePictures(sysOrigin, items.stream() + .filter(Objects::nonNull) + .filter(item -> isBadgeReward(item.getType()) + && StringUtils.isBlank(item.getCover())) + .map(RewardItem::getContent).toList()); + items.stream().filter(Objects::nonNull) + .filter(item -> isBadgeReward(item.getType()) + && StringUtils.isBlank(item.getCover())) + .forEach(item -> { + BadgePictureConfig picture = pictures.get(parseRewardResourceId(item.getContent())); + if (picture == null) { + return; + } + if (StringUtils.isBlank(item.getCover())) { + item.setCover(picture.getSelectUrl()); + } + if (StringUtils.isBlank(item.getSourceUrl())) { + item.setSourceUrl(picture.getAnimationUrl()); + } + }); + } + + private Map loadBadgePictures(String sysOrigin, + List contents) { + Set badgeIds = contents.stream().map(this::parseRewardResourceId) + .filter(Objects::nonNull).collect(Collectors.toSet()); + if (StringUtils.isBlank(sysOrigin) || badgeIds.isEmpty()) { + return Collections.emptyMap(); + } + try { + return Optional.ofNullable(badgePictureConfigService.mapBadge(sysOrigin, badgeIds)) + .orElse(Collections.emptyMap()); + } catch (Exception exception) { + // 图片配置只影响 H5 展示;查询失败时保留奖励组的发奖字段,不能让整个活动详情降级为空。 + log.warn("Load Aslan Gift Challenge badge pictures failed, origin={}, badgeIds={}", + sysOrigin, badgeIds, exception); + return Collections.emptyMap(); + } + } + + private Long parseRewardResourceId(String content) { + try { + return StringUtils.isBlank(content) ? null : Long.valueOf(content.trim()); + } catch (NumberFormatException ignored) { + return null; + } + } + + private boolean isBadgeReward(String type) { + return "BADGE".equals(type) || "PROP_COUPON".equals(type); + } + private List toRewardItems(List items) { return Optional.ofNullable(items).orElse(Collections.emptyList()).stream() .sorted(Comparator.comparingInt(item -> Optional.ofNullable(item.getSort()).orElse(0)))