Explorar el Código

Merge remote-tracking branch 'origin/master'

zhangqin hace 3 días
padre
commit
aaa86a74ab
Se han modificado 25 ficheros con 606 adiciones y 46 borrados
  1. 46 3
      fs-admin/src/main/java/com/fs/course/controller/FsUserVideoController.java
  2. 5 0
      fs-admin/src/main/java/com/fs/course/controller/FsVideoResourceController.java
  3. 72 0
      fs-company/src/main/java/com/fs/company/controller/course/FsCourseLinkController.java
  4. 9 0
      fs-service/src/main/java/com/fs/config/cloud/CloudHostProper.java
  5. 23 0
      fs-service/src/main/java/com/fs/core/config/VolcEngineConfiguration.java
  6. 36 0
      fs-service/src/main/java/com/fs/core/service/impl/STSService.java
  7. 3 0
      fs-service/src/main/java/com/fs/course/domain/FsCourseRedPacketLog.java
  8. 3 0
      fs-service/src/main/java/com/fs/course/domain/FsVideoResource.java
  9. 1 1
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java
  10. 21 0
      fs-service/src/main/java/com/fs/course/param/FsCourseLinkRoomNewParam.java
  11. 58 0
      fs-service/src/main/java/com/fs/course/service/HsyAssumeRoleService.java
  12. 3 0
      fs-service/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java
  13. 6 0
      fs-service/src/main/java/com/fs/course/service/IFsUserVideoService.java
  14. 118 24
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  15. 122 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserVideoServiceImpl.java
  16. 47 0
      fs-service/src/main/java/com/fs/course/service/impl/VodUploadMediaProcessListener.java
  17. 2 2
      fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java
  18. 3 2
      fs-service/src/main/java/com/fs/qw/mapper/QwExternalContactMapper.java
  19. 3 0
      fs-service/src/main/resources/application-common.yml
  20. 3 10
      fs-service/src/main/resources/application-config-dev.yml
  21. 3 2
      fs-service/src/main/resources/application-config-druid-hcl.yml
  22. 1 0
      fs-service/src/main/resources/application-config-druid-hzyy.yml
  23. 2 1
      fs-service/src/main/resources/application-config-druid-jzzx.yml
  24. 5 1
      fs-service/src/main/resources/mapper/course/FsCourseRedPacketLogMapper.xml
  25. 11 0
      fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserController.java

+ 46 - 3
fs-admin/src/main/java/com/fs/course/controller/FsUserVideoController.java

@@ -13,6 +13,7 @@ import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.domain.FsUserVideo;
 import com.fs.course.domain.FsVideoResource;
 import com.fs.course.param.*;
+import com.fs.course.service.HsyAssumeRoleService;
 import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.course.service.IFsUserVideoService;
 import com.fs.course.service.IFsVideoResourceService;
@@ -23,17 +24,18 @@ import com.fs.his.service.IFsPackageService;
 import com.fs.his.vo.FsPackageVO;
 import com.fs.system.oss.CloudStorageService;
 import com.fs.system.oss.OSSFactory;
+import com.volcengine.model.response.AssumeRoleResponse;
+import com.volcengine.service.vod.IVodService;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.io.FilenameUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 import org.springframework.web.multipart.MultipartFile;
 
 import java.io.*;
-import java.util.Date;
-import java.util.List;
-import java.util.UUID;
+import java.util.*;
 
 /**
  * 课堂视频Controller
@@ -60,6 +62,12 @@ public class FsUserVideoController extends BaseController
     @Autowired
     private IFsUserCourseVideoService fsUserCourseVideoService;
 
+
+
+    @Autowired
+    private RedisTemplate redisTemplate;
+    @Autowired
+    private HsyAssumeRoleService hsyAssumeRoleService;
     /**
      * 查询课堂视频列表
      */
@@ -389,4 +397,39 @@ public class FsUserVideoController extends BaseController
             return R.error("服务器内部错误");
         }
     }
+
+
+    @PreAuthorize("@ss.hasPermi('course:userVideo:uploadUserVideo')")
+    @Log(title = "课堂视频", businessType = BusinessType.INSERT)
+    @PostMapping("/uploadUserVideo")
+    public AjaxResult upload(@RequestParam("file") MultipartFile file, @RequestParam("uploadId")String uploadId) {
+        String vid = fsUserVideoService.uploadVideo(file, uploadId);
+
+        Map<String, Object> res = new HashMap<>();
+        res.put("uploadId", uploadId);
+        res.put("vid", vid);
+        return new AjaxResult(200, "上传成功", res);
+    }
+
+
+    @GetMapping("/uploadProgress")
+    public AjaxResult getUploadProgress(@RequestParam String uploadId) {
+        Object percentObj = redisTemplate.opsForValue()
+                .get("vod:upload:" + uploadId);
+
+        int percent = 0;
+        if (percentObj != null) {
+            percent = Integer.parseInt(percentObj.toString());
+        }
+
+        Map<String, Object> res = new HashMap<>();
+        res.put("percent", percent);
+        return AjaxResult.success(res);
+    }
+
+    @GetMapping("/HsyAssumeRoleService")
+    public AjaxResult HsyAssumeRoleService() throws Exception {
+        AssumeRoleResponse roleResponse = hsyAssumeRoleService.getRoleResponse();
+        return AjaxResult.success(roleResponse);
+    }
 }

+ 5 - 0
fs-admin/src/main/java/com/fs/course/controller/FsVideoResourceController.java

@@ -3,6 +3,7 @@ package com.fs.course.controller;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
 import com.baomidou.mybatisplus.core.toolkit.Wrappers;
 import com.fs.common.annotation.Log;
 import com.fs.common.core.controller.BaseController;
@@ -15,12 +16,14 @@ import com.fs.config.cloud.CloudHostProper;
 import com.fs.course.business.FsVideoResourceBusinessService;
 import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsVideoResource;
