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

Commit 060e4490 authored by wangyifan's avatar wangyifan

草莓护照- 徽章信息缓存

parent 1c969740
......@@ -20,6 +20,8 @@ public class AdamRedisConst {
public static final String INFO_THIRD_PARTY = PREFIX.concat("info:third_party:");
public static final String INFO_ENTERS = PREFIX.concat("info:enters:");
public static final String INFO_ADDRESSES = PREFIX.concat("info:addresses:");
public static final String INFO_CAOMEI_BADGE_PUBLISHED = PREFIX.concat("info:caomei:badge:published");
public static final String INFO_CAOMEI_BADGE_USER = PREFIX.concat("info:caomei:badge:user:");
/**
* {adam:info:biz:{uid},List<com.liquidnet.service.adam.dto.vo.AdamUserBizAcctVo>}
*/
......
package com.liquidnet.service.adam.service;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiPassportHomeVo;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiPassportUserClaimedBadgeVo;
import com.liquidnet.service.base.ResponseDto;
import java.util.List;
/**
* 草莓护照(用户端)
*/
......@@ -11,7 +14,7 @@ public interface IAdamCaomeiPassportUserService {
/**
* 绑定实体护照:校验编号有效性、是否可绑;通过则写入并发放已上架护照纪念徽章(type=1)
*/
ResponseDto<String> bindPassport(String passportNo);
ResponseDto<List<AdamCaomeiPassportUserClaimedBadgeVo>> bindPassport(String passportNo);
/**
* 护照首页:个人信息、实名状态、已认领墙、按类型分组的全部上架徽章
......
......@@ -26,4 +26,10 @@ public interface IAdamCaomeiBadgeAdminService extends IService<AdamCaomeiBadge>
* @return
*/
PageInfo<AdamCaomeiBadgeClaimUserVo> listClaimUsers(AdamCaomeiBadgeClaimUserSearchParam param);
/**
* 清除用户端「上架草莓徽章列表」Redis 缓存,与 adam 侧 {@code AdamRdmService#delPublishedCaomeiBadges} 一致。
* 后台新增/编辑/上下架成功后应调用,避免用户端仍读到旧列表。
*/
void delPublishedCaomeiBadges();
}
......@@ -5,6 +5,8 @@ import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageInfo;
import com.liquidnet.client.admin.zhengzai.adam.service.IAdamCaomeiBadgeAdminService;
import com.liquidnet.common.cache.redis.util.RedisDataSourceUtil;
import com.liquidnet.service.adam.constant.AdamRedisConst;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeClaimCountDto;
import com.liquidnet.service.adam.dto.AdamCaomeiBadgeClaimUserDto;
import com.liquidnet.service.adam.dto.param.AdamCaomeiBadgeClaimUserSearchParam;
......@@ -16,6 +18,7 @@ import com.liquidnet.service.adam.mapper.AdamCaomeiBadgeMapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.Collections;
......@@ -26,6 +29,33 @@ import java.util.stream.Collectors;
@Slf4j
@Service
public class AdamCaomeiBadgeAdminServiceImpl extends ServiceImpl<AdamCaomeiBadgeMapper, AdamCaomeiBadge> implements IAdamCaomeiBadgeAdminService {
@Autowired
private RedisDataSourceUtil redisDataSourceUtil;
@Override
public void delPublishedCaomeiBadges() {
redisDataSourceUtil.getRedisAdamUtil().del(AdamRedisConst.INFO_CAOMEI_BADGE_PUBLISHED);
}
@Override
public boolean save(AdamCaomeiBadge entity) {
boolean ok = super.save(entity);
if (ok) {
delPublishedCaomeiBadges();
}
return ok;
}
@Override
public boolean updateById(AdamCaomeiBadge entity) {
boolean ok = super.updateById(entity);
if (ok) {
delPublishedCaomeiBadges();
}
return ok;
}
@Override
public PageInfo<AdamCaomeiBadgeVo> listWithClaimedCount(AdamCaomeiBadgeSearchParam param) {
LambdaQueryWrapper<AdamCaomeiBadge> queryWrapper = Wrappers.lambdaQuery(AdamCaomeiBadge.class);
......
......@@ -9,6 +9,7 @@ import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import java.util.Date;
import java.util.List;
public interface AdamCaomeiBadgeMapper extends BaseMapper<AdamCaomeiBadge> {
......@@ -60,6 +61,19 @@ public interface AdamCaomeiBadgeMapper extends BaseMapper<AdamCaomeiBadge> {
})
int insertPassportBindingBadgesForUser(@Param("userId") String userId);
@Insert({
"<script>",
"insert ignore into adam_caomei_user_badge (user_id, badge_id, source, created_at) values ",
"<foreach collection='badgeIds' item='badgeId' separator=','>",
"(#{userId}, #{badgeId}, #{source}, #{createdAt})",
"</foreach>",
"</script>"
})
int insertUserBadgesBatch(@Param("userId") String userId,
@Param("badgeIds") List<String> badgeIds,
@Param("source") Integer source,
@Param("createdAt") Date createdAt);
/**
* 根据身份证号查询用户已支付的演出ID列表
*/
......
......@@ -4,6 +4,7 @@ import com.github.xiaoymin.knife4j.annotations.ApiOperationSupport;
import com.github.xiaoymin.knife4j.annotations.ApiSupport;
import com.liquidnet.service.adam.dto.param.AdamCaomeiPassportNoParam;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiPassportHomeVo;
import com.liquidnet.service.adam.dto.vo.AdamCaomeiPassportUserClaimedBadgeVo;
import com.liquidnet.service.adam.service.IAdamCaomeiPassportUserService;
import com.liquidnet.service.base.ResponseDto;
import io.swagger.annotations.Api;
......@@ -14,6 +15,7 @@ import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import java.util.List;
@ApiSupport(order = 10045)
@Api(tags = "草莓护照(用户端)")
......@@ -29,7 +31,7 @@ public class AdamCaomeiPassportUserController {
@ApiOperationSupport(order = 1)
@ApiOperation("绑定实体护照(内含编号有效性、是否可绑等校验)")
@PostMapping("bind")
public ResponseDto<String> bind(@Valid @RequestBody AdamCaomeiPassportNoParam param) {
public ResponseDto<List<AdamCaomeiPassportUserClaimedBadgeVo>> bind(@Valid @RequestBody AdamCaomeiPassportNoParam param) {
return adamCaomeiPassportUserService.bindPassport(param.getPassportNo());
}
......
......@@ -9,10 +9,14 @@ import com.liquidnet.commons.lang.util.IdentityUtils;
import com.liquidnet.commons.lang.util.JsonUtils;
import com.liquidnet.commons.lang.util.SensitizeUtil;
import com.liquidnet.service.adam.constant.AdamRedisConst;
import com.liquidnet.service.adam.dto.AdamCaomeiPassportUserBadgeDto;
import com.liquidnet.service.adam.dto.AdamUserInfoDto;
import com.liquidnet.service.adam.dto.vo.*;
import com.liquidnet.service.adam.entity.AdamCaomeiBadge;
import com.liquidnet.service.adam.entity.AdamEnters;
import com.liquidnet.service.adam.entity.AdamUserMember;
import com.liquidnet.service.adam.mapper.AdamCaomeiBadgeMapper;
import com.liquidnet.service.adam.mapper.AdamCaomeiPassportMapper;
import com.liquidnet.service.adam.mapper.AdamEntersMapper;
import com.liquidnet.service.adam.mapper.AdamUserMapper;
import com.liquidnet.service.adam.mapper.AdamUserMemberMapper;
......@@ -46,6 +50,10 @@ public class AdamRdmService {
AdamUserMemberMapper adamUserMemberMapper;
@Autowired
AdamEntersMapper adamEntersMapper;
@Autowired
AdamCaomeiBadgeMapper adamCaomeiBadgeMapper;
@Autowired
AdamCaomeiPassportMapper adamCaomeiPassportMapper;
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | Switch config */
......@@ -630,6 +638,73 @@ public class AdamRdmService {
redisUtil.del(AdamRedisConst.INFO_ADDRESSES.concat(uid));
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | 草莓徽章缓存 */
public boolean setPublishedCaomeiBadges(List<AdamCaomeiBadge> badges) {
return redisUtil.set(AdamRedisConst.INFO_CAOMEI_BADGE_PUBLISHED, badges, 300);
}
/**
* 获取所有已上架的徽章列表
* @return
*/
public List<AdamCaomeiBadge> getPublishedCaomeiBadges() {
String rk = AdamRedisConst.INFO_CAOMEI_BADGE_PUBLISHED;
List<AdamCaomeiBadge> badges = (List<AdamCaomeiBadge>) redisUtil.get(rk);
if (CollectionUtils.isEmpty(badges)) {
badges = adamCaomeiBadgeMapper.selectList(
new QueryWrapper<AdamCaomeiBadge>().lambda()
.eq(AdamCaomeiBadge::getDisplayStatus, 1)
.orderByAsc(AdamCaomeiBadge::getType)
.orderByDesc(AdamCaomeiBadge::getSort)
.orderByDesc(AdamCaomeiBadge::getMid)
);
if (!CollectionUtils.isEmpty(badges)) {
setPublishedCaomeiBadges(badges);
}
}
return badges;
}
public void delPublishedCaomeiBadges() {
redisUtil.del(AdamRedisConst.INFO_CAOMEI_BADGE_PUBLISHED);
}
/**
* 设置用户领取徽章redis
* @param uid
* @param badges
* @return
*/
public boolean setUserCaomeiBadgesByUid(String uid, List<AdamCaomeiPassportUserBadgeDto> badges) {
return redisUtil.set(AdamRedisConst.INFO_CAOMEI_BADGE_USER.concat(uid), badges, 300);
}
/**
* 获取用户徽章
* @param uid
* @return
*/
public List<AdamCaomeiPassportUserBadgeDto> getUserCaomeiBadgesByUid(String uid) {
String rk = AdamRedisConst.INFO_CAOMEI_BADGE_USER.concat(uid);
List<AdamCaomeiPassportUserBadgeDto> badges = (List<AdamCaomeiPassportUserBadgeDto>) redisUtil.get(rk);
if (CollectionUtils.isEmpty(badges)) {
badges = adamCaomeiPassportMapper.selectUserBadgesByUid(uid);
if (!CollectionUtils.isEmpty(badges)) {
setUserCaomeiBadgesByUid(uid, badges);
}
}
return badges;
}
/**
* 删除用户徽章redis
* @param uid
*/
public void delUserCaomeiBadgesByUid(String uid) {
redisUtil.del(AdamRedisConst.INFO_CAOMEI_BADGE_USER.concat(uid));
}
/* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - | List<购买会员黑名单UID> */
public boolean setBlacklistForMember(List<String> uids) {
......
......@@ -72,6 +72,7 @@ public class AdamCaomeiBadgeUserServiceImpl implements IAdamCaomeiBadgeUserServi
// 发放徽章 (source: 1-绑定护照自动发放/护照徽章认领)
adamCaomeiBadgeMapper.insertUserBadge(uid, badgeId, 1);
adamRdmService.delUserCaomeiBadgesByUid(uid);
log.info("[claimBadge] 护照纪念徽章认领成功, uid: {}, badgeId: {}", uid, badgeId);
return ResponseDto.success(badgeId);
......@@ -101,6 +102,7 @@ public class AdamCaomeiBadgeUserServiceImpl implements IAdamCaomeiBadgeUserServi
// for (AdamCaomeiBadge perfBadge : performanceBadges) {
// 发放徽章 (source: 2-购票自动发放/演出徽章认领)
adamCaomeiBadgeMapper.insertUserBadge(uid, badgeId, 2);
adamRdmService.delUserCaomeiBadgesByUid(uid);
// }
log.info("[claimBadge] 演出纪念徽章认领成功, uid: {}, badgeId: {}, 发放数量: {}", uid, badgeId, 1);
......
......@@ -40,7 +40,7 @@ public class AdamCaomeiPassportUserServiceImpl implements IAdamCaomeiPassportUse
@Override
@Transactional(rollbackFor = Exception.class)
public ResponseDto<String> bindPassport(String passportNo) {
public ResponseDto<List<AdamCaomeiPassportUserClaimedBadgeVo>> bindPassport(String passportNo) {
String uid = CurrentUtil.getCurrentUid();
// 1. 先校验护照信息是否满足绑定条件
......@@ -78,13 +78,41 @@ public class AdamCaomeiPassportUserServiceImpl implements IAdamCaomeiPassportUse
int updatedRows = adamCaomeiPassportMapper.bindPassportAtomic(passportNo.trim(), uid);
if (updatedRows > 0) {
// 4. 绑定成功后,自动发放已上架的“护照纪念徽章”(type=1)
adamCaomeiBadgeMapper.insertPassportBindingBadgesForUser(uid);
// 4. 优先复用已上架徽章缓存,再筛选护照纪念徽章(type=1),用于批量发放和返回给前端弹窗
List<AdamCaomeiBadge> publishedBadges = adamRdmService.getPublishedCaomeiBadges();
if (publishedBadges == null) {
publishedBadges = new ArrayList<>();
}
List<AdamCaomeiBadge> passportTypeBadges = publishedBadges.stream()
.filter(b -> b.getType() != null && b.getType() == 1)
.collect(Collectors.toList());
List<String> badgeIds = passportTypeBadges.stream()
.map(AdamCaomeiBadge::getBadgeId)
.filter(StringUtils::isNotBlank)
.collect(Collectors.toList());
Date grantAt = new Date();
if (!badgeIds.isEmpty()) {
adamCaomeiBadgeMapper.insertUserBadgesBatch(uid, badgeIds, 1, grantAt);
}
// 清理用户徽章缓存,下次读取首页时回源并重建
adamRdmService.delUserCaomeiBadgesByUid(uid);
// 5. 返回本次绑定场景下的护照类型徽章列表(用于前端弹窗)
List<AdamCaomeiPassportUserClaimedBadgeVo> grantedBadges = passportTypeBadges.stream().map(b -> {
AdamCaomeiPassportUserClaimedBadgeVo v = new AdamCaomeiPassportUserClaimedBadgeVo();
v.setBadgeId(b.getBadgeId());
v.setName(StringUtils.defaultString(b.getName()));
v.setIcon(StringUtils.defaultString(b.getIcon()));
v.setType(b.getType());
v.setClaimedAt(grantAt);
v.setSource(1);
return v;
}).collect(Collectors.toList());
log.info("[bindPassport] 护照绑定成功, uid: {}, passportSn: {}", uid, passportNo);
return ResponseDto.success(passportNo);
return ResponseDto.success(grantedBadges);
}
// 5. 如果原子性修改未成功,说明在并发情况下被别人抢先绑定了
// 6. 如果原子性修改未成功,说明在并发情况下被别人抢先绑定了
log.error("[bindPassport] 并发绑定失败,护照已被抢占, passportSn: {}", passportNo);
return ResponseDto.failure(ErrorMapping.get("10602"));
}
......@@ -139,7 +167,10 @@ public class AdamCaomeiPassportUserServiceImpl implements IAdamCaomeiPassportUse
log.info("[getPassportHome] 用户已支付的演出订单数量, uid: {}, 数量: {}", uid, paidPerformanceIds.size());
// 6. 查询用户已认领的所有徽章记录 (用于展示徽章墙)
List<AdamCaomeiPassportUserBadgeDto> rows = adamCaomeiPassportMapper.selectUserBadgesByUid(uid);
List<AdamCaomeiPassportUserBadgeDto> rows = adamRdmService.getUserCaomeiBadgesByUid(uid);
if (rows == null) {
rows = new ArrayList<>();
}
List<AdamCaomeiPassportUserClaimedBadgeVo> claimed = rows.stream().map(r -> {
AdamCaomeiPassportUserClaimedBadgeVo v = new AdamCaomeiPassportUserClaimedBadgeVo();
v.setBadgeId(r.getBadgeId());
......@@ -159,13 +190,10 @@ public class AdamCaomeiPassportUserServiceImpl implements IAdamCaomeiPassportUse
.collect(Collectors.toMap(AdamCaomeiPassportUserBadgeDto::getBadgeId, Function.identity(), (a, b) -> a));
// 7. 查询所有已上架的徽章配置,并按类型升序、排序值降序、ID降序排序
List<AdamCaomeiBadge> published = adamCaomeiBadgeMapper.selectList(
Wrappers.lambdaQuery(AdamCaomeiBadge.class)
.eq(AdamCaomeiBadge::getDisplayStatus, 1)
.orderByAsc(AdamCaomeiBadge::getType)
.orderByDesc(AdamCaomeiBadge::getSort)
.orderByDesc(AdamCaomeiBadge::getMid)
);
List<AdamCaomeiBadge> published = adamRdmService.getPublishedCaomeiBadges();
if (published == null) {
published = new ArrayList<>();
}
log.info("[getPassportHome] 系统已上架的徽章数量, uid: {}, 数量: {}", uid, published.size());
// 8. 组装全部上架徽章列表 (扁平结构,前端按 type 筛选展示)
......
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