Explorar el Código

益寿缘app-完善下发奖励接口

cgp hace 1 semana
padre
commit
001554ff0f

+ 9 - 0
fs-service/src/main/java/com/fs/app/mapper/FsAppRoleMapper.java

@@ -66,4 +66,13 @@ public interface FsAppRoleMapper extends BaseMapper<FsAppRole>{
      * @return 结果
      */
     int deleteFsAppRoleByIds(Long[] ids);
+
+    /**
+     * 根据用户ID查询其拥有的最高级别的APP角色信息
+     *
+     * @param userId 用户ID
+     * @param levelSortAsc 指定排序方式,'true'表示数字越小级别越高,'false'表示数字越大级别越高
+     * @return FsAppRole 最高级别的角色信息
+     */
+    FsAppRole selectHighestLevelAppRoleByUserId(Long userId, String levelSortAsc);
 }

+ 3 - 0
fs-service/src/main/java/com/fs/his/domain/FsUser.java

@@ -160,6 +160,9 @@ public class FsUser extends BaseEntity
     //APP角色(角色id逗号隔开)
     private String appRoles;
 
+    /** app奖励实际观看天数 */
+    private Integer appRewardsViewedDays;
+
     /** app登录后不为null(表示是否下载app) */
     private String historyApp;
     public void setNickName(String nickname)

+ 6 - 1
fs-service/src/main/java/com/fs/his/domain/FsUserRewards.java