+import com.fs.course.service.IFsUserVideoService;
 import com.fs.course.service.IFsVideoResourceService;
 import com.fs.course.vo.FsVideoResourceVO;
 import com.fs.framework.web.service.TokenService;
 import com.fs.system.service.ISysConfigService;
 import com.github.pagehelper.PageHelper;
 import lombok.AllArgsConstructor;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
@@ -45,6 +48,8 @@ public class FsVideoResourceController extends BaseController {
     private final CloudHostProper cloudHostProper;
 
     private final FsVideoResourceBusinessService videoResourceBusinessService;
+    @Autowired
+    private IFsUserVideoService fsUserVideoService;
 
     /**
      * 查询视频素材库列表

+ 72 - 0
fs-company/src/main/java/com/fs/company/controller/course/FsCourseLinkController.java

@@ -7,15 +7,23 @@ import com.fs.common.core.domain.R;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.company.domain.CompanyUser;
+import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.course.domain.FsCourseLink;
 import com.fs.course.param.FsCourseLinkCreateParam;
+import com.fs.course.param.FsCourseLinkRoomNewParam;
 import com.fs.course.service.IFsCourseLinkService;
+import com.fs.course.service.IFsUserCourseVideoService;
 import com.fs.framework.security.LoginUser;
 import com.fs.framework.security.SecurityUtils;
+import com.fs.qw.service.IQwUserService;
+import com.fs.qw.vo.QwUserVO;
+import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
 
+import java.util.Arrays;
 import java.util.List;
 
 /**
@@ -31,6 +39,17 @@ public class FsCourseLinkController extends BaseController
     @Autowired
     private IFsCourseLinkService fsCourseLinkService;
 
+    @Autowired
+    private IFsUserCourseVideoService fsUserCourseVideoService;
+
+    @Autowired
+    private IQwUserService qwUserService;
+
+    @Autowired
+    private CompanyUserMapper companyUserMapper;
+
+
+
     /**
      * 查询短链列表
      */
@@ -110,4 +129,57 @@ public class FsCourseLinkController extends BaseController
         return fsCourseLinkService.createLinkUrl(param);
     }
 
+    /**
+     * 根据销售id去查询已经绑定的企微
+     * @return
+     */
+    //@PreAuthorize("@ss.hasPermi('course:courseLink:queryQwIds')")
+    @GetMapping("/queryQwIds")
+    public R selectQwUserIdByCompanyUserId()
+    {
+        LoginUser loginUser = SecurityUtils.getLoginUser();
+        CompanyUser companyUser = companyUserMapper.selectCompanyUserById(loginUser.getUser().getUserId());
+        String qwUserId = companyUser.getQwUserId();
+        if(qwUserId == null || qwUserId.isEmpty()){
+            return R.error("请先绑定企微");
+        }
+        Long[] array = Arrays.stream(qwUserId.split(","))
+                .map(String::trim) // 去除空白
+                .filter(str -> !str.isEmpty()) // 过滤空字符串
+                .map(str -> {
+                    try {
+                        return Long.parseLong(str);
+                    } catch (NumberFormatException e) {
+                        return null; // 非数字设为null
+                    }
+                })
+                .filter(num -> num != null) // 过滤null(非数字值)
+                .toArray(Long[]::new);
+        List<QwUserVO> qwUserVOS = qwUserService.selectQwUserVOByIds(array);
+        return R.ok().put("list",qwUserVOS);
+    }
+
+    /**
+     * 创建课程看课链接
+     * @param param
+     * @return
+     */
+    //@PreAuthorize("@ss.hasPermi('course:courseLink:createRoomLink')")
+    @PostMapping("/createRoomLink")
+    @ApiOperation("创建群发链接")
+    public R createRoomLink(@RequestBody FsCourseLinkRoomNewParam param) {
+
+        if (param.getCourseId()==null){
+            return R.error("课程id不能为空");
+        }
+        if (param.getVideoId()==null){
+            return R.error("视频id不能为空");
+        }
+        if (param.getQwUserId()==null){
+            return R.error("销售企微不能为空");
+        }
+
+        return fsUserCourseVideoService.createRoomMiniLinkByCourse(param);
+    }
+
 }

+ 9 - 0
fs-service/src/main/java/com/fs/config/cloud/CloudHostProper.java

@@ -23,4 +23,13 @@ public class CloudHostProper {
     //火山云空间绑定域名
     @Value("${cloud_host.volcengineUrl}")
     public String volcengineUrl;
+
+//    @Value("${cloud_host.accessKey}")
+//    public String accessKey;
+//
+//    @Value("${cloud_host.secretKey}")
+//    public String secretKey;
+//
+//    @Value("${cloud_host.region}")
+//    public String region;
 }

+ 23 - 0
fs-service/src/main/java/com/fs/core/config/VolcEngineConfiguration.java

@@ -1,5 +1,10 @@
 package com.fs.core.config;
 
+import com.hc.openapi.tool.fastjson.JSON;
+import com.volcengine.model.request.AssumeRoleRequest;
+import com.volcengine.model.response.AssumeRoleResponse;
+import com.volcengine.service.sts.ISTSService;
+import com.volcengine.service.sts.impl.STSServiceImpl;
 import com.volcengine.service.vod.IVodService;
 import com.volcengine.service.vod.impl.VodServiceImpl;
 import org.springframework.beans.factory.annotation.Value;
@@ -26,5 +31,23 @@ public class VolcEngineConfiguration {
 
         return vodService;
     }
+
+
+
+    public static void main(String[] args) throws Exception {
+        ISTSService stsService = STSServiceImpl.getInstance();
+
+        stsService.setAccessKey("AKLTNmMwNjJkNDFhYTVjNDIzYzhhNzEyZmZmZTlmYzBhNGM");
+        stsService.setSecretKey("T0RaaFl6UmhZV1V4WXpKbU5EWTBNMkZpT0RNNU9UY3daak0wTjJFd09XUQ==");
+
+        AssumeRoleRequest request = new AssumeRoleRequest();
+        request.setRoleSessionName("just_for_test");
+        request.setDurationSeconds(900);
+        request.setRoleTrn("trn:iam::2114522511:role/hylj");
+
+        AssumeRoleResponse resp = stsService.assumeRole(request);
+        System.out.println(JSON.toJSONString(resp));
+
+    }
 }
 

