Browse Source

app首次登录领取积分和app首次登录填写获取奖励的收获地址

xw 2 ngày trước cách đây
mục cha
commit
ed2af7b644

+ 12 - 0
fs-company/src/main/java/com/fs/company/controller/qw/QwExternalContactController.java

@@ -614,6 +614,18 @@ public class QwExternalContactController extends BaseController
         return qwExternalContactService.updateQwExternalContactBindUserId(qwExternalContact);
     }
 
+    /**
+     * 修改绑定会员的「首次登录奖励地址」(fs_user.first_login_reward_address)
+     */
+    @PreAuthorize("@ss.hasPermi('qw:externalContact:edit') or @ss.hasPermi('qw:externalContact:deptEdit') or @ss.hasPermi('qw:externalContact:myEdit')")
+    @Log(title = "企微客户-首次登录奖励地址", businessType = BusinessType.UPDATE)
+    @PutMapping("/firstLoginRewardAddress")
+    public R updateFirstLoginRewardAddress(@RequestBody QwExternalContactFirstLoginRewardAddressParam param)
+    {
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        return qwExternalContactService.updateBoundFsUserFirstLoginRewardAddress(param, loginUser.getCompany().getCompanyId());
+    }
+
     /**
      * 解除绑定小程序用户
      */

+ 2 - 0
fs-service/src/main/java/com/fs/his/config/IntegralConfig.java

@@ -7,6 +7,8 @@ import java.io.Serializable;
 @Data
 public class IntegralConfig implements Serializable {
     private Integer integralNewTask;
+    /** 首次登录 app 发放积分 */
+    private Integer integralFirstLoginApp;
     private Integer integralRatio; //消费购买比例
     private Integer integralShare;//分享获取积分
     private Integer integralFollow;//随访获取积分

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

@@ -55,6 +55,13 @@ public class FsUser extends BaseEntity
     @Excel(name = "用户积分")
     private Long integral;
 
+    /** 首次登录 app 时间(用于判断是否可发放首次登录积分奖励) */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date firstLoginAppTime;
+
+    /** 首次登录可领取奖励的地址(由用户在 app 填写) */
+    private String firstLoginRewardAddress;
+
     /** 1为正常,0为禁止 */
     @Excel(name = "1为正常,0为禁止")
     private Integer status;

+ 1 - 0
fs-service/src/main/java/com/fs/his/enums/FsUserIntegralLogTypeEnum.java

@@ -35,6 +35,7 @@ public enum FsUserIntegralLogTypeEnum {
     TYPE_25(25, "直播完课积分"),
     TYPE_26(26, "直播红包积分"),
     TYPE_27(27, "积分订单取消退回积分"),
+    TYPE_28(28, "首次登录 app 获得积分"),
     ;
 
 

+ 10 - 0
fs-service/src/main/java/com/fs/his/service/IFsUserNewTaskService.java

@@ -66,4 +66,14 @@ public interface IFsUserNewTaskService
     void performTaskTwo(Long userId);
 
     void performTaskThree(Long userId,Long integral,Long orderId);
+
+    /**
+     * 判断用户是否首次登录 app:
+     * - 若 `fs_user.first_login_app_time` 为空,则写入时间并按配置发放积分
+     * - 若不为空,则不发放
+     *
+     * @param userId 用户ID
+     * @return 1 表示刚完成首次登录发放,0 表示已处理或不满足
+     */
+    int performFirstLoginApp(Long userId);
 }

+ 55 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsUserNewTaskServiceImpl.java

@@ -217,4 +217,59 @@ public class FsUserNewTaskServiceImpl implements IFsUserNewTaskService
             }
         }
     }
