project in meituan 02

一个较为复杂的需求,我只负责芥末端的部分

明确需求

M端

新增【渠道来源奖励】:

  • 点击【新建来源奖励】和删除:增删交互同banner区,新增条目为【来源N】和【来源配置】
  • 每一条配置右侧展示所配置的来源参数,多条目下便于识别
  • 点击【来源配置】,拉起二级配置窗口
  • 当前最多支持配置10条
  • 删除:需要重置该来源的总领取上限和每日上限
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
{
"retainWindow": {
.....
},
"sourceConfigList" : [//来源配置
{
"id":"xxxxxx",//服务端生成,编辑的时候前端带过来
"crowdConfig" : {
"crowdType" : 0, //人群类型 0:"不限制", 3:"按人群限制"
"crowdIds" : [111,222,333] //crmid list
},
"totalLimit" : 10, //每人总领取上限,-1代表不限制次数
"dailyLimit" : 2, //每日次数限制,不可填-1
"userSource" : "aaa", //链接来源参数,最长30字符
"rewardNumType" : 1,//奖励数值模型
"lotteryId" : 111, //发放奖励
"imgUrl" : "1.gif" //上传图片支持gif,尺寸[w590,h790]
}
]
}

C端

M端开发

整体流程与之前的需求修改的地方差别不大

注意点

这次遇到的问题明显比上次多了不少,总结一下遇到的几个需要注意的地方

测试问题
  • 使用 JsonUtil.deserializeObject并传入Json字符串来构造对象,并且要非常注意其中的每一项都要与对象中的字段一一对应
  • 注意静态方法不能在方法外使用@Resource来使用,而是直接通过类名来调用
  • 注意UT类必须继承BaseTest类
校验重复问题
  • 相较于使用循环,更好的办法是使用哈希表并传入所有对象,然后再判断哈希表的大小与列表是否相等
  • 但是使用哈希表(在不重写equals和hashcode方法的情况下)出现了对象内容完全相同,却无法判断出相等的结果的问题,存在问题,后面经过提示发现原来只要判断其中一个字段的重复性。所以以后必须先理解清楚需求!!
字段设置问题
  • 由于本次需求需要使用一个列表的字段,我一开始自然而然地跟前一个需求一样,使用一个对象来表示,然后再在这个对象中储存一个列表。这样其实会用到两个类型的对象,其实是不必要的,实际上可以直接使用List<ObjectName>来表示。

C端开发