+ 36 - 0
fs-service/src/main/java/com/fs/core/service/impl/STSService.java

@@ -0,0 +1,36 @@
+package com.fs.core.service.impl;
+
+import com.alibaba.fastjson.JSON;
+import com.fs.config.cloud.CloudHostProper;
+import com.volcengine.model.request.AssumeRoleRequest;
+import com.volcengine.model.response.AssumeRoleResponse;
+import com.volcengine.service.sts.ISTSService;
+import com.volcengine.service.sts.impl.STSServiceImpl;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+@Service
+public class STSService {
+
+    private final CloudHostProper cloudHostProper;
+
+    public STSService(CloudHostProper cloudHostProper) {
+        this.cloudHostProper = cloudHostProper;
+    }
+
+//    public AssumeRoleResponse geSTSToken() throws Exception {
+//        ISTSService stsService = STSServiceImpl.getInstance();
+//
+//        stsService.setAccessKey(cloudHostProper.accessKey);
+//        stsService.setSecretKey(cloudHostProper.secretKey);
+//
+//        AssumeRoleRequest request = new AssumeRoleRequest();
+//        request.setRoleSessionName("just_for_test");
+//        request.setDurationSeconds(900);
+//        request.setRoleTrn("trn:iam::yourAccountID:role/yourRoleName");
+//
+//        AssumeRoleResponse resp = stsService.assumeRole(request);
+//        return resp;
+//    }
+
+}

+ 3 - 0
fs-service/src/main/java/com/fs/course/domain/FsCourseRedPacketLog.java

@@ -73,4 +73,7 @@ public class FsCourseRedPacketLog extends BaseEntity
      */
     private BigDecimal accBalanceAfter;
 
+    //商户号
+    private String mchId;
+
 }

+ 3 - 0
fs-service/src/main/java/com/fs/course/domain/FsVideoResource.java

@@ -99,4 +99,7 @@ public class FsVideoResource {
     private Integer sort;
 
     private Long userId;
+
+    //火山云vid
+    private String hsyVid;
 }

+ 1 - 1
fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java

