Переглянути джерело

Merge branch 'refs/heads/master' into openIm

# Conflicts:
#	fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java
#	fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
caoliqin 13 годин тому
батько
коміт
a7b8e51134
46 змінених файлів з 824 додано та 43 видалено
  1. 8 5
      fs-admin/src/main/java/com/fs/his/controller/FsFirstDiagnosisController.java
  2. 12 4
      fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreProductScrmController.java
  3. 60 0
      fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java
  4. 13 0
      fs-company/src/main/java/com/fs/company/controller/qw/QwSopController.java
  5. 19 2
      fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java
  6. 15 1
      fs-qwhook/src/main/java/com/fs/app/controller/QwUserController.java
  7. 131 0
      fs-service/src/main/java/com/fs/company/param/CompanyUserCodeParam.java
  8. 2 0
      fs-service/src/main/java/com/fs/company/service/ICompanyUserService.java
  9. 5 3
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  10. 121 2
      fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java
  11. 5 0
      fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java
  12. 3 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  13. 2 0
      fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogStatisticsListVO.java
  14. 2 2
      fs-service/src/main/java/com/fs/erp/service/impl/DfOrderServiceImpl.java
  15. 34 0
      fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java
  16. 12 0
      fs-service/src/main/java/com/fs/his/dto/FindUsersByDTO.java
  17. 1 0
      fs-service/src/main/java/com/fs/his/mapper/FsAppVersionMapper.java
  18. 19 0
      fs-service/src/main/java/com/fs/his/mapper/FsFirstDiagnosisMapper.java
  19. 4 0
      fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java
  20. 12 0
      fs-service/src/main/java/com/fs/his/param/FindUserByParam.java
  21. 25 0
      fs-service/src/main/java/com/fs/his/param/FsFirstDiagnosisParam.java
  22. 4 1
      fs-service/src/main/java/com/fs/his/service/IFsFirstDiagnosisService.java
  23. 8 0
      fs-service/src/main/java/com/fs/his/service/IFsUserService.java
  24. 8 0
      fs-service/src/main/java/com/fs/his/service/impl/FsFirstDiagnosisServiceImpl.java
  25. 7 0
      fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java
  26. 23 0
      fs-service/src/main/java/com/fs/his/utils/qrcode/QRCodeUtils.java
  27. 81 0
      fs-service/src/main/java/com/fs/his/vo/FsFirstDiagnosisVO.java
  28. 1 1
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  29. 4 2
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java
  30. 3 0
      fs-service/src/main/java/com/fs/qw/domain/QwExternalContact.java
  31. 2 0
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  32. 3 0
      fs-service/src/main/java/com/fs/sop/mapper/QwSopLogsMapper.java
  33. 3 0
      fs-service/src/main/java/com/fs/sop/mapper/QwSopMapper.java
  34. 2 0
      fs-service/src/main/java/com/fs/sop/service/IQwSopService.java
  35. 5 0
      fs-service/src/main/java/com/fs/sop/service/impl/QwSopServiceImpl.java
  36. 6 1
      fs-service/src/main/resources/application-config-druid-syysy.yml
  37. 3 0
      fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml
  38. 2 2
      fs-service/src/main/resources/mapper/course/FsUserCourseVideoMapper.xml
  39. 38 0
      fs-service/src/main/resources/mapper/his/FsUserMapper.xml
  40. 3 0
      fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml
  41. 10 0
      fs-service/src/main/resources/mapper/sop/QwSopLogsMapper.xml
  42. 33 5
      fs-service/src/main/resources/mapper/sop/QwSopMapper.xml
  43. 20 12
      fs-service/src/main/resources/mapper/sop/SopUserLogsMapper.xml
  44. 37 0
      fs-user-app/src/main/java/com/fs/app/controller/UserController.java
  45. 10 0
      fs-user-app/src/main/java/com/fs/app/param/FriendsSearchParam.java
  46. 3 0
      fs-websocket/src/main/java/com/fs/websocket/bean/SendMsgVO.java

+ 8 - 5
fs-admin/src/main/java/com/fs/his/controller/FsFirstDiagnosisController.java

@@ -1,6 +1,9 @@
 package com.fs.his.controller;
 
 import java.util.List;
+
+import com.fs.his.param.FsFirstDiagnosisParam;
+import com.fs.his.vo.FsFirstDiagnosisVO;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -38,10 +41,10 @@ public class FsFirstDiagnosisController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('his:fsFirstDiagnosis:list')")
     @GetMapping("/list")
-    public TableDataInfo list(FsFirstDiagnosis fsFirstDiagnosis)
+    public TableDataInfo list(FsFirstDiagnosisParam fsFirstDiagnosis)
     {
         startPage();
-        List<FsFirstDiagnosis> list = fsFirstDiagnosisService.selectFsFirstDiagnosisList(fsFirstDiagnosis);
+        List<FsFirstDiagnosisVO> list = fsFirstDiagnosisService.selectFsFirstDiagnosisVOList(fsFirstDiagnosis);
         return getDataTable(list);
     }
 
@@ -51,10 +54,10 @@ public class FsFirstDiagnosisController extends BaseController
     @PreAuthorize("@ss.hasPermi('his:fsFirstDiagnosis:export')")
     @Log(title = "初诊单", businessType = BusinessType.EXPORT)
     @GetMapping("/export")
-    public AjaxResult export(FsFirstDiagnosis fsFirstDiagnosis)
+    public AjaxResult export(FsFirstDiagnosisParam fsFirstDiagnosis)
     {
-        List<FsFirstDiagnosis> list = fsFirstDiagnosisService.selectFsFirstDiagnosisList(fsFirstDiagnosis);
-        ExcelUtil<FsFirstDiagnosis> util = new ExcelUtil<FsFirstDiagnosis>(FsFirstDiagnosis.class);
+        List<FsFirstDiagnosisVO> list = fsFirstDiagnosisService.selectFsFirstDiagnosisVOList(fsFirstDiagnosis);
+        ExcelUtil<FsFirstDiagnosisVO> util = new ExcelUtil<FsFirstDiagnosisVO>(FsFirstDiagnosisVO.class);
         return util.exportExcel(list, "初诊单数据");
     }
 

+ 12 - 4
fs-admin/src/main/java/com/fs/hisStore/controller/FsStoreProductScrmController.java

@@ -1,5 +1,6 @@
 package com.fs.hisStore.controller;
 
+import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
@@ -22,6 +23,7 @@ import com.fs.hisStore.service.IFsStoreProductAttrValueScrmService;
 import com.fs.hisStore.service.IFsStoreProductScrmService;
 import com.mysql.cj.util.StringUtils;
 import io.swagger.annotations.ApiOperation;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.HttpStatus;
 import org.springframework.http.ResponseEntity;
@@ -39,6 +41,7 @@ import java.util.List;
  * @author fs
  * @date 2022-03-15
  */
+@Slf4j
 @RestController
 @RequestMapping("/store/store/storeProduct")
 public class FsStoreProductScrmController extends BaseController
