Quellcode durchsuchen

feat:公司码相关功能

caoliqin vor 2 Wochen
Ursprung
Commit
5a82947007

+ 0 - 28
fs-app-task/src/main/resources/application.yml

@@ -8,31 +8,3 @@ spring:
     #    active: druid-hdt
 #    active: dev
     active: druid-hst-test
-
-task:
-  enabled: true # 开启定时任务开关
-  mode: consumer # 区分是生产任务-producer,还是消费任务-consumer
-  producer:
-    gen-user-logs-delay: 60000 # 生成营期任务的间隔时间,单位毫秒
-    gen-user-logs-info-fragmentation-cron: 0 5 * * * * # 营期分片频率
-    gen-sop-logs-delay: 10000 #根据营期分片生成待执行记录的间隔时长
-    sync-watchlog-to-urgent-class-cron: 0 0 0/2 * * * # 同步看课记录到催课看板的频率
-    handle-repeat-data-delay: 1200000 # 处理待发记录重复数据的频次 20min = 20 * 60 * 1000
-    node: 0
-  consumer:
-    node: 0 # 消费节点编号,为消费者时必须设置且不可重复
-    future-timeout-seconds: 30 # future 等待时长
-    compensate-fixed-delay: 10000 # 消费补偿的间隔频率(10s)
-    consume-max-handle-minutes: 60 # 针对单条数据处理最大时长,超过则算作超时,单位分钟(60min)
-    consume-bind-tag-fixed-delay: 5000 # 消费绑定标签的间隔频率(5s)
-    consume-unbind-tag-fixed-delay: 5000 # 消费解绑标签的间隔频率(5s)
-    consume-bind-customer-fixed-delay: 5000 # 消费绑定客服的间隔频率(5s)
-    consume-unbind-customer-fixed-delay: 5000 # 消费解绑客服的间隔频率(5s)
-    consume-send-welcome-fixed-delay: 5000 # 消费发送欢迎语消息的间隔频率(5s)
-    consume-clear-app-sop-logs-cron: "0 0 1 * * *"
-    consume-clear-app-welcome-send-logs-cron: "0 5 1 * * *"
-    consume-auto-remind-ai-fixed-delay: 30000 # 自动 提醒/触达 ai的间隔频率(10s)
-    thread-pool:
-      core-pool-size: 30
-      max-pool-size: 50
-      thread-name-prefix: app-sop-send-thread-

+ 5 - 0
fs-service/src/main/java/com/fs/app/invitation/domain/AppCompanyInvitationCode.java

@@ -46,4 +46,9 @@ public class AppCompanyInvitationCode {
 
     private Date updateTime;
 
+    /**
+     * 公司二维码url
+     */
+    private String qrCode;
+
 }

+ 6 - 0
fs-service/src/main/java/com/fs/app/invitation/mapper/AppCompanyInvitationCodeMapper.java

@@ -2,6 +2,7 @@ package com.fs.app.invitation.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.app.invitation.domain.AppCompanyInvitationCode;
+import org.apache.ibatis.annotations.Param;
 
 import java.util.List;
 
@@ -60,4 +61,9 @@ public interface AppCompanyInvitationCodeMapper extends BaseMapper<AppCompanyInv
      * @return 结果
      */
     int deleteAppCompanyInvitationCodeByIds(Long[] ids);
+
+    /**
+     * 根据公司id查询可用销售id列表
+     */
+    List<Long> selectInvitationCodeListByCompanyId(@Param("companyId") Long companyId);
 }

+ 5 - 0
fs-service/src/main/java/com/fs/app/invitation/service/IAppCompanyInvitationCodeService.java

@@ -59,4 +59,9 @@ public interface IAppCompanyInvitationCodeService extends IService<AppCompanyInv
      * @return 结果
      */
     int deleteAppCompanyInvitationCodeById(Long id);
+
+    /**
+     * 根据公司id轮询获取销售邀请码(销售id)
+     */
+    Long pollInvitationCodeByCompanyId(Long companyId);
 }

