Преглед на файлове

Merge branch 'master' of http://1.14.104.71:10880/root/ylrz_his_scrm_java

caoliqin преди 1 ден
родител
ревизия
7b399b3e2b
променени са 19 файла, в които са добавени 1346 реда и са изтрити 176 реда
  1. 5 5
      fs-admin/src/main/java/com/fs/company/controller/CompanyController.java
  2. 10 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserCourseVideoController.java
  3. 20 0
      fs-admin/src/main/java/com/fs/fastGpt/GptRoleController.java
  4. 4 4
      fs-admin/src/main/java/com/fs/qw/controller/QwCompanyController.java
  5. 688 84
      fs-admin/src/main/java/com/fs/qw/controller/QwUserController.java
  6. 33 9
      fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java
  7. 6 4
      fs-service/src/main/java/com/fs/course/mapper/FsUserCourseVideoMapper.java
  8. 21 0
      fs-service/src/main/java/com/fs/course/param/BatchEditCoverParam.java
  9. 5 0
      fs-service/src/main/java/com/fs/course/service/IFsUserCourseVideoService.java
  10. 8 0
      fs-service/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  11. 24 0
      fs-service/src/main/java/com/fs/his/service/impl/FsStoreAfterSalesServiceImpl.java
  12. 25 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java
  13. 71 0
      fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreOrderScrmServiceImpl.java
  14. 26 0
      fs-service/src/main/java/com/fs/live/service/impl/LiveAfterSalesServiceImpl.java
  15. 95 12
      fs-service/src/main/java/com/fs/live/service/impl/LiveRedConfServiceImpl.java
  16. 7 0
      fs-service/src/main/resources/mapper/course/FsUserCourseVideoMapper.xml
  17. 9 0
      fs-service/src/main/resources/mapper/his/FsUserMapper.xml
  18. 94 58
      fs-user-app/src/main/java/com/fs/app/controller/live/LiveCompletionPointsController.java
  19. 195 0
      fs-user-app/src/main/java/com/fs/app/controller/live/LiveOrderController.java

+ 5 - 5
fs-admin/src/main/java/com/fs/company/controller/CompanyController.java

@@ -134,7 +134,7 @@ public class CompanyController extends BaseController
      * 新增企业
      */
     @PreAuthorize("@ss.hasPermi('company:company:add')")
-    @Log(title = "企业", businessType = BusinessType.INSERT)
+    @Log(title = "企业", businessType = BusinessType.INSERT, isStoreLog = true)
     @PostMapping
     public R add(@RequestBody Company company)
     {
@@ -153,7 +153,7 @@ public class CompanyController extends BaseController
      * 修改企业
      */
     @PreAuthorize("@ss.hasPermi('company:company:edit')")
-    @Log(title = "企业", businessType = BusinessType.UPDATE)
+    @Log(title = "企业", businessType = BusinessType.UPDATE, isStoreLog = true)
     @PutMapping
     public AjaxResult edit(@RequestBody Company company)
     {
@@ -186,7 +186,7 @@ public class CompanyController extends BaseController
      * 删除企业
      */
     @PreAuthorize("@ss.hasPermi('company:company:remove')")
-    @Log(title = "企业", businessType = BusinessType.DELETE)
+    @Log(title = "企业", businessType = BusinessType.DELETE, isStoreLog = true)
 	@DeleteMapping("/{companyIds}")
     public AjaxResult remove(@PathVariable Long[] companyIds)
     {
@@ -244,7 +244,7 @@ public class CompanyController extends BaseController
 
 
     @PreAuthorize("@ss.hasPermi('company:company:recharge')")
-    @Log(title = "企业转账", businessType = BusinessType.INSERT)
+    @Log(title = "企业转账", businessType = BusinessType.INSERT, isStoreLog = true)
     @PostMapping(value = "/recharge")
     @Transactional
     @RepeatSubmit
@@ -270,7 +270,7 @@ public class CompanyController extends BaseController
     }
 
     @PreAuthorize("@ss.hasPermi('company:company:deduct')")
-    @Log(title = "企业扣款", businessType = BusinessType.INSERT)
+    @Log(title = "企业扣款", businessType = BusinessType.INSERT, isStoreLog = true)
     @PostMapping(value = "/deduct")
     @Transactional
     @RepeatSubmit

+ 10 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserCourseVideoController.java

@@ -15,6 +15,7 @@ import com.fs.course.config.CourseConfig;
 import com.fs.course.domain.FsUserCourse;
 import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.mapper.FsUserCourseVideoMapper;
+import com.fs.course.param.BatchEditCoverParam;
 import com.fs.course.param.BatchRedUpdate;
 import com.fs.course.param.BatchVideoSvae;
 import com.fs.course.param.CourseVideoUpdates;
@@ -29,6 +30,7 @@ import com.github.pagehelper.PageInfo;
 import io.swagger.annotations.ApiOperation;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
 
 import java.util.HashMap;
@@ -270,4 +272,12 @@ public class FsUserCourseVideoController extends BaseController
     public AjaxResult batchDown(@PathVariable String[] videoIds) {
         return toAjax(fsUserCourseVideoService.batchDown(videoIds));
     }
+
+    @ApiOperation("批量修改视频封面图")
+    @PreAuthorize("@ss.hasPermi('course:userCourseVideo:batchEditCover')")
+    @Log(title = "课堂视频", businessType = BusinessType.UPDATE)
+    @PostMapping("/batchEditCover")
+    public AjaxResult batchEditCover(@Validated @RequestBody BatchEditCoverParam param) {
+        return toAjax(fsUserCourseVideoService.batchEditCover(param));
+    }
 }

+ 20 - 0
fs-admin/src/main/java/com/fs/fastGpt/GptRoleController.java

@@ -6,6 +6,7 @@ import com.fs.common.core.domain.AjaxResult;
 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.ServletUtils;
 import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.fastGpt.domain.FastGptRole;
 import com.fs.fastGpt.service.IFastGptRoleService;
@@ -49,6 +50,25 @@ public class GptRoleController extends BaseController
         return getDataTable(list);
     }
 
+    /**
+     * 查询应用列表
+     */
+    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptRole:newList')")
+    @GetMapping("/newList")
+    public TableDataInfo newList(FastGptRole fastGptRole)
+    {
+        startPage();
+        fastGptRole.setCompanyId(fastGptRole.getCompanyId());
+        List<FastGptRoleVO> list = fastGptRoleService.selectFastGptRoleListVONew(fastGptRole);
+        for (FastGptRoleVO fastGptRoleVO : list) {
+            String reminderWords = fastGptRoleVO.getReminderWords();
+            if (reminderWords!=null && reminderWords.length()>110) {
+                fastGptRoleVO.setReminderWords(reminderWords.substring(0,110)+"...");
+            }
+        }
+        return getDataTable(list);
+    }
+
     /**
      * 导出应用列表
      */

+ 4 - 4
fs-admin/src/main/java/com/fs/qw/controller/QwCompanyController.java

@@ -87,7 +87,7 @@ public class QwCompanyController extends BaseController
      * 导出企微主体列表
      */
     @PreAuthorize("@ss.hasPermi('qw:qwCompany:export')")