@@ -73,6 +76,7 @@ public class FsStoreProductScrmController extends BaseController
     @GetMapping("/list")
     public TableDataInfo list(FsStoreProductScrm fsStoreProduct)
     {
+        log.info("查询商品列表 参数: {}", fsStoreProduct);
         startPage();
         List<FsStoreProductListVO> list;
         if(StringUtils.isNullOrEmpty(fsStoreProduct.getBarCode())){
@@ -134,12 +138,16 @@ public class FsStoreProductScrmController extends BaseController
     @PostMapping(value = "/addOrEdit")
     public R addOrEdit(@RequestBody FsStoreProductAddEditParam fsStoreProduct)
     {
-        if (fsStoreProduct.getIsShow() ==1){
-            logger.info("商品上架:{}",fsStoreProduct.getProductName()+new Date());
+        if(ObjectUtils.isNotNull(fsStoreProduct.getIsShow())) {
+            if (fsStoreProduct.getIsShow() ==1){
+                logger.info("商品上架:{}",fsStoreProduct.getProductName()+new Date());
+            }
         }
 
-        if (fsStoreProduct.getIsDisplay() ==1){
-            logger.info("商品前端展示:{}",fsStoreProduct.getProductName()+new Date());
+        if(ObjectUtils.isNotNull(fsStoreProduct.getIsDisplay())) {
+            if (fsStoreProduct.getIsDisplay() ==1){
+                logger.info("商品前端展示:{}",fsStoreProduct.getProductName()+new Date());
+            }
         }
         return fsStoreProductService.addOrEdit(fsStoreProduct);
     }

+ 60 - 0
fs-company/src/main/java/com/fs/company/controller/company/CompanyUserController.java

@@ -2,6 +2,7 @@ package com.fs.company.controller.company;
 
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
+import com.baidu.dev2.thirdparty.jackson.databind.ObjectMapper;
 import com.fs.common.annotation.Log;
 import com.fs.common.constant.UserConstants;
 import com.fs.common.core.controller.BaseController;
@@ -17,6 +18,7 @@ import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.domain.*;
 import com.fs.company.mapper.CompanyUserDelayTimeMapper;
 import com.fs.company.param.CompanyUserAreaParam;
+import com.fs.company.param.CompanyUserCodeParam;
 import com.fs.company.param.CompanyUserQwParam;
 import com.fs.company.service.*;
 import com.fs.company.utils.DomainUtil;
@@ -26,7 +28,10 @@ import com.fs.course.config.CourseConfig;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.security.SecurityUtils;
 import com.fs.framework.service.TokenService;
+import com.fs.his.utils.qrcode.QRCodeUtils;
 import com.fs.his.vo.OptionsVO;
+import com.fs.qw.domain.QwCompany;
+import com.fs.qw.service.IQwCompanyService;
 import com.fs.qw.vo.CompanyUserQwVO;
 import com.fs.qw.vo.QwUserVO;
 import com.fs.system.service.ISysConfigService;
@@ -40,6 +45,8 @@ import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
+import java.net.URLEncoder;
+import java.nio.charset.StandardCharsets;
 import java.util.*;
 import java.util.stream.Collectors;
 
@@ -73,6 +80,12 @@ public class CompanyUserController extends BaseController
     private ISysConfigService configService;
     @Autowired
     private RedisCache redisCache;
+
+    @Autowired
+    IQwCompanyService iQwCompanyService;
+
+    private static final String appLink = "https://jump.ylrztop.com/jumpapp/pages/index/index?link=";
+
     /**
      * 获取用户列表
      */
@@ -203,6 +216,53 @@ public class CompanyUserController extends BaseController
         return toAjax(companyUserService.insertUser(user));
     }
 
+    /**
+    * 生成创建销售的二维码
+    */
+
+    @PreAuthorize("@ss.hasPermi('company:user:addCodeUrl')")
+    @Log(title = "生成创建销售的二维码", businessType = BusinessType.INSERT)
+    @PostMapping("/addCodeUrl")
+    public R addCodeUrl( @RequestBody CompanyUserCodeParam user) throws Exception {
+
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        Long companyId = loginUser.getCompany().getCompanyId();
+
+        QwCompany qwCompany = iQwCompanyService.getQwCompanyByRedis(user.getCorpId());
+        if (qwCompany == null || qwCompany.getCorpId() == null){
+            return R.error("企业信息不存在");
+        }
+
+        //部门
+        Long deptId = user.getDeptId();
+
+        //角色组
+        Long[] roleIds = user.getRoleIds();
+
+        //区域
+        String addressId = user.getAddressId();
+
+
+        Map<String, Object> stateMap = new HashMap<>();
+        stateMap.put("companyId", companyId);
+        stateMap.put("deptId", deptId);
+        stateMap.put("roleIds", roleIds); // 数组可以直接存放
+        stateMap.put("addressId", addressId);
+
+        // 使用JSON库将Map转换为字符串
+        ObjectMapper objectMapper = new ObjectMapper();
+        String status = objectMapper.writeValueAsString(stateMap);
+
+        // 如果要将status作为URL参数传递,记得进行URL编码!
+        String encodedStatus = URLEncoder.encode(status, StandardCharsets.UTF_8.toString());
+
+        String url="https://open.weixin.qq.com/connect/oauth2/authorize?appid="+user.getCorpId()+"&redirect_uri=" +
+                "http://"+qwCompany.getRealmNameUrl()+"/qwh5/pages/user/index?corpId="+user.getCorpId() +
+                "&response_type=code&scope=snsapi_base&state="+encodedStatus+"&agentid="+qwCompany.getServerAgentId()+"#wechat_redirect";
+
+        R andUpload = QRCodeUtils.createAndUpload(url);
+        return  R.ok().put("data",andUpload);
+    }
     /**
      * 修改用户
      */

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

@@ -326,4 +326,17 @@ public class QwSopController extends BaseController
         Long companyId = loginUser.getCompany().getCompanyId();
         return R.ok().put("data", companySopRoleService.getRolesByCompanyId(companyId));
     }
+
+    /**
+     * 查询所有企微sop下拉列表
+     */
+    @GetMapping("/info")
+    public TableDataInfo sopInfo(QwSop qwSop)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        qwSop.setCompanyId(loginUser.getCompany().getCompanyId());
+        List<QwSop> qwSops = qwSopService.selectAllQwSopInfo(qwSop);
+        return getDataTable(qwSops);
+    }
 }

+ 19 - 2
fs-qw-task/src/main/java/com/fs/app/controller/CommonController.java

@@ -13,7 +13,10 @@ import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
 import com.fs.course.service.*;
 import com.fs.his.domain.FsUser;
 import com.fs.his.service.IFsInquiryOrderService;
+import com.fs.his.utils.qrcode.QRCodeUtils;
+import com.fs.qw.domain.QwCompany;
 import com.fs.qw.mapper.QwExternalContactMapper;
+import com.fs.qw.service.IQwCompanyService;
 import com.fs.qw.service.IQwExternalContactService;
 import com.fs.qw.service.IQwMaterialService;
 import com.fs.qwApi.service.QwApiService;
@@ -225,10 +228,24 @@ public class CommonController {
         return R.ok();
     }
 
+    @Autowired
+    IQwCompanyService iQwCompanyService;
     @GetMapping("/testSop2")
     public R testSop2() throws Exception {
-//        qwSopLogsService.createCorpMassSending();
-        return R.ok();
+
+        String cropId="ww401085d7b785aae8";
+
+        QwCompany qwCompany = iQwCompanyService.getQwCompanyByRedis(cropId);
+
+        String status="100_asddas_6666";
+
+        String url="https://open.weixin.qq.com/connect/oauth2/authorize?appid="+cropId+"&redirect_uri=" +
+                "http://"+qwCompany.getRealmNameUrl()+"/qwh5/pages/user/index?corpId="+cropId +
+                "&response_type=code&scope=snsapi_base&state="+status+"&agentid="+qwCompany.getServerAgentId()+"#wechat_redirect";
+
+        R andUpload = QRCodeUtils.createAndUpload(url);
+
+        return R.ok().put("data",andUpload);
     }
 
     @Autowired

+ 15 - 1
fs-qwhook/src/main/java/com/fs/app/controller/QwUserController.java

@@ -1,9 +1,13 @@
 package com.fs.app.controller;
 
 import com.fs.common.BeanCopyUtils;
+import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.R;
+import com.fs.common.enums.BusinessType;
 import com.fs.common.exception.CustomException;
+import com.fs.company.param.CompanyUserCodeParam;
+import com.fs.company.service.ICompanyUserService;
 import com.fs.qw.domain.QwExternalContactInfo;
 import com.fs.qw.domain.QwTagGroup;
 import com.fs.qw.param.ExternalContactDetailsParam;
