xw il y a 1 jour
Parent
commit
0b85d518bc

+ 8 - 0
fs-admin/src/main/java/com/fs/qw/controller/QwSopController.java

@@ -153,6 +153,10 @@ public class QwSopController extends BaseController
     @PostMapping
     public AjaxResult add(@RequestBody QwSop qwSop)
     {
+        String validateMsg = qwSopService.validateBeforeSave(qwSop);
+        if (validateMsg != null) {
+            return AjaxResult.error(validateMsg);
+        }
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
@@ -170,6 +174,10 @@ public class QwSopController extends BaseController
     @PutMapping
     public R edit(@RequestBody QwSop qwSop)
     {
+        String validateMsg = qwSopService.validateBeforeSave(qwSop);
+        if (validateMsg != null) {
+            return R.error(validateMsg);
+        }
         return qwSopService.updateQwSop(qwSop);
     }
 

+ 4 - 0
fs-company/src/main/java/com/fs/company/controller/qw/QwSopController.java

@@ -247,6 +247,10 @@ public class QwSopController extends BaseController
     @PostMapping
     public AjaxResult add(@RequestBody QwSop qwSop)
     {
+        String validateMsg = qwSopService.validateBeforeSave(qwSop);
+        if (validateMsg != null) {
+            return AjaxResult.error(validateMsg);
+        }
         SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
 
         LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());

+ 5 - 0
fs-qw-task/src/main/java/com/fs/app/taskService/impl/SopLogsTaskServiceImpl.java

@@ -792,6 +792,11 @@ public class SopLogsTaskServiceImpl implements SopLogsTaskService {
         Long courseId = content.getCourseId();
         Long videoId = content.getVideoId();
         Long liveId = content.getLiveId();
+        // 「可选ipad」官方群发模式:当 SOP 未绑定企微员工(qw_sop.qw_user_ids 为空)时,
+        // 强制本条规则走 isOfficial=1 官方群发,由 corpId + 官方接口承担发送,无需员工 iPad
+        if (logVo != null && StringUtils.isEmpty(logVo.getQwUserIds())) {
+            content.setIsOfficial("1");
+        }
         Integer isOfficial = content.getIsOfficial() != null ? Integer.valueOf(content.getIsOfficial()) : 0;
 
 

+ 7 - 0
fs-service/src/main/java/com/fs/qw/domain/QwCompany.java

@@ -100,6 +100,13 @@ public class QwCompany extends BaseEntity
 
     private String qwApiUrl;
 
+    /**
+     * 可选ipad:1-允许(不绑ipad走官方群发),0/null-必须绑ipad(默认)
+     * 销售端开启后,SOP(群发助手/课程模板)可不选员工,走 isOfficial=1 官方群发
+     */
+    @Excel(name = "可选ipad")
+    private Integer allowOfficial;
+
     /** 批量操作的ID数组 */
     private Long[] ids;
 

+ 3 - 3
fs-service/src/main/java/com/fs/qw/mapper/QwUserMapper.java

@@ -302,10 +302,10 @@ public interface QwUserMapper extends BaseMapper<QwUser>
 //    @Select("select login_code_url from qw_user where app_key=#{key}")
     String selectQwUserByAppKeyToIP(String key);
 
-    @Select("select corp_id as dictValue,corp_name as dictLabel,corp_id,corp_name from qw_company where FIND_IN_SET(#{companyId},company_ids)")
+    @Select("select corp_id as dictValue,corp_name as dictLabel,corp_id,corp_name,allow_official from qw_company where FIND_IN_SET(#{companyId},company_ids)")
     List<QwOptionsVO> selectQwCompanyListOptionsVOByCompanyId(Long companyId);
 
-    @Select("select corp_id as dictValue,corp_name as dictLabel,corp_id,corp_name from qw_company where status=1")
+    @Select("select corp_id as dictValue,corp_name as dictLabel,corp_id,corp_name,allow_official from qw_company where status=1")
     List<QwOptionsVO> selectQwCompanyListOptionsVOAll();
 
     @Select("select  *  from qw_user where qw_hook_id=#{qwHookId} ")
@@ -341,7 +341,7 @@ public interface QwUserMapper extends BaseMapper<QwUser>
             "</script>")
     List<QwUserVO> selectQwUserVOByIds(Long[] ids);
 
-    @Select("select corp_id as dictValue,corp_name as dictLabel from qw_company")
+    @Select("select corp_id as dictValue,corp_name as dictLabel,allow_official from qw_company")
     List<QwOptionsVO> selectQwCompanyListAllOptionsVO();
 
     @Select("select qw_user_name from qw_user  WHERE qw_user_id=#{userId} and corp_id=#{corpId}")

+ 4 - 0
fs-service/src/main/java/com/fs/qw/vo/QwOptionsVO.java

@@ -8,4 +8,8 @@ public class QwOptionsVO {
     String dictLabel;
     String CorpId;
     String corpName;
+    /**
+     * 企微主体「可选ipad」:1=允许不选员工走官方群发;0/null=须选员工(iPad 链路)
+     */
+    private Integer allowOfficial;
 }

+ 3 - 1
fs-service/src/main/java/com/fs/qwApi/service/impl/QwApiServiceImpl.java

@@ -213,7 +213,9 @@ public class QwApiServiceImpl implements QwApiService {
         JSONObject groupList=new JSONObject();
         groupList.put("group_list",msgTemplate.getTagFilterGroupList());
         jsonObject.put("tag_filter",groupList);
-        jsonObject.put("sender",msgTemplate.getSender());
+        if (StringUtils.isNotEmpty(msgTemplate.getSender())) {
+            jsonObject.put("sender", msgTemplate.getSender());
+        }
         jsonObject.put("allow_select",msgTemplate.getAllowSelect());
 
         JSONObject Content=new JSONObject();

+ 4 - 1
fs-service/src/main/java/com/fs/sop/mapper/QwSopMapper.java

@@ -254,8 +254,11 @@ public interface QwSopMapper extends BaseMapper<QwSop> {
             "qw_external_contact qec " +
             "\tleft JOIN qw_user qu on qu.id=qec.qw_user_id\n" +
             "\tLEFT JOIN company_user cu on cu.user_id=qu.company_user_id " +
-            "WHERE qec.status=0 and qec.corp_id =#{map.cropId} and qec.qw_user_id  in " +
+            "WHERE qec.status=0 and qec.corp_id =#{map.cropId} " +
+            "<if test='map.userIdsSelectList != null and map.userIdsSelectList.size > 0'>" +
+            " and qec.qw_user_id  in " +
             " <foreach collection='map.userIdsSelectList'  item='item' index='index'  open='(' separator=',' close=')'> #{item} </foreach> " +
+            "</if> " +
             "<if test ='map.filterType==2 and map.tagsIdsSelectList!=null'> " +
             "  and ( \n" +
             "    <foreach collection='map.tagsIdsSelectList' item='item' index='index' separator=' or '> \n" +

+ 10 - 0
fs-service/src/main/java/com/fs/sop/service/IQwSopService.java

@@ -1,6 +1,8 @@
 package com.fs.sop.service;
 
+import com.fs.common.annotation.DataSource;
 import com.fs.common.core.domain.R;
+import com.fs.common.enums.DataSourceType;
 import com.fs.course.vo.FsCourseWatchLogStatisticsListVO;
 import com.fs.qw.domain.QwSopUpdateStatus;
 import com.fs.qw.param.CourseQuizRedEnvelopeStatsParam;
@@ -51,6 +53,14 @@ public interface IQwSopService
      */
     public int insertQwSop(QwSop qwSop);
 
+    /**
+     * 保存/修改前置校验:当 qwUserIds 为空时,仅在「群发助手 / 课程模板」且公司开启「可选ipad」时放行
+     *
+     * @param qwSop 企微sop
+     * @return null=通过;否则返回错误信息
+     */
+    public String validateBeforeSave(QwSop qwSop);
+
     /**
      * 修改企微sop
      *

+ 24 - 9
fs-service/src/main/java/com/fs/sop/service/impl/QwSopLogsServiceImpl.java

@@ -1987,21 +1987,36 @@ public class QwSopLogsServiceImpl extends ServiceImpl<QwSopLogsMapper, QwSopLogs
     private void handleGroup(List<QwSopLogs> logsGroup, Queue<QwSopLogs> updateQueue) {
 
         try {
-            String firstKey = logsGroup.get(0).getCorpId() + "|" + logsGroup.get(0).getQwUserid();
-            String[] keyParts = firstKey.split("\\|");
-            String corpId = keyParts[0].trim();
-            String qwUserid = keyParts[1].trim();
-
-            QwUser qwUser = qwExternalContactService.getQwUserByRedis(corpId, qwUserid);
-            if (qwUser == null || qwUser.getIsDel() != 0) {
-                logger.error("员工信息无效-不存在或被删除,corpId:{},userId:{}", corpId, qwUserid);
+            QwSopLogs first = logsGroup.get(0);
+            String corpId = first.getCorpId() == null ? "" : first.getCorpId().trim();
+            if (StringUtils.isEmpty(corpId)) {
+                logger.error("corpId 为空,无法创建企微群发");
                 logsGroup.forEach(log -> {
                     log.setSendStatus(5L);
-                    log.setRemark("员工信息无效");
+                    log.setRemark("corpId 为空");
                     updateQueue.add(log);
                 });
                 return;
             }
+            // 可选 iPad / 官方群发:可不选员工,qw_userid 为空时不校验 QwUser,接口侧不传 sender(见 QwApiServiceImpl#addMsgTemplateBySop)
+            String rawSender = first.getQwUserid();
+            String qwUserid = StringUtils.isEmpty(rawSender) ? null : rawSender.trim();
+            if (StringUtils.isNotEmpty(qwUserid) && !"null".equalsIgnoreCase(qwUserid)) {
+                QwUser qwUser = qwExternalContactService.getQwUserByRedis(corpId, qwUserid);
+                if (qwUser == null || qwUser.getIsDel() != 0) {
+                    logger.error("员工信息无效-不存在或被删除,corpId:{},userId:{}", corpId, qwUserid);
+                    logsGroup.forEach(log -> {
+                        log.setSendStatus(5L);
+                        log.setRemark("员工信息无效");
+                        updateQueue.add(log);
+                    });
+                    return;
+                }
+            } else {
+                qwUserid = null;
+            }
+
+            String firstKey = corpId + "|" + (qwUserid == null ? "" : qwUserid);
 
             // 提取内容与目标客户列表
             String contentJson = logsGroup.get(0).getContentJson();

+ 35 - 0
fs-service/src/main/java/com/fs/sop/service/impl/QwSopServiceImpl.java

@@ -17,9 +17,11 @@ import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.course.vo.FsCourseWatchLogStatisticsListVO;
 import com.fs.his.mapper.FsUserMapper;
 import com.fs.his.service.IFsUserService;
+import com.fs.qw.domain.QwCompany;
 import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwSopUpdateStatus;
 import com.fs.qw.domain.QwUser;
+import com.fs.qw.mapper.QwCompanyMapper;
 import com.fs.qw.mapper.QwUserMapper;
 import com.fs.qw.param.*;
 import com.fs.qw.result.QwFilterSopCustomersResult;
@@ -142,6 +144,9 @@ public class QwSopServiceImpl implements IQwSopService
 
     @Autowired
     private FsUserMapper fsUserMapper;
+
+    @Autowired
+    private QwCompanyMapper qwCompanyMapper;
     /**
      * 查询企微sop
      *
@@ -154,6 +159,36 @@ public class QwSopServiceImpl implements IQwSopService
         return qwSopMapper.selectQwSopById(id);
     }
 
+    /**
+     */
+    @Override
+    public String validateBeforeSave(QwSop qwSop) {
+        if (qwSop == null) {
+            return null;
+        }
+
+        if (StringUtils.isNotEmpty(qwSop.getQwUserIds())) {
+            return null;
+        }
+
+        Integer sendType = null;
+        if (StringUtils.isNotEmpty(qwSop.getTempId())) {
+            QwSopTemp temp = qwSopTempMapper.selectQwSopTempById(qwSop.getTempId());
+            if (temp != null) {
+                sendType = temp.getSendType();
+            }
+        }
+        if (sendType == null || (sendType != 2 && sendType != 11)) {
+            return "请选择使用员工";
+        }
+
+        QwCompany company = qwCompanyMapper.selectQwCompanyByCorpId(qwSop.getCorpId());
+        if (company == null || !Integer.valueOf(1).equals(company.getAllowOfficial())) {
+            return "请选择使用员工";
+        }
+        return null;
+    }
+
 
 
     /**

+ 6 - 0
fs-service/src/main/java/com/fs/sop/vo/SopUserLogsVo.java

@@ -49,4 +49,10 @@ public class SopUserLogsVo  {
      */
     private Integer isSampSend;
 
+    /**
+     * 该 SOP 配置的企微员工集合(qw_sop.qw_user_ids)。
+     * 为空表示「不绑ipad走官方群发」模式:运行期所有规则强制 isOfficial=1
+     */
+    private String qwUserIds;
+
 }

+ 9 - 2
fs-service/src/main/resources/mapper/qw/QwCompanyMapper.xml

@@ -33,10 +33,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="shareAppId"    column="share_app_id"    />
         <result property="shareAgentId"    column="share_agent_id"    />
         <result property="shareSchema"    column="share_schema"    />
+        <result property="allowOfficial"    column="allow_official"    />
     </resultMap>
 
     <sql id="selectQwCompanyVo">
-        select id, corp_id, corp_name, open_secret, open_corp_id, server_agent_id, server_book_corp_id, server_book_secret, token, encoding_aes_key, provider_secret, realm_name_url, notify_url, chat_toolbar, chat_toolbar_oauth, company_ids, status, create_time, update_time, create_by,is_buy,mini_app_id,company_server_num,share_app_id,share_agent_id,share_schema from qw_company
+        select id, corp_id, corp_name, open_secret, open_corp_id, server_agent_id, server_book_corp_id, server_book_secret, token, encoding_aes_key, provider_secret, realm_name_url, notify_url, chat_toolbar, chat_toolbar_oauth, company_ids, status, create_time, update_time, create_by,is_buy,mini_app_id,company_server_num,share_app_id,share_agent_id,share_schema,allow_official from qw_company
     </sql>
 
     <select id="selectQwCompanyList" parameterType="QwCompany" resultMap="QwCompanyResult">
@@ -64,6 +65,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="shareAppId != null  and shareAppId != ''"> and share_app_id = #{shareAppId}</if>
             <if test="shareAgentId != null  and shareAgentId != ''"> and share_agent_id = #{shareAgentId}</if>
             <if test="shareSchema != null  and shareSchema != ''"> and share_schema = #{shareSchema}</if>
+            <if test="allowOfficial != null"> and allow_official = #{allowOfficial}</if>
         </where>
     </select>
 
@@ -95,6 +97,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             qc.share_app_id,
             qc.share_agent_id,
             qc.share_schema,
+            qc.allow_official,
             sc.`name` AS mini_name,
             qc.qw_api_url
         FROM
@@ -124,6 +127,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="shareAppId != null  and shareAppId != ''"> and qc.share_app_id = #{shareAppId}</if>
             <if test="shareAgentId != null  and shareAgentId != ''"> and qc.share_agent_id = #{shareAgentId}</if>
             <if test="shareSchema != null  and shareSchema != ''"> and qc.share_schema = #{shareSchema}</if>
+            <if test="allowOfficial != null"> and qc.allow_official = #{allowOfficial}</if>
             <if test="miniName != null  and miniName != ''">and sc.`name` like concat('%', #{miniName}, '%') </if>
         </where>
     </select>
@@ -164,11 +168,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="isBuy != null">is_buy,</if>
             <if test="miniAppId != null">mini_app_id,</if>
             <if test="companyServerNum != null">company_server_num,</if>
-            <if test="createUserId != null != null">create_user_id,</if>
+            <if test="createUserId != null">create_user_id,</if>
             <if test="createDeptId != null">create_dept_id,</if>
             <if test="shareAppId != null">share_app_id,</if>
             <if test="shareAgentId != null">share_agent_id,</if>
             <if test="shareSchema != null">share_schema,</if>
+            <if test="allowOfficial != null">allow_official,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="corpId != null">#{corpId},</if>
@@ -198,6 +203,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="shareAppId != null">#{shareAppId},</if>
             <if test="shareAgentId != null">#{shareAgentId},</if>
             <if test="shareSchema != null">#{shareSchema},</if>
+            <if test="allowOfficial != null">#{allowOfficial},</if>
          </trim>
     </insert>
 
@@ -229,6 +235,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="shareAppId != null">share_app_id = #{shareAppId},</if>
             <if test="shareAgentId != null">share_agent_id = #{shareAgentId},</if>
             <if test="shareSchema != null">share_schema = #{shareSchema},</if>
+            <if test="allowOfficial != null">allow_official = #{allowOfficial},</if>
         </trim>
         where id = #{id}
     </update>

+ 2 - 1
fs-service/src/main/resources/mapper/sop/SopUserLogsMapper.xml

@@ -203,7 +203,8 @@
                b.is_register,
                b.chat_id,
                b.filter_mode,
-               b.is_samp_send
+               b.is_samp_send,
+               b.qw_user_ids
         from sop_user_logs a
         inner join qw_sop b on a.sop_id = b.id
         inner join qw_sop_temp c on b.temp_id = c.id