ソースを参照

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

 Conflicts:
	fs-service-system/src/main/java/com/fs/store/mapper/FsStoreOrderMapper.java
15376779826 3 日 前
コミット
b0288f3b83
55 ファイル変更3141 行追加242 行削除
  1. 131 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserWatchCourseStatisticsController.java
  2. 113 0
      fs-admin/src/main/java/com/fs/course/controller/FsUserWatchStatisticsController.java
  3. 41 0
      fs-admin/src/main/java/com/fs/store/controller/FsStoreOrderAuditController.java
  4. 15 21
      fs-admin/src/main/java/com/fs/store/controller/FsStoreOrderController.java
  5. 57 15
      fs-admin/src/main/java/com/fs/task/StoreTask.java
  6. 126 0
      fs-company/src/main/java/com/fs/course/controller/FsUserWatchCourseStatisticsController.java
  7. 106 0
      fs-company/src/main/java/com/fs/course/controller/FsUserWatchStatisticsController.java
  8. 43 0
      fs-company/src/main/java/com/fs/store/controller/FsStoreOrderAuditController.java
  9. 8 1
      fs-company/src/main/java/com/fs/store/controller/FsStoreOrderController.java
  10. 38 0
      fs-service-system/src/main/java/com/fs/course/config/CourseMaConfig.java
  11. 118 0
      fs-service-system/src/main/java/com/fs/course/domain/FsUserWatchCourseStatistics.java
  12. 60 0
      fs-service-system/src/main/java/com/fs/course/domain/FsUserWatchStatistics.java
  13. 8 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserCoursePeriodDaysMapper.java
  14. 66 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserWatchCourseStatisticsMapper.java
  15. 73 0
      fs-service-system/src/main/java/com/fs/course/mapper/FsUserWatchStatisticsMapper.java
  16. 66 0
      fs-service-system/src/main/java/com/fs/course/service/IFsUserWatchCourseStatisticsService.java
  17. 66 0
      fs-service-system/src/main/java/com/fs/course/service/IFsUserWatchStatisticsService.java
  18. 11 8
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserCourseVideoServiceImpl.java
  19. 209 0
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserWatchCourseStatisticsServiceImpl.java
  20. 178 0
      fs-service-system/src/main/java/com/fs/course/service/impl/FsUserWatchStatisticsServiceImpl.java
  21. 93 0
      fs-service-system/src/main/java/com/fs/course/vo/FsUserWatchCourseStatisticsExportVO.java
  22. 5 1
      fs-service-system/src/main/java/com/fs/course/vo/newfs/FsUserCourseVideoLinkDetailsVO.java
  23. 13 2
      fs-service-system/src/main/java/com/fs/crm/service/impl/CrmCustomerServiceImpl.java
  24. 1 0
      fs-service-system/src/main/java/com/fs/store/config/StoreConfig.java
  25. 11 0
      fs-service-system/src/main/java/com/fs/store/domain/FsStoreAfterSalesItem.java
  26. 58 0
      fs-service-system/src/main/java/com/fs/store/domain/FsStoreOrderAudit.java
  27. 38 0
      fs-service-system/src/main/java/com/fs/store/domain/FsStoreOrderAuditLog.java
  28. 30 0
      fs-service-system/src/main/java/com/fs/store/enums/OrderAuditStateEnum.java
  29. 7 0
      fs-service-system/src/main/java/com/fs/store/mapper/FsStoreOrderAuditLogMapper.java
  30. 18 0
      fs-service-system/src/main/java/com/fs/store/mapper/FsStoreOrderAuditMapper.java
  31. 13 2
      fs-service-system/src/main/java/com/fs/store/mapper/FsStoreOrderMapper.java
  32. 17 0
      fs-service-system/src/main/java/com/fs/store/mapper/FsUserMapper.java
  33. 19 0
      fs-service-system/src/main/java/com/fs/store/param/FsStoreOrderAuditParam.java
  34. 23 0
      fs-service-system/src/main/java/com/fs/store/param/FsStoreOrderAuditReviewParam.java
  35. 4 1
      fs-service-system/src/main/java/com/fs/store/param/FsStoreOrderCreateParam.java
  36. 3 2
      fs-service-system/src/main/java/com/fs/store/param/FsStoreOrderCreateUserParam.java
  37. 17 0
      fs-service-system/src/main/java/com/fs/store/service/IFsStoreOrderAuditLogService.java
  38. 27 0
      fs-service-system/src/main/java/com/fs/store/service/IFsStoreOrderAuditService.java
  39. 35 29
      fs-service-system/src/main/java/com/fs/store/service/impl/FsStoreAfterSalesServiceImpl.java
  40. 37 0
      fs-service-system/src/main/java/com/fs/store/service/impl/FsStoreOrderAuditLogServiceImpl.java
  41. 94 0
      fs-service-system/src/main/java/com/fs/store/service/impl/FsStoreOrderAuditServiceImpl.java
  42. 141 151
      fs-service-system/src/main/java/com/fs/store/service/impl/FsStoreOrderServiceImpl.java
  43. 7 7
      fs-service-system/src/main/java/com/fs/store/service/impl/FsUserServiceImpl.java
  44. 23 0
      fs-service-system/src/main/java/com/fs/store/vo/FsStoreOrderAuditLogVO.java
  45. 77 0
      fs-service-system/src/main/java/com/fs/store/vo/FsStoreOrderAuditVO.java
  46. 108 0
      fs-service-system/src/main/resources/application-config-bly.yml
  47. 137 0
      fs-service-system/src/main/resources/application-druid-bly.yml
  48. 21 0
      fs-service-system/src/main/resources/mapper/course/FsUserCoursePeriodDaysMapper.xml
  49. 221 0
      fs-service-system/src/main/resources/mapper/course/FsUserWatchCourseStatisticsMapper.xml
  50. 147 0
      fs-service-system/src/main/resources/mapper/course/FsUserWatchStatisticsMapper.xml
  51. 6 1
      fs-service-system/src/main/resources/mapper/store/FsStoreAfterSalesItemMapper.xml
  52. 7 0
      fs-service-system/src/main/resources/mapper/store/FsStoreOrderAuditLogMapper.xml
  53. 44 0
      fs-service-system/src/main/resources/mapper/store/FsStoreOrderAuditMapper.xml
  54. 102 0
      fs-service-system/src/main/resources/mapper/store/FsUserMapper.xml
  55. 4 1
      fs-user-app/src/main/java/com/fs/app/controller/CompanyOrderController.java

+ 131 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserWatchCourseStatisticsController.java

@@ -0,0 +1,131 @@
+package com.fs.course.controller;
+
+import java.util.List;
+import java.util.stream.Collectors;
+import com.fs.course.vo.FsUserWatchCourseStatisticsExportVO;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.BeanUtils;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.course.domain.FsUserWatchCourseStatistics;
+import com.fs.course.service.IFsUserWatchCourseStatisticsService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 会员看课统计-按课程统计Controller
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+@RestController
+@RequestMapping("/course/userWatchCourseStatistics")
+public class FsUserWatchCourseStatisticsController extends BaseController
+{
+    @Autowired
+    private IFsUserWatchCourseStatisticsService fsUserWatchCourseStatisticsService;
+
+    /**
+     * 查询会员看课统计-按课程统计列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchCourseStatistics:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsUserWatchCourseStatistics fsUserWatchCourseStatistics)
+    {
+        startPage();
+        List<FsUserWatchCourseStatistics> list = fsUserWatchCourseStatisticsService.selectFsUserWatchCourseStatisticsList(fsUserWatchCourseStatistics);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出会员看课统计-按课程统计列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchCourseStatistics:export')")
+    @Log(title = "会员看课统计-按课程统计", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsUserWatchCourseStatistics fsUserWatchCourseStatistics)
+    {
+        List<FsUserWatchCourseStatistics> list = fsUserWatchCourseStatisticsService.selectFsUserWatchCourseStatisticsList(fsUserWatchCourseStatistics);
+        ExcelUtil<FsUserWatchCourseStatistics> util = new ExcelUtil<FsUserWatchCourseStatistics>(FsUserWatchCourseStatistics.class);
+        return util.exportExcel(list, "会员观看数据明细");
+    }
+
+    /**
+     * 获取会员看课统计-按课程统计详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchCourseStatistics:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fsUserWatchCourseStatisticsService.selectFsUserWatchCourseStatisticsById(id));
+    }
+
+    /**
+     * 新增会员看课统计-按课程统计
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchCourseStatistics:add')")
+    @Log(title = "会员看课统计-按课程统计", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsUserWatchCourseStatistics fsUserWatchCourseStatistics)
+    {
+        return toAjax(fsUserWatchCourseStatisticsService.insertFsUserWatchCourseStatistics(fsUserWatchCourseStatistics));
+    }
+
+    /**
+     * 修改会员看课统计-按课程统计
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchCourseStatistics:edit')")
+    @Log(title = "会员看课统计-按课程统计", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsUserWatchCourseStatistics fsUserWatchCourseStatistics)
+    {
+        return toAjax(fsUserWatchCourseStatisticsService.updateFsUserWatchCourseStatistics(fsUserWatchCourseStatistics));
+    }
+
+    /**
+     * 删除会员看课统计-按课程统计
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchCourseStatistics:remove')")
+    @Log(title = "会员看课统计-按课程统计", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fsUserWatchCourseStatisticsService.deleteFsUserWatchCourseStatisticsByIds(ids));
+    }
+
+    /**
+     * 导出会员看课统计-按课程汇总统计列表
+     */
+    @Log(title = "会员看课统计-按课程汇总统计", businessType = BusinessType.EXPORT)
+    @GetMapping("/exportTotal")
+    public AjaxResult exportTotal(FsUserWatchCourseStatistics fsUserWatchCourseStatistics)
+    {
+        List<FsUserWatchCourseStatistics> list = fsUserWatchCourseStatisticsService.selectFsUserWatchCourseStatisticsList(fsUserWatchCourseStatistics);
+        List<FsUserWatchCourseStatisticsExportVO> listVO = list.stream().map(v -> {
+            FsUserWatchCourseStatisticsExportVO vo = new FsUserWatchCourseStatisticsExportVO();
+            BeanUtils.copyProperties(v, vo);
+            return vo;
+        }).collect(Collectors.toList());
+        ExcelUtil<FsUserWatchCourseStatisticsExportVO> util = new ExcelUtil<FsUserWatchCourseStatisticsExportVO>(FsUserWatchCourseStatisticsExportVO.class);
+        return util.exportExcel(listVO, "会员观看数据明细汇总");
+    }
+
+
+    @PostMapping("/test")
+    @ApiOperation("测试看课统计明细定时任务")
+    public void userCourseCountTask() {
+        fsUserWatchCourseStatisticsService.insertWatchCourseStatistics();
+    }
+}

+ 113 - 0
fs-admin/src/main/java/com/fs/course/controller/FsUserWatchStatisticsController.java

@@ -0,0 +1,113 @@
+package com.fs.course.controller;
+
+import java.util.List;
+
+import io.swagger.annotations.ApiOperation;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.enums.BusinessType;
+import com.fs.course.domain.FsUserWatchStatistics;
+import com.fs.course.service.IFsUserWatchStatisticsService;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.common.core.page.TableDataInfo;
+
+/**
+ * 会员看课统计-按营期统计Controller
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+@RestController
+@RequestMapping("/course/userWatchStatistics")
+public class FsUserWatchStatisticsController extends BaseController
+{
+    @Autowired
+    private IFsUserWatchStatisticsService fsUserWatchStatisticsService;
+
+    /**
+     * 查询会员看课统计-按营期统计列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchStatistics:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsUserWatchStatistics fsUserWatchStatistics)
+    {
+        startPage();
+        List<FsUserWatchStatistics> list = fsUserWatchStatisticsService.selectFsUserWatchStatisticsList(fsUserWatchStatistics);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出会员看课统计-按营期统计列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchStatistics:export')")
+    @Log(title = "会员看课统计-按营期统计", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsUserWatchStatistics fsUserWatchStatistics)
+    {
+        List<FsUserWatchStatistics> list = fsUserWatchStatisticsService.selectFsUserWatchStatisticsList(fsUserWatchStatistics);
+        ExcelUtil<FsUserWatchStatistics> util = new ExcelUtil<FsUserWatchStatistics>(FsUserWatchStatistics.class);
+        return util.exportExcel(list, "会员看课统计-按营期统计数据");
+    }
+
+    /**
+     * 获取会员看课统计-按营期统计详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchStatistics:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fsUserWatchStatisticsService.selectFsUserWatchStatisticsById(id));
+    }
+
+    /**
+     * 新增会员看课统计-按营期统计
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchStatistics:add')")
+    @Log(title = "会员看课统计-按营期统计", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsUserWatchStatistics fsUserWatchStatistics)
+    {
+        return toAjax(fsUserWatchStatisticsService.insertFsUserWatchStatistics(fsUserWatchStatistics));
+    }
+
+    /**
+     * 修改会员看课统计-按营期统计
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchStatistics:edit')")
+    @Log(title = "会员看课统计-按营期统计", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsUserWatchStatistics fsUserWatchStatistics)
+    {
+        return toAjax(fsUserWatchStatisticsService.updateFsUserWatchStatistics(fsUserWatchStatistics));
+    }
+
+    /**
+     * 删除会员看课统计-按营期统计
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchStatistics:remove')")
+    @Log(title = "会员看课统计-按营期统计", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fsUserWatchStatisticsService.deleteFsUserWatchStatisticsByIds(ids));
+    }
+
+
+    @PostMapping("/test")
+    @ApiOperation("测试营期看课统计定时任务")
+    public void userCourseCountTask() {
+        fsUserWatchStatisticsService.insertStatistics();
+    }
+
+}

+ 41 - 0
fs-admin/src/main/java/com/fs/store/controller/FsStoreOrderAuditController.java

@@ -0,0 +1,41 @@
+package com.fs.store.controller;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.domain.entity.SysUser;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.core.security.SecurityUtils;
+import com.fs.store.param.FsStoreOrderAuditParam;
+import com.fs.store.param.FsStoreOrderAuditReviewParam;
+import com.fs.store.service.IFsStoreOrderAuditService;
+import com.fs.store.vo.FsStoreOrderAuditVO;
+import lombok.AllArgsConstructor;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+@RestController
+@RequestMapping("/store/storeOrderAudit")
+@AllArgsConstructor
+public class FsStoreOrderAuditController extends BaseController {
+
+    private final IFsStoreOrderAuditService fsStoreOrderAuditService;
+
+    @PreAuthorize("@ss.hasAnyPermi('store:storeOrderAudit:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsStoreOrderAuditParam param) {
+        startPage();
+        List<FsStoreOrderAuditVO> list = fsStoreOrderAuditService.selectStoreOrderAuditVOList(param);
+        return getDataTable(list);
+    }
+
+    @PreAuthorize("@ss.hasAnyPermi('store:storeOrderAudit:audit')")
+    @PostMapping("/audit")
+    public AjaxResult audit(@Valid @RequestBody FsStoreOrderAuditReviewParam param) {
+        SysUser user = SecurityUtils.getLoginUser().getUser();
+        fsStoreOrderAuditService.audit(param, user.getUserId(), true);
+        return AjaxResult.success();
+    }
+}

+ 15 - 21
fs-admin/src/main/java/com/fs/store/controller/FsStoreOrderController.java

@@ -1,23 +1,19 @@
 package com.fs.store.controller;
 
-import java.math.BigDecimal;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.List;
-import java.util.Objects;
-
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.StrUtil;
-import cn.hutool.json.JSONUtil;
 import com.alibaba.fastjson.JSONObject;
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
 import com.fs.common.core.domain.R;
-import com.fs.common.utils.DateUtils;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
 import com.fs.common.utils.ParseUtils;
 import com.fs.common.utils.StringUtils;
-import com.fs.company.domain.CompanyMoneyLogs;
+import com.fs.common.utils.poi.ExcelUtil;
 import com.fs.company.param.CompanyStoreOrderMoneyLogsListParam;
 import com.fs.company.service.ICompanyMoneyLogsService;
-import com.fs.company.vo.CompanyMoneyLogsVO;
 import com.fs.company.vo.CompanyStoreOrderMoneyLogsVO;
 import com.fs.erp.domain.ErpDeliverys;
 import com.fs.erp.domain.ErpOrderQuery;
@@ -28,7 +24,6 @@ import com.fs.his.config.FsErpConfig;
 import com.fs.his.utils.ConfigUtil;
 import com.fs.store.domain.*;
 import com.fs.store.dto.ExpressInfoDTO;
-import com.fs.store.dto.FsStoreCartDTO;
 import com.fs.store.dto.StoreOrderExpressExportDTO;
 import com.fs.store.dto.StoreOrderProductDTO;
 import com.fs.store.enums.ShipperCodeEnum;
@@ -36,22 +31,16 @@ import com.fs.store.param.*;
 import com.fs.store.service.*;
 import com.fs.store.vo.*;
 import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
 import org.springframework.security.access.prepost.PreAuthorize;
-import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.validation.annotation.Validated;
 import org.springframework.web.bind.annotation.*;
-import com.fs.common.annotation.Log;
-import com.fs.common.core.controller.BaseController;
-import com.fs.common.core.domain.AjaxResult;
-import com.fs.common.enums.BusinessType;
-import com.fs.common.utils.poi.ExcelUtil;
-import com.fs.common.core.page.TableDataInfo;
 import org.springframework.web.multipart.MultipartFile;
 
 import javax.servlet.http.HttpServletRequest;
-
-import static com.fs.store.constants.StoreConstants.DELIVERY;
+import java.util.ArrayList;
+import java.util.List;
 
 /**
  * 订单Controller
@@ -93,6 +82,8 @@ public class FsStoreOrderController extends BaseController {
     private IErpOrderService k9OrderService;
     @Autowired
     private ConfigUtil configUtil;
+    @Autowired
+    private IFsStoreOrderAuditLogService orderAuditLogService;
 
     private IErpOrderService getErpService(){
         //判断是否开启erp
@@ -355,7 +346,10 @@ public class FsStoreOrderController extends BaseController {
             moneyLogsMap.setBusinessId(order.getId().toString());
             tuiMoneyLogs=moneyLogsService.selectCompanyStoreOrderMoneyLogsList(moneyLogsMap);
         }
-        return R.ok().put("order", order).put("items", items).put("logs", logs).put("user", user).put("payments",payments).put("tuiMoneyLogs",tuiMoneyLogs);
+
+        List<FsStoreOrderAuditLogVO> auditLogs = orderAuditLogService.selectStoreOrderAuditLogVOByOrderId(order.getId());
+        return R.ok().put("order", order).put("items", items).put("logs", logs).put("user", user).put("payments",payments).put("tuiMoneyLogs",tuiMoneyLogs)
+                .put("auditLogs",auditLogs);
     }
 
     @GetMapping(value = "/queryAddress/{id}")

+ 57 - 15
fs-admin/src/main/java/com/fs/task/StoreTask.java

@@ -7,7 +7,8 @@ import com.fs.common.core.redis.RedisCache;
 import com.fs.company.service.ICompanyService;
 import com.fs.company.vo.RedPacketMoneyVO;
 import com.fs.course.mapper.FsCourseRedPacketLogMapper;
-import com.fs.course.service.IFsCourseWatchLogService;
+import com.fs.course.service.IFsUserWatchCourseStatisticsService;
+import com.fs.course.service.IFsUserWatchStatisticsService;
 import com.fs.erp.domain.ErpDeliverys;
 import com.fs.erp.domain.ErpGoods;
 import com.fs.erp.domain.ErpOrderQuery;
@@ -22,7 +23,6 @@ import com.fs.his.utils.ConfigUtil;
 import com.fs.pay.pay.domain.OrderResult;
 import com.fs.pay.pay.dto.OrderQueryDTO;
 import com.fs.pay.pay.service.PayService;
-import com.fs.qw.service.IQwWorkTaskService;
 import com.fs.store.config.StoreConfig;
 import com.fs.store.domain.*;
 import com.fs.store.dto.ExpressInfoDTO;
@@ -126,13 +126,41 @@ public class StoreTask
     @Autowired
     private FsCourseRedPacketLogMapper fsCourseRedPacketLogMapper;
 
+
+    @Autowired
+    IErpOrderService erpOrderService;
+
+    @Autowired
+    private IFsUserWatchCourseStatisticsService fsUserWatchCourseStatisticsService;
+
+    @Autowired
+    private IFsUserWatchStatisticsService fsUserWatchStatisticsService;
+
     public void PushErp() throws ParseException {
-        List<Long> ids = fsStoreOrderMapper.selectFsStoreOrderNoCreateOms();
+        List<Long> ids;
+        // 开启审核
+        if (getAuditSwitch()) {
+            ids = fsStoreOrderMapper.selectFsStoreOrderNoCreateOmsAndReviewed();
+        } else {
+            ids = fsStoreOrderMapper.selectFsStoreOrderNoCreateOms();
+        }
         for (Long id : ids) {
             fsStoreOrderService.createOmsOrder(id);
         }
+    }
 
-
+    /**
+     * 获取是否需要订单审核
+     * @return boolean
+     */
+    private boolean getAuditSwitch() {
+        try {
+            String json = configService.selectConfigByKey("store.config");
+            StoreConfig config = JSONUtil.toBean(json,StoreConfig.class);
+            return config.getAuditSwitch() == 1;
+        } catch (Exception e) {
+            return false;
+        }
     }
 
     public void redPacketSubMoney() throws Exception
