receivablesRanKing(BiTimeUtil.BiTimeEntity biTimeEntity);
+}
diff --git a/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/framework/package-info.java b/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/framework/package-info.java
new file mode 100644
index 000000000..8b004cf8c
--- /dev/null
+++ b/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/framework/package-info.java
@@ -0,0 +1,6 @@
+/**
+ * 属于 bi 模块的 framework 封装
+ *
+ * @author 芋道源码
+ */
+package cn.iocoder.yudao.module.bi.framework;
diff --git a/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/framework/web/config/BiWebConfiguration.java b/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/framework/web/config/BiWebConfiguration.java
new file mode 100644
index 000000000..ad10cdccd
--- /dev/null
+++ b/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/framework/web/config/BiWebConfiguration.java
@@ -0,0 +1,24 @@
+package cn.iocoder.yudao.module.bi.framework.web.config;
+
+import cn.iocoder.yudao.framework.swagger.config.YudaoSwaggerAutoConfiguration;
+import org.springdoc.core.models.GroupedOpenApi;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * bi 模块的 web 组件的 Configuration
+ *
+ * @author 芋道源码
+ */
+@Configuration(proxyBeanMethods = false)
+public class BiWebConfiguration {
+
+ /**
+ * bi 模块的 API 分组
+ */
+ @Bean
+ public GroupedOpenApi biGroupedOpenApi() {
+ return YudaoSwaggerAutoConfiguration.buildGroupedOpenApi("bi");
+ }
+
+}
diff --git a/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/framework/web/package-info.java b/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/framework/web/package-info.java
new file mode 100644
index 000000000..e17abbb1c
--- /dev/null
+++ b/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/framework/web/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * bi 模块的 web 配置
+ */
+package cn.iocoder.yudao.module.bi.framework.web;
diff --git a/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/package-info.java b/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/package-info.java
new file mode 100644
index 000000000..b45b8458d
--- /dev/null
+++ b/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/package-info.java
@@ -0,0 +1,7 @@
+/**
+ * crm 包下,商业智能(Business Intelligence)。
+ * 例如说:报表、图表、数据分析等等
+ *
+ * 1. Controller URL:以 /bi/ 开头,避免和其它 Module 冲突
+ */
+package cn.iocoder.yudao.module.bi;
\ No newline at end of file
diff --git a/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/service/ranking/BiRankingService.java b/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/service/ranking/BiRankingService.java
new file mode 100644
index 000000000..98f1346de
--- /dev/null
+++ b/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/service/ranking/BiRankingService.java
@@ -0,0 +1,30 @@
+package cn.iocoder.yudao.module.bi.service.ranking;
+
+import cn.iocoder.yudao.module.bi.controller.admin.ranking.vo.BiContractRanKingRespVO;
+import cn.iocoder.yudao.module.bi.controller.admin.ranking.vo.BiRankReqVO;
+import cn.iocoder.yudao.module.bi.controller.admin.ranking.vo.BiReceivablesRanKingRespVO;
+
+import java.util.List;
+
+/**
+ * BI 排行榜 Service 接口
+ *
+ * @author anhaohao
+ */
+public interface BiRankingService {
+ /**
+ * 合同金额排行榜
+ *
+ * @param biRankReqVO 参数
+ * @return List
+ */
+ List contractRanKing(BiRankReqVO biRankReqVO);
+
+ /**
+ * 回款金额排行榜
+ *
+ * @param biRankReqVO 参数
+ * @return List
+ */
+ List receivablesRanKing(BiRankReqVO biRankReqVO);
+}
diff --git a/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/service/ranking/BiRankingServiceImpl.java b/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/service/ranking/BiRankingServiceImpl.java
new file mode 100644
index 000000000..1ed468547
--- /dev/null
+++ b/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/service/ranking/BiRankingServiceImpl.java
@@ -0,0 +1,53 @@
+package cn.iocoder.yudao.module.bi.service.ranking;
+
+import cn.iocoder.yudao.module.bi.controller.admin.ranking.vo.BiContractRanKingRespVO;
+import cn.iocoder.yudao.module.bi.controller.admin.ranking.vo.BiParams;
+import cn.iocoder.yudao.module.bi.controller.admin.ranking.vo.BiRankReqVO;
+import cn.iocoder.yudao.module.bi.controller.admin.ranking.vo.BiReceivablesRanKingRespVO;
+import cn.iocoder.yudao.module.bi.dal.mysql.BiRankingMapper;
+import cn.iocoder.yudao.module.bi.util.BiTimeUtil;
+import jakarta.annotation.Resource;
+import org.springframework.stereotype.Service;
+import org.springframework.validation.annotation.Validated;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * @author anhaohao
+ */
+@Service(value = "biRankService")
+@Validated
+public class BiRankingServiceImpl implements BiRankingService {
+
+ @Resource
+ private BiRankingMapper biRankingMapper;
+
+ @Override
+ public List contractRanKing(BiRankReqVO biRankReqVO) {
+ BiParams biParams = new BiParams();
+ biParams.setType(biRankReqVO.getType());
+ biParams.setDeptId(biRankReqVO.getDeptId());
+ biParams.setIsUser(0);
+ BiTimeUtil.BiTimeEntity biTimeEntity = BiTimeUtil.analyzeType(biParams);
+ List userIds = biTimeEntity.getUserIds();
+ if (userIds.isEmpty()) {
+ return new ArrayList<>();
+ }
+ return biRankingMapper.contractRanKing(biTimeEntity);
+ }
+
+ @Override
+ public List receivablesRanKing(BiRankReqVO biRankReqVO) {
+ BiParams biParams = new BiParams();
+ biParams.setType(biRankReqVO.getType());
+ biParams.setDeptId(biRankReqVO.getDeptId());
+ biParams.setIsUser(0);
+ BiTimeUtil.BiTimeEntity biTimeEntity = BiTimeUtil.analyzeType(biParams);
+ List userIds = biTimeEntity.getUserIds();
+ if (userIds.isEmpty()) {
+ return new ArrayList<>();
+ }
+ return biRankingMapper.receivablesRanKing(biTimeEntity);
+ }
+}
diff --git a/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/util/BiTimeUtil.java b/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/util/BiTimeUtil.java
new file mode 100644
index 000000000..b1b68aea2
--- /dev/null
+++ b/yudao-module-crm/yudao-module-bi-biz/src/main/java/cn/iocoder/yudao/module/bi/util/BiTimeUtil.java
@@ -0,0 +1,249 @@
+package cn.iocoder.yudao.module.bi.util;
+
+import cn.hutool.core.date.DateUnit;
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.extra.spring.SpringUtil;
+import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
+import cn.iocoder.yudao.module.bi.controller.admin.ranking.vo.BiParams;
+import cn.iocoder.yudao.module.system.api.dept.DeptApi;
+import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
+import cn.iocoder.yudao.module.system.api.user.AdminUserApi;
+import cn.iocoder.yudao.module.system.api.user.dto.AdminUserRespDTO;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * bi时间工具类
+ *
+ * @author anhaohao
+ */
+public class BiTimeUtil {
+
+ public static BiTimeEntity analyzeType(BiParams biParams) {
+ // 解析时间
+ BiTimeEntity biTimeEntity = analyzeTime(biParams);
+ // 解析权限
+ biTimeEntity.setUserIds(analyzeAuth(biParams));
+ return biTimeEntity;
+ }
+
+ /**
+ * 解析权限
+ *
+ * @param biParams bi参数
+ * @return List
+ */
+ public static List analyzeAuth(BiParams biParams) {
+ List userIdList = new ArrayList<>();
+ Long deptId = biParams.getDeptId();
+ Long userId = biParams.getUserId();
+ Integer isUser = biParams.getIsUser();
+ // 获取部门和用户的api
+ DeptApi deptApi = SpringUtil.getBean("deptApiImpl");
+ AdminUserApi adminUserApi = SpringUtil.getBean("adminUserApiImpl");
+ // 0.部门 1.用户
+ if (isUser == 0) {
+ if (deptId == null) {
+ deptId = adminUserApi.getUser(SecurityFrameworkUtils.getLoginUserId()).getDeptId();
+ }
+ List childDeptList = deptApi.getChildDeptList(deptId);
+ List deptIds = new ArrayList<>();
+ deptIds.add(deptId);
+ if (childDeptList != null && !childDeptList.isEmpty()) {
+ for (DeptRespDTO deptRespDTO : childDeptList) {
+ deptIds.add(deptRespDTO.getId());
+ }
+ }
+ // 获取部门下的用户
+ adminUserApi.getUserListByDeptIds(deptIds).forEach(adminUserRespDTO -> userIdList.add(adminUserRespDTO.getId()));
+ } else {
+ if (userId == null) {
+ List userListBySubordinate = adminUserApi.getUserListBySubordinate(SecurityFrameworkUtils.getLoginUserId());
+ userListBySubordinate.forEach(adminUserRespDTO -> userIdList.add(adminUserRespDTO.getId()));
+ } else {
+ userIdList.add(userId);
+ }
+ }
+ return userIdList;
+ }
+
+
+ /**
+ * 解析时间
+ *
+ * @param biParams bi参数
+ * @return BiTimeEntity
+ */
+ public static BiTimeEntity analyzeTime(BiParams biParams) {
+ Date beginDate = DateUtil.date();
+ Date endDate = DateUtil.date();
+ int cycleNum = 12;
+ String sqlDateFormat = "%Y%m";
+ String dateFormat = "yyyyMM";
+ String type = biParams.getType();
+ String startTime = biParams.getStartTime();
+ String endTime = biParams.getEndTime();
+ if (StrUtil.isNotEmpty(type)) {
+ //1.今天 2.昨天 3.本周 4.上周 5.本月 6.上月 7.本季度 8.上季度 9.本年 10 上年
+ switch (type) {
+ case "1":
+ beginDate = DateUtil.beginOfDay(DateUtil.date());
+ endDate = DateUtil.endOfDay(DateUtil.date());
+ sqlDateFormat = "%Y%m%d";
+ dateFormat = "yyyyMMdd";
+ cycleNum = 1;
+ break;
+ case "2":
+ beginDate = DateUtil.beginOfDay(new Date(System.currentTimeMillis() - 86400000));
+ endDate = DateUtil.endOfDay(new Date(System.currentTimeMillis() - 86400000));
+ sqlDateFormat = "%Y%m%d";
+ dateFormat = "yyyyMMdd";
+ cycleNum = 1;
+ break;
+ case "3":
+ beginDate = DateUtil.beginOfWeek(DateUtil.date());
+ endDate = DateUtil.endOfWeek(DateUtil.date());
+ sqlDateFormat = "%Y%m%d";
+ dateFormat = "yyyyMMdd";
+ cycleNum = 7;
+ break;
+ case "4":
+ beginDate = DateUtil.beginOfWeek(DateUtil.offsetWeek(DateUtil.date(), -1));
+ endDate = DateUtil.endOfWeek(DateUtil.offsetWeek(DateUtil.date(), -1));
+ sqlDateFormat = "%Y%m%d";
+ dateFormat = "yyyyMMdd";
+ cycleNum = 7;
+ break;
+ case "5":
+ beginDate = DateUtil.beginOfMonth(DateUtil.date());
+ endDate = DateUtil.endOfMonth(DateUtil.date());
+ sqlDateFormat = "%Y%m%d";
+ dateFormat = "yyyyMMdd";
+ cycleNum = (int) DateUtil.between(beginDate, endDate, DateUnit.DAY) + 1;
+ break;
+ case "6":
+ beginDate = DateUtil.beginOfMonth(DateUtil.offsetMonth(DateUtil.date(), -1));
+ endDate = DateUtil.endOfMonth(DateUtil.offsetMonth(DateUtil.date(), -1));
+ sqlDateFormat = "%Y%m%d";
+ dateFormat = "yyyyMMdd";
+ cycleNum = (int) DateUtil.between(beginDate, endDate, DateUnit.DAY) + 1;
+ break;
+ case "7":
+ beginDate = DateUtil.beginOfQuarter(DateUtil.date());
+ endDate = DateUtil.endOfQuarter(DateUtil.date());
+ cycleNum = 3;
+ break;
+ case "8":
+ beginDate = DateUtil.beginOfQuarter(DateUtil.offsetMonth(DateUtil.date(), -3));
+ endDate = DateUtil.endOfQuarter(DateUtil.offsetMonth(DateUtil.date(), -3));
+ cycleNum = 3;
+ break;
+ case "9":
+ beginDate = DateUtil.beginOfYear(DateUtil.date());
+ endDate = DateUtil.endOfYear(DateUtil.date());
+ break;
+ case "10":
+ beginDate = DateUtil.beginOfYear(DateUtil.offsetMonth(DateUtil.date(), -12));
+ endDate = DateUtil.endOfYear(DateUtil.offsetMonth(DateUtil.date(), -12));
+ break;
+ default:
+ break;
+ }
+ } else if (StrUtil.isNotEmpty(startTime) && StrUtil.isNotEmpty(endTime)) {
+ Date start;
+ Date end;
+ if (startTime.length() == 6) {
+ start = DateUtil.parse(startTime, "yyyyMM");
+ end = DateUtil.endOfMonth(DateUtil.parse(endTime, "yyyyMM"));
+ } else {
+ start = DateUtil.parse(startTime);
+ end = DateUtil.parse(endTime);
+ }
+ Integer startMonth = Integer.valueOf(DateUtil.format(start, "yyyyMM"));
+ int endMonth = Integer.parseInt(DateUtil.format(end, "yyyyMM"));
+ if (startMonth.equals(endMonth)) {
+ sqlDateFormat = "%Y%m%d";
+ dateFormat = "yyyyMMdd";
+ long diffDay = DateUtil.between(start, end, DateUnit.DAY);
+ cycleNum = (int) diffDay + 1;
+ } else {
+ sqlDateFormat = "%Y%m";
+ dateFormat = "yyyyMM";
+ int diffYear = Integer.parseInt(Integer.toString(endMonth).substring(0, 4)) - Integer.parseInt(startMonth.toString().substring(0, 4));
+ int diffMonth = endMonth % 100 - startMonth % 100 + 1;
+ cycleNum = diffYear * 12 + diffMonth;
+ }
+ beginDate = start;
+ endDate = end;
+ }
+ Integer beginTime = Integer.valueOf(DateUtil.format(beginDate, dateFormat));
+ Integer finalTime = Integer.valueOf(DateUtil.format(endDate, dateFormat));
+ return new BiTimeEntity(sqlDateFormat, dateFormat, beginDate, endDate, cycleNum, beginTime, finalTime, new ArrayList<>());
+ }
+
+ @Data
+ @Accessors(chain = true)
+ public static class BiTimeEntity {
+ /**
+ * sql日期格式化
+ */
+ private String sqlDateFormat;
+
+ /**
+ * 日期格式化
+ */
+ private String dateFormat;
+
+ /**
+ * 开始时间
+ */
+ private Date beginDate;
+
+ /**
+ * 结束时间
+ */
+ private Date endDate;
+
+ /**
+ * 周期
+ */
+ private Integer cycleNum;
+
+ /**
+ * 开始时间 字符串格式 如20200101
+ */
+ private Integer beginTime;
+
+ /**
+ * 结束时间 字符串格式 如20200101
+ */
+ private Integer finalTime;
+
+ /**
+ * user列表
+ */
+ private List userIds = new ArrayList<>();
+ private Integer page;
+ private Integer limit;
+
+ public BiTimeEntity(String sqlDateFormat, String dateFormat, Date beginDate, Date endDate, Integer cycleNum, Integer beginTime, Integer finalTime, List userIds) {
+ this.sqlDateFormat = sqlDateFormat;
+ this.dateFormat = dateFormat;
+ this.beginDate = beginDate;
+ this.endDate = endDate;
+ this.cycleNum = cycleNum;
+ this.beginTime = beginTime;
+ this.finalTime = finalTime;
+ this.userIds = userIds;
+ }
+
+ public BiTimeEntity() {
+ }
+
+ }
+}
diff --git a/yudao-module-crm/yudao-module-bi-biz/src/main/resources/mapper/rank/BiRankingMapper.xml b/yudao-module-crm/yudao-module-bi-biz/src/main/resources/mapper/rank/BiRankingMapper.xml
new file mode 100644
index 000000000..9bee4fbaf
--- /dev/null
+++ b/yudao-module-crm/yudao-module-bi-biz/src/main/resources/mapper/rank/BiRankingMapper.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
diff --git a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java
index f8059fbb7..bdc3ba59b 100644
--- a/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java
+++ b/yudao-module-system/yudao-module-system-api/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApi.java
@@ -50,4 +50,12 @@ public interface DeptApi {
return CollectionUtils.convertMap(list, DeptRespDTO::getId);
}
+ /**
+ * 获得指定部门的所有子部门
+ *
+ * @param id 部门编号
+ * @return 子部门列表
+ */
+ List getChildDeptList(Long id);
+
}
diff --git a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java
index 76685cf31..00d79ceaa 100644
--- a/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java
+++ b/yudao-module-system/yudao-module-system-biz/src/main/java/cn/iocoder/yudao/module/system/api/dept/DeptApiImpl.java
@@ -4,6 +4,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.api.dept.dto.DeptRespDTO;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.service.dept.DeptService;
+import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import jakarta.annotation.Resource;
@@ -19,6 +20,7 @@ import java.util.List;
public class DeptApiImpl implements DeptApi {
@Resource
+ @Lazy // 延迟加载,解决相互依赖的问题
private DeptService deptService;
@Override
@@ -38,4 +40,11 @@ public class DeptApiImpl implements DeptApi {
deptService.validateDeptList(ids);
}
+ @Override
+ public List getChildDeptList(Long id) {
+ List childDeptList = deptService.getChildDeptList(id);
+ return BeanUtils.toBean(childDeptList, DeptRespDTO.class);
+ }
+
+
}
diff --git a/yudao-server/pom.xml b/yudao-server/pom.xml
index 85b284c3c..3caebe16c 100644
--- a/yudao-server/pom.xml
+++ b/yudao-server/pom.xml
@@ -97,6 +97,11 @@
yudao-module-crm-biz
${revision}
+
+ cn.iocoder.boot
+ yudao-module-bi-biz
+ ${revision}
+