@@ -51,6 +55,9 @@ public class QwUserController extends BaseController {
     @Autowired
     private ISopUserLogsInfoService iSopUserLogsInfoService;
 
+    @Autowired
+    private ICompanyUserService companyUserService;
+
     @GetMapping("/details")
     @ApiOperation("会员看课详情")
     public R getUserDetails(@ApiParam(value = "外部联系人id", required = true) @RequestParam Long contactId,
@@ -150,5 +157,12 @@ public class QwUserController extends BaseController {
         return R.error();
     }
 
-
+    /**
+    * 注册或者绑定销售
+    */
+    @PostMapping("/registerCompany")
+    @Log(title = "注册或者绑定销售", businessType = BusinessType.INSERT)
+    public R registerCompany(@RequestBody CompanyUserCodeParam userCodeParam) {
+        return companyUserService.registerCompany(userCodeParam);
+    }
 }

+ 131 - 0
fs-service/src/main/java/com/fs/company/param/CompanyUserCodeParam.java

@@ -0,0 +1,131 @@
+package com.fs.company.param;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.company.domain.CompanyDept;
+import com.fs.company.domain.CompanyPost;
+import com.fs.company.domain.CompanyRole;
+import lombok.Data;
+
+import java.util.Date;
+import java.util.List;
+
+@Data
+public class CompanyUserCodeParam {
+
+    private static final long serialVersionUID = 1L;
+
+
+    /**
+    * 注册类型 1、新注册 2、已有销售账号-绑定
+    */
+    private Integer bindType;
+
+    /** 用户ID */
+    private Long userId;
+
+    /** 公司ID */
+    @Excel(name = "公司ID")
+    private Long companyId;
+
+    /** 公司名称 */
+    private String companyName;
+
+
+    private String corpId;
+
+    /** 归属部门ID */
+    @Excel(name = "部门ID")
+    private Long deptId;
+
+    /** 用户账号 */
+    @Excel(name = "用户账号")
+    private String userName;
+
+    /** 用户昵称 */
+    @Excel(name = "用户昵称")
+    private String nickName;
+
+    /** 用户类型(00系统用户) */
+    @Excel(name = "用户类型", readConverterExp = "0=0系统用户")
+    private String userType;
+
+    /** 手机号码 */
+    @Excel(name = "手机号码")
+    private String phonenumber;
+    /** 岗位 */
+    private List<CompanyPost> posts;
+
+    /** 用户性别(0男 1女 2未知) */
+    @Excel(name = "用户性别", readConverterExp = "0=男,1=女,2=未知")
+    private String sex;
+
+    /** 头像地址 */
+    @Excel(name = "头像地址")
+    private String avatar;
+
+
+    /** 密码 */
+    @Excel(name = "密码")
+    private String password;
+
+    /** 帐号状态(0正常 1停用) */
+    @Excel(name = "帐号状态", readConverterExp = "0=正常,1=停用")
+    private String status;
+
+    /** 删除标志(0代表存在 2代表删除) */
+    private String delFlag;
+
+    private String qwUserId;
+
+    private String qwUserIdByStr;
+
+    private Integer qwStatus;
+
+
+    /** 是否删除 */
+    @Excel(name = "是否删除")
+    private Integer isDel;
+
+    private String openId;
+
+
+    private CompanyDept dept;
+
+    /** 角色对象 */
+    private List<CompanyRole> roles;
+
+    /** 角色组 */
+    private Long[] roleIds;
+
+    /** 岗位组 */
+    private Long[] postIds;
+
+    private String postName;
+
+    private String deptName;
+
+    private String addressId;
+
+    //绑定二维码
+    private String bindCode;
+
+    /** 看课域名 */
+    private String domain;
+
+    /** 是否审核 */
+    private Integer isAudit;
+
+    /** 用户上级id */
+    private Long parentId;
+
+    /** 微信小程序OPENID(如果有小程序授权) */
+    private String  maOpenId;
+
+    /** 是否需要单独注册会员,1-是,0-否(用于个微销售分享看课) */
+    private Integer isNeedRegisterMember;
+
+    /** 是否允许所有方式注册会员,1-是,0-否,默认1(用于个微注册会员) */
+    private Integer isAllowedAllRegister;
+
+}

+ 2 - 0
fs-service/src/main/java/com/fs/company/service/ICompanyUserService.java

@@ -3,6 +3,7 @@ package com.fs.company.service;
 import com.fs.common.core.domain.R;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.param.CompanyUserAreaParam;
+import com.fs.company.param.CompanyUserCodeParam;
 import com.fs.company.param.CompanyUserQwParam;
 import com.fs.company.vo.*;
 import com.fs.his.vo.CitysAreaVO;
@@ -93,6 +94,7 @@ public interface ICompanyUserService {
     int checkUserName(String userName);
 
     public int insertUser(CompanyUser user);
+    public R registerCompany(CompanyUserCodeParam userCodeParam);
     public int updateUser(CompanyUser user);
 
 

+ 5 - 3
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -109,9 +109,11 @@ public class CompanyServiceImpl implements ICompanyService
     @Override
     public Company selectCompanyById(Long companyId){
         Company company = companyMapper.selectCompanyById(companyId);
-        List<CompanyMiniapp> miniApp = companyMiniappService.getMiniAppListByCompanyList(Collections.singletonList(company.getCompanyId()));
-        company.setMiniAppMaster(GET_MINI_APP_STR.apply(0, miniApp));
-        company.setMiniAppServer(GET_MINI_APP_STR.apply(1, miniApp));
+        if(company != null) {
+            List<CompanyMiniapp> miniApp = companyMiniappService.getMiniAppListByCompanyList(Collections.singletonList(company.getCompanyId()));
+            company.setMiniAppMaster(GET_MINI_APP_STR.apply(0, miniApp));
+            company.setMiniAppServer(GET_MINI_APP_STR.apply(1, miniApp));
+        }
         return company;
     }
 

+ 121 - 2
fs-service/src/main/java/com/fs/company/service/impl/CompanyUserServiceImpl.java

@@ -2,18 +2,23 @@ package com.fs.company.service.impl;
 
 import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
+import com.fs.common.BeanCopyUtils;
 import com.fs.common.annotation.DataScope;
+import com.fs.common.constant.UserConstants;
+import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
+import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.CustomException;
 import com.fs.common.exception.ServiceException;
 import com.fs.common.exception.file.OssException;
-import com.fs.common.utils.DateUtils;
-import com.fs.common.utils.StringUtils;
+import com.fs.common.utils.*;
 import com.fs.company.domain.*;
 import com.fs.company.mapper.*;
 import com.fs.company.param.CompanyUserAreaParam;
+import com.fs.company.param.CompanyUserCodeParam;
 import com.fs.company.param.CompanyUserQwParam;
+import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.company.vo.*;
 import com.fs.course.service.IFsUserCompanyUserService;
@@ -21,8 +26,11 @@ import com.fs.his.mapper.FsUserMapper;
 import com.fs.his.service.IFsCityService;
 import com.fs.his.vo.CitysAreaVO;
 import com.fs.his.vo.OptionsVO;
+import com.fs.qw.domain.QwUser;
 import com.fs.qw.dto.UserProjectDTO;
 import com.fs.qw.mapper.QwUserMapper;
+import com.fs.qw.service.IQwExternalContactService;
+import com.fs.qw.service.IQwUserService;
 import com.fs.qw.vo.CompanyUserQwVO;
 import com.fs.qw.vo.QwOptionsVO;
 import com.fs.qw.vo.QwUserVO;
@@ -83,6 +91,18 @@ public class CompanyUserServiceImpl implements ICompanyUserService
     @Autowired
     private IFsUserCompanyUserService userCompanyUserService;
 
+    @Autowired
+    private ICompanyUserService companyUserService;
+
+    @Autowired
+    private ICompanyService companyService;
+
+    @Autowired
+    private IQwExternalContactService qwExternalContactService;
+
+    @Autowired
+    private IQwUserService qwUserService;
+
     /**
      * 查询物业公司管理员信息
      *
@@ -264,6 +284,105 @@ public class CompanyUserServiceImpl implements ICompanyUserService
         return rows;
     }
 
+    @Override
+    public R registerCompany(CompanyUserCodeParam userCodeParam) {
+        CompanyUser user=new CompanyUser();
+
+        QwUser qwUser = qwUserMapper.selectQwUserByCorpIdAndUserId(userCodeParam.getCorpId(),userCodeParam.getQwUserIdByStr());
+
+        if (qwUser==null){
+            return R.error("无企微员工信息,无法处理,请先同步员工:"+ userCodeParam.getQwUserIdByStr()+"--"+userCodeParam.getCorpId());
+        }
+
+        if (qwUser.getCompanyUserId()!=null&&!qwUser.getCompanyUserId().equals(qwUser.getCompanyUserId())){
+            return R.error( qwUser.getQwUserName()+"已经被其他人绑定,请先解绑");
+        }
+
+        if (userCodeParam.getBindType()==1){
+
+            if (!PatternUtils.checkPassword(userCodeParam.getPassword())) {
+                return R.error("密码格式不正确,需包含字母、数字和特殊字符,长度为 8-20 位");
+            }
+
+            //判断用户数量是否已达到上线
+            Integer count=companyUserService.selectCompanyUserCountByCompanyId(userCodeParam.getCompanyId());
+            Company company=companyService.selectCompanyById(userCodeParam.getCompanyId());
+
+            if(count>company.getLimitUserCount()){
+                return R.error("用户数量已达到上限");
+            }
+
+            if (UserConstants.NOT_UNIQUE.equals(String.valueOf(companyUserService.checkUserName(userCodeParam.getUserName()))))
+            {
+                return R.error("新增用户'" + userCodeParam.getUserName() + "'失败,登录账号已存在");
+            }
+            user.setNickName(userCodeParam.getNickName());
+            user.setUserName(userCodeParam.getUserName());
+            user.setSex(userCodeParam.getSex());
+            user.setStatus("1");
+            user.setDept(userCodeParam.getDept());
+            user.setRoleIds(userCodeParam.getRoleIds());
+            user.setAddressId(userCodeParam.getAddressId());
+            user.setCompanyId(userCodeParam.getCompanyId());
+            user.setCreateBy(userCodeParam.getQwUserId());
+            user.setPassword(SecurityUtils.encryptPassword(userCodeParam.getPassword()));
+            user.setCreateTime(new Date());
+            user.setUserType("01");//一般用户
+            user.setIsAudit(1);
+
+            int i = companyUserService.insertUser(user);
+            if (i>0){
+
+                CompanyUser companyUser = companyUserMapper.selectUserByUserName(userCodeParam.getUserName());
+
+                bindQwUserToCompanyUser(qwUser, companyUser);
+
+            }else {
+                return R.error("销售创建失败");
+            }
+
+            return R.ok();
+
+        }
+        else if (userCodeParam.getBindType()==2){
+
+            // 绑定到现有用户
+            CompanyUser companyUser = companyUserMapper.selectUserByUserName(userCodeParam.getUserName());
+            if (companyUser == null) {
+                return R.error("指定的用户不存在");
+            }
+
+            bindQwUserToCompanyUser(qwUser, companyUser);
+        }
+
+        return R.error();
+    }
+
+    private void bindQwUserToCompanyUser(QwUser qwUser, CompanyUser companyUser) {
+        // 更新公司用户的企微ID列表
+        Set<String> qwUserIdSet = new HashSet<>();
+
+        if (!StringUtil.strIsNullOrEmpty(companyUser.getQwUserId())) {
+            String[] existingIds = companyUser.getQwUserId().split(",");
+            qwUserIdSet.addAll(Arrays.stream(existingIds)
+                    .filter(id -> !id.isEmpty())
+                    .collect(Collectors.toSet()));
+        }
+
+        qwUserIdSet.add(String.valueOf(qwUser.getId()));
+        companyUser.setQwUserId(String.join(",", qwUserIdSet));
+        companyUser.setQwStatus(1);
+        companyUserMapper.updateCompanyUser(companyUser);
+
+        // 更新企微用户信息
+        QwUser qw = new QwUser();
+        qw.setCompanyUserId(companyUser.getUserId());
+        qw.setId(qwUser.getId());
+        qw.setStatus(1);
+        qw.setCompanyId(companyUser.getCompanyId());
+        qwUserService.updateQwUser(qw);
+    }
+
     /**
      * 修改保存用户信息
      *

+ 5 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseWatchLogListParam.java

@@ -80,4 +80,9 @@ public class FsCourseWatchLogListParam implements Serializable {
      * 所属项目
      */
     private Integer project;
+
+    /**
+     * sop主键id
+     */
+    private String sopId;
 }

+ 3 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -2517,6 +2517,9 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         FsUserCourseVideo courseVideo = fsUserCourseVideoMapper.selectFsUserCourseVideoByVideoIdAndUserId(videoId,userId);
 
         BeanCopyUtils.copy(courseVideo,fsUserCourseVideoQVO);
+        if(courseVideo != null && courseVideo.getRedPacketMoney() != null){
+            fsUserCourseVideoQVO.setRedPacketMoney(courseVideo.getRedPacketMoney().toString());
+        }
 
         if (StringUtils.isNotEmpty(courseVideo.getQuestionBankId())){
             List<FsCourseQuestionBank> fsCourseQuestionBanks = courseQuestionBankMapper.selectFsCourseQuestionBankByIdVO(courseVideo.getQuestionBankId().split(","));

+ 2 - 0
fs-service/src/main/java/com/fs/course/vo/FsCourseWatchLogStatisticsListVO.java

@@ -30,6 +30,8 @@ public class FsCourseWatchLogStatisticsListVO {
     private String type3;
     @Excel(name = "看课中断")
     private String type4;
+    @Excel(name = "上线数")
+    private Long onLineNum;
     @Excel(name = "企业微信员工名称")
     private String qwUserName;
 

+ 2 - 2
fs-service/src/main/java/com/fs/erp/service/impl/DfOrderServiceImpl.java

@@ -474,8 +474,8 @@ public class DfOrderServiceImpl implements IErpOrderService
             orderPayMethod = 1;
         } else { // 如果是线上付款
             orderPayMethod = 2;
-            // 货到付款金额 = 物流代收金额-优惠金额
-            vo.setCollectingMoney(fsStoreOrder.getPayDelivery().subtract(couponPrice).doubleValue());
+            // 货到付款金额 = 订单剩余支付金额
+            vo.setCollectingMoney(fsStoreOrder.getPayRemain().doubleValue());
             vo.setCollectionCardNumber(config.getMonthlyCard()); // 就是月结账号
         }
         //订单付款方式 1:在线支付 2:货到付款

+ 34 - 0
fs-service/src/main/java/com/fs/fastGpt/service/impl/AiHookServiceImpl.java

@@ -450,6 +450,12 @@ public class AiHookServiceImpl implements AiHookService {
             if(!contentEmj.contains("表情包")){
                 if(!contentEmj.isEmpty()){
                     addSaveAiMsg(1,1,contentEmj,user,fastGptChatSession.getSessionId(),role.getRoleId(),qwExternalContacts,fastGptChatSession.getUserId(),null,null,null);
+                    //通过用户发送的对话去查询用户是否为新客,是就删除sop,否就不做处理
+                    cleanNewUserDialogue(user, qwExternalContacts);
+                    //用户是未回复状态
+                    if(qwExternalContacts.getIsReply() == 0){
+                        qwExternalContactMapper.updateQwExternalContactIsRePlyById(qwExternalContacts.getId());
+                    }
                 }else {
                     contentEmj ="用户发送表情:"+qwContent;
                     if (type==16){
@@ -592,6 +598,34 @@ public class AiHookServiceImpl implements AiHookService {
         return R.ok();
     }
 
+    /**
+     * 通过用户发送的对话去查询用户是否为新客,是就删除sop,否就不做处理
+     * @param user
+     * @param qwExternalContacts
+     */
+    private void cleanNewUserDialogue(QwUser user, QwExternalContact qwExternalContacts) {
+        String redisKey = "qwNewChat:" + user.getQwUserId() + ":" + user.getCorpId() + ":" + qwExternalContacts.getExternalUserId();
+        String key  = (String) redisCache.getCacheObject(redisKey);
+        if(!StringUtil.strIsNullOrEmpty(key)){
+            try {
+                QwSopLogs qwSopLogs = new QwSopLogs();
+                qwSopLogs.setQwUserid(user.getQwUserId());
+                qwSopLogs.setCorpId(user.getCorpId());
+                qwSopLogs.setExternalUserId(qwExternalContacts.getExternalUserId());
+                qwSopLogs.setSendStatus(3L);
+                qwSopLogs.setSendType(4);
+                List<QwSopLogs> qwSopLogsList = qwSopLogsMapper.selectQwSopLogsList(qwSopLogs);
+                if(qwSopLogsList != null && !qwSopLogsList.isEmpty()){
+                    qwSopLogsMapper.batchUpdateQwSopLogsNewUserById(qwSopLogsList);
+                }
+            } catch (Exception e) {
+                log.error("停用新客对话sop失败:" + redisKey + "原因:" + e);
+            }finally {
+                redisCache.deleteObject(redisKey);
+            }
+        }
+    }
+
 
 
     /**

+ 12 - 0
fs-service/src/main/java/com/fs/his/dto/FindUsersByDTO.java

@@ -0,0 +1,12 @@
+package com.fs.his.dto;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class FindUsersByDTO implements Serializable {
+    private Long id;
+    private Long type;
+    private String name;
+}

+ 1 - 0
fs-service/src/main/java/com/fs/his/mapper/FsAppVersionMapper.java

@@ -66,6 +66,7 @@ public interface FsAppVersionMapper
 
     @Select("select * from fs_app_version where type=#{type} and app_type=3 order by version_code desc limit 1")
     FsAppVersion getNewUserAppVersion(Integer type);
+
     @Select("select * from fs_app_version where type=#{type} and app_type=#{appType} order by version_code desc limit 1")
     FsAppVersion getNewVersion1(@Param("appType")Integer appType, @Param("type") int type);
 }

+ 19 - 0
fs-service/src/main/java/com/fs/his/mapper/FsFirstDiagnosisMapper.java

@@ -6,7 +6,9 @@ import com.fs.course.param.FsFirstDiagnosisListUParam;
 import com.fs.course.vo.FsFirstDiagnosisListUVO;
 import com.fs.his.domain.FsFirstDiagnosis;
 import com.fs.his.param.FsDiagnosisListDParam;
+import com.fs.his.param.FsFirstDiagnosisParam;
 import com.fs.his.vo.FsDiagnosisListDVO;
+import com.fs.his.vo.FsFirstDiagnosisVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 
@@ -100,4 +102,21 @@ public interface FsFirstDiagnosisMapper extends BaseMapper<FsFirstDiagnosis>{
             "WHERE fd.id = #{id}" +
             "</script>"})
     FsFirstDiagnosisListUVO getInfo(Long id);
+
+    @Select({"<script>" +
+            "SELECT fd.*,u.nick_name userName,qu.qw_user_name FROM fs_first_diagnosis fd  " +
+            " LEFT JOIN fs_user u ON u.user_id = fd.user_id " +
+            " LEFT JOIN qw_user qu ON qu.id = fd.qw_user_id " +
+            " WHERE 1 = 1 " +
+            "<if test='param.doctorStatus != null'> AND fd.doctor_status = #{param.doctorStatus} </if>" +
+            "<if test='param.userStatus != null'> AND fd.user_status = #{param.userStatus} </if>" +
+            "<if test='param.doctorCertificate != null'> AND fd.doctor_certificate = #{param.doctorCertificate} </if>" +
+            "<if test='param.phone != null'> AND fd.phone = like concat('%',#{param.phone},'%') </if>" +
+            "<if test='param.patientName != null'> and fd.patient_name like concat('%',#{param.patientName},'%') </if>" +
+            "<if test='param.doctorName != null'> and fd.doctor_name like concat('%',#{param.doctorName},'%') </if>" +
+            "<if test='param.userName != null'> and u.nick_name  like concat('%',#{param.userName},'%') </if>" +
+            "<if test='param.qwUserName != null'> and qu.qw_user_name like concat('%',#{param.qwUserName},'%') </if>" +
+            " ORDER BY fd.create_time desc " +
+            "</script>"})
+    List<FsFirstDiagnosisVO> selectFsFirstDiagnosisVOList(@Param("param") FsFirstDiagnosisParam param);
 }

+ 4 - 0
fs-service/src/main/java/com/fs/his/mapper/FsUserMapper.java

@@ -8,6 +8,8 @@ import com.fs.course.domain.FsUserWatchStatistics;
 import com.fs.course.param.CourseAnalysisParam;
 import com.fs.course.vo.newfs.FsCourseAnalysisCountVO;
 import com.fs.his.domain.FsUser;
+import com.fs.his.dto.FindUsersByDTO;
+import com.fs.his.param.FindUserByParam;
 import com.fs.his.param.FsUserParam;
 import com.fs.his.vo.FsUserVO;
 import com.fs.his.vo.FsUserExportListVO;
@@ -395,4 +397,6 @@ public interface FsUserMapper
     List<FsCompanyUserListQueryVO> selectFsCompanyUserListQuery(@Param("maps")FsUser fsUser);
 
     List<FsUser> selectFsUserListLimit(FsUser fsUser);
+
+    List<FindUsersByDTO> findUsersByParam(FindUserByParam param);
 }

+ 12 - 0
fs-service/src/main/java/com/fs/his/param/FindUserByParam.java

@@ -0,0 +1,12 @@
+package com.fs.his.param;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class FindUserByParam implements Serializable {
+    private Integer pageNum;
+    private Integer pageSize;
+    private String keywords;
+}

+ 25 - 0
fs-service/src/main/java/com/fs/his/param/FsFirstDiagnosisParam.java

@@ -0,0 +1,25 @@
+package com.fs.his.param;
+
+import lombok.Data;
+
+@Data
+public class FsFirstDiagnosisParam extends BaseParam{
+
+    private String patientName;
+
+    private String phone;
+
+    private String doctorName;
+
+    private String qwUserName;
+
+    private String userName;
+
+    private String doctorCertificate;
+
+    /** 医生是否填写:0-未填写 1-已填写 */
+    private Integer doctorStatus;
+
+    /** 用户是否答复:0-未答复 1-已答复 */
+    private Integer userStatus;
+}

+ 4 - 1
fs-service/src/main/java/com/fs/his/service/IFsFirstDiagnosisService.java

@@ -8,8 +8,9 @@ import com.fs.course.vo.FsFirstDiagnosisListUVO;
 import com.fs.his.domain.FsFirstDiagnosis;
 import com.fs.his.param.FsDiagnosisFillDParam;
 import com.fs.his.param.FsDiagnosisListDParam;
+import com.fs.his.param.FsFirstDiagnosisParam;
 import com.fs.his.vo.FsDiagnosisListDVO;
-import org.apache.ibatis.annotations.Param;
+import com.fs.his.vo.FsFirstDiagnosisVO;
 
 /**
  * 初诊单Service接口
@@ -34,6 +35,8 @@ public interface IFsFirstDiagnosisService extends IService<FsFirstDiagnosis>{
      */
     List<FsFirstDiagnosis> selectFsFirstDiagnosisList(FsFirstDiagnosis fsFirstDiagnosis);
 
+    List<FsFirstDiagnosisVO> selectFsFirstDiagnosisVOList(FsFirstDiagnosisParam param);
+
     /**
      * 新增初诊单
      * 

+ 8 - 0
fs-service/src/main/java/com/fs/his/service/IFsUserService.java

@@ -12,6 +12,8 @@ import com.fs.course.param.newfs.FsUserCourseBeMemberParam;
 import com.fs.course.vo.newfs.FsCourseAnalysisVO;
 import com.fs.his.domain.FsUser;
 import com.fs.his.domain.FsUserAddress;
+import com.fs.his.dto.FindUsersByDTO;
+import com.fs.his.param.FindUserByParam;
 import com.fs.his.param.FsUserParam;
 import com.fs.his.vo.FsUserVO;
 import com.fs.his.vo.FsUserExportListVO;
@@ -46,6 +48,12 @@ public interface IFsUserService
      */
     public FsUser selectFsUserByUserId(Long userId);
 
+    /**
+     * 通过参数查找用户
+     * @param param 参数
+     * @return 用户列表
+     */
+    public List<FindUsersByDTO> findUserByParam(FindUserByParam param);
     /**
      * 查询用户列表
      *

+ 8 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsFirstDiagnosisServiceImpl.java

@@ -1,5 +1,6 @@
 package com.fs.his.service.impl;
 
+import java.util.Collections;
 import java.util.List;
 
 import com.fs.common.core.domain.R;
@@ -11,8 +12,10 @@ import com.fs.course.vo.FsFirstDiagnosisListUVO;
 import com.fs.his.domain.FsDoctor;
 import com.fs.his.param.FsDiagnosisFillDParam;
 import com.fs.his.param.FsDiagnosisListDParam;
+import com.fs.his.param.FsFirstDiagnosisParam;
 import com.fs.his.service.IFsDoctorService;
 import com.fs.his.vo.FsDiagnosisListDVO;
+import com.fs.his.vo.FsFirstDiagnosisVO;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
@@ -61,6 +64,11 @@ public class FsFirstDiagnosisServiceImpl extends ServiceImpl<FsFirstDiagnosisMap
         return baseMapper.selectFsFirstDiagnosisList(fsFirstDiagnosis);
     }
 
+    @Override
+    public List<FsFirstDiagnosisVO> selectFsFirstDiagnosisVOList(FsFirstDiagnosisParam param) {
+        return diagnosisMapper.selectFsFirstDiagnosisVOList(param);
+    }
+
     /**
      * 新增初诊单
      * 

+ 7 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsUserServiceImpl.java

@@ -40,8 +40,10 @@ import com.fs.his.config.IntegralConfig;
 import com.fs.his.domain.*;
 import com.fs.his.domain.FsUserAddress;
 import com.fs.his.domain.FsUserIntegralLogs;
+import com.fs.his.dto.FindUsersByDTO;
 import com.fs.his.enums.FsUserIntegralLogTypeEnum;
 import com.fs.his.mapper.*;
+import com.fs.his.param.FindUserByParam;
 import com.fs.his.param.FsUserAddIntegralTemplateParam;
 import com.fs.his.param.FsUserParam;
 import com.fs.his.service.IFsUserIntegralLogsService;
@@ -175,6 +177,11 @@ public class FsUserServiceImpl implements IFsUserService
         return fsUserMapper.selectFsUserByUserId(userId);
     }
 
+    @Override
+    public List<FindUsersByDTO> findUserByParam(FindUserByParam param) {
+        return fsUserMapper.findUsersByParam(param);
+    }
+
     /**
      * 查询用户列表
      *

+ 23 - 0
fs-service/src/main/java/com/fs/his/utils/qrcode/QRCodeUtils.java

@@ -1,5 +1,8 @@
 package com.fs.his.utils.qrcode;
 
+import com.fs.common.core.domain.R;
+import com.fs.system.oss.CloudStorageService;
+import com.fs.system.oss.OSSFactory;
 import com.google.zxing.*;
 import com.google.zxing.client.j2se.BufferedImageLuminanceSource;
 import com.google.zxing.client.j2se.MatrixToImageConfig;
@@ -94,6 +97,26 @@ public class QRCodeUtils {
         MatrixToImageWriter.writeToPath(bitMatrix, FORMAT, path);
     }
 
+    /**
+     * @title 生成二维码图片
+     * @description 根据文本内容生成默认格式的二维图图片 并直接上传云端 返回一张图片
+     * @param text 文本内容
+     */
+    public static R createAndUpload(String text) throws Exception {
+        // 生成二维码字节数据
+        BitMatrix bitMatrix = createBitMatrix(text, BarcodeFormat.QR_CODE, QRCODE_WIDTH, QRCODE_HEIGHT, ENCODE_HINTS);
+        ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
+        MatrixToImageWriter.writeToStream(bitMatrix, FORMAT, outputStream);
+        byte[] qrCodeBytes = outputStream.toByteArray();
+
+        // 直接使用字节数组上传
+        String fileName = "qrcode_" + System.currentTimeMillis() + "." + FORMAT.toLowerCase();
+        CloudStorageService storage = OSSFactory.build();
+        String url = storage.upload(qrCodeBytes, fileName);
+
+        return R.ok().put("url", url);
+    }
+
     /**
      * @title 生成二维码图片
      * @description 根据文本内容生成二维码图片的字节输出流

+ 81 - 0
fs-service/src/main/java/com/fs/his/vo/FsFirstDiagnosisVO.java

@@ -0,0 +1,81 @@
+package com.fs.his.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.util.Date;
+
+@Data
+public class FsFirstDiagnosisVO {
+    /** $column.columnComment */
+    private Long id;
+
+    /** 患者姓名 */
+    @Excel(name = "患者姓名")
+    private String patientName;
+
+    /** 年龄 */
+    @Excel(name = "年龄")
+    private String age;
+
+    /** 0-未知 1-男性 2-女性 */
+    @Excel(name = "性别",readConverterExp = "0=未知,1=男,2=女")
+    private Long gender;
+
+    /** 电话 */
+    @Excel(name = "电话")
+    private String phone;
+
+    /** 身体状况 */
+    @Excel(name = "身体状况")
+    private String physicalCondition;
+
+    /** 日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "日期", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date dateTime;
+
+    /** 出版诊断 */
+    @Excel(name = "出版诊断")
+    private String firstDiagnosis;
+
+    /** 医生id */
+    @Excel(name = "医生id")
+    private Long doctorId;
+
+    /** 医生名称 */
+    @Excel(name = "医生名称")
+    private String doctorName;
+
+    /** 职称 */
+    @Excel(name = "职称")
+    private String doctorDep;
+
+    /** 用户id */
+    @Excel(name = "用户id")
+    private Long userId;
+
+    /** 医生证号 */
+    @Excel(name = "医生证号")
+    private String doctorCertificate;
+
+    /** 医生是否填写:0-未填写 1-已填写 */
+    @Excel(name = "")
+    private Integer doctorStatus;
+
+    /** 用户是否答复:0-未答复 1-已答复 */
+    private Integer userStatus;
+
+    /**
+     * 销售id
+     */
+    private Long qwUserId;
+
+    /**
+     * 销售名称
+     */
+    private String qwUserName;
+
+    private String userName;
+}

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

@@ -740,7 +740,7 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService
             if (flag==0) {
                 return R.error("订单创建失败");
             }
-            if (!isPay){
+            if (!isPay && storeOrder.getCompanyId()!=null){
                 // 添加订单审核
                 addOrderAudit(storeOrder);
             }

+ 4 - 2
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreProductScrmServiceImpl.java

@@ -167,8 +167,10 @@ public class FsStoreProductScrmServiceImpl implements IFsStoreProductScrmService
                 String[] split = item.getCompanyIds().split(",");
 
                 for (String companyId : split) {
-                    String companyName = companyCacheService.selectCompanyNameById(Long.valueOf(companyId));
-                    companyNameList.add(companyName);
+                    if(StringUtils.isNotBlank(companyId)) {
+                        String companyName = companyCacheService.selectCompanyNameById(Long.valueOf(companyId));
+                        companyNameList.add(companyName);
+                    }
                 }
                 item.setCompanyName(String.join(",",companyNameList));
             }

+ 3 - 0
fs-service/src/main/java/com/fs/qw/domain/QwExternalContact.java

@@ -145,4 +145,7 @@ public class QwExternalContact extends BaseEntity
     // 是否已购0 否 1程序内下单 2程序外下单
     private Integer payOrder;
 
+    //用户是否回复  0未回复  1已回复
+    private Integer isReply;
+
 }

+ 2 - 0
fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java

@@ -436,4 +436,6 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
     List<QwUserDelLossLogVO> selectQwUserDelLossList(@Param("param") QwUserDelLossLogParam param);
 
     List<QwExternalContact> selectQwExternalContactByFsUserIdAndCompany(@Param("userId")Long userId,@Param("companyUserId") Long companyUserId);
+
+    void updateQwExternalContactIsRePlyById(@Param("id")Long id);
 }

+ 3 - 0
fs-service/src/main/java/com/fs/sop/mapper/QwSopLogsMapper.java

@@ -329,4 +329,7 @@ public interface QwSopLogsMapper extends BaseMapper<QwSopLogs> {
 
     @DataSource(DataSourceType.SOP)
     void batchUpdateQwSopLogsById(@Param("data") List<QwSopLogs> logs);
+
+    @DataSource(DataSourceType.SOP)
+    void batchUpdateQwSopLogsNewUserById(@Param("data")List<QwSopLogs> qwSopLogsList);
 }

+ 3 - 0
fs-service/src/main/java/com/fs/sop/mapper/QwSopMapper.java

@@ -424,4 +424,7 @@ public interface QwSopMapper extends BaseMapper<QwSop> {
      * **/
     @DataSource(DataSourceType.SOP)
     List<QwSop> getQwSopInfoById(@Param("ids") Set<String> ids);
+
+    @DataSource(DataSourceType.SOP)
+    List<QwSop> selectAllQwSopInfo(QwSop qwSop);
 }

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

@@ -96,4 +96,6 @@ public interface IQwSopService
     List<QwSopTask> getQwSopTaskList(GetSOPTaskDataParam param);
 
     List<QwSop> selectQwSopByTempId(String tempId);
+
+    List<QwSop> selectAllQwSopInfo(QwSop qwSop);
 }

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

@@ -1085,6 +1085,11 @@ public class QwSopServiceImpl implements IQwSopService
         return sopUserLogs.stream().collect(Collectors.groupingBy(SopUserLogs::getSopId));
     }
 