更改的地方:

  • 在 client包和common包下的pom中将 grocerymkthd-game-center-common 修改版本为SNAPSHOT,在总的pom下将 interactcommon.activity.version修改版本为SNAPSHOT

  • 增加一个 GameLoginRewardDTO,用于表示C端的字段,其中有String toastString imgUrl两个信息,使用 @ThriftStruct并加入相应方法

  • MainSceneResp中加入这个 GameLoginRewardDTO loginReward字段,并加入相应 gettersetter

  • MainSceneReq加入一个loginSource的String字段

  • UserRewardService下增加一个 buildLoginReward的方法。这里是整个需求逻辑的关键和核心!

    • 入参分析:

      • MainSceneReq:继承了 BaseRequest(包含userId,poi,channel),自身包含幂等ID idempotent,loginSource(新加)
      • MainSceneResponse:继承了 BaseResponse(包含code,message),自身包含userAssets, activityId…. retainPopDisplay等等配置内容,另外新增loginReward
      • ActivityDTO: 包含activityId, commonConfig,strategyConfigList, bizConfig, activityStatus, featureMap, version
      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
      100
      101
      102
      103
      104
      105
      106
      107
      108
      109
      110
      111
      112
      113
      114
      115
      116
      117
      118
      119
      120
      121
      122
      123
      124
      125
      126
      127
      128
      129
      130
      131
      132
      133
      134
      135
      /**
      * 登陆奖励
      *
      * @param request
      * @param resp
      * @param activityDto
      */
      public void buildLoginReward(MainSceneReq request, MainSceneResp resp, ActivityDTO activityDto) {
      if (StringUtils.isBlank(request.getLoginSource()) || mccConfig.isDegradeLoginAward()) {
      //无登陆来源标志,属于未更新的状态
      return;
      }
      GameCenterExtraConfigDTO extraConfigDto = GsonUtil.parseObject(activityDto.getBizConfig(), GameCenterExtraConfigDTO.class);
      if (CollectionUtils.isEmpty(extraConfigDto.getSourceConfigList())) {
      //未配置登陆奖励信息
      return;
      }
      //过滤出来源配置相同的sourceConfig
      SourceConfigDTO sourceConfigDTO = extraConfigDto.getSourceConfigList().stream()
      .filter(sourceConfig -> request.getLoginSource().equals(sourceConfig.getUserSource()))
      .findAny().orElse(null);
      if (null == sourceConfigDTO) {
      //未匹配到对应的source配置信息
      return;
      }
      if (!crowdCheck(sourceConfigDTO.getCrowdConfig(), request.getUserId())) {
      //人群不匹配
      log.info("登陆奖励匹配失败,人群ID不符,userId:{}", request.getUserId());
      return;
      }
      resp.setLoginReward(buildLoginReward(request, sourceConfigDTO));
      }

      /** TODO:
      * 1. 库存校验,总库存与日库存
      * 2. toast构建
      * 3. 用户奖励发放
      * 4. 消耗库存,总库存与日库存
      */
      private GameLoginRewardDTO buildLoginReward(MainSceneReq request, SourceConfigDTO sourceConfigDTO) {
      GameLoginRewardDTO loginRewardDTO = new GameLoginRewardDTO();
      // toast构建
      SourceAwardToastEnum toastEnum = buildAwardStockToast(request.getUserId(), sourceConfigDTO);
      // 库存校验
      if (toastEnum != SourceAwardToastEnum.NO_LIMIT) {
      //已没有奖励
      loginRewardDTO.setToast(toastEnum.getDesc());
      return loginRewardDTO;
      }
      // 用户奖励发放
      if (!issueLoginAward(request, sourceConfigDTO)) {
      log.error("UserRewardService 登陆奖励发放失败,userId:{}", request.getUserId());
      return null;
      }
      loginRewardDTO.setImgUrl(sourceConfigDTO.getImgUrl());
      // 消耗库存
      if (userStockService.increaseSourceAwardStock(request.getUserId(), sourceConfigDTO)) {
      Cat.logMetricForCount("奖励库存扣减失败");
      log.error("UserRewardService increaseSourceAwardStock 奖励库存扣减失败,userId:{}", request.getUserId());
      }
      return loginRewardDTO;
      }

      private Boolean issueLoginAward(MainSceneReq request, SourceConfigDTO sourceConfigDTO) {
      UserLotteryQuery lotteryRequest = buildUserLotteryRequest(request, sourceConfigDTO.getLotteryId());
      UserLotteryResponse lotteryResult = lotteryGateway.userLottery(lotteryRequest);
      if (lotteryResult == null || lotteryResult.getStatusCode() != StatusCodeEnum.SUCCESS.getCode()) {
      log.error("UserRewardService.issueLoginAward error,request:{},lotteryRequest:{}", request, lotteryRequest);
      return Boolean.FALSE;
      }
      LotteryResultDTO lotteryResultDTO = lotteryResult.getLotteryResultDTOMap().get(String.valueOf(sourceConfigDTO.getLotteryId()));
      if (null == lotteryResultDTO || Boolean.FALSE.equals(lotteryResultDTO.getLucky())) {
      log.error("UserRewardService.issueLoginAward error, request:{}, lotteryRequest:{}, lotteryResult:{}", request, lotteryRequest, lotteryResult);
      return Boolean.FALSE;
      }
      CatMetricReporter.reportMetricForCount(CatMetricConstant.ISSUE_POINT_AMOUNT, lotteryResultDTO.getPrizeResultDTOS().get(0).getPrizeAmount());
      return Boolean.TRUE;
      }

      /**
      * 已消耗完「总领取次数」,展示toast1“你已领取完奖励”
      * <p>
      * 未消耗完「总领取次数」但已消耗完「每日可领取次数」,展示toast2“你今天已领取过奖励,请明天再来”
      *
      * @param userId
      * @param sourceConfigDTO
      * @return
      */
      private SourceAwardToastEnum buildAwardStockToast(Long userId, SourceConfigDTO sourceConfigDTO) {
      Integer totalLimit = sourceConfigDTO.getTotalLimit();
      Integer dailyLimit = sourceConfigDTO.getDailyLimit();
      if (totalLimit < 0 && dailyLimit < 0) {
      //不限制库存
      return SourceAwardToastEnum.NO_LIMIT;
      }
      Map<String, Integer> stockMap = userStockService.getSourceAwardStock(userId, sourceConfigDTO);
      if (totalLimit > 0) {
      Integer totalCount = stockMap.get(userStockService.genPersonTotalStockSubKey(sourceConfigDTO.getId()));
      if (totalCount >= totalLimit) {
      //已达总库存限制
      return SourceAwardToastEnum.TOTAL_OVER;
      }
      }
      if (dailyLimit > 0) {
      Integer dailyCount = stockMap.get(userStockService.genPersonTotalStockSubKey(sourceConfigDTO.getId()));
      if (dailyCount >= dailyLimit) {
      //已达日库存限制
      return SourceAwardToastEnum.DAILY_OVER;
      }
      }
      return SourceAwardToastEnum.NO_LIMIT;
      }

      private Boolean crowdCheck(CrowdConfigDTO crowdConfig, Long userId) {
      if (null == crowdConfig.getCrowdType()) {
      return Boolean.FALSE;
      } else if (CrowdTypeEnum.NO_LIMIT.getValue() == crowdConfig.getCrowdType()) {
      return Boolean.TRUE;
      } else if (CrowdTypeEnum.CROWD_USER.getValue() == crowdConfig.getCrowdType()) {
      if (CollectionUtils.isEmpty(crowdConfig.getCrowdIds())) {
      return Boolean.FALSE;
      }
      //人群类型为Crowd_User,需要按id匹配
      Map<Long, Boolean> crowdIdMap = crowdGateway.checkUserBelongToCrowdIds(userId, crowdConfig.getCrowdIds());
      if (MapUtils.isEmpty(crowdIdMap)) {
      return Boolean.TRUE;
      }
      for (Map.Entry<Long, Boolean> entry : crowdIdMap.entrySet()) {
      if (Boolean.TRUE.equals(entry.getValue())) {
      return Boolean.TRUE;
      }
      }
      }
      return Boolean.FALSE;
      }
  • 涉及到的要更改/新增的其它类:

    • 在core.enums下建立一个枚举类 SourceAwardToastEnum用于表示游戏中心人群类型

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      public enum SourceAwardToastEnum{	
      //可以发放奖励
      NO_LIMIT(0,"无限制"),

      //已消耗完【总领取次数】
      TOTAL_OVER(1,"你已领取完奖励"),

      //未消耗完【总领取次数】,但已消耗完【每日领取次数】
      DAILY_OVER(2,"你今天已领取过奖励,请明天再来"),
      ;

      private int code;

      private String desc;

      SourceAwardToastEnum(int code, String desc){
      this.code = code;
      this.desc = desc;
      }

      @ThriftEnumValue
      public int getCode(){return code;}

      public String getDesc(){return desc;}
      }
    • 新增一个UserStockService类,注入CellarGateway,

  • GameCenterLocalService的“合并奖励结果”部分加入一条

    userRewardService.buildLoginReward(req, resp, activityDto);

语法学习记录

  • java8使用stream().filter()来过滤list对象(查找符合条件的对象集合)

    1
    2
    3
    4
    //匹配来源配置相同的!因为来源配置保证不重复,因此只会取出一个对象
    SourceConfigDTO sourceConfigDTO = extraConfigDto.getSourceConfigList().stream()
    .filter(sourceConfig -> request.getLoginSource().equals(sourceConfig.getUserSource()))
    .findAny().orElse(null);
  • 版权声明: 本博客所有文章除特别声明外,著作权归作者所有。转载请注明出处!
  • Copyrights © 2022 ZHU
  • 访问人数: | 浏览次数:

请我喝杯咖啡吧~

支付宝
微信