+
+    /**
+     * app 首次登录积分奖励发放
+     * 通过 fs_user.first_login_app_time 判断是否首次;并按配置 his.integral.integralFirstLoginApp 发放积分。
+     */
+    @Override
+    @Transactional
+    public int performFirstLoginApp(Long userId) {
+        FsUser fsUser = fsUserMapper.selectFsUserByIdForUpdate(userId);
+        if (fsUser == null) {
+            return 0;
+        }
+        if (fsUser.getFirstLoginAppTime() != null) {
+            return 0;
+        }
+
+        Date now = new Date();
+        Integer awardPoints = null;
+        try {
+            String json = configService.selectConfigByKey("his.integral");
+            if (json != null) {
+                IntegralConfig config = JSONUtil.toBean(json, IntegralConfig.class);
+                awardPoints = config.getIntegralFirstLoginApp();
+            }
+        } catch (Exception ignore) {
+            // 配置解析失败时不阻断登录,只写入首次登录时间
+        }
+
+        long currentIntegral = fsUser.getIntegral() != null ? fsUser.getIntegral() : 0L;
+        long newBalance = currentIntegral;
+        if (awardPoints != null && awardPoints > 0) {
+            newBalance = currentIntegral + awardPoints.longValue();
+        }
+
+        FsUser update = new FsUser();
+        update.setUserId(userId);
+        update.setFirstLoginAppTime(now);
+        if (awardPoints != null && awardPoints > 0) {
+            update.setIntegral(newBalance);
+        }
+        fsUserMapper.updateFsUser(update);
+
+        if (awardPoints != null && awardPoints > 0) {
+            FsUserIntegralLogs integralLogs = new FsUserIntegralLogs();
+            integralLogs.setUserId(userId);
+            integralLogs.setIntegral(awardPoints.longValue());
+            integralLogs.setBalance(newBalance);
+            integralLogs.setLogType(FsUserIntegralLogTypeEnum.TYPE_28.getValue());
+            integralLogs.setBusinessId(userId.toString());
+            integralLogs.setCreateTime(new Date());
+            fsUserIntegralLogsMapper.insertFsUserIntegralLogs(integralLogs);
+        }
+
+        return 1;
+    }
 }

+ 9 - 1
fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java

@@ -65,6 +65,12 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
             "\tid = #{id}")
     public QwExternalContactFsUserVO selectQwExternalContactByIdForFsUser(@Param("id") Long id);
 
+
+    @Select("SELECT qu.company_id FROM qw_external_contact ec " +
+            "LEFT JOIN qw_user qu ON ec.user_id = qu.qw_user_id AND qu.corp_id = ec.corp_id " +
+            "WHERE ec.id = #{id}")
+    Long selectCompanyIdByExternalContactIdJoinQwUser(@Param("id") Long id);
+
     @Select("select * from qw_external_contact where fs_user_id=#{id} ")
     public List<QwExternalContact> selectQwExternalContactByMiniUserId(Long id);
 