+    @Override
+    public List<QwSop> selectAllQwSopInfo(QwSop qwSop) {
+        return qwSopMapper.selectAllQwSopInfo(qwSop);
+    }
+
     /**
      *新增员工执行SOP
      */

+ 6 - 1
fs-service/src/main/resources/application-config-druid-syysy.yml

@@ -85,4 +85,9 @@ ipad:
 wx_miniapp_temp:
   pay_order_temp_id:
   inquiry_temp_id:
-
+# 聚水潭API配置
+jst:
+  app_key: 5dea3c46f0214985b1fd43d6428b2b12 #聚水潭2025-09-03
+  app_secret: 3f382758a4f4470e80932a24912be0ce #聚水潭2025-09-0
+  authorization_code: 999999
+  shop_code: "18886784"

+ 3 - 0
fs-service/src/main/resources/mapper/course/FsCourseWatchLogMapper.xml

@@ -141,6 +141,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test ='maps.project !=null'>
                 and l.project = #{maps.project}
             </if>
+            <if test="maps.sopId != null  and maps.sopId != '' ">
+                and l.sop_id = #{maps.sopId}
+            </if>
         </where>
          order by l.finish_time desc,l.update_time desc,l.create_time desc
     </select>

+ 2 - 2
fs-service/src/main/resources/mapper/course/FsUserCourseVideoMapper.xml

@@ -363,8 +363,8 @@
         and fcpd.del_flag = 0 and video.is_del = 0 and fcp.del_flag = '0' and c.del_flag = '0'
     </select>
 
-    <select id="selectFsUserCourseVideoByVideoIdAndUserId" resultType="com.fs.course.domain.FsUserCourseVideo">
-        select *  from fs_user_course_video
+    <select id="selectFsUserCourseVideoByVideoIdAndUserId" resultMap="FsUserCourseVideoResult">
+        <include refid="selectFsUserCourseVideoVo"/>
         where video_id=#{videoId} and is_del = 0
         <if test="userId != null">
            and user_id = #{userId}

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

@@ -1891,4 +1891,42 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         order by user_id desc
         limit 10
     </select>
+    <select id="findUsersByParam" resultType="com.fs.his.dto.FindUsersByDTO">
+        SELECT user_id as id, 0 as type,
+        CONCAT(user_id, '_', IFNULL(nick_name, '')) as name
+        FROM fs_user
+        WHERE user_id = #{keywords}
+        UNION
+        <!-- fs_user 按nick_name前缀搜索 -->
+        SELECT user_id as id, 0 as type,
+        CONCAT(user_id, '_', IFNULL(nick_name, '')) as name
+        FROM fs_user
+        WHERE nick_name LIKE CONCAT(#{keywords}, '%')
+        UNION
+        <!-- fs_user 按phone精确搜索(仅当keywords是数字时) -->
+        <if test="keywords.matches('\\d+')">
+            SELECT user_id as id, 0 as type,
+            CONCAT(user_id, '_', IFNULL(nick_name, '')) as name
+            FROM fs_user
+            WHERE phone = #{keywords}
+            UNION
+        </if>
+        <!-- company_user相关查询 -->
+        SELECT user_id as id, 1 as type,
+        IFNULL(nick_name, '') as name
+        FROM company_user
+        WHERE user_id = #{keywords}
+        UNION
+        SELECT user_id as id, 1 as type,
+        IFNULL(nick_name, '') as name
+        FROM company_user
+        WHERE nick_name LIKE CONCAT(#{keywords}, '%')
+        <if test="keywords.matches('\\d+')">
+            UNION
+            SELECT user_id as id, 1 as type,
+            IFNULL(nick_name, '') as name
+            FROM company_user
+            WHERE phonenumber = #{keywords}
+        </if>
+    </select>
 </mapper>

+ 3 - 0
fs-service/src/main/resources/mapper/qw/QwExternalContactMapper.xml

@@ -599,6 +599,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             #{id}
         </foreach>
     </update>
+    <update id="updateQwExternalContactIsRePlyById">
+        update qw_external_contact set is_reply = 1 where id = #{id}
+    </update>
 
     <select id="selectExternalByFsUserIds" resultType="QwExternalContact">
         select * from qw_external_contact

+ 10 - 0
fs-service/src/main/resources/mapper/sop/QwSopLogsMapper.xml

@@ -824,6 +824,16 @@
             #{log.id}
         </foreach>
     </update>
+    <update id="batchUpdateQwSopLogsNewUserById">
+        UPDATE  qw_sop_logs
+        SET
+        send_status = 5,
+        remark = '新客对话已回复'
+        WHERE id IN
+        <foreach collection="data" item="log" open="(" separator="," close=")">
+            #{log.id}
+        </foreach>
+    </update>
 
     <select id="selectByQwUserId" resultType="com.fs.sop.domain.QwSopLogs">
         select ql.*,

+ 33 - 5
fs-service/src/main/resources/mapper/sop/QwSopMapper.xml

@@ -425,13 +425,17 @@
         FROM sop_user_logs log
         RIGHT JOIN qw_sop ON log.sop_id = qw_sop.id
         <where>
-        <![CDATA[
+            <![CDATA[
             log.start_time <= CURDATE()
+            ]]>
             AND log.status = 1
-            AND qw_sop.status in(2,3)
-            AND qw_sop.is_auto_sop = 1 AND log.start_time >= #{startDate}
-            AND log.start_time <= #{endDate}
-         ]]>
+            AND qw_sop.status in(2,3,6)
+            <if test="startDate!=null and endDate!=null">
+                <![CDATA[
+                AND log.start_time >= #{startDate}
+                AND log.start_time <= #{endDate}
+            ]]>
+            </if>
             <if test="companyId != null">
                 AND qw_sop.company_id = ${companyId}
             </if>
@@ -541,4 +545,28 @@
     <select id="getQwSopInfoById" resultType="com.fs.sop.domain.QwSop">
         SELECT id,filter_mode,chat_id FROM qw_sop where id IN <foreach collection="ids" item="item" index="index" open="(" separator="," close=")">#{item}</foreach>
     </select>
+
+    <select id="selectAllQwSopInfo" parameterType="QwSop" resultMap="QwSopResult">
+        <include refid="selectQwSopVo"/>
+        <where>
+            <if test="name != null  and name != ''"> and name like concat('%', #{name}, '%')</if>
+            <if test="status != null "> and status = #{status}</if>
+            <if test="type != null "> and type = #{type}</if>
+            <if test="id != null "> and id = #{id}</if>
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="qwUserIds != null  and qwUserIds != ''"> and FIND_IN_SET(#{qwUserIds}, qw_user_ids) > 0</if>
+            <if test="createBy != null  and createBy != ''"> and create_by = #{createBy}</if>
+            <if test="corpId != null  and corpId != ''"> and corp_id = #{corpId}</if>
+            <if test="createTime != null "> and DATE(create_time) = #{createTime}</if>
+            <if test="sendType != null "> and send_type = #{sendType}</if>
+            <if test="expiryTime != null "> and expiry_time = #{expiryTime}</if>
+            <if test="isAutoSop != null "> and is_auto_sop = #{isAutoSop}</if>
+            <if test="autoSopTime != null "> and auto_sop_time = #{autoSopTime}</if>
+            <if test="minSend != null "> and min_send = #{minSend}</if>
+            <if test="maxSend != null "> and max_send = #{maxSend}</if>
+            <if test="stopTime != null "> and stop_time = #{stopTime}</if>
+        </where>
+        order by create_time desc
+    </select>
+
 </mapper>

+ 20 - 12
fs-service/src/main/resources/mapper/sop/SopUserLogsMapper.xml

@@ -436,23 +436,31 @@
         </where>
     </select>
     <select id="querySopUserLogsByParam" resultType="com.fs.sop.domain.SopUserLogs">
-        <![CDATA[
         SELECT DISTINCT log.id as id,
-                        log.qw_user_id as qw_user_id,
-                        log.start_time as start_time,
-                        log.sop_id as sop_id
+        log.qw_user_id as qw_user_id,
+        log.start_time as start_time,
+        log.sop_id as sop_id
         FROM sop_user_logs log
-                 LEFT JOIN qw_sop ON log.sop_id = qw_sop.id
-        WHERE log.start_time <= CURDATE()
-          AND log.status = 1
-          AND qw_sop.status in(2,3)
-          AND (
-            (qw_sop.is_auto_sop = 1 AND log.start_time >= #{startDate}
+        LEFT JOIN qw_sop ON log.sop_id = qw_sop.id
+        <where>
+            <![CDATA[
+            log.start_time <= CURDATE()
+            ]]>
+            AND log.status = 1
+            AND qw_sop.status in(2,3)
+
+            <if test="startDate != null and endDate != null">
+                AND (
+                <![CDATA[
+                log.start_time >= #{startDate}
                 AND log.start_time <= #{endDate}
+            ]]>
+
                 )
-            )
+            </if>
+
+        </where>
         ORDER BY log.start_time DESC
-        ]]>
     </select>
 
     <select id="getSopUserLogsInfoById" resultType="com.fs.sop.domain.SopUserLogs">

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

@@ -1,8 +1,10 @@
 package com.fs.app.controller;
 
 
+import cn.hutool.core.lang.Validator;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
 import com.fs.app.annotation.Login;
+import com.fs.app.param.FriendsSearchParam;
 import com.fs.app.param.FsDoctorRegisterParam;
 import com.fs.app.param.FsUserEditParam;
 import com.fs.common.core.domain.R;
@@ -14,12 +16,15 @@ import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.his.domain.FsDoctor;
 import com.fs.his.domain.FsPackage;
 import com.fs.his.domain.FsUser;
+import com.fs.his.dto.FindUsersByDTO;
+import com.fs.his.param.FindUserByParam;
 import com.fs.his.param.FsUserCouponUParam;
 import com.fs.his.param.FsUserEditPushParam;
 import com.fs.his.service.IFsDoctorService;
 import com.fs.his.service.IFsPackageService;
 import com.fs.his.service.IFsUserCouponService;
 import com.fs.his.service.IFsUserService;
+import com.fs.his.utils.PhoneUtil;
 import com.fs.his.utils.qrcode.QRCodeUtils;
 import com.fs.his.vo.FsDoctorListUVO;
 import com.fs.his.vo.FsUserCouponCountUVO;
@@ -31,12 +36,14 @@ import com.fs.system.oss.OSSFactory;
 import com.github.pagehelper.PageHelper;
 import com.github.pagehelper.PageInfo;
 import com.google.zxing.WriterException;
+import io.jsonwebtoken.lang.Assert;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.ibatis.annotations.Param;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.core.parameters.P;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
@@ -48,6 +55,7 @@ import java.awt.image.BufferedImage;
 import java.io.*;
 import java.math.BigDecimal;
 import java.net.URL;
+import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
@@ -77,6 +85,35 @@ public class UserController extends  AppBaseController {
     @Autowired
     private IFsUserCourseVideoService courseVideoService;
 
+
+    @Autowired
+    private IFsUserService fsUserService;
+    @Login
+    @ApiOperation("获取用户信息")
+    @PostMapping("/friendsSearch")
+    public R friendsSearch(@RequestBody FindUserByParam param){
+        if(param.getPageNum() == null) {
+            throw new IllegalArgumentException("分页参数 pageNum 不能为空!");
+        }
+        if(param.getPageSize() == null) {
+            throw new IllegalArgumentException("分页参数 pageSize 不能为空!");
+        }
+
+        if(StringUtils.isBlank(param.getKeywords())) {
+            return R.ok().put("data",new ArrayList<>());
+        }
+        PageHelper.startPage(param.getPageNum(), param.getPageSize());
+
+        if(StringUtils.isNotBlank(param.getKeywords())){
+            if(Validator.isMobile(param.getKeywords())) {
+                param.setKeywords(PhoneUtil.encryptPhone(param.getKeywords()));
+            }
+        }
+        List<FindUsersByDTO> list = fsUserService.findUserByParam(param);
+        PageInfo<FindUsersByDTO> listPageInfo=new PageInfo<>(list);
+
+        return R.ok().put("data", listPageInfo);
+    }
     /**
      * 获取用户信息
      * @param request

+ 10 - 0
fs-user-app/src/main/java/com/fs/app/param/FriendsSearchParam.java

@@ -0,0 +1,10 @@
+package com.fs.app.param;
+
+import lombok.Data;
+
+import java.io.Serializable;
+
+@Data
+public class FriendsSearchParam implements Serializable {
+    private String keyword;
+}

+ 3 - 0
fs-websocket/src/main/java/com/fs/websocket/bean/SendMsgVO.java

@@ -14,6 +14,9 @@ public class SendMsgVO {
     @ApiModelProperty("用户id")
     private Long userId;
 
+    @ApiModelProperty(value = "用户头像,主要用于评论显示")
+    private String avatar;
+
     @ApiModelProperty(value = "用户名称")
     private String nickName;