@@ -146,28 +174,27 @@ public class StoreTask
     //每5分钟执行一次
     public void deliveryOp()
     {
-        IErpOrderService erpOrderService = getErpOrderService();
-        Set<String> orders=redisTemplate.keys(DELIVERY+":*");
-        for(String extndOrderId:orders){
-            String orderCode=(String)redisTemplate.opsForValue().get(extndOrderId);
-            ErpOrderQueryRequert request=new ErpOrderQueryRequert();
-            String[] extendOrder=extndOrderId.split(":");
-            request.setCode(extendOrder[1]);
-            ErpOrderQueryResponse response=erpOrderService.getOrder(request);
+        List<FsStoreOrder> list = fsStoreOrderMapper.selectUpdateExpress();
+        for (FsStoreOrder order : list){
+            ErpOrderQueryRequert request = new ErpOrderQueryRequert();
+            request.setCode(order.getExtendOrderId());
+            ErpOrderQueryResponse response = erpOrderService.getOrder(request);
             if(response.getOrders()!=null&&response.getOrders().size()>0){
                 for(ErpOrderQuery orderQuery : response.getOrders()){
                     if(orderQuery.getDeliverys()!=null&&orderQuery.getDeliverys().size()>0){
                         for(ErpDeliverys delivery:orderQuery.getDeliverys()){
                             if(delivery.getDelivery()&&StringUtils.isNotEmpty(delivery.getMail_no())){
                                 //更新商订单状态 删除REDIS
-                                orderService.deliveryOrder(orderCode,delivery.getMail_no(),delivery.getExpress_code(),delivery.getExpress_name());
-                                redisCache.deleteObject(DELIVERY+":"+extendOrder[1]);
+                                orderService.deliveryOrder(order.getOrderCode(),delivery.getMail_no(),delivery.getExpress_code(),delivery.getExpress_name());
+                                redisCache.deleteObject(DELIVERY+":"+order.getExtendOrderId());
                             }
                         }
 
                     }
                 }
+
             }
+
         }
 
     }
@@ -427,7 +454,22 @@ public class StoreTask
         return erpOrderService;
     }
 
+    /**
+     * 添加看课汇总统计
+     */
+    public void insertWatchStatistics(){
+        /***************************************进入营期会员看课汇总统计定时任务****************************************/
+        fsUserWatchStatisticsService.insertStatistics();
+        /***************************************营期会员看课汇总统计定时任务结束***************************************/
+    }
 
-
+    /**
+     * 添加看课明细统计
+     */
+    public void insertWatchCourseStatistics(){
+        /***************************************进入营期会员看课明细统计定时任务*******************************/
+        fsUserWatchCourseStatisticsService.insertWatchCourseStatistics();
+        /***************************************进入营期会员看课明细统计定时任务结束**********************************************/
+    }
 
 }

+ 126 - 0
fs-company/src/main/java/com/fs/course/controller/FsUserWatchCourseStatisticsController.java

@@ -0,0 +1,126 @@
+package com.fs.course.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.ServletUtils;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.core.security.LoginUser;
+import com.fs.core.web.service.TokenService;
+import com.fs.course.domain.FsUserWatchCourseStatistics;
+import com.fs.course.service.IFsUserWatchCourseStatisticsService;
+import com.fs.course.vo.FsUserWatchCourseStatisticsExportVO;
+import io.swagger.annotations.ApiOperation;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 会员看课统计-按课程统计Controller
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+@RestController
+@RequestMapping("/course/userWatchCourseStatistics")
+public class FsUserWatchCourseStatisticsController extends BaseController
+{
+    @Autowired
+    private IFsUserWatchCourseStatisticsService fsUserWatchCourseStatisticsService;
+    @Autowired
+    private TokenService tokenService;
+    /**
+     * 查询会员看课统计-按课程统计列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchCourseStatistics:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsUserWatchCourseStatistics fsUserWatchCourseStatistics)
+    {
+        startPage();
+        LoginUser loginUser = tokenService.getLoginUser(ServletUtils.getRequest());
+        fsUserWatchCourseStatistics.setCompanyId( loginUser.getCompany().getCompanyId());
+        List<FsUserWatchCourseStatistics> list = fsUserWatchCourseStatisticsService.selectFsUserWatchCourseStatisticsList(fsUserWatchCourseStatistics);
+        return getDataTable(list);
+    }
+
+    @PreAuthorize("@ss.hasPermi('course:userWatchCourseStatistics:export')")
+    @Log(title = "会员看课统计-按课程统计", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsUserWatchCourseStatistics fsUserWatchCourseStatistics)
+    {
+        List<FsUserWatchCourseStatistics> list = fsUserWatchCourseStatisticsService.selectFsUserWatchCourseStatisticsList(fsUserWatchCourseStatistics);
+        ExcelUtil<FsUserWatchCourseStatistics> util = new ExcelUtil<FsUserWatchCourseStatistics>(FsUserWatchCourseStatistics.class);
+        return util.exportExcel(list, "会员观看数据明细");
+    }
+    /**
+     * 导出会员看课统计-按课程汇总统计列表
+     */
+    @Log(title = "会员看课统计-按课程汇总统计", businessType = BusinessType.EXPORT)
+    @GetMapping("/exportTotal")
+    public AjaxResult exportTotal(FsUserWatchCourseStatistics fsUserWatchCourseStatistics)
+    {
+        List<FsUserWatchCourseStatistics> list = fsUserWatchCourseStatisticsService.selectFsUserWatchCourseStatisticsList(fsUserWatchCourseStatistics);
+        List<FsUserWatchCourseStatisticsExportVO> listVO = list.stream().map(v -> {
+            FsUserWatchCourseStatisticsExportVO vo = new FsUserWatchCourseStatisticsExportVO();
+            BeanUtils.copyProperties(v, vo);
+            return vo;
+        }).collect(Collectors.toList());
+        ExcelUtil<FsUserWatchCourseStatisticsExportVO> util = new ExcelUtil<FsUserWatchCourseStatisticsExportVO>(FsUserWatchCourseStatisticsExportVO.class);
+        return util.exportExcel(listVO, "会员观看数据明细汇总");
+    }
+    /**
+     * 获取会员看课统计-按课程统计详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchCourseStatistics:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fsUserWatchCourseStatisticsService.selectFsUserWatchCourseStatisticsById(id));
+    }
+
+    /**
+     * 新增会员看课统计-按课程统计
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchCourseStatistics:add')")
+    @Log(title = "会员看课统计-按课程统计", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsUserWatchCourseStatistics fsUserWatchCourseStatistics)
+    {
+        return toAjax(fsUserWatchCourseStatisticsService.insertFsUserWatchCourseStatistics(fsUserWatchCourseStatistics));
+    }
+
+    /**
+     * 修改会员看课统计-按课程统计
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchCourseStatistics:edit')")
+    @Log(title = "会员看课统计-按课程统计", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsUserWatchCourseStatistics fsUserWatchCourseStatistics)
+    {
+        return toAjax(fsUserWatchCourseStatisticsService.updateFsUserWatchCourseStatistics(fsUserWatchCourseStatistics));
+    }
+
+    /**
+     * 删除会员看课统计-按课程统计
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchCourseStatistics:remove')")
+    @Log(title = "会员看课统计-按课程统计", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fsUserWatchCourseStatisticsService.deleteFsUserWatchCourseStatisticsByIds(ids));
+    }
+
+
+    @PostMapping("/test")
+    @ApiOperation("测试看课统计明细定时任务")
+    public void userCourseCountTask() {
+        fsUserWatchCourseStatisticsService.insertWatchCourseStatistics();
+    }
+}

+ 106 - 0
fs-company/src/main/java/com/fs/course/controller/FsUserWatchStatisticsController.java

@@ -0,0 +1,106 @@
+package com.fs.course.controller;
+
+import com.fs.common.annotation.Log;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.common.enums.BusinessType;
+import com.fs.common.utils.poi.ExcelUtil;
+import com.fs.course.domain.FsUserWatchStatistics;
+import com.fs.course.service.IFsUserWatchStatisticsService;
+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.List;
+
+/**
+ * 会员看课统计-按营期统计Controller
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+@RestController
+@RequestMapping("/course/userWatchStatistics")
+public class FsUserWatchStatisticsController extends BaseController
+{
+    @Autowired
+    private IFsUserWatchStatisticsService fsUserWatchStatisticsService;
+
+    /**
+     * 查询会员看课统计-按营期统计列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchStatistics:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsUserWatchStatistics fsUserWatchStatistics)
+    {
+        startPage();
+        List<FsUserWatchStatistics> list = fsUserWatchStatisticsService.selectFsUserWatchStatisticsList(fsUserWatchStatistics);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出会员看课统计-按营期统计列表
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchStatistics:export')")
+    @Log(title = "会员看课统计-按营期统计", businessType = BusinessType.EXPORT)
+    @GetMapping("/export")
+    public AjaxResult export(FsUserWatchStatistics fsUserWatchStatistics)
+    {
+        List<FsUserWatchStatistics> list = fsUserWatchStatisticsService.selectFsUserWatchStatisticsList(fsUserWatchStatistics);
+        ExcelUtil<FsUserWatchStatistics> util = new ExcelUtil<FsUserWatchStatistics>(FsUserWatchStatistics.class);
+        return util.exportExcel(list, "会员看课统计-按营期统计数据");
+    }
+
+    /**
+     * 获取会员看课统计-按营期统计详细信息
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchStatistics:query')")
+    @GetMapping(value = "/{id}")
+    public AjaxResult getInfo(@PathVariable("id") Long id)
+    {
+        return AjaxResult.success(fsUserWatchStatisticsService.selectFsUserWatchStatisticsById(id));
+    }
+
+    /**
+     * 新增会员看课统计-按营期统计
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchStatistics:add')")
+    @Log(title = "会员看课统计-按营期统计", businessType = BusinessType.INSERT)
+    @PostMapping
+    public AjaxResult add(@RequestBody FsUserWatchStatistics fsUserWatchStatistics)
+    {
+        return toAjax(fsUserWatchStatisticsService.insertFsUserWatchStatistics(fsUserWatchStatistics));
+    }
+
+    /**
+     * 修改会员看课统计-按营期统计
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchStatistics:edit')")
+    @Log(title = "会员看课统计-按营期统计", businessType = BusinessType.UPDATE)
+    @PutMapping
+    public AjaxResult edit(@RequestBody FsUserWatchStatistics fsUserWatchStatistics)
+    {
+        return toAjax(fsUserWatchStatisticsService.updateFsUserWatchStatistics(fsUserWatchStatistics));
+    }
+
+    /**
+     * 删除会员看课统计-按营期统计
+     */
+    @PreAuthorize("@ss.hasPermi('course:userWatchStatistics:remove')")
+    @Log(title = "会员看课统计-按营期统计", businessType = BusinessType.DELETE)
+	@DeleteMapping("/{ids}")
+    public AjaxResult remove(@PathVariable Long[] ids)
+    {
+        return toAjax(fsUserWatchStatisticsService.deleteFsUserWatchStatisticsByIds(ids));
+    }
+
+
+    @PostMapping("/test")
+    @ApiOperation("测试营期看课统计定时任务")
+    public void userCourseCountTask() {
+        fsUserWatchStatisticsService.insertStatistics();
+    }
+
+}

+ 43 - 0
fs-company/src/main/java/com/fs/store/controller/FsStoreOrderAuditController.java

@@ -0,0 +1,43 @@
+package com.fs.store.controller;
+
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.domain.AjaxResult;
+import com.fs.common.core.page.TableDataInfo;
+import com.fs.company.domain.CompanyUser;
+import com.fs.core.security.SecurityUtils;
+import com.fs.store.param.FsStoreOrderAuditParam;
+import com.fs.store.param.FsStoreOrderAuditReviewParam;
+import com.fs.store.service.IFsStoreOrderAuditService;
+import com.fs.store.vo.FsStoreOrderAuditVO;
+import lombok.AllArgsConstructor;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+@RestController
+@RequestMapping("/store/storeOrderAudit")
+@AllArgsConstructor
+public class FsStoreOrderAuditController extends BaseController {
+
+    private final IFsStoreOrderAuditService fsStoreOrderAuditService;
+
+    @PreAuthorize("@ss.hasAnyPermi('store:storeOrderAudit:list')")
+    @GetMapping("/list")
+    public TableDataInfo list(FsStoreOrderAuditParam param) {
+        startPage();
+        CompanyUser user = SecurityUtils.getLoginUser().getUser();
+        param.setCompanyId(user.getCompanyId());
+        List<FsStoreOrderAuditVO> list = fsStoreOrderAuditService.selectStoreOrderAuditVOList(param);
+        return getDataTable(list);
+    }
+
+    @PreAuthorize("@ss.hasAnyPermi('store:storeOrderAudit:audit')")
+    @PostMapping("/audit")
+    public AjaxResult audit(@Valid @RequestBody FsStoreOrderAuditReviewParam param) {
+        CompanyUser user = SecurityUtils.getLoginUser().getUser();
+        fsStoreOrderAuditService.audit(param, user.getUserId(), false);
+        return AjaxResult.success();
+    }
+}

+ 8 - 1
fs-company/src/main/java/com/fs/store/controller/FsStoreOrderController.java

@@ -28,6 +28,7 @@ import com.fs.store.param.FsStoreOrderCreateUserParam;
 import com.fs.store.param.FsStoreOrderFinishParam;
 import com.fs.store.param.FsStoreOrderParam;
 import com.fs.store.service.*;
+import com.fs.store.vo.FsStoreOrderAuditLogVO;
 import com.fs.store.vo.FsStoreOrderExportVO;
 import com.fs.store.vo.FsStoreOrderItemExportVO;
 import com.fs.store.vo.FsStoreOrderVO;
@@ -69,6 +70,9 @@ public class FsStoreOrderController extends BaseController
     private TokenService tokenService;
     @Autowired
     private ICrmCustomerService crmCustomerService;
+    @Autowired
+    private IFsStoreOrderAuditLogService orderAuditLogService;
+
     /**
      * 查询订单列表
      */
@@ -202,7 +206,10 @@ public class FsStoreOrderController extends BaseController
         if(order.getCustomerId()!=null&&order.getCustomerId()>0){
             customer=crmCustomerService.selectCrmCustomerById(order.getCustomerId());
         }
-        return R.ok().put("order", order).put("items", items).put("logs",logs).put("user",user).put("customer",customer).put("payments",payments) ;
+
+        List<FsStoreOrderAuditLogVO> auditLogs = orderAuditLogService.selectStoreOrderAuditLogVOByOrderId(order.getId());
+        return R.ok().put("order", order).put("items", items).put("logs",logs).put("user",user).put("customer",customer).put("payments",payments)
+                .put("auditLogs", auditLogs);
     }
 
     @GetMapping(value = "/queryAddress/{id}")

+ 38 - 0
fs-service-system/src/main/java/com/fs/course/config/CourseMaConfig.java

@@ -0,0 +1,38 @@
+package com.fs.course.config;
+
+import lombok.Data;
+
+@Data
+public class CourseMaConfig {
+
+    /**
+     * 设置微信小程序的名称
+     */
+    private String name;
+
+    /**
+     * 设置微信小程序的appid
+     */
+    private String appid;
+
+    /**
+     * 设置微信小程序的Secret
+     */
+    private String secret;
+
+    /**
+     * 设置微信小程序消息服务器配置的token
+     */
+    private String token;
+
+    /**
+     * 设置微信小程序消息服务器配置的EncodingAESKey
+     */
+    private String aesKey;
+
+    /**
+     * 消息格式,XML或者JSON
+     */
+    private String msgDataFormat;
+
+}

+ 118 - 0
fs-service-system/src/main/java/com/fs/course/domain/FsUserWatchCourseStatistics.java

@@ -0,0 +1,118 @@
+package com.fs.course.domain;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 会员看课统计-按课程统计对象 fs_user_watch_course_statistics
+ *
+ * @author fs
+ * @date 2025-06-17
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsUserWatchCourseStatistics extends BaseEntity{
+
+    /** 主键id */
+    private Long id;
+
+    /** 营期id */
+//    @Excel(name = "营期id")
+    private Long periodId;
+
+    /** 营期名称 */
+    @Excel(name = "营期名称")
+    private String periodName;
+
+    /** 营期开始日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "营期日期", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date periodStartingTime;
+
+    /** 课程开始日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "播出时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date courseStartDateTime;
+
+    /** 课程id */
+//    @Excel(name = "课程id")
+    private Long courseId;
+
+    /** 课程名称 */
+    @Excel(name = "课程名称")
+    private String courseName;
+
+    /** 视频id */
+//    @Excel(name = "视频id")
+    private Long videoId;
+
+    /** 视频标题 */
+    @Excel(name = "视频小节")
+    private String videoTitle;
+
+    /** 销售公司id */
+//    @Excel(name = "销售公司id")
+    private Long companyId;
+
+    /** 销售公司名称 */
+    @Excel(name = "销售公司")
+    private String companyName;
+
+    /** 销售id */
+//    @Excel(name = "销售id")
+    private Long companyUserId;
+
+    /** 销售名称 */
+    @Excel(name = "所属销售")
+    private String companyUserName;
+
+    /** 新增会员数量 */
+    @Excel(name = "新增会员")
+    private Integer newUserNum;
+
+    /** 会员数量 */
+    @Excel(name = "会员数量")
+    private Integer userNum;
+
+    /** 观看人数 */
+    @Excel(name = "观看人数")
+    private Integer watchNum;
+
+    /** 完播人数 */
+    @Excel(name = "完播人数")
+    private Integer completeWatchNum;
+
+    /** 完播率 */
+    @Excel(name = "完播率")
+    private BigDecimal completeWatchRate;
+
+    /** 答题人数 */
+    @Excel(name = "答题人数")
+    private Integer answerNum;
+
+    /** 答题正确人数 */
+    @Excel(name = "正确人数")
+    private Integer answerRightNum;
+
+    /** 答题正确率 */
+    @Excel(name = "正确率")
+    private BigDecimal answerRightRate;
+
+    /** 红包领取数量 */
+    @Excel(name = "红包领取个数")
+    private Integer redPacketNum;
+
+    /** 红包领取总额 */
+    @Excel(name = "红包领取总额")
+    private BigDecimal redPacketAmount;
+
+
+
+
+}

+ 60 - 0
fs-service-system/src/main/java/com/fs/course/domain/FsUserWatchStatistics.java

@@ -0,0 +1,60 @@
+package com.fs.course.domain;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.fs.common.annotation.Excel;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.EqualsAndHashCode;
+
+/**
+ * 会员看课统计-按营期统计对象 fs_user_watch_statistics
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsUserWatchStatistics extends BaseEntity{
+
+    /** 主键id */
+    private Long id;
+
+    /** 营期id */
+//    @Excel(name = "营期id")
+    private Long periodId;
+
+    /** 营期名称 */
+    @Excel(name = "营期名称")
+    private String periodName;
+
+    /** 营期开始日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "营期开始日期", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date periodStartingTime;
+
+    /** 新增会员数量 */
+    @Excel(name = "新增会员数量")
+    private Integer newUserNum;
+
+    /** 会员数量 */
+    @Excel(name = "会员数量")
+    private Integer userNum;
+
+    /** 观看人数 */
+    @Excel(name = "观看人数")
+    private Integer watchNum;
+
+    /** 完播人数 */
+    @Excel(name = "完播人数")
+    private Integer completeWatchNum;
+
+    /** 完播率 */
+    @Excel(name = "完播率")
+    private BigDecimal completeWatchRate;
+
+
+}