-    @Log(title = "企微主体", businessType = BusinessType.EXPORT)
+    @Log(title = "企微主体", businessType = BusinessType.EXPORT, isStoreLog = true)
     @GetMapping("/export")
     public AjaxResult export(QwCompany qwCompany)
     {
@@ -110,7 +110,7 @@ public class QwCompanyController extends BaseController
      * 新增企微主体
      */
     @PreAuthorize("@ss.hasPermi('qw:qwCompany:add')")
-    @Log(title = "企微主体", businessType = BusinessType.INSERT)
+    @Log(title = "企微主体", businessType = BusinessType.INSERT, isStoreLog = true)
     @PostMapping
     public AjaxResult add(@RequestBody QwCompany qwCompany)
     {
@@ -125,7 +125,7 @@ public class QwCompanyController extends BaseController
      * 修改企微主体
      */
     @PreAuthorize("@ss.hasPermi('qw:qwCompany:edit')")
-    @Log(title = "企微主体", businessType = BusinessType.UPDATE)
+    @Log(title = "企微主体", businessType = BusinessType.UPDATE, isStoreLog = true)
     @PutMapping
     public AjaxResult edit(@RequestBody QwCompany qwCompany)
     {
@@ -136,7 +136,7 @@ public class QwCompanyController extends BaseController
      * 删除企微主体
      */
     @PreAuthorize("@ss.hasPermi('qw:qwCompany:remove')")
-    @Log(title = "企微主体", businessType = BusinessType.DELETE)
+    @Log(title = "企微主体", businessType = BusinessType.DELETE, isStoreLog = true)
 	@DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable Long[] ids)
     {

+ 688 - 84
fs-admin/src/main/java/com/fs/qw/controller/QwUserController.java

@@ -1,32 +1,39 @@
 package com.fs.qw.controller;
 
+import cn.hutool.core.util.ObjectUtil;
 import com.alibaba.fastjson.JSON;
 import com.fs.common.annotation.Log;
 import com.fs.common.annotation.RepeatSubmit;
+import com.fs.common.constant.Constants;
 import com.fs.common.core.domain.R;
 import com.fs.common.core.controller.BaseController;
 import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.model.LoginUser;
 import com.fs.common.core.page.TableDataInfo;
 import com.fs.common.enums.BusinessType;
-import com.fs.common.utils.ServletUtils;
-import com.fs.company.domain.Company;
+import com.fs.common.exception.ServiceException;
+import com.fs.common.exception.user.UserPasswordNotMatchException;
+import com.fs.common.utils.MessageUtils;
+import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.domain.CompanyUser;
 import com.fs.company.mapper.CompanyUserMapper;
 import com.fs.company.service.ICompanyUserService;
+import com.fs.company.service.impl.CompanyDeptServiceImpl;
+import com.fs.fastGpt.domain.FastGptRole;
+import com.fs.fastGpt.mapper.FastGptRoleMapper;
+import com.fs.framework.manager.AsyncManager;
+import com.fs.framework.manager.factory.AsyncFactory;
 import com.fs.qw.domain.QwExternalContact;
-import com.fs.qw.domain.QwExternalContactTransferCompanyAudit;
 import com.fs.qw.domain.QwUser;
 import com.fs.qw.mapper.QwCompanyMapper;
 import com.fs.qw.mapper.QwExternalContactMapper;
-import com.fs.qw.param.QwFsUserParam;
-import com.fs.qw.param.QwUserBingParam;
-import com.fs.qw.param.QwUserListParam;
+import com.fs.qw.param.*;
 import com.fs.qw.service.IQwDeptService;
 import com.fs.qw.service.IQwExternalContactTransferCompanyAuditService;
 import com.fs.qw.service.IQwUserService;
 import com.fs.qw.vo.QwOptionsVO;
 import com.fs.qw.vo.QwUserVO;
+import com.fs.qw.vo.UpdateSendTypeVo;
 import com.fs.qwApi.domain.QwExternalContactAllListResult;
 import com.fs.qwApi.domain.inner.ExternalContact;
 import com.fs.qwApi.domain.inner.ExternalContactInfo;
@@ -34,14 +41,18 @@ import com.fs.qwApi.domain.inner.FollowInfo;
 import com.fs.qwApi.param.QwExternalListParam;
 import com.fs.qwApi.service.QwApiService;
 import com.fs.voice.utils.StringUtil;
+import com.github.pagehelper.PageHelper;
+import com.github.pagehelper.PageInfo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.authentication.AuthenticationManager;
+import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.Authentication;
 import org.springframework.web.bind.annotation.*;
 
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Set;
+import javax.annotation.Resource;
+import java.util.*;
 import java.util.stream.Collectors;
 
 /**
@@ -77,25 +88,612 @@ public class QwUserController extends BaseController {
     @Autowired
     private QwExternalContactMapper qwExternalContactMapper;
 
-    @GetMapping("/getQwUserAll")
-    public AjaxResult getQwUserAll(){
-        return AjaxResult.success(qwUserService.getQwUserAll());
+    @Autowired
+    private CompanyDeptServiceImpl companyDeptService;
+
+    @Resource
+    private AuthenticationManager authenticationManager;
+    @Autowired
+    private FastGptRoleMapper fastGptRoleMapper;
+
+    /**
+     * 查询企微员工列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:staffList')")
+    @GetMapping("/staffList")
+    public TableDataInfo staffList(QwUserListParam qwUser) {
+
+        // 添加企微部门查询条件
+        Long deptId = qwUser.getDeptId();
+        if(deptId!=null && qwUser.getCorpId()!=null){
+            List<Long> qwDeptIdList = new ArrayList<>();
+            if (deptId!=null){
+                qwDeptIdList.add(deptId);
+            }
+            // 本部门的下级部门
+            List<Long> deptList = qwUserService.selectDeptByParentId(deptId,qwUser.getCorpId());
+            if (!deptList.isEmpty()){
+                qwDeptIdList.addAll(deptList);
+            }
+            qwUser.setQwDeptIdList(qwDeptIdList);
+        }
+        startPage();
+        List<QwUserVO> list = qwUserService.selectQwUserListStaffVO(qwUser);
+        return getDataTable(list);
     }
 
     /**
-     * 获取企微信息
-     * **/
-    @GetMapping("/getQwUserInfo")
-    public R getQwUserInfo(QwFsUserParam param){
-        return R.ok().put("data",qwUserService.getQwUserInfo(param));
+     * 查询企微员工列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:staffListPost')")
+    @PostMapping("/staffListPost")
+    public TableDataInfo staffListPost(@RequestBody QwUserListParam qwUser) {
+        // 添加企微部门查询条件
+        Long deptId = qwUser.getDeptId();
+        if(deptId!=null && qwUser.getCorpId()!=null){
+            List<Long> qwDeptIdList = new ArrayList<>();
+            qwDeptIdList.add(deptId);
+            // 本部门的下级部门
+            List<Long> deptList = qwUserService.selectDeptByParentId(deptId,qwUser.getCorpId());
+            if (!deptList.isEmpty()){
+                qwDeptIdList.addAll(deptList);
+            }
+            qwUser.setQwDeptIdList(qwDeptIdList);
+        }
+        startPage();
+        List<QwUserVO> list = qwUserService.selectQwUserListStaffVO(qwUser);
+        return getDataTable(list);
+    }
+
+    /**
+     * 查询我的企微员工列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:myStaffList')")
+    @GetMapping("/myStaffList")
+    public TableDataInfo myStaffList(QwUserListParam qwUser) {
+        startPage();
+        qwUser.setCompanyId(qwUser.getCompanyId());
+        List<QwUserVO> list = qwUserService.selectQwUserListStaffVO(qwUser);
+        return getDataTable(list);
+    }
+
+
+    /**
+     * 导出企微员工列表
+     * @param qwUser
+     * @return AjaxResult
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:export')")
+    @Log(title = "企微员工", businessType = BusinessType.EXPORT)
+    @GetMapping("/exportStaff")
+    public AjaxResult export(QwUserListParam qwUser) {
+        qwUser.setCompanyId(qwUser.getCompanyId());
+        List<QwUserVO> list = qwUserService.selectQwUserListStaffVO(qwUser);
+        ExcelUtil<QwUserVO> util = new ExcelUtil<QwUserVO>(QwUserVO.class);
+        return util.exportExcel(list, "企微员工数据");
+    }
+
+    /**
+     * 导出企微用户列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:export')")
+    @Log(title = "企微用户", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(QwUser qwUser) {
+        qwUser.setCompanyId(qwUser.getCompanyId());
+        List<QwUser> list = qwUserService.selectQwUserList(qwUser);
+        ExcelUtil<QwUser> util = new ExcelUtil<QwUser>(QwUser.class);
+        return util.exportExcel(list, "企微用户数据");
+    }
+
+
+    /**
+     * 查询我的部门 企业微信员工列表
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:myDepartList')")
+    @GetMapping("/myDepartList")
+    public TableDataInfo myDepartList(QwUserListParam qwUser)
+    {
+
+        qwUser.setCompanyId(qwUser.getCompanyId());
+//        qwUser.setUserType(loginUser.getUser().getUserType());
+        List<Long> combinedList = new ArrayList<>();
+        //本部门
+        Long deptId = getLoginUser().getUser().getDeptId();
+        if (deptId!=null){
+            combinedList.add(deptId);
+        }
+        //本部门的下级部门
+        List<Long> deptList = companyDeptService.selectCompanyDeptByParentId(deptId);
+        if (!deptList.isEmpty()){
+            combinedList.addAll(deptList);
+        }
+
+        // 添加企微部门查询条件
+        Long qwDeptId = qwUser.getDeptId();
+        if(qwDeptId!=null && qwUser.getCorpId()!=null){
+            List<Long> qwDeptIdList = new ArrayList<>();
+            if (qwDeptId!=null){
+                qwDeptIdList.add(qwDeptId);
+            }
+            // 本部门的下级部门
+            List<Long> qwDeptList = qwUserService.selectDeptByParentId(qwDeptId,qwUser.getCorpId());
+            if (!qwDeptList.isEmpty()){
+                qwDeptIdList.addAll(qwDeptList);
+            }
+            qwUser.setQwDeptIdList(qwDeptIdList);
+        }
+
+
+        qwUser.setCuDeptIdList(combinedList);
+//        qwUser.setUserType(loginUser.getUser().getUserType());
+
+        startPage();
+        List<QwUserVO> list = qwUserService.selectQwUserListStaffVO(qwUser);
+        return getDataTable(list);
+    }
+
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/loginQwIpad")
+    public R loginQwIpad(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.loginQwIpad(loginParam);
+    }
+
+
+    /**
+     * 查询部门下的 企业微信账号
+     */
+    @PostMapping("/getQwUserByDept")
+    public R getQwUserByDept(@RequestBody QwUserByDeptParam deptParam){
+        deptParam.setCompanyId(deptParam.getCompanyId());
+        return R.ok().put("data",qwUserService.getQwUserByDept(deptParam)) ;
+    }
+
+
+
+
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/getQwIpad")
+    @RepeatSubmit
+    public R getQwIpad(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.getQwIpad(loginParam);
+    }
+
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/delQwIpad")
+    @RepeatSubmit
+    public R delQwIpad(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.delQwIpad(loginParam);
+    }
+
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/qrCodeStatus")
+    public R qrCodeStatus(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.qrCodeStatus(loginParam);
+    }
+    //输入验证码
+    @PostMapping("/qrCodeVerify")
+    public R qrCodeVerify(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.qrCodeVerify(loginParam);
+    }
+
+    @PostMapping("/outLoginQwIpad")
+    public R outLoginQwIpad(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.outLoginQwIpad(loginParam);
+    }
+
+    @PostMapping("/twoCode")
+    public R twoCode(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.getTwoCode(loginParam);
+    }
+    @PostMapping("/twoCodeStatus")
+    public R TwoCodeStatus(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.getTwoCodeStatus(loginParam);
+    }
+
+    @PostMapping("/getQwIpadStatus")
+    public R getQwIpadStatus(@RequestBody QwLoginHookParam loginParam){
+        return qwUserService.getLoginQwIpadStatus(loginParam);
+    }
+    /**
+     * 直接授权key
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:authAppKey')")
+    @PostMapping("/authAppKey")
+    public R authAppKey(@RequestBody QwUser param){
+        return qwUserService.authAppKey(param);
+    }
+
+    /**
+     * 输入授权key
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:authAppKey')")
+    @PostMapping("/handleInputAuthAppKey")
+    public R handleInputAuthAppKey(@RequestBody QwUser param){
+        return qwUserService.handleInputAuthAppKey(param);
     }
 
-   @GetMapping("/getMyQwCompanyList")
-    public R getMyQwCompanyList()
+
+    /**
+     * 登录企业微信(发起登录)
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/loginQwCode")
+    public R loginQwCode(@RequestBody QwLoginParam loginParam){
+        return qwUserService.loginQwCode(loginParam);
+    }
+
+    /**
+     * 登录请求-刷新获取二维码
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/loginQwCodeUrl")
+    public R loginQwCodeUrl(@RequestBody QwLoginParam loginParam){
+        return qwUserService.loginQwCodeUrl(loginParam);
+    }
+    /**
+     * 取redis里的登录二维码
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/getQwCodeUrl")
+    public R getQwCodeUrl(@RequestBody QwLoginParam loginParam) throws InterruptedException {
+        return qwUserService.getQwCodeUrl(loginParam);
+    }
+
+    /**
+     * 登录企业微信(传输验证信息)
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/loginQwCodeMsg")
+    public R loginQwCodeMsg(@RequestBody QwLoginParam loginParam){
+        return qwUserService.loginQwCodeMsg(loginParam);
+    }
+
+    /**
+     * 退出企业微信(退出插件)
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/logoutQwLogout")
+    public R logoutQwLogout(@RequestBody QwLoginParam loginParam){
+        return qwUserService.logoutQwLogout(loginParam);
+    }
+
+//    /**
+//     * 企业微信(修改登录状态)
+//     */
+//    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+//    @PostMapping("/modifyLoginQwStatus")
+//    public R modifyLoginQwStatus(@RequestBody QwLoginParam loginParam){
+//        return qwUserService.modifyLoginQwStatus(loginParam);
+//    }
+//
+    /**
+     * 查询企业微信登录状态
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:login')")
+    @PostMapping("/getLoginQwStatus")
+    public R getLoginQwStatus(@RequestBody QwLoginParam loginParam){
+        return qwUserService.getLoginQwStatus(loginParam);
+    }
+
+    @PutMapping
+    public AjaxResult updateUser(@RequestBody QwUser qwUser){
+        return toAjax(qwUserService.updateQwUser(qwUser));
+    }
+
+    /**
+     * 自动发课启用禁用
+     * @param qwUser
+     * @return
+     */
+    @PostMapping("/updateIsAuto")
+    @PreAuthorize("@ss.hasPermi('qw:user:isauto')")
+    public AjaxResult updateIsAuto(@RequestBody QwUser qwUser){
+        return toAjax(qwUserService.updateQwUser(qwUser));
+    }
+
+    /**
+     * 企业微信员工账号 绑定 云主机
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:loginIp')")
+    @Log(title = "绑定 云主机", businessType = BusinessType.INSERT)
+    @GetMapping("/qwBindCloudHost/{appKey}")
+    public R qwBindCloudHost(@PathVariable("appKey") String appKey){
+        return qwUserService.qwBindCloudHost(appKey);
+    }
+
+    /**
+     * 获取云主机的账密
+     *
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:cloudAP')")
+    @PostMapping("/selectCloudAP")
+    public R selectCloudAP(@RequestBody QwCloudAPParam param) throws Exception {
+        return qwUserService.selectCloudAP(param);
+    }
+
+    /**
+     * 根据销售账号密码 获取 他的所有企业微信账号以及云主机和账号密码
+     */
+    @PostMapping("/selectCloudByCompany")
+    public R selectCloudByCompany(@RequestBody QwCloudIPByCompanyParam param) throws Exception {
+
+        // 用户验证
+        Authentication authentication = null;
+        try
+        {
+            // 该方法会去调用UserDetailsServiceImpl.loadUserByUsername
+            authentication = authenticationManager
+                    .authenticate(new UsernamePasswordAuthenticationToken(param.getCompanyAdmin(), param.getCompanyPassWord()));
+        }
+        catch (Exception e)
+        {
+            if (e instanceof BadCredentialsException)
+            {
+                AsyncManager.me().execute(AsyncFactory.recordLogininfor(param.getCompanyAdmin(), Constants.LOGIN_FAIL, MessageUtils.message("user.password.not.match")));
+                throw new UserPasswordNotMatchException();
+            }
+            else
+            {
+                AsyncManager.me().execute(AsyncFactory.recordLogininfor(param.getCompanyAdmin(), Constants.LOGIN_FAIL, e.getMessage()));
+                throw new ServiceException(e.getMessage());
+            }
+        }
+        LoginUser loginUser=(LoginUser) authentication.getPrincipal();
+
+        return qwUserService.selectCloudByCompany(loginUser.getUser().getCompanyId(),loginUser.getUser().getUserId());
+    }
+
+
+    /**
+     * 企业微信员工账号 绑定 云主机
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:bindIp')")
+    @GetMapping("/qwBindCloudHostByIp/{appKey}/{IP}")
+    public R qwBindCloudHostByIp(@PathVariable("appKey") String appKey,@PathVariable("IP") String IP){
+        return qwUserService.qwBindCloudHostByIp(appKey,IP);
+    }
+
+    /**
+     * 企业微信员工账号 解除绑定 云主机
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:loginIpOut')")
+    @Log(title = "解除绑定 云主机", businessType = BusinessType.UPDATE)
+    @GetMapping("/qwUnbindCloudHost/{appKey}")
+    public R qwUnbindCloudHost(@PathVariable("appKey") String appKey){
+        return qwUserService.qwUnbindCloudHost(appKey);
+    }
+
+    /**
+     * 根据销售名称模糊查询
+     * @param qwUserName  名称
+     * @return  list
+     */
+    @GetMapping("/getQwUserListLikeName")
+    public R getQwUserListLikeName(@RequestParam(required = false) String qwUserName,
+                                   @RequestParam(required = false, defaultValue = "1") Integer pageNum,
+                                   @RequestParam(required = false, defaultValue = "10") Integer pageSize) {
+        Map<String,Object> params = new HashMap<>();
+        params.put("qwUserName", qwUserName);
+
+        PageHelper.startPage(pageNum, pageSize);
+        List<QwOptionsVO> qwUserList = companyUserService.selectQwUserListLikeName(params);
+        return R.ok().put("data", new PageInfo<>(qwUserList));
+    }
+
+    /**
+     * 查询企微用户列表
+     */
+//    @PreAuthorize("@ss.hasPermi('qw:user:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(QwUserListParam qwUser)
     {
-        List<QwOptionsVO> list = qwUserService.selectQwCompanyListOptionsVOAll();
+        startPage();
+        qwUser.setCompanyId(qwUser.getCompanyId());
+        if (ObjectUtil.isNotEmpty(qwUser.getIsRemark())&&qwUser.getIsRemark().equals("1")){
+            qwUser.setCompanyUserId(getLoginUser().getUser().getUserId());
+        }else if (ObjectUtil.isNotEmpty(qwUser.getIsRemark())&&qwUser.getIsRemark().equals("2")){
+            qwUser.setDeptId(getLoginUser().getDeptId());
+            qwUser.setCorpId(null);
+        }
+
+        List<QwUserVO> list = qwUserService.selectQwUserListVO(qwUser);
+        return getDataTable(list);
+    }
+
+    /**
+     * 查询企微用户列表
+     */
+
+    @GetMapping("/userList")
+    public TableDataInfo userList(QwUserListParam qwUser)
+    {
+        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwUser.setCompanyId(loginUser.getCompany().getCompanyId());
+
+        List<QwUserVO> list = qwUserService.selectAllQwUserListVO(qwUser);
+        return getDataTable(list);
+    }
+    //    /**
+//     * 查询我的企微用户列表
+//     */
+//    @PreAuthorize("@ss.hasPermi('qw:user:myList')")
+//    @GetMapping("/myList")
+//    public TableDataInfo myList(QwUserParam qwUser)
+//    {
+//        startPage();
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        qwUser.setCompanyId(loginUser.getCompany().getCompanyId());
+//        qwUser.setCompanyUserId(loginUser.getCompany().getUserId());
+//        List<QwUserVO> list = qwUserService.selectQwUserListVO(qwUser);
+//        return getDataTable(list);
+//    }
+    @GetMapping("/getQwAllUserList")
+    public R getQwAllUserList(@RequestParam String corpId, @RequestParam Long companyId)
+    {
+        List<QwUserVO> list = companyUserService.selectCompanyQwUserList(corpId, companyId);
         return  R.ok().put("data",list);
     }
+    /**
+     * 查询企微用户列表-下拉框
+     */
+    @GetMapping("/qwList")
+    public TableDataInfo getQwList(QwUser qwUser)
+    {
+        startPage();
+
+        if(ObjectUtil.notEqual(qwUser.getDisableCompanyId(),1)){
+            qwUser.setCompanyId(qwUser.getCompanyId());
+        }
+        List<QwUser> list = qwUserService.selectQwUserList(qwUser);
+        return getDataTable(list);
+    }
+    /**
+     * 查询企微员工列表-用于员工管理绑定
+     */
+    @GetMapping("/getQwUserList")
+    public R getQwUserList()
+    {
+        QwUserParam qwUser = new QwUserParam();
+        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByCompanyId(getLoginUser().getUser().getCompanyId());
+        qwUser.setCorpId(strings);
+        if (strings==null||strings.size()==0){
+            return  R.ok().put("data",null);
+        }
+        qwUser.setIsDel(0);
+        List<QwUserVO> list = qwUserService.selectQwUserListBindVO(qwUser);
+        return  R.ok().put("data",list);
+    }
+
+    @GetMapping("/getMyQwUserList")
+    public R getMyQwUserList()
+    {
+        List<QwOptionsVO> list = qwUserService.selectQwUserListOptionsVOByCompanyUserId(getLoginUser().getUser().getUserId());
+        return  R.ok().put("data",list);
+    }
+    @GetMapping("/getMyQwCompanyList/{companyId}")
+    public R getMyQwCompanyList(@PathVariable Long companyId)
+    {
+        List<QwOptionsVO> list = qwUserService.selectQwCompanyListOptionsVOByCompanyId(companyId);
+        return  R.ok().put("data",list);
+    }
+    /**
+     * 获取企微用户详细信息
+     */
+
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(qwUserService.selectQwUserVOById(id));
+    }
+    /**
+     * 批量查询 企微用户详细信息
+     */
+    @GetMapping(value = "/getInfo/{ids}")
+    public AjaxResult getInfoByIds(@PathVariable("ids") Long[] ids)
+    {
+        return AjaxResult.success(qwUserService.selectQwUserVOByIds(ids));
+    }
+
+
+//    /**
+//     * 新增企微用户
+//     */
+//    @PreAuthorize("@ss.hasPermi('qw:user:add')")
+//    @Log(title = "企微用户", businessType = BusinessType.INSERT)
+//    @PostMapping
+//    public R add()
+//    {
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//
+//        return R.ok(qwUserService.syncQwUser(loginUser.getCompany().getCompanyId()));
+//    }
+
+
+
+    /**
+     * 同步企微用户
+     */
+    @RepeatSubmit
+    @PreAuthorize("@ss.hasPermi('qw:user:sync')")
+    @Log(title = "企微用户", businessType = BusinessType.INSERT)
+    @PostMapping("sync/{corpId}")
+    public R sync(@PathVariable String corpId)
+    {
+        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByCompanyId(getLoginUser().getUser().getCompanyId());
+        for (String string : strings) {
+
+            if (string.equals(corpId)){
+                qwUserService.syncQwUser(string);
+                qwDeptService.insertOrUpdateQwDept(string);
+                logger.info("同步完成");
+            }
+        }
+        return R.ok();
+    }
+    @RepeatSubmit
+    @PreAuthorize("@ss.hasPermi('qw:user:sync')")
+    @Log(title = "同步企微用户名称", businessType = BusinessType.INSERT)
+    @PostMapping("syncName/{corpId}")
+    public R syncName(@PathVariable String corpId)
+    {
+        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByCompanyId(getLoginUser().getUser().getCompanyId());
+        for (String string : strings) {
+            if (string.equals(corpId)){
+                qwUserService.syncQwUserName(string);
+            }
+        }
+        return R.ok();
+    }
+    /**
+     * 绑定AI客服
+     */
+//    @PreAuthorize("@ss.hasPermi('qw:user:bindAi')")
+    @Log(title = "企微用户", businessType = BusinessType.UPDATE)
+    @PutMapping("/bindAi")
+    @RepeatSubmit
+    public R edit(@RequestBody QwUserBindAi param)
+    {
+        QwUser qwUser=new QwUser();
+        qwUser.setId(param.getId());
+        qwUser.setFastGptRoleId(param.getFastGptRoleId());
+        FastGptRole role = fastGptRoleMapper.selectFastGptRoleByRoleId(param.getFastGptRoleId());
+
+        if (role.getBindCorpId()!=null){
+            if (role.getBindCorpId().equals(param.getCorpId())){
+                qwUserService.updateQwUser(qwUser);
+            }else {
+                return R.error("该角色已绑定其他企业");
+            }
+        }else {
+            int i = qwUserService.updateQwUser(qwUser);
+
+            FastGptRole fastGptRole=new FastGptRole();
+            fastGptRole.setRoleId(param.getFastGptRoleId());
+            fastGptRole.setBindCorpId(param.getCorpId());
+            fastGptRoleMapper.updateFastGptRole(fastGptRole);
+
+            if (i>0){
+                return R.ok();
+            }else {
+                return R.error("绑定失败");
+            }
+        }
+
+        return R.ok();
+    }
+
+    /**
+     * 解除应用绑定
+     */
+//    @PreAuthorize("@ss.hasPermi('fastGpt:fastGptRole:relieve')")
+    @Log(title = "解除应用", businessType = BusinessType.UPDATE)
+    @GetMapping("/relieveFastGptRoleById/{id}")
+    public R relieveFastGptRoleById(@PathVariable("id") Long id)
+    {
+        return qwUserService.relieveFastGptRoleById(id);
+    }
 
     /**
      * 绑定企微用户
@@ -188,6 +786,7 @@ public class QwUserController extends BaseController {
         }
         return R.error("绑定失败");
 
+
     }
 
 
@@ -201,6 +800,52 @@ public class QwUserController extends BaseController {
         syncMyQwExternalContact(qu,null);
     }
 
+    /** 修改企微用户的欢迎语 */
+    @PostMapping("/weclomeQwUser")
+    public R weclomeQwUser(@RequestBody QwUser qwUser) throws Exception {
+
+
+        return  qwUserService.weclomeQwUser(qwUser);
+    }
+
+    /**
+     * 删除企微用户
+     */
+    @PreAuthorize("@ss.hasPermi('qw:user:remove')")
+    @Log(title = "企微用户", businessType = BusinessType.DELETE)
+    @DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(qwUserService.deleteQwUserByIds(ids));
+    }
+
+
+    /**
+     * 获取企业微信用户列表
+     */
+    @GetMapping("/qwUserList/{corpId}")
+    public TableDataInfo qwUserList(@PathVariable String corpId)
+    {
+
+//        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+//        Long companyId = loginUser.getCompany().getCompanyId();
+
+        List<QwOptionsVO> list = qwUserService.selectQwUserListOptionsVO(corpId);
+        return getDataTable(list);
+    }
+    @GetMapping("/getQwUserAll")
+    public AjaxResult getQwUserAll(){
+        return AjaxResult.success(qwUserService.getQwUserAll());
+    }
+
+    /**
+     * 获取企微信息
+     * **/
+    @GetMapping("/getQwUserInfo")
+    public R getQwUserInfo(QwFsUserParam param){
+        return R.ok().put("data",qwUserService.getQwUserInfo(param));
+    }
+
     public R  syncMyQwExternalContact(QwUser qwUser,String getNextCursor )  {
 
         String qwUserId = qwUser.getQwUserId();
@@ -286,79 +931,38 @@ public class QwUserController extends BaseController {
         return R.ok();
     }
 
-
-    /**
-     * 同步企微用户
-     */
-    @RepeatSubmit
-    @PreAuthorize("@ss.hasPermi('qw:user:sync')")
-    @Log(title = "企微用户", businessType = BusinessType.INSERT)
-    @PostMapping("sync/{corpId}")
-    public R sync(@PathVariable("corpId") String corpId)
-    {
-
-        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByAll();
-        for (String string : strings) {
-
-            if (string.equals(corpId)){
-                qwUserService.syncQwUser(string);
-                qwDeptService.insertOrUpdateQwDept(string);
-                logger.info("同步完成");
-            }
-        }
-        return R.ok();
-    }
-
-    /**
-     * 获取企微用户详细信息
-     */
-
-    @GetMapping(value = "/{id}")
-    public AjaxResult getInfo(@PathVariable("id") Long id)
-    {
-        return AjaxResult.success(qwUserService.selectQwUserVOById(id));
-    }
-    /**
-     * 批量查询 企微用户详细信息
-     */
-    @GetMapping(value = "/getInfo/{ids}")
-    public AjaxResult getInfoByIds(@PathVariable("ids") Long[] ids)
-    {
-        return AjaxResult.success(qwUserService.selectQwUserVOByIds(ids));
+    //    /**
+//     * 重启云主机
+//     * @return
+//     */
+//    @PutMapping("/restartHost")
+//    public R restartCloudHost(@RequestParam String serverIp) {
+//        return qwUserService.restartCloudHost(serverIp);
+//    }
+    @PostMapping("/updateSendType")
+    public R updateSendType(@RequestBody UpdateSendTypeVo vo) {
+        return qwUserService.updateSendType(vo);
     }
 
-    @RepeatSubmit
-    @PreAuthorize("@ss.hasPermi('qw:user:sync')")
-    @Log(title = "同步企微用户名称", businessType = BusinessType.INSERT)
-    @PostMapping("syncName/{corpId}")
-    public R syncName(@PathVariable("corpId") String corpId)
-    {
-        List<String> strings = qwCompanyMapper.selectQwCompanyCorpIdListByAll();
-        for (String string : strings) {
-            if (string.equals(corpId)){
-                qwUserService.syncQwUserName(string);
-            }
-        }
+    @GetMapping("/changeVideoStatus")
+    public R changeVideoStatus(Long id) {
+        qwUserService.changeVideoStatus(id);
         return R.ok();
     }
 
-
-    /**
-     * 查询企微用户列表
-     */
-
-    @GetMapping("/userList")
-    public TableDataInfo userList(QwUserListParam qwUser)
+    @GetMapping("/companyQwUserlist")
+    public TableDataInfo companyQwUserlist(@RequestParam Long companyId,
+                                           @RequestParam String corpId,
+                                           @RequestParam(required = false) String nickName)
     {
         startPage();
-        List<QwUserVO> list = qwUserService.selectAllQwUserListVO(qwUser);
+        List<QwUserVO> list = qwUserService.selectQwUserListByCompanyIdAndCorpIdAndNickName(companyId, corpId, nickName);
         return getDataTable(list);
     }
 
-    @GetMapping("/getQwAllUserList")
-    public R getQwAllUserList(@RequestParam String corpId, @RequestParam Long companyId)
+    @GetMapping("/updateFastGptRoleStatusById/{id}")
+    public R updateFastGptRoleStatusById(@PathVariable Long id)
     {
-        List<QwUserVO> list = companyUserService.selectCompanyQwUserList(corpId, companyId);
-        return  R.ok().put("data",list);
+        return qwUserService.updateQwUserFastGptRoleStatusById(id);
     }
 }

+ 33 - 9
fs-service/src/main/java/com/fs/company/service/impl/CompanyServiceImpl.java

@@ -522,13 +522,23 @@ public class CompanyServiceImpl implements ICompanyService
                 orderMap.setTuiMoneyStatus(1);
                 storeOrderMapper.updateFsStoreOrder(orderMap);
                 // order.getPayRemain() 数据库实际没有这个字段了 直接使用 应付金额
-                BigDecimal money = order.getPayPrice();
-                company.setMoney(company.getMoney().add(money));
+                // 卓美,按照润天进行百分比进行分佣
+                String json =configService.selectConfigByKey("store.config");
+                com.fs.store.config.StoreConfig config= JSONUtil.toBean(json, com.fs.store.config.StoreConfig.class);
+                //支付金额-(订单金额*rate%)
+                BigDecimal tuiMoney = BigDecimal.ZERO;
+                if (config != null && config.getTuiMoneyRate() != null) {
+                    Double rate = config.getTuiMoneyRate() / 100d;
+                    tuiMoney = order.getPayPrice().subtract(order.getTotalPrice().multiply(new BigDecimal(rate)));
+                } else {
+                    tuiMoney = order.getPayPrice();
+                }
+                company.setMoney(company.getMoney().add(tuiMoney));
                 companyMapper.updateCompany(company);
                 CompanyMoneyLogs log=new CompanyMoneyLogs();
                 log.setCompanyId(company.getCompanyId());
                 log.setRemark("佣金入账");
-                log.setMoney(money);
+                log.setMoney(tuiMoney);
                 log.setLogsType(3);
                 log.setBalance(company.getMoney());
                 log.setCreateTime(new Date());
@@ -547,20 +557,34 @@ public class CompanyServiceImpl implements ICompanyService
         if(order.getCompanyId()>0){
             Company company=companyMapper.selectCompanyByIdForUpdate(order.getCompanyId());
             if(company!=null){
-                company.setMoney(company.getMoney().subtract(order.getTuiMoney()));
-                company.setTuiMoney(company.getTuiMoney().subtract(order.getTuiMoney()));
+                // 卓美,按照润天进行百分比进行分佣
+                String json =configService.selectConfigByKey("store.config");
+                com.fs.store.config.StoreConfig config= JSONUtil.toBean(json, com.fs.store.config.StoreConfig.class);
+                //支付金额-(订单金额*rate%)
+                BigDecimal tuiMoney = BigDecimal.ZERO;
+                if (config != null && config.getTuiMoneyRate() != null) {
+                    Double rate = config.getTuiMoneyRate() / 100d;
+                    tuiMoney = order.getPayPrice().subtract(order.getTotalPrice().multiply(new BigDecimal(rate)));
+                } else {
+                    tuiMoney = order.getPayPrice();
+                }
+                company.setMoney(company.getMoney().add(tuiMoney));
                 companyMapper.updateCompany(company);
                 //写入日志
                 CompanyMoneyLogs log=new CompanyMoneyLogs();
                 log.setCompanyId(order.getCompanyId());
-                log.setRemark("订单佣金退款");
-                log.setMoney(order.getTuiMoney().multiply(new BigDecimal(-1)));
-                log.setLogsType(4);
+                log.setRemark("佣金入账");
+                log.setMoney(tuiMoney);
+                log.setLogsType(3);
                 log.setBalance(company.getMoney());
                 log.setCreateTime(new Date());
                 log.setBusinessId(order.getOrderId().toString());
                 moneyLogsMapper.insertCompanyMoneyLogs(log);
-
+                LiveOrder liveOrder = new LiveOrder();
+                liveOrder.setOrderId(order.getOrderId());
+                liveOrder.setTuiMoneyStatus(1);
+                liveOrder.setTuiMoney(tuiMoney);
+                liveOrderMapper.updateLiveOrder(liveOrder);
             }
         }
     }

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

@@ -3,10 +3,7 @@ package com.fs.course.mapper;
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.course.domain.FsUserCourseVideo;
 import com.fs.course.domain.FsVideoResource;
-import com.fs.course.param.CourseVideoUpdates;
-import com.fs.course.param.FsCourseListBySidebarParam;
-import com.fs.course.param.FsUserCourseVideoListUParam;
-import com.fs.course.param.FsUserCourseVideoParam;
+import com.fs.course.param.*;
 import com.fs.course.param.newfs.UserCourseVideoPageParam;
 import com.fs.course.vo.*;
 import com.fs.course.vo.newfs.FsUserCourseVideoPageListVO;
@@ -287,4 +284,9 @@ public interface FsUserCourseVideoMapper extends BaseMapper<FsUserCourseVideo> {
      * 下架
      */
     int batchDown(String[] videoIds);
+
+    /**
+     * 批量修改视频封面
+     */
+    int batchEditCover(BatchEditCoverParam param);
 }

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

@@ -0,0 +1,21 @@
+package com.fs.course.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotBlank;
+import javax.validation.constraints.NotEmpty;
+import java.util.List;
+
+@Data
+public class BatchEditCoverParam {
+    /**
+     * 封面图
+     */
+    @NotBlank(message = "封面图不能为空")
+    private String thumbnail;
+    /**
+     * 视频小节
+     */
+    @NotEmpty(message = "小节ID不能为空")
+    private List<Long> videoIds;
+}

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

@@ -249,4 +249,9 @@ public interface IFsUserCourseVideoService extends IService<FsUserCourseVideo> {
      * 视频下架
      */
     int batchDown(String[] videoIds);
+
+    /**
+     * 批量修改视频封面
+     */
+    int batchEditCover(BatchEditCoverParam param);
 }

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

@@ -4460,5 +4460,13 @@ public class FsUserCourseVideoServiceImpl extends ServiceImpl<FsUserCourseVideoM
     public int batchDown(String[] videoIds) {
         return baseMapper.batchDown(videoIds);
     }
+
+    /**
+     * 批量修改视频封面
+     */
+    @Override
+    public int batchEditCover(BatchEditCoverParam param) {
+        return baseMapper.batchEditCover(param);
+    }
 }
 

+ 24 - 0
fs-service/src/main/java/com/fs/his/service/impl/FsStoreAfterSalesServiceImpl.java

@@ -646,6 +646,30 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService {
     @Override
     @Transactional
     public R applyAfterSales(long uesrId, FsStoreAfterSalesApplyParam param) {
+        // 查询配置:是否删除历史售后数据
+        try {
+            String deleteAfterSalesConfig = configService.selectConfigByKey("delete_after_sales");
+            if (StringUtils.isNotEmpty(deleteAfterSalesConfig) && "true".equalsIgnoreCase(deleteAfterSalesConfig.trim())) {
+                // 查询历史售后数据,将 is_del 设置为 1
+                FsStoreAfterSales queryAfterSales = new FsStoreAfterSales();
+                queryAfterSales.setOrderId(param.getOrderId());
+                List<FsStoreAfterSales> historyAfterSalesList = fsStoreAfterSalesMapper.selectFsStoreAfterSalesList(queryAfterSales);
+                if (historyAfterSalesList != null && !historyAfterSalesList.isEmpty()) {
+                    for (FsStoreAfterSales historyAfterSales : historyAfterSalesList) {
+                        if (historyAfterSales.getIsDel() != null && historyAfterSales.getIsDel() == 0) {
+                            FsStoreAfterSales updateAfterSales = new FsStoreAfterSales();
+                            updateAfterSales.setId(historyAfterSales.getId());
+                            updateAfterSales.setIsDel(1);
+                            fsStoreAfterSalesMapper.updateFsStoreAfterSales(updateAfterSales);
+                            logger.info("删除历史售后数据,售后ID:{},订单ID:{}", historyAfterSales.getId(), param.getOrderId());
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            logger.error("查询或更新历史售后数据失败", e);
+        }
+        
         FsStoreOrder order = fsStoreOrderMapper.selectFsStoreOrderByOrderId(param.getOrderId());
         if (!order.getUserId().equals(uesrId)) {
             throw new CustomException("非法操作");

+ 25 - 0
fs-service/src/main/java/com/fs/hisStore/service/impl/FsStoreAfterSalesScrmServiceImpl.java

@@ -277,6 +277,31 @@ public class FsStoreAfterSalesScrmServiceImpl implements IFsStoreAfterSalesScrmS
     @Transactional
     public R applyForAfterSales(long userId, FsStoreAfterSalesParam storeAfterSalesParam) {
         logger.info("申请退款请求信息:"+JSONUtil.toJsonStr(storeAfterSalesParam));
+        
+        // 查询配置:是否删除历史售后数据
+        try {
+            String deleteAfterSalesConfig = configService.selectConfigByKey("delete_after_sales");
+            if (StringUtils.isNotEmpty(deleteAfterSalesConfig) && "true".equalsIgnoreCase(deleteAfterSalesConfig.trim())) {
+                // 查询历史售后数据,将 is_del 设置为 1
+                FsStoreAfterSalesScrm queryAfterSales = new FsStoreAfterSalesScrm();
+                queryAfterSales.setOrderCode(storeAfterSalesParam.getOrderCode());
+                List<FsStoreAfterSalesScrm> historyAfterSalesList = fsStoreAfterSalesMapper.selectFsStoreAfterSalesList(queryAfterSales);
+                if (historyAfterSalesList != null && !historyAfterSalesList.isEmpty()) {
+                    for (FsStoreAfterSalesScrm historyAfterSales : historyAfterSalesList) {
+                        if (historyAfterSales.getIsDel() != null && historyAfterSales.getIsDel() == 0) {
+                            FsStoreAfterSalesScrm updateAfterSales = new FsStoreAfterSalesScrm();
+                            updateAfterSales.setId(historyAfterSales.getId());
+                            updateAfterSales.setIsDel(1);
+                            fsStoreAfterSalesMapper.updateFsStoreAfterSales(updateAfterSales);
+                            logger.info("删除历史售后数据,售后ID:{},订单号:{}", historyAfterSales.getId(), storeAfterSalesParam.getOrderCode());
+                        }
+                    }
+                }
+            }
+        } catch (Exception e) {
+            logger.error("查询或更新历史售后数据失败", e);
+        }
+        
         FsStoreOrderScrm order=orderService.selectFsStoreOrderByOrderCode(storeAfterSalesParam.getOrderCode());
         Integer orderStatus = order.getStatus();
         if(!order.getUserId().equals(userId)){

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

@@ -363,6 +363,12 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
     @Autowired
     private FsStoreAfterSalesItemScrmMapper fsStoreAfterSalesItemMapper;
 
+    @Autowired
+    private FsStoreAfterSalesScrmMapper fsStoreAfterSalesScrmMapper;
+
+    @Autowired
+    private FsStoreAfterSalesStatusScrmMapper fsStoreAfterSalesStatusScrmMapper;
+
     @Autowired
     private FsPackageOrderMapper fsPackageOrderMapper;
 
@@ -2581,6 +2587,71 @@ public class FsStoreOrderScrmServiceImpl implements IFsStoreOrderScrmService {
         if (order.getTuiUserId() != null && order.getTuiUserId() > 0) {
             userService.subTuiMoney(order);
         }
+        // 卓美财务要求,如果退款就必须生成售后订单
+        // 检查配置,如果delete_after_sales为true,创建退款成功的售后订单
+        String deleteAfterSalesConfig = configService.selectConfigByKey("delete_after_sales");
+        if ("true".equals(deleteAfterSalesConfig)) {
+            try {
+                // 检查是否已存在售后订单
+                FsStoreAfterSalesScrm existingAfterSales = new FsStoreAfterSalesScrm();
+                existingAfterSales.setOrderCode(order.getOrderCode());
+                List<FsStoreAfterSalesScrm> existingList = fsStoreAfterSalesScrmMapper.selectFsStoreAfterSalesList(existingAfterSales);
+                
+                // 如果不存在售后订单,则创建
+                if (existingList == null || existingList.isEmpty()) {
+                    // 创建售后订单
+                    FsStoreAfterSalesScrm storeAfterSales = new FsStoreAfterSalesScrm();
+                    storeAfterSales.setOrderCode(order.getOrderCode());
+                    storeAfterSales.setRefundAmount(order.getPayMoney());
+                    storeAfterSales.setServiceType(0); // 0仅退款
+                    storeAfterSales.setReasons("退款成功自动生成售后订单");
+                    storeAfterSales.setExplains("退款成功自动生成售后订单");
+                    storeAfterSales.setExplainImg(null);
+                    storeAfterSales.setStatus(AfterSalesStatusEnum.STATUS_4.getValue()); // 4财务已审核(退款成功)
+                    storeAfterSales.setSalesStatus(3); // 3已完成
+                    storeAfterSales.setCreateTime(Timestamp.valueOf(LocalDateTime.now()));
+                    storeAfterSales.setIsDel(0);
+                    storeAfterSales.setUserId(order.getUserId());
+                    storeAfterSales.setOrderStatus(order.getStatus());
+                    storeAfterSales.setCompanyId(order.getCompanyId());
+                    storeAfterSales.setCompanyUserId(order.getCompanyUserId());
+                    if (order.getPackageJson() != null) {
+                        storeAfterSales.setPackageJson(order.getPackageJson());
+                    }
+                    if (order.getIsPackage() != null) {
+                        storeAfterSales.setIsPackage(order.getIsPackage());
+                    }
+                    fsStoreAfterSalesScrmMapper.insertFsStoreAfterSales(storeAfterSales);
+
+                    // 创建售后商品详情
+                    List<FsStoreOrderItemVO> orderItemVO = fsStoreOrderItemMapper.selectFsStoreOrderItemListByOrderId(order.getId());
+                    if (orderItemVO != null && !orderItemVO.isEmpty()) {
+                        for (FsStoreOrderItemVO item : orderItemVO) {
+                            FsStoreAfterSalesItemScrm afterSalesItem = new FsStoreAfterSalesItemScrm();
+                            afterSalesItem.setStoreAfterSalesId(storeAfterSales.getId());
+                            afterSalesItem.setProductId(item.getProductId());
+                            afterSalesItem.setNum(Math.toIntExact(item.getNum()));
+                            afterSalesItem.setJsonInfo(item.getJsonInfo());
+                            afterSalesItem.setIsDel(0);
+                            fsStoreAfterSalesItemMapper.insertFsStoreAfterSalesItem(afterSalesItem);
+                        }
+                    }
+
+                    // 创建售后状态记录
+                    FsStoreAfterSalesStatusScrm storeAfterSalesStatus = new FsStoreAfterSalesStatusScrm();
+                    storeAfterSalesStatus.setStoreAfterSalesId(storeAfterSales.getId());
+                    storeAfterSalesStatus.setChangeType(AfterSalesStatusEnum.STATUS_4.getValue());
+                    storeAfterSalesStatus.setChangeMessage(AfterSalesStatusEnum.STATUS_4.getDesc());
+                    storeAfterSalesStatus.setChangeTime(Timestamp.valueOf(LocalDateTime.now()));
+                    storeAfterSalesStatus.setOperator("系统");
+                    fsStoreAfterSalesStatusScrmMapper.insertFsStoreAfterSalesStatus(storeAfterSalesStatus);
+                }
+            } catch (Exception e) {
+                logger.error("创建售后订单失败", e);
+                // 不抛出异常,避免影响退款流程
+            }
+        }
+
         return R.ok();
 
     }

+ 26 - 0
fs-service/src/main/java/com/fs/live/service/impl/LiveAfterSalesServiceImpl.java

@@ -410,6 +410,32 @@ public class LiveAfterSalesServiceImpl implements ILiveAfterSalesService {
             log.info("申请售后订单锁获取成功,订单号:{}", param.getOrderCode());
             
             LiveOrder order=liveOrderService.selectOrderIdByOrderCode(param.getOrderCode());
+            
+            // 查询配置:是否删除历史售后数据
+            try {
+                String deleteAfterSalesConfig = configService.selectConfigByKey("delete_after_sales");
+                if (StringUtils.isNotEmpty(deleteAfterSalesConfig) && "true".equalsIgnoreCase(deleteAfterSalesConfig.trim())) {
+                    // 查询历史售后数据,将 is_del 设置为 1
+                    if (order != null && order.getOrderId() != null) {
+                        LiveAfterSales queryAfterSales = new LiveAfterSales();
+                        queryAfterSales.setOrderId(order.getOrderId());
+                        List<LiveAfterSales> historyAfterSalesList = baseMapper.selectLiveAfterSalesList(queryAfterSales);
+                        if (historyAfterSalesList != null && !historyAfterSalesList.isEmpty()) {
+                            for (LiveAfterSales historyAfterSales : historyAfterSalesList) {
+                                if (historyAfterSales.getIsDel() != null && historyAfterSales.getIsDel() == 0) {
+                                    LiveAfterSales updateAfterSales = new LiveAfterSales();
+                                    updateAfterSales.setId(historyAfterSales.getId());
+                                    updateAfterSales.setIsDel(1);
+                                    baseMapper.updateLiveAfterSales(updateAfterSales);
+                                    log.info("删除历史售后数据,售后ID:{},订单ID:{}", historyAfterSales.getId(), order.getOrderId());
+                                }
+                            }
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                log.error("查询或更新历史售后数据失败", e);
+            }
             if(!order.getUserId().equals(userId)){
                 throw new CustomException("非法操作");
             }

+ 95 - 12
fs-service/src/main/java/com/fs/live/service/impl/LiveRedConfServiceImpl.java

@@ -270,7 +270,8 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
             liveRedConf.setRedStatus(2L);
             baseMapper.updateLiveRedConf(liveRedConf);
             Set<String> range = CollUtil.newHashSet(String.valueOf(red.getRedId()));
-            finishRedStatusBySetIds(range);
+//            finishRedStatusBySetIds(range);
+            updateDbByRed(liveRedConf);
             return R.error("手慢了,红包已被抢完~");
         }
         // 记录用户红包
@@ -280,14 +281,44 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
         record.setUserId(red.getUserId());
         record.setIntegral(integral);
         record.setCreateTime(new Date());
-        // 更新用户余额
-        BigDecimal balanceAmount = BigDecimal.valueOf(integral);
-        int updateResult = fsUserScrmMapper.incrIntegral(red.getUserId(), balanceAmount);
-        if (updateResult <= 0) {
-            log.error("更新用户余额失败,userId: {}, balance: {}", red.getUserId(), balanceAmount);
-            return R.error("更新用户余额失败");
+        
+        // 双重检查:先检查 Redis(已有),再检查数据库(防止重复领取)
+        String redisKey = String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_RED, red.getLiveId(), red.getRedId());
+        Object redisRecord = redisCache.hashGet(redisKey, String.valueOf(red.getUserId()));
+        if (ObjectUtil.isNotEmpty(redisRecord)) {
+            log.warn("用户 {} 在 Redis 中已存在红包记录 redId: {},跳过重复处理", red.getUserId(), red.getRedId());
+            return R.error("您已经领取过红包了!");
+        }
+        
+        LiveUserRedRecord queryRecord = new LiveUserRedRecord();
+        queryRecord.setUserId(red.getUserId());
+        queryRecord.setRedId(red.getRedId());
+        List<LiveUserRedRecord> existingRecords = userRedRecordMapper.selectLiveUserRedRecordList(queryRecord);
+        if (existingRecords != null && !existingRecords.isEmpty()) {
+            log.warn("用户 {} 在数据库中已存在红包记录 redId: {},跳过重复处理", red.getUserId(), red.getRedId());
+            // 如果数据库已有记录但 Redis 没有,同步到 Redis
+            redisCache.hashPut(redisKey, String.valueOf(red.getUserId()), JSONUtil.toJsonStr(existingRecords.get(0)));
+            return R.error("您已经领取过红包了!");
+        }
+        
+        // 先插入数据库记录(使用数据库约束防止重复)
+        int insertResult = userRedRecordMapper.insertLiveUserRedRecord(record);
+        if (insertResult <= 0) {
+            log.error("插入红包记录失败,userId: {}, redId: {}", red.getUserId(), red.getRedId());
+            return R.error("领取红包失败,请稍后重试");
+        }
+        
+        // 插入后再次验证,防止并发插入导致重复
+        List<LiveUserRedRecord> verifyRecords = userRedRecordMapper.selectLiveUserRedRecordList(queryRecord);
+        if (verifyRecords != null && verifyRecords.size() > 1) {
+            // 发现重复记录,删除刚插入的记录并回滚
+            log.error("检测到重复红包记录,userId: {}, redId: {},记录数: {}", red.getUserId(), red.getRedId(), verifyRecords.size());
+            // 删除最后插入的记录(通常是当前请求插入的)
+            userRedRecordMapper.deleteLiveUserRedRecordById(record.getId());
+            return R.error("您已经领取过红包了!");
         }
 
+
         // 查询用户当前余额
         com.fs.hisStore.domain.FsUserScrm user = fsUserScrmMapper.selectFsUserById(red.getUserId());
         Long currentIntegral = user.getIntegral() != null ? user.getIntegral() : 0L;
@@ -304,11 +335,22 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
         integralLogs.setStatus(0);
         integralLogs.setCreateTime(new Date());
         fsUserIntegralLogsMapper.insertFsUserIntegralLogs(integralLogs);
+        // 更新用户余额
+        BigDecimal balanceAmount = BigDecimal.valueOf(integral);
+        int updateResult = fsUserScrmMapper.incrIntegral(red.getUserId(), balanceAmount);
+        if (updateResult <= 0) {
+            log.error("更新用户余额失败,userId: {}, balance: {}", red.getUserId(), balanceAmount);
+            // 回滚:删除已插入的记录
+            userRedRecordMapper.deleteLiveUserRedRecordById(record.getId());
+            return R.error("更新用户余额失败");
+        }
+
+        // 最后更新 Redis 缓存(确保原子性:先插入数据库,再更新 Redis)
+        redisCache.hashPut(redisKey, String.valueOf(red.getUserId()), JSONUtil.toJsonStr(record));
 
         // WebSocket 通知
         //String msg = String.format("用户 %d 抢到了红包 %d,获得 %d 芳华币", userId, redId, integral);
         //WebSocketServer.notifyUsers(msg);
-        redisCache.hashPut(String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_RED, red.getLiveId(), red.getRedId()), String.valueOf(red.getUserId()), JSONUtil.toJsonStr(record));
         return R.ok("恭喜您成功抢到" + integral + "芳华币");
 /*        } catch (Exception e) {
             e.printStackTrace();
@@ -358,6 +400,9 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
         // 插入抽奖记录
         for (Long id : redIds) {
             LiveRedConf liveRedConf = baseMapper.selectLiveRedConfByRedId(id);
+            if (liveRedConf == null) {
+                continue;
+            }
             // 更新数据库
             updateDbByRed(liveRedConf);
             String hashKey = String.format(LiveKeysConstant.LIVE_HOME_PAGE_CONFIG_RED, liveRedConf.getLiveId(), liveRedConf.getRedId());
@@ -367,11 +412,49 @@ public class LiveRedConfServiceImpl implements ILiveRedConfService {
                 liveUserRedRecords = hashEntries.values().stream()
                         .map(value -> JSONUtil.toBean(JSONUtil.parseObj(value), LiveUserRedRecord.class))
                         .collect(Collectors.toList());
-                userRedRecordMapper.insertLiveUserRedRecordBatch(liveUserRedRecords);
+                
+                // 过滤掉已经存在于数据库中的记录(避免重复增加积分)
+                List<LiveUserRedRecord> newRecords = new ArrayList<>();
                 for (LiveUserRedRecord liveUserRedRecord : liveUserRedRecords) {
-                    userService.incrIntegral(Collections.singletonList(liveUserRedRecord.getUserId()), liveUserRedRecord.getIntegral());
-                    // 保存用户领取芳华币记录 方便统计计算
-                    saveUserRewardRecord(liveUserRedRecord);
+                    // 检查数据库中是否已存在该记录(使用 redId 和 userId 组合查询)
+                    LiveUserRedRecord queryRecord = new LiveUserRedRecord();
+                    queryRecord.setUserId(liveUserRedRecord.getUserId());
+                    queryRecord.setRedId(liveUserRedRecord.getRedId());
+                    List<LiveUserRedRecord> existingRecords = userRedRecordMapper.selectLiveUserRedRecordList(queryRecord);
+                    
+                    // 如果不存在,则添加到新记录列表
+                    if (existingRecords == null || existingRecords.isEmpty()) {
+                        newRecords.add(liveUserRedRecord);
+                    }
+                }
+                
+                // 只插入新记录(这些记录可能是从 Redis 同步过来的,但还没有插入数据库)
+                if (CollUtil.isNotEmpty(newRecords)) {
+                    try {
+                        userRedRecordMapper.insertLiveUserRedRecordBatch(newRecords);
+                        
+                        // 只为新记录增加积分(避免重复增加)
+                        // 注意:如果 claimRedPacket 已经处理过,这里不应该再增加积分
+                        // 所以这里只处理那些在 claimRedPacket 中还没有处理过的记录
+                        for (LiveUserRedRecord liveUserRedRecord : newRecords) {
+                            // 再次检查:如果 claimRedPacket 已经处理过,数据库中应该有记录
+                            // 如果这里还能插入,说明 claimRedPacket 没有处理过,需要增加积分
+                            LiveUserRedRecord verifyRecord = new LiveUserRedRecord();
+                            verifyRecord.setUserId(liveUserRedRecord.getUserId());
+                            verifyRecord.setRedId(liveUserRedRecord.getRedId());
+                            List<LiveUserRedRecord> verifyRecords = userRedRecordMapper.selectLiveUserRedRecordList(verifyRecord);
+                            // 确保只有一条记录,且积分还没有增加过
+                            if (verifyRecords != null && verifyRecords.size() == 1) {
+                                // 检查积分日志,如果已经增加过积分,则不再增加
+                                // 这里简化处理:如果记录存在且只有一条,说明是新的,需要增加积分
+                                userService.incrIntegral(Collections.singletonList(liveUserRedRecord.getUserId()), liveUserRedRecord.getIntegral());
+                                // 保存用户领取芳华币记录 方便统计计算
+                                saveUserRewardRecord(liveUserRedRecord);
+                            }
+                        }
+                    } catch (Exception e) {
+                        log.error("批量插入红包记录失败,redId: {}", id, e);
+                    }
                 }
             }
             redisCache.deleteObject(hashKey);

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

@@ -510,4 +510,11 @@
             #{videoId}
         </foreach>
     </update>
+
+    <update id="batchEditCover" parameterType="com.fs.course.param.BatchEditCoverParam">
+        update fs_user_course_video set thumbnail = #{thumbnail} where video_id in
+        <foreach item="videoId" collection="videoIds" open="(" separator="," close=")">
+            #{videoId}
+        </foreach>
+    </update>
 </mapper>

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

@@ -2433,5 +2433,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         select * from fs_user where phone=#{phone}
     </select>
 
+    <!-- 批量更新用户积分(增加积分) -->
+    <update id="batchUpdateUserIntegral">
+        update fs_user
+        set integral = integral + #{addIntegral}
+        where user_id in
+        <foreach collection="userIds" item="userId" open="(" separator="," close=")">
+            #{userId}
+        </foreach>
+    </update>
 
 </mapper>

+ 94 - 58
fs-user-app/src/main/java/com/fs/app/controller/live/LiveCompletionPointsController.java

@@ -16,6 +16,10 @@ import com.fs.live.domain.LiveCompletionPointsRecord;
 import com.fs.live.mapper.LiveCompletionPointsRecordMapper;
 import com.fs.live.service.ILiveCompletionPointsRecordService;
 import com.fs.live.service.ILiveService;
+import org.redisson.api.RLock;
+import org.redisson.api.RedissonClient;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.web.bind.annotation.*;
@@ -28,6 +32,7 @@ import java.util.Date;
 import java.util.HashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.concurrent.TimeUnit;
 
 /**
  * 直播完课积分Controller
@@ -36,6 +41,8 @@ import java.util.Map;
 @RequestMapping("/app/live/completion")
 public class LiveCompletionPointsController extends AppBaseController {
 
+    private static final Logger logger = LoggerFactory.getLogger(LiveCompletionPointsController.class);
+
     @Autowired
     private ILiveCompletionPointsRecordService completionPointsRecordService;
 
@@ -51,6 +58,9 @@ public class LiveCompletionPointsController extends AppBaseController {
     @Autowired
     private LiveCompletionPointsRecordMapper completionPointsRecordMapper;
 
+    @Autowired
+    private RedissonClient redissonClient;
+
     /**
      * 领取完课积分
      */
@@ -298,81 +308,107 @@ public class LiveCompletionPointsController extends AppBaseController {
      * 没达到,返回报错
      */
     @PostMapping("/receive-points")
-    @RepeatSubmit
     public R receivePoints(@RequestParam Long liveId) {
         Long userId = Long.parseLong(getUserId());
         
+        // 创建唯一锁,确保同一个 liveId 和 userId 只能有一个线程在执行
+        String lockKey = String.format("receivePoints:liveId:%d:userId:%d", liveId, userId);
+        RLock lock = redissonClient.getLock(lockKey);
+        
         try {
-            // 1. 查询当前用户和当前直播间的最近一次完课记录(不限制日期)
-            LiveCompletionPointsRecord record = completionPointsRecordMapper.selectLatestByUserAndLiveId(liveId, userId);
-            
-            if (record == null) {
-                return R.error("您还没有看课记录,无法领取积分");
+            // 尝试获取锁,等待时间0秒,锁持有时间15秒
+            boolean locked = lock.tryLock(0, 15, TimeUnit.SECONDS);
+            if (!locked) {
+                logger.warn("获取领取积分锁失败,liveId: {}, userId: {}", liveId, userId);
+                return R.error("系统繁忙,请稍后重试");
             }
 
-            // 2. 获取直播间信息和配置
-            Live live = liveService.selectLiveByLiveId(liveId);
-            if (live == null) {
-                return R.error("直播间不存在");
-            }
+            try {
+                // 1. 查询当前用户和当前直播间的最近一次完课记录(不限制日期)
+                LiveCompletionPointsRecord record = completionPointsRecordMapper.selectLatestByUserAndLiveId(liveId, userId);
+                
+                if (record == null) {
+                    return R.error("您还没有看课记录,无法领取积分");
+                }
 
-            // 3. 检查看课记录里面的时长是否达到完课标准
-            Long watchDuration = record.getWatchDuration();
-            if (watchDuration == null || watchDuration <= 0) {
-                return R.error("您的看课时长不足,无法领取积分");
-            }
+                // 2. 获取直播间信息和配置
+                Live live = liveService.selectLiveByLiveId(liveId);
+                if (live == null) {
+                    return R.error("直播间不存在");
+                }
 
-            // 4. 检查完课比例是否达到标准
-            BigDecimal completionRate = record.getCompletionRate();
-            if (completionRate == null) {
-                // 重新计算完课比例
-                Long videoDuration = live.getDuration();
-                if (videoDuration == null || videoDuration <= 0) {
-                    return R.error("直播间视频时长配置错误");
+                // 3. 检查看课记录里面的时长是否达到完课标准
+                Long watchDuration = record.getWatchDuration();
+                if (watchDuration == null || watchDuration <= 0) {
+                    return R.error("您的看课时长不足,无法领取积分");
                 }
-                completionRate = BigDecimal.valueOf(watchDuration)
-                        .multiply(BigDecimal.valueOf(100))
-                        .divide(BigDecimal.valueOf(videoDuration), 2, java.math.RoundingMode.HALF_UP);
-                if (completionRate.compareTo(BigDecimal.valueOf(100)) > 0) {
-                    completionRate = BigDecimal.valueOf(100);
+
+                // 4. 检查完课比例是否达到标准
+                BigDecimal completionRate = record.getCompletionRate();
+                if (completionRate == null) {
+                    // 重新计算完课比例
+                    Long videoDuration = live.getDuration();
+                    if (videoDuration == null || videoDuration <= 0) {
+                        return R.error("直播间视频时长配置错误");
+                    }
+                    completionRate = BigDecimal.valueOf(watchDuration)
+                            .multiply(BigDecimal.valueOf(100))
+                            .divide(BigDecimal.valueOf(videoDuration), 2, java.math.RoundingMode.HALF_UP);
+                    if (completionRate.compareTo(BigDecimal.valueOf(100)) > 0) {
+                        completionRate = BigDecimal.valueOf(100);
+                    }
+                    record.setCompletionRate(completionRate);
                 }
-                record.setCompletionRate(completionRate);
-            }
 
-            // 5. 从直播间配置获取完课标准
-            String configJson = live.getConfigJson();
-            Integer requiredCompletionRate = null;
-            if (configJson != null && !configJson.isEmpty()) {
-                try {
-                    com.alibaba.fastjson.JSONObject jsonConfig = com.alibaba.fastjson.JSON.parseObject(configJson);
-                    requiredCompletionRate = jsonConfig.getInteger("completionRate");
-                } catch (Exception e) {
-                    // 解析失败,忽略
+                // 5. 从直播间配置获取完课标准
+                String configJson = live.getConfigJson();
+                Integer requiredCompletionRate = null;
+                if (configJson != null && !configJson.isEmpty()) {
+                    try {
+                        com.alibaba.fastjson.JSONObject jsonConfig = com.alibaba.fastjson.JSON.parseObject(configJson);
+                        requiredCompletionRate = jsonConfig.getInteger("completionRate");
+                    } catch (Exception e) {
+                        // 解析失败,忽略
+                    }
                 }
-            }
 
-            // 6. 判断是否达到完课标准
-            if (requiredCompletionRate != null && completionRate.compareTo(BigDecimal.valueOf(requiredCompletionRate)) < 0) {
-                return R.error("您的完课比例未达到标准(" + requiredCompletionRate + "%),当前完课比例:" + completionRate + "%");
-            }
+                // 6. 判断是否达到完课标准
+                if (requiredCompletionRate != null && completionRate.compareTo(BigDecimal.valueOf(requiredCompletionRate)) < 0) {
+                    return R.error("您的完课比例未达到标准(" + requiredCompletionRate + "%),当前完课比例:" + completionRate + "%");
+                }
 
-            // 7. 检查是否已领取
-            if (record.getReceiveStatus() != null && record.getReceiveStatus() == 1) {
-                return R.error("该完课积分已领取");
-            }
+                // 7. 检查是否已领取
+                if (record.getReceiveStatus() != null && record.getReceiveStatus() == 1) {
+                    return R.error("该完课积分已领取");
+                }
 
-            // 8. 领取积分(更新看课记录的领取状态,给用户加积分)
-            LiveCompletionPointsRecord receivedRecord = completionPointsRecordService.receiveCompletionPoints(record.getId(), userId);
+                // 8. 领取积分(更新看课记录的领取状态,给用户加积分)
+                LiveCompletionPointsRecord receivedRecord = completionPointsRecordService.receiveCompletionPoints(record.getId(), userId);
 
-            ReceivePointsVO vo = new ReceivePointsVO();
-            vo.setRecord(receivedRecord);
-            vo.setPoints(receivedRecord.getPointsAwarded());
-            vo.setContinuousDays(receivedRecord.getContinuousDays());
-            
-            return R.ok().put("data", vo);
-        } catch (BaseException e) {
-            return R.error(e.getMessage());
+                ReceivePointsVO vo = new ReceivePointsVO();
+                vo.setRecord(receivedRecord);
+                vo.setPoints(receivedRecord.getPointsAwarded());
+                vo.setContinuousDays(receivedRecord.getContinuousDays());
+                
+                return R.ok().put("data", vo);
+            } catch (BaseException e) {
+                return R.error(e.getMessage());
+            } catch (Exception e) {
+                logger.error("领取积分失败,liveId: {}, userId: {}", liveId, userId, e);
+                return R.error("领取失败: " + e.getMessage());
+            } finally {
+                // 释放锁
+                if (lock.isHeldByCurrentThread()) {
+                    lock.unlock();
+                    logger.debug("领取积分锁已释放,liveId: {}, userId: {}", liveId, userId);
+                }
+            }
+        } catch (InterruptedException e) {
+            Thread.currentThread().interrupt();
+            logger.error("获取领取积分锁被中断,liveId: {}, userId: {}", liveId, userId, e);
+            return R.error("系统繁忙,请稍后重试");
         } catch (Exception e) {
+            logger.error("领取积分异常,liveId: {}, userId: {}", liveId, userId, e);
             return R.error("领取失败: " + e.getMessage());
         }
     }

+ 195 - 0
fs-user-app/src/main/java/com/fs/app/controller/live/LiveOrderController.java

@@ -32,8 +32,10 @@ import com.fs.his.service.IFsExpressService;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.hisStore.domain.*;
 import com.fs.hisStore.dto.FsStoreOrderComputeDTO;
+import com.fs.hisStore.enums.AfterSalesStatusEnum;
 import com.fs.hisStore.enums.OrderInfoEnum;
 import com.fs.hisStore.mapper.FsStorePaymentScrmMapper;
+import com.fs.hisStore.service.IFsStoreAfterSalesScrmService;
 import com.fs.hisStore.param.*;
 import com.fs.hisStore.service.IFsStoreOrderScrmService;
 import com.fs.hisStore.service.IFsUserScrmService;
@@ -43,9 +45,11 @@ import com.fs.huifuPay.domain.HuiFuQueryOrderResult;
 import com.fs.huifuPay.domain.HuifuCreateOrderResult;
 import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayQueryRequest;
 import com.fs.huifuPay.service.HuiFuService;
+import com.fs.live.domain.LiveAfterSales;
 import com.fs.live.domain.LiveOrder;
 import com.fs.live.domain.LiveOrderPayment;
 import com.fs.live.dto.LiveOrderComputeDTO;
+import com.fs.live.enums.LiveAfterSalesStatusEnum;
 import com.fs.live.enums.LiveOrderCancleReason;
 import com.fs.live.mapper.LiveOrderPaymentMapper;
 import com.fs.live.param.*;
@@ -83,7 +87,9 @@ import org.springframework.web.bind.annotation.*;
 
 import javax.servlet.http.HttpServletRequest;
 import java.math.BigDecimal;
+import java.sql.Timestamp;
 import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
 import java.util.*;
 import java.util.concurrent.TimeUnit;
 
@@ -130,6 +136,12 @@ public class LiveOrderController extends AppBaseController
     IPayService ybPayService;
     @Autowired
     private HuiFuService huiFuService;
+    @Autowired
+    private IFsStoreOrderScrmService fsStoreOrderScrmService;
+    @Autowired
+    private IFsStoreAfterSalesScrmService fsStoreAfterSalesScrmService;
+    @Autowired
+    private FsStorePaymentScrmMapper fsStorePaymentScrmMapper;
 
 
 
@@ -914,5 +926,188 @@ public class LiveOrderController extends AppBaseController
     }
 
 
+    @ApiOperation("批量生成售后订单并退款")
+//    @PostMapping("/batchCreateAfterSalesAndRefund")
+    public R batchCreateAfterSalesAndRefund(@RequestBody List<String> payCodes) {
+        if (payCodes == null || payCodes.isEmpty()) {
+            return R.error("支付订单号列表不能为空");
+        }
+
+        List<Map<String, Object>> results = new ArrayList<>();
+        
+        for (String payCode : payCodes) {
+            Map<String, Object> result = new HashMap<>();
+            result.put("payCode", payCode);
+            
+            try {
+                // 先查询支付记录
+                // 先尝试查询商城订单支付记录
+                FsStorePaymentScrm storePayment = fsStorePaymentScrmMapper.selectFsStorePaymentByCode(payCode.split("-")[1]);
+                
+                if (storePayment != null) {
+                    // 商城订单支付记录
+                    result.put("paymentType", "store");
+                    result.put("paymentId", storePayment.getPaymentId());
+                    
+                    // 根据支付记录查询订单
+                    FsStoreOrderScrm storeOrder = null;
+                    if (storePayment.getOrderId() != null) {
+                        storeOrder = fsStoreOrderScrmService.selectFsStoreOrderById(storePayment.getOrderId());
+                    } else if (StringUtils.isNotEmpty(storePayment.getBusinessOrderId())) {
+                        // 如果没有orderId,尝试通过businessOrderId查询
+                        storeOrder = fsStoreOrderScrmService.selectFsStoreOrderByOrderCode(storePayment.getBusinessOrderId());
+                    }
+                    
+                    if (storeOrder == null) {
+                        result.put("success", false);
+                        result.put("message", "根据支付记录未找到对应的订单");
+                        results.add(result);
+                        continue;
+                    }
+                    
+                    result.put("orderCode", storeOrder.getOrderCode());
+                    result.put("orderId", storeOrder.getId());
+                    
+                    // 查询售后记录
+                    FsStoreAfterSalesScrm queryAfterSales = new FsStoreAfterSalesScrm();
+                    queryAfterSales.setOrderCode(storeOrder.getOrderCode());
+                    List<FsStoreAfterSalesScrm> existingAfterSales = fsStoreAfterSalesScrmService.selectFsStoreAfterSalesList(queryAfterSales);
+                    
+                    FsStoreAfterSalesScrm afterSales = null;
+                    if (existingAfterSales == null || existingAfterSales.isEmpty()) {
+                        // 创建售后记录
+                        afterSales = new FsStoreAfterSalesScrm();
+                        afterSales.setOrderCode(storeOrder.getOrderCode());
+                        afterSales.setRefundAmount(storeOrder.getPayMoney());
+                        afterSales.setServiceType(0); // 0仅退款
+                        afterSales.setReasons("系统自动生成售后订单");
+                        afterSales.setExplains("系统自动生成售后订单");
+                        afterSales.setExplainImg(null);
+                        afterSales.setStatus(AfterSalesStatusEnum.STATUS_4.getValue()); // 4财务已审核(退款成功)
+                        afterSales.setSalesStatus(3); // 3已完成
+                        afterSales.setCreateTime(Timestamp.valueOf(LocalDateTime.now()));
+                        afterSales.setIsDel(0);
+                        afterSales.setUserId(storeOrder.getUserId());
+                        afterSales.setOrderStatus(storeOrder.getStatus());
+                        afterSales.setCompanyId(storeOrder.getCompanyId());
+                        afterSales.setCompanyUserId(storeOrder.getCompanyUserId());
+                        if (storeOrder.getPackageJson() != null) {
+                            afterSales.setPackageJson(storeOrder.getPackageJson());
+                        }
+                        if (storeOrder.getIsPackage() != null) {
+                            afterSales.setIsPackage(storeOrder.getIsPackage());
+                        }
+                        fsStoreAfterSalesScrmService.insertFsStoreAfterSales(afterSales);
+                        result.put("afterSalesCreated", true);
+                    } else {
+                        afterSales = existingAfterSales.get(0);
+                        result.put("afterSalesCreated", false);
+                        result.put("afterSalesId", afterSales.getId());
+                    }
+                    
+                    // 修改订单状态为退款成功
+                    storeOrder.setStatus(OrderInfoEnum.STATUS_NE2.getValue()); // -2退款成功
+                    storeOrder.setRefundStatus(OrderInfoEnum.REFUND_STATUS_2.getValue()); // 2已退款
+                    storeOrder.setRefundPrice(storeOrder.getPayMoney());
+                    fsStoreOrderScrmService.updateFsStoreOrder(storeOrder);
+                    
+                    result.put("success", true);
+                    result.put("message", "处理成功");
+                    result.put("afterSalesId", afterSales != null ? afterSales.getId() : null);
+                    
+                } else {
+                    // 尝试查询直播订单支付记录
+                    LiveOrderPayment livePayment = liveOrderPaymentMapper.selectLiveOrderPaymentByPaymentCode(payCode.split("-")[1]);
+                    
+                    if (livePayment != null) {
+                        // 直播订单支付记录
+                        result.put("paymentType", "live");
+                        result.put("paymentId", livePayment.getPaymentId());
+                        
+                        // 根据支付记录查询订单
+                        LiveOrder liveOrder = null;
+                        if (StringUtils.isNotEmpty(livePayment.getBusinessId())) {
+                            // businessId 是订单ID(Long类型转String)
+                            try {
+                                Long orderId = Long.parseLong(livePayment.getBusinessId());
+                                liveOrder = orderService.selectLiveOrderByOrderId(String.valueOf(orderId));
+                            } catch (NumberFormatException e) {
+                                // 如果不是数字,可能是订单号
+                                liveOrder = orderService.selectLiveOrderByOrderId(livePayment.getBusinessId());
+                            }
+                        } else if (StringUtils.isNotEmpty(livePayment.getBusinessCode())) {
+                            // 通过businessCode(订单号)查询
+                            liveOrder = orderService.selectLiveOrderByOrderId(livePayment.getBusinessCode());
+                        }
+                        
+                        if (liveOrder == null) {
+                            result.put("success", false);
+                            result.put("message", "根据支付记录未找到对应的订单");
+                            results.add(result);
+                            continue;
+                        }
+                        
+                        result.put("orderCode", liveOrder.getOrderCode());
+                        result.put("orderId", liveOrder.getOrderId());
+                        
+                        // 查询售后记录
+                        LiveAfterSales queryAfterSales = new LiveAfterSales();
+                        queryAfterSales.setOrderId(liveOrder.getOrderId());
+                        List<LiveAfterSales> existingAfterSales = liveAfterSalesService.selectLiveAfterSalesList(queryAfterSales);
+                        
+                        LiveAfterSales afterSales = null;
+                        if (existingAfterSales == null || existingAfterSales.isEmpty()) {
+                            // 创建售后记录
+                            afterSales = new LiveAfterSales();
+                            afterSales.setOrderId(liveOrder.getOrderId());
+                            afterSales.setRefundAmount(liveOrder.getPayMoney());
+                            afterSales.setRefundType(0); // 0仅退款
+                            afterSales.setReasons("系统自动生成售后订单");
+                            afterSales.setExplains("系统自动生成售后订单");
+                            afterSales.setExplainImg(null);
+                            afterSales.setStatus(LiveAfterSalesStatusEnum.STATUS_4.getValue()); // 4财务已审核(退款成功)
+                            afterSales.setSalesStatus(3); // 3已完成
+                            afterSales.setCreateTime(Timestamp.valueOf(LocalDateTime.now()));
+                            afterSales.setIsDel(0);
+                            afterSales.setUserId(Long.valueOf(liveOrder.getUserId()));
+                            afterSales.setOrderStatus(liveOrder.getStatus());
+                            afterSales.setCompanyId(liveOrder.getCompanyId());
+                            afterSales.setCompanyUserId(liveOrder.getCompanyUserId());
+                            liveAfterSalesService.insertLiveAfterSales(afterSales);
+                            result.put("afterSalesCreated", true);
+                        } else {
+                            afterSales = existingAfterSales.get(0);
+                            result.put("afterSalesCreated", false);
+                            result.put("afterSalesId", afterSales.getId());
+                        }
+                        
+                        // 修改订单状态为退款成功
+                        liveOrder.setStatus(OrderInfoEnum.STATUS_NE2.getValue()); // -2退款成功
+                        liveOrder.setRefundStatus(String.valueOf(OrderInfoEnum.REFUND_STATUS_2.getValue())); // 2已退款
+                        liveOrder.setRefundMoney(liveOrder.getPayMoney());
+                        liveOrder.setRefundTime(new Date());
+                        orderService.updateLiveOrder(liveOrder);
+                        
+                        result.put("success", true);
+                        result.put("message", "处理成功");
+                        result.put("afterSalesId", afterSales != null ? afterSales.getId() : null);
+                        
+                    } else {
+                        result.put("success", false);
+                        result.put("message", "未找到对应的支付记录");
+                    }
+                }
+                
+            } catch (Exception e) {
+                logger.error("处理支付订单失败: " + payCode, e);
+                result.put("success", false);
+                result.put("message", "处理失败: " + e.getMessage());
+            }
+            
+            results.add(result);
+        }
+        
+        return R.ok().put("results", results);
+    }
 
 }