+ 20 - 0
fs-service/src/main/java/com/fs/app/invitation/service/impl/AppCompanyInvitationCodeServiceImpl.java

@@ -5,6 +5,8 @@ import com.fs.aiSipCall.utils.DateUtils;
 import com.fs.app.invitation.domain.AppCompanyInvitationCode;
 import com.fs.app.invitation.mapper.AppCompanyInvitationCodeMapper;
 import com.fs.app.invitation.service.IAppCompanyInvitationCodeService;
+import com.fs.common.core.redis.RedisCache;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
 import java.util.List;
@@ -18,6 +20,11 @@ import java.util.List;
 @Service
 public class AppCompanyInvitationCodeServiceImpl extends ServiceImpl<AppCompanyInvitationCodeMapper, AppCompanyInvitationCode> implements IAppCompanyInvitationCodeService {
 
+    private static final String INVITATION_CODE_ROUND_ROBIN_KEY = "app:invitation_code:round_robin:";
+
+    @Autowired
+    private RedisCache redisCache;
+
     /**
      * 查询app-公司邀请码,公司扩展
      *
@@ -85,4 +92,17 @@ public class AppCompanyInvitationCodeServiceImpl extends ServiceImpl<AppCompanyI
     public int deleteAppCompanyInvitationCodeById(Long id) {
         return baseMapper.deleteAppCompanyInvitationCodeById(id);
     }
+
+    @Override
+    public Long pollInvitationCodeByCompanyId(Long companyId) {
+        List<Long> invitationCodeList = baseMapper.selectInvitationCodeListByCompanyId(companyId);
+        if (invitationCodeList == null || invitationCodeList.isEmpty()) {
+            return null;
+        }
+
+        // 轮询,按照公司id一次增加1,取余
+        Long seq = redisCache.incr(INVITATION_CODE_ROUND_ROBIN_KEY + companyId, 1L);
+        int index = Math.toIntExact(((seq - 1) % invitationCodeList.size()));
+        return invitationCodeList.get(index);
+    }
 }

+ 44 - 0
fs-service/src/main/java/com/fs/app/invitation/service/impl/AppInvitationCodeServiceImpl.java

@@ -16,8 +16,11 @@ import com.fs.app.invitation.service.IAppInvitationCodeTagService;
 import com.fs.app.invitation.vo.AppInvitationCodeVO;
 import com.fs.common.BeanCopyUtils;
 import com.fs.common.core.domain.R;
+import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.bean.BeanUtils;
+import com.fs.his.utils.qrcode.QRCodeUtils;
 import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
@@ -25,6 +28,7 @@ import java.util.ArrayList;
 import java.util.List;
 import java.util.stream.Collectors;
 
+@Slf4j
 @Service
 @RequiredArgsConstructor
 public class AppInvitationCodeServiceImpl extends ServiceImpl<AppInvitationCodeMapper, AppInvitationCode> implements IAppInvitationCodeService {
@@ -94,6 +98,9 @@ public class AppInvitationCodeServiceImpl extends ServiceImpl<AppInvitationCodeM
         }
         //公司邀请码
         else {
+            AppCompanyInvitationCode companyCode = this.appCompanyInvitationCodeService
+                    .selectAppCompanyInvitationCodeById(reqData.getId());
+
             this.appCompanyInvitationCodeService.lambdaUpdate()
                     .set(true, AppCompanyInvitationCode::getRemark, reqData.getRemark())
                     .set(true, AppCompanyInvitationCode::getTagIds,
@@ -102,6 +109,10 @@ public class AppInvitationCodeServiceImpl extends ServiceImpl<AppInvitationCodeM
                             ObjectUtil.isNotEmpty(reqData.getCustomerIds()) ? reqData.getCustomerIds().stream().map(String::valueOf).collect(Collectors.joining(",")) : null)
                     .eq(AppCompanyInvitationCode::getId, reqData.getId())
                     .update();
+            R qrResult = ensureCompanyInvitationQrCode(companyCode);
+            if (qrResult != null) {
+                return qrResult;
+            }
         }
         return R.ok();
     }
@@ -145,6 +156,7 @@ public class AppInvitationCodeServiceImpl extends ServiceImpl<AppInvitationCodeM
             AppInvitationCodeVO vo = new AppInvitationCodeVO();
             vo.setId(one.getId());
             vo.setInvitationCode(one.getCompanyId() + "");
+            vo.setQrCode(one.getQrCode());
             return vo;
         }
         AppInvitationCodeVO vo = new AppInvitationCodeVO();
@@ -152,4 +164,36 @@ public class AppInvitationCodeServiceImpl extends ServiceImpl<AppInvitationCodeM
         vo.setInvitationCode(one.getCompanyId() + "");
         return vo;
     }
+
+    /**
+     * 公司邀请码二维码:内容为公司id(邀请码)
+     *
+     * @return 成功返回 null
+     */
+    private R ensureCompanyInvitationQrCode(AppCompanyInvitationCode companyCode) {
+        if (companyCode == null || companyCode.getCompanyId() == null) {
+            return null;
+        }
+        if (StringUtils.isNotEmpty(companyCode.getQrCode())) {
+            return null;
+        }
+        try {
+            String invitationCode = String.valueOf(companyCode.getCompanyId());
+            R uploadResult = QRCodeUtils.createAndUpload(invitationCode);
+            Object url = uploadResult.get("url");
+            if (url == null || StringUtils.isEmpty(url.toString())) {
+                return R.error("二维码生成失败");
+            }
+            String qrCodeUrl = url.toString();
+            this.appCompanyInvitationCodeService.lambdaUpdate()
+                    .set(AppCompanyInvitationCode::getQrCode, qrCodeUrl)
+                    .eq(AppCompanyInvitationCode::getId, companyCode.getId())
+                    .update();
+            companyCode.setQrCode(qrCodeUrl);
+            return null;
+        } catch (Exception e) {
+            log.error("公司邀请码二维码生成失败, companyId={}", companyCode.getCompanyId(), e);
+            return R.error("二维码生成失败:" + e.getMessage());
+        }
+    }
 }

+ 3 - 0
fs-service/src/main/java/com/fs/app/invitation/vo/AppInvitationCodeVO.java

@@ -34,4 +34,7 @@ public class AppInvitationCodeVO {
 
     private String companyName;
 
+    /** 公司邀请码二维码地址 */
+    private String qrCode;
+
 }

+ 13 - 0
fs-service/src/main/java/com/fs/app/invitation/vo/AppRandomInvitationCodeVO.java

@@ -0,0 +1,13 @@
+package com.fs.app.invitation.vo;
+
+import lombok.Data;
+
+@Data
+public class AppRandomInvitationCodeVO {
+
+    /**
+     * 邀请码
+     */
+    private Long invitationCode;
+
+}

+ 28 - 2
fs-service/src/main/resources/application-config-druid-hst.yml

@@ -111,8 +111,34 @@ config:
   receiver-prefix: U # IM发送消息,接收人前缀
   batch-size: 500 # 批量处理数据大小
   load: # 自定义条件加载配置
-    include: "sendSopLogs,generateSopLogs,consumer" # 包含需要加载的配置条件,*则是加载全部,如果需要指定加载组件,只需要和组件配置的名称一致即可,多个用英文逗号隔开,不需要用yml数组格式
+    include: "sendSopLogs,generateSopLogs,consumer,generateUrgentClass" # 包含需要加载的配置条件,*则是加载全部,如果需要指定加载组件,只需要和组件配置的名称一致即可,多个用英文逗号隔开,不需要用yml数组格式
 #    exclude: "*" # 排除需要加载的配置条件,*则是排除全部,如果需要指定排除组件,只需要和组件配置的名称一致即可,多个用英文逗号隔开,不需要用yml数组格式,优先级高于include
 
-
+task:
+  enabled: true # 开启定时任务开关
+  mode: consumer # 区分是生产任务-producer,还是消费任务-consumer
+  producer:
+    gen-user-logs-delay: 60000 # 生成营期任务的间隔时间,单位毫秒
+    gen-user-logs-info-fragmentation-cron: 0 5 * * * * # 营期分片频率
+    gen-sop-logs-delay: 10000 #根据营期分片生成待执行记录的间隔时长
+    sync-watchlog-to-urgent-class-cron: 0 0 0/2 * * * # 同步看课记录到催课看板的频率
+    handle-repeat-data-delay: 1200000 # 处理待发记录重复数据的频次 20min = 20 * 60 * 1000
+    node: 0
+  consumer:
+    node: 0 # 消费节点编号,为消费者时必须设置且不可重复
+    future-timeout-seconds: 30 # future 等待时长
+    compensate-fixed-delay: 10000 # 消费补偿的间隔频率(10s)
+    consume-max-handle-minutes: 60 # 针对单条数据处理最大时长,超过则算作超时,单位分钟(60min)
+    consume-bind-tag-fixed-delay: 5000 # 消费绑定标签的间隔频率(5s)
+    consume-unbind-tag-fixed-delay: 5000 # 消费解绑标签的间隔频率(5s)
+    consume-bind-customer-fixed-delay: 5000 # 消费绑定客服的间隔频率(5s)
+    consume-unbind-customer-fixed-delay: 5000 # 消费解绑客服的间隔频率(5s)
+    consume-send-welcome-fixed-delay: 5000 # 消费发送欢迎语消息的间隔频率(5s)
+    consume-clear-app-sop-logs-cron: "0 0 1 * * *"
+    consume-clear-app-welcome-send-logs-cron: "0 5 1 * * *"
+    consume-auto-remind-ai-fixed-delay: 30000 # 自动 提醒/触达 ai的间隔频率(10s)
+    thread-pool:
+      core-pool-size: 30
+      max-pool-size: 50
+      thread-name-prefix: app-sop-send-thread-
 

+ 18 - 1
fs-service/src/main/resources/mapper/app/AppCompanyInvitationCodeMapper.xml

@@ -13,10 +13,11 @@
         <result property="isDelete"    column="is_delete"    />
         <result property="createTime"    column="create_time"    />
         <result property="updateTime"    column="update_time"    />
+        <result property="qrCode"    column="qr_code"    />
     </resultMap>
 
     <sql id="selectAppCompanyInvitationCodeVo">
-        select id, company_id, remark, tag_ids, customer_ids, is_delete, create_time, update_time from app_company_invitation_code
+        select id, company_id, remark, tag_ids, customer_ids, is_delete, create_time, update_time, qr_code from app_company_invitation_code
     </sql>
 
     <select id="selectAppCompanyInvitationCodeList" parameterType="AppCompanyInvitationCode" resultMap="AppCompanyInvitationCodeResult">
@@ -44,6 +45,7 @@
             <if test="isDelete != null">is_delete,</if>
             <if test="createTime != null">create_time,</if>
             <if test="updateTime != null">update_time,</if>
+            <if test="qrCode != null">qr_code,</if>
         </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="companyId != null">#{companyId},</if>
@@ -53,6 +55,7 @@
             <if test="isDelete != null">#{isDelete},</if>
             <if test="createTime != null">#{createTime},</if>
             <if test="updateTime != null">#{updateTime},</if>
+            <if test="qrCode != null">#{qrCode},</if>
         </trim>
     </insert>
 
@@ -66,6 +69,7 @@
             <if test="isDelete != null">is_delete = #{isDelete},</if>
             <if test="createTime != null">create_time = #{createTime},</if>
             <if test="updateTime != null">update_time = #{updateTime},</if>
+            <if test="qrCode != null">qr_code = #{qrCode},</if>
         </trim>
         where id = #{id}
     </update>
@@ -80,4 +84,17 @@
             #{id}
         </foreach>
     </delete>
+
+    <select id="selectInvitationCodeListByCompanyId" resultType="java.lang.Long">
+        SELECT m.id
+        FROM app_company_invitation_code c
+        INNER JOIN app_customer_role_member m ON FIND_IN_SET(m.app_customer_id, c.customer_ids) > 0
+        WHERE c.company_id = #{companyId}
+          AND c.is_delete = 0
+          AND c.customer_ids IS NOT NULL
+          AND c.customer_ids != ''
+          AND m.is_delete = 0
+          AND m.id IS NOT NULL
+        ORDER BY m.role_member_id ASC
+    </select>
 </mapper>

+ 4 - 1
fs-service/src/main/resources/mapper/app/AppInvitationCodeMapper.xml

@@ -98,6 +98,7 @@
             c.company_id AS invitationCode,
             c.company_name AS companyName,
             acic.remark AS remark,
+            acic.qr_code AS qrCode,
             acic.tag_names AS tagNames,
             acic.customer_names AS customerNames,
             c.create_time AS createTime,
@@ -108,6 +109,7 @@
             SELECT
                 ic.company_id,
                 ic.remark,
+                ic.qr_code,
                 GROUP_CONCAT( DISTINCT ft.`name` ) AS tag_names,
                 GROUP_CONCAT( DISTINCT ft.`id` ) AS tag_ids,
                 GROUP_CONCAT( DISTINCT acr.role_name ) AS customer_names,
@@ -120,7 +122,8 @@
                 ic.is_delete = 0
             GROUP BY
                 ic.company_id,
-                ic.remark
+                ic.remark,
+                ic.qr_code
         ) acic ON acic.company_id = c.company_id
         WHERE
             c.is_del = 0

+ 29 - 0
fs-user-app/src/main/java/com/fs/app/controller/UserBindInvitationCodeController.java

@@ -0,0 +1,29 @@
+package com.fs.app.controller;
+
+import com.fs.app.invitation.dto.AppUserInvitationCodeDTO;
+import com.fs.app.invitation.service.IAppUserInvitationCodeService;
+import com.fs.common.core.domain.R;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+/**
+ * app-用户绑定邀请码
+ */
+@RestController
+@RequestMapping("/app/userInvited")
+@RequiredArgsConstructor
+public class UserBindInvitationCodeController {
+
+    private final IAppUserInvitationCodeService appUserInvitationCodeService;
+
+    @PostMapping("/bindInvForUser")
+    public R bindInvForUser(@RequestBody AppUserInvitationCodeDTO reqData) {
+        return appUserInvitationCodeService.bindInvForUser(reqData);
+    }
+
+
+
+}

+ 20 - 0
fs-user-app/src/main/java/com/fs/app/controller/UserInvitedController.java

@@ -3,6 +3,8 @@ package com.fs.app.controller;
 import java.util.List;
 
 import com.fs.app.annotation.Login;
+import com.fs.app.invitation.service.IAppCompanyInvitationCodeService;
+import com.fs.app.invitation.vo.AppRandomInvitationCodeVO;
 import com.fs.common.utils.StringUtils;
 import com.fs.his.domain.FsUserInvited;
 import com.fs.his.service.IFsUserInvitedService;
@@ -26,6 +28,9 @@ public class UserInvitedController extends AppBaseController {
     @Autowired
     private IFsUserInvitedService fsUserInvitedService;
 
+    @Autowired
+    private IAppCompanyInvitationCodeService appCompanyInvitationCodeService;
+
     /**
      * 查询用户填写邀请码记录列表
      */
@@ -86,4 +91,19 @@ public class UserInvitedController extends AppBaseController {
     {
         return AjaxResult.success(fsUserInvitedService.getDownloadPoster());
     }
+
+    /**
+     * 根据公司id轮询获取销售邀请码(销售id)
+     */
+    @GetMapping("/getInvitationCode")
+    public AjaxResult getInvitationCode(@RequestParam Long companyId)
+    {
+        Long invitationCode = appCompanyInvitationCodeService.pollInvitationCodeByCompanyId(companyId);
+        if (invitationCode == null) {
+            return AjaxResult.error("暂无可用的销售");
+        }
+        AppRandomInvitationCodeVO vo = new AppRandomInvitationCodeVO();
+        vo.setInvitationCode(invitationCode);
+        return AjaxResult.success(vo);
+    }
 }