+ 8 - 0
fs-service-system/src/main/java/com/fs/course/mapper/FsUserCoursePeriodDaysMapper.java

@@ -2,6 +2,7 @@ package com.fs.course.mapper;
 
 import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.fs.course.domain.FsUserCoursePeriodDays;
+import com.fs.course.domain.FsUserWatchCourseStatistics;
 import com.fs.his.vo.OptionsVO;
 import org.apache.ibatis.annotations.Param;
 
@@ -95,4 +96,11 @@ public interface FsUserCoursePeriodDaysMapper extends BaseMapper<FsUserCoursePer
      * @param now  时间
      */
     void endPeriodCourse(@Param("now") LocalDateTime now);
+
+    /**
+     * 查询视频相关信息
+     * @return
+     */
+    List<FsUserWatchCourseStatistics> selectDaysCountList();
+
 }

+ 66 - 0
fs-service-system/src/main/java/com/fs/course/mapper/FsUserWatchCourseStatisticsMapper.java

@@ -0,0 +1,66 @@
+package com.fs.course.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.course.domain.FsUserWatchCourseStatistics;
+
+/**
+ * 会员看课统计-按课程统计Mapper接口
+ *
+ * @author fs
+ * @date 2025-06-17
+ */
+public interface FsUserWatchCourseStatisticsMapper extends BaseMapper<FsUserWatchCourseStatistics>{
+    /**
+     * 查询会员看课统计-按课程统计
+     *
+     * @param id 会员看课统计-按课程统计主键
+     * @return 会员看课统计-按课程统计
+     */
+    FsUserWatchCourseStatistics selectFsUserWatchCourseStatisticsById(Long id);
+
+    /**
+     * 查询会员看课统计-按课程统计列表
+     *
+     * @param fsUserWatchCourseStatistics 会员看课统计-按课程统计
+     * @return 会员看课统计-按课程统计集合
+     */
+    List<FsUserWatchCourseStatistics> selectFsUserWatchCourseStatisticsList(FsUserWatchCourseStatistics fsUserWatchCourseStatistics);
+
+    /**
+     * 新增会员看课统计-按课程统计
+     *
+     * @param fsUserWatchCourseStatistics 会员看课统计-按课程统计
+     * @return 结果
+     */
+    int insertFsUserWatchCourseStatistics(FsUserWatchCourseStatistics fsUserWatchCourseStatistics);
+
+    /**
+     * 修改会员看课统计-按课程统计
+     *
+     * @param fsUserWatchCourseStatistics 会员看课统计-按课程统计
+     * @return 结果
+     */
+    int updateFsUserWatchCourseStatistics(FsUserWatchCourseStatistics fsUserWatchCourseStatistics);
+
+    /**
+     * 删除会员看课统计-按课程统计
+     *
+     * @param id 会员看课统计-按课程统计主键
+     * @return 结果
+     */
+    int deleteFsUserWatchCourseStatisticsById(Long id);
+
+    /**
+     * 批量删除会员看课统计-按课程统计
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsUserWatchCourseStatisticsByIds(Long[] ids);
+
+    /**
+     * 插入看课统计数据
+     */
+    void insertFsUserWatchCourseStatisticsTask(FsUserWatchCourseStatistics fsUserWatchCourseStatistics);
+}

+ 73 - 0
fs-service-system/src/main/java/com/fs/course/mapper/FsUserWatchStatisticsMapper.java

@@ -0,0 +1,73 @@
+package com.fs.course.mapper;
+
+import java.util.List;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.course.domain.FsUserWatchStatistics;
+
+/**
+ * 会员看课统计-按营期统计Mapper接口
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+public interface FsUserWatchStatisticsMapper extends BaseMapper<FsUserWatchStatistics>{
+    /**
+     * 查询会员看课统计-按营期统计
+     *
+     * @param id 会员看课统计-按营期统计主键
+     * @return 会员看课统计-按营期统计
+     */
+    FsUserWatchStatistics selectFsUserWatchStatisticsById(Long id);
+
+    /**
+     * 查询会员看课统计-按营期统计列表
+     *
+     * @param fsUserWatchStatistics 会员看课统计-按营期统计
+     * @return 会员看课统计-按营期统计集合
+     */
+    List<FsUserWatchStatistics> selectFsUserWatchStatisticsList(FsUserWatchStatistics fsUserWatchStatistics);
+
+    /**
+     * 新增会员看课统计-按营期统计
+     *
+     * @param fsUserWatchStatistics 会员看课统计-按营期统计
+     * @return 结果
+     */
+    int insertFsUserWatchStatistics(FsUserWatchStatistics fsUserWatchStatistics);
+
+    /**
+     * 修改会员看课统计-按营期统计
+     *
+     * @param fsUserWatchStatistics 会员看课统计-按营期统计
+     * @return 结果
+     */
+    int updateFsUserWatchStatistics(FsUserWatchStatistics fsUserWatchStatistics);
+
+    /**
+     * 删除会员看课统计-按营期统计
+     *
+     * @param id 会员看课统计-按营期统计主键
+     * @return 结果
+     */
+    int deleteFsUserWatchStatisticsById(Long id);
+
+    /**
+     * 批量删除会员看课统计-按营期统计
+     *
+     * @param ids 需要删除的数据主键集合
+     * @return 结果
+     */
+    int deleteFsUserWatchStatisticsByIds(Long[] ids);
+
+    /**
+     * 获取看课统计-按营期统计
+     * @return
+     */
+    List<FsUserWatchStatistics> getCourseWatchStatistics();
+
+    /**
+     * 插入看课统计数据
+     */
+    void insertFsUserWatchStatisticsTask(FsUserWatchStatistics fsUserWatchStatistics);
+
+}

+ 66 - 0
fs-service-system/src/main/java/com/fs/course/service/IFsUserWatchCourseStatisticsService.java

@@ -0,0 +1,66 @@
+package com.fs.course.service;
+
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.course.domain.FsUserWatchCourseStatistics;
+
+/**
+ * 会员看课统计-按课程统计Service接口
+ *
+ * @author fs
+ * @date 2025-06-17
+ */
+public interface IFsUserWatchCourseStatisticsService extends IService<FsUserWatchCourseStatistics>{
+    /**
+     * 查询会员看课统计-按课程统计
+     *
+     * @param id 会员看课统计-按课程统计主键
+     * @return 会员看课统计-按课程统计
+     */
+    FsUserWatchCourseStatistics selectFsUserWatchCourseStatisticsById(Long id);
+
+    /**
+     * 查询会员看课统计-按课程统计列表
+     *
+     * @param fsUserWatchCourseStatistics 会员看课统计-按课程统计
+     * @return 会员看课统计-按课程统计集合
+     */
+    List<FsUserWatchCourseStatistics> selectFsUserWatchCourseStatisticsList(FsUserWatchCourseStatistics fsUserWatchCourseStatistics);
+
+    /**
+     * 新增会员看课统计-按课程统计
+     *
+     * @param fsUserWatchCourseStatistics 会员看课统计-按课程统计
+     * @return 结果
+     */
+    int insertFsUserWatchCourseStatistics(FsUserWatchCourseStatistics fsUserWatchCourseStatistics);
+
+    /**
+     * 修改会员看课统计-按课程统计
+     *
+     * @param fsUserWatchCourseStatistics 会员看课统计-按课程统计
+     * @return 结果
+     */
+    int updateFsUserWatchCourseStatistics(FsUserWatchCourseStatistics fsUserWatchCourseStatistics);
+
+    /**
+     * 批量删除会员看课统计-按课程统计
+     *
+     * @param ids 需要删除的会员看课统计-按课程统计主键集合
+     * @return 结果
+     */
+    int deleteFsUserWatchCourseStatisticsByIds(Long[] ids);
+
+    /**
+     * 删除会员看课统计-按课程统计信息
+     *
+     * @param id 会员看课统计-按课程统计主键
+     * @return 结果
+     */
+    int deleteFsUserWatchCourseStatisticsById(Long id);
+
+    /**
+     * 新增按营期课程统计会员看课表数据
+     */
+    void insertWatchCourseStatistics();
+}

+ 66 - 0
fs-service-system/src/main/java/com/fs/course/service/IFsUserWatchStatisticsService.java

@@ -0,0 +1,66 @@
+package com.fs.course.service;
+
+import java.util.List;
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.course.domain.FsUserWatchStatistics;
+
+/**
+ * 会员看课统计-按营期统计Service接口
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+public interface IFsUserWatchStatisticsService extends IService<FsUserWatchStatistics>{
+    /**
+     * 查询会员看课统计-按营期统计
+     *
+     * @param id 会员看课统计-按营期统计主键
+     * @return 会员看课统计-按营期统计
+     */
+    FsUserWatchStatistics selectFsUserWatchStatisticsById(Long id);
+
+    /**
+     * 查询会员看课统计-按营期统计列表
+     *
+     * @param fsUserWatchStatistics 会员看课统计-按营期统计
+     * @return 会员看课统计-按营期统计集合
+     */
+    List<FsUserWatchStatistics> selectFsUserWatchStatisticsList(FsUserWatchStatistics fsUserWatchStatistics);
+
+    /**
+     * 新增会员看课统计-按营期统计
+     *
+     * @param fsUserWatchStatistics 会员看课统计-按营期统计
+     * @return 结果
+     */
+    int insertFsUserWatchStatistics(FsUserWatchStatistics fsUserWatchStatistics);
+
+    /**
+     * 修改会员看课统计-按营期统计
+     *
+     * @param fsUserWatchStatistics 会员看课统计-按营期统计
+     * @return 结果
+     */
+    int updateFsUserWatchStatistics(FsUserWatchStatistics fsUserWatchStatistics);
+
+    /**
+     * 批量删除会员看课统计-按营期统计
+     *
+     * @param ids 需要删除的会员看课统计-按营期统计主键集合
+     * @return 结果
+     */
+    int deleteFsUserWatchStatisticsByIds(Long[] ids);
+
+    /**
+     * 删除会员看课统计-按营期统计信息
+     *
+     * @param id 会员看课统计-按营期统计主键
+     * @return 结果
+     */
+    int deleteFsUserWatchStatisticsById(Long id);
+
+    /**
+     * 新增按营期统计会员看课表数据
+     */
+    void insertStatistics();
+}

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

@@ -1019,14 +1019,14 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
             courseWatchLogMapper.insertFsCourseWatchLog(fsCourseWatchLog);
         }
 
-//        // 判断是否重粉,添加关系表数据
+        // 添加会员销售关系表数据
         // 逻辑调整:如果会员已经绑定了销售,直接提示,不添加重粉数据了-2025年6月16日14点58分
-//        FsUserCompanyUser fsUserCompanyUser = getFsUserCompanyUser(param, fsUser);
-//        QueryWrapper<FsUserCompanyUser> queryWrapper = new QueryWrapper<FsUserCompanyUser>().eq("user_id", param.getUserId()).eq("company_user_id", param.getCompanyUserId());
-//        Integer i = fsUserCompanyUserMapper.selectCount(queryWrapper);
-//        if(i == 0) {
-//            fsUserCompanyUserMapper.insertFsUserCompanyUser(fsUserCompanyUser);
-//        }
+        FsUserCompanyUser fsUserCompanyUser = getFsUserCompanyUser(param, fsUser);
+        QueryWrapper<FsUserCompanyUser> queryWrapper = new QueryWrapper<FsUserCompanyUser>().eq("user_id", param.getUserId()).eq("company_user_id", param.getCompanyUserId());
+        Integer i = fsUserCompanyUserMapper.selectCount(queryWrapper);
+        if(i == 0) {
+            fsUserCompanyUserMapper.insertFsUserCompanyUser(fsUserCompanyUser);
+        }
 
 //        // 如果重粉需要打上重粉标签
 //        if(1 == fsUserCompanyUser.getIsRepeatFans()){
@@ -1097,6 +1097,7 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
 
         long duration = 0L;
         long tipsTime = 0L;
+        long tipsTime2 = 0L;
         int isFinish = 0;
         FsUserCourseVideoLinkDetailsVO vo = new FsUserCourseVideoLinkDetailsVO();
         vo.setCourseVideoDetails(courseVideoDetails);
@@ -1121,9 +1122,11 @@ public class FsUserCourseVideoServiceImpl implements IFsUserCourseVideoService
         }
 //
         if (courseVideoDetails != null && courseVideoDetails.getDuration() != null){
-            tipsTime = courseVideoDetails.getDuration()/2;
+            tipsTime = courseVideoDetails.getDuration() / 3;
+            tipsTime2 = (courseVideoDetails.getDuration() * 2) / 3;
         }
         vo.setTipsTime(tipsTime);
