
一、前言前面我们实现了用户返利这一节主要是为了对接前端但是就目前的接口而言是不够的上一节实现用户返利也只是实现了签到接口。事实上前端还需要以下信息用户的额度、用户是否已经签到、用户的权重。二、应用接口实现1.获取用户是否已经签到这是前端需要知道的信息这样才可以对签到按钮展示不同动画比如如果已经签到就应该灰度显示。这个接口实现很简单前面我们利用日期作为返利订单的唯一标识现在我们也可以利用日期去查询返利订单如果查到了就说明已经签过到了反之就是没签到返回一个布尔值即可RequestMapping(value is_calendar_sign_rebate, method RequestMethod.POST) Override public ResponseBoolean isCalendarSignRebate(String userId) { try { log.info(查询用户是否完成日历签到返利开始 userId:{}, userId); String outBusinessNo dateFormat.format(new Date()); ListBehaviorRebateOrderEntity behaviorRebateOrderEntities behaviorRebateService.queryOrderByOutBusinessNo(userId, outBusinessNo); log.info(查询用户是否完成日历签到返利完成 userId:{} orders.size:{}, userId, behaviorRebateOrderEntities.size()); return Response.Booleanbuilder() .code(ResponseCode.SUCCESS.getCode()) .data(!behaviorRebateOrderEntities.isEmpty()) .info(ResponseCode.SUCCESS.getInfo()) .build(); } catch (Exception e) { log.error(查询用户是否完成日历签到返利失败 userId:{}, userId, e); return Response.Booleanbuilder() .code(ResponseCode.UN_ERROR.getCode()) .data(false) .info(ResponseCode.UN_ERROR.getInfo()) .build(); } }从仓储一路透传上来仓储中仍然采用自己查自己的方式只填充想查询的字段即可Override public ListBehaviorRebateOrderEntity queryOrderByOutBusinessNo(String userId, String outBusinessNo) { //1.请求对象 UserBehaviorRebateOrder userBehaviorRebateOrderReq new UserBehaviorRebateOrder(); userBehaviorRebateOrderReq.setUserId(userId); userBehaviorRebateOrderReq.setOutBusinessNo(outBusinessNo); ListUserBehaviorRebateOrder userBehaviorRebateOrders userBehaviorRebateOrderDao.queryOrderByOutBusinessNo(userBehaviorRebateOrderReq); ListBehaviorRebateOrderEntity behaviorRebateOrderEntities new ArrayList(userBehaviorRebateOrders.size()); for (UserBehaviorRebateOrder userBehaviorRebateOrder : userBehaviorRebateOrders) { BehaviorRebateOrderEntity behaviorRebateOrderEntity BehaviorRebateOrderEntity.builder() .userId(userBehaviorRebateOrder.getUserId()) .orderId(userBehaviorRebateOrder.getOrderId()) .behaviorType(userBehaviorRebateOrder.getBehaviorType()) .rebateDesc(userBehaviorRebateOrder.getRebateDesc()) .rebateType(userBehaviorRebateOrder.getRebateType()) .rebateConfig(userBehaviorRebateOrder.getRebateConfig()) .outBusinessNo(userBehaviorRebateOrder.getOutBusinessNo()) .bizId(userBehaviorRebateOrder.getBizId()) .build(); } return behaviorRebateOrderEntities; }2.用户额度查询这个接口也不难就是要把所有用户的总/日/月的次数和剩余次数传回前端RequestMapping(value query_user_activity_account, method RequestMethod.POST) Override public ResponseUserActivityAccountResponseDTO queryUserActivityAccount(UserActivityAccountRequestDTO request) { try { log.info(查询用户活动账户开始 userId:{} activityId:{}, request.getUserId(), request.getActivityId()); if (StrUtil.isBlank(request.getUserId()) || null request.getActivityId()) { throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo()); } ActivityAccountEntity activityAccountEntity raffleActivityAccountQuotaService.queryActivityAccountEntity(request.getActivityId(), request.getUserId()); UserActivityAccountResponseDTO userActivityAccountResponseDTO UserActivityAccountResponseDTO.builder() .totalCount(activityAccountEntity.getTotalCount()) .totalCountSurplus(activityAccountEntity.getTotalCountSurplus()) .dayCount(activityAccountEntity.getDayCount()) .dayCountSurplus(activityAccountEntity.getDayCountSurplus()) .monthCount(activityAccountEntity.getMonthCount()) .monthCountSurplus(activityAccountEntity.getMonthCountSurplus()) .build(); log.info(查询用户活动账户完成 userId:{} activityId:{} dto:{}, request.getUserId(), request.getActivityId(), JSON.toJSONString(userActivityAccountResponseDTO)); return Response.UserActivityAccountResponseDTObuilder() .code(ResponseCode.SUCCESS.getCode()) .data(userActivityAccountResponseDTO) .info(ResponseCode.SUCCESS.getInfo()) .build(); } catch (Exception e) { log.error(查询用户活动账户失败 userId:{} activityId:{}, request.getUserId(), request.getActivityId(), e); return Response.UserActivityAccountResponseDTObuilder() .code(ResponseCode.UN_ERROR.getCode()) .info(ResponseCode.UN_ERROR.getInfo()) .build(); } }3.抽奖权重查询回顾一下权重的概念权重是按照用户的积分来指定奖池而我们的系统中暂时没有增加积分系统所以暂时利用已使用的额度来表示积分后续会用额度映射积分。//3.找出最小符合的值 Integer userScore repository.queryActivityAccountTotalUserCount(userId, strategyId);Getter Builder AllArgsConstructor NoArgsConstructor public class RuleWeightVO { private String ruleValue; private Integer weight; private ListInteger awardIds; private ListAward awardList; Getter Builder AllArgsConstructor NoArgsConstructor public static class Award { private Integer awardId; private String awardTitle; } }RequestMapping(value query_raffle_strategy_rule_weight, method RequestMethod.POST) Override public ResponseListRaffleStrategyRuleWeightResponseDTO queryRaffleStrategyRuleWeight(RaffleStrategyRuleWeightRequestDTO requestDTO) { try { log.info(查询抽奖策略权重规则配置开始 userId:{} activity:{}, requestDTO.getUserId(), requestDTO.getActivityId()); //1.参数校验 if (StringUtils.isBlank(requestDTO.getUserId()) || null requestDTO.getActivityId()) { throw new AppException(ResponseCode.ILLEGAL_PARAMETER.getCode(), ResponseCode.ILLEGAL_PARAMETER.getInfo()); } //2.查询用户抽奖总次数 Integer userActivityAccountTotalUseCount raffleActivityAccountQuotaService.queryRaffleActivityAccountPartakeCount(requestDTO.getActivityId(), requestDTO.getUserId()); // 3. 查询规则 ListRaffleStrategyRuleWeightResponseDTO raffleStrategyRuleWeightList new ArrayList(); ListRuleWeightVO ruleWeightVOList raffleRule.queryAwardRuleWeightByActivityId(requestDTO.getActivityId()); for (RuleWeightVO ruleWeightVO : ruleWeightVOList) { //转换对象 ListRaffleStrategyRuleWeightResponseDTO.StrategyAward strategyAwards new ArrayList(); ListRuleWeightVO.Award awardList ruleWeightVO.getAwardList(); for (RuleWeightVO.Award award : awardList) { RaffleStrategyRuleWeightResponseDTO.StrategyAward strategyAward new RaffleStrategyRuleWeightResponseDTO.StrategyAward(); strategyAward.setAwardId(award.getAwardId()); strategyAward.setAwardTitle(award.getAwardTitle()); strategyAwards.add(strategyAward); } //封装对象 RaffleStrategyRuleWeightResponseDTO raffleStrategyRuleWeightResponseDTO new RaffleStrategyRuleWeightResponseDTO(); raffleStrategyRuleWeightResponseDTO.setRuleWeightCount(ruleWeightVO.getWeight()); raffleStrategyRuleWeightResponseDTO.setStrategyAwards(strategyAwards); raffleStrategyRuleWeightResponseDTO.setUserActivityAccountTotalUseCount(userActivityAccountTotalUseCount); raffleStrategyRuleWeightList.add(raffleStrategyRuleWeightResponseDTO); } ResponseListRaffleStrategyRuleWeightResponseDTO response Response.ListRaffleStrategyRuleWeightResponseDTObuilder() .code(ResponseCode.SUCCESS.getCode()) .info(ResponseCode.SUCCESS.getInfo()) .data(raffleStrategyRuleWeightList) .build(); log.info(查询抽奖策略权重规则配置完成 userId:{} activity:{}, requestDTO.getUserId(), requestDTO.getActivityId()); return response; } catch (Exception e) { log.info(查询抽奖策略权重规则配置失败 userId:{} activity:{}, requestDTO.getUserId(), requestDTO.getActivityId(), e); return Response.ListRaffleStrategyRuleWeightResponseDTObuilder() .code(ResponseCode.UN_ERROR.getCode()) .info(ResponseCode.UN_ERROR.getInfo()) .build(); } }三、前后端联调注意调一下前端的方法名有个名字写错了。具体修改点可以直接问AI。四、bug修复1.日期static实体类的充血方法和格式化属性都要加上static因为所有对象应该共享同一个当前时间。Month同理。Data Builder NoArgsConstructor AllArgsConstructor public class RaffleActivityAccountDay { private final static SimpleDateFormat dateFormatDay new SimpleDateFormat(yyyy-MM-dd); /** 自增ID */ private String id; /** 用户ID */ private String userId; /** 活动ID */ private Long activityId; /** 日期yyyy-mm-dd */ private String day; /** 日次数 */ private Integer dayCount; /** 日次数-剩余 */ private Integer dayCountSurplus; /** 创建时间 */ private Date createTime; /** 更新时间 */ private Date updateTime; public static String currentDay() { return dateFormatDay.format(new Date()); } }这里也要改否则返利的额度无法入账。2.MQ死循环对于唯一索引冲突不再报错直接return跳过Slf4j Component public class RebateMessageCustomer { Value(${spring.rabbitmq.topic.send_rebate}) private String topic; Resource private IRaffleActivityAccountQuotaService raffleActivityAccountQuotaService; RabbitListener(queuesToDeclare Queue(value ${spring.rabbitmq.topic.send_rebate})) public void listener(String message) { try { log.info(监听用户行为返利消息 topic:{} message:{}, topic, message); //1.转换消息 BaseEvent.EventMessageSendRebateMessageEvent.SendRebateMessage eventMessage JSON.parseObject(message, new TypeReferenceBaseEvent.EventMessageSendRebateMessageEvent.SendRebateMessage() { }.getType()); SendRebateMessageEvent.SendRebateMessage rebateMessage eventMessage.getData(); if (!RebateTypeVO.SKU.getCode().equals(rebateMessage.getRebateType())) { log.info(监听用户行为返利消息 - 非SKU奖励暂时不处理 topic:{} message:{}, topic, message); return; } //2.入账奖励 SkuRechargeEntity skuRechargeEntity new SkuRechargeEntity(); skuRechargeEntity.setUserId(rebateMessage.getUserId()); skuRechargeEntity.setSku(Long.valueOf(rebateMessage.getRebateConfig())); skuRechargeEntity.setOutBusinessNo(rebateMessage.getBizId()); raffleActivityAccountQuotaService.createOrder(skuRechargeEntity); }catch (AppException e) { if (ResponseCode.INDEX_DUP.getCode().equals(e.getCode())) { // 幂等命中该 bizId 已入账静默确认不重投 log.warn(返利入账幂等命中跳过); return; } log.error(监听用户行为返利消息消费失败 topic:{} message:{}, topic, message, e); throw e; // 真失败才重投 } catch (Exception e) { log.error(监听用户行为返利消息消费失败 topic:{} message:{}, topic, message, e); throw e; } } }3.无法判断用户当日是否签到前面的代码少加了一行4.权重查不到用户额度我发现只能查到userxiaofuge2的账户权重总额度。检查后发现是因为分库分表导致的其他不在这个表中的用户是查不到的根因在于Dao的分库分表方法的参数用了基本类型Long activityId和String userId。修改后如下DBRouter Integer queryRaffleActivityAccountPartakeCount(RaffleActivityAccount raffleActivityAccountReq);注意DBRouter的参数必须是对象不能是Long activityId和String userId。5.ctrlaltv失灵原因是开了QQ音乐在后台没关。。。。。。