瀏覽代碼

新增统计汇总

lmx 1 周之前
父節點
當前提交
2219f5e4e6

+ 30 - 0
fs-admin/src/main/java/com/fs/course/controller/FsCourseTrafficLogController.java

@@ -9,7 +9,9 @@ import com.fs.common.core.domain.R;
 import com.fs.common.exception.CustomException;
 import com.fs.course.param.FsCourseTrafficLogParam;
 import com.fs.course.param.InternetTrafficParam;
+import com.fs.course.param.StatisticsSummaryParam;
 import com.fs.course.vo.FsCourseTrafficLogListVO;
+import com.fs.course.vo.StatisticsSummaryVO;
 import com.fs.qw.param.QwWatchLogStatisticsListParam;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -135,4 +137,32 @@ public class FsCourseTrafficLogController extends BaseController
         return R.ok().put("data", null);  // 返回计算结果
     }
 
+    /**
+     * 流量统计汇总
+     * @param param
+     * @return
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseTrafficLog:statistics')")
+    @GetMapping("/statisticsSummaryList")
+    public TableDataInfo statisticsSummaryList(StatisticsSummaryParam param)
+    {
+        List<StatisticsSummaryVO> list = fsCourseTrafficLogService.getStatisticsSummaryList(param);
+        return getDataTable(list);
+    }
+
+    /**
+     * 导出汇总统计报表
+     * @param param
+     * @return
+     */
+    @PreAuthorize("@ss.hasPermi('course:courseTrafficLog:statisticsExport')")
+    @GetMapping("/exportStatisticsSummary")
+    public AjaxResult exportStatisticsSummary(StatisticsSummaryParam param)
+    {
+        List<StatisticsSummaryVO> list = fsCourseTrafficLogService.getStatisticsSummaryListNotPage(param);
+        ExcelUtil<StatisticsSummaryVO> util = new ExcelUtil<StatisticsSummaryVO>(StatisticsSummaryVO.class);
+        return util.exportExcel(list, "看课流量统计汇总");
+
+    }
+
 }

+ 7 - 0
fs-service/src/main/java/com/fs/company/mapper/CompanyDeptMapper.java

@@ -1,6 +1,7 @@
 package com.fs.company.mapper;
 
 import com.fs.company.domain.CompanyDept;
+import com.fs.course.vo.DeptFullPathVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 
@@ -126,4 +127,10 @@ public interface CompanyDeptMapper
     List<CompanyDept> selectCompanyDeptByIds(@Param("depts")List<Long> deptIds);
 
     List<Long> getCurrentDeptIdDownTreeIds(@Param("deptId") Long deptId);
+
+    /**
+     * 根据公司id -> 部门 & 全路径查询
+     * @return
+     */
+    List<DeptFullPathVO> getDeptFullPathList(@Param("companyId") Long companyId);
 }

+ 33 - 0
fs-service/src/main/java/com/fs/course/mapper/FsCourseTrafficLogMapper.java

@@ -7,8 +7,10 @@ import com.fs.common.annotation.DataSource;
 import com.fs.common.enums.DataSourceType;
 import com.fs.course.domain.FsCourseTrafficLog;
 import com.fs.course.param.FsCourseTrafficLogParam;
+import com.fs.course.param.StatisticsSummaryParam;
 import com.fs.course.param.TrafficRecord;
 import com.fs.course.vo.FsCourseTrafficLogListVO;
+import com.fs.course.vo.StatisticsSummaryVO;
 import org.apache.ibatis.annotations.Param;
 import org.apache.ibatis.annotations.Select;
 import org.apache.ibatis.annotations.Update;
@@ -195,4 +197,35 @@ public interface FsCourseTrafficLogMapper
     @Select("SELECT SUM(T.internet_traffic)/1024 AS totalInternetTraffic FROM fs_course_traffic_log T " +
             "WHERE DATE(T.create_time) = DATE(CURDATE() - INTERVAL 1 DAY) AND T.company_id = #{companyId} GROUP BY T.company_id ")
     Long sumTrafficByCompanyYesterday(Long companyId);
+
+    @Select({
+            "<script>" +
+            "SELECT \n" +
+            "t1.company_id,\n" +
+            "t2.company_name,\n" +
+            "<if test='param.statisticsType == 2'>\n" +
+                "t4.dept_id,\n" +
+                "t4.dept_name,\n" +
+            "</if>" +
+            "sum(internet_traffic) as totalInternetTraffic,\n" +
+            "CONCAT(#{param.startDate},'到',#{param.endDate}) as dateRange\n" +
+            "FROM fs_course_traffic_log t1\n" +
+            "inner join company t2 on t1.company_id = t2.company_id\n" +
+            "<if test='param.statisticsType == 2'>\n" +
+                "inner join company_user t3 on t1.company_user_id = t3.user_id\n" +
+                "inner join company_dept t4 on t4.dept_id = t3.dept_id\n" +
+            "</if>" +
+            "WHERE t1.create_time BETWEEN #{param.startDate} AND #{param.endDateQueryValue}\n" +
+            "<if test='param.companyId != null'>\n" +
+            "and t2.company_id = #{param.companyId}\n" +
+            "</if>" +
+            "<if test='param.statisticsType == 2'>\n" +
+                "GROUP BY t4.dept_id  " +
+            "</if>" +
+            "<if test='param.statisticsType == 1'>\n" +
+                "GROUP BY t1.company_id  " +
+            "</if>" +
+            "</script>"
+    })
+    List<StatisticsSummaryVO> getStatisticsSummaryList(@Param("param") StatisticsSummaryParam param);
 }

+ 28 - 0
fs-service/src/main/java/com/fs/course/param/StatisticsSummaryParam.java

@@ -0,0 +1,28 @@
+package com.fs.course.param;
+
+import lombok.Data;
+
+/**
+ * @author MixLiu
+ * @date 2025/11/10 下午2:26)
+ */
+@Data
+public class StatisticsSummaryParam {
+
+    /**
+     * 统计维度 1、按照公司 2、按照部门
+     */
+    private Integer statisticsType;
+
+    /**
+     * 公司id
+     */
+    private Long companyId;
+
+
+    private String startDate;
+
+    private String endDate;
+
+    private String endDateQueryValue;
+}

+ 15 - 0
fs-service/src/main/java/com/fs/course/service/IFsCourseTrafficLogService.java

@@ -4,7 +4,9 @@ import java.util.List;
 import com.fs.course.domain.FsCourseTrafficLog;
 import com.fs.course.param.FsCourseTrafficLogParam;
 import com.fs.course.param.InternetTrafficParam;
+import com.fs.course.param.StatisticsSummaryParam;
 import com.fs.course.vo.FsCourseTrafficLogListVO;
+import com.fs.course.vo.StatisticsSummaryVO;
 
 /**
  * 短链课程流量记录Service接口
@@ -80,4 +82,17 @@ public interface IFsCourseTrafficLogService
      * 定时统计流量总数
      */
     void sumTrafficlog();
+
+    /**
+     * 流量统计汇总
+     * @param param
+     * @return
+     */
+    List<StatisticsSummaryVO> getStatisticsSummaryList(StatisticsSummaryParam param);
+    /**
+     * 获取流量统计列表(不分页)
+     * @param param
+     * @return
+     */
+    List<StatisticsSummaryVO> getStatisticsSummaryListNotPage(StatisticsSummaryParam param);
 }

+ 119 - 0
fs-service/src/main/java/com/fs/course/service/impl/FsCourseTrafficLogServiceImpl.java

@@ -1,23 +1,35 @@
 package com.fs.course.service.impl;
 
+import java.math.BigDecimal;
 import java.text.SimpleDateFormat;
+import java.time.LocalDate;
 import java.util.*;
+import java.util.stream.Collectors;
 
 import com.alibaba.fastjson.JSONObject;
 import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
 import com.baomidou.mybatisplus.core.toolkit.ObjectUtils;
+import com.fs.common.core.controller.BaseController;
+import com.fs.common.core.page.PageDomain;
+import com.fs.common.core.page.TableSupport;
 import com.fs.common.exception.CustomException;
 import com.fs.common.utils.DateUtils;
 import com.fs.common.utils.DictUtils;
 import com.fs.common.utils.date.DateUtil;
+import com.fs.common.utils.sql.SqlUtil;
 import com.fs.company.cache.ICompanyCacheService;
+import com.fs.company.mapper.CompanyDeptMapper;
 import com.fs.course.param.FsCourseTrafficLogParam;
 import com.fs.course.param.InternetTrafficParam;
+import com.fs.course.param.StatisticsSummaryParam;
 import com.fs.course.param.TrafficRecord;
+import com.fs.course.vo.DeptFullPathVO;
 import com.fs.course.vo.FsCourseTrafficLogListVO;
+import com.fs.course.vo.StatisticsSummaryVO;
 import com.fs.store.service.cache.IFsUserCourseCacheService;
 import com.fs.system.domain.SysConfig;
 import com.fs.system.service.ISysConfigService;
+import com.github.pagehelper.PageHelper;
 import com.hc.openapi.tool.util.StringUtils;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.beans.factory.annotation.Autowired;
@@ -47,6 +59,9 @@ public class FsCourseTrafficLogServiceImpl implements IFsCourseTrafficLogService
     @Autowired
     private IFsUserCourseCacheService fsUserCourseCacheService;
 
+    @Autowired
+    CompanyDeptMapper companyDeptMapper;
+
     /**
      * 查询短链课程流量记录
      *
@@ -402,4 +417,108 @@ public class FsCourseTrafficLogServiceImpl implements IFsCourseTrafficLogService
                 minutes % 60,
                 seconds % 60);
     }
+
+    /**
+     * 流量统计汇总
+     * @param param
+     * @return
+     */
+    public List<StatisticsSummaryVO> getStatisticsSummaryList(StatisticsSummaryParam param){
+
+        if(StringUtils.isBlank(param.getStartDate()) && StringUtils.isBlank(param.getEndDate())){
+            throw new CustomException("搜索必须要一个时间范围!");
+        }
+        param.setEndDateQueryValue(addOneDay(param.getEndDate()));
+        if(null == param.getStatisticsType()){
+            throw new CustomException("请选择统计类型");
+        }
+        Map<Long,DeptFullPathVO> deptMp = new HashMap<>();
+        boolean byDept = param.getStatisticsType().equals(2);
+        //按照部门方式汇总
+        if(byDept){
+            if(null == param.getCompanyId()){
+                throw new CustomException("请选择公司");
+            }
+            List<DeptFullPathVO> deptInfoList = companyDeptMapper.getDeptFullPathList(param.getCompanyId());
+            deptMp = deptInfoList.stream().collect(Collectors.toMap(DeptFullPathVO::getDeptId, item -> item));
+            if(CollectionUtils.isEmpty(deptInfoList)){
+                throw new CustomException("该公司下没有部门");
+            }
+        }
+        functionStartPage();
+        List<StatisticsSummaryVO> res =  fsCourseTrafficLogMapper.getStatisticsSummaryList(param);
+        //获取配置流量比例
+        SysConfig config = iSysConfigService.selectConfigByConfigKey("statis.config");
+        JSONObject jsonObject = JSONObject.parseObject(config.getConfigValue());
+        float trafficPrice = jsonObject.getFloatValue("trafficPrice");
+
+        for (StatisticsSummaryVO item : res) {
+            item.formatTraffic();
+            item.calculateAmount(new BigDecimal(trafficPrice));
+            if(byDept){
+                item.setDeptName(deptMp.get(item.getDeptId()).getFullPath());
+            }
+        }
+        return res;
+    }
+    public static String addOneDay(String dateStr) {
+        // 将字符串解析为LocalDate对象
+        LocalDate date = LocalDate.parse(dateStr);
+
+        // 加一天
+        LocalDate newDate = date.plusDays(1);
+
+        // 转换回字符串
+        return newDate.toString();
+    }
+    protected void functionStartPage()
+    {
+        PageDomain pageDomain = TableSupport.buildPageRequest();
+        Integer pageNum = pageDomain.getPageNum();
+        Integer pageSize = pageDomain.getPageSize();
+        if (com.fs.common.utils.StringUtils.isNotNull(pageNum) && com.fs.common.utils.StringUtils.isNotNull(pageSize))
+        {
+            String orderBy = SqlUtil.escapeOrderBySql(pageDomain.getOrderBy());
+            Boolean reasonable = pageDomain.getReasonable();
+            PageHelper.startPage(pageNum, pageSize, orderBy).setReasonable(reasonable);
+        }
+    }
+
+    public List<StatisticsSummaryVO> getStatisticsSummaryListNotPage(StatisticsSummaryParam param){
+        if(StringUtils.isBlank(param.getStartDate()) && StringUtils.isBlank(param.getEndDate())){
+            throw new CustomException("必须要一个时间范围!");
+        }
+        param.setEndDateQueryValue(addOneDay(param.getEndDate()));
+        if(null == param.getStatisticsType()){
+            throw new CustomException("请选择统计类型");
+        }
+        Map<Long,DeptFullPathVO> deptMp = new HashMap<>();
+        boolean byDept = param.getStatisticsType().equals(2);
+        //按照部门方式汇总
+        if(byDept){
+            if(null == param.getCompanyId()){
+                throw new CustomException("请选择公司");
+            }
+            List<DeptFullPathVO> deptInfoList = companyDeptMapper.getDeptFullPathList(param.getCompanyId());
+            deptMp = deptInfoList.stream().collect(Collectors.toMap(DeptFullPathVO::getDeptId, item -> item));
+            if(CollectionUtils.isEmpty(deptInfoList)){
+                throw new CustomException("该公司下没有部门");
+            }
+        }
+
+        List<StatisticsSummaryVO> res =  fsCourseTrafficLogMapper.getStatisticsSummaryList(param);
+        //获取配置流量比例
+        SysConfig config = iSysConfigService.selectConfigByConfigKey("statis.config");
+        JSONObject jsonObject = JSONObject.parseObject(config.getConfigValue());
+        float trafficPrice = jsonObject.getFloatValue("trafficPrice");
+
+        for (StatisticsSummaryVO item : res) {
+            item.formatTraffic();
+            item.calculateAmount(new BigDecimal(trafficPrice));
+            if(byDept){
+                item.setDeptName(deptMp.get(item.getDeptId()).getFullPath());
+            }
+        }
+        return res;
+    }
 }

+ 17 - 0
fs-service/src/main/java/com/fs/course/vo/DeptFullPathVO.java

@@ -0,0 +1,17 @@
+package com.fs.course.vo;
+
+import lombok.Data;
+
+/**
+ * @author MixLiu
+ * @date 2025/11/11 上午10:44)
+ */
+@Data
+public class DeptFullPathVO {
+    //部门id
+    private Long deptId;
+    //部门名称
+    private String deptName;
+    //部门全路径
+    private String fullPath;
+}

+ 63 - 0
fs-service/src/main/java/com/fs/course/vo/StatisticsSummaryVO.java

@@ -0,0 +1,63 @@
+package com.fs.course.vo;
+
+import com.fs.common.annotation.Excel;
+import lombok.Data;
+
+import java.math.BigDecimal;
+
+/**
+ * @author MixLiu
+ * @date 2025/11/10 下午3:47)
+ */
+@Data
+public class StatisticsSummaryVO {
+
+    private Long companyId;
+
+    @Excel(name = "公司名称")
+    private String companyName;
+
+    private Long deptId;
+
+    @Excel(name = "部门名称")
+    private String deptName;
+
+    @Excel(name = "统计日期范围")
+    private String dateRange;
+
+    // 原始字节数(不用于导出)
+    private Long totalInternetTraffic;
+
+    @Excel(name = "总流量 (GB)")
+    private String formattedTotalTraffic;
+
+    @Excel(name = "金额(元)")
+    private String totalAmount;
+
+    // 工具方法:根据 totalInternetTraffic 自动计算 formattedTotalTraffic
+    public void formatTraffic() {
+        if (this.totalInternetTraffic == null) {
+            this.formattedTotalTraffic = "0.0000 GB";
+        } else {
+            BigDecimal gb = new BigDecimal(this.totalInternetTraffic.doubleValue()).divide(new BigDecimal(1024 * 1024 * 1024)).setScale(4, BigDecimal.ROUND_HALF_UP);
+//            double gb = this.totalInternetTraffic.doubleValue() / (1024 * 1024 * 1024);
+            this.formattedTotalTraffic = String.format("%.4f GB", gb);
+        }
+    }
+
+    /**
+     * 根据 totalInternetTraffic 计算金额值
+     * @param proportion
+     */
+    public void calculateAmount(BigDecimal proportion){
+        if (this.totalInternetTraffic == null) {
+            this.totalAmount = "0.00(元)";
+        } else {
+            BigDecimal gb = new BigDecimal(this.totalInternetTraffic.doubleValue()).divide(new BigDecimal(1024 * 1024 * 1024)).setScale(4, BigDecimal.ROUND_HALF_UP);
+            BigDecimal bigDecimal = proportion.multiply(gb).setScale(2, BigDecimal.ROUND_HALF_UP);
+            this.totalAmount = String.format("%.2f(元)",bigDecimal);
+        }
+    }
+
+
+}

+ 28 - 0
fs-service/src/main/resources/mapper/company/CompanyDeptMapper.xml

@@ -198,4 +198,32 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         )
         SELECT dept_id FROM cte ORDER BY level;
     </select>
+
+    <select id="getDeptFullPathList" parameterType="Long" resultType="com.fs.course.vo.DeptFullPathVO">
+        WITH RECURSIVE dept_path AS (
+            SELECT
+                dept_id,
+                parent_id,
+                dept_name,
+                company_id,
+                dept_name AS full_path
+            FROM company_dept
+            WHERE parent_id = 0
+              AND company_id = #{companyId}
+            UNION ALL
+            SELECT
+                c.dept_id,
+                c.parent_id,
+                c.dept_name,
+                c.company_id,
+                CONCAT(d.full_path, '_', c.dept_name) AS full_path
+            FROM company_dept c
+                     INNER JOIN dept_path d
+                                ON c.parent_id = d.dept_id
+            WHERE c.company_id =  #{companyId}
+        )
+        SELECT dept_id, dept_name, full_path
+        FROM dept_path
+        ORDER BY full_path
+    </select>
 </mapper>