Prechádzať zdrojové kódy

Merge remote-tracking branch 'origin/master'

yuhongqi 1 týždeň pred
rodič
commit
f744faa53f
27 zmenil súbory, kde vykonal 1236 pridanie a 12 odobranie
  1. 326 0
      fs-admin/src/main/java/com/fs/qw/controller/IpadAllocationRecordsController.java
  2. 20 0
      fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java
  3. 36 3
      fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java
  4. 2 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  5. 1 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsUserScrmServiceImpl.java
  6. 2 1
      fs-service/src/main/java/com/fs/ipad/vo/BaseVo.java
  7. 72 0
      fs-service/src/main/java/com/fs/qw/domain/IpadAllocationDetails.java
  8. 61 0
      fs-service/src/main/java/com/fs/qw/domain/IpadAllocationRecords.java
  9. 63 0
      fs-service/src/main/java/com/fs/qw/mapper/IpadAllocationRecordsMapper.java
  10. 2 0
      fs-service/src/main/java/com/fs/qw/mapper/QwIpadServerMapper.java
  11. 18 0
      fs-service/src/main/java/com/fs/qw/param/ServerParam.java
  12. 62 0
      fs-service/src/main/java/com/fs/qw/service/IIpadAllocationRecordsService.java
  13. 2 0
      fs-service/src/main/java/com/fs/qw/service/IQwIpadServerService.java
  14. 92 0
      fs-service/src/main/java/com/fs/qw/service/impl/IpadAllocationRecordsServiceImpl.java
  15. 5 0
      fs-service/src/main/java/com/fs/qw/service/impl/QwIpadServerServiceImpl.java
  16. 71 0
      fs-service/src/main/java/com/fs/qw/utils/HMACAuth.java
  17. 12 0
      fs-service/src/main/java/com/fs/qw/vo/IpadAllocationRecordsVO.java
  18. 1 1
      fs-service/src/main/resources/application-common.yml
  19. 95 0
      fs-service/src/main/resources/application-config-druid-cfryt-test.yml
  20. 2 2
      fs-service/src/main/resources/application-config-druid-cfryt.yml
  21. 5 5
      fs-service/src/main/resources/application-config-druid-sft.yml
  22. 172 0
      fs-service/src/main/resources/application-druid-cfryt-test.yml
  23. 3 0
      fs-service/src/main/resources/application-druid-jnmy-test.yml
  24. 3 0
      fs-service/src/main/resources/application-druid-jnmy.yml
  25. 103 0
      fs-service/src/main/resources/mapper/qw/IpadAllocationRecordsMapper.xml
  26. 4 0
      fs-service/src/main/resources/mapper/qw/QwIpadServerMapper.xml
  27. 1 0
      fs-user-app/src/main/java/com/fs/app/controller/store/WxH5MpScrmController.java

+ 326 - 0
fs-admin/src/main/java/com/fs/qw/controller/IpadAllocationRecordsController.java

@@ -0,0 +1,326 @@
+package com.fs.qw.controller;
+
+import cn.hutool.http.HttpRequest;
+import cn.hutool.http.HttpUtil;
+import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.DateUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.qw.domain.IpadAllocationDetails;
+import com.fs.qw.domain.IpadAllocationRecords;
+import com.fs.qw.domain.QwIpadServer;
+import com.fs.qw.mapper.IpadAllocationRecordsMapper;
+import com.fs.qw.param.ServerParam;
+import com.fs.qw.service.IIpadAllocationRecordsService;
+import com.fs.qw.service.IQwIpadServerService;
+import com.fs.qw.utils.HMACAuth;
+import com.fs.qw.vo.IpadAllocationRecordsVO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+/**
+ * 分配记录Controller
+ *
+ * @author fs
+ * @date 2025-11-20
+ */
+@RestController
+@RequestMapping("/qw/records")
+@Slf4j
+public class IpadAllocationRecordsController extends BaseController {
+    @Autowired
+    private IIpadAllocationRecordsService ipadAllocationRecordsService;
+    @Autowired
+    private IQwIpadServerService serverService;
+
+
+    @Autowired
+    private IpadAllocationRecordsMapper ipadAllocationRecordsMapper;
+    @Value("${ipad.url}")
+    private String ipadServerUrl;
+
+    /**
+     * 查询分配记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('shop:records:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(IpadAllocationRecords ipadAllocationRecords) {
+        startPage();
+        List<IpadAllocationRecords> list = ipadAllocationRecordsService.selectIpadAllocationRecordsList(ipadAllocationRecords);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出分配记录列表
+     */
+    @PreAuthorize("@ss.hasPermi('shop:records:export')")
+    @Log(title = "分配记录", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(IpadAllocationRecords ipadAllocationRecords) {
+        List<IpadAllocationRecords> list = ipadAllocationRecordsService.selectIpadAllocationRecordsList(ipadAllocationRecords);
+        ExcelUtil<IpadAllocationRecords> util = new ExcelUtil<IpadAllocationRecords>(IpadAllocationRecords.class);
+        return util.exportExcel(list, "分配记录数据");
+    }
+
+    /**
+     * 获取分配记录详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('shop:records:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id) {
+        return AjaxResult.success(ipadAllocationRecordsService.selectIpadAllocationRecordsById(id));
+    }
+
+    /**
+     * 新增分配记录
+     */
+    @PreAuthorize("@ss.hasPermi('shop:records:add')")
+    @Log(title = "分配记录", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody IpadAllocationRecords ipadAllocationRecords) {
+        return toAjax(ipadAllocationRecordsService.insertIpadAllocationRecords(ipadAllocationRecords));
+    }
+
+    /**
+     * 修改分配记录
+     */
+    @PreAuthorize("@ss.hasPermi('shop:records:edit')")
+    @Log(title = "分配记录", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody IpadAllocationRecords ipadAllocationRecords) {
+        return toAjax(ipadAllocationRecordsService.updateIpadAllocationRecords(ipadAllocationRecords));
+    }
+
+    /**
+     * 删除分配记录
+     */
+    @PreAuthorize("@ss.hasPermi('shop:records:remove')")
+    @Log(title = "分配记录", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids) {
+        return toAjax(ipadAllocationRecordsService.deleteIpadAllocationRecordsByIds(ids));
+    }
+
+    //根据记录id查询他的服务器详情
+    @GetMapping("/server/{id}")
+    public AjaxResult getServerInfo(@PathVariable("id") Long id) {
+        IpadAllocationRecords ipadAllocationRecords = ipadAllocationRecordsService.selectIpadAllocationRecordsById(id);
+        if (ipadAllocationRecords == null) {
+            return AjaxResult.error("记录不存在");
+        }
+        List<IpadAllocationDetails> detailsList = JSON.parseArray(ipadAllocationRecords.getServerJson(), IpadAllocationDetails.class);
+        return AjaxResult.success(detailsList);
+    }
+
+    @Value("${ipad.companyId:13}")
+    private Long companyId;
+    //发起申请ipad服务器
+    @GetMapping("/apply")
+    @Transactional(rollbackFor = Exception.class)
+    public AjaxResult apply(Integer applyCount,String area) throws Exception {
+        //校验count是否为正整数
+        if (applyCount == null || applyCount <= 0) {
+            return AjaxResult.error("申请数量必须为正整数");
+        }
+        //校验area是否为空
+        if (area == null || area.isEmpty()) {
+            return AjaxResult.error("区域不能为空");
+        }
+
+        //插入数据库申请表
+        IpadAllocationRecords ipadAllocationRecords = new IpadAllocationRecords();
+        ipadAllocationRecords.setCompanyId(13L);
+        ipadAllocationRecords.setApplyQuantity(applyCount);
+        ipadAllocationRecords.setSubmitTime(LocalDateTime.now());
+        ipadAllocationRecords.setRegion(area);
+        ipadAllocationRecordsMapper.insert(ipadAllocationRecords);
+        //发起http请求
+        //调用ipad服务器申请接口
+        HashMap<Object, Object> param = new HashMap<>();
+        param.put("allocationId", ipadAllocationRecords.getId());//携带id过去,以后查询结果
+        param.put("companyId", companyId);//公司id自己去配置文件改自己公司的id去腕表找
+        param.put("region", area);//区域
+        param.put("applyQuantity", applyCount);
+        String paramJson = JSON.toJSONString(param);
+        HttpRequest post = HttpUtil.createPost(ipadServerUrl + "/ipad/records/addRecords");
+        log.info("发起请求,申请参数:{},url:{}", paramJson, ipadServerUrl + "/ipad/records/addRecords");
+        post.body(paramJson);
+        post.header("Authorization", HMACAuth.generateToken());
+        //发起请求
+        String result = post.execute().body();
+        log.info("发起ipad服务器申请,申请数量:{},申请id:{},申请结果:{}", applyCount, ipadAllocationRecords.getId(), result);
+        //code不为200,回滚数据库
+        AjaxResult response = JSON.parseObject(result, AjaxResult.class);
+        if (response == null || (Integer) response.get("code") != 200) {
+            ipadAllocationRecordsMapper.deleteById(ipadAllocationRecords.getId());
+            throw new Exception("申请失败");
+        }
+        return AjaxResult.success("申请成功");
+    }
+
+    //批量更新分配记录状态
+    @PostMapping("/batchUpdate")
+    @Transactional(rollbackFor = Exception.class)
+    public AjaxResult batchUpdate(@RequestBody Long[] ids) throws Exception {
+        //校验ids是否为空
+        if (ids == null || ids.length == 0) {
+            return AjaxResult.error("ids不能为空");
+        }
+        //发起http请求
+        //调用ipad服务器更新接口
+        HashMap<Object, Object> param = new HashMap<>();
+        param.put("ids", ids);
+        String paramJson = JSON.toJSONString(param);
+        HttpRequest post = HttpUtil.createPost(ipadServerUrl + "/ipad/records/updateRecords");
+        post.body(paramJson);
+        post.header("Authorization", HMACAuth.generateToken());
+        //发起请求
+        String result = post.execute().body();
+
+        log.info("发起ipad服务器更新状态,更新数量:{},拉取结果:{}", ids.length, result);
+
+        //解析返回的JSON结果
+        AjaxResult response = JSON.parseObject(result, AjaxResult.class);
+
+        //首先判断code是否为200
+        if (response != null && (Integer) response.get("code") == 200) {
+            //获取data字段
+            Object data = response.get("data");
+
+            //将data转换为List<IpadAllocationRecordsVO>
+            List<IpadAllocationRecordsVO> recordsList = JSON.parseArray(JSON.toJSONString(data), IpadAllocationRecordsVO.class);
+
+            for (IpadAllocationRecordsVO vo : recordsList) {
+                IpadAllocationRecords ipadAllocationRecords = ipadAllocationRecordsMapper.selectById(vo.getId());
+                if (!ipadAllocationRecords.getAuditStatus().equals(0)) {
+                    ipadAllocationRecords.setServerJson(JSON.toJSONString(vo.getDetailsList()));
+                    ipadAllocationRecordsMapper.updateById(ipadAllocationRecords);
+                    log.info("分配记录id:{},状态已更新,无需重复更新", vo.getId());
+                    continue;
+                }
+                log.info("更新分配记录,记录id:{},更新状态:{},分配数量:{},审核时间:{},拒绝原因:{},公司名称:{}",
+                        vo.getId(), vo.getAuditStatus(), vo.getAllocatedQuantity(), vo.getReviewTime(), vo.getRejectionReason(), vo.getCompanyName());
+                if (ipadAllocationRecords != null) {
+                    ipadAllocationRecords.setAuditStatus(vo.getAuditStatus());
+                    ipadAllocationRecords.setUpdatedTime(LocalDateTime.now());
+                    ipadAllocationRecords.setAllocatedQuantity(vo.getAllocatedQuantity());
+                    ipadAllocationRecords.setReviewTime(vo.getReviewTime());
+                    ipadAllocationRecords.setRejectionReason(vo.getRejectionReason());
+                    ipadAllocationRecords.setCompanyName(vo.getCompanyName());
+                    ipadAllocationRecords.setServerJson(JSON.toJSONString(vo.getDetailsList()));
+                    ipadAllocationRecords.setRegion(vo.getRegion());
+                    ipadAllocationRecordsMapper.updateById(ipadAllocationRecords);
+                }
+                List<IpadAllocationDetails> detailsList = vo.getDetailsList();
+                for (IpadAllocationDetails details : detailsList) {
+                    //去查询是否有相同端口和ip的服务器,有则去更新total_count和count都加上allocatedSeats,没有则添加一条服务器记录。
+                    Integer allocatedSeats = details.getAllocatedSeats();
+                    QwIpadServer server = serverService.selectIpAndPort(details.getIpAddress(), details.getPort());
+                    if (server != null) {
+                        server.setTotalCount(server.getTotalCount() + allocatedSeats);
+                        server.setCount(server.getCount() + allocatedSeats);
+                        server.setUpdateTime(DateUtils.getNowDate());
+//                        server.setRecordId(details.getAllocationId());//后面的审核记录会覆盖之前的审核记录id,这里存的是最新的审核记录id
+                        serverService.updateById(server);
+                    } else {
+                        //添加一条服务器记录
+                        QwIpadServer newServer = new QwIpadServer();
+//                        newServer.setRecordId(details.getAllocationId());
+                        newServer.setIp(details.getIpAddress());
+                        newServer.setPort(String.valueOf(details.getPort()));
+                        newServer.setTotalCount(Long.valueOf(allocatedSeats));
+                        newServer.setCount(Long.valueOf(allocatedSeats));
+                        newServer.setAddressId("520000000000");
+                        newServer.setUrl(details.getAccessUrl());
+                        newServer.setCreateTime(DateUtils.getNowDate());
+                        serverService.getBaseMapper().insert(newServer);
+                    }
+                }
+            }
+
+            //返回成功结果
+            return AjaxResult.success("更新成功", recordsList);
+        } else {
+            //返回错误结果
+            String errorMsg = response != null ? (String) response.get("msg") : "更新失败";
+            return AjaxResult.error(errorMsg);
+        }
+    }
+
+    //释放已申请的ipad服务器
+    //释放哪条记录的那台服务器多少台
+    @PostMapping("/release")
+    @Transactional(rollbackFor = Exception.class)
+    public AjaxResult release(@RequestBody ServerParam serverParam) throws Exception {
+        //校验参数是否为空
+        if (serverParam == null) {
+            return AjaxResult.error("serverParam不能为空");
+        }
+
+        QwIpadServer server = serverService.selectIpAndPort(serverParam.getIpAddress(), Long.valueOf(serverParam.getPort()));
+        if (server == null) {
+            log.info("服务器地址:{},端口:{},未查询到服务器记录,无法释放", serverParam.getIpAddress(), serverParam.getPort());
+            return AjaxResult.error("未查询到服务器记录,无法释放");
+        }
+        if (serverParam.getReleaseCount() > server.getCount()) {
+            log.info("服务器id:{},释放数量:{},剩余数量:{},释放数量大于剩余数量,无法释放", server.getId(), serverParam.getReleaseCount(), server.getCount());
+            return AjaxResult.error("释放数量大于剩余数量,无法释放");
+        }
+        IpadAllocationRecords record = ipadAllocationRecordsMapper.selectById(serverParam.getRecordId());
+        if (record == null) {
+            log.info("服务器id:{},释放数量:{},申请记录为空,无法释放", server.getId(), serverParam.getReleaseCount());
+            return AjaxResult.error("申请记录为空,无法释放");
+        }
+        record.setAllocatedQuantity(record.getAllocatedQuantity() - serverParam.getReleaseCount());
+//        record.setApplyQuantity(record.getApplyQuantity() - serverParam.getReleaseCount());
+
+        server.setCount(server.getCount() - serverParam.getReleaseCount());
+        server.setTotalCount(server.getTotalCount() - serverParam.getReleaseCount());
+        ipadAllocationRecordsMapper.updateById(record);
+        serverService.updateById(server);
+
+        ServerParam build = ServerParam.builder()
+                .ipadServerId(serverParam.getIpadServerId())
+                .recordId(serverParam.getRecordId())
+                .releaseCount(serverParam.getReleaseCount())
+                .ipAddress(serverParam.getIpAddress())
+                .port(serverParam.getPort())
+                .build();
+
+        //发起http请求
+        String body = JSON.toJSONString(build);
+
+        HttpRequest post = HttpUtil.createPost(ipadServerUrl + "/ipad/records/release");
+        post.body(body);
+        post.header("Authorization", HMACAuth.generateToken());
+        //发起请求
+        String result = post.execute().body();
+        log.info("发起ipad服务器释放状态,释放数量:{},拉取结果:{}", body, result);
+
+        //解析返回的JSON结果
+        AjaxResult response = JSON.parseObject(result, AjaxResult.class);
+
+        //首先判断code是否为200,不是回滚事务
+        if (!response.get("code").equals(200)) {
+            String errorMsg = response != null ? (String) response.get("msg") : "释放失败";
+            throw new Exception(errorMsg);
+        }
+        return AjaxResult.success("释放成功");
+    }
+}

+ 20 - 0
fs-company-app/src/main/java/com/fs/app/controller/FsUserCourseVideoController.java

@@ -9,9 +9,11 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.StringUtils;
+import com.fs.company.domain.Company;
 import com.fs.company.domain.CompanyMiniapp;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.service.ICompanyMiniappService;
+import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.course.domain.FsUserCoursePeriod;
 import com.fs.course.dto.BatchSendCourseDTO;
@@ -39,6 +41,7 @@ import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.extern.slf4j.Slf4j;
+import org.apache.commons.collections.CollectionUtils;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.*;
@@ -57,6 +60,9 @@ public class FsUserCourseVideoController extends AppBaseController {
     @Autowired
     private IFsUserCourseVideoService fsUserCourseVideoService;
 
+    @Autowired
+    private ICompanyService iCompanyService;
+
     @Autowired
     private IFsUserCourseService fsUserCourseService;
 
@@ -290,6 +296,20 @@ public class FsUserCourseVideoController extends AppBaseController {
         return ResponseResult.ok(courseLinkService.getGotoWxAppLink(linkStr,appid));
     }
 
+    @GetMapping("/getGotoAppLink")
+    @ApiOperation("获取跳转微信小程序的链接地址")
+    public ResponseResult<String> getGotoWxAppLink(String linkStr, Long companyId) {
+        Company company = iCompanyService.selectCompanyById(companyId);
+        String appid = null;
+        if (CollectionUtils.isNotEmpty(company.getMiniAppMaster())){
+            appid = company.getMiniAppMaster().get(0);
+        }else if (CollectionUtils.isNotEmpty(company.getMiniAppServer())){
+            appid = company.getMiniAppServer().get(0);
+        }else {
+            return ResponseResult.fail(500,"请在后台配置主备小程序!");
+        }
+        return ResponseResult.ok(courseLinkService.getGotoWxAppLink(linkStr,appid));
+    }
     /**
      * 获取跳转微信小程序的链接地址
      */

+ 36 - 3
fs-ipad-task/src/main/java/com/fs/app/service/IpadSendServer.java

@@ -1,11 +1,13 @@
 package com.fs.app.service;
 
 import com.alibaba.fastjson.JSON;
+import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.fs.common.core.redis.RedisCache;
 import com.fs.common.exception.base.BaseException;
 import com.fs.common.utils.StringUtils;
 import com.fs.common.utils.date.DateUtil;
+import com.fs.common.utils.spring.SpringUtils;
 import com.fs.company.domain.CompanyMiniapp;
 import com.fs.company.service.ICompanyMiniappService;
 import com.fs.course.domain.FsCoursePlaySourceConfig;
@@ -13,6 +15,7 @@ import com.fs.course.domain.FsCourseWatchLog;
 import com.fs.course.service.IFsCourseWatchLogService;
 import com.fs.ipad.IpadSendUtils;
 import com.fs.ipad.vo.*;
+import com.fs.qw.domain.QwExternalContact;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.domain.QwUserVideo;
 import com.fs.qw.mapper.QwExternalContactMapper;
@@ -27,11 +30,13 @@ import com.fs.sop.service.impl.QwSopLogsServiceImpl;
 import com.fs.wxwork.dto.*;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
+import org.springframework.beans.factory.annotation.Value;
 import org.springframework.stereotype.Service;
 
 import java.time.LocalDateTime;
 import java.util.List;
 import java.util.Map;
+import java.util.stream.Collectors;
 
 @Slf4j
 @Service
@@ -51,16 +56,43 @@ public class IpadSendServer {
     private void sendMiniProgram(BaseVo vo, QwSopCourseFinishTempSetting.Setting content, Map<String, FsCoursePlaySourceConfig> miniMap, Long companyId) {
         String appid = content.getMiniprogramAppid();
         if(companyId != null && content.getMiniType() != null){
-            List<CompanyMiniapp> list = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().eq("company_id", companyId).eq("type", content.getMiniType()));
-            if(!list.isEmpty() && list.get(0) != null && StringUtils.isNotEmpty(list.get(0).getAppId())){
-                appid = list.get(0).getAppId();
+            List<CompanyMiniapp> list = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().eq("company_id", companyId));
+            List<CompanyMiniapp> collect = list.stream().filter(e -> e.getType().equals(content.getMiniType())).collect(Collectors.toList());
+            if(!collect.isEmpty() && collect.get(0) != null && StringUtils.isNotEmpty(collect.get(0).getAppId())){
+                appid = collect.get(0).getAppId();
+            }
+            String signProjectName = SpringUtils.getProperty("cloud_host.company_name");
+            //区分新老用户,新用户发送备用小程序,老用户发送主小程序
+            if(signProjectName.equals("济南联志健康")){
+                if(!vo.isRoom()){
+                    try {
+                        log.error("1.打印小程序信息------------------》:{}",appid);
+                        log.error("2.打印企业信息------------------》:{}",companyId);
+                        QwExternalContact qwExternalContact = qwExternalContactMapper.selectOne(new LambdaQueryWrapper<QwExternalContact>().eq(QwExternalContact::getQwUserId,vo.getQwUserId()).eq(QwExternalContact::getExternalUserId,vo.getExId()));
+                        log.error("3.打印外部联系人ID-------------------》{}",qwExternalContact.getId());
+                        LocalDateTime createTime = qwExternalContact.getCreateTime() == null ? LocalDateTime.now() : DateUtil.dateToLocalDateTime(qwExternalContact.getCreateTime());
+                        LocalDateTime lastTime = LocalDateTime.of(2025, 11, 6, 23, 59, 59);
+                        int listIndex = createTime.isAfter(lastTime) ? 1 : 0 ;
+                        List<CompanyMiniapp> collect2 = list.stream().filter(e -> e.getType().equals(listIndex)).collect(Collectors.toList());
+                        if(!collect2.isEmpty() && collect2.get(0) != null && StringUtils.isNotEmpty(collect2.get(0).getAppId())){
+                            appid = collect2.get(0).getAppId();
+                            log.error("5.打印小程序信息2------------------》:{}",appid);
+                        }
+                    } catch (Exception e) {
+                        e.printStackTrace();
+                        log.error("6.输出外部联系人ID-------------->{}",vo.getExId());
+                        log.error("7.数据异常----------------------》{}",e.getMessage());
+                    }
+                }
             }
         }
         FsCoursePlaySourceConfig courseMaConfig = miniMap.get(appid);
+        log.error("8.打印小程序配置信息------------------》:{}",1);
         if(courseMaConfig == null){
             List<CompanyMiniapp> list = companyMiniappService.list(new QueryWrapper<CompanyMiniapp>().eq("company_id", companyId).eq("type", 1));
             if(!list.isEmpty() && list.get(0) != null && StringUtils.isNotEmpty(list.get(0).getAppId())){
                 courseMaConfig = miniMap.get(list.get(0).getAppId());
+                log.error("9.打印小程序配置信息--------最终------------------》:{}",courseMaConfig);
             }
         }
         if(courseMaConfig == null){
@@ -341,6 +373,7 @@ public class IpadSendServer {
         vo.setServerId(qwUser.getServerId());
         vo.setCorpCode(parentVo.getCorpCode());
         vo.setCorpId(parentVo.getCorpId());
+        vo.setQwUserId(qwUser.getId());
         try {
             content.setSendStatus(1);
             switch (content.getContentType()) {

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

@@ -2199,6 +2199,8 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
         // 逻辑调整:如果会员已经绑定了销售,直接提示,不添加重粉数据了-2025年6月16日14点53分
         if (!param.getCompanyUserId().equals(userCompanyUser.getCompanyUserId())){
+            log.error("进入::isAddCompanyUser 该用户fsUser={},companyUser={},param={}",fsUser,companyUser,param);
+
             return ResponseResult.fail(500,"该用户("+fsUser.getUserId() + ")已成为其他销售会员");
         }
 

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

@@ -1001,6 +1001,7 @@ public class FsUserScrmServiceImpl implements IFsUserScrmService
         // 逻辑调整:会员与销售的绑定关系通过中间表关联 /20250625 17:13
         FsUserCompanyUserScrm userCompanyUser = userCompanyUserService.selectByUserIdAndProjectId(fsUser.getUserId(), param.getProjectId());
         if (Objects.nonNull(userCompanyUser) && !userCompanyUser.getCompanyUserId().equals(param.getCompanyUserId())){
+            log.error("进入::FsUserCompanyUserScrm 该用户fsUser={},companyUser={},param={}",fsUser,userCompanyUser,param);
             return ResponseResult.fail(500,"该用户("+fsUser.getUserId() + ")已成为其他销售会员");
         }
 

+ 2 - 1
fs-service/src/main/java/com/fs/ipad/vo/BaseVo.java

@@ -15,7 +15,7 @@ public class BaseVo{
     private String corpId;
     private String corpCode;
     private boolean isRoom;
-
+    private Long qwUserId;
 
     public void setBase(BaseVo vo){
         this.uuid = vo.getUuid();
@@ -24,5 +24,6 @@ public class BaseVo{
         this.corpCode = vo.getCorpCode();
         this.exId = vo.getExId();
         this.isRoom = vo.isRoom();
+        this.qwUserId = vo.qwUserId;
     }
 }

+ 72 - 0
fs-service/src/main/java/com/fs/qw/domain/IpadAllocationDetails.java

@@ -0,0 +1,72 @@
+package com.fs.qw.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 分配记录详情对象 ipad_allocation_details
+ *
+ * @author fs
+ * @date 2025-11-19
+ */
+@Data
+public class IpadAllocationDetails
+{
+    private static final long serialVersionUID = 1L;
+
+    @TableId(type = IdType.AUTO)
+    /** 记录ID */
+    private Long id;
+
+    /** 分配记录ID */
+    @Excel(name = "分配记录ID")
+    private Long allocationId;
+
+    /** IPAD记录ID */
+    @Excel(name = "IPAD记录ID")
+    private Long ipadRecordId;
+
+    /** IP地址 */
+    @Excel(name = "IP地址")
+    private String ipAddress;
+
+    /** 端口号 */
+    @Excel(name = "端口号")
+    private Long port;
+
+    /** 访问地址 */
+    @Excel(name = "访问地址")
+    private String accessUrl;
+
+    /** 地区 */
+    @Excel(name = "地区")
+    private String region;
+
+    /** 分配坐席数量 */
+    @Excel(name = "分配坐席数量")
+    private Integer allocatedSeats;
+
+    /** 分配状态: 1-有效, 0-已释放 */
+    @Excel(name = "分配状态: 1-有效, 0-已释放")
+    private Long allocationStatus;
+
+    /** 分配时间 */
+//    @JsonFormat(pattern = "yyyy-MM-dd")
+//    @Excel(name = "分配时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private LocalDateTime allocatedTime;
+
+    /** 释放时间 */
+//    @JsonFormat(pattern = "yyyy-MM-dd")
+//    @Excel(name = "释放时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private LocalDateTime releaseTime;
+
+    /** 创建时间 */
+//    @JsonFormat(pattern = "yyyy-MM-dd")
+//    @Excel(name = "创建时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private LocalDateTime createdTime;
+
+}

+ 61 - 0
fs-service/src/main/java/com/fs/qw/domain/IpadAllocationRecords.java

@@ -0,0 +1,61 @@
+package com.fs.qw.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ * 分配记录对象 ipad_allocation_records
+ *
+ * @author fs
+ * @date 2025-11-20
+ */
+@Data
+public class IpadAllocationRecords {
+
+    /** 主键ID */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+
+    /** 申请公司ID */
+    @Excel(name = "申请公司ID")
+    private Long companyId;
+
+    /** 申请公司名字 */
+    @Excel(name = "申请公司名字")
+    private String companyName;
+
+
+    /** 申请数量 */
+    @Excel(name = "申请数量")
+    private Integer applyQuantity;
+
+    /** 实际分配数量 */
+    @Excel(name = "实际分配数量")
+    private Integer allocatedQuantity;
+
+
+    private LocalDateTime submitTime;
+
+
+    private LocalDateTime reviewTime;
+
+    private Integer auditStatus;
+
+    /** 拒绝原因 */
+    @Excel(name = "拒绝原因")
+    private String rejectionReason;
+
+
+    private LocalDateTime createdTime;
+
+
+    private LocalDateTime updatedTime;
+
+    private String serverJson;
+
+    private String region;
+}

+ 63 - 0
fs-service/src/main/java/com/fs/qw/mapper/IpadAllocationRecordsMapper.java

@@ -0,0 +1,63 @@
+package com.fs.qw.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.qw.domain.IpadAllocationRecords;
+
+import java.util.List;
+
+/**
+ * 分配记录Mapper接口
+ *
+ * @author fs
+ * @date 2025-11-20
+ */
+public interface IpadAllocationRecordsMapper extends BaseMapper<IpadAllocationRecords>{
+    /**
+     * 查询分配记录
+     *
+     * @param id 分配记录主键
+     * @return 分配记录
+     */
+    IpadAllocationRecords selectIpadAllocationRecordsById(Long id);
+
+    /**
+     * 查询分配记录列表
+     *
+     * @param ipadAllocationRecords 分配记录
+     * @return 分配记录集合
+     */
+    List<IpadAllocationRecords> selectIpadAllocationRecordsList(IpadAllocationRecords ipadAllocationRecords);
+
+    /**
+     * 新增分配记录
+     *
+     * @param ipadAllocationRecords 分配记录
+     * @return 结果
+     */
+    int insertIpadAllocationRecords(IpadAllocationRecords ipadAllocationRecords);
+
+    /**
+     * 修改分配记录
+     *
+     * @param ipadAllocationRecords 分配记录
+     * @return 结果
+     */
+    int updateIpadAllocationRecords(IpadAllocationRecords ipadAllocationRecords);
+
+    /**
+     * 删除分配记录
+     *
+     * @param id 分配记录主键
+     * @return 结果
+     */
+    int deleteIpadAllocationRecordsById(Long id);
+
+    /**
+     * 批量删除分配记录
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteIpadAllocationRecordsByIds(Long[] ids);
+}

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

@@ -2,6 +2,7 @@ package com.fs.qw.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.qw.domain.QwIpadServer;
+import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
 
@@ -74,4 +75,5 @@ public interface QwIpadServerMapper extends BaseMapper<QwIpadServer>{
     @Select("select sum(qis.total_count) as total_count ,sum(qis.count) as count from qw_ipad_server qis")
     QwIpadServer getPadInfo();
 
+    QwIpadServer selectIpAndPort(@Param("ipAddress") String ipAddress, @Param("port") Long port);
 }

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

@@ -0,0 +1,18 @@
+package com.fs.qw.param;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+public class ServerParam {
+    private Long ipadServerId;
+    private Long recordId;
+    private Integer releaseCount;
+    private String ipAddress;
+    private String port;
+}

+ 62 - 0
fs-service/src/main/java/com/fs/qw/service/IIpadAllocationRecordsService.java

@@ -0,0 +1,62 @@
+package com.fs.qw.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.qw.domain.IpadAllocationRecords;
+
+import java.util.List;
+
+/**
+ * 分配记录Service接口
+ *
+ * @author fs
+ * @date 2025-11-20
+ */
+public interface IIpadAllocationRecordsService extends IService<IpadAllocationRecords> {
+    /**
+     * 查询分配记录
+     *
+     * @param id 分配记录主键
+     * @return 分配记录
+     */
+    IpadAllocationRecords selectIpadAllocationRecordsById(Long id);
+
+    /**
+     * 查询分配记录列表
+     *
+     * @param ipadAllocationRecords 分配记录
+     * @return 分配记录集合
+     */
+    List<IpadAllocationRecords> selectIpadAllocationRecordsList(IpadAllocationRecords ipadAllocationRecords);
+
+    /**
+     * 新增分配记录
+     *
+     * @param ipadAllocationRecords 分配记录
+     * @return 结果
+     */
+    int insertIpadAllocationRecords(IpadAllocationRecords ipadAllocationRecords);
+
+    /**
+     * 修改分配记录
+     *
+     * @param ipadAllocationRecords 分配记录
+     * @return 结果
+     */
+    int updateIpadAllocationRecords(IpadAllocationRecords ipadAllocationRecords);
+
+    /**
+     * 批量删除分配记录
+     *
+     * @param ids 需要删除的分配记录主键集合
+     * @return 结果
+     */
+    int deleteIpadAllocationRecordsByIds(Long[] ids);
+
+    /**
+     * 删除分配记录信息
+     *
+     * @param id 分配记录主键
+     * @return 结果
+     */
+    int deleteIpadAllocationRecordsById(Long id);
+}

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

@@ -69,4 +69,6 @@ public interface IQwIpadServerService extends IService<QwIpadServer>{
     Long selectQwIpadServerByOtherAddressId();
 
     QwIpadServer getPadInfo();
+
+    QwIpadServer selectIpAndPort(String ipAddress, Long port);
 }

+ 92 - 0
fs-service/src/main/java/com/fs/qw/service/impl/IpadAllocationRecordsServiceImpl.java

@@ -0,0 +1,92 @@
+package com.fs.qw.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.qw.domain.IpadAllocationRecords;
+import com.fs.qw.mapper.IpadAllocationRecordsMapper;
+import com.fs.qw.service.IIpadAllocationRecordsService;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+
+
+/**
+ * 分配记录Service业务层处理
+ *
+ * @author fs
+ * @date 2025-11-20
+ */
+@Service
+public class IpadAllocationRecordsServiceImpl extends ServiceImpl<IpadAllocationRecordsMapper, IpadAllocationRecords> implements IIpadAllocationRecordsService {
+
+    /**
+     * 查询分配记录
+     *
+     * @param id 分配记录主键
+     * @return 分配记录
+     */
+    @Override
+    public IpadAllocationRecords selectIpadAllocationRecordsById(Long id)
+    {
+        return baseMapper.selectIpadAllocationRecordsById(id);
+    }
+
+    /**
+     * 查询分配记录列表
+     *
+     * @param ipadAllocationRecords 分配记录
+     * @return 分配记录
+     */
+    @Override
+    public List<IpadAllocationRecords> selectIpadAllocationRecordsList(IpadAllocationRecords ipadAllocationRecords)
+    {
+        return baseMapper.selectIpadAllocationRecordsList(ipadAllocationRecords);
+    }
+
+    /**
+     * 新增分配记录
+     *
+     * @param ipadAllocationRecords 分配记录
+     * @return 结果
+     */
+    @Override
+    public int insertIpadAllocationRecords(IpadAllocationRecords ipadAllocationRecords)
+    {
+        return baseMapper.insertIpadAllocationRecords(ipadAllocationRecords);
+    }
+
+    /**
+     * 修改分配记录
+     *
+     * @param ipadAllocationRecords 分配记录
+     * @return 结果
+     */
+    @Override
+    public int updateIpadAllocationRecords(IpadAllocationRecords ipadAllocationRecords)
+    {
+        return baseMapper.updateIpadAllocationRecords(ipadAllocationRecords);
+    }
+
+    /**
+     * 批量删除分配记录
+     *
+     * @param ids 需要删除的分配记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteIpadAllocationRecordsByIds(Long[] ids)
+    {
+        return baseMapper.deleteIpadAllocationRecordsByIds(ids);
+    }
+
+    /**
+     * 删除分配记录信息
+     *
+     * @param id 分配记录主键
+     * @return 结果
+     */
+    @Override
+    public int deleteIpadAllocationRecordsById(Long id)
+    {
+        return baseMapper.deleteIpadAllocationRecordsById(id);
+    }
+}

+ 5 - 0
fs-service/src/main/java/com/fs/qw/service/impl/QwIpadServerServiceImpl.java

@@ -118,4 +118,9 @@ private QwIpadServerMapper qwIpadServerMapper;
     public QwIpadServer getPadInfo() {
         return qwIpadServerMapper.getPadInfo();
     }
+
+    @Override
+    public QwIpadServer selectIpAndPort(String ipAddress, Long port) {
+        return qwIpadServerMapper.selectIpAndPort(ipAddress, port);
+    }
 }

+ 71 - 0
fs-service/src/main/java/com/fs/qw/utils/HMACAuth.java

@@ -0,0 +1,71 @@
+package com.fs.qw.utils;
+
+import javax.crypto.Mac;
+import javax.crypto.spec.SecretKeySpec;
+import java.util.Base64;
+
+public class HMACAuth {
+    private static final String SECRET_KEY = "ipad-secret-key-!#$";
+    private static final String ALGORITHM = "HmacSHA256";
+    // 设置token过期时间为5分钟(单位:毫秒)
+    private static final long TOKEN_EXPIRATION_TIME = 1 * 60 * 1000;
+
+    public static String generateToken() throws Exception {
+        String timestamp = String.valueOf(System.currentTimeMillis());
+        Mac mac = Mac.getInstance(ALGORITHM);
+        SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
+        mac.init(keySpec);
+        byte[] signature = mac.doFinal(timestamp.getBytes());
+        return timestamp + ":" + Base64.getEncoder().encodeToString(signature);
+    }
+
+    public static void validateToken(String token) throws Exception {
+        try {
+            String[] parts = token.split(":");
+            if (parts.length != 2) throw new Exception("格式错误");
+
+            String timestamp = parts[0];
+            String receivedSignature = parts[1];
+
+            // 检查token是否过期
+            long tokenTime = Long.parseLong(timestamp);
+            long currentTime = System.currentTimeMillis();
+            if (currentTime - tokenTime > TOKEN_EXPIRATION_TIME) {
+                throw new Exception("Token已过期");
+            }
+
+            // 重新计算签名
+            Mac mac = Mac.getInstance(ALGORITHM);
+            SecretKeySpec keySpec = new SecretKeySpec(SECRET_KEY.getBytes(), ALGORITHM);
+            mac.init(keySpec);
+            byte[] expectedSignature = mac.doFinal(timestamp.getBytes());
+            String expected = Base64.getEncoder().encodeToString(expectedSignature);
+
+            if (!expected.equals(receivedSignature)) {
+                throw new Exception("签名校验失败");
+            }
+
+        } catch (NumberFormatException e) {
+            throw new Exception("Token格式错误");
+        } catch (Exception e) {
+            throw new Exception("Token校验失败");
+        }
+    }
+
+    public static void main(String[] args) {
+        try {
+            String token = generateToken();
+            // 测试有效的token
+            System.out.println("Generated Token: " + token);
+            validateToken(token);
+            System.out.println("Token is valid.");
+
+            // 测试过期的token
+            String expiredToken = "1763774896180:Rd4jyhRi6NyA7hfPanmxhSWXN+WxCaeKphvnODfakos=";
+            validateToken(expiredToken);
+            System.out.println("Expired token is valid.");
+        } catch (Exception e) {
+            System.out.println("Error: " + e.getMessage());
+        }
+    }
+}

+ 12 - 0
fs-service/src/main/java/com/fs/qw/vo/IpadAllocationRecordsVO.java

@@ -0,0 +1,12 @@
+package com.fs.qw.vo;
+
+import com.fs.qw.domain.IpadAllocationDetails;
+import com.fs.qw.domain.IpadAllocationRecords;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+public class IpadAllocationRecordsVO extends IpadAllocationRecords {
+    List<IpadAllocationDetails> detailsList;
+}

+ 1 - 1
fs-service/src/main/resources/application-common.yml

@@ -74,7 +74,7 @@ token:
     # 令牌密钥
     secret: abcdefghijklmnopqrstuvwxyz
     # 令牌有效期(默认30分钟)
-    expireTime: 180
+    expireTime: 720
 mybatis-plus:
   # 搜索指定包别名
   typeAliasesPackage: com.fs.**.domain,com.fs.**.bo

+ 95 - 0
fs-service/src/main/resources/application-config-druid-cfryt-test.yml

@@ -0,0 +1,95 @@
+baidu:
+  token: 1
+  back-domain: https://www.xxxx.com
+#配置
+logging:
+  level:
+    org.springframework.web: INFO
+    com.github.binarywang.demo.wx.cp: DEBUG
+    me.chanjar.weixin: DEBUG
+wx:
+  miniapp:
+    configs:
+      - appid: w   #中康智慧
+        secret: 5
+        token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
+        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
+        msgDataFormat: JSON
+      - appid: w   #中康未来智慧药房
+        secret: 9
+        token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
+        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
+        msgDataFormat: JSON
+  cp:
+    corpId: wwb
+    appConfigs:
+      - agentId: 100005
+        secret: ec7okROXJqkNafq66aKNv0asTzQIG0CYrj3vyBbo
+        token: PPKOdAloMO
+        aesKey: PKvaxtpSvNGpfTDm7VUHIK8Wok2ESyYX24qpXJAdMP
+  pay:
+    appId: wx #微信公众号或者小程序等的appid
+    mchId: 1611045 #微信支付商户号
+    mchKey: 8cab128997a3547c10898b877f38 #微信支付商户密钥
+    subAppId:  #服务商模式下的子商户公众账号ID
+    subMchId:  #服务商模式下的子商户号
+    keyPath: c:\\cert\\apiclient_cert.p12 # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
+    notifyUrl: https://usepp.his.runtzh.com/app/wxpay/wxPayNotify
+  mp:
+    useRedis: false
+    redisConfig:
+      host: 127.0.0.1
+      port: 6379
+      timeout: 2000
+    configs:
+      - appId: wx17f36a56c701bdea # 第一个公众号的appid   //公众号名称
+        secret: 185030bbe7f8d7a0c16b94dd9d4ea542 # 公众号的appsecret
+        token: PPKOdAlCoMO # 接口配置里的Token值
+        aesKey: Eswa6VjwtVcw03qZy6Wllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值
+aifabu:  #爱链接
+  appKey: 7b471be905ab17ef358c610dd117601d008
+watch:
+  watchUrl: watch.ylrzcloud.com/prod-api
+#  account: tcloud
+#  password: mdf-m2h_6yw2$hq
+  account1: ccif #866655060138751
+  password1: cp-t5or_6xw7$mt
+  account2: tcloud #rt500台
+  password2: mdf-m2h_6yw2$hq
+  account3: whr
+  password3: v9xsKuqn_$d2y
+
+fs :
+  commonApi: http://159.75.111.224:8010
+  h5CommonApi: http://159.75.111.224:8010
+nuonuo:
+  key: 10924508
+  secret: A2EB20764D304D16
+# 存储捅配置
+tencent_cloud_config:
+  secret_id: AKIDiMq9lDf2EOM9lIfqqfKo7FNgM5meD0sT
+  secret_key: u5SuS80342xzx8FRBukza9lVNHKNMSaB
+  bucket: syysy-1323137866
+  app_id: 1323137866
+  region: ap-chongqing
+  proxy: syysy
+cloud_host:
+  company_name: 赤峰润
+  projectCode: SYYSY
+#看课授权时显示的头像
+headerImg:
+  imgUrl: https://ysy-1329817240.cos.ap-guangzhou.myqcloud.com/ysy/20250820/2c47e4f105b641b4a49df50a77338e32.png
+ipad:
+  ipadUrl: http://ipad.ysya.top
+  aiApi: http://49.232.181.28:3000/api
+  voiceApi:
+  commonApi:
+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"

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

@@ -69,10 +69,10 @@ nuonuo:
 tencent_cloud_config:
   secret_id: AKIDiMq9lDf2EOM9lIfqqfKo7FNgM5meD0sT
   secret_key: u5SuS80342xzx8FRBukza9lVNHKNMSaB
-  bucket: syysy-1323137866
+  bucket: cfryt-1323137866
   app_id: 1323137866
   region: ap-chongqing
-  proxy: syysy
+  proxy: cfryt
 cloud_host:
   company_name: 赤峰润
   projectCode: SYYSY

+ 5 - 5
fs-service/src/main/resources/application-config-druid-sft.yml

@@ -67,12 +67,12 @@ nuonuo:
   secret: A2EB20764D304D16
 # 存储捅配置
 tencent_cloud_config:
-  secret_id: AKIDiMq9lDf2EOM9lIfqqfKo7FNgM5meD0sT
-  secret_key: u5SuS80342xzx8FRBukza9lVNHKNMSaB
-  bucket: sft-1323137866
-  app_id: 1323137866
+  secret_id: AKIDpY8d3KcptqdCaoqeVHuKYZMvITULzQta
+  secret_key: cBShmjU4SwNejgS2PYUFnR7dzAC6pW3Q
+  bucket: sft-1361917636
+  app_id: 1361917636
   region: ap-chongqing
-  proxy: sft
+  proxy: fs
 cloud_host:
   company_name: 四福堂
   projectCode: SFT

+ 172 - 0
fs-service/src/main/resources/application-druid-cfryt-test.yml

@@ -0,0 +1,172 @@
+# 数据源配置
+spring:
+    profiles:
+        include: config-druid-cfryt-test,common
+    # redis 配置
+    redis:
+        host: r-2zenxuh8uodh6yqsbwpd.redis.rds.aliyuncs.com
+        port: 6379
+        # 数据库索引
+        database: 0
+        # 密码
+        password: Ylrz_tM8
+        # 连接超时时间
+        timeout: 10s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+    datasource:
+        #        clickhouse:
+        #            type: com.alibaba.druid.pool.DruidDataSource
+        #            driverClassName: com.clickhouse.jdbc.ClickHouseDriver
+        #            url: jdbc:clickhouse://1.14.104.71:8123/sop_test?compress=0&use_server_time_zone=true&use_client_time_zone=false&timezone=Asia/Shanghai
+        #            username: rt_2024
+        #            password: Yzx_19860213
+        #            initialSize: 10
+        #            maxActive: 100
+        #            minIdle: 10
+        #            maxWait: 6000
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://rm-2ze62885pk08tlkf74o.mysql.rds.aliyuncs.com:3306/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrz_tM818782145I@
+                # 从库数据源
+                slave:
+                    # 从数据源开关/默认关闭
+                    url: jdbc:mysql://rm-2ze62885pk08tlkf74o.mysql.rds.aliyuncs.com:3306/fs_his?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrz_tM818782145I@
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 2000
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+        sop:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://rm-2ze62885pk08tlkf74o.mysql.rds.aliyuncs.com:3306/fs_his_sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: Ylrz_tM818782145I@
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 200
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+rocketmq:
+    name-server: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+    consumer:
+        group: voice-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+custom:
+    token: "1o62d3YxvdHd4LEUiltnu7sK"
+    encoding-aes-key: "UJfTQ5qKTKlegjkXtp1YuzJzxeHlUKvq5GyFbERN1iU"
+    corp-id: "ww51717e2b71d5e2d3"
+    secret: "6ODAmw-8W4t6h9mdzHh2Z4Apwj8mnsyRnjEDZOHdA7k"
+    private-key-path: "privatekey.pem"
+    webhook-url: "https://your-server.com/wecom/archive"
+# token配置
+token:
+    # 令牌自定义标识
+    header: Authorization
+    # 令牌密钥
+    secret: abcdefghijklmnopqrstuvwxyz
+    # 令牌有效期(默认30分钟)
+    expireTime: 180
+openIM:
+    secret: openIM123
+    userID: imAdmin
+    url: https://web.im.ysya.top/api
+#是否使用新im
+im:
+    type: OPENIM
+#是否为新商户,新商户不走mpOpenId
+isNewWxMerchant: true
+

+ 3 - 0
fs-service/src/main/resources/application-druid-jnmy-test.yml

@@ -156,5 +156,8 @@ openIM:
 im:
     type: OPENIM
 isNewWxMerchant: true
+ipad:
+    url: http://localhost:8999/dev-api
+    companyId: 13
 
 

+ 3 - 0
fs-service/src/main/resources/application-druid-jnmy.yml

@@ -160,5 +160,8 @@ im:
     type: OPENIM
 #是否为新商户,新商户不走mpOpenId
 isNewWxMerchant: true
+ipad:
+    url: https://manwatch.ylrzcloud.com/prod-api
+    companyId: 13
 
 

+ 103 - 0
fs-service/src/main/resources/mapper/qw/IpadAllocationRecordsMapper.xml

@@ -0,0 +1,103 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.qw.mapper.IpadAllocationRecordsMapper">
+
+    <resultMap type="IpadAllocationRecords" id="IpadAllocationRecordsResult">
+        <result property="id"    column="id"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="companyName"    column="company_name"    />
+        <result property="applyQuantity"    column="apply_quantity"    />
+        <result property="allocatedQuantity"    column="allocated_quantity"    />
+        <result property="submitTime"    column="submit_time"    />
+        <result property="reviewTime"    column="review_time"    />
+        <result property="auditStatus"    column="audit_status"    />
+        <result property="rejectionReason"    column="rejection_reason"    />
+        <result property="createdTime"    column="created_time"    />
+        <result property="updatedTime"    column="updated_time"    />
+    </resultMap>
+
+    <sql id="selectIpadAllocationRecordsVo">
+        select id, company_id, company_name, apply_quantity, allocated_quantity, submit_time, review_time, audit_status, rejection_reason, created_time, updated_time, server_json from ipad_allocation_records
+    </sql>
+
+    <select id="selectIpadAllocationRecordsList" parameterType="IpadAllocationRecords" resultMap="IpadAllocationRecordsResult">
+        <include refid="selectIpadAllocationRecordsVo"/>
+        <where>
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="companyName != null  and companyName != ''"> and company_name like concat('%', #{companyName}, '%')</if>
+            <if test="applyQuantity != null "> and apply_quantity = #{applyQuantity}</if>
+            <if test="allocatedQuantity != null "> and allocated_quantity = #{allocatedQuantity}</if>
+            <if test="submitTime != null "> and submit_time = #{submitTime}</if>
+            <if test="reviewTime != null "> and review_time = #{reviewTime}</if>
+            <if test="auditStatus != null "> and audit_status = #{auditStatus}</if>
+            <if test="rejectionReason != null  and rejectionReason != ''"> and rejection_reason = #{rejectionReason}</if>
+            <if test="createdTime != null "> and created_time = #{createdTime}</if>
+            <if test="updatedTime != null "> and updated_time = #{updatedTime}</if>
+        </where>
+    </select>
+
+    <select id="selectIpadAllocationRecordsById" parameterType="Long" resultMap="IpadAllocationRecordsResult">
+        <include refid="selectIpadAllocationRecordsVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertIpadAllocationRecords" parameterType="IpadAllocationRecords">
+        insert into ipad_allocation_records
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="id != null">id,</if>
+            <if test="companyId != null">company_id,</if>
+            <if test="companyName != null">company_name,</if>
+            <if test="applyQuantity != null">apply_quantity,</if>
+            <if test="allocatedQuantity != null">allocated_quantity,</if>
+            <if test="submitTime != null">submit_time,</if>
+            <if test="reviewTime != null">review_time,</if>
+            <if test="auditStatus != null">audit_status,</if>
+            <if test="rejectionReason != null">rejection_reason,</if>
+            <if test="createdTime != null">created_time,</if>
+            <if test="updatedTime != null">updated_time,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="id != null">#{id},</if>
+            <if test="companyId != null">#{companyId},</if>
+            <if test="companyName != null">#{companyName},</if>
+            <if test="applyQuantity != null">#{applyQuantity},</if>
+            <if test="allocatedQuantity != null">#{allocatedQuantity},</if>
+            <if test="submitTime != null">#{submitTime},</if>
+            <if test="reviewTime != null">#{reviewTime},</if>
+            <if test="auditStatus != null">#{auditStatus},</if>
+            <if test="rejectionReason != null">#{rejectionReason},</if>
+            <if test="createdTime != null">#{createdTime},</if>
+            <if test="updatedTime != null">#{updatedTime},</if>
+        </trim>
+    </insert>
+
+    <update id="updateIpadAllocationRecords" parameterType="IpadAllocationRecords">
+        update ipad_allocation_records
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="companyName != null">company_name = #{companyName},</if>
+            <if test="applyQuantity != null">apply_quantity = #{applyQuantity},</if>
+            <if test="allocatedQuantity != null">allocated_quantity = #{allocatedQuantity},</if>
+            <if test="submitTime != null">submit_time = #{submitTime},</if>
+            <if test="reviewTime != null">review_time = #{reviewTime},</if>
+            <if test="auditStatus != null">audit_status = #{auditStatus},</if>
+            <if test="rejectionReason != null">rejection_reason = #{rejectionReason},</if>
+            <if test="createdTime != null">created_time = #{createdTime},</if>
+            <if test="updatedTime != null">updated_time = #{updatedTime},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteIpadAllocationRecordsById" parameterType="Long">
+        delete from ipad_allocation_records where id = #{id}
+    </delete>
+
+    <delete id="deleteIpadAllocationRecordsByIds" parameterType="String">
+        delete from ipad_allocation_records where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+</mapper>

+ 4 - 0
fs-service/src/main/resources/mapper/qw/QwIpadServerMapper.xml

@@ -38,6 +38,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <include refid="selectQwIpadServerVo"/>
         where id = #{id}
     </select>
+    <select id="selectIpAndPort" resultType="com.fs.qw.domain.QwIpadServer">
+        select id, title, address_id, ip, port, url, total_count, count, create_time, update_time from qw_ipad_server
+        where ip = #{ipAddress} and port = #{port}
+    </select>
 
     <insert id="insertQwIpadServer" parameterType="QwIpadServer">
         insert into qw_ipad_server

+ 1 - 0
fs-user-app/src/main/java/com/fs/app/controller/store/WxH5MpScrmController.java

@@ -103,6 +103,7 @@ public class WxH5MpScrmController {
             // 检查用户是否已绑定其他销售
             FsUserCompanyUserScrm userCompanyUser = userCompanyUserService.selectByUserIdAndProjectId(user.getUserId(), param.getProjectId());
             if (Objects.nonNull(userCompanyUser) && !param.getCompanyUserId().equals(userCompanyUser.getCompanyUserId())){
+                log.error("进入::课程分享链接公众号登录 该用户fsUser={},companyUser={},param={}",user,companyUser,param);
                 return R.error(500, "该用户("+user.getUserId() + ")已成为其他销售会员");
             }