@@ -41,7 +41,7 @@ public class FsUserRewards {
     private String orderCode;
 
     /**
-     * 产品类型: 1-药品 2-套餐包
+     * 产品类型: 1-套餐包 2-药品
      */
     private Integer productType;
 
@@ -87,6 +87,11 @@ public class FsUserRewards {
     /** 商品图片*/
     private String goodsUrl;
 
+    /**
+     * 剩余过期天数
+     * */
+    @TableField(exist = false)
+    private Long expireDays;
     /**
      * 用户领取奖品的地址id
      * */

+ 19 - 0
fs-service/src/main/java/com/fs/his/dto/ClaimRewardsAddDTO.java

@@ -0,0 +1,19 @@
+package com.fs.his.dto;
+
+import lombok.Data;
+
+@Data
+public class ClaimRewardsAddDTO {
+    /**
+     * 用户id
+     * */
+    private Long fsUserId;
+    /**
+     * 奖品id
+     * */
+    private Long rewardsId;
+    /**
+     * 收货地址id
+     * */
+    private Long addressId;
+}

+ 14 - 8
fs-service/src/main/java/com/fs/his/service/IAppUserRewardService.java

@@ -2,16 +2,30 @@ package com.fs.his.service;
 
 import com.fs.app.domain.FsAppRole;
 import com.fs.his.domain.FsUserRewards;
+import com.fs.his.dto.ClaimRewardsAddDTO;
 
 import java.util.List;
 
 public interface IAppUserRewardService {
+
+    /**
+     * 用户领取奖励
+     * @param claimRewardsAddDTO 用户ID,奖品ID,地址ID
+     * */
+    public void claimRewards(ClaimRewardsAddDTO claimRewardsAddDTO);
+
     /**
      * 为当前用户增加首次注册登录待领取的奖励
      * @param fsUserId 用户ID
      * */
     public void addUserFirstLoginRewards(Long fsUserId);
 
+    /**
+     * 为当前用户增加看课奖励待领取的奖励
+     * @param fsUserId 用户ID
+     * */
+    public void addUserWatchCourseRewards(Long fsUserId);
+
     /**
      * 获取当前用户所有的奖励
      * @param fsUserRewards 查询条件
@@ -19,14 +33,6 @@ public interface IAppUserRewardService {
      * */
     public List<FsUserRewards> getMyRewardList(FsUserRewards fsUserRewards);
 
-    /**
-     * 用户领取奖励
-     * @param fsUserId 用户ID
-     * @param rewardsId 奖品ID
-     * @param addressId 地址ID
-     * */
-    public void claimRewards(Long fsUserId,Long rewardsId,Long addressId);
-
     /**
      * 判断指定用户是否满足首次注册登录的条件
      * @param fsUserId 用户ID

+ 2 - 2
fs-service/src/main/java/com/fs/his/service/IFsPackageOrderService.java

@@ -106,8 +106,8 @@ public interface IFsPackageOrderService
 
     R addPackageOrder(FsPackageOrderAddParam fsPackageOrder);
 
-    //app首次登陆注册、看课奖励创建0元订单接口
-    R addAppPackageOrder(FsPackageOrderAddRewardsParam rewardsParam);
+    //创建App奖品套餐包订单
+    R addPackageOrderForAppRewards(FsPackageOrderAddRewardsParam rewardsParam);
 
     byte[] getWxaCodeUnLimit(Long orderId);
 

+ 268 - 103
fs-service/src/main/java/com/fs/his/service/impl/AppUserRewardServiceImpl.java

@@ -6,6 +6,7 @@ import com.fs.common.exception.CustomException;
 import com.fs.common.utils.DateUtils;
 import com.fs.his.domain.FsUser;
 import com.fs.his.domain.FsUserRewards;
+import com.fs.his.dto.ClaimRewardsAddDTO;
 import com.fs.his.enums.ActivityTypeEnum;
 import com.fs.his.mapper.FsUserMapper;
 import com.fs.his.mapper.FsUserRewardsMapper;
@@ -13,6 +14,8 @@ import com.fs.his.service.IAppUserRewardService;
 import com.fs.his.strategy.RewardResult;
 import com.fs.his.strategy.RewardStrategy;
 import com.fs.his.strategy.RewardStrategyFactory;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -21,9 +24,8 @@ import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
 import java.math.BigDecimal;
-import java.util.Collections;
-import java.util.Date;
-import java.util.List;
+import java.math.RoundingMode;
+import java.util.*;
 
 @Slf4j
 @Service
@@ -38,30 +40,59 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
     @Autowired
     private FsAppRoleMapper appRoleMapper;
 
+    @Autowired
+    private ObjectMapper objectMapper;
+
     @Autowired
     private RewardStrategyFactory strategyFactory;
 
 
+    @Override
+    public void claimRewards(ClaimRewardsAddDTO claimRewardsAddDTO) {
+        Long fsUserId=claimRewardsAddDTO.getFsUserId();
+        Long rewardsId=claimRewardsAddDTO.getRewardsId();
+        FsUserRewards reward = rewardsMapper.selectByUserIdAndRewardsId(claimRewardsAddDTO.getFsUserId(), rewardsId);
+        if (reward==null){
+            log.info("用户:{}没有奖品:{}", fsUserId, rewardsId);
+            return;
+        }
+        // 校验是否过期(目前只有看课奖品需要校验过期)
+        if (ActivityTypeEnum.WATCH_COURSE.getCode().equals(reward.getActivityType())) {
+            if (isExpired(reward)) {
+                log.info("奖品已过期: rewardsId={}", rewardsId);
+                // 更新奖品状态为已过期
+                rewardsMapper.updateStatus(reward.getId(), 2, null, null);
+                return;
+            }
+        }
+        reward.setAddressId(claimRewardsAddDTO.getAddressId());
+        //执行奖品领取逻辑
+        RewardStrategy strategy = strategyFactory.getStrategy(reward.getActivityType(), reward.getRewardType());
+        RewardResult result = strategy.process(reward);
+        //RewardResult result = new RewardResult();
+        result.setSuccess(true);
+        if (result.isSuccess()) {
+            // 更新奖品状态
+            rewardsMapper.updateStatus(rewardsId, 1, result.getOrderCode(), DateUtils.getNowDate());
+            log.info("奖品领取成功: rewardsId={}, orderCode={}", rewardsId, result.getOrderCode());
+        } else {
+            log.error("奖品领取失败: rewardsId={}, reason={}", rewardsId, result.getMessage());
+            throw new CustomException(result.getMessage());
+        }
+    }
+
     @Override
     @Transactional(rollbackFor = Exception.class)
     public void addUserFirstLoginRewards(Long fsUserId) {
         String activityType = ActivityTypeEnum.FIRST_LOGIN.getCode();
 
-        // TODO 根据配置获取奖品类型
         FsAppRole appRoleConfig = getAppRoleConfig(fsUserId);
-        if (appRoleConfig==null){
+        if (appRoleConfig == null) {
             log.info("当前用户未匹配到App奖励角色");
             return;
         }
-        Integer rewardType = appRoleConfig.getCourseRewardType();
-        Integer productType=appRoleConfig.getCourseProductType();
-        Long goodsId=appRoleConfig.getCourseProductId();
-        BigDecimal rewardAmount=appRoleConfig.getCourseRedPacket();
-        Long rewardPoints=appRoleConfig.getCourseIntegral().longValue();
-        String goodsName = appRoleConfig.getGoodsName();
-        String goodsUrl = appRoleConfig.getGoodsUrl();
         // 构建奖品记录
-        FsUserRewards reward = buildAppUserReward(fsUserId, activityType, rewardType, productType, goodsId, rewardAmount,rewardPoints,goodsName,goodsUrl);
+        FsUserRewards reward = buildAppUserReward(fsUserId, activityType, appRoleConfig);
         try {
             // 插入奖品表
             int insertResult = rewardsMapper.insertFsUserRewards(reward);
@@ -75,7 +106,7 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
 
                 if (updateResult > 0) {
                     log.info("用户首次注册奖励发放成功: userId={}, rewardType={}, rewardsId={}",
-                            fsUserId, rewardType, reward.getId());
+                            fsUserId, reward.getRewardType(), reward.getId());
                 } else {
                     log.error("用户首次登录时间更新失败: userId={}, 但奖品已发放, rewardsId={}",
                             fsUserId, reward.getId());
@@ -89,105 +120,163 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
     }
 
     @Override
-    public List<FsUserRewards> getMyRewardList(FsUserRewards fsUserRewards) {
-        List<FsUserRewards> fsUserRewardsList = rewardsMapper.selectFsUserRewardsList(fsUserRewards);
-        if (CollectionUtils.isEmpty(fsUserRewardsList)){
-            return Collections.emptyList();
+    @Transactional(rollbackFor = Exception.class)
+    public void addUserWatchCourseRewards(Long fsUserId) {
+        String activityType = ActivityTypeEnum.WATCH_COURSE.getCode();
+
+        //根据配置获取奖品类型、金额等
+        FsAppRole appRoleConfig = getAppRoleConfig(fsUserId);
+        if (appRoleConfig == null) {
+            log.info("当前用户未匹配到App奖励角色");
+            return;
+        }
+
+        // 构建奖品记录 - 把appRoleConfig传进去,让build方法自己处理
+        FsUserRewards reward = buildAppUserReward(fsUserId, activityType, appRoleConfig);
+
+        // 插入奖品表
+        int insertResult = rewardsMapper.insertFsUserRewards(reward);
+        if (insertResult > 0) {
+            log.info("用户看课奖励发放成功: userId={}, rewardType={}, rewardsId={}",
+                    fsUserId, reward.getRewardType(), reward.getId());
+        } else {
+            log.error("用户看课奖励发放失败: userId={}", fsUserId);
         }
-        return fsUserRewardsList;
     }
 
     /**
+     * 根据活动类型和角色配置,构建奖品记录
      * @param fsUserId 用户ID
      * @param activityType 活动类型
-     * @param rewardType 奖品类型
-     * @param productType  产品类型 1-药品 2-套餐包
-     * @param goodsId  1-红包 2-积分 3-商品
-     * @param rewardAmount 红包
-     * @param rewardPoints 积分
-     * */
-    private FsUserRewards buildAppUserReward(Long fsUserId, String activityType, Integer rewardType, Integer productType, Long goodsId,BigDecimal rewardAmount,Long rewardPoints,String goodsName,String goodsUrl) {
+     * @param appRoleConfig 角色配置
+     */
+    private FsUserRewards buildAppUserReward(Long fsUserId, String activityType, FsAppRole appRoleConfig) {
         FsUserRewards reward = new FsUserRewards();
         reward.setFsUserId(fsUserId);
         reward.setActivityType(activityType);
-        reward.setRewardType(rewardType);
         reward.setStatus(0); // 待领取
         reward.setCreateTime(DateUtils.getNowDate());
-        if (ActivityTypeEnum.FIRST_LOGIN.getCode().equals(activityType)){
-            reward.setIsFirstLogin(1); // 标记为首次注册奖励(用于虚拟列索引),首次登录奖励 必传1!!!
-        }
-        if (rewardType == 1){//红包
-            reward.setRewardAmount(rewardAmount);
-        }
-        else if (rewardType == 2){//积分
-            reward.setRewardPoints(rewardPoints);
-        }else if (rewardType == 3){//实物商品
-            reward.setProductType(productType); // 1-药品 2-套餐包
-            reward.setGoodsId(goodsId);    // 药品/套餐包id
-            reward.setGoodsName(goodsName);
-            reward.setGoodsUrl(goodsUrl);
+
+        // 根据活动类型设置不同字段
+        if (ActivityTypeEnum.FIRST_LOGIN.getCode().equals(activityType)) {
+            // 首次登录奖励
+            reward.setIsFirstLogin(1); // 标记为首次注册奖励
+
+            // 首次登录用 register 相关字段
+            reward.setRewardType(appRoleConfig.getRegisterRewardType());
+
+            if (appRoleConfig.getRegisterRewardType() == 1) { // 红包
+                reward.setRewardAmount(appRoleConfig.getRegisterRedPacket());
+            } else if (appRoleConfig.getRegisterRewardType() == 2) { // 积分
+                reward.setRewardPoints(appRoleConfig.getRegisterIntegral() != null ?
+                        appRoleConfig.getRegisterIntegral().longValue() : 0L);
+            } else if (appRoleConfig.getRegisterRewardType() == 3) { // 实物商品
+                reward.setProductType(appRoleConfig.getRegisterProductType());
+                reward.setGoodsId(appRoleConfig.getRegisterProductId());
+                reward.setGoodsName(appRoleConfig.getGoodsName());
+                reward.setGoodsUrl(appRoleConfig.getGoodsUrl());
+            }
+
+        } else if (ActivityTypeEnum.WATCH_COURSE.getCode().equals(activityType)) {
+            // 看课奖励
+            reward.setRewardType(appRoleConfig.getCourseRewardType());
+
+            // 获取当前用户的有效看课天数
+            FsUser fsUser = fsUserMapper.selectFsUserById(fsUserId);
+            if (fsUser == null) {
+                log.info("用户不存在,用户id:{}", fsUserId);
+                return reward;
+            }
+            Integer targetDay = fsUser.getAppRewardsViewedDays() == null ? 0 : fsUser.getAppRewardsViewedDays();
+
+            // 根据奖品类型设置具体值
+            if (appRoleConfig.getCourseRewardType() == 1) { // 红包
+                BigDecimal amount = calculateRewardValue(appRoleConfig, targetDay, true);
+                reward.setRewardAmount(amount);
+            } else if (appRoleConfig.getCourseRewardType() == 2) { // 积分
+                Long points = calculateRewardValue(appRoleConfig, targetDay, false);
+                reward.setRewardPoints(points);
+            } else if (appRoleConfig.getCourseRewardType() == 3) { // 实物商品
+                reward.setProductType(appRoleConfig.getCourseProductType());
+                reward.setGoodsId(appRoleConfig.getCourseProductId());
+                reward.setGoodsName(appRoleConfig.getGoodsName());
+                reward.setGoodsUrl(appRoleConfig.getGoodsUrl());
+            }
         }
+
         return reward;
     }
 
-    //为当前用户新增看课类型的奖励
-    @Transactional(rollbackFor = Exception.class)
-    public void addUserWatchCourseRewards(Long fsUserId) {
-        String activityType = ActivityTypeEnum.WATCH_COURSE.getCode();
-        // TODO 根据配置获取奖品类型、金额等
-        FsAppRole appRoleConfig = getAppRoleConfig(fsUserId);
-        if (appRoleConfig==null){
-            log.info("当前用户未匹配到App奖励角色");
-            return;
-        }
-        Integer rewardType = appRoleConfig.getRegisterRewardType();
-        Integer productType=appRoleConfig.getRegisterProductType();
-        Long goodsId=appRoleConfig.getRegisterProductId();
-        BigDecimal rewardAmount=appRoleConfig.getRegisterRedPacket();
-        Long rewardPoints=appRoleConfig.getRegisterIntegral().longValue();
-        String goodsName = appRoleConfig.getGoodsName();
-        String goodsUrl = appRoleConfig.getGoodsUrl();
-        // 构建奖品记录
-        FsUserRewards reward = buildAppUserReward(fsUserId, activityType, rewardType, productType, goodsId, rewardAmount,rewardPoints,goodsName,goodsUrl);
-        // 插入奖品表
-        int insertResult = rewardsMapper.insertFsUserRewards(reward);
-        if (insertResult > 0) {
-            log.info("用户看课奖励发放成功: userId={}, rewardType={}, rewardsId={}",
-                    fsUserId, rewardType, reward.getId());
+    /**
+     * 计算奖励值(红包或积分)
+     * @param appRoleConfig 角色配置
+     * @param targetDay 目标天数
+     * @param isRedPackage true-红包,false-积分
+     */
+    private <T extends Number> T calculateRewardValue(FsAppRole appRoleConfig, Integer targetDay, boolean isRedPackage) {
+        if (appRoleConfig.getCourseRewardRuleType() == 2) {
+            // 定制规则
+            String courseRewardRule = appRoleConfig.getCourseRewardRule();
+            if (courseRewardRule == null || courseRewardRule.isEmpty()) {
+                return isRedPackage ? (T) BigDecimal.ZERO : (T) Long.valueOf(0L);
+            }
+
+            try {
+                List<Map<String, Object>> rewardList = objectMapper.readValue(
+                        courseRewardRule,
+                        new TypeReference<List<Map<String, Object>>>() {}
+                );
+
+                for (Map<String, Object> item : rewardList) {
+                    Integer day = ((Number) item.get("day")).intValue();
+                    if (day.equals(targetDay)) {
+                        if (isRedPackage) {
+                            return (T) new BigDecimal(item.get("amount").toString());
+                        } else {
+                            return (T) Long.valueOf(((Number) item.get("amount")).longValue());
+                        }
+                    }
+                }
+                return isRedPackage ? (T) BigDecimal.ZERO : (T) Long.valueOf(0L);
+
+            } catch (Exception e) {
+                log.error("解析规则失败: {}", courseRewardRule, e);
+                return isRedPackage ? (T) BigDecimal.ZERO : (T) Long.valueOf(0L);
+            }
         } else {
-            log.error("用户看课奖励发放失败: userId={}, 但奖品已发放, rewardsId={}",
-                    fsUserId, reward.getId());
+            // 平均规则
+            if (isRedPackage) {
+                BigDecimal total = appRoleConfig.getCourseRedPacket();
+                Long days = appRoleConfig.getCourseNeedDay();
+                if (total == null || days == null || days == 0) {
+                    return (T) BigDecimal.ZERO;
+                }
+                return (T) total.divide(BigDecimal.valueOf(days), 2, RoundingMode.HALF_UP);
+            } else {
+                Long total = appRoleConfig.getCourseIntegral() != null ?
+                        appRoleConfig.getCourseIntegral().longValue() : 0L;
+                Long days = appRoleConfig.getCourseNeedDay();
+                if (days == null || days == 0) {
+                    return (T) Long.valueOf(0L);
+                }
+                return (T) Long.valueOf(total / days);
+            }
         }
     }
 
+
     @Override
-    public void claimRewards(Long fsUserId,Long rewardsId,Long addressId) {
-        FsUserRewards reward = rewardsMapper.selectByUserIdAndRewardsId(fsUserId, rewardsId);
-        if (reward==null){
-            log.info("用户:{}没有奖品:{}", fsUserId, rewardsId);
-            return;
-        }
-        // 校验是否过期(目前只有看课奖品需要校验过期)
-        if (ActivityTypeEnum.WATCH_COURSE.getCode().equals(reward.getActivityType())) {
-            if (isExpired(reward.getCreateTime())) {
-                log.info("奖品已过期: rewardsId={}", rewardsId);
-                // 更新奖品状态为已过期
-                rewardsMapper.updateStatus(reward.getId(), 2, null, null);
-                return;
-            }
+    public List<FsUserRewards> getMyRewardList(FsUserRewards fsUserRewards) {
+        List<FsUserRewards> fsUserRewardsList = rewardsMapper.selectFsUserRewardsList(fsUserRewards);
+        if (CollectionUtils.isEmpty(fsUserRewardsList)) {
+            return Collections.emptyList();
         }
-        reward.setAddressId(addressId);
-        //执行奖品领取逻辑
-        RewardStrategy strategy = strategyFactory.getStrategy(reward.getActivityType(), reward.getRewardType());
-        RewardResult result = strategy.process(reward);
-        if (result.isSuccess()) {
-            // 更新奖品状态
-            rewardsMapper.updateStatus(rewardsId, 1, result.getOrderCode(), DateUtils.getNowDate());
-            log.info("奖品领取成功: rewardsId={}, orderCode={}", rewardsId, result.getOrderCode());
-        } else {
-            log.error("奖品领取失败: rewardsId={}, reason={}", rewardsId, result.getMessage());
-            throw new CustomException(result.getMessage());
+        // 计算每个奖品的剩余过期天数
+        for (FsUserRewards reward : fsUserRewardsList) {
+            Long expireDays = calculateExpireDays(reward);
+            reward.setExpireDays(expireDays);
         }
+        return fsUserRewardsList;
     }
 
     @Override
@@ -223,9 +312,8 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
         String activityType = ActivityTypeEnum.FIRST_LOGIN.getCode();
         FsUserRewards queryReward = rewardsMapper.selectByUserIdAndActivityType(fsUserId, activityType);
         if (queryReward == null) {
-            //TODO 查询当前用户对应的角色配置返回红包、积分、商品弹窗信息
-            fsAppRole = appRoleMapper.selectFsAppRoleAndRewardsById(1L);
-            fsAppRole.setShow(true);//弹窗显示
+            //查询当前用户对应的角色配置返回红包、积分、商品弹窗信息
+            fsAppRole = getAppRoleConfig(fsUserId);
             return fsAppRole;
         }
         return fsAppRole;
@@ -240,26 +328,103 @@ public class AppUserRewardServiceImpl implements IAppUserRewardService {
         FsUserRewards queryReward = rewardsMapper.selectByUserIdAndActivityType(fsUserId, activityType);
         if (queryReward == null) {
             //TODO 查询当前用户对应的角色配置返回红包、积分、商品弹窗信息(还需要判断当前用户是否满足看课奖励)
-            fsAppRole = appRoleMapper.selectFsAppRoleById(1L);
-            //从缓存查询当前用户看课天数对比角色要求的看课天数,达标才返回
-            fsAppRole.setShow(true);//弹窗显示
+            fsAppRole = getAppRoleConfig(fsUserId);
             return fsAppRole;
         }
         return fsAppRole;
     }
 
-    //奖品是否过期的判断方法
-    private boolean isExpired(Date createTime) {
-        // TODO 实现过期判断逻辑
-        return false;
+    /**
+     * 积分、商品是否过期的判断方法(红包需要另外方法计算)
+     * @param reward: 奖品
+     * @return true: 过期 false: 未过期
+     * */
+    private boolean isExpired(FsUserRewards reward) {
+        // 奖品产生时间
+        Date rewardTime = reward.getCreateTime();
+        // 获取当前时间
+        Date currentTime = DateUtils.getNowDate();
+        // 获取当前角色配置(奖品有效期)
+        FsAppRole fsAppRole = appRoleMapper.selectHighestLevelAppRoleByUserId(reward.getFsUserId(), "false");
+
+        // 有效天数
+        if (fsAppRole == null) {
+            log.info("未找到用户:{}对应的角色配置", reward.getFsUserId());
+            return false;
+        }
+
+        // 奖品有效期(天)
+        Long validDays = fsAppRole.getCourseDay();
+        if (validDays == null || validDays <= 0) {
+            // 如果有效期配置为0或负数,可以认为是永久有效
+            return false;
+        }
+        // 将奖品生成时间加上有效天数,得到过期时间点
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(rewardTime);
+        calendar.add(Calendar.DAY_OF_MONTH, validDays.intValue()); // 加上有效天数
+
+        // 获取加完天数后的日期,清零时分秒,只比较日期
+        Date expirationDate = DateUtils.truncate(calendar.getTime(), Calendar.DAY_OF_MONTH);
+        // 同样,将当前时间也清零时分秒进行比较
+        Date currentDate = DateUtils.truncate(currentTime, Calendar.DAY_OF_MONTH);
+
+        // 如果过期日期 < 当前日期,则表示已过期
+        return expirationDate.before(currentDate);
     }
+
     //根据用户id、角色id获取配置的奖品信息
     private FsAppRole getAppRoleConfig(Long fsUserId){
-        //TODO 获取当前用户最高权重角色id
-        FsAppRole fsAppRole = appRoleMapper.selectFsAppRoleAndRewardsById(1L);
+        //获取当前用户最高权重角色, "false"表示查询等级数字最小的权重角色
+        FsAppRole fsAppRole = appRoleMapper.selectHighestLevelAppRoleByUserId(fsUserId,"false");
         if (fsAppRole==null){
+            log.info("未找到用户:{}对应的角色配置", fsUserId);
             return null;
         }
+        fsAppRole.setShow(true);//弹窗显示
         return fsAppRole;
     }
+    /**
+     * 计算单个奖品的剩余过期天数
+     * @param reward 奖品实体
+     * @return 剩余天数。null: 表示无有效期或查询失败;0: 今天过期或已过期;正数: 剩余天数
+     */
+    private Long calculateExpireDays(FsUserRewards reward) {
+        // 1. 获取奖品创建时间
+        Date rewardCreateTime = reward.getCreateTime();
+        if (rewardCreateTime == null) {
+            return null; // 如果创建时间为空,无法计算
+        }
+
+        // 2. 获取用户的角色配置,以确定有效期
+        FsAppRole fsAppRole = appRoleMapper.selectHighestLevelAppRoleByUserId(reward.getFsUserId(), "false");
+        if (fsAppRole == null) {
+            // 如果找不到角色配置,可以视为永不过期,返回一个很大的数,或者返回null
+            // 这里假设没有配置就不过期
+            return null;
+        }
+
+        Long validDays = fsAppRole.getCourseDay();
+        if (validDays == null || validDays <= 0) {
+            // 如果有效期配置为0或负数,也视为永不过期
+            return null;
+        }
+
+        // 3. 计算过期日期
+        Calendar cal = Calendar.getInstance();
+        cal.setTime(rewardCreateTime);
+        cal.add(Calendar.DAY_OF_MONTH, validDays.intValue());
+        Date expireDate = DateUtils.truncate(cal.getTime(), Calendar.DAY_OF_MONTH);
+
+        // 4. 计算剩余天数
+        Date now = DateUtils.truncate(new Date(), Calendar.DAY_OF_MONTH);
+
+        long diffInMillis = expireDate.getTime() - now.getTime();
+        long daysDiff = diffInMillis / (1000 * 60 * 60 * 24);
+
+        // 5. 返回结果
+        // 如果差值小于0,说明已过期,返回0
+        // 如果差值>=0,说明未过期,返回剩余天数
+        return Math.max(0L, daysDiff);
+    }
 }

+ 1 - 1
fs-service/src/main/java/com/fs/his/service/impl/FsPackageOrderServiceImpl.java

@@ -2141,7 +2141,7 @@ public class FsPackageOrderServiceImpl implements IFsPackageOrderService
 
     @Override
     @Transactional
-    public R addAppPackageOrder(FsPackageOrderAddRewardsParam param) {
+    public R addPackageOrderForAppRewards(FsPackageOrderAddRewardsParam param) {
         Long doctorId = null;
         FsPackage fsPackage=fsPackageMapper.selectFsPackageByPackageId(param.getPackageId());
         if(fsPackage.getStatus().equals(0)){

+ 0 - 2
fs-service/src/main/java/com/fs/his/strategy/impl/FirstLoginPointsStrategy.java

@@ -13,7 +13,6 @@ import org.springframework.aop.framework.AopContext;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
-import org.springframework.transaction.support.TransactionSynchronizationManager;
 
 import java.util.Date;
 
@@ -44,7 +43,6 @@ public class FirstLoginPointsStrategy  implements RewardStrategy {
     }
     @Transactional(rollbackFor = Exception.class)
     public void addUserAddressIntegral(Long userId, Long points) {
-        //log.debug("当前是否在事务中: " + TransactionSynchronizationManager.isActualTransactionActive());
         FsUser user = fsUserMapper.selectFsUserById(userId);
         if (user!=null){
             //获取首次注册类型的积分记录,没有再添加积分

+ 15 - 1
fs-service/src/main/java/com/fs/his/strategy/impl/FirstLoginProductStrategy.java

@@ -10,6 +10,8 @@ import com.fs.his.param.FsPackageOrderAddRewardsParam;
 import com.fs.his.service.IFsPackageOrderService;
 import com.fs.his.strategy.RewardResult;
 import com.fs.his.strategy.RewardStrategy;
+import com.fs.hisStore.param.FsStoreOrderCreateParamAppReward;
+import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
@@ -23,6 +25,9 @@ public class FirstLoginProductStrategy implements RewardStrategy {
     
     @Autowired
     private IFsPackageOrderService packageOrderService;
+
+    @Autowired
+    private IFsStoreOrderScrmService storeOrderScrmService;
     @Override
     public RewardResult process(FsUserRewards reward) {
         log.info("处理首次登录商品发放: rewardId={}", reward.getId());
@@ -36,7 +41,16 @@ public class FirstLoginProductStrategy implements RewardStrategy {
             }
             // 调用创建0元订单接口
             FsPackageOrderAddRewardsParam param = buildOrderCreateParam(reward, fsUser);
-            R result = packageOrderService.addAppPackageOrder(param);
+            //互联网医院和商城要调用不同的订单创建逻辑
+            R result;
+            if (reward.getProductType() == 1) {
+                //套餐包订单
+                result = packageOrderService.addPackageOrderForAppRewards(param);
+            } else {
+                //商城订单
+                FsStoreOrderCreateParamAppReward storeAppReward=new FsStoreOrderCreateParamAppReward();
+                result = storeOrderScrmService.createOrderScrmForAppRewards(reward.getFsUserId(),storeAppReward);
+            }
 
             // 检查订单创建是否成功
             if (!result.isSuccess()) {

+ 15 - 1
fs-service/src/main/java/com/fs/his/strategy/impl/WatchCourseProductStrategy.java

@@ -10,6 +10,8 @@ import com.fs.his.param.FsPackageOrderAddRewardsParam;
 import com.fs.his.service.IFsPackageOrderService;
 import com.fs.his.strategy.RewardResult;
 import com.fs.his.strategy.RewardStrategy;
+import com.fs.hisStore.param.FsStoreOrderCreateParamAppReward;
+import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
@@ -23,6 +25,9 @@ public class WatchCourseProductStrategy  implements RewardStrategy {
 
     @Autowired
     private IFsPackageOrderService packageOrderService;
+
+    @Autowired
+    private IFsStoreOrderScrmService storeOrderScrmService;
     @Override
     public RewardResult process(FsUserRewards reward) {
         log.info("处理看课商品发放: rewardId={}", reward.getId());
@@ -36,7 +41,16 @@ public class WatchCourseProductStrategy  implements RewardStrategy {
             }
             // 调用创建0元订单接口
             FsPackageOrderAddRewardsParam param = buildOrderCreateParam(reward, fsUser);
-            R result = packageOrderService.addAppPackageOrder(param);
+            //互联网医院和商城要调用不同的订单创建逻辑
+            R result;
+            if (reward.getProductType() == 1) {
+                //套餐包订单
+                result = packageOrderService.addPackageOrderForAppRewards(param);
+            } else {
+                //商城订单
+                FsStoreOrderCreateParamAppReward storeAppReward=new FsStoreOrderCreateParamAppReward();
+                result = storeOrderScrmService.createOrderScrmForAppRewards(reward.getFsUserId(),storeAppReward);
+            }
 
             // 检查订单创建是否成功
             if (!result.isSuccess()) {

+ 58 - 0
fs-service/src/main/java/com/fs/hisStore/param/FsStoreOrderCreateParamAppReward.java

@@ -0,0 +1,58 @@
+package com.fs.hisStore.param;
+
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotNull;
+import javax.validation.constraints.Size;
+import java.io.Serializable;
+import java.math.BigDecimal;
+
+@Data
+public class FsStoreOrderCreateParamAppReward implements Serializable {
+    private Long companyId;
+    private Long companyUserId;
+    private Long userId;
+    private String orderKey;
+    @ApiModelProperty(value = "地址ID")
+    @NotNull(message = "地址不能为空")
+    private Long addressId;
+
+    @ApiModelProperty(value = "来源")
+    private String from;
+
+    @Size(max = 200,message = "长度超过了限制")
+    @ApiModelProperty(value = "备注")
+    private String mark;
+
+    @NotBlank(message="请选择支付方式")
+    @ApiModelProperty(value = "支付方式")
+    private String payType;
+
+    @ApiModelProperty(value = "使用积分 1使用")
+    private Integer useIntegral;
+    private BigDecimal payPrice; //制单 改价 订单总价
+    private Long paymentId;
+
+    @ApiModelProperty(value = "优惠券")
+    private Long couponUserId;
+
+    private Long tuiUserId;
+
+    private String createOrderKey;
+
+    //订单创建类型 1普通订单 2套餐订单 3制单
+    private Integer orderCreateType;
+
+    private Long customerId;
+
+    private BigDecimal amount; //货到付款代收金额
+
+    private Integer orderType; //订单类型
+    private Integer orderMedium; //媒体来源
+    private Boolean isUserApp = true;
+    private Integer erpType;//erp类型
+    private BigDecimal costPrice;//商品成本价
+    private Long goodsId;//商品id
+}

+ 5 - 0
fs-service/src/main/java/com/fs/hisStore/service/IFsStoreOrderScrmService.java

@@ -105,6 +105,11 @@ public interface IFsStoreOrderScrmService
 
     R createOrder(long userId, FsStoreOrderCreateParam param);
 
+    /**
+     * 创建App奖品商城订单
+     * */
+    R createOrderScrmForAppRewards(long userId, FsStoreOrderCreateParamAppReward param);
+
     R createOrderByPrescribe(Long prescribeId);
 
     void cancelOrder(Long orderId);

+ 136 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java

@@ -1018,6 +1018,142 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         }
     }
 
+    @Override
+    @Transactional
+    public R createOrderScrmForAppRewards(long userId, FsStoreOrderCreateParamAppReward param) {
+        FsStoreOrderComputedParam computedParam = new FsStoreOrderComputedParam();
+        BeanUtils.copyProperties(param, computedParam);
+        //计算金额
+        FsStoreOrderComputeDTO dto = this.computedOrder(userId, computedParam);
+        //获取地址
+        FsUserAddressScrm address = userAddressMapper.selectFsUserAddressById(param.getAddressId());
+        //生成分布式唯一值
+        String orderSn = IdUtil.getSnowflake(0, 0).nextIdStr();
+        //组合数据
+        FsStoreOrderScrm storeOrder = new FsStoreOrderScrm();
+        //订单类型
+        storeOrder.setErpType(param.getErpType());
+        storeOrder.setStoreHouseCode("CK01");
+        storeOrder.setUserId(userId);
+        storeOrder.setOrderCode(orderSn);
+        storeOrder.setRealName(address.getRealName());
+        storeOrder.setUserPhone(address.getPhone());
+        storeOrder.setUserAddress(address.getProvince() + " " + address.getCity() +
+                " " + address.getDistrict() + " " + address.getDetail().trim());
+        storeOrder.setTotalPrice(dto.getTotalPrice());
+        storeOrder.setTotalPostage(dto.getPayPostage());
+
+        storeOrder.setPayPostage(dto.getPayPostage());
+        storeOrder.setDeductionPrice(dto.getDeductionPrice());
+        storeOrder.setPaid(0);
+        storeOrder.setPayType(param.getPayType());
+        storeOrder.setUseIntegral(BigDecimal.valueOf(dto.getUsedIntegral()));
+        storeOrder.setBackIntegral(BigDecimal.ZERO);
+        storeOrder.setGainIntegral(BigDecimal.ZERO);
+        storeOrder.setMark(param.getMark());
+        //成本价
+        storeOrder.setCost(param.getCostPrice());
+        storeOrder.setIsChannel(1);
+        storeOrder.setShippingType(1);
+        storeOrder.setCreateTime(new Date());
+
+        String json = configService.selectConfigByKey("store.config");
+        StoreConfig config = JSONUtil.toBean(json, StoreConfig.class);
+        if (config.getServiceFee() != null) {
+            storeOrder.setServiceFee(config.getServiceFee());
+        }
+
+        //后台制单处理
+        if (param.getPayPrice() != null && param.getPayPrice().compareTo(BigDecimal.ZERO) > 0) {
+            if (param.getPayPrice().compareTo(dto.getTotalPrice()) > 0) {
+                return R.error("改价价格不能大于商品总价");
+            }
+            storeOrder.setPayPrice(param.getPayPrice());
+        } else {
+            storeOrder.setPayPrice(dto.getPayPrice());
+        }
+        storeOrder.setStatus(1); //待发货
+        storeOrder.setOrderCreateType(param.getOrderCreateType());
+        storeOrder.setIsPrescribe(0);
+        if (storeOrder.getCustomerId() == null) {
+            storeOrder.setCustomerId(param.getCustomerId());//6.13 添加客户id
+        }
+
+        storeOrder.setOrderType(param.getOrderType());
+        storeOrder.setOrderMedium(param.getOrderMedium());
+        Integer flag = fsStoreOrderMapper.insertFsStoreOrder(storeOrder);
+        if (flag == 0) {
+            return R.error("订单创建失败");
+        }
+        //插入生成订单档期日志
+        FsOrderSopLog fsOrderSopLog = new FsOrderSopLog();
+        fsOrderSopLog.setOrderId(storeOrder.getId());
+        fsOrderSopLog.setStatus(0);
+        fsOrderSopLog.setFsUserId(storeOrder.getUserId());
+        fsOrderSopLog.setType(2);
+        fsOrderSopLog.setCreateTime(DateUtils.getNowDate());
+        fsOrderSopLogService.insertFsOrderSopLog(fsOrderSopLog);
+
+        List<FsStoreCartQueryVO> carts = new ArrayList<>();
+        FsStoreCartQueryVO vo = new FsStoreCartQueryVO();
+        vo.setProductId(param.getGoodsId());
+        vo.setCartNum(1);//数量
+        carts.add(vo);
+        //减库存加销量
+        //fsStoreProductAttrValueMapper.decProductAttrStock(productAttrValueId,1);
+        fsStoreProductMapper.decProductAttrStock(param.getGoodsId(),1);
+        //this.deStockIncSale(carts);
+        //保存OrderItem
+        List<FsStoreOrderItemScrm> listOrderItem = new ArrayList<>();
+        //保存购物车商品信息
+        FsStoreCartDTO fsStoreCartDTO = new FsStoreCartDTO();
+        fsStoreCartDTO.setProductId(vo.getProductId());
+        fsStoreCartDTO.setPrice(vo.getPrice());
+        fsStoreCartDTO.setSku(vo.getProductAttrName());
+        fsStoreCartDTO.setProductName(vo.getProductName());
+        fsStoreCartDTO.setNum(vo.getCartNum());
+        fsStoreCartDTO.setBarCode(vo.getBarCode());
+        fsStoreCartDTO.setGroupBarCode(vo.getGroupBarCode());
+        fsStoreCartDTO.setBrokerage(vo.getBrokerage());
+        fsStoreCartDTO.setBrokerageTwo(vo.getBrokerageTwo());
+        fsStoreCartDTO.setBrokerageThree(vo.getBrokerageThree());
+        if (StringUtils.isEmpty(vo.getProductAttrImage())) {
+            fsStoreCartDTO.setImage(vo.getProductImage());
+        } else {
+            fsStoreCartDTO.setImage(vo.getProductAttrImage());
+        }
+
+        FsStoreOrderItemScrm item = new FsStoreOrderItemScrm();
+        item.setOrderId(storeOrder.getId());
+        item.setOrderCode(orderSn);
+        item.setCartId(vo.getId());
+        item.setProductId(vo.getProductId());
+        item.setJsonInfo(JSONUtil.toJsonStr(fsStoreCartDTO));
+        item.setNum(vo.getCartNum());
+        item.setIsAfterSales(0);
+        fsStoreOrderItemMapper.insertFsStoreOrderItem(item);
+        listOrderItem.add(item);
+        if (listOrderItem.size() > 0) {
+            String itemJson = JSONUtil.toJsonStr(listOrderItem);
+            storeOrder.setItemJson(itemJson);
+            fsStoreOrderMapper.updateFsStoreOrder(storeOrder);
+        }
+
+        //添加记录
+        orderStatusService.create(storeOrder.getId(), OrderLogEnum.CREATE_ORDER.getValue(),
+                OrderLogEnum.CREATE_ORDER.getDesc());
+
+        //添加支付到期时间
+        Calendar calendar = Calendar.getInstance();
+        calendar.setTime(storeOrder.getCreateTime());
+        if (config.getUnPayTime() != null) {
+            calendar.add(Calendar.MINUTE, config.getUnPayTime());
+        }
+        SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
+        String payLimitTime = format.format(calendar.getTime());
+        return R.ok().put("order", storeOrder).put("payLimitTime", payLimitTime);
+    }
+
     @Override
     public R createOrderByPrescribe(Long prescribeId) {
         FsPrescribe prescribe = prescribeMapper.selectFsPrescribeByPrescribeId(prescribeId);

+ 65 - 0
fs-service/src/main/resources/mapper/app/FsAppRoleMapper.xml

@@ -91,6 +91,71 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             AND fsps.product_id = far.register_product_id
         WHERE far.id = #{id}
     </select>
+    <select id="selectHighestLevelAppRoleByUserId" parameterType="map" resultMap="FsAppRoleResult">
+        SELECT
+            r.id,
+            r.role_name,
+            r.role_type,
+            r.register_reward_type,
+            r.register_red_packet,
+            r.register_product_id,
+            r.register_product_type,
+            r.register_integral,
+            r.course_day,
+            r.course_reward_type,
+            r.course_red_packet,
+            r.course_integral,
+            r.course_product_type,
+            r.course_product_id,
+            r.course_need_day,
+            r.course_reward_rule_type,
+            r.course_reward_rule,
+            r.role_level,
+            r.role_tags,
+            r.role_package_product_ids,
+            r.role_store_product_ids,
+            r.create_time,
+            r.update_time
+        FROM
+            fs_user u
+                JOIN
+            JSON_TABLE(
+                    CASE
+                        -- 如果 app_roles 为空或为 NULL,则返回一个空数组的 JSON,避免错误
+                        WHEN u.app_roles IS NULL OR TRIM(u.app_roles) = '' THEN '[]'
+                        -- 否则,将其安全地转换为标准的 JSON 数组格式 ["id1", "id2", ...]
+                        ELSE CONCAT('["', REPLACE(u.app_roles, ',', '","'), '"]')
+                        END,
+                    '$[*]' COLUMNS ( role_id BIGINT PATH '$' )
+            ) AS jt ON jt.role_id IS NOT NULL
+                JOIN
+            fs_app_role r ON r.id = jt.role_id
+        WHERE
+            u.user_id = #{userId}
+          AND r.role_level = (
+            -- 子查询:在用户拥有的所有角色中,查找最小的(即最高级的)role_level
+            SELECT
+                CASE
+                    WHEN #{levelSortAsc} = 'true' THEN MIN(r2.role_level)
+                    ELSE MAX(r2.role_level)
+                    END
+            FROM
+                fs_user u2
+                    JOIN
+                JSON_TABLE(
+                        CASE
+                            -- 在子查询中也需要进行同样的处理
+                            WHEN u2.app_roles IS NULL OR TRIM(u2.app_roles) = '' THEN '[]'
+                            ELSE CONCAT('["', REPLACE(u2.app_roles, ',', '","'), '"]')
+                            END,
+                        '$[*]' COLUMNS ( role_id BIGINT PATH '$' )
+                ) AS jt2 ON jt2.role_id IS NOT NULL
+                    JOIN
+                fs_app_role r2 ON r2.id = jt2.role_id
+            WHERE
+                u2.user_id = u.user_id
+        )
+    </select>
 
     <insert id="insertFsAppRole" parameterType="FsAppRole" useGeneratedKeys="true" keyProperty="id">
         insert into fs_app_role

+ 6 - 1
fs-service/src/main/resources/mapper/his/FsUserMapper.xml

@@ -50,10 +50,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="qwUserId"    column="qw_user_id"    />
         <result property="firstLoginTime"    column="first_login_time"    />
         <result property="appRoles"    column="app_roles"    />
+        <result property="appRewardsViewedDays"    column="app_rewards_viewed_days"    />
     </resultMap>
 
     <sql id="selectFsUserVo">
-        select user_id,qw_ext_id,sex,is_buy,course_ma_open_id,is_push,is_add_qw,source,login_device,is_individuation_push,store_open_id,password,jpush_id, is_vip,vip_start_date,vip_end_date,vip_level,vip_status,nick_name,integral_status, avatar, phone, integral,sign_num, status, tui_user_id, tui_time, tui_user_count, ma_open_id, mp_open_id, union_id, is_del, user_code, remark, create_time, update_time, last_ip, balance,is_weixin_auth,parent_id,qw_user_id,company_id,company_user_id,first_login_time,app_roles from fs_user
+        select user_id,qw_ext_id,sex,is_buy,course_ma_open_id,is_push,is_add_qw,source,login_device,is_individuation_push,store_open_id,password,jpush_id, is_vip,vip_start_date,vip_end_date,vip_level,vip_status,nick_name,integral_status, avatar, phone, integral,sign_num, status, tui_user_id, tui_time, tui_user_count, ma_open_id, mp_open_id, union_id, is_del, user_code, remark, create_time, update_time, last_ip, balance,is_weixin_auth,parent_id,qw_user_id,company_id,company_user_id,first_login_time,app_roles,app_rewards_viewed_days from fs_user
     </sql>
 
     <select id="selectFsUserList" parameterType="FsUser" resultMap="FsUserResult">
@@ -76,6 +77,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="userCode != null  and userCode != ''"> and user_code = #{userCode}</if>
             <if test="lastIp != null  and lastIp != ''"> and last_ip = #{lastIp}</if>
             <if test="balance != null "> and balance = #{balance}</if>
+            <if test="appRewardsViewedDays != null "> and app_rewards_viewed_days = #{appRewardsViewedDays}</if>
         </where>
     </select>
 
@@ -610,6 +612,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyUserId != null">company_user_id,</if>
             <if test="firstLoginTime != null">first_login_time,</if>
             <if test="appRoles != null">app_roles,</if>
+            <if test="appRewardsViewedDays != null">app_rewards_viewed_days,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="nickName != null">#{nickName},</if>
@@ -655,6 +658,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyUserId != null">#{companyUserId},</if>
             <if test="firstLoginTime != null">#{firstLoginTime},</if>
             <if test="appRoles != null">#{appRoles},</if>
+            <if test="appRewardsViewedDays != null">#{appRewardsViewedDays},</if>
          </trim>
     </insert>
 
@@ -706,6 +710,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
             <if test="firstLoginTime != null">first_login_time = #{firstLoginTime},</if>
             <if test="appRoles != null">app_roles = #{appRoles},</if>
+            <if test="appRewardsViewedDays != null">app_rewards_viewed_days = #{appRewardsViewedDays},</if>
         </trim>
         where user_id = #{userId}
     </update>