+        vo.setTipsTime2(tipsTime2);
         //判断是否完课
         if (watchLog!=null && watchLog.getLogType() == 2) {
             isFinish = 1;

+ 209 - 0
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserWatchCourseStatisticsServiceImpl.java

@@ -0,0 +1,209 @@
+package com.fs.course.service.impl;
+
+import java.math.BigDecimal;
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.course.mapper.FsUserCoursePeriodDaysMapper;
+import com.fs.store.mapper.FsUserMapper;
+import com.google.common.collect.Lists;
+import lombok.AllArgsConstructor;
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.beans.BeanUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fs.course.mapper.FsUserWatchCourseStatisticsMapper;
+import com.fs.course.domain.FsUserWatchCourseStatistics;
+import com.fs.course.service.IFsUserWatchCourseStatisticsService;
+
+
+/**
+ * 会员看课统计-按课程统计Service业务层处理
+ *
+ * @author fs
+ * @date 2025-06-17
+ */
+@Service
+@AllArgsConstructor
+public class FsUserWatchCourseStatisticsServiceImpl extends ServiceImpl<FsUserWatchCourseStatisticsMapper, FsUserWatchCourseStatistics> implements IFsUserWatchCourseStatisticsService {
+
+    private FsUserCoursePeriodDaysMapper fsUserCoursePeriodDaysMapper;
+
+    private FsUserMapper fsUserMapper;
+
+    @Autowired
+    private SqlSessionFactory sqlSessionFactory;
+
+    /**
+     * 查询会员看课统计-按课程统计
+     *
+     * @param id 会员看课统计-按课程统计主键
+     * @return 会员看课统计-按课程统计
+     */
+    @Override
+    public FsUserWatchCourseStatistics selectFsUserWatchCourseStatisticsById(Long id)
+    {
+        return baseMapper.selectFsUserWatchCourseStatisticsById(id);
+    }
+
+    /**
+     * 查询会员看课统计-按课程统计列表
+     *
+     * @param fsUserWatchCourseStatistics 会员看课统计-按课程统计
+     * @return 会员看课统计-按课程统计
+     */
+    @Override
+    public List<FsUserWatchCourseStatistics> selectFsUserWatchCourseStatisticsList(FsUserWatchCourseStatistics fsUserWatchCourseStatistics)
+    {
+        return baseMapper.selectFsUserWatchCourseStatisticsList(fsUserWatchCourseStatistics);
+    }
+
+    /**
+     * 新增会员看课统计-按课程统计
+     *
+     * @param fsUserWatchCourseStatistics 会员看课统计-按课程统计
+     * @return 结果
+     */
+    @Override
+    public int insertFsUserWatchCourseStatistics(FsUserWatchCourseStatistics fsUserWatchCourseStatistics)
+    {
+        return baseMapper.insertFsUserWatchCourseStatistics(fsUserWatchCourseStatistics);
+    }
+
+    /**
+     * 修改会员看课统计-按课程统计
+     *
+     * @param fsUserWatchCourseStatistics 会员看课统计-按课程统计
+     * @return 结果
+     */
+    @Override
+    public int updateFsUserWatchCourseStatistics(FsUserWatchCourseStatistics fsUserWatchCourseStatistics)
+    {
+        return baseMapper.updateFsUserWatchCourseStatistics(fsUserWatchCourseStatistics);
+    }
+
+    /**
+     * 批量删除会员看课统计-按课程统计
+     *
+     * @param ids 需要删除的会员看课统计-按课程统计主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserWatchCourseStatisticsByIds(Long[] ids)
+    {
+        return baseMapper.deleteFsUserWatchCourseStatisticsByIds(ids);
+    }
+
+    /**
+     * 删除会员看课统计-按课程统计信息
+     *
+     * @param id 会员看课统计-按课程统计主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserWatchCourseStatisticsById(Long id)
+    {
+        return baseMapper.deleteFsUserWatchCourseStatisticsById(id);
+    }
+
+    @Override
+    public void insertWatchCourseStatistics() {
+        // 1、获取统计数据
+        // 查询课程相关数据
+        List<FsUserWatchCourseStatistics> fsUserWatchCourseStatistics = fsUserCoursePeriodDaysMapper.selectDaysCountList();
+
+        // 查询统计数据
+        List<FsUserWatchCourseStatistics> watchLog = fsUserMapper.selectWatchLogCount();
+        List<FsUserWatchCourseStatistics> redPacketLog = fsUserMapper.selectRedPacketLogCount();
+        List<FsUserWatchCourseStatistics> answerLog = fsUserMapper.selectAnswerLogCount();
+        List<FsUserWatchCourseStatistics> userTotal = fsUserMapper.selectFsUserDetail();
+
+        // 转化为自定义键的map
+        Map<String, FsUserWatchCourseStatistics> watchLogMap = watchLog.stream().collect(Collectors.toMap(k -> String.format("%s-%s-%s", k.getPeriodId(), k.getVideoId(), k.getCompanyUserId()), v -> v));
+        Map<String, FsUserWatchCourseStatistics> redPacketLogMap = redPacketLog.stream().collect(Collectors.toMap(k -> String.format("%s-%s-%s", k.getPeriodId(), k.getVideoId(), k.getCompanyUserId()), v -> v));
+        Map<String, FsUserWatchCourseStatistics> answerLogMap = answerLog.stream().collect(Collectors.toMap(k -> String.format("%s-%s-%s", k.getPeriodId(), k.getVideoId(), k.getCompanyUserId()), v -> v));
+        Map<Long, FsUserWatchCourseStatistics> userTotalMap = userTotal.stream()
+                .collect(Collectors.toMap(
+                        FsUserWatchCourseStatistics::getCompanyUserId,
+                        Function.identity()
+                ));
+
+
+        // 处理数据
+        List<FsUserWatchCourseStatistics> list = new ArrayList<>();
+        for (FsUserWatchCourseStatistics data : fsUserWatchCourseStatistics) {
+            FsUserWatchCourseStatistics vo = new FsUserWatchCourseStatistics();
+            String key = String.format("%s-%s-%s", data.getPeriodId(), data.getVideoId(), data.getCompanyUserId());
+            FsUserWatchCourseStatistics watchLogData = watchLogMap.get(key);
+            FsUserWatchCourseStatistics redPacketLogData = redPacketLogMap.get(key);
+            FsUserWatchCourseStatistics answerLogData = answerLogMap.get(key);
+            FsUserWatchCourseStatistics userTotalData = userTotalMap.get(data.getCompanyUserId());
+            BeanUtils.copyProperties(data, vo);
+
+            // 单独一个一个set,不用copy,避免copy出来的结果被前面的覆盖
+            if(userTotalData != null){
+                vo.setUserNum(userTotalData.getUserNum());
+                vo.setNewUserNum(userTotalData.getNewUserNum());
+            } else {
+                vo.setUserNum(0);
+                vo.setNewUserNum(0);
+            }
+
+            if(watchLogData != null) {
+                vo.setWatchNum(watchLogData.getWatchNum());
+                vo.setCompleteWatchNum(watchLogData.getCompleteWatchNum());
+                vo.setCompleteWatchRate(watchLogData.getCompleteWatchRate());
+            } else {
+                vo.setWatchNum(0);
+                vo.setCompleteWatchNum(0);
+                vo.setCompleteWatchRate(BigDecimal.ZERO);
+            }
+
+            if(redPacketLogData != null) {
+                vo.setRedPacketNum(redPacketLogData.getRedPacketNum());
+                vo.setRedPacketAmount(redPacketLogData.getRedPacketAmount());
+            } else {
+                vo.setRedPacketNum(0);
+                vo.setRedPacketAmount(BigDecimal.ZERO);
+            }
+
+            if(answerLogData != null) {
+                vo.setAnswerNum(answerLogData.getAnswerNum());
+                vo.setAnswerRightNum(answerLogData.getAnswerRightNum());
+                vo.setAnswerRightRate(answerLogData.getAnswerRightRate());
+            } else {
+                vo.setAnswerNum(0);
+                vo.setAnswerRightNum(0);
+                vo.setAnswerRightRate(BigDecimal.ZERO);
+            }
+
+            vo.setCreateTime(new Date());
+            vo.setUpdateTime(new Date());
+            list.add(vo);
+        }
+
+        //2、分批次插入数据
+        this.batchInsert(list);
+
+    }
+
+    private void batchInsert(List<FsUserWatchCourseStatistics> list) {
+        // 分批次处理,一次提交500条
+        List<List<FsUserWatchCourseStatistics>> batches = Lists.partition(list, 500);
+        batches.forEach(batch -> {
+            SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
+            try {
+                FsUserWatchCourseStatisticsMapper mapper = sqlSession.getMapper(FsUserWatchCourseStatisticsMapper.class);
+                batch.forEach(mapper::insertFsUserWatchCourseStatisticsTask);
+                sqlSession.commit();
+            } finally {
+                sqlSession.close();
+            }
+        });
+    }
+
+}

+ 178 - 0
fs-service-system/src/main/java/com/fs/course/service/impl/FsUserWatchStatisticsServiceImpl.java

@@ -0,0 +1,178 @@
+package com.fs.course.service.impl;
+
+import java.math.BigDecimal;
+import java.util.Date;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.course.mapper.FsUserCoursePeriodMapper;
+import com.fs.store.mapper.FsUserMapper;
+import com.google.common.collect.Lists;
+import lombok.AllArgsConstructor;
+import org.apache.ibatis.session.ExecutorType;
+import org.apache.ibatis.session.SqlSession;
+import org.apache.ibatis.session.SqlSessionFactory;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.stereotype.Service;
+import com.fs.course.mapper.FsUserWatchStatisticsMapper;
+import com.fs.course.domain.FsUserWatchStatistics;
+import com.fs.course.service.IFsUserWatchStatisticsService;
+import org.springframework.transaction.annotation.Transactional;
+
+/**
+ * 会员看课统计-按营期统计Service业务层处理
+ *
+ * @author fs
+ * @date 2025-06-16
+ */
+@Service
+@AllArgsConstructor
+public class FsUserWatchStatisticsServiceImpl extends ServiceImpl<FsUserWatchStatisticsMapper, FsUserWatchStatistics> implements IFsUserWatchStatisticsService {
+
+    private final FsUserCoursePeriodMapper fsUserCoursePeriodMapper;
+
+    private final FsUserMapper fsUserMapper;
+
+    @Autowired
+    private SqlSessionFactory sqlSessionFactory;
+
+    /**
+     * 查询会员看课统计-按营期统计
+     *
+     * @param id 会员看课统计-按营期统计主键
+     * @return 会员看课统计-按营期统计
+     */
+    @Override
+    public FsUserWatchStatistics selectFsUserWatchStatisticsById(Long id)
+    {
+        return baseMapper.selectFsUserWatchStatisticsById(id);
+    }
+
+    /**
+     * 查询会员看课统计-按营期统计列表
+     *
+     * @param fsUserWatchStatistics 会员看课统计-按营期统计
+     * @return 会员看课统计-按营期统计
+     */
+    @Override
+    public List<FsUserWatchStatistics> selectFsUserWatchStatisticsList(FsUserWatchStatistics fsUserWatchStatistics)
+    {
+        return baseMapper.selectFsUserWatchStatisticsList(fsUserWatchStatistics);
+    }
+
+    /**
+     * 新增会员看课统计-按营期统计
+     *
+     * @param fsUserWatchStatistics 会员看课统计-按营期统计
+     * @return 结果
+     */
+    @Override
+    public int insertFsUserWatchStatistics(FsUserWatchStatistics fsUserWatchStatistics)
+    {
+        return baseMapper.insertFsUserWatchStatistics(fsUserWatchStatistics);
+    }
+
+    /**
+     * 修改会员看课统计-按营期统计
+     *
+     * @param fsUserWatchStatistics 会员看课统计-按营期统计
+     * @return 结果
+     */
+    @Override
+    public int updateFsUserWatchStatistics(FsUserWatchStatistics fsUserWatchStatistics)
+    {
+        return baseMapper.updateFsUserWatchStatistics(fsUserWatchStatistics);
+    }
+
+    /**
+     * 批量删除会员看课统计-按营期统计
+     *
+     * @param ids 需要删除的会员看课统计-按营期统计主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserWatchStatisticsByIds(Long[] ids)
+    {
+        return baseMapper.deleteFsUserWatchStatisticsByIds(ids);
+    }
+
+    /**
+     * 删除会员看课统计-按营期统计信息
+     *
+     * @param id 会员看课统计-按营期统计主键
+     * @return 结果
+     */
+    @Override
+    public int deleteFsUserWatchStatisticsById(Long id)
+    {
+        return baseMapper.deleteFsUserWatchStatisticsById(id);
+    }
+
+    @Override
+    @Transactional
+    public void insertStatistics() {
+        // 1、获取统计结果
+        // 获取所有营期
+//        FsUserCoursePeriod periodParam = new FsUserCoursePeriod();
+//        List<FsUserCoursePeriod> fsUserCoursePeriods = fsUserCoursePeriodMapper.selectFsUserCoursePeriodList(periodParam);
+
+        //获取会员数量和新增会员数量
+        List<FsUserWatchStatistics> userTotal = fsUserMapper.selectFsUserTotal();
+        FsUserWatchStatistics userTotalStatistics = new FsUserWatchStatistics();
+        if(!userTotal.isEmpty()){
+            userTotalStatistics = userTotal.get(0);
+        }
+
+        // 获取看课统计
+        List<FsUserWatchStatistics> courseWatchStatistics = baseMapper.getCourseWatchStatistics();
+
+        // 转化map
+        Map<Long, FsUserWatchStatistics> watchStatisticsMap = courseWatchStatistics.stream()
+                .collect(Collectors.toMap(
+                        FsUserWatchStatistics::getPeriodId,
+                        Function.identity()
+                ));
+        // 组装数据
+//        List<FsUserWatchStatistics> list = fsUserCoursePeriods.stream().map(v -> {
+//            FsUserWatchStatistics fsUserWatchStatistics = new FsUserWatchStatistics();
+//            BeanUtils.copyProperties(v, fsUserWatchStatistics);
+//            FsUserWatchStatistics count = watchStatisticsMap.getOrDefault(v.getPeriodId(), fsUserWatchStatistics);
+//            fsUserWatchStatistics.setWatchNum(count.getWatchNum());
+//            fsUserWatchStatistics.setCompleteWatchNum(count.getCompleteWatchNum());
+//            fsUserWatchStatistics.setCompleteWatchRate(count.getCompleteWatchRate());
+//            return fsUserWatchStatistics;
+//        }).collect(Collectors.toList());
+        for (FsUserWatchStatistics courseWatchStatistic : courseWatchStatistics) {
+            FsUserWatchStatistics count = watchStatisticsMap.getOrDefault(courseWatchStatistic.getPeriodId(), courseWatchStatistic);
+            // 单独set,不用copy,避免copy出来的结果被前面的覆盖
+            courseWatchStatistic.setUserNum(userTotalStatistics.getUserNum() != null ? userTotalStatistics.getUserNum():0);
+            courseWatchStatistic.setNewUserNum(userTotalStatistics.getNewUserNum() != null ? userTotalStatistics.getNewUserNum():0);
+            courseWatchStatistic.setWatchNum(count.getWatchNum() !=null ? count.getWatchNum():0);
+            courseWatchStatistic.setCompleteWatchNum(count.getCompleteWatchNum() != null ? count.getCompleteWatchNum():0);
+            courseWatchStatistic.setCompleteWatchRate(count.getCompleteWatchRate() != null ? count.getCompleteWatchRate(): BigDecimal.ZERO);
+            courseWatchStatistic.setCreateTime(new Date());
+            courseWatchStatistic.setUpdateTime(new Date());
+        }
+
+        //2、分批次插入数据
+        this.batchInsert(courseWatchStatistics);
+    }
+
+    private void batchInsert(List<FsUserWatchStatistics> list) {
+        // 分批次处理,一次提交500条
+        List<List<FsUserWatchStatistics>> batches = Lists.partition(list, 500);
+        batches.forEach(batch -> {
+            SqlSession sqlSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
+            try {
+                FsUserWatchStatisticsMapper mapper = sqlSession.getMapper(FsUserWatchStatisticsMapper.class);
+                batch.forEach(mapper::insertFsUserWatchStatisticsTask);
+                sqlSession.commit();
+            } finally {
+                sqlSession.close();
+            }
+        });
+    }
+}

+ 93 - 0
fs-service-system/src/main/java/com/fs/course/vo/FsUserWatchCourseStatisticsExportVO.java

@@ -0,0 +1,93 @@
+package com.fs.course.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import com.fs.common.annotation.Excel;
+import com.fs.common.core.domain.BaseEntity;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+import java.math.BigDecimal;
+import java.util.Date;
+
+@Data
+@EqualsAndHashCode(callSuper = true)
+public class FsUserWatchCourseStatisticsExportVO extends BaseEntity{
+
+    /** 主键id */
+    private Long id;
+
+    /** 营期id */
+//    @Excel(name = "营期id")
+    private Long periodId;
+
+    /** 营期名称 */
+    @Excel(name = "营期名称")
+    private String periodName;
+
+    /** 营期开始日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "营期日期", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date periodStartingTime;
+
+    /** 课程开始日期 */
+    @JsonFormat(pattern = "yyyy-MM-dd")
+    @Excel(name = "播出时间", width = 30, dateFormat = "yyyy-MM-dd")
+    private Date courseStartDateTime;
+
+    /** 课程id */
+//    @Excel(name = "课程id")
+    private Long courseId;
+
+    /** 课程名称 */
+    @Excel(name = "课程名称")
+    private String courseName;
+
+    /** 视频id */
+//    @Excel(name = "视频id")
+    private Long videoId;
+
+    /** 视频标题 */
+    @Excel(name = "视频小节")
+    private String videoTitle;
+
+    /** 销售公司id */
+//    @Excel(name = "销售公司id")
+    private Long companyId;
+
+    /** 销售公司名称 */
+    @Excel(name = "销售公司")
+    private String companyName;
+
+    /** 销售id */
+//    @Excel(name = "销售id")
+    private Long companyUserId;
+
+    /** 销售名称 */
+    @Excel(name = "所属销售")
+    private String companyUserName;
+
+    /** 新增会员数量 */
+    @Excel(name = "新增会员")
+    private Integer newUserNum;
+
+    /** 会员数量 */
+    @Excel(name = "会员数量")
+    private Integer userNum;
+
+    /** 观看人数 */
+    @Excel(name = "观看人数")
+    private Integer watchNum;
+
+    /** 完播人数 */
+    @Excel(name = "完播人数")
+    private Integer completeWatchNum;
+
+    /** 完播率 */
+    @Excel(name = "完播率")
+    private BigDecimal completeWatchRate;
+
+    /** 红包领取数量 */
+    @Excel(name = "红包领取个数")
+    private Integer redPacketNum;
+
+}

+ 5 - 1
fs-service-system/src/main/java/com/fs/course/vo/newfs/FsUserCourseVideoLinkDetailsVO.java

@@ -29,6 +29,10 @@ public class FsUserCourseVideoLinkDetailsVO {
     private LocalDateTime endDateTime;
     private boolean isRang;
 
-    @ApiModelProperty(value = "视频提示时间")
+    @ApiModelProperty(value = "视频提示时间-第一阶段")
     private Long tipsTime;
+
+    @ApiModelProperty(value = "视频提示时间-第二阶段")
+    private Long tipsTime2;
+
 }

+ 13 - 2
fs-service-system/src/main/java/com/fs/crm/service/impl/CrmCustomerServiceImpl.java

@@ -797,17 +797,28 @@ public class CrmCustomerServiceImpl extends ServiceImpl<CrmCustomerMapper, CrmCu
     }
 
     @Override
+    @Transactional
     public R assistToUser(String opeUserName, Long opeUserId, CrmCustomeAssignParam param) {
         Integer index=0;
         for(CrmCustomerAssignUserDTO userDTO:param.getUsers()){
             CompanyUser companyUser=companyUserMapper.selectCompanyUserById(userDTO.getCompanyUserId());
             for(int i=0;i<userDTO.getCount();i++){
                 CrmCustomer customer=crmCustomerMapper.selectCrmCustomerById(param.getCustomerIds().get(index));
+                //不能选择客户拥有者本人
+                CrmCustomerUser crmCustomerUser = crmCustomerUserMapper.selectCrmCustomerUserById(customer.getCustomerUserId());
+                if (Objects.equals(crmCustomerUser.getCompanyUserId(), userDTO.getCompanyUserId())){
+                    return R.error("不能选择自己来协作!");
+                }
+                //查询协作客户
                 CrmCustomerAssist customerAssist=new CrmCustomerAssist();
-                customerAssist.setCompanyId(param.getCompanyId());
+                customerAssist.setCustomerId(customer.getCustomerId());
                 customerAssist.setCompanyUserId(userDTO.getCompanyUserId());
+                List<CrmCustomerAssist> tempList = assistMapper.selectCrmCustomerAssistList(customerAssist);
+                if(tempList!=null && !tempList.isEmpty()){
+                    return R.error("销售:" + companyUser.getNickName() +"(" + userDTO.getCompanyUserId() + ")已拥有改该协作客户:" + customer.getCustomerName());
+                }
+                customerAssist.setCompanyId(param.getCompanyId());
                 customerAssist.setCompanyUserName(companyUser.getNickName());
-                customerAssist.setCustomerId(customer.getCustomerId());
                 customerAssist.setCreateTime(new Date());
                 assistMapper.insertCrmCustomerAssist(customerAssist);
                 //写日志

+ 1 - 0
fs-service-system/src/main/java/com/fs/store/config/StoreConfig.java

@@ -21,5 +21,6 @@ public class StoreConfig implements Serializable {
     private String refundConsignee;
     private String refundPhoneNumber;
     private String refundAddress;
+    private Integer auditSwitch; // 订单审核开关
 
 }

+ 11 - 0
fs-service-system/src/main/java/com/fs/store/domain/FsStoreAfterSalesItem.java

@@ -24,6 +24,9 @@ public class FsStoreAfterSalesItem extends BaseEntity
     @Excel(name = "商品id")
     private Long productId;
 
+    /** 数量 */
+    private Integer num;
+
     /** 退货东西的详情信息 */
     @Excel(name = "退货东西的详情信息")
     private String jsonInfo;
@@ -75,4 +78,12 @@ public class FsStoreAfterSalesItem extends BaseEntity
     public void setIsDel(Integer isDel) {
         this.isDel = isDel;
     }
+
+    public Integer getNum() {
+        return num;
+    }
+
+    public void setNum(Integer num) {
+        this.num = num;
+    }
 }

+ 58 - 0
fs-service-system/src/main/java/com/fs/store/domain/FsStoreOrderAudit.java

@@ -0,0 +1,58 @@
+package com.fs.store.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@TableName("fs_store_order_audit")
+public class FsStoreOrderAudit {
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    /**
+     * 订单ID
+     */
+    private Long orderId;
+    /**
+     * 公司ID
+     */
+    private Long companyId;
+    /**
+     * 销售ID
+     */
+    private Long companyUserId;
+    /**
+     * 审核状态 0:待销售审核 1:销售审核拒绝 2:待总后台审核 3:总后台审核拒绝 4:审核通过
+     */
+    private Integer auditStatus;
+    /**
+     * 销售审核时间
+     */
+    private LocalDateTime companyAuditTime;
+    /**
+     * 销售审核人ID
+     */
+    private Long companyAuditUserId;
+    /**
+     * 总后台审核时间
+     */
+    private LocalDateTime adminAuditTime;
+    /**
+     * 总后台审核人ID
+     */
+    private Long adminAuditUserId;
+    /**
+     * 拒绝原因
+     */
+    private String reason;
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+}

+ 38 - 0
fs-service-system/src/main/java/com/fs/store/domain/FsStoreOrderAuditLog.java

@@ -0,0 +1,38 @@
+package com.fs.store.domain;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@TableName("fs_store_order_audit_log")
+@Data
+public class FsStoreOrderAuditLog {
+    /**
+     * 主键ID
+     */
+    @TableId(type = IdType.AUTO)
+    private Long id;
+    /**
+     * 审核ID
+     */
+    private Long auditId;
+    /**
+     * 订单ID
+     */
+    private Long orderId;
+    /**
+     * 审核人ID
+     */
+    private Long auditUserId;
+    /**
+     * 备注
+     */
+    private String content;
+    /**
+     * 创建时间
+     */
+    private LocalDateTime createTime;
+}

+ 30 - 0
fs-service-system/src/main/java/com/fs/store/enums/OrderAuditStateEnum.java

@@ -0,0 +1,30 @@
+package com.fs.store.enums;
+
+import lombok.Getter;
+
+import java.util.Arrays;
+
+@Getter
+public enum OrderAuditStateEnum {
+    COMPANY_PENDING(0, "待销售审核"),
+    COMPANY_REJECT(1, "销售审核拒绝"),
+    ADMIN_PENDING(2, "待总后台审核"),
+    ADMIN_REJECT(3, "总后台审核拒绝"),
+    APPROVED(4, "审核通过"),
+    ;
+
+    private final Integer value;
+    private final String desc;
+
+    OrderAuditStateEnum(Integer value, String desc) {
+        this.value = value;
+        this.desc = desc;
+    }
+
+    public static OrderAuditStateEnum getOrderAuditStateEnum(Integer value) {
+        return Arrays.stream(values())
+                .filter(orderAuditStateEnum -> orderAuditStateEnum.getValue().equals(value))
+                .findFirst()
+                .orElse(null);
+    }
+}

+ 7 - 0
fs-service-system/src/main/java/com/fs/store/mapper/FsStoreOrderAuditLogMapper.java

@@ -0,0 +1,7 @@
+package com.fs.store.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.store.domain.FsStoreOrderAuditLog;
+
+public interface FsStoreOrderAuditLogMapper extends BaseMapper<FsStoreOrderAuditLog> {
+}

+ 18 - 0
fs-service-system/src/main/java/com/fs/store/mapper/FsStoreOrderAuditMapper.java

@@ -0,0 +1,18 @@
+package com.fs.store.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.fs.store.domain.FsStoreOrderAudit;
+import com.fs.store.param.FsStoreOrderAuditParam;
+import com.fs.store.vo.FsStoreOrderAuditVO;
+
+import java.util.List;
+
+public interface FsStoreOrderAuditMapper extends BaseMapper<FsStoreOrderAudit> {
+
+    /**
+     * 查询订单审核列表
+     * @param param 参数
+     * @return  list
+     */
+    List<FsStoreOrderAuditVO> selectStoreOrderAuditVOList(FsStoreOrderAuditParam param);
+}

+ 13 - 2
fs-service-system/src/main/java/com/fs/store/mapper/FsStoreOrderMapper.java

@@ -8,18 +8,23 @@ import com.alibaba.fastjson.JSONObject;
 import com.fs.api.param.OrderListParam;
 import com.fs.api.vo.OrderListVO;
 import com.fs.company.param.CompanyStatisticsParam;
-import com.fs.company.vo.CompanySmsLogsStatisticsVO;
 import com.fs.company.vo.CompanyTuiMoneyStatisticsVO;
 import com.fs.statis.domain.Report;
 import com.fs.statis.param.ReportParam;
 import com.fs.store.domain.FsStoreOrder;
 import com.fs.store.domain.FsStoreOrderItem;
-import com.fs.store.param.*;
+import com.fs.store.param.FsCustomerStoreOrderListQueryParam;
+import com.fs.store.param.FsMyStoreOrderQueryParam;
+import com.fs.store.param.FsStoreOrderParam;
+import com.fs.store.param.FsStoreStatisticsParam;
 import com.fs.store.vo.*;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
 
+import java.util.List;
+import java.util.Map;
+
 /**
  * 订单Mapper接口
  *
@@ -1032,5 +1037,11 @@ public interface FsStoreOrderMapper
     @Select("select id from fs_store_order WHERE `status`= 1  and  extend_order_id is null ")
     List<Long> selectFsStoreOrderNoCreateOms();
 
+    @Select("select * from fs_store_order where status = 1 and extend_order_id is not null and delivery_id is null ")
+    List<FsStoreOrder> selectUpdateExpress();
+
+    @Select("select fso.id from fs_store_order fso inner join fs_store_order_audit fsoa on fsoa.order_id = fso.id where fso.`status`= 1 and fso.extend_order_id is null and fsoa.audit_status = 4")
+    List<Long> selectFsStoreOrderNoCreateOmsAndReviewed();
+
     List<Report> selectOrderByCustomerIds(@Param("map") ReportParam param);
 }

+ 17 - 0
fs-service-system/src/main/java/com/fs/store/mapper/FsUserMapper.java

@@ -1,5 +1,7 @@
 package com.fs.store.mapper;
 
+import com.fs.course.domain.FsUserWatchCourseStatistics;
+import com.fs.course.domain.FsUserWatchStatistics;
 import com.fs.course.vo.newfs.FsCourseAnalysisCountVO;
 import com.fs.his.vo.OptionsVO;
 import com.fs.qw.param.QwFsUserParam;
@@ -298,4 +300,19 @@ public interface FsUserMapper
      * @return
      */
     Integer selectFsUserByUserIds(@Param("userIds") String[] userIds, @Param("companyUserId") Long companyUserId);
+
+    /**
+     * 获取会员总数和新增总数-可以按营期/课程
+     * @return
+     */
+    List<FsUserWatchStatistics> selectFsUserTotal();
+
+    List<FsUserWatchCourseStatistics> selectWatchLogCount();
+
+    List<FsUserWatchCourseStatistics> selectRedPacketLogCount();
+
+    List<FsUserWatchCourseStatistics> selectAnswerLogCount();
+
+    List<FsUserWatchCourseStatistics> selectFsUserDetail();
+
 }

+ 19 - 0
fs-service-system/src/main/java/com/fs/store/param/FsStoreOrderAuditParam.java

@@ -0,0 +1,19 @@
+package com.fs.store.param;
+
+import lombok.Data;
+
+@Data
+public class FsStoreOrderAuditParam {
+    /**
+     * 公司ID
+     */
+    private Long companyId;
+    /**
+     * 销售名称
+     */
+    private String companyUserName;
+    /**
+     * 审核状态
+     */
+    private Integer auditStatus;
+}

+ 23 - 0
fs-service-system/src/main/java/com/fs/store/param/FsStoreOrderAuditReviewParam.java

@@ -0,0 +1,23 @@
+package com.fs.store.param;
+
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+public class FsStoreOrderAuditReviewParam {
+    /**
+     * 审核记录ID
+     */
+    @NotNull(message = "记录ID不能为空")
+    private Long auditId;
+    /**
+     * 审核类型 0:审核拒绝 1:审核通过
+     */
+    @NotNull(message = "审核类型不能为空")
+    private Integer reviewType;
+    /**
+     * 审核拒绝原因
+     */
+    private String reviewContent;
+}

+ 4 - 1
fs-service-system/src/main/java/com/fs/store/param/FsStoreOrderCreateParam.java

@@ -35,7 +35,7 @@ public class FsStoreOrderCreateParam implements Serializable
 
     @ApiModelProperty(value = "使用积分 1使用")
     private Integer useIntegral;
-    private BigDecimal payPrice;
+    private BigDecimal payPrice; //制单 改价 订单总价
     private Long paymentId;
 
     @ApiModelProperty(value = "优惠券")
@@ -48,4 +48,7 @@ public class FsStoreOrderCreateParam implements Serializable
     //订单创建类型 1普通订单 2套餐订单 3制单
     private Integer orderCreateType;
 
+    private Long customerId;
+
+    private BigDecimal amount; //货到付款代收金额
 }

+ 3 - 2
fs-service-system/src/main/java/com/fs/store/param/FsStoreOrderCreateUserParam.java

@@ -27,6 +27,7 @@ public class FsStoreOrderCreateUserParam implements Serializable
     @NotNull(message = "付款方式不能为空")
     private Integer payType;
     private Long paymentId;
-    private BigDecimal payPrice;
-
+    private BigDecimal payPrice; //改价 订单总价
+    private Long customerId;
+    private BigDecimal amount; //货到付款代收金额
 }

+ 17 - 0
fs-service-system/src/main/java/com/fs/store/service/IFsStoreOrderAuditLogService.java

@@ -0,0 +1,17 @@
+package com.fs.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.store.domain.FsStoreOrderAuditLog;
+import com.fs.store.vo.FsStoreOrderAuditLogVO;
+
+import java.util.List;
+
+public interface IFsStoreOrderAuditLogService extends IService<FsStoreOrderAuditLog> {
+
+    /**
+     * 根据订单ID查询审批记录列表
+     * @param orderId 订单ID
+     * @return list
+     */
+    List<FsStoreOrderAuditLogVO> selectStoreOrderAuditLogVOByOrderId(Long orderId);
+}

+ 27 - 0
fs-service-system/src/main/java/com/fs/store/service/IFsStoreOrderAuditService.java

@@ -0,0 +1,27 @@
+package com.fs.store.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.fs.store.domain.FsStoreOrderAudit;
+import com.fs.store.param.FsStoreOrderAuditParam;
+import com.fs.store.param.FsStoreOrderAuditReviewParam;
+import com.fs.store.vo.FsStoreOrderAuditVO;
+
+import java.util.List;
+
+public interface IFsStoreOrderAuditService extends IService<FsStoreOrderAudit> {
+    /**
+     * 查询订单审核列表
+     * @param param 参数
+     * @return  list
+     */
+    List<FsStoreOrderAuditVO> selectStoreOrderAuditVOList(FsStoreOrderAuditParam param);
+
+    /**
+     * 审核
+     *
+     * @param param   参数
+     * @param userId  用户ID
+     * @param isAdmin 是否管理端
+     */
+    void audit(FsStoreOrderAuditReviewParam param, Long userId, boolean isAdmin);
+}

+ 35 - 29
fs-service-system/src/main/java/com/fs/store/service/impl/FsStoreAfterSalesServiceImpl.java

@@ -1,13 +1,5 @@
 package com.fs.store.service.impl;
 
-import java.lang.reflect.Field;
-import java.math.BigDecimal;
-import java.sql.Timestamp;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.time.LocalDateTime;
-import java.util.*;
-
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.json.JSONUtil;
@@ -26,26 +18,21 @@ import com.fs.erp.service.IErpOrderService;
 import com.fs.huifuPay.domain.HuiFuRefundResult;
 import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayRefundRequest;
 import com.fs.huifuPay.service.HuiFuService;
-import com.fs.pay.pay.domain.QueryRefundResult;
-import com.fs.pay.pay.domain.RefundResult;
-import com.fs.pay.pay.dto.RefundOrderDTO;
 import com.fs.pay.pay.service.PayService;
-import com.fs.pay.pay.service.impl.PayApiServiceImpl;
-import com.fs.pay.service.impl.PayServiceImpl;
+import com.fs.pay.service.IPayService;
 import com.fs.store.config.StoreConfig;
 import com.fs.store.domain.*;
 import com.fs.store.dto.StoreOrderProductDTO;
 import com.fs.store.enums.AfterSalesStatusEnum;
 import com.fs.store.enums.AfterStatusEnum;
 import com.fs.store.enums.OrderInfoEnum;
+import com.fs.store.mapper.FsStoreAfterSalesMapper;
 import com.fs.store.mapper.FsStoreOrderItemMapper;
 import com.fs.store.param.*;
 import com.fs.store.service.*;
 import com.fs.store.vo.FsStoreAfterSalesQueryVO;
 import com.fs.store.vo.FsStoreAfterSalesVO;
 import com.fs.store.vo.FsStoreOrderItemVO;
-import com.fs.pay.service.IPayService;
-import com.fs.pay.service.dto.RefundDTO;
 import com.fs.system.service.ISysConfigService;
 import com.fs.wx.pay.config.WxPayProperties;
 import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
@@ -55,16 +42,22 @@ import com.github.binarywang.wxpay.bean.result.WxPayRefundResult;
 import com.github.binarywang.wxpay.config.WxPayConfig;
 import com.github.binarywang.wxpay.exception.WxPayException;
 import com.github.binarywang.wxpay.service.WxPayService;
-import com.hc.openapi.tool.util.Ids;
 import lombok.Synchronized;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
-import com.fs.store.mapper.FsStoreAfterSalesMapper;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.transaction.interceptor.TransactionAspectSupport;
 
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.util.*;
+
 /**
  * 售后记录Service业务层处理
  *
@@ -282,6 +275,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService
             FsStoreAfterSalesItem storeAfterSalesItem = new FsStoreAfterSalesItem();
             storeAfterSalesItem.setStoreAfterSalesId(storeAfterSales.getId());
             storeAfterSalesItem.setProductId(item.getProductId());
+            storeAfterSalesItem.setNum(productParam.getNum());
             storeAfterSalesItem.setJsonInfo(item.getJsonInfo());
             storeAfterSalesItem.setIsDel(0);
             afterSalesItemService.insertFsStoreAfterSalesItem(storeAfterSalesItem);
@@ -529,13 +523,9 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService
 //
 //        return R.ok("操作成功");
 //    }
-
+    @Transactional(rollbackFor = Exception.class)
     @Override
-    public synchronized R refundMoney(FsStoreAfterSalesRefundParam param) {
-       return refundStoreMoney(param);
-    }
-    @Transactional
-    R refundStoreMoney(FsStoreAfterSalesRefundParam param){
+    public R refundMoney(FsStoreAfterSalesRefundParam param) {
         FsStoreAfterSales storeAfterSales = fsStoreAfterSalesMapper.selectFsStoreAfterSalesById(param.getSalesId());
         if (storeAfterSales == null) {
             throw new CustomException("未查询到售后订单信息");
@@ -555,6 +545,14 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService
         order.setRefundStatus(OrderInfoEnum.REFUND_STATUS_2.getValue());
         orderService.updateFsStoreOrder(order);
 
+        BigDecimal refundAmount = param.getRefundAmount();
+        if (Objects.isNull(refundAmount)) {
+            throw new CustomException("退款金额不能为空");
+        }
+        if (order.getPayMoney().compareTo(refundAmount) < 0) {
+            throw new CustomException("退款金额不能大于支付金额");
+        }
+
         FsStoreAfterSalesStatus storeAfterSalesStatus = new FsStoreAfterSalesStatus();
         storeAfterSalesStatus.setStoreAfterSalesId(storeAfterSales.getId());
         storeAfterSalesStatus.setChangeType(4);
@@ -565,12 +563,20 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService
         //退库存
         //获取订单下的商品
         List<FsStoreOrderItemVO> orderItemVOS=fsStoreOrderItemMapper.selectFsStoreOrderItemListByOrderId(order.getId());
-        for(FsStoreOrderItemVO vo:orderItemVOS){
-            if(vo.getIsAfterSales()==1){
-                productService.incProductStock(vo.getNum(), vo.getProductId()
-                        , vo.getProductAttrValueId());
+
+        // 获取售后商品
+        FsStoreAfterSalesItem params = new FsStoreAfterSalesItem();
+        params.setStoreAfterSalesId(storeAfterSales.getId());
+        List<FsStoreAfterSalesItem> fsStoreAfterSalesItems = afterSalesItemService.selectFsStoreAfterSalesItemList(params);
+
+        // 退库存
+        for (FsStoreAfterSalesItem item : fsStoreAfterSalesItems) {
+            FsStoreOrderItemVO itemVO = orderItemVOS.stream().filter(i -> i.getProductId().equals(item.getProductId())).findFirst().orElse(null);
+            if(Objects.nonNull(itemVO) && itemVO.getIsAfterSales() == 1 && Objects.nonNull(item.getNum())){
+                productService.incProductStock(item.getNum().longValue(), item.getProductId(), null);
             }
         }
+
         //将钱退还给用户
         if(order.getPayMoney().compareTo(BigDecimal.ZERO)==1){
             List<FsStorePayment> payments=paymentService.selectFsStorePaymentByOrderId(order.getId());
@@ -591,7 +597,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService
                         refundRequest.setOutTradeNo("store-"+payment.getPayCode());
                         refundRequest.setOutRefundNo("store-"+payment.getPayCode());
                         refundRequest.setTotalFee(WxPayUnifiedOrderRequest.yuanToFen(payment.getPayMoney().toString()));
-                        refundRequest.setRefundFee(WxPayUnifiedOrderRequest.yuanToFen(payment.getPayMoney().toString()));
+                        refundRequest.setRefundFee(WxPayUnifiedOrderRequest.yuanToFen(refundAmount.toString()));
                         try {
                             WxPayRefundResult refundResult = wxPayService.refund(refundRequest);
                             WxPayRefundQueryResult refundQueryResult = wxPayService.refundQuery("", refundResult.getOutTradeNo(), refundResult.getOutRefundNo(), refundResult.getRefundId());
@@ -611,7 +617,7 @@ public class FsStoreAfterSalesServiceImpl implements IFsStoreAfterSalesService
                         }
                     }else if (payment.getPayMode()!=null&&payment.getPayMode().equals("hf")){
                         V2TradePaymentScanpayRefundRequest request = new V2TradePaymentScanpayRefundRequest();
-                        request.setOrdAmt(payment.getPayMoney().toString());
+                        request.setOrdAmt(refundAmount.toString());
                         request.setOrgReqDate(new SimpleDateFormat("yyyyMMdd").format(payment.getCreateTime()));
                         request.setReqSeqId("refund-"+payment.getPayCode());
                         Map<String, Object> extendInfoMap = new HashMap<>();

+ 37 - 0
fs-service-system/src/main/java/com/fs/store/service/impl/FsStoreOrderAuditLogServiceImpl.java

@@ -0,0 +1,37 @@
+package com.fs.store.service.impl;
+
+import com.baomidou.mybatisplus.core.conditions.Wrapper;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.store.domain.FsStoreOrderAuditLog;
+import com.fs.store.mapper.FsStoreOrderAuditLogMapper;
+import com.fs.store.service.IFsStoreOrderAuditLogService;
+import com.fs.store.vo.FsStoreOrderAuditLogVO;
+import org.springframework.stereotype.Service;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+public class FsStoreOrderAuditLogServiceImpl extends ServiceImpl<FsStoreOrderAuditLogMapper, FsStoreOrderAuditLog> implements IFsStoreOrderAuditLogService {
+
+
+    /**
+     * 根据订单ID查询审批记录列表
+     * @param orderId 订单ID
+     * @return list
+     */
+    @Override
+    public List<FsStoreOrderAuditLogVO> selectStoreOrderAuditLogVOByOrderId(Long orderId) {
+        Wrapper<FsStoreOrderAuditLog> wrapper = Wrappers.<FsStoreOrderAuditLog>lambdaQuery()
+                .eq(FsStoreOrderAuditLog::getOrderId, orderId)
+                .orderByAsc(FsStoreOrderAuditLog::getCreateTime);
+        return baseMapper.selectList(wrapper).stream().map(l -> {
+            FsStoreOrderAuditLogVO vo = new FsStoreOrderAuditLogVO();
+            vo.setId(l.getId());
+            vo.setCreateTime(l.getCreateTime());
+            vo.setContent(l.getContent());
+            return vo;
+        }).collect(Collectors.toList());
+    }
+}

+ 94 - 0
fs-service-system/src/main/java/com/fs/store/service/impl/FsStoreOrderAuditServiceImpl.java

@@ -0,0 +1,94 @@
+package com.fs.store.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.fs.common.exception.CustomException;
+import com.fs.common.utils.StringUtils;
+import com.fs.store.domain.FsStoreOrderAudit;
+import com.fs.store.domain.FsStoreOrderAuditLog;
+import com.fs.store.enums.OrderAuditStateEnum;
+import com.fs.store.mapper.FsStoreOrderAuditMapper;
+import com.fs.store.param.FsStoreOrderAuditParam;
+import com.fs.store.param.FsStoreOrderAuditReviewParam;
+import com.fs.store.service.IFsStoreOrderAuditLogService;
+import com.fs.store.service.IFsStoreOrderAuditService;
+import com.fs.store.vo.FsStoreOrderAuditVO;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Objects;
+
+@Service
+@AllArgsConstructor
+public class FsStoreOrderAuditServiceImpl extends ServiceImpl<FsStoreOrderAuditMapper, FsStoreOrderAudit> implements IFsStoreOrderAuditService {
+
+    private final IFsStoreOrderAuditLogService fsStoreOrderAuditLogService;
+
+    /**
+     * 查询订单审核列表
+     * @param param 参数
+     * @return  list
+     */
+    @Override
+    public List<FsStoreOrderAuditVO> selectStoreOrderAuditVOList(FsStoreOrderAuditParam param) {
+        return baseMapper.selectStoreOrderAuditVOList(param);
+    }
+
+    /**
+     * 审核
+     *
+     * @param param   参数
+     * @param userId  用户ID
+     * @param isAdmin 是否管理端
+     */
+    @Transactional(rollbackFor = Exception.class)
+    @Override
+    public void audit(FsStoreOrderAuditReviewParam param, Long userId, boolean isAdmin) {
+        FsStoreOrderAudit fsStoreOrderAudit = baseMapper.selectById(param.getAuditId());
+        if (Objects.isNull(fsStoreOrderAudit)) {
+            throw new CustomException("记录不存在");
+        }
+        OrderAuditStateEnum orderAuditStateEnum = OrderAuditStateEnum.getOrderAuditStateEnum(fsStoreOrderAudit.getAuditStatus());
+        if (isAdmin && OrderAuditStateEnum.ADMIN_PENDING != orderAuditStateEnum) {
+            throw new CustomException("请勿重复审核");
+        }
+        if (!isAdmin && OrderAuditStateEnum.COMPANY_PENDING != orderAuditStateEnum) {
+            throw new CustomException("请勿重复审核");
+        }
+
+        LocalDateTime now = LocalDateTime.now();
+        OrderAuditStateEnum newStatus;
+        String message;
+        if (param.getReviewType() == 1) { // 审核通过
+            newStatus = isAdmin ? OrderAuditStateEnum.APPROVED : OrderAuditStateEnum.ADMIN_PENDING;
+            message = isAdmin ? "总后台:审核通过" : "销售:审核通过";
+        } else { // 审核拒绝
+            newStatus = isAdmin ? OrderAuditStateEnum.ADMIN_REJECT : OrderAuditStateEnum.COMPANY_REJECT;
+            message = isAdmin ? "总后台:审核拒绝" : "销售:审核拒绝";
+            if (StringUtils.isBlank(param.getReviewContent())) {
+                throw new CustomException("被拒原因不能为空");
+            }
+            fsStoreOrderAudit.setReason(param.getReviewContent());
+        }
+
+        if (isAdmin) {
+            fsStoreOrderAudit.setAdminAuditTime(now);
+            fsStoreOrderAudit.setAdminAuditUserId(userId);
+        } else {
+            fsStoreOrderAudit.setCompanyAuditTime(now);
+            fsStoreOrderAudit.setCompanyAuditUserId(userId);
+        }
+        fsStoreOrderAudit.setAuditStatus(newStatus.getValue());
+
+        baseMapper.updateById(fsStoreOrderAudit);
+
+        FsStoreOrderAuditLog auditLog = new FsStoreOrderAuditLog();
+        auditLog.setAuditId(fsStoreOrderAudit.getId());
+        auditLog.setOrderId(fsStoreOrderAudit.getOrderId());
+        auditLog.setContent(message);
+        auditLog.setCreateTime(LocalDateTime.now());
+        fsStoreOrderAuditLogService.save(auditLog);
+    }
+}

+ 141 - 151
fs-service-system/src/main/java/com/fs/store/service/impl/FsStoreOrderServiceImpl.java

@@ -1,15 +1,5 @@
 package com.fs.store.service.impl;
 
-import java.lang.reflect.Field;
-import java.math.BigDecimal;
-import java.nio.charset.Charset;
-import java.sql.Timestamp;
-import java.text.ParseException;
-import java.text.SimpleDateFormat;
-import java.util.*;
-import java.util.concurrent.TimeUnit;
-import java.util.stream.Collectors;
-
 import cn.hutool.core.net.URLDecoder;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.core.util.NumberUtil;
@@ -41,6 +31,7 @@ import com.fs.company.service.ICompanyService;
 import com.fs.company.service.ICompanyUserService;
 import com.fs.erp.domain.*;
 import com.fs.erp.dto.*;
+import com.fs.erp.mapper.FsErpFinishPushMapper;
 import com.fs.erp.service.IErpGoodsService;
 import com.fs.erp.service.IErpOrderService;
 import com.fs.his.config.FsErpConfig;
@@ -50,6 +41,7 @@ import com.fs.huifuPay.sdk.opps.core.request.V2TradePaymentScanpayRefundRequest;
 import com.fs.huifuPay.service.HuiFuService;
 import com.fs.pay.pay.domain.RefundResult;
 import com.fs.pay.pay.service.PayService;
+import com.fs.pay.service.IPayService;
 import com.fs.store.config.StoreConfig;
 import com.fs.store.config.StoreIntegralConfig;
 import com.fs.store.constants.StoreConstants;
@@ -58,11 +50,11 @@ import com.fs.store.dto.*;
 import com.fs.store.enums.*;
 import com.fs.store.mapper.FsStoreCartMapper;
 import com.fs.store.mapper.FsStoreOrderItemMapper;
+import com.fs.store.mapper.FsStoreOrderMapper;
 import com.fs.store.mapper.FsUserAddressMapper;
 import com.fs.store.param.*;
 import com.fs.store.service.*;
 import com.fs.store.vo.*;
-import com.fs.pay.service.IPayService;
 import com.fs.system.service.ISysConfigService;
 import com.fs.wx.pay.config.WxPayProperties;
 import com.github.binarywang.wxpay.bean.request.WxPayRefundRequest;
@@ -80,15 +72,24 @@ import org.springframework.aop.framework.AopContext;
 import org.springframework.beans.BeanUtils;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
-import org.springframework.beans.factory.annotation.Value;
 import org.springframework.context.ApplicationEventPublisher;
 import org.springframework.context.annotation.EnableAspectJAutoProxy;
 import org.springframework.stereotype.Service;
-import com.fs.store.mapper.FsStoreOrderMapper;
 import org.springframework.transaction.annotation.Propagation;
 import org.springframework.transaction.annotation.Transactional;
 import org.springframework.transaction.interceptor.TransactionAspectSupport;
 
+import java.lang.reflect.Field;
+import java.math.BigDecimal;
+import java.nio.charset.Charset;
+import java.sql.Timestamp;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.concurrent.TimeUnit;
+import java.util.stream.Collectors;
+
 import static com.fs.store.constants.StoreConstants.DELIVERY;
 
 /**
@@ -196,6 +197,9 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
     @Autowired
     private WxPayProperties wxPayProperties;
 
+    @Autowired
+    private FsErpFinishPushMapper fsErpFinishPushMapper;
+
     @Autowired
     @Qualifier("erpOrderServiceImpl")
     private IErpOrderService gyOrderService;
@@ -207,6 +211,12 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
     @Autowired
     @Qualifier("k9OrderServiceImpl")
     private IErpOrderService k9OrderService;
+
+    @Autowired
+    private IFsStoreOrderAuditService orderAuditService;
+    @Autowired
+    private IFsStoreOrderAuditLogService orderAuditLogService;
+
     /**
      * 查询订单
      *
@@ -552,21 +562,34 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
                 storeOrder.setServiceFee(config.getServiceFee());
             }
 
+            //后台制单处理
+            if(param.getPayPrice()!=null&&param.getPayPrice().compareTo(BigDecimal.ZERO)>0){
+                if (param.getPayPrice().compareTo(dto.getTotalPrice()) > 0) {
+                    return R.error("改价价格不能大于商品总价");
+                }
+                storeOrder.setPayPrice(param.getPayPrice());
+            }
+            else{
+                storeOrder.setPayPrice(dto.getPayPrice());
+            }
 
+            //付款方式
             if(param.getPayType().equals("1")){
                 //全款支付
                 storeOrder.setStatus(0);
             }
             else if(param.getPayType().equals("2")){
-                //货到付款
+                //物流代收
                 storeOrder.setStatus(1);
             }
-            //后台制单处理
-            if(param.getPayPrice()!=null&&param.getPayPrice().compareTo(BigDecimal.ZERO)>0){
-                storeOrder.setPayPrice(param.getPayPrice());
-            }
-            else{
-                storeOrder.setPayPrice(dto.getPayPrice());
+            else if(param.getPayType().equals("3")){
+                //货到付款
+                storeOrder.setStatus(1);
+                BigDecimal amount = param.getAmount();  //货到付款 自定义代收金额
+                if (amount != null && amount.compareTo(BigDecimal.ZERO) > 0){
+                    storeOrder.setPayMoney(amount);
+                    storeOrder.setPayDelivery(storeOrder.getPayPrice().subtract(amount) );
+                }
             }
             storeOrder.setOrderCreateType(param.getOrderCreateType());
             Long prescribe = carts.stream().filter(item -> item.getProductType()!=null&&item.getProductType()==2).count();
@@ -575,6 +598,9 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
             } else {
                 storeOrder.setIsPrescribe(0);
             }
+            if (storeOrder.getCustomerId() == null){
+                storeOrder.setCustomerId(param.getCustomerId());//6.13 添加客户id
+            }
             Integer flag=fsStoreOrderMapper.insertFsStoreOrder(storeOrder);
             if (flag==0) {
                 return R.error("订单创建失败");
@@ -651,7 +677,9 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
             //添加支付到期时间
             Calendar calendar = Calendar.getInstance();
             calendar.setTime(storeOrder.getCreateTime());
-            calendar.add(Calendar.MINUTE,config.getUnPayTime());
+            if (config.getUnPayTime() != null){
+                calendar.add(Calendar.MINUTE,config.getUnPayTime());
+            }
             SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
             String payLimitTime = format.format(calendar.getTime() );
             //删除推荐订单KEY
@@ -977,6 +1005,8 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
             createParam.setCompanyUserId(param.getCompanyUserId());
             createParam.setPaymentId(param.getPaymentId());
             createParam.setPayPrice(param.getPayPrice());
+            createParam.setCustomerId(param.getCustomerId());
+            createParam.setAmount(param.getAmount());
             return this.createOrder(param.getUserId(),createParam);
         }
         else{
@@ -1245,6 +1275,18 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
                 }
             }
 
+
+//            // 同步订单完成状态到erp
+//            // 如果是线上支付
+//            if("1".equals(order.getPayType())){
+//                FsErpFinishPush fsErpFinishPush = new FsErpFinishPush();
+//                fsErpFinishPush.setOrderId(orderId);
+//                fsErpFinishPush.setTaskStatus(0);
+//                fsErpFinishPush.setRetryCount(0);
+//                fsErpFinishPush.setCreateTime(new Date());
+//                fsErpFinishPushMapper.insert(fsErpFinishPush);
+//            }
+
             //模板消息支付成功发布事件
             TemplateBean templateBean = TemplateBean.builder()
                     .orderId(order.getId().toString())
@@ -1323,6 +1365,12 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
             storeOrder.setStatus(OrderInfoEnum.STATUS_1.getValue());
             storeOrder.setPayTime(new Date());
             fsStoreOrderMapper.updateFsStoreOrder(storeOrder);
+            // 添加订单审核
+            addOrderAudit(order);
+            return "SUCCESS";
+
+
+
             //非处方直接提交OMS
 //            if(order.getIsPrescribe().equals(0)){
 //                createOmsOrder(order.getId());
@@ -1341,9 +1389,46 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
 //            TransactionAspectSupport.currentTransactionStatus().setRollbackOnly();
 //            return "";
 //        }
-        return "SUCCESS";
     }
 
+    /**
+     * 添加审核
+     * @param order 订单信息
+     */
+    private void addOrderAudit(FsStoreOrder order) {
+        if (!getAuditSwitch()) {
+            return;
+        }
+
+        FsStoreOrderAudit orderAudit = new FsStoreOrderAudit();
+        orderAudit.setOrderId(order.getId());
+        orderAudit.setCompanyId(order.getCompanyId());
+        orderAudit.setCompanyUserId(order.getCompanyUserId());
+        orderAudit.setAuditStatus(OrderAuditStateEnum.COMPANY_PENDING.getValue());
+        orderAudit.setCreateTime(LocalDateTime.now());
+        orderAuditService.save(orderAudit);
+
+        FsStoreOrderAuditLog auditLog = new FsStoreOrderAuditLog();
+        auditLog.setAuditId(orderAudit.getId());
+        auditLog.setOrderId(order.getId());
+        auditLog.setContent("系统:提交审核");
+        auditLog.setCreateTime(LocalDateTime.now());
+        orderAuditLogService.save(auditLog);
+    }
+
+    /**
+     * 获取是否需要订单审核
+     * @return boolean
+     */
+    private boolean getAuditSwitch() {
+        try {
+            String json = configService.selectConfigByKey("store.config");
+            StoreConfig config = JSONUtil.toBean(json,StoreConfig.class);
+            return config.getAuditSwitch() == 1;
+        } catch (Exception e) {
+            return false;
+        }
+    }
 
     public  boolean containsAddress(String companyName) {
         String[] items= {"新疆","西藏","内蒙古","海南"};
@@ -1395,18 +1480,9 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
             erpOrder.setVip_code(order.getUserId().toString());
         }
         erpOrder.setPlatform_code(order.getOrderCode());
-//        if(order.getStoreHouseCode()==null){
-//            erpOrder.setWarehouse_code("CQDS001");
-//        }
-//        else{
-            erpOrder.setWarehouse_code(erpConfig.getErpWarehouseCode());
-//        }
-//        if(order.getStoreHouseCode().equals("YDSP001")){
-//            erpOrder.setShop_code("RunDayWuHan");
-//        }
-//        else{
-            erpOrder.setShop_code(erpConfig.getErpShopCode());
-//        }
+        erpOrder.setWarehouse_code(erpConfig.getErpWarehouseCode());
+        erpOrder.setShop_code(erpConfig.getErpShopCode());
+
 //      erpOrder.setPost_fee(order.getTotalPostage().doubleValue());
         erpOrder.setSeller_memo(order.getMark());
         // order.setCurrency_code("JCZD");
@@ -1423,114 +1499,19 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
             payment.setPaytime(new Timestamp(timeLong));
         }
         payments.add(payment);
-        //1.新疆、西藏、内蒙古、海南的订单(代收+非代收)全部发EMS
-        //2.全款订单选择中通,快递ID 452  ZTO 中通商城标快 ZTBK.17782167502
-        //3.新疆  西藏 内蒙古 海南  全款订单走顺丰丰云配  【非全款】的就根据公司配置那来 配的哪个走哪个快递
-        //4.把现在发中通的规则改成发顺丰风云配 SF.0235402855
-        //5.全款支付且应付金额小于等于100元,快递方式默认中通小件455 ZTO 中通小件 ZTPDD
-        if(order.getCompanyId()!=null&& order.getStoreHouseCode()!=null&& order.getStoreHouseCode().equals("WHSP001")){
-            //杭州家有仙妻实业有限公司 全部订单快递方式设置为商城德邦快递
-            FsExpress express=expressService.selectFsExpressByOmsCode("SF-WHC");
+        if(order.getCompanyId()!=null){
+
+            FsExpress  express=expressService.selectFsExpressByOmsCode("SF");
             erpOrder.setExpress_code(express.getOmsCode());
             order.setDeliveryName(express.getName());
             order.setDeliverySn(express.getCode());
-            if(order.getPayDelivery().compareTo(new BigDecimal(0))==0){
 
-            }
-            else{
-                //物流代收金额
-                ErpOrderPayment codPayment=new ErpOrderPayment();
-                codPayment.setPay_type_code("cod");
-                codPayment.setPayment(order.getPayDelivery().doubleValue());
-                payments.add(codPayment);
-                erpOrder.setCod(true);
-            }
-        }
-        else if(order.getCompanyId()!=null){
-            Company company = companyService.selectCompanyById(order.getCompanyId());
-            if (order.getPayDelivery().compareTo(new BigDecimal(0)) == 0) {  //没有物流代收(全款订单)
-                FsExpress express = new FsExpress();
-                //express=expressService.selectFsExpressByOmsCode("SF.0235402855");
-                if(order.getPayPrice().compareTo(new BigDecimal(100))<=0){
-                    express=expressService.selectFsExpressByOmsCode("ZTPDD");
-                }
-                else{
-                    boolean found = containsAddress(order.getUserAddress());
-                    if(found){
-                        express=expressService.selectFsExpressByOmsCode("SF.0235402855");
-                    }
-                    else{
-                        express=expressService.selectFsExpressByOmsCode("SF.0235402855");
-                    }
-                }
-                erpOrder.setExpress_code(express.getOmsCode());
-                order.setDeliveryName(express.getName());
-                order.setDeliverySn(express.getCode());
-            }
-            else {
-                FsExpress express = new FsExpress();
-                boolean found = containsAddress(order.getUserAddress());
-                if(found){
-                    if(StringUtils.isNotEmpty(company.getOmsCode())){
-                        express = expressService.selectFsExpressByOmsCode(company.getOmsCode());
-                    }else{
-                        express=expressService.selectFsExpressByOmsCode("EMS.1");
-                    }
-                }
-                else{
-                    if(StringUtils.isNotEmpty(company.getOmsCode())){
-                        express = expressService.selectFsExpressByOmsCode(company.getOmsCode());
-                    }else{
-                        express = expressService.selectFsExpressByOmsCode("SF.0235402855");
-                    }
-                }
-                erpOrder.setExpress_code(express.getOmsCode());
-                order.setDeliveryName(express.getName());
-                order.setDeliverySn(express.getCode());
-                //物流代收金额
-                ErpOrderPayment codPayment = new ErpOrderPayment();
-                codPayment.setPay_type_code("cod");
-                codPayment.setPayment(order.getPayDelivery().doubleValue());
-                payments.add(codPayment);
-                erpOrder.setCod(true);
-            }
         }
         else{
-            if(order.getPayDelivery().compareTo(new BigDecimal(0))==0){  //没有物流代收(全款订单)
-                FsExpress express=new FsExpress();
-                if(order.getPayPrice().compareTo(new BigDecimal(100))<=0){
-                    express=expressService.selectFsExpressByOmsCode("ZTPDD");
-                }else{
-                    boolean found = containsAddress(order.getUserAddress());
-                    if(found){
-                        express=expressService.selectFsExpressByOmsCode("SF.0235402855");
-                    }
-                    else{
-                        express=expressService.selectFsExpressByOmsCode("SF.0235402855");
-                    }
-                }
-                erpOrder.setExpress_code(express.getOmsCode());
-                order.setDeliveryName(express.getName());
-                order.setDeliverySn(express.getCode());
-            }
-            else{
-                ErpOrderPayment codPayment=new ErpOrderPayment();
-                codPayment.setPay_type_code("cod");
-                codPayment.setPayment(order.getPayDelivery().doubleValue());
-                payments.add(codPayment);
-                erpOrder.setCod(true);
-                FsExpress express=new FsExpress();
-                boolean found = containsAddress(order.getUserAddress());
-                if(found){
-                    express=expressService.selectFsExpressByOmsCode("EMS.1");
-                }
-                else {
-                    express=expressService.selectFsExpressByOmsCode("SF.0235402855");
-                }
-                erpOrder.setExpress_code(express.getOmsCode());
-                order.setDeliveryName(express.getName());
-                order.setDeliverySn(express.getCode());
-            }
+            FsExpress express=expressService.selectFsExpressByOmsCode("SF");
+            erpOrder.setExpress_code(express.getOmsCode());
+            order.setDeliveryName(express.getName());
+            order.setDeliverySn(express.getCode());
         }
         erpOrder.setPayments(payments);
         if(order.getCompanyId()!=null){
@@ -1554,13 +1535,9 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
             }
         }
 
-        if(order.getStoreHouseCode().equals("WHSP001")){
-            //武汉不备注快递史
-            //erpOrder.setSeller_memo(erpOrder.getSeller_memo()+"-"+order.getDeliveryName());
-        }
-        else{
-            erpOrder.setSeller_memo(erpOrder.getSeller_memo()+"-"+ order.getDeliveryName());
-        }
+
+        erpOrder.setSeller_memo(erpOrder.getSeller_memo()+"-"+ order.getDeliveryName());
+
         ErpRemarkDTO remarkDTO=new ErpRemarkDTO();
         remarkDTO.setTotalPrice(order.getTotalPrice());
         remarkDTO.setPayPrice(order.getPayPrice());
@@ -1569,11 +1546,6 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
         remarkDTO.setCouponMoney(order.getCouponPrice());
         remarkDTO.setOrderId(order.getOrderCode());
         remarkDTO.setYdMoney(order.getPayPrice().subtract(order.getPayMoney().subtract(order.getPayDelivery())));
-//        //套餐标题也传过去
-//        if (order.getOrderCreateType() == 2){
-//            JSONObject jsonStr = JSONObject.parseObject(order.getPackageJson());
-//            remarkDTO.setPackageTitle(jsonStr.getString("title"));
-//        }
 
         if(order.getPayTime()!=null){
             SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
@@ -2037,9 +2009,9 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
         if(money==null){
             return R.error("请输入价格");
         }
-        if(money.compareTo(BigDecimal.ZERO)<=0){
-            return R.error("价格应大于0");
-        }
+//        if(money.compareTo(BigDecimal.ZERO)<=0){
+//            return R.error("价格应大于0");
+//        }
         String key=redisCache.getCacheObject("createOrderKey:"+createOrderKey);
         if (StringUtils.isEmpty(key)) {
             throw new CustomException("订单已过期",501);
@@ -2150,6 +2122,9 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
         storeOrder.setIsPayRemain(1);
         fsStoreOrderMapper.updateFsStoreOrder(storeOrder);
 
+        // 添加审核
+        addOrderAudit(order);
+
         return "SUCCESS";
     }
 
@@ -2242,6 +2217,21 @@ public class FsStoreOrderServiceImpl implements IFsStoreOrderService
                         }
                         successNum++;
                         importSuccessMsg.append("<br/>" + successNum + "、快递单号 " +dto.getDeliveryId() + " 导入成功");
+
+
+                        // 同步订单完成状态到erp
+                        // 如果是物流代收 或者 货到付款
+//                        if("2".equals(order.getPayType()) || "3".equals(order.getPayType())){
+//                            // 已结算
+//                            if("1".equals(dto.getDeliveryPayStatus())){
+//                                FsErpFinishPush fsErpFinishPush = new FsErpFinishPush();
+//                                fsErpFinishPush.setOrderId(order.getId());
+//                                fsErpFinishPush.setTaskStatus(0);
+//                                fsErpFinishPush.setRetryCount(0);
+//                                fsErpFinishPush.setCreateTime(new Date());
+//                                fsErpFinishPushMapper.insert(fsErpFinishPush);
+//                            }
+//                        }
                     }
                     else{
                         String msg = "<br/>" + failureNum + "、快递单号 " + dto.getDeliveryId() + " 未签收或已导入,不能导入";

+ 7 - 7
fs-service-system/src/main/java/com/fs/store/service/impl/FsUserServiceImpl.java

@@ -851,13 +851,13 @@ public class FsUserServiceImpl implements IFsUserService
             return ResponseResult.fail(405,"销售不存在");
         }
 