@@ -230,10 +236,12 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
     public int deleteQwExternalContactByIds(Long[] ids);
 
     @Select({"<script> " +
-            "select ec.*,qu.qw_user_name,qd.dept_name as departmentName,cw.name way_name,wg.group_name way_group_name from qw_external_contact ec " +
+            "select ec.*,qu.qw_user_name,qd.dept_name as departmentName,cw.name way_name,wg.group_name way_group_name," +
+            "fu.first_login_app_time as firstLoginAppTime,fu.first_login_reward_address as firstLoginRewardAddress from qw_external_contact ec " +
             "left join qw_user qu on ec.user_id=qu.qw_user_id and qu.corp_id=ec.corp_id " +
             "left join qw_dept qd on qd.dept_id=qu.department and qd.corp_id=qu.corp_id " +
             "left join company_user cu on ec.company_user_id=cu.user_id " +
+            "left join fs_user fu on fu.user_id = ec.fs_user_id " +
             "left join qw_contact_way cw on cw.id = ec.way_id " +
             "left join qw_contact_way_group wg on wg.id=cw.group_id" +
             "<where>  \n" +

+ 18 - 0
fs-service/src/main/java/com/fs/qw/param/QwExternalContactFirstLoginRewardAddressParam.java

@@ -0,0 +1,18 @@
+package com.fs.qw.param;
+
+import lombok.Data;
+
+/**
+ * 维护企微客户所绑定会员的「首次登录奖励地址」
+ */
+@Data
+public class QwExternalContactFirstLoginRewardAddressParam {
+
+    /** 企微客户主键 qw_external_contact.id */
+    private Long id;
+
+    /**
+     * 首次登录可领取奖励地址;传空字符串表示清空
+     */
+    private String firstLoginRewardAddress;
+}

+ 6 - 0
fs-service/src/main/java/com/fs/qw/service/IQwExternalContactService.java

@@ -158,6 +158,12 @@ public interface IQwExternalContactService extends IService<QwExternalContact> {
     R setCustomerCourseSopList(QwFsCustomerCourseSopListParam param);
 
     R updateQwExternalContactBindUserId(QwExternalContact qwExternalContact);
+
+    /**
+     * 修改当前企微客户所绑定小程序会员的首次登录奖励地址(fs_user.first_login_reward_address)
+     */
+    R updateBoundFsUserFirstLoginRewardAddress(QwExternalContactFirstLoginRewardAddressParam param, Long companyId);
+
     R updateQwExternalContactUnBindUserId(Long id);
     QwExternalContact getQwExternalContactDetails(QwExternalContactHParam contactHParam);
 

+ 36 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwExternalContactServiceImpl.java

@@ -4965,6 +4965,42 @@ public class QwExternalContactServiceImpl extends ServiceImpl<QwExternalContactM
         return R.ok();
     }
 
+    @Override
+    public R updateBoundFsUserFirstLoginRewardAddress(QwExternalContactFirstLoginRewardAddressParam param, Long companyId) {
+        if (param == null || param.getId() == null) {
+            return R.error("客户id不能为空");
+        }
+        if (companyId == null) {
+            return R.error("公司信息异常");
+        }
+        QwExternalContact ec = qwExternalContactMapper.selectQwExternalContactById(param.getId());
+        if (ec == null) {
+            return R.error("客户不存在");
+        }
+        Long rowCompanyId = ec.getCompanyId();
+        if (rowCompanyId == null) {
+            rowCompanyId = qwExternalContactMapper.selectCompanyIdByExternalContactIdJoinQwUser(param.getId());
+        }
+        if (!Objects.equals(rowCompanyId, companyId)) {
+            return R.error("无权限操作该客户");
+        }
+        if (ec.getFsUserId() == null) {
+            return R.error("该客户未绑定小程序会员,无法维护奖励地址");
+        }
+        String address = param.getFirstLoginRewardAddress() != null ? param.getFirstLoginRewardAddress().trim() : "";
+        if (address.length() > 2000) {
+            return R.error("地址长度不能超过2000字");
+        }
+        if (fsUserMapper.selectFsUserByUserId(ec.getFsUserId()) == null) {
+            return R.error("会员不存在");
+        }
+        FsUser patch = new FsUser();
+        patch.setUserId(ec.getFsUserId());
+        patch.setFirstLoginRewardAddress(address);
+        fsUserMapper.updateFsUser(patch);
+        return R.ok();
+    }
+
     @Override
     public R updateQwExternalContactUnBindUserId(Long id) {
 

+ 8 - 0
fs-service/src/main/java/com/fs/qw/vo/QwExternalContactVO.java

@@ -122,6 +122,14 @@ public class QwExternalContactVO {
     private String stageStatus;
     private String customerName;
     private Long fsUserId;
+
+    /** 关联小程序用户首次登录 app 时间 */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private Date firstLoginAppTime;
+
+    /** 关联小程序用户首次登录可领取奖励地址 */
+    private String firstLoginRewardAddress;
+
     @Excel(name = "客户等级")
     private Long level;
     //下单次数

+ 4 - 0
fs-service/src/main/resources/mapper/his/FsUserMapper.xml

@@ -10,6 +10,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="avatar"    column="avatar"    />
         <result property="phone"    column="phone"    />
         <result property="integral"    column="integral"    />
+        <result property="firstLoginAppTime"    column="first_login_app_time"    />
+        <result property="firstLoginRewardAddress"    column="first_login_reward_address"    />
         <result property="status"    column="status"    />
         <result property="tuiUserId"    column="tui_user_id"    />
         <result property="tuiTime"    column="tui_time"    />
@@ -679,6 +681,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="avatar != null">avatar = #{avatar},</if>
             <if test="phone != null">phone = #{phone},</if>
             <if test="integral != null">integral = #{integral},</if>
+            <if test="firstLoginAppTime != null">first_login_app_time = #{firstLoginAppTime},</if>
+            <if test="firstLoginRewardAddress != null">first_login_reward_address = #{firstLoginRewardAddress},</if>
             <if test="signNum != null">sign_num = #{signNum},</if>
             <if test="status != null">status = #{status},</if>
             <if test="tuiUserId != null">tui_user_id = #{tuiUserId},</if>

+ 38 - 0
fs-user-app/src/main/java/com/fs/app/controller/AppLoginController.java

@@ -97,6 +97,8 @@ public class AppLoginController extends AppBaseController{
             user.setPassword(Md5Utils.hash(param.getPassword()));
             user.setUpdateTime(new DateTime());
             userService.updateFsUser(user);
+            // 注册也视为首次使用 app:补写首次登录时间并发放奖励(如符合条件)
+            userNewTaskService.performFirstLoginApp(fsUser.getUserId());
             return R.ok("注册成功");
         } else {
             // 创建新用户
@@ -108,6 +110,8 @@ public class AppLoginController extends AppBaseController{
             user.setCreateTime(new Date());
 
             if (userService.insertFsUser(user) > 0) {
+                // 注册也视为首次使用 app:补写首次登录时间并发放奖励(如符合条件)
+                userNewTaskService.performFirstLoginApp(user.getUserId());
                 return R.ok("注册成功");
             } else {
                 return R.error("注册失败");
@@ -200,6 +204,8 @@ public class AppLoginController extends AppBaseController{
         user.setPassword(Md5Utils.hash(password));
         user.setCreateTime(new Date());
         if (userService.insertFsUser(user) > 0) {
+            // 注册也视为首次使用 app:补写首次登录时间并发放奖励(如符合条件)
+            userNewTaskService.performFirstLoginApp(user.getUserId());
             return R.ok("注册成功");
         } else {
             return R.error("注册失败");
@@ -297,6 +303,8 @@ public class AppLoginController extends AppBaseController{
                     user.setJpushId(param.getJpushId());
                 }
                 userService.insertFsUser(user);
+                // 新用户第一次使用 app:补写首次登录时间并发放积分(如符合条件)
+                userNewTaskService.performFirstLoginApp(user.getUserId());
                 map.put("isNew", true);
                 map.put("unionid",unionid);
                 return R.ok(map);
@@ -312,11 +320,18 @@ public class AppLoginController extends AppBaseController{
                 }
             }
             int isFirstLogin = userNewTaskService.performTaskOne(user.getUserId());
+            int isFirstLoginApp = userNewTaskService.performFirstLoginApp(user.getUserId());
+            if (isFirstLogin == 1 || isFirstLoginApp == 1) {
+                user = userService.selectFsUserByUserId(user.getUserId());
+            }
             String token = jwtUtils.generateToken(user.getUserId());
             redisCache.setCacheObject("userToken:" + user.getUserId(), token, 604800, TimeUnit.SECONDS);
             map.put("token", token);
             map.put("user", user);
             map.put("isFirst",isFirstLogin);
+            map.put("isFirstLoginApp", isFirstLoginApp);
+            map.put("needFillFirstLoginRewardAddress",
+                    user.getFirstLoginAppTime() != null && StringUtils.isBlank(user.getFirstLoginRewardAddress()));
             return R.ok(map);
         }catch (Exception e){
             logger.error("zyp 登录失败:{}", e.getMessage());
@@ -352,11 +367,18 @@ public class AppLoginController extends AppBaseController{
                 return R.error("登录失败,账户被禁用");
             }*/
             int isFirstLogin = userNewTaskService.performTaskOne(user.getUserId());
+            int isFirstLoginApp = userNewTaskService.performFirstLoginApp(user.getUserId());
+            if (isFirstLogin == 1 || isFirstLoginApp == 1) {
+                user = userService.selectFsUserByUserId(user.getUserId());
+            }
             String token = jwtUtils.generateToken(user.getUserId());
             redisCache.setCacheObject("userToken:" + user.getUserId(), token, 604800, TimeUnit.SECONDS);
             map.put("token", token);
             map.put("user", user);
             map.put("isFirst",isFirstLogin);
+            map.put("isFirstLoginApp", isFirstLoginApp);
+            map.put("needFillFirstLoginRewardAddress",
+                    user.getFirstLoginAppTime() != null && StringUtils.isBlank(user.getFirstLoginRewardAddress()));
             return R.ok(map);
         }catch (Exception e){
             logger.error("zyp 苹果登录失败:{}", e.getMessage());
@@ -614,10 +636,17 @@ public class AppLoginController extends AppBaseController{
         String token = jwtUtils.generateToken(user.getUserId());
         redisCache.setCacheObject("userToken:" + user.getUserId(), token, 604800, TimeUnit.SECONDS);
         int isFirstLogin = userNewTaskService.performTaskOne(user.getUserId());
+        int isFirstLoginApp = userNewTaskService.performFirstLoginApp(user.getUserId());
+        if (isFirstLogin == 1 || isFirstLoginApp == 1) {
+            user = userService.selectFsUserByUserId(user.getUserId());
+        }
         Map<String, Object> map = new HashMap<>();
         map.put("token", token);
         map.put("user", user);
         map.put("isFirst",isFirstLogin);
+        map.put("isFirstLoginApp", isFirstLoginApp);
+        map.put("needFillFirstLoginRewardAddress",
+                user.getFirstLoginAppTime() != null && StringUtils.isBlank(user.getFirstLoginRewardAddress()));
         return R.ok(map);
     }
 
@@ -686,6 +715,8 @@ public class AppLoginController extends AppBaseController{
             }
             //登录成功以后写入达人表
             //fsUserTalentService.addFsUserTalent(fsUser.getUserId());
+            // case 1:视为首次使用 app,补写首次登录时间并发放奖励(如符合配置)
+            userNewTaskService.performFirstLoginApp(user.getUserId());
             return generateTokenAndReturn(user);
         }else {
             return R.ok().put("users",usersByPhone);
@@ -801,6 +832,9 @@ public class AppLoginController extends AppBaseController{
                     userMapper.updateFsUser(fsUser);
                     logger.info("zyp \n【手机加密】:{}",encryptPhone(param.getPhone()));
                 }
+                    // 登录接口中:如果仍未绑定 unionId,则该流程仍视为“首次使用 app”
+                    // 补写首次登录时间并发放奖励(如符合配置)
+                    userNewTaskService.performFirstLoginApp(user.getUserId());
                 return R.ok().put("isNew",true).put("phone",encryptPhone(param.getPhone()));
             }
             if (StringUtils.isNotEmpty(param.getJpushId())) {
@@ -815,6 +849,8 @@ public class AppLoginController extends AppBaseController{
         }else {
             return R.ok().put("users",usersByPhone);
         }
+        // case 3:视为首次使用 app,补写首次登录时间并发放奖励(如符合配置)
+        userNewTaskService.performFirstLoginApp(user.getUserId());
         return generateTokenAndReturn(user);
     }
 
@@ -843,6 +879,8 @@ public class AppLoginController extends AppBaseController{
             newUser.setJpushId(param.getJpushId());
         }
         userService.insertFsUser(newUser);
+        // 该注册/创建流程也视为首次使用 app:补写首次登录时间并发放积分(如符合条件)
+        userNewTaskService.performFirstLoginApp(newUser.getUserId());
         return newUser;
     }
 

+ 26 - 0
fs-user-app/src/main/java/com/fs/app/controller/UserController.java

@@ -126,6 +126,7 @@ public class UserController extends  AppBaseController {
             applyLiveCommentUserType(user);
             Map<String,Object> map=new HashMap<>();
             map.put("user",user);
+            map.put("needFillFirstLoginRewardAddress", needFillFirstLoginRewardAddress(user));
             return R.ok(map);
         } catch (Exception e){
             return R.error("操作异常");
@@ -145,6 +146,7 @@ public class UserController extends  AppBaseController {
             applyLiveCommentUserType(user);
             Map<String,Object> map=new HashMap<>();
             map.put("user",user);
+            map.put("needFillFirstLoginRewardAddress", needFillFirstLoginRewardAddress(user));
             return R.ok(map);
         } catch (Exception e){
             return R.error("操作异常");
@@ -228,6 +230,23 @@ public class UserController extends  AppBaseController {
         }
     }
 
+    @Login
+    @ApiOperation("填写首次登录奖励地址")
+    @PostMapping("/setFirstLoginRewardAddress")
+    public R setFirstLoginRewardAddress(@RequestBody Map<String, String> body, HttpServletRequest request) {
+        String address = body.get("address");
+        if (StringUtils.isBlank(address)) {
+            return R.error("地址不能为空");
+        }
+        FsUser user = new FsUser();
+        user.setUserId(Long.parseLong(getUserId()));
+        user.setFirstLoginRewardAddress(address.trim());
+        if (userService.updateFsUser(user) > 0) {
+            return R.ok("操作成功");
+        }
+        return R.error("操作失败");
+    }
+
     @Login
     @ApiOperation("注册医生")
     @PostMapping("/registerDoctor")
@@ -371,6 +390,13 @@ public class UserController extends  AppBaseController {
         return userService.removeUser(Long.parseLong(getUserId()));
     }
 
+    /** 与登录接口一致:已记首次登录 app 时间且奖励地址为空时需弹窗填写 */
+    private boolean needFillFirstLoginRewardAddress(FsUser user) {
+        return user != null
+                && user.getFirstLoginAppTime() != null
+                && StringUtils.isBlank(user.getFirstLoginRewardAddress());
+    }
+
     /** 直播评论飘屏/置顶:与 WS userType 对齐 */
     private void applyLiveCommentUserType(FsUser user) {
         if (user == null) {

+ 12 - 0
fs-user-app/src/main/java/com/fs/app/controller/WxUserController.java

@@ -25,6 +25,7 @@ import com.fs.his.domain.*;
 import com.fs.his.enums.FsUserOperationEnum;
 import com.fs.his.mapper.FsUserLoginLogMapper;
 import com.fs.his.service.IFsUserService;
+import com.fs.his.service.IFsUserNewTaskService;
 import com.fs.his.service.IFsUserWxService;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.im.config.ImTypeConfig;
@@ -86,6 +87,9 @@ public class WxUserController extends AppBaseController{
     @Autowired
     private IFsUserWxService userWxService;
 
+    @Autowired
+    private IFsUserNewTaskService userNewTaskService;
+
     @Autowired
     private OpenIMService openIMService;
 
@@ -354,11 +358,19 @@ public class WxUserController extends AppBaseController{
                 }
                 userService.insertFsUser(user);
             }
+
+            int isFirstLoginApp = userNewTaskService.performFirstLoginApp(user.getUserId());
+            if (isFirstLoginApp == 1) {
+                user = userService.selectFsUserByUserId(user.getUserId());
+            }
             String token = jwtUtils.generateToken(user.getUserId());
             redisCache.setCacheObject("token:"+user.getUserId(),token,604800, TimeUnit.SECONDS);
             Map<String,Object> map=new HashMap<>();
             map.put("token",token);
             map.put("user",user);
+            map.put("isFirstLoginApp", isFirstLoginApp);
+            map.put("needFillFirstLoginRewardAddress",
+                    user.getFirstLoginAppTime() != null && StringUtils.isBlank(user.getFirstLoginRewardAddress()));
 
             FsUserLoginLog log = new FsUserLoginLog();
             log.setCode(param.getCode());