记得上下班打卡 | git大法好,push需谨慎

Commit 13107e71 authored by wangyifan's avatar wangyifan

Merge branch 'dev-caomeihuizhang' into container-test

parents fc707e10 c43bc066
......@@ -78,4 +78,6 @@ ALTER TABLE `adam_caomei_badge` ADD COLUMN `sort` int(11) NOT NULL DEFAULT 0 COM
ALTER TABLE `adam_caomei_badge` ADD COLUMN `share_text` varchar(255) NOT NULL DEFAULT '' COMMENT '徽章分享文案' AFTER `sort`;
-- 2026-04-23 新增徽章副标题字段
ALTER TABLE `adam_caomei_badge` ADD COLUMN `sub_title` varchar(32) NOT NULL DEFAULT '' COMMENT '徽章副标题(最多20字)' AFTER `name`;
\ No newline at end of file
ALTER TABLE `adam_caomei_badge` ADD COLUMN `sub_title` varchar(32) NOT NULL DEFAULT '' COMMENT '徽章副标题(最多20字)' AFTER `name`;
-- TODO 上线前需要运行:adam_caomei_passport_inserts_BATCH-CAOMEI-B1.sql
\ No newline at end of file
......@@ -5,12 +5,14 @@ import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty;
import java.util.List;
@Data
@ApiModel("草莓徽章-认领参数")
public class AdamCaomeiBadgeClaimParam {
@NotBlank(message = "徽章ID不能为空")
@ApiModelProperty(value = "徽章ID", required = true)
private String badgeId;
@NotEmpty(message = "徽章ID列表不能为空")
@ApiModelProperty(value = "徽章ID列表", required = true)
private List<@NotBlank(message = "徽章ID不能为空") String> badgeIds;
}
......@@ -14,7 +14,7 @@ public interface IAdamCaomeiBadgeUserService {
/**
* 认领徽章
*/
ResponseDto<String> claimBadge(String badgeId, String uid);
ResponseDto<List<String>> claimBadges(List<String> badgeIds, String uid);
/**
* 补签申请记录列表(用户端)
......
......@@ -87,6 +87,18 @@ public interface AdamCaomeiBadgeMapper extends BaseMapper<AdamCaomeiBadge> {
@Select("select count(1) from adam_caomei_user_badge where user_id = #{userId} and badge_id = #{badgeId}")
int checkUserBadgeExists(@Param("userId") String userId, @Param("badgeId") String badgeId);
@Select({
"<script>",
"select badge_id from adam_caomei_user_badge",
"where user_id = #{userId} and badge_id in",
"<foreach collection='badgeIds' item='badgeId' open='(' separator=',' close=')'>",
"#{badgeId}",
"</foreach>",
"</script>"
})
List<String> selectClaimedBadgeIdsByUserAndBadgeIds(@Param("userId") String userId,
@Param("badgeIds") List<String> badgeIds);
@Insert({
"insert into adam_caomei_user_badge (user_id, badge_id, source, created_at) ",
"values (#{userId}, #{badgeId}, #{source}, now())"
......
......@@ -36,9 +36,9 @@ public class AdamCaomeiBadgeUserController {
@ApiOperationSupport(order = 1)
@ApiOperation("认领徽章")
@PostMapping("claim")
public ResponseDto<String> claim(@Valid @RequestBody AdamCaomeiBadgeClaimParam param) {
public ResponseDto<List<String>> claim(@Valid @RequestBody AdamCaomeiBadgeClaimParam param) {
String uid = CurrentUtil.getCurrentUid();
return adamCaomeiBadgeUserService.claimBadge(param.getBadgeId(), uid);
return adamCaomeiBadgeUserService.claimBadges(param.getBadgeIds(), uid);
}
@ApiOperationSupport(order = 2)
......
......@@ -30,8 +30,11 @@ import org.springframework.transaction.annotation.Transactional;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.HashSet;
import java.util.stream.Collectors;
@Slf4j
......@@ -51,21 +54,36 @@ public class AdamCaomeiBadgeUserServiceImpl implements IAdamCaomeiBadgeUserServi
@Override
@Transactional(rollbackFor = Exception.class)
public ResponseDto<String> claimBadge(String badgeId, String uid) {
public ResponseDto<List<String>> claimBadges(List<String> badgeIds, String uid) {
if (StringUtils.isBlank(uid)) {
return ResponseDto.failure(ErrorMapping.get("10001"));
}
if (badgeIds == null || badgeIds.isEmpty()) {
return ResponseDto.failure("徽章ID列表不能为空");
}
List<String> requestBadgeIds = badgeIds.stream()
.filter(StringUtils::isNotBlank)
.map(StringUtils::trim)
.collect(Collectors.collectingAndThen(Collectors.toCollection(LinkedHashSet::new), ArrayList::new));
if (requestBadgeIds.isEmpty()) {
return ResponseDto.failure("徽章ID列表不能为空");
}
// 1. 查询徽章配置
AdamCaomeiBadge badge = adamCaomeiBadgeMapper.selectOne(
// 1) 批量查询徽章配置(一次查询)
List<AdamCaomeiBadge> badges = adamCaomeiBadgeMapper.selectList(
Wrappers.lambdaQuery(AdamCaomeiBadge.class)
.eq(AdamCaomeiBadge::getBadgeId, badgeId)
.in(AdamCaomeiBadge::getBadgeId, requestBadgeIds)
.eq(AdamCaomeiBadge::getDisplayStatus, 1)
);
if (badge == null) {
log.error("[claimBadge] 徽章不存在或未上架, uid: {}, badgeId: {}", uid, badgeId);
if (badges == null || badges.size() != requestBadgeIds.size()) {
log.error("[claimBadges] 存在徽章不存在或未上架, uid: {}, badgeIds: {}", uid, requestBadgeIds);
return ResponseDto.failure(ErrorMapping.get("10607"));
}
Map<String, AdamCaomeiBadge> badgeMap = badges.stream()
.collect(Collectors.toMap(AdamCaomeiBadge::getBadgeId, b -> b));
List<AdamCaomeiBadge> orderedBadges = requestBadgeIds.stream()
.map(badgeMap::get)
.collect(Collectors.toList());
// 2. 与收货地址 add 一致:先取 Redis(或回源)用户徽章列表,再判重;DB 再兜一层防缓存未命中或其它写入路径
List<AdamCaomeiPassportUserBadgeDto> badgeVos = adamRdmService.getUserCaomeiBadgesByUid(uid);
......@@ -74,19 +92,35 @@ public class AdamCaomeiBadgeUserServiceImpl implements IAdamCaomeiBadgeUserServi
} else {
badgeVos = new ArrayList<>(badgeVos);
}
if (badgeVos.stream().anyMatch(b -> badgeId.equals(b.getBadgeId()))) {
log.info("[claimBadge] 用户已领取过该徽章(Redis列表), uid: {}, badgeId: {}", uid, badgeId);
Set<String> claimedInCache = badgeVos.stream()
.map(AdamCaomeiPassportUserBadgeDto::getBadgeId)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
if (requestBadgeIds.stream().anyMatch(claimedInCache::contains)) {
log.info("[claimBadges] 用户已领取过徽章(Redis列表), uid: {}, badgeIds: {}", uid, requestBadgeIds);
return ResponseDto.failure(ErrorMapping.get("10608"));
}
int count = adamCaomeiBadgeMapper.checkUserBadgeExists(uid, badgeId);
if (count > 0) {
log.info("[claimBadge] 用户已领取过该徽章, uid: {}, badgeId: {}", uid, badgeId);
List<String> claimedInDb = adamCaomeiBadgeMapper.selectClaimedBadgeIdsByUserAndBadgeIds(uid, requestBadgeIds);
if (claimedInDb != null && !claimedInDb.isEmpty()) {
log.info("[claimBadges] 用户已领取过徽章(DB), uid: {}, badgeIds: {}", uid, claimedInDb);
return ResponseDto.failure(ErrorMapping.get("10608"));
}
int type = badge.getType() == null ? 0 : badge.getType();
boolean hasType1 = orderedBadges.stream().anyMatch(b -> b.getType() != null && b.getType() == 1);
boolean hasType2 = orderedBadges.stream().anyMatch(b -> b.getType() != null && b.getType() == 2);
boolean hasType3 = orderedBadges.stream().anyMatch(b -> b.getType() != null && b.getType() == 3);
boolean hasUnknownType = orderedBadges.stream().anyMatch(b -> b.getType() == null || (b.getType() != 1 && b.getType() != 2 && b.getType() != 3));
if (hasType3) {
log.error("[claimBadges] 含特殊徽章不可自助领取, uid: {}, badgeIds: {}", uid, requestBadgeIds);
return ResponseDto.failure(ErrorMapping.get("10612"));
}
if (hasUnknownType) {
log.error("[claimBadges] 含未知徽章类型, uid: {}, badgeIds: {}", uid, requestBadgeIds);
return ResponseDto.failure(ErrorMapping.get("10613"));
}
if (type == 1) {
if (hasType1) {
AdamCaomeiPassport bound = adamCaomeiPassportMapper.selectOne(
Wrappers.lambdaQuery(AdamCaomeiPassport.class)
.eq(AdamCaomeiPassport::getUserId, uid)
......@@ -94,57 +128,92 @@ public class AdamCaomeiBadgeUserServiceImpl implements IAdamCaomeiBadgeUserServi
.last("limit 1")
);
if (bound == null) {
log.error("[claimBadge] 认领护照纪念徽章需先绑定护照, uid: {}, badgeId: {}", uid, badgeId);
log.error("[claimBadges] 认领护照纪念徽章需先绑定护照, uid: {}, badgeIds: {}", uid, requestBadgeIds);
return ResponseDto.failure(ErrorMapping.get("10609"));
}
}
// 发放徽章 (source: 1-绑定护照自动发放/护照徽章认领):先写 Redis,再 MQ 落库
grantUserBadgeRedisThenMq(uid, badge, 1, badgeVos);
log.info("[claimBadge] 护照纪念徽章认领成功, uid: {}, badgeId: {}", uid, badgeId);
return ResponseDto.success(badgeId);
} else if (type == 2) {
Set<String> paidPerformanceSet = Collections.emptySet();
Set<String> passedApplyBadgeIds = Collections.emptySet();
Set<String> passedApplyPerformanceIds = Collections.emptySet();
Map<String, Set<String>> perfAllBadgeIds = Collections.emptyMap();
if (hasType2) {
AdamRealInfoVo real = adamRdmService.getRealInfoVoByUidPlain(uid);
if (real == null || real.getState() == null || real.getState() != 1 || StringUtils.isBlank(real.getIdCard())) {
log.error("[claimBadge] 认领演出徽章需先实名, uid: {}, badgeId: {}", uid, badgeId);
log.error("[claimBadges] 认领演出徽章需先实名, uid: {}, badgeIds: {}", uid, requestBadgeIds);
return ResponseDto.failure(ErrorMapping.get("10610"));
}
List<String> paidPerformanceIds = adamRdmService.getPaidPerformanceIdsByIdCard(real.getIdCard());
paidPerformanceSet = paidPerformanceIds == null ? Collections.emptySet() : new HashSet<>(paidPerformanceIds);
String idCard = real.getIdCard();
List<String> paidPerformanceIds = adamRdmService.getPaidPerformanceIdsByIdCard(idCard);
boolean hasPaidRecord = paidPerformanceIds != null && paidPerformanceIds.contains(badge.getPerformanceId());
String perfId = StringUtils.trimToEmpty(badge.getPerformanceId());
boolean hasPassedApply;
if (StringUtils.isNotBlank(perfId)) {
hasPassedApply = countApplyForUserPerformanceAudit(uid, perfId, 1) > 0;
} else {
Integer passedApplyCount = badgeApplyRecordMapper.selectCount(
Wrappers.lambdaQuery(AdamCaomeiBadgeApplyRecord.class)
.eq(AdamCaomeiBadgeApplyRecord::getUserId, uid)
.eq(AdamCaomeiBadgeApplyRecord::getBadgeId, badgeId)
.eq(AdamCaomeiBadgeApplyRecord::getAuditStatus, 1)
List<AdamCaomeiBadgeApplyRecord> passedApplyRecords = badgeApplyRecordMapper.selectList(
Wrappers.lambdaQuery(AdamCaomeiBadgeApplyRecord.class)
.eq(AdamCaomeiBadgeApplyRecord::getUserId, uid)
.eq(AdamCaomeiBadgeApplyRecord::getAuditStatus, 1)
);
passedApplyBadgeIds = new HashSet<>();
passedApplyPerformanceIds = new HashSet<>();
if (passedApplyRecords != null) {
for (AdamCaomeiBadgeApplyRecord r : passedApplyRecords) {
if (r == null) {
continue;
}
if (StringUtils.isNotBlank(r.getBadgeId())) {
passedApplyBadgeIds.add(r.getBadgeId());
}
if (StringUtils.isNotBlank(r.getPerformanceId())) {
passedApplyPerformanceIds.add(r.getPerformanceId());
}
}
}
Set<String> targetPerfIds = orderedBadges.stream()
.filter(b -> b.getType() != null && b.getType() == 2)
.map(AdamCaomeiBadge::getPerformanceId)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toSet());
if (!targetPerfIds.isEmpty()) {
List<AdamCaomeiBadge> perfBadges = adamCaomeiBadgeMapper.selectList(
Wrappers.lambdaQuery(AdamCaomeiBadge.class)
.eq(AdamCaomeiBadge::getType, 2)
.in(AdamCaomeiBadge::getPerformanceId, targetPerfIds)
);
hasPassedApply = passedApplyCount != null && passedApplyCount > 0;
perfAllBadgeIds = perfBadges.stream()
.filter(b -> StringUtils.isNotBlank(b.getPerformanceId()) && StringUtils.isNotBlank(b.getBadgeId()))
.collect(Collectors.groupingBy(AdamCaomeiBadge::getPerformanceId,
Collectors.mapping(AdamCaomeiBadge::getBadgeId, Collectors.toSet())));
}
}
if (!hasPaidRecord && !hasPassedApply) {
log.error("[claimBadge] 无购票记录且无通过的补签申请,无法认领, uid: {}, badgeId: {}", uid, badgeId);
return ResponseDto.failure(ErrorMapping.get("10611"));
List<String> claimedBadgeIds = new ArrayList<>();
for (AdamCaomeiBadge badge : orderedBadges) {
int type = badge.getType() == null ? 0 : badge.getType();
int source = 1;
if (type == 2) {
String perfId = StringUtils.trimToEmpty(badge.getPerformanceId());
boolean hasPaidRecord = StringUtils.isNotBlank(perfId) && paidPerformanceSet.contains(perfId);
boolean hasPassedApply = false;
if (StringUtils.isNotBlank(perfId)) {
if (passedApplyPerformanceIds.contains(perfId)) {
hasPassedApply = true;
} else {
Set<String> badgeIdSet = perfAllBadgeIds.getOrDefault(perfId, Collections.emptySet());
hasPassedApply = badgeIdSet.stream().anyMatch(passedApplyBadgeIds::contains);
}
}
if (!hasPaidRecord && !hasPassedApply) {
log.error("[claimBadges] 无购票记录且无通过补签,无法认领, uid: {}, badgeId: {}", uid, badge.getBadgeId());
return ResponseDto.failure(ErrorMapping.get("10611"));
}
source = hasPaidRecord ? 2 : 3;
}
int source = hasPaidRecord ? 2 : 3;
grantUserBadgeRedisThenMq(uid, badge, source, badgeVos);
log.info("[claimBadge] 演出纪念徽章认领成功, uid: {}, badgeId: {}, 发放数量: {}", uid, badgeId, 1);
return ResponseDto.success(badgeId);
} else if (type == 3) {
log.error("[claimBadge] 特殊徽章不可自助领取, uid: {}, badgeId: {}", uid, badgeId);
return ResponseDto.failure(ErrorMapping.get("10612"));
claimedBadgeIds.add(badge.getBadgeId());
}
log.error("[claimBadge] 未知的徽章类型, uid: {}, badgeId: {}, type: {}", uid, badgeId, type);
return ResponseDto.failure(ErrorMapping.get("10613"));
log.info("[claimBadges] 批量认领成功, uid: {}, badgeIds: {}", uid, claimedBadgeIds);
return ResponseDto.success(claimedBadgeIds);
}
/**
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment