feat(oa): 添加仪表板功能模块
- 新增仪表板控制器、服务接口及其实现类- 创建仪表板相关的数据传输对象(DTO) - 设计并实现仪表板数据的获取和统计逻辑 - 添加仪表板数据的数据库访问接口和映射文件
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
package com.gear.oa.controller;
|
||||
|
||||
import com.gear.common.core.controller.BaseController;
|
||||
import com.gear.common.core.domain.R;
|
||||
import com.gear.oa.domain.vo.dashboard.DashboardOverviewVO;
|
||||
import com.gear.oa.service.IGearDashboardService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
/**
|
||||
* 仪表板控制器
|
||||
*
|
||||
* @author Joshi
|
||||
* @date 2025-09-17
|
||||
*/
|
||||
@RequiredArgsConstructor
|
||||
@RestController
|
||||
@RequestMapping("/oa/dashboard")
|
||||
public class GearDashboardController extends BaseController {
|
||||
|
||||
private final IGearDashboardService dashboardService;
|
||||
|
||||
/**
|
||||
* 获取仪表板总览数据
|
||||
*
|
||||
* @return 仪表板总览数据
|
||||
*/
|
||||
@GetMapping("/overview")
|
||||
public R<DashboardOverviewVO> getDashboardOverview() {
|
||||
DashboardOverviewVO overview = dashboardService.getDashboardOverview();
|
||||
return R.ok(overview);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
package com.gear.oa.domain.vo.dashboard;
|
||||
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 日趋势数据VO
|
||||
*
|
||||
* @author Joshi
|
||||
* @date 2025-09-17
|
||||
*/
|
||||
@Data
|
||||
public class DailyTrendVO {
|
||||
|
||||
/**
|
||||
* 日期(格式:MM-dd)
|
||||
*/
|
||||
private String date;
|
||||
|
||||
/**
|
||||
* 数值(订单数量或薪资金额)
|
||||
*/
|
||||
private BigDecimal value;
|
||||
|
||||
/**
|
||||
* 标签(用于显示)
|
||||
*/
|
||||
private String label;
|
||||
|
||||
public DailyTrendVO() {}
|
||||
|
||||
public DailyTrendVO(String date, BigDecimal value) {
|
||||
this.date = date;
|
||||
this.value = value;
|
||||
this.label = date;
|
||||
}
|
||||
|
||||
public DailyTrendVO(String date, BigDecimal value, String label) {
|
||||
this.date = date;
|
||||
this.value = value;
|
||||
this.label = label;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.gear.oa.domain.vo.dashboard;
|
||||
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 仪表板总览VO
|
||||
*
|
||||
* @author Joshi
|
||||
* @date 2025-09-17
|
||||
*/
|
||||
@Data
|
||||
public class DashboardOverviewVO {
|
||||
|
||||
/**
|
||||
* 订单统计数据
|
||||
*/
|
||||
private OrderStatisticsVO orderStatistics;
|
||||
|
||||
/**
|
||||
* 薪资支出统计数据
|
||||
*/
|
||||
private SalaryStatisticsVO salaryStatistics;
|
||||
|
||||
/**
|
||||
* 库存排行数据
|
||||
*/
|
||||
private List<StockRankingVO> stockRanking;
|
||||
|
||||
/**
|
||||
* 其他统计数据
|
||||
*/
|
||||
private OtherStatisticsVO otherStatistics;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.gear.oa.domain.vo.dashboard;
|
||||
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 订单统计VO
|
||||
*
|
||||
* @author Joshi
|
||||
* @date 2025-09-17
|
||||
*/
|
||||
@Data
|
||||
public class OrderStatisticsVO {
|
||||
|
||||
/**
|
||||
* 今日订单数
|
||||
*/
|
||||
private Integer todayOrderCount;
|
||||
|
||||
/**
|
||||
* 本周订单数
|
||||
*/
|
||||
private Integer weekOrderCount;
|
||||
|
||||
/**
|
||||
* 本月订单数
|
||||
*/
|
||||
private Integer monthOrderCount;
|
||||
|
||||
/**
|
||||
* 近一周订单数量趋势(7天数据)
|
||||
*/
|
||||
private List<DailyTrendVO> weeklyTrend;
|
||||
|
||||
/**
|
||||
* 订单总金额(本周)
|
||||
*/
|
||||
private BigDecimal weekTotalAmount;
|
||||
|
||||
/**
|
||||
* 与上周对比增长率
|
||||
*/
|
||||
private BigDecimal growthRate;
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
package com.gear.oa.domain.vo.dashboard;
|
||||
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 其他统计数据VO
|
||||
*
|
||||
* @author Joshi
|
||||
* @date 2025-09-17
|
||||
*/
|
||||
@Data
|
||||
public class OtherStatisticsVO {
|
||||
|
||||
/**
|
||||
* 活跃客户数
|
||||
*/
|
||||
private Integer activeCustomerCount;
|
||||
|
||||
/**
|
||||
* 待处理订单数
|
||||
*/
|
||||
private Integer pendingOrderCount;
|
||||
|
||||
/**
|
||||
* 库存预警数量
|
||||
*/
|
||||
private Integer lowStockCount;
|
||||
|
||||
/**
|
||||
* 本月营收
|
||||
*/
|
||||
private BigDecimal monthlyRevenue;
|
||||
|
||||
/**
|
||||
* 员工总数
|
||||
*/
|
||||
private Integer totalEmployeeCount;
|
||||
|
||||
/**
|
||||
* 今日出勤率
|
||||
*/
|
||||
private BigDecimal todayAttendanceRate;
|
||||
|
||||
/**
|
||||
* 产品总数
|
||||
*/
|
||||
private Integer totalProductCount;
|
||||
|
||||
/**
|
||||
* 供应商总数
|
||||
*/
|
||||
private Integer totalSupplierCount;
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
package com.gear.oa.domain.vo.dashboard;
|
||||
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 薪资统计VO
|
||||
*
|
||||
* @author Joshi
|
||||
* @date 2025-09-17
|
||||
*/
|
||||
@Data
|
||||
public class SalaryStatisticsVO {
|
||||
|
||||
/**
|
||||
* 今日薪资支出
|
||||
*/
|
||||
private BigDecimal todaySalary;
|
||||
|
||||
/**
|
||||
* 本周薪资支出
|
||||
*/
|
||||
private BigDecimal weekSalary;
|
||||
|
||||
/**
|
||||
* 本月薪资支出
|
||||
*/
|
||||
private BigDecimal monthSalary;
|
||||
|
||||
/**
|
||||
* 近一周薪资支出趋势(7天数据)
|
||||
*/
|
||||
private List<DailyTrendVO> weeklyTrend;
|
||||
|
||||
/**
|
||||
* 与上周对比增长率
|
||||
*/
|
||||
private BigDecimal growthRate;
|
||||
|
||||
/**
|
||||
* 平均日薪资支出
|
||||
*/
|
||||
private BigDecimal avgDailySalary;
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
package com.gear.oa.domain.vo.dashboard;
|
||||
|
||||
import lombok.Data;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
/**
|
||||
* 库存排行VO
|
||||
*
|
||||
* @author Joshi
|
||||
* @date 2025-09-17
|
||||
*/
|
||||
@Data
|
||||
public class StockRankingVO {
|
||||
|
||||
/**
|
||||
* 排名
|
||||
*/
|
||||
private Integer rank;
|
||||
|
||||
/**
|
||||
* 物品ID
|
||||
*/
|
||||
private Long itemId;
|
||||
|
||||
/**
|
||||
* 物品名称
|
||||
*/
|
||||
private String itemName;
|
||||
|
||||
/**
|
||||
* 物品编号
|
||||
*/
|
||||
private String itemCode;
|
||||
|
||||
/**
|
||||
* 物品类型(raw_material/product)
|
||||
*/
|
||||
private String itemType;
|
||||
|
||||
/**
|
||||
* 物品类型名称
|
||||
*/
|
||||
private String itemTypeName;
|
||||
|
||||
/**
|
||||
* 库存数量
|
||||
*/
|
||||
private BigDecimal quantity;
|
||||
|
||||
/**
|
||||
* 单位
|
||||
*/
|
||||
private String unit;
|
||||
|
||||
/**
|
||||
* 仓库名称
|
||||
*/
|
||||
private String warehouseName;
|
||||
|
||||
/**
|
||||
* 产品分类(如果是产品)
|
||||
*/
|
||||
private String categoryName;
|
||||
|
||||
/**
|
||||
* 负责人
|
||||
*/
|
||||
private String owner;
|
||||
}
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.gear.oa.mapper;
|
||||
|
||||
import com.gear.oa.domain.vo.dashboard.StockRankingVO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 仪表板数据访问层
|
||||
*
|
||||
* @author Joshi
|
||||
* @date 2025-09-17
|
||||
*/
|
||||
@Mapper
|
||||
public interface GearDashboardMapper {
|
||||
|
||||
/**
|
||||
* 获取今日订单数
|
||||
*/
|
||||
Integer getTodayOrderCount();
|
||||
|
||||
/**
|
||||
* 获取本周订单数
|
||||
*/
|
||||
Integer getWeekOrderCount();
|
||||
|
||||
/**
|
||||
* 获取本月订单数
|
||||
*/
|
||||
Integer getMonthOrderCount();
|
||||
|
||||
/**
|
||||
* 获取上周订单数
|
||||
*/
|
||||
Integer getLastWeekOrderCount();
|
||||
|
||||
/**
|
||||
* 获取本周订单总金额
|
||||
*/
|
||||
BigDecimal getWeekOrderAmount();
|
||||
|
||||
/**
|
||||
* 获取近一周订单趋势
|
||||
*/
|
||||
List<Map<String, Object>> getWeeklyOrderTrend();
|
||||
|
||||
/**
|
||||
* 获取今日薪资支出
|
||||
*/
|
||||
BigDecimal getTodaySalary();
|
||||
|
||||
/**
|
||||
* 获取本周薪资支出
|
||||
*/
|
||||
BigDecimal getWeekSalary();
|
||||
|
||||
/**
|
||||
* 获取本月薪资支出
|
||||
*/
|
||||
BigDecimal getMonthSalary();
|
||||
|
||||
/**
|
||||
* 获取上周薪资支出
|
||||
*/
|
||||
BigDecimal getLastWeekSalary();
|
||||
|
||||
/**
|
||||
* 获取近一周薪资趋势
|
||||
*/
|
||||
List<Map<String, Object>> getWeeklySalaryTrend();
|
||||
|
||||
/**
|
||||
* 获取库存排行(前10)
|
||||
*/
|
||||
List<StockRankingVO> getStockRanking();
|
||||
|
||||
/**
|
||||
* 获取活跃客户数
|
||||
*/
|
||||
Integer getActiveCustomerCount();
|
||||
|
||||
/**
|
||||
* 获取待处理订单数
|
||||
*/
|
||||
Integer getPendingOrderCount();
|
||||
|
||||
/**
|
||||
* 获取库存预警数量
|
||||
*/
|
||||
Integer getLowStockCount();
|
||||
|
||||
/**
|
||||
* 获取本月营收
|
||||
*/
|
||||
BigDecimal getMonthlyRevenue();
|
||||
|
||||
/**
|
||||
* 获取员工总数
|
||||
*/
|
||||
Integer getTotalEmployeeCount();
|
||||
|
||||
/**
|
||||
* 获取今日出勤率
|
||||
*/
|
||||
BigDecimal getTodayAttendanceRate();
|
||||
|
||||
/**
|
||||
* 获取产品总数
|
||||
*/
|
||||
Integer getTotalProductCount();
|
||||
|
||||
/**
|
||||
* 获取供应商总数
|
||||
*/
|
||||
Integer getTotalSupplierCount();
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.gear.oa.service;
|
||||
|
||||
import com.gear.oa.domain.vo.dashboard.DashboardOverviewVO;
|
||||
|
||||
/**
|
||||
* 仪表板服务接口
|
||||
*
|
||||
* @author Joshi
|
||||
* @date 2025-09-17
|
||||
*/
|
||||
public interface IGearDashboardService {
|
||||
|
||||
/**
|
||||
* 获取仪表板总览数据
|
||||
*
|
||||
* @return 仪表板总览数据
|
||||
*/
|
||||
DashboardOverviewVO getDashboardOverview();
|
||||
}
|
||||
@@ -0,0 +1,211 @@
|
||||
package com.gear.oa.service.impl;
|
||||
|
||||
import com.gear.oa.domain.vo.dashboard.*;
|
||||
import com.gear.oa.mapper.GearDashboardMapper;
|
||||
import com.gear.oa.service.IGearDashboardService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* 仪表板服务实现类
|
||||
*
|
||||
* @author Joshi
|
||||
* @date 2025-09-17
|
||||
*/
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class GearDashboardServiceImpl implements IGearDashboardService {
|
||||
|
||||
private final GearDashboardMapper dashboardMapper;
|
||||
|
||||
@Override
|
||||
public DashboardOverviewVO getDashboardOverview() {
|
||||
DashboardOverviewVO overview = new DashboardOverviewVO();
|
||||
|
||||
// 获取订单统计数据
|
||||
overview.setOrderStatistics(getOrderStatistics());
|
||||
|
||||
// 获取薪资统计数据
|
||||
overview.setSalaryStatistics(getSalaryStatistics());
|
||||
|
||||
// 获取库存排行数据
|
||||
overview.setStockRanking(getStockRanking());
|
||||
|
||||
// 获取其他统计数据
|
||||
overview.setOtherStatistics(getOtherStatistics());
|
||||
|
||||
return overview;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取订单统计数据
|
||||
*/
|
||||
private OrderStatisticsVO getOrderStatistics() {
|
||||
OrderStatisticsVO statistics = new OrderStatisticsVO();
|
||||
|
||||
// 获取今日、本周、本月订单数
|
||||
statistics.setTodayOrderCount(dashboardMapper.getTodayOrderCount());
|
||||
statistics.setWeekOrderCount(dashboardMapper.getWeekOrderCount());
|
||||
statistics.setMonthOrderCount(dashboardMapper.getMonthOrderCount());
|
||||
|
||||
// 获取本周订单总金额
|
||||
statistics.setWeekTotalAmount(dashboardMapper.getWeekOrderAmount());
|
||||
|
||||
// 获取近一周订单趋势
|
||||
List<Map<String, Object>> weeklyData = dashboardMapper.getWeeklyOrderTrend();
|
||||
statistics.setWeeklyTrend(convertToTrendList(weeklyData));
|
||||
|
||||
// 计算增长率
|
||||
Integer lastWeekCount = dashboardMapper.getLastWeekOrderCount();
|
||||
statistics.setGrowthRate(calculateGrowthRate(statistics.getWeekOrderCount(), lastWeekCount));
|
||||
|
||||
return statistics;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取薪资统计数据
|
||||
*/
|
||||
private SalaryStatisticsVO getSalaryStatistics() {
|
||||
SalaryStatisticsVO statistics = new SalaryStatisticsVO();
|
||||
|
||||
// 获取今日、本周、本月薪资支出
|
||||
statistics.setTodaySalary(dashboardMapper.getTodaySalary());
|
||||
statistics.setWeekSalary(dashboardMapper.getWeekSalary());
|
||||
statistics.setMonthSalary(dashboardMapper.getMonthSalary());
|
||||
|
||||
// 获取近一周薪资趋势
|
||||
List<Map<String, Object>> weeklyData = dashboardMapper.getWeeklySalaryTrend();
|
||||
statistics.setWeeklyTrend(convertToTrendList(weeklyData));
|
||||
|
||||
// 计算增长率
|
||||
BigDecimal lastWeekSalary = dashboardMapper.getLastWeekSalary();
|
||||
statistics.setGrowthRate(calculateGrowthRate(statistics.getWeekSalary(), lastWeekSalary));
|
||||
|
||||
// 计算平均日薪资
|
||||
if (statistics.getWeekSalary() != null) {
|
||||
statistics.setAvgDailySalary(statistics.getWeekSalary().divide(new BigDecimal(7), 2, RoundingMode.HALF_UP));
|
||||
}
|
||||
|
||||
return statistics;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取库存排行数据
|
||||
*/
|
||||
private List<StockRankingVO> getStockRanking() {
|
||||
List<StockRankingVO> rankings = dashboardMapper.getStockRanking();
|
||||
|
||||
// 设置排名
|
||||
for (int i = 0; i < rankings.size(); i++) {
|
||||
rankings.get(i).setRank(i + 1);
|
||||
|
||||
// 设置物品类型名称
|
||||
String itemType = rankings.get(i).getItemType();
|
||||
if ("product".equals(itemType)) {
|
||||
rankings.get(i).setItemTypeName("产品");
|
||||
} else if ("raw_material".equals(itemType)) {
|
||||
rankings.get(i).setItemTypeName("原材料");
|
||||
}
|
||||
}
|
||||
|
||||
return rankings;
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取其他统计数据
|
||||
*/
|
||||
private OtherStatisticsVO getOtherStatistics() {
|
||||
OtherStatisticsVO statistics = new OtherStatisticsVO();
|
||||
|
||||
statistics.setActiveCustomerCount(dashboardMapper.getActiveCustomerCount());
|
||||
statistics.setPendingOrderCount(dashboardMapper.getPendingOrderCount());
|
||||
statistics.setLowStockCount(dashboardMapper.getLowStockCount());
|
||||
statistics.setMonthlyRevenue(dashboardMapper.getMonthlyRevenue());
|
||||
statistics.setTotalEmployeeCount(dashboardMapper.getTotalEmployeeCount());
|
||||
statistics.setTodayAttendanceRate(dashboardMapper.getTodayAttendanceRate());
|
||||
statistics.setTotalProductCount(dashboardMapper.getTotalProductCount());
|
||||
statistics.setTotalSupplierCount(dashboardMapper.getTotalSupplierCount());
|
||||
|
||||
return statistics;
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换趋势数据
|
||||
*/
|
||||
private List<DailyTrendVO> convertToTrendList(List<Map<String, Object>> data) {
|
||||
List<DailyTrendVO> trends = new ArrayList<>();
|
||||
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("MM-dd");
|
||||
|
||||
for (Map<String, Object> item : data) {
|
||||
DailyTrendVO trend = new DailyTrendVO();
|
||||
|
||||
// 处理日期
|
||||
Object dateObj = item.get("date");
|
||||
if (dateObj instanceof LocalDate) {
|
||||
trend.setDate(((LocalDate) dateObj).format(formatter));
|
||||
} else if (dateObj instanceof String) {
|
||||
trend.setDate((String) dateObj);
|
||||
}
|
||||
|
||||
// 处理数值
|
||||
Object valueObj = item.get("value");
|
||||
if (valueObj instanceof BigDecimal) {
|
||||
trend.setValue((BigDecimal) valueObj);
|
||||
} else if (valueObj instanceof Integer) {
|
||||
trend.setValue(new BigDecimal((Integer) valueObj));
|
||||
} else if (valueObj instanceof Long) {
|
||||
trend.setValue(new BigDecimal((Long) valueObj));
|
||||
}
|
||||
|
||||
trend.setLabel(trend.getDate());
|
||||
trends.add(trend);
|
||||
}
|
||||
|
||||
return trends;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算增长率
|
||||
*/
|
||||
private BigDecimal calculateGrowthRate(Object current, Object previous) {
|
||||
if (current == null || previous == null) {
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
BigDecimal currentValue = convertToBigDecimal(current);
|
||||
BigDecimal previousValue = convertToBigDecimal(previous);
|
||||
|
||||
if (previousValue.compareTo(BigDecimal.ZERO) == 0) {
|
||||
return currentValue.compareTo(BigDecimal.ZERO) > 0 ? new BigDecimal(100) : BigDecimal.ZERO;
|
||||
}
|
||||
|
||||
return currentValue.subtract(previousValue)
|
||||
.divide(previousValue, 4, RoundingMode.HALF_UP)
|
||||
.multiply(new BigDecimal(100))
|
||||
.setScale(2, RoundingMode.HALF_UP);
|
||||
}
|
||||
|
||||
/**
|
||||
* 转换为BigDecimal
|
||||
*/
|
||||
private BigDecimal convertToBigDecimal(Object value) {
|
||||
if (value instanceof BigDecimal) {
|
||||
return (BigDecimal) value;
|
||||
} else if (value instanceof Integer) {
|
||||
return new BigDecimal((Integer) value);
|
||||
} else if (value instanceof Long) {
|
||||
return new BigDecimal((Long) value);
|
||||
} else if (value instanceof String) {
|
||||
return new BigDecimal((String) value);
|
||||
}
|
||||
return BigDecimal.ZERO;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user