-        // 判断是否绑定了销售
-//        FsUserCompanyUser fsUserCompanyUser = getFsUserCompanyUser(param, fsUser);
-//        QueryWrapper<FsUserCompanyUser> queryWrapper = new QueryWrapper<FsUserCompanyUser>().eq("user_id", param.getUserId()).eq("company_user_id", param.getCompanyUserId());
-//        Integer i = fsUserCompanyUserMapper.selectCount(queryWrapper);
-//        if(i == 0) {
-//            fsUserCompanyUserMapper.insertFsUserCompanyUser(fsUserCompanyUser);
-//        }
+        // 添加关系表数据
+        FsUserCompanyUser fsUserCompanyUser = getFsUserCompanyUser(param, fsUser);
+        QueryWrapper<FsUserCompanyUser> queryWrapper = new QueryWrapper<FsUserCompanyUser>().eq("user_id", param.getUserId()).eq("company_user_id", param.getCompanyUserId());
+        Integer i = fsUserCompanyUserMapper.selectCount(queryWrapper);
+        if(i == 0) {
+            fsUserCompanyUserMapper.insertFsUserCompanyUser(fsUserCompanyUser);
+        }
 
         // 关联销售
         if(fsUser.getCompanyUserId() == null) {

+ 23 - 0
fs-service-system/src/main/java/com/fs/store/vo/FsStoreOrderAuditLogVO.java

@@ -0,0 +1,23 @@
+package com.fs.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class FsStoreOrderAuditLogVO {
+    /**
+     * 主键ID
+     */
+    private Long id;
+    /**
+     * 备注
+     */
+    private String content;
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+}

+ 77 - 0
fs-service-system/src/main/java/com/fs/store/vo/FsStoreOrderAuditVO.java

@@ -0,0 +1,77 @@
+package com.fs.store.vo;
+
+import com.fasterxml.jackson.annotation.JsonFormat;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class FsStoreOrderAuditVO {
+    /**
+     * 主键ID
+     */
+    private Long id;
+    /**
+     * 订单ID
+     */
+    private Long orderId;
+    /**
+     * 订单编码
+     */
+    private String orderCode;
+    /**
+     * 归属公司ID
+     */
+    private Long companyId;
+    /**
+     * 归属公司名称
+     */
+    private String companyName;
+    /**
+     * 归属销售ID
+     */
+    private Long companyUserId;
+    /**
+     * 归属销售名称
+     */
+    private String companyUserName;
+    /**
+     * 审核状态
+     */
+    private Integer auditStatus;
+    /**
+     * 销售公司审核时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime companyAuditTime;
+    /**
+     * 销售公司审核人ID
+     */
+    private Long companyAuditUserId;
+    /**
+     * 销售公司审核人名称
+     */
+    private String companyAuditUserName;
+    /**
+     * 总后台审核时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime adminAuditTime;
+    /**
+     * 总后台审核人ID
+     */
+    private Long adminAuditUserId;
+    /**
+     * 总后台审核人名称
+     */
+    private String adminAuditUserName;
+    /**
+     * 被拒原因
+     */
+    private String reason;
+    /**
+     * 创建时间
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+    private LocalDateTime createTime;
+}

+ 108 - 0
fs-service-system/src/main/resources/application-config-bly.yml

@@ -0,0 +1,108 @@
+#配置
+fsConfig:
+  #快递鸟
+  kdnId: 1886082
+  kdnKeyId: 5a66df03-3d88-469a-ab42-23cb082b57ac
+  kdnUrl: http://api.kdniao.com/Ebusiness/EbusinessOrderHandle.aspx
+  kdnSubscribeUrl: https://api.kdniao.com/api/dist
+  kdnAddressUrl: https://api.kdniao.com/api/dist
+  #ERP配置
+  erpOpen: 1
+  erpAppKey: 108123
+  erpSessionKey: 9caae15474cb443ea22235e7bb86016b
+  erpSecret: 96f774dbd60847b59a16f92fd963a0c8
+  erpUrl: http://v2.api.guanyierp.com/rest/erp_open
+  erpShopCode: test
+  #ERP-hc
+  erpWdAppKey: beiliyou2-gw
+  erpWdAppsecret: 37c7cebf6e5af56c783d865b63553993
+  erpWdSid: beiliyou2
+  erpWdShopCode: ziyou123456
+  erpWdBaseUrl: https://api.wangdian.cn/openapi2/
+  erpWarehouseCode: "02"
+  #第三方支付配置
+  payOpen: 1
+  payPartnerId: 22051909542647100020
+  payKey: f256bd35aa36115d729537e1a1e01b92
+  payGateWayUrl: https://openapi.t2bank.cn/gateway.html
+  payNotifyUrl: https://api.yjf.runtzh.com/app/pay/payNotify
+  refundNotifyUrl: https://api.yjf.runtzh.com/app/pay/refundNotify
+  # 腾讯云IM
+  sdkAppId: 1400693126
+  sdkAppKey: 9afa6e63db943293680e37b3ba032e52cdb238112750806e82e58e9240604b70
+  # 处方接口Test
+#  prescribeUrl: https://yixian-new-test.yixianmedical.com/platform-shenfang/nethosp/webservice/jsonapi
+#  actId:  uporder
+#  appId: 1646204278
+#  manuId:  3981112bfcc64bf68f7744ffec7e3ca7
+#  callbackUrl:  https://api.hospital.ifeiyu100.com/app/prescribe/presribeNotify
+  # 处方接口g
+  prescribeUrl: https://app3.nxk520.com/platform-shenfang/nethosp/webservice/jsonapi
+  actId: uporder
+  appId: 1661496555
+  manuId: 0212af1e742b41b09089afeec98f8276
+  callbackUrl: https://api.yjf.runtzh.com/app/prescribe/presribeNotify
+logging:
+  level:
+    org.springframework.web: INFO
+    com.github.binarywang.demo.wx.cp: DEBUG
+    me.chanjar.weixin: DEBUG
+wx:
+  cp:
+    corpId: wwb2a1055fb6c9a7c2
+    appConfigs:
+      - agentId: 1000002
+        secret: bhj3402rPCT0YGcosffyTO3eUMs1G2MFHMspXVBNf-c
+        token: PPKOdAlCoMO
+        aesKey: PKvaxtpSv8NGpfTDm7VUHIK8Wok2ESyYX24qpXJAdMP
+  miniapp:
+    configs:
+      - appid: wx11a2ce7c2bbc4521   #倍力优会员商城
+        secret: d680dc8ff20258b158c9355f8b7769ae
+        token: Ncbnd7lJvkripVOpyTFAna6NAWCxCrvC
+        aesKey: HlEiBB55eaWUaeBVAQO3cWKWPYv1vOVQSq7nFNICw4E
+        msgDataFormat: JSON
+
+  pay:
+    appId: wx11a2ce7c2bbc4521 #微信公众号或者小程序等的appid
+    mchId: 1703311381 #微信支付商户号
+    mchKey: FotTIbIzn4AisMW7de712LJQIazSqqAl #微信支付商户密钥
+    v3Key: y5Eo99q93qzdQRAs6E2BDKIF7f3EnS3G
+    subAppId:  #服务商模式下的子商户公众账号ID
+    subMchId:  #服务商模式下的子商户号
+    keyPath: c:\\Tools\\cert\\apiclient_cert.p12 # p12证书的位置,可以指定绝对路径,也可以指定类路径(以classpath:开头)
+    notifyUrl:  https://userapp.bly.ylrztop.com/app/wxpay/wxPayNotify
+  mp:
+    useRedis: false
+    redisConfig:
+      host: 127.0.0.1
+      port: 6379
+      timeout: 2000
+    configs:
+      - appId: wx568ea6b70350c585 # 第一个公众号的appid  倍力优
+        secret: b14343e22871b1c207df5d3321e826b4 # 公众号的appsecret
+        token: PPKOdAlCoMO # 接口配置里的Token值
+        aesKey: Eswa6VjwtVMCcw03qZy6fWllgrv5aytIA1SZPEU0kU2 # 接口配置里的EncodingAESKey值
+jpush:
+  appKey: cc9a0120a3e4270c9cba340d
+  masterSecret: cfc2575d3cd7470d584c990c
+  liveTime: 1000
+  apnsProduction: true
+aifabu:  #爱链接
+  appKey: 7b471be905ab17e00f3b858c6710dd117601d008
+
+tencent_cloud_config:
+  secret_id: AKIDiMq9lDf2EOM9lIfqqfKo7FNgM5meD0sT
+  secret_key: u5SuS80342xzx8FRBukza9lVNHKNMSaB
+  bucket: beliyo-1323137866
+  app_id: 1323137866
+  region: ap-chongqing
+  proxy: beliyo
+cloud_host:
+  company_name: 倍力优
+
+
+
+
+
+

+ 137 - 0
fs-service-system/src/main/resources/application-druid-bly.yml

@@ -0,0 +1,137 @@
+# 数据源配置
+spring:
+    # redis 配置
+    redis:
+        # 地址
+        host: 192.168.0.224
+        # 端口,默认为6379
+        port: 6379
+        # 密码
+        password:
+        # 连接超时时间
+        timeout: 30s
+        lettuce:
+            pool:
+                # 连接池中的最小空闲连接
+                min-idle: 0
+                # 连接池中的最大空闲连接
+                max-idle: 8
+                # 连接池的最大数据库连接数
+                max-active: 8
+                # #连接池最大阻塞等待时间(使用负值表示没有限制)
+                max-wait: -1ms
+        database: 0
+    datasource:
+        mysql:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://192.168.0.224:3306/bly_store?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: bly@2025
+                # 从库数据源
+                slave:
+                    # 从数据源开关/默认关闭
+                    enabled: false
+                    url:
+                    username:
+                    password:
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+        sop:
+            type: com.alibaba.druid.pool.DruidDataSource
+            driverClassName: com.mysql.cj.jdbc.Driver
+            druid:
+                # 主库数据源
+                master:
+                    url: jdbc:mysql://192.168.0.224:3306/sop?useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=true&serverTimezone=GMT%2B8
+                    username: root
+                    password: bly@2025
+                # 初始连接数
+                initialSize: 5
+                # 最小连接池数量
+                minIdle: 10
+                # 最大连接池数量
+                maxActive: 20
+                # 配置获取连接等待超时的时间
+                maxWait: 60000
+                # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
+                timeBetweenEvictionRunsMillis: 60000
+                # 配置一个连接在池中最小生存的时间,单位是毫秒
+                minEvictableIdleTimeMillis: 300000
+                # 配置一个连接在池中最大生存的时间,单位是毫秒
+                maxEvictableIdleTimeMillis: 900000
+                # 配置检测连接是否有效
+                validationQuery: SELECT 1 FROM DUAL
+                testWhileIdle: true
+                testOnBorrow: false
+                testOnReturn: false
+                webStatFilter:
+                    enabled: true
+                statViewServlet:
+                    enabled: true
+                    # 设置白名单,不填则允许所有访问
+                    allow:
+                    url-pattern: /druid/*
+                    # 控制台管理用户名和密码
+                    login-username: fs
+                    login-password: 123456
+                filter:
+                    stat:
+                        enabled: true
+                        # 慢SQL记录
+                        log-slow-sql: true
+                        slow-sql-millis: 1000
+                        merge-sql: true
+                    wall:
+                        config:
+                            multi-statement-allow: true
+rocketmq:
+    name-server: rmq-1243b25nj.rocketmq.gz.public.tencenttdmq.com:8080 # RocketMQ NameServer 地址
+    producer:
+        group: my-producer-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey
+    consumer:
+        group: test-group
+        access-key: ak1243b25nj17d4b2dc1a03 # 替换为实际的 accessKey
+        secret-key: sk08a7ea1f9f4b0237 # 替换为实际的 secretKey

+ 21 - 0
fs-service-system/src/main/resources/mapper/course/FsUserCoursePeriodDaysMapper.xml

@@ -174,4 +174,25 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         where ucpd.status = 1 and ucpd.end_date_time < #{now}
         ]]>
     </update>
+
+    <select id="selectDaysCountList" resultType="FsUserWatchCourseStatistics">
+        SELECT
+            a.*,
+            a.start_date_time as courseStartDateTime
+             ,b.course_name
+             ,c.title as videoTitle
+             ,period.period_name,period.period_starting_time
+             ,company.company_id,company.company_name
+             ,company_user.user_id as companyUserId, company_user.nick_name as companyUserName
+        FROM
+            fs_user_course_period_days a
+                INNER JOIN fs_user_course b ON a.course_id = b.course_id
+                INNER JOIN fs_user_course_video c ON a.video_id = c.video_id
+                left join fs_user_course_period period on period.period_id = a.period_id
+                left join company on FIND_IN_SET(company.company_id, period.company_id) > 0
+                left join company_user on company_user.company_id = company.company_id
+        where period.period_name is not null
+        ORDER BY
+            a.day_date
+    </select>
 </mapper>

+ 221 - 0
fs-service-system/src/main/resources/mapper/course/FsUserWatchCourseStatisticsMapper.xml

@@ -0,0 +1,221 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.course.mapper.FsUserWatchCourseStatisticsMapper">
+
+    <resultMap type="FsUserWatchCourseStatistics" id="FsUserWatchCourseStatisticsResult">
+        <result property="id"    column="id"    />
+        <result property="periodId"    column="period_id"    />
+        <result property="periodName"    column="period_name"    />
+        <result property="courseId"    column="course_id"    />
+        <result property="courseName"    column="course_name"    />
+        <result property="videoId"    column="video_id"    />
+        <result property="videoTitle"    column="video_title"    />
+        <result property="companyId"    column="company_id"    />
+        <result property="companyName"    column="company_name"    />
+        <result property="companyUserId"    column="company_user_id"    />
+        <result property="courseStartDateTime"    column="course_start_date_time"    />
+        <result property="companyUserName"    column="company_user_name"    />
+        <result property="periodStartingTime"    column="period_starting_time"    />
+        <result property="newUserNum"    column="new_user_num"    />
+        <result property="userNum"    column="user_num"    />
+        <result property="watchNum"    column="watch_num"    />
+        <result property="completeWatchNum"    column="complete_watch_num"    />
+        <result property="completeWatchRate"    column="complete_watch_rate"    />
+        <result property="answerNum"    column="answer_num"    />
+        <result property="answerRightNum"    column="answer_right_num"    />
+        <result property="answerRightRate"    column="answer_right_rate"    />
+        <result property="redPacketNum"    column="red_packet_num"    />
+        <result property="redPacketAmount"    column="red_packet_amount"    />
+    </resultMap>
+
+    <sql id="selectFsUserWatchCourseStatisticsVo">
+        select id, period_id, period_name, course_id, course_name, video_id, video_title, company_id, company_name, company_user_id, course_start_date_time, company_user_name, period_starting_time, new_user_num, user_num, watch_num, complete_watch_num, complete_watch_rate, answer_num, answer_right_num, answer_right_rate, red_packet_num, red_packet_amount from fs_user_watch_course_statistics
+    </sql>
+
+    <select id="selectFsUserWatchCourseStatisticsList" parameterType="FsUserWatchCourseStatistics" resultMap="FsUserWatchCourseStatisticsResult">
+        <include refid="selectFsUserWatchCourseStatisticsVo"/>
+        <where>
+            <if test="periodId != null "> and period_id = #{periodId}</if>
+            <if test="periodName != null  and periodName != ''"> and period_name like concat('%', #{periodName}, '%')</if>
+            <if test="courseId != null "> and course_id = #{courseId}</if>
+            <if test="courseName != null  and courseName != ''"> and course_name like concat('%', #{courseName}, '%')</if>
+            <if test="videoId != null "> and video_id = #{videoId}</if>
+            <if test="videoTitle != null  and videoTitle != ''"> and video_title = #{videoTitle}</if>
+            <if test="companyId != null "> and company_id = #{companyId}</if>
+            <if test="companyName != null  and companyName != ''"> and company_name like concat('%', #{companyName}, '%')</if>
+            <if test="companyUserId != null "> and company_user_id = #{companyUserId}</if>
+            <if test="courseStartDateTime != null "> and course_start_date_time = #{courseStartDateTime}</if>
+            <if test="companyUserName != null  and companyUserName != ''"> and company_user_name like concat('%', #{companyUserName}, '%')</if>
+            <if test="periodStartingTime != null "> and period_starting_time = #{periodStartingTime}</if>
+            <if test="newUserNum != null "> and new_user_num = #{newUserNum}</if>
+            <if test="userNum != null "> and user_num = #{userNum}</if>
+            <if test="watchNum != null "> and watch_num = #{watchNum}</if>
+            <if test="completeWatchNum != null "> and complete_watch_num = #{completeWatchNum}</if>
+            <if test="completeWatchRate != null "> and complete_watch_rate = #{completeWatchRate}</if>
+            <if test="answerNum != null "> and answer_num = #{answerNum}</if>
+            <if test="answerRightNum != null "> and answer_right_num = #{answerRightNum}</if>
+            <if test="answerRightRate != null "> and answer_right_rate = #{answerRightRate}</if>
+            <if test="redPacketNum != null "> and red_packet_num = #{redPacketNum}</if>
+            <if test="redPacketAmount != null "> and red_packet_amount = #{redPacketAmount}</if>
+        </where>
+    </select>
+
+    <select id="selectFsUserWatchCourseStatisticsById" parameterType="Long" resultMap="FsUserWatchCourseStatisticsResult">
+        <include refid="selectFsUserWatchCourseStatisticsVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertFsUserWatchCourseStatistics" parameterType="FsUserWatchCourseStatistics" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_user_watch_course_statistics
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="periodId != null">period_id,</if>
+            <if test="periodName != null">period_name,</if>
+            <if test="courseId != null">course_id,</if>
+            <if test="courseName != null">course_name,</if>
+            <if test="videoId != null">video_id,</if>
+            <if test="videoTitle != null">video_title,</if>
+            <if test="companyId != null">company_id,</if>
+            <if test="companyName != null">company_name,</if>
+            <if test="companyUserId != null">company_user_id,</if>
+            <if test="courseStartDateTime != null">course_start_date_time,</if>
+            <if test="companyUserName != null">company_user_name,</if>
+            <if test="periodStartingTime != null">period_starting_time,</if>
+            <if test="newUserNum != null">new_user_num,</if>
+            <if test="userNum != null">user_num,</if>
+            <if test="watchNum != null">watch_num,</if>
+            <if test="completeWatchNum != null">complete_watch_num,</if>
+            <if test="completeWatchRate != null">complete_watch_rate,</if>
+            <if test="answerNum != null">answer_num,</if>
+            <if test="answerRightNum != null">answer_right_num,</if>
+            <if test="answerRightRate != null">answer_right_rate,</if>
+            <if test="redPacketNum != null">red_packet_num,</if>
+            <if test="redPacketAmount != null">red_packet_amount,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="periodId != null">#{periodId},</if>
+            <if test="periodName != null">#{periodName},</if>
+            <if test="courseId != null">#{courseId},</if>
+            <if test="courseName != null">#{courseName},</if>
+            <if test="videoId != null">#{videoId},</if>
+            <if test="videoTitle != null">#{videoTitle},</if>
+            <if test="companyId != null">#{companyId},</if>
+            <if test="companyName != null">#{companyName},</if>
+            <if test="companyUserId != null">#{companyUserId},</if>
+            <if test="courseStartDateTime != null">#{courseStartDateTime},</if>
+            <if test="companyUserName != null">#{companyUserName},</if>
+            <if test="periodStartingTime != null">#{periodStartingTime},</if>
+            <if test="newUserNum != null">#{newUserNum},</if>
+            <if test="userNum != null">#{userNum},</if>
+            <if test="watchNum != null">#{watchNum},</if>
+            <if test="completeWatchNum != null">#{completeWatchNum},</if>
+            <if test="completeWatchRate != null">#{completeWatchRate},</if>
+            <if test="answerNum != null">#{answerNum},</if>
+            <if test="answerRightNum != null">#{answerRightNum},</if>
+            <if test="answerRightRate != null">#{answerRightRate},</if>
+            <if test="redPacketNum != null">#{redPacketNum},</if>
+            <if test="redPacketAmount != null">#{redPacketAmount},</if>
+         </trim>
+    </insert>
+
+    <update id="updateFsUserWatchCourseStatistics" parameterType="FsUserWatchCourseStatistics">
+        update fs_user_watch_course_statistics
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="periodId != null">period_id = #{periodId},</if>
+            <if test="periodName != null">period_name = #{periodName},</if>
+            <if test="courseId != null">course_id = #{courseId},</if>
+            <if test="courseName != null">course_name = #{courseName},</if>
+            <if test="videoId != null">video_id = #{videoId},</if>
+            <if test="videoTitle != null">video_title = #{videoTitle},</if>
+            <if test="companyId != null">company_id = #{companyId},</if>
+            <if test="companyName != null">company_name = #{companyName},</if>
+            <if test="companyUserId != null">company_user_id = #{companyUserId},</if>
+            <if test="courseStartDateTime != null">course_start_date_time = #{courseStartDateTime},</if>
+            <if test="companyUserName != null">company_user_name = #{companyUserName},</if>
+            <if test="periodStartingTime != null">period_starting_time = #{periodStartingTime},</if>
+            <if test="newUserNum != null">new_user_num = #{newUserNum},</if>
+            <if test="userNum != null">user_num = #{userNum},</if>
+            <if test="watchNum != null">watch_num = #{watchNum},</if>
+            <if test="completeWatchNum != null">complete_watch_num = #{completeWatchNum},</if>
+            <if test="completeWatchRate != null">complete_watch_rate = #{completeWatchRate},</if>
+            <if test="answerNum != null">answer_num = #{answerNum},</if>
+            <if test="answerRightNum != null">answer_right_num = #{answerRightNum},</if>
+            <if test="answerRightRate != null">answer_right_rate = #{answerRightRate},</if>
+            <if test="redPacketNum != null">red_packet_num = #{redPacketNum},</if>
+            <if test="redPacketAmount != null">red_packet_amount = #{redPacketAmount},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteFsUserWatchCourseStatisticsById" parameterType="Long">
+        delete from fs_user_watch_course_statistics where id = #{id}
+    </delete>
+
+    <delete id="deleteFsUserWatchCourseStatisticsByIds" parameterType="String">
+        delete from fs_user_watch_course_statistics where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+
+    <insert id="insertFsUserWatchCourseStatisticsTask" parameterType="FsUserWatchCourseStatistics" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_user_watch_course_statistics
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="periodId != null">period_id,</if>
+            <if test="periodName != null">period_name,</if>
+            <if test="courseId != null">course_id,</if>
+            <if test="courseName != null">course_name,</if>
+            <if test="videoId != null">video_id,</if>
+            <if test="videoTitle != null">video_title,</if>
+            <if test="companyId != null">company_id,</if>
+            <if test="companyName != null">company_name,</if>
+            <if test="companyUserId != null">company_user_id,</if>
+            <if test="courseStartDateTime != null">course_start_date_time,</if>
+            <if test="companyUserName != null">company_user_name,</if>
+            <if test="periodStartingTime != null">period_starting_time,</if>
+            <if test="newUserNum != null">new_user_num,</if>
+            <if test="userNum != null">user_num,</if>
+            <if test="watchNum != null">watch_num,</if>
+            <if test="completeWatchNum != null">complete_watch_num,</if>
+            <if test="completeWatchRate != null">complete_watch_rate,</if>
+            <if test="answerNum != null">answer_num,</if>
+            <if test="answerRightNum != null">answer_right_num,</if>
+            <if test="answerRightRate != null">answer_right_rate,</if>
+            <if test="redPacketNum != null">red_packet_num,</if>
+            <if test="redPacketAmount != null">red_packet_amount,</if>
+            <if test="createTime != null">create_time,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="periodId != null">#{periodId},</if>
+            <if test="periodName != null">#{periodName},</if>
+            <if test="courseId != null">#{courseId},</if>
+            <if test="courseName != null">#{courseName},</if>
+            <if test="videoId != null">#{videoId},</if>
+            <if test="videoTitle != null">#{videoTitle},</if>
+            <if test="companyId != null">#{companyId},</if>
+            <if test="companyName != null">#{companyName},</if>
+            <if test="companyUserId != null">#{companyUserId},</if>
+            <if test="courseStartDateTime != null">#{courseStartDateTime},</if>
+            <if test="companyUserName != null">#{companyUserName},</if>
+            <if test="periodStartingTime != null">#{periodStartingTime},</if>
+            <if test="newUserNum != null">#{newUserNum},</if>
+            <if test="userNum != null">#{userNum},</if>
+            <if test="watchNum != null">#{watchNum},</if>
+            <if test="completeWatchNum != null">#{completeWatchNum},</if>
+            <if test="completeWatchRate != null">#{completeWatchRate},</if>
+            <if test="answerNum != null">#{answerNum},</if>
+            <if test="answerRightNum != null">#{answerRightNum},</if>
+            <if test="answerRightRate != null">#{answerRightRate},</if>
+            <if test="redPacketNum != null">#{redPacketNum},</if>
+            <if test="redPacketAmount != null">#{redPacketAmount},</if>
+            <if test="createTime != null">#{createTime},</if>
+        </trim>
+        on duplicate key update
+        <trim suffixOverrides=",">
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+    </insert>
+
+</mapper>

+ 147 - 0
fs-service-system/src/main/resources/mapper/course/FsUserWatchStatisticsMapper.xml

@@ -0,0 +1,147 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.course.mapper.FsUserWatchStatisticsMapper">
+
+    <resultMap type="FsUserWatchStatistics" id="FsUserWatchStatisticsResult">
+        <result property="id"    column="id"    />
+        <result property="periodId"    column="period_id"    />
+        <result property="periodName"    column="period_name"    />
+        <result property="periodStartingTime"    column="period_starting_time"    />
+        <result property="newUserNum"    column="new_user_num"    />
+        <result property="userNum"    column="user_num"    />
+        <result property="watchNum"    column="watch_num"    />
+        <result property="completeWatchNum"    column="complete_watch_num"    />
+        <result property="completeWatchRate"    column="complete_watch_rate"    />
+    </resultMap>
+
+    <sql id="selectFsUserWatchStatisticsVo">
+        select id, period_id, period_name, period_starting_time, new_user_num, user_num, watch_num, complete_watch_num, complete_watch_rate from fs_user_watch_statistics
+    </sql>
+
+    <select id="selectFsUserWatchStatisticsList" parameterType="FsUserWatchStatistics" resultMap="FsUserWatchStatisticsResult">
+        <include refid="selectFsUserWatchStatisticsVo"/>
+        <where>
+            <if test="periodId != null "> and period_id = #{periodId}</if>
+            <if test="periodName != null  and periodName != ''"> and period_name like concat('%', #{periodName}, '%')</if>
+            <if test="periodStartingTime != null "> and period_starting_time = #{periodStartingTime}</if>
+            <if test="newUserNum != null "> and new_user_num = #{newUserNum}</if>
+            <if test="userNum != null "> and user_num = #{userNum}</if>
+            <if test="watchNum != null "> and watch_num = #{watchNum}</if>
+            <if test="completeWatchNum != null "> and complete_watch_num = #{completeWatchNum}</if>
+            <if test="completeWatchRate != null "> and complete_watch_rate = #{completeWatchRate}</if>
+        </where>
+    </select>
+
+    <select id="selectFsUserWatchStatisticsById" parameterType="Long" resultMap="FsUserWatchStatisticsResult">
+        <include refid="selectFsUserWatchStatisticsVo"/>
+        where id = #{id}
+    </select>
+
+    <insert id="insertFsUserWatchStatistics" parameterType="FsUserWatchStatistics" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_user_watch_statistics
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="periodId != null">period_id,</if>
+            <if test="periodName != null">period_name,</if>
+            <if test="periodStartingTime != null">period_starting_time,</if>
+            <if test="newUserNum != null">new_user_num,</if>
+            <if test="userNum != null">user_num,</if>
+            <if test="watchNum != null">watch_num,</if>
+            <if test="completeWatchNum != null">complete_watch_num,</if>
+            <if test="completeWatchRate != null">complete_watch_rate,</if>
+         </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="periodId != null">#{periodId},</if>
+            <if test="periodName != null">#{periodName},</if>
+            <if test="periodStartingTime != null">#{periodStartingTime},</if>
+            <if test="newUserNum != null">#{newUserNum},</if>
+            <if test="userNum != null">#{userNum},</if>
+            <if test="watchNum != null">#{watchNum},</if>
+            <if test="completeWatchNum != null">#{completeWatchNum},</if>
+            <if test="completeWatchRate != null">#{completeWatchRate},</if>
+         </trim>
+    </insert>
+
+    <update id="updateFsUserWatchStatistics" parameterType="FsUserWatchStatistics">
+        update fs_user_watch_statistics
+        <trim prefix="SET" suffixOverrides=",">
+            <if test="periodId != null">period_id = #{periodId},</if>
+            <if test="periodName != null">period_name = #{periodName},</if>
+            <if test="periodStartingTime != null">period_starting_time = #{periodStartingTime},</if>
+            <if test="newUserNum != null">new_user_num = #{newUserNum},</if>
+            <if test="userNum != null">user_num = #{userNum},</if>
+            <if test="watchNum != null">watch_num = #{watchNum},</if>
+            <if test="completeWatchNum != null">complete_watch_num = #{completeWatchNum},</if>
+            <if test="completeWatchRate != null">complete_watch_rate = #{completeWatchRate},</if>
+        </trim>
+        where id = #{id}
+    </update>
+
+    <delete id="deleteFsUserWatchStatisticsById" parameterType="Long">
+        delete from fs_user_watch_statistics where id = #{id}
+    </delete>
+
+    <delete id="deleteFsUserWatchStatisticsByIds" parameterType="String">
+        delete from fs_user_watch_statistics where id in
+        <foreach item="id" collection="array" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+    </delete>
+
+    <select id="getCourseWatchStatistics" resultType="FsUserWatchStatistics">
+        SELECT
+            count( DISTINCT CASE WHEN fwl.log_type != 3 THEN fwl.user_id END ) AS watchNum,
+            count( DISTINCT CASE WHEN fwl.log_type = 2 THEN fwl.user_id END ) AS completeWatchNum,
+            ifnull(
+                ROUND(
+                    (
+                        COUNT( DISTINCT CASE WHEN fwl.log_type = 2 THEN fwl.user_id END ) / count( DISTINCT CASE WHEN fwl.log_type != 3 THEN fwl.user_id END )) * 100,
+                    2
+                    ),
+                    0
+            ) AS completeWatchRate,
+            fwl.period_id
+            ,fs_user_course_period.period_name,
+            fs_user_course_period.period_starting_time
+        FROM
+            fs_course_watch_log fwl
+                LEFT JOIN fs_user_course_period ON fwl.period_id = fs_user_course_period.period_id
+        WHERE
+            fwl.send_type = 1
+            and fs_user_course_period.period_name is not null
+        GROUP BY
+            fwl.period_id
+    </select>
+
+    <insert id="insertFsUserWatchStatisticsTask" parameterType="FsUserWatchStatistics" useGeneratedKeys="true" keyProperty="id">
+        insert into fs_user_watch_statistics
+        <trim prefix="(" suffix=")" suffixOverrides=",">
+            <if test="periodId != null">period_id,</if>
+            <if test="periodName != null">period_name,</if>
+            <if test="periodStartingTime != null">period_starting_time,</if>
+            <if test="newUserNum != null">new_user_num,</if>
+            <if test="userNum != null">user_num,</if>
+            <if test="watchNum != null">watch_num,</if>
+            <if test="completeWatchNum != null">complete_watch_num,</if>
+            <if test="completeWatchRate != null">complete_watch_rate,</if>
+            <if test="createTime != null">create_time,</if>
+        </trim>
+        <trim prefix="values (" suffix=")" suffixOverrides=",">
+            <if test="periodId != null">#{periodId},</if>
+            <if test="periodName != null">#{periodName},</if>
+            <if test="periodStartingTime != null">#{periodStartingTime},</if>
+            <if test="newUserNum != null">#{newUserNum},</if>
+            <if test="userNum != null">#{userNum},</if>
+            <if test="watchNum != null">#{watchNum},</if>
+            <if test="completeWatchNum != null">#{completeWatchNum},</if>
+            <if test="completeWatchRate != null">#{completeWatchRate},</if>
+            <if test="createTime != null">#{createTime},</if>
+        </trim>
+        on duplicate key update
+        <trim suffixOverrides=",">
+            <if test="updateTime != null">update_time = #{updateTime},</if>
+        </trim>
+    </insert>
+
+</mapper>

+ 6 - 1
fs-service-system/src/main/resources/mapper/store/FsStoreAfterSalesItemMapper.xml

@@ -8,12 +8,13 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="id"    column="id"    />
         <result property="storeAfterSalesId"    column="store_after_sales_id"    />
         <result property="productId"    column="product_id"    />
+        <result property="num"    column="num"    />
         <result property="jsonInfo"    column="json_info"    />
         <result property="isDel"    column="is_del"    />
     </resultMap>
 
     <sql id="selectFsStoreAfterSalesItemVo">
-        select id, store_after_sales_id, product_id, json_info, is_del from fs_store_after_sales_item
+        select id, store_after_sales_id, product_id,num, json_info, is_del from fs_store_after_sales_item
     </sql>
 
     <select id="selectFsStoreAfterSalesItemList" parameterType="FsStoreAfterSalesItem" resultMap="FsStoreAfterSalesItemResult">
@@ -21,6 +22,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <where>  
             <if test="storeAfterSalesId != null "> and store_after_sales_id = #{storeAfterSalesId}</if>
             <if test="productId != null "> and product_id = #{productId}</if>
+            <if test="num != null "> and num = #{num}</if>
             <if test="jsonInfo != null  and jsonInfo != ''"> and json_info = #{jsonInfo}</if>
             <if test="isDel != null  and isDel != ''"> and is_del = #{isDel}</if>
         </where>
@@ -36,12 +38,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <trim prefix="(" suffix=")" suffixOverrides=",">
             <if test="storeAfterSalesId != null">store_after_sales_id,</if>
             <if test="productId != null">product_id,</if>
+            <if test="num != null">num,</if>
             <if test="jsonInfo != null and jsonInfo != ''">json_info,</if>
             <if test="isDel != null">is_del,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="storeAfterSalesId != null">#{storeAfterSalesId},</if>
             <if test="productId != null">#{productId},</if>
+            <if test="num != null">#{num},</if>
             <if test="jsonInfo != null and jsonInfo != ''">#{jsonInfo},</if>
             <if test="isDel != null">#{isDel},</if>
          </trim>
@@ -52,6 +56,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <trim prefix="SET" suffixOverrides=",">
             <if test="storeAfterSalesId != null">store_after_sales_id = #{storeAfterSalesId},</if>
             <if test="productId != null">product_id = #{productId},</if>
+            <if test="num != null">num = #{num},</if>
             <if test="jsonInfo != null and jsonInfo != ''">json_info = #{jsonInfo},</if>
             <if test="isDel != null">is_del = #{isDel},</if>
         </trim>

+ 7 - 0
fs-service-system/src/main/resources/mapper/store/FsStoreOrderAuditLogMapper.xml

@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.store.mapper.FsStoreOrderAuditLogMapper">
+
+</mapper>

+ 44 - 0
fs-service-system/src/main/resources/mapper/store/FsStoreOrderAuditMapper.xml

@@ -0,0 +1,44 @@
+<?xml version="1.0" encoding="UTF-8" ?>
+<!DOCTYPE mapper
+PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
+"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
+<mapper namespace="com.fs.store.mapper.FsStoreOrderAuditMapper">
+
+    <select id="selectStoreOrderAuditVOList" resultType="com.fs.store.vo.FsStoreOrderAuditVO">
+        select
+            fsoa.id,
+            fsoa.order_id,
+            fsoa.company_id,
+            fsoa.company_user_id,
+            fsoa.audit_status,
+            fsoa.company_audit_time,
+            fsoa.company_audit_user_id,
+            fsoa.admin_audit_time,
+            fsoa.admin_audit_user_id,
+            fsoa.reason,
+            fsoa.create_time,
+            fso.order_code,
+            c.company_name,
+            cu.nick_name companyUserName,
+            cau.nick_name companyAuditUserName,
+            aau.nick_name adminAuditUserName
+        from fs_store_order_audit fsoa
+        left join fs_store_order fso on fso.id = fsoa.order_id
+        left join company c on c.company_id = fsoa.company_id
+        left join company_user cu on cu.user_id = fsoa.company_user_id
+        left join company_user cau on cau.user_id = fsoa.company_audit_user_id
+        left join sys_user aau on aau.user_id = fsoa.admin_audit_user_id
+        <where>
+            <if test="companyId != null">
+                and fsoa.company_id = #{companyId}
+            </if>
+            <if test="companyUserName != null and companyUserName != ''">
+                and cu.nick_name like concat('%', #{companyUserName}, '%')
+            </if>
+            <if test="auditStatus != null">
+                and fsoa.audit_status = #{auditStatus}
+            </if>
+        </where>
+        order by fsoa.admin_audit_time desc, fsoa.company_audit_time desc, fsoa.create_time desc
+    </select>
+</mapper>

+ 102 - 0
fs-service-system/src/main/resources/mapper/store/FsUserMapper.xml

@@ -1447,4 +1447,106 @@
         </if>
     </select>
 
+    <select id="selectFsUserTotal" resultType="FsUserWatchStatistics">
+        SELECT
+            count( fs_user.user_id ) as userNum,
+            count( DISTINCT CASE WHEN to_days( fs_user.create_time ) = to_days( now()) THEN fs_user.user_id END ) as newUserNum
+        FROM
+            fs_user
+            LEFT JOIN company_user ON company_user.user_id = fs_user.company_user_id
+            LEFT JOIN company ON company.company_id = fs_user.company_id
+        WHERE
+            fs_user.is_del = 0
+          AND fs_user.`status` = 1
+    </select>
+
+
+    <select id="selectWatchLogCount" resultType="FsUserWatchCourseStatistics">
+        SELECT
+            count( DISTINCT CASE WHEN fwl.log_type != 3 THEN fwl.user_id END ) AS watchNum,
+
+            count( DISTINCT CASE WHEN fwl.log_type = 2 THEN fwl.user_id END ) AS completeWatchNum,
+
+            ifnull(
+                    ROUND(
+                            (
+                                COUNT( DISTINCT CASE WHEN fwl.log_type = 2 THEN fwl.user_id END ) / count( DISTINCT CASE WHEN fwl.log_type != 3 THEN fwl.user_id END )) * 100,
+                            2
+                    ),
+                    0
+            ) AS completeWatchRate,
+            fwl.period_id, fwl.video_id, fwl.company_user_id, fwl.company_id
+        FROM
+            fs_course_watch_log fwl
+        WHERE
+            fwl.send_type = 1
+        GROUP BY
+            fwl.period_id, fwl.video_id, fwl.company_user_id
+    </select>
+
+    <select id="selectRedPacketLogCount" resultType="FsUserWatchCourseStatistics">
+        SELECT
+            count( flog.log_id ) AS redPacketNum,
+            ifnull ( sum( flog.amount ), 0 ) AS redPacketAmount,
+            flog.period_id,
+            flog.video_id,
+            flog.company_user_id,
+            flog.company_id
+        FROM
+            fs_course_red_packet_log flog
+                LEFT JOIN fs_user ON fs_user.user_id = flog.user_id
+                LEFT JOIN company_user ON company_user.user_id = fs_user.company_user_id
+        GROUP BY
+            flog.period_id,
+            flog.video_id,
+            flog.company_user_id
+    </select>
+
+    <select id="selectAnswerLogCount" resultType="FsUserWatchCourseStatistics">
+        SELECT
+            count( DISTINCT fs_user.user_id ) AS answerNum,
+            COUNT( DISTINCT CASE WHEN fs_course_answer_logs.is_right = 1 THEN fs_user.user_id END ) AS answerRightNum,
+            ifnull(
+                    ROUND(
+                            (
+                                COUNT( DISTINCT CASE WHEN fs_course_answer_logs.is_right = 1 THEN fs_user.user_id END ) / count( DISTINCT fs_user.user_id )) * 100,
+                            2
+                    ),
+                    0
+            ) AS answerRightRate,
+            fs_course_answer_logs.period_id,
+            fs_course_answer_logs.video_id,
+            fs_course_answer_logs.company_user_id,
+            fs_course_answer_logs.company_id
+        FROM
+            fs_course_answer_logs
+                LEFT JOIN fs_user ON fs_user.user_id = fs_course_answer_logs.user_id
+                LEFT JOIN company_user ON company_user.user_id = fs_user.company_user_id
+        GROUP BY
+            fs_course_answer_logs.period_id,
+            fs_course_answer_logs.video_id,
+            fs_course_answer_logs.company_user_id
+    </select>
+
+    <select id="selectFsUserDetail" resultType="FsUserWatchCourseStatistics">
+        SELECT
+        count( fs_user.user_id ) as userNum,
+        count( DISTINCT CASE WHEN to_days( fs_user.create_time ) = to_days( now()) THEN fs_user.user_id END ) as newUserNum
+        ,company.company_id,
+        company.company_name,
+        company_user.user_id AS companyUserId,
+        company_user.nick_name AS companyUserName
+
+        FROM
+        fs_user
+        LEFT JOIN company_user ON company_user.user_id = fs_user.company_user_id
+        LEFT JOIN company ON company.company_id = fs_user.company_id
+        WHERE
+        fs_user.is_del = 0
+        AND fs_user.`status` = 1
+        AND company_user.user_id is not null
+        GROUP BY
+        fs_user.company_user_id
+    </select>
+
 </mapper>

+ 4 - 1
fs-user-app/src/main/java/com/fs/app/controller/CompanyOrderController.java

@@ -80,13 +80,16 @@ public class CompanyOrderController extends  AppBaseController {
     @GetMapping("/updateSalseOrderMoney")
     public R updateSalseOrderMoney(@RequestParam("token")String token,
                                    @RequestParam("createOrderKey")String createOrderKey,
-                                   @RequestParam("money") BigDecimal money,
+                                   @RequestParam(value = "money",required = false) BigDecimal money,
                                    @RequestParam(value = "payAmount",required = false) BigDecimal payAmount,
                                    HttpServletRequest request){
         Long userId=redisCache.getCacheObject("company-user-token:"+token);
         if(userId==null){
             return R.error(403,"用户失效");
         }
+        if (money == null){
+            money = BigDecimal.ZERO;
+        }
         return orderService.updateSalseOrderMoney(createOrderKey,money,payAmount);
     }