@@ -269,7 +269,7 @@ public interface FsUserCourseVideoMapper extends BaseMapper<FsUserCourseVideo> {
     @Select("select * from fs_user_course_video where line_two is not null and job_id is null")
     List<FsUserCourseVideo> selectVideoByHuaWei();
 
-    @Select("select * from fs_user_course_video where job_id is not null and vid is null")
+    @Select("select * from fs_user_course_video where job_id is not null and  (vid is null or vid='')")
     List<FsUserCourseVideo> selectVideoByJobId();
 
     @Select("select * from fs_user_course_video where vid is not null")

+ 21 - 0
fs-service/src/main/java/com/fs/course/param/FsCourseLinkRoomNewParam.java

@@ -0,0 +1,21 @@
+package com.fs.course.param;
+
+import lombok.Data;
+
+/**
+ * 这个是弘珍的功能,创建课程群发链接
+ */
+@Data
+public class FsCourseLinkRoomNewParam {
+
+    private Long videoId;
+
+    private Long qwUserId;
+
+    private String corpId;
+
+    private Long courseId;
+
+    private String title;//视频标题
+
+}

+ 58 - 0
fs-service/src/main/java/com/fs/course/service/HsyAssumeRoleService.java

@@ -0,0 +1,58 @@
+package com.fs.course.service;
+
+import com.fs.config.cloud.CloudHostProper;
+import com.hc.openapi.tool.fastjson.JSON;
+import com.volcengine.model.request.AssumeRoleRequest;
+import com.volcengine.model.response.AssumeRoleResponse;
+import com.volcengine.service.sts.ISTSService;
+import com.volcengine.service.sts.impl.STSServiceImpl;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.stereotype.Service;
+
+@Service
+public class HsyAssumeRoleService {
+    @Autowired
+    private CloudHostProper cloudHostProper;
+
+    @Value("${hsy.role_access_key:''}")
+    private String role_access_key;
+
+    @Value("${hsy.role_secret_key:''}")
+    private String role_secret_key;
+
+    @Value("${hsy.role_trn:''}")
+    private String role_trn;
+    public AssumeRoleResponse getRoleResponse() throws Exception {
+        ISTSService stsService = STSServiceImpl.getInstance();
+
+        stsService.setAccessKey(role_access_key);
+        stsService.setSecretKey(role_secret_key);
+
+//        stsService.setAccessKey("AKLTZTc4YTE4ZjI2OWViNDNjZGI2NjhiYTI5Njc5ZjA1Mzk");
+//        stsService.setSecretKey("WXpjelpUYzFOakF5TUdObE5EZGtNR0ZsWXpKaU1tTmtZakk1WXpObE4yRQ==");
+
+        AssumeRoleRequest request = new AssumeRoleRequest();
+        request.setRoleSessionName(cloudHostProper.spaceName);
+        request.setDurationSeconds(900);
+        request.setRoleTrn(role_trn);
+
+        AssumeRoleResponse resp = stsService.assumeRole(request);
+        return resp;
+    }
+
+    public static void main(String[] args) throws Exception {
+        ISTSService stsService = STSServiceImpl.getInstance();
+
+        stsService.setAccessKey("AKLTNmMwNjJkNDFhYTVjNDIzYzhhNzEyZmZmZTlmYzBhNGM");
+        stsService.setSecretKey("T0RaaFl6UmhZV1V4WXpKbU5EWTBNMkZpT0RNNU9UY3daak0wTjJFd09XUQ==");
+
+        AssumeRoleRequest request = new AssumeRoleRequest();
+        request.setRoleSessionName("just_for_test");
+        request.setDurationSeconds(900);
+        request.setRoleTrn("trn:iam::2114522511:role/hylj");
+        AssumeRoleResponse resp = stsService.assumeRole(request);
+        System.out.println(JSON.toJSONString(resp));
+
+    }
+}

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

@@ -17,6 +17,7 @@ import com.fs.course.vo.newfs.FsUserVideoListVO;
 import com.fs.his.domain.FsUser;
 import com.fs.his.vo.OptionsVO;
 import com.fs.qw.param.FsUserCourseRedPageParam;
+import org.springframework.web.multipart.MultipartFile;
 
 import java.util.List;
 import java.util.Map;
@@ -240,4 +241,6 @@ public interface IFsUserCourseVideoService extends IService<FsUserCourseVideo> {
      * @return
      */
     R getVideoInfoByVid();
+
+    R createRoomMiniLinkByCourse(FsCourseLinkRoomNewParam param);
 }

+ 6 - 0
fs-service/src/main/java/com/fs/course/service/IFsUserVideoService.java

@@ -9,6 +9,7 @@ import com.fs.course.vo.FsUserVideoListPVO;
 import com.fs.course.vo.FsUserVideoPVO;
 import com.fs.course.param.FsUserVideoParam;
 import com.fs.course.vo.FsUserVideoListUVO;
+import org.springframework.web.multipart.MultipartFile;
 
 /**
  * 课堂视频Service接口
@@ -104,4 +105,9 @@ public interface IFsUserVideoService {
     R deleteFsUserVideoByVideoIdWithVerify(Long videoId, Long userId);
 
     R updateVideoStatusWithVerify(FsUserVideo fsUserVideo, Long userId);
+
+    String getVideoInfoByVid(String vid);
+
+
+    String uploadVideo(MultipartFile file, String uploadId);
 }

+ 118 - 24
fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java

@@ -42,6 +42,7 @@ import com.fs.course.param.newfs.FsUserCourseAddCompanyUserParam;
 import com.fs.course.param.newfs.FsUserCourseVideoLinkParam;
 import com.fs.course.param.newfs.FsUserCourseVideoUParam;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
+import com.fs.course.service.IFsCourseLinkService;
 import com.fs.course.service.IFsUserCompanyBindService;
 import com.fs.course.service.IFsUserCompanyUserService;
 import com.fs.course.service.IFsUserCourseVideoService;
@@ -82,14 +83,12 @@ import com.fs.system.mapper.SysDictDataMapper;
 import com.fs.system.service.ISysConfigService;
 import com.fs.voice.utils.StringUtil;
 import com.github.binarywang.wxpay.bean.transfer.TransferBillsResult;
+import com.volcengine.helper.VodUploadProgressListener;
+import com.volcengine.model.beans.Functions;
 import com.volcengine.service.vod.IVodService;
 import com.volcengine.service.vod.model.business.VodUrlUploadURLSet;
-import com.volcengine.service.vod.model.request.VodGetMediaInfosRequest;
-import com.volcengine.service.vod.model.request.VodQueryUploadTaskInfoRequest;
-import com.volcengine.service.vod.model.request.VodUrlUploadRequest;
-import com.volcengine.service.vod.model.response.VodGetMediaInfosResponse;
-import com.volcengine.service.vod.model.response.VodQueryUploadTaskInfoResponse;
-import com.volcengine.service.vod.model.response.VodUrlUploadResponse;
+import com.volcengine.service.vod.model.request.*;
+import com.volcengine.service.vod.model.response.*;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.commons.collections4.CollectionUtils;
 import org.redisson.api.RLock;
@@ -100,9 +99,12 @@ import org.springframework.beans.BeanUtils;
 import org.springframework.beans.BeansException;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.multipart.MultipartFile;
 
+import java.io.File;
 import java.math.BigDecimal;
 import java.math.RoundingMode;
 import java.text.SimpleDateFormat;
@@ -249,6 +251,11 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
 
     @Autowired
     private IFsUserIntegralLogsService iFsUserIntegralLogsService;
+    @Autowired
+    private RedisTemplate redisTemplate;
+
+    @Autowired
+    private IFsCourseLinkService linkService;
 
 
     /**
@@ -968,26 +975,46 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
                 return R.error(567, "群聊通用链接").put("qwExternalId", contact.getId());
             }
         }
-        if ("今正科技".equals(cloudHostProper.getCompanyName())) {
-            QwExternalContact UnionEXt = qwExternalContactMapper.selectQwExternalByUnionID(user.getUnionId());
-            if (UnionEXt != null) {
-                log.info("匹配到的第一个企微用户:" + UnionEXt.getUserId());
-                log.info("企微id:" + UnionEXt.getId());
-                log.info("用户:" + param.getVideoId());
-                log.info("企微用户:" + param.getQwUserId());
-                param.setQwExternalId(UnionEXt.getId());
-                FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByExt(UnionEXt.getId(), param.getVideoId(), param.getQwUserId());
-                if (log == null) {
-                    param.setUserId(user.getUserId());
-                    createWatchLog(param);
-                } else {
-                    if (log.getUserId() == null || log.getUserId().equals(0L) || !log.getUserId().equals(param.getUserId())) {
-                        log.setUserId(param.getUserId());
+
+        boolean bool1 = "今正科技".equals(cloudHostProper.getCompanyName());
+        boolean bool2 = "弘珍医药".equals(cloudHostProper.getCompanyName());
+        if (bool1 || bool2) {
+            //一个都找不到
+            List<QwExternalContact> qwExternalContactList = qwExternalContactMapper.selectQwExternalByUnionID(user.getUnionId());
+
+            if (qwExternalContactList != null && !qwExternalContactList.isEmpty()) {
+                QwExternalContact UnionEXt = qwExternalContactList.get(0);
+                for (QwExternalContact qwExternalContact : qwExternalContactList) {
+                    if (qwExternalContact.getFsUserId() == null || qwExternalContact.getFsUserId() != 0L) {
+                        qwExternalContact.setFsUserId(user.getUserId());
+                        qwExternalContactMapper.updateQwExternalContact(qwExternalContact);
                     }
-                    log.setUpdateTime(new Date());
-                    courseWatchLogMapper.updateFsCourseWatchLog(log);
+                    try {
+                        if (qwExternalContact.getQwUserId().equals(Long.parseLong(param.getQwUserId()))) {
+                            UnionEXt = qwExternalContact;
+                        }
+                    } catch (Exception e) {
+                        log.error("群聊链接匹配销售失败");
+                    }
+
+                    log.info("匹配到的第一个企微用户:" + UnionEXt.getUserId());
+                    log.info("企微id:" + UnionEXt.getId());
+                    log.info("用户:" + param.getVideoId());
+                    log.info("企微用户:" + param.getQwUserId());
+                    param.setQwExternalId(UnionEXt.getId());
+                    FsCourseWatchLog log = courseWatchLogMapper.getWatchCourseVideoByExt(UnionEXt.getId(), param.getVideoId(), param.getQwUserId());
+                    if (log == null) {
+                        param.setUserId(user.getUserId());
+                        createWatchLog(param);
+                    } else {
+                        if (log.getUserId() == null || log.getUserId().equals(0L) || !log.getUserId().equals(param.getUserId())) {
+                            log.setUserId(param.getUserId());
+                        }
+                        log.setUpdateTime(new Date());
+                        courseWatchLogMapper.updateFsCourseWatchLog(log);
+                    }
+                    return R.error(567, "群聊通用链接").put("qwExternalId", UnionEXt.getId());
                 }
-                return R.error(567, "群聊通用链接").put("qwExternalId", UnionEXt.getId());
             }
         }
 
@@ -1877,6 +1904,10 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
                 }else {
                     redPacketLog.setOutBatchNo(sendRedPacket.get("orderCode").toString());
                 }
+                //存储商户ID
+                if (StringUtils.isNotEmpty(sendRedPacket.get("mchId").toString())){
+                    redPacketLog.setMchId(sendRedPacket.get("mchId").toString());
+                }
                 // 添加红包记录
                 redPacketLog.setCourseId(param.getCourseId());
                 redPacketLog.setCompanyId(param.getCompanyId());
@@ -4192,6 +4223,37 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
         return R.ok();
     }
 
+    @Override
+    public R createRoomMiniLinkByCourse(FsCourseLinkRoomNewParam param) {
+
+        QwUser qwUser = qwUserMapper.selectQwUserById(param.getQwUserId());
+
+        if (qwUser == null || qwUser.getCompanyId() == null || qwUser.getCompanyUserId() == null) {
+            return R.error("员工未绑定 销售公司 或 销售 请先绑定");
+        }
+
+        QwCompany qwCompany = iQwCompanyService.getQwCompanyByRedis(param.getCorpId());
+
+        if (qwCompany == null) {
+            return R.error().put("msg", "企业不存在,请联系管理员");
+        }
+        if (qwCompany.getMiniAppId() == null) {
+            return R.error().put("msg", "当前企业未绑定小程序,请联系管理员");
+        }
+
+        //生成小程序链接
+        String linkByMiniApp = createLinkByMiniApp(new Date(), param.getCourseId(), param.getVideoId(), qwUser, null, 2, null, 1);
+
+        if(StringUtils.isNotEmpty(linkByMiniApp)){
+            linkByMiniApp = linkByMiniApp.replaceAll(".html", "");
+        }
+        String link = linkService.getGotoWxAppLink(linkByMiniApp, qwCompany.getMiniAppId());
+
+        System.out.println("生成课程通链: " + link + " :" + param);
+
+        return R.ok().put("link", link);
+    }
+
     private final ExecutorService uploadExecutor = new ThreadPoolExecutor(
             8,  // core
             16, // max
@@ -4228,6 +4290,9 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
                 System.out.println(resp.getResponseMetadata().getError());
                 System.exit(-1);
             }else {
+                if (StringUtils.isEmpty(resp.getResult().getData().getMediaInfoList(0).getVid())){
+                    return;
+                }
                 FsUserCourseVideo video = new FsUserCourseVideo();
                 video.setVideoId(courseVideo.getVideoId());
                 video.setVid(resp.getResult().getData().getMediaInfoList(0).getVid());
@@ -4250,6 +4315,15 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
                 System.out.println(resp.getResponseMetadata().getError());
                 System.exit(-1);
             }else {
+                //如果路径是空的直接返回
+                if (StringUtils.isEmpty(resp.getResult().getMediaInfoList(0).getSourceInfo().getStoreUri())){
+                    return;
+                }
+                //如果是未发布状态修改发布状态
+                if (resp.getResult().getMediaInfoList(0).getBasicInfo().getPublishStatus().equals("Unpublished")){
+                    updateMediaPublishStatus(courseVideo.getVid());
+                }
+
                 //更新小节
                 FsUserCourseVideo video = new FsUserCourseVideo();
                 video.setVideoId(courseVideo.getVideoId());
@@ -4267,6 +4341,26 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
         }
     }
 
+    //修改发布状态
+    public void updateMediaPublishStatus(String vid){
+        String statusPublished = "Published";
+
+        try {
+            // publish
+            VodUpdateMediaPublishStatusRequest.Builder req = VodUpdateMediaPublishStatusRequest.newBuilder();
+            req.setVid(vid);
+            req.setStatus(statusPublished);
+
+            VodUpdateMediaPublishStatusResponse resp = vodService.updateMediaPublishStatus(req.build());
+            System.out.println(resp);
+
+            Thread.sleep(1000);
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+    }
+
 
     public void uploadSingleTaskWithRetry(FsUserCourseVideo courseVideo,Integer type) {
         int maxRetry = 3;

+ 122 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsUserVideoServiceImpl.java

@@ -1,10 +1,14 @@
 package com.fs.course.service.impl;
 
+import java.io.File;
 import java.io.IOException;
 import java.io.InputStream;
+import java.text.SimpleDateFormat;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.stream.Collectors;
 
+import com.alibaba.fastjson.JSON;
 import com.fs.common.core.domain.R;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.StringUtils;
@@ -20,11 +24,20 @@ import com.fs.course.vo.FsUserVideoPVO;
 import com.fs.course.param.FsUserVideoParam;
 import com.fs.course.vo.FsUserVideoListUVO;
 
+import com.volcengine.helper.VodUploadProgressListener;
+import com.volcengine.model.beans.Functions;
+import com.volcengine.service.vod.IVodService;
+import com.volcengine.service.vod.model.request.VodGetMediaInfosRequest;
+import com.volcengine.service.vod.model.request.VodUploadMediaRequest;
+import com.volcengine.service.vod.model.response.VodCommitUploadInfoResponse;
+import com.volcengine.service.vod.model.response.VodGetMediaInfosResponse;
+import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Service;
 import com.fs.course.domain.FsUserVideo;
 import com.fs.course.service.IFsUserVideoService;
+import org.springframework.web.multipart.MultipartFile;
 
 /**
  * 课堂视频Service业务层处理
@@ -32,6 +45,7 @@ import com.fs.course.service.IFsUserVideoService;
  * @author fs
  * @date 2024-05-15
  */
+@Slf4j
 @Service
 public class FsUserVideoServiceImpl implements IFsUserVideoService {
     @Autowired
@@ -46,6 +60,9 @@ public class FsUserVideoServiceImpl implements IFsUserVideoService {
     private IRecommendationService recommendationService;
     @Autowired
     private RedisTemplate redisTemplate;
+
+    @Autowired
+    private IVodService vodService;
     private static final String LIKE_KEY_PREFIX = "like:video:";
     private static final String FAVORITE_KEY_PREFIX = "favorite:video:";
 
@@ -444,5 +461,110 @@ public class FsUserVideoServiceImpl implements IFsUserVideoService {
         return url;
     }
 
+    //本地文件视频上传
+    @Override
+    public String uploadVideo(MultipartFile file, String uploadId) {
+        log.info("上传视频开始",uploadId);
+        File tempFile = null;
+        try {
+            // 将 MultipartFile 转换为临时文件
+            String originalFilename = file.getOriginalFilename();
+            String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
+            tempFile = File.createTempFile("upload_", suffix);
+            file.transferTo(tempFile);
+
+            // 点播空间名称
+            String space = "rt-huoshan";
+            // 本地临时文件路径(用于上传)
+            String localFilePath = tempFile.getAbsolutePath();
+
+            // 火山云存储路径(文件上传后在火山云的路径)
+            String datePath = new SimpleDateFormat("yyyyMMdd").format(new Date());
+            String fileName = System.currentTimeMillis() + ".mp4";
+            String remoteFileName = "course/" + datePath + "/" + fileName;
+
+            // 上传功能函数,可用于实现截图、设置媒资信息、触发工作流进行转码等功能。详见[上传功能函数说明](https://www.volcengine.com/docs/4/1185644)
+            List<Functions> functionsList = new ArrayList<>();
+            Functions getMetaFunc = Functions.GetMetaFunction();
+            functionsList.add(getMetaFunc);
+            Functions snapShotFunc = Functions.SnapShotFunction(2.3);
+            functionsList.add(snapShotFunc);
+            Functions addOptionInfo = Functions.AddOptionInfoFunction(fileName, "test", "用户上传视频", 0, true);
+            functionsList.add(addOptionInfo);
+
+            // 使用工作流进行转码
+
+            List<String> impTemplateIds = new ArrayList<>();
+//            impTemplateIds.add("a4a7066c968344e9bf7a56ec0891fbb8");
+//            FunctionsWorkflowTemplate impTemplate = new FunctionsWorkflowTemplate(impTemplateIds, "imp");
+//
+//            List<String> transcodeTemplateIds = new ArrayList<>();
+//            transcodeTemplateIds.add("a4a7066c968344e9bf7a56ec0891fbb8");
+//            FunctionsWorkflowTemplate transcodeTemplate = new FunctionsWorkflowTemplate(transcodeTemplateIds,
+//                    "transcode");
+
+//            List<FunctionsWorkflowTemplate> templates = new ArrayList<>();
+//            templates.add(impTemplate);
+//            templates.add(transcodeTemplate);
+
+//            Functions workflowFunc = Functions.StartWorkFlowFunction(templates);
+//            functionsList.add(workflowFunc);
+
+            VodUploadMediaRequest vodUploadMediaRequest = VodUploadMediaRequest.newBuilder()
+                    .setSpaceName(space)
+                    .setFilePath(localFilePath)  // 使用本地临时文件路径
+                    .setFileName(remoteFileName)  // 火山云存储路径
+                    .setFunctions(JSON.toJSONString(functionsList))
+                    .setStorageClass(1)
+                    .build();
+
+            // 需要进度条功能时添加相应 listener,如无需求,传 null 值即可
+            VodUploadProgressListener listener = new VodUploadMediaProcessListener(uploadId, redisTemplate);
+
+            VodCommitUploadInfoResponse vodCommitUploadInfoResponse = vodService.uploadMedia(vodUploadMediaRequest, listener);
+
+            redisTemplate.opsForValue()
+                    .set("vod:upload:" + uploadId, 100, 10, TimeUnit.MINUTES);
+
+            if (vodCommitUploadInfoResponse.getResponseMetadata().hasError()) {
+                throw new RuntimeException("火山云上传失败: " + vodCommitUploadInfoResponse.getResponseMetadata().getError());
+            }
+
+            // 返回视频 VID
+            String vid = vodCommitUploadInfoResponse.getResult().getData().getVid();
+            System.out.println("视频上传成功,VID: " + vid);
+            System.out.println("RequestId: " + vodCommitUploadInfoResponse.getResponseMetadata().getRequestId());
+
+            return vid;
+        } catch (Exception e) {
+            throw new RuntimeException("视频上传失败: " + e.getMessage(), e);
+        } finally {
+            // 删除临时文件
+            if (tempFile != null && tempFile.exists()) {
+                tempFile.delete();
+            }
+        }
+    }
+
+    //根据vid查询视频信息
+    @Override
+    public String getVideoInfoByVid(String vid) {
+        try {
+            VodGetMediaInfosRequest.Builder reqBuilder = VodGetMediaInfosRequest.newBuilder();
+            reqBuilder.setVids("vid");
+
+            VodGetMediaInfosResponse resp = vodService.getMediaInfos20230701(reqBuilder.build());
+            if (resp.getResponseMetadata().hasError()) {
+                System.out.println(resp.getResponseMetadata().getError());
+                System.exit(-1);
+            }else {
+                return resp.getResult().getMediaInfoList(0).getSourceInfo().getStoreUri();
+            }
+
+        } catch (Exception e) {
+            e.printStackTrace();
+        }
+        return null;
+    }
 
 }

+ 47 - 0
fs-service/src/main/java/com/fs/course/service/impl/VodUploadMediaProcessListener.java

@@ -0,0 +1,47 @@
+package com.fs.course.service.impl;
+
+
+import com.volcengine.helper.VodUploadProgressEvent;
+import com.volcengine.helper.VodUploadProgressListener;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.redis.core.RedisTemplate;
+
+import java.util.concurrent.TimeUnit;
+
+@Slf4j
+public class VodUploadMediaProcessListener implements VodUploadProgressListener {
+
+    private long bytesUploaded = 0;
+    private long fileSize = -1;
+
+    private final String uploadId;
+    private final RedisTemplate<String, Object> redisTemplate;
+
+    public VodUploadMediaProcessListener(String uploadId,
+                                         RedisTemplate redisTemplate) {
+        this.uploadId = uploadId;
+        this.redisTemplate = redisTemplate;
+    }
+
+    @Override
+    public void progressChanged(VodUploadProgressEvent event) {
+        switch (event.getEventType()) {
+            case FILE_SIZE_EVENT:
+                fileSize = event.getByteSize();
+                break;
+
+            case UPLOAD_BYTES_EVENT:
+                bytesUploaded += event.getByteSize();
+                if (fileSize > 0) {
+                    int percent = Math.min(
+                            (int) (bytesUploaded * 100.0 / fileSize),
+                            100
+                    );
+                    redisTemplate.opsForValue()
+                            .set("vod:upload:" + uploadId, percent, 10, TimeUnit.MINUTES);
+                    log.info("uploadId={} progress={}%", uploadId, percent);
+                }
+                break;
+        }
+    }
+}

+ 2 - 2
fs-service/src/main/java/com/fs/his/service/impl/FsStorePaymentServiceImpl.java

@@ -867,7 +867,7 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
         try {
             TransferBillsResult transferBillsResult = transferService.transferBills(request);
             logger.info("商家转账支付完成:[msg:{}]", transferBillsResult);
-            return R.ok("发送红包成功").put("data", transferBillsResult);
+            return R.ok("发送红包成功").put("data", transferBillsResult).put("mchId", config.getMchId());
         } catch (Exception e) {
             logger.error("商家转账支付失败:参数: {} :原因: {}",JSON.toJSONString(param), e.getMessage(),e);
             throw new RuntimeException(e);
@@ -918,7 +918,7 @@ public class FsStorePaymentServiceImpl implements IFsStorePaymentService {
 
         try {
             TransferBatchesResult transferBatchesResult = transferService.transferBatches(request);
-            return R.ok("发送红包成功").put("orderCode", transferBatchesResult.getOutBatchNo()).put("batchId", transferBatchesResult.getBatchId());
+            return R.ok("发送红包成功").put("orderCode", transferBatchesResult.getOutBatchNo()).put("batchId", transferBatchesResult.getBatchId()).put("mchId", config.getMchId());
         } catch (Exception e) {
             logger.error("商家转账支付失败:参数: {} :原因: {}",JSON.toJSONString(param), e.getMessage(),e);
             throw new RuntimeException(e);

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

@@ -551,8 +551,9 @@ public interface QwExternalContactMapper extends BaseMapper<QwExternalContact> {
 
 
     List<QwExternalContact> getGroupChatUserByChatIdAndUserName(@Param("userId")String userId,@Param("userName")String userName,@Param("corpId") String corpId,@Param("chatId") String chatId);
-    @Select("select * from qw_external_contact where unionid = #{unionID} order by create_time asc limit 1 ")
-    QwExternalContact selectQwExternalByUnionID(String unionId);
+
+    @Select("select * from qw_external_contact where unionid = #{unionID} order by create_time asc  ")
+    List<QwExternalContact> selectQwExternalByUnionID(String unionId);
 
     @Select("<script> " +
             "WITH contact_analysis AS (\n" +

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

@@ -147,4 +147,7 @@ hsy:
   access_key: AKLTZTc4YTE4ZjI2OWViNDNjZGI2NjhiYTI5Njc5ZjA1Mzk
   secret_key: WXpjelpUYzFOakF5TUdObE5EZGtNR0ZsWXpKaU1tTmtZakk1WXpObE4yRQ==
   region: cn-north-1
+  role_access_key: AKLTNmMwNjJkNDFhYTVjNDIzYzhhNzEyZmZmZTlmYzBhNGM
+  role_secret_key: T0RaaFl6UmhZV1V4WXpKbU5EWTBNMkZpT0RNNU9UY3daak0wTjJFd09XUQ==
+  role_trn: trn:iam::2114522511:role/hylj
 

+ 3 - 10
fs-service/src/main/resources/application-config-dev.yml

@@ -87,26 +87,19 @@ nuonuo:
   key: 10924508
   secret: A2EB20764D304D16
 
-# 存储捅配置
 # 存储捅配置
 tencent_cloud_config:
   secret_id: AKIDiMq9lDf2EOM9lIfqqfKo7FNgM5meD0sT
   secret_key: u5SuS80342xzx8FRBukza9lVNHKNMSaB
-  bucket: cqsft-1323137866
+  bucket: myhk-1323137866
   app_id: 1323137866
   region: ap-chongqing
-  proxy: cqsft
-tmp_secret_config:
-  secret_id: AKIDCj7NSNAovtqeJpBau8GZ4CGB71thXIxX
-  secret_key: lTB5zwqqz7CNhzDOWivFWedgfTBgxgBT
-  bucket: fs-1319721001
-  app_id: 1319721001
-  region: ap-chongqing
-  proxy: fs
+  proxy: myhk
 cloud_host:
   company_name: 金康健
   projectCode: DEV
   spaceName:
+  volcengineUrl:
 headerImg:
   imgUrl: https://jz-cos-1356808054.cos.ap-chengdu.myqcloud.com/fs/20250515/0877754b59814ea8a428fa3697b20e68.png
 ipad:

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

@@ -42,8 +42,8 @@ wx:
       port: 6379
       timeout: 2000
     configs:
-      - appId: wx5d3e8c6791f0676d # 第一个公众号的appid  //公众号名称:善德佑
-        secret: d336ec63efd88b47f89b6a329a41c98d # 公众号的appsecret
+      - appId: wx3182d526fd77e0b3 # 第一个公众号的appid  //公众号名称:善德佑
+        secret: d608cd3f7aa1fb10b5bed2532746c6b5 # 公众号的appsecret
         token: PPKOdAlCoMO # 接口配置里的Token值
         aesKey: Eswa6VjwtVMCcw03qZy6fWllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值
 aifabu:  #爱链接
@@ -91,6 +91,7 @@ cloud_host:
   company_name: 恒春来
   projectCode: HCL
   spaceName:
+  volcengineUrl:
 #看课授权时显示的头像
 headerImg:
   imgUrl: http://hcl-1b2b.obs.cn-south-1.myhuaweicloud.com/fs/20250815/1755228988455.png

+ 1 - 0
fs-service/src/main/resources/application-config-druid-hzyy.yml

@@ -91,6 +91,7 @@ cloud_host:
   company_name: 弘珍医药
   projectCode: HZYY
   spaceName:
+  volcengineUrl:
 #看课授权时显示的头像
 headerImg:
   imgUrl: https://hzyy.obs.cn-north-4.myhuaweicloud.com/fs/20250616/1750067609692.png

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

@@ -95,11 +95,12 @@ cloud_host:
   company_name: 九州在线
   projectCode: JZZX
   spaceName:
+  volcengineUrl:
 headerImg:
   imgUrl: https://jiuzhouzaixian.obs.cn-southwest-2.myhuaweicloud.com/fs/20250623/1750665141214.png
 ipad:
   ipadUrl: http://ipad.jiuzhouzaixian.com
-  aiApi: http://1.95.196.10:3000/api
+  aiApi: http://49.232.181.28:3000/api
   voiceApi:
   commonApi:
 wx_miniapp_temp:

+ 5 - 1
fs-service/src/main/resources/mapper/course/FsCourseRedPacketLogMapper.xml

@@ -23,10 +23,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="periodId"    column="period_id"    />
         <result property="batchId"    column="batch_id"    />
         <result property="appId"    column="app_id"    />
+        <result property="mchId"    column="mch_id"    />
     </resultMap>
 
     <sql id="selectFsCourseRedPacketLogVo">
-        select log_id,watch_log_id, remark,out_batch_no,status,update_time,course_id, user_id, video_id, company_user_id, company_id, amount, create_time, qw_user_id,period_id,result,app_id,batch_id from fs_course_red_packet_log
+        select log_id,watch_log_id,mch_id, remark,out_batch_no,status,update_time,course_id, user_id, video_id, company_user_id, company_id, amount, create_time, qw_user_id,period_id,result,app_id,batch_id from fs_course_red_packet_log
     </sql>
 
     <select id="selectFsCourseRedPacketLogList" parameterType="FsCourseRedPacketLog" resultMap="FsCourseRedPacketLogResult">
@@ -112,6 +113,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="appId != null">app_id,</if>
             <if test="accBalanceBefore != null">acc_balance_before,</if>
             <if test="accBalanceAfter != null">acc_balance_after,</if>
+            <if test="mchId != null">mch_id,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="courseId != null">#{courseId},</if>
@@ -133,6 +135,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="appId != null">#{appId},</if>
             <if test="accBalanceBefore != null">#{accBalanceBefore},</if>
             <if test="accBalanceAfter != null">#{accBalanceAfter},</if>
+            <if test="mchId != null">#{mchId},</if>
         </trim>
     </insert>
 
@@ -155,6 +158,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="remark != null">remark = #{remark},</if>
             <if test="batchId != null">batch_id = #{batchId},</if>
             <if test="appId != null">app_id = #{appId},</if>
+            <if test="mchId != null">mch_id = #{mchId},</if>
         </trim>
         where log_id = #{logId}
     </update>

+ 11 - 0
fs-user-app/src/main/java/com/fs/app/controller/course/CourseFsUserController.java

@@ -6,11 +6,14 @@ import cn.hutool.core.util.ObjectUtil;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fs.app.annotation.UserOperationLog;
 import com.fs.app.controller.AppBaseController;
+import com.fs.common.annotation.Log;
 import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.domain.ResponseResult;
 import com.fs.app.annotation.Login;
 import com.fs.common.core.domain.model.LoginUser;
+import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.CloudHostUtils;
 import com.fs.common.utils.SecurityUtils;
 import com.fs.course.dto.BatchSendCourseDTO;
@@ -31,9 +34,14 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
+import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.web.bind.annotation.*;
+import org.springframework.web.multipart.MultipartFile;
 
 import javax.validation.Valid;
+import java.util.HashMap;
+import java.util.Map;
 
 @Api("会员-看课接口")
 @RestController
@@ -54,6 +62,9 @@ public class CourseFsUserController extends AppBaseController {
     @Autowired
     private IFsCourseQuestionBankService questionBankService;
 
+    @Autowired
+    private RedisTemplate redisTemplate;
+
 
 
     @Login