diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/SalaryDashboardController.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/SalaryDashboardController.java new file mode 100644 index 0000000..06abefb --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/controller/SalaryDashboardController.java @@ -0,0 +1,31 @@ +package com.ruoyi.oa.controller; + + +import com.ruoyi.common.core.AjaxResult; +import com.ruoyi.common.core.controller.BaseController; +import com.ruoyi.common.core.domain.R; +import com.ruoyi.oa.domain.vo.SalaryDashboardVo; +import com.ruoyi.oa.service.ISalaryDashboardService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/oa/salary/dashboard") +public class SalaryDashboardController extends BaseController { + + @Autowired + private ISalaryDashboardService salaryDashboardService; + + @GetMapping + public RgetDashboardData( + @RequestParam(required = true) Long payYear, + @RequestParam(required = true) Long payMonth + ) { + return R.ok( + salaryDashboardService.getDashboardData(payYear, payMonth) + ); + } +} diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/OaEmployee.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/OaEmployee.java index 4cec3c3..4506da9 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/OaEmployee.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/OaEmployee.java @@ -31,6 +31,10 @@ public class OaEmployee extends BaseEntity { * 姓名 */ private String employeeName; + /** + * 部门ID + */ + private Long deptId; /** * 公司 */ diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/CardDataVo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/CardDataVo.java new file mode 100644 index 0000000..b313920 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/CardDataVo.java @@ -0,0 +1,16 @@ +package com.ruoyi.oa.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class CardDataVo { + private BigDecimal totalSalaryExpenditure; + private BigDecimal lastMonthTotalSalaryExpenditureRate; + private BigDecimal avgDepartmentExpenditure; + private BigDecimal lastMonthAvgDepartmentExpenditureRate; + private BigDecimal avgPersonSalary; + private BigDecimal lastMonthAvgPersonSalaryRate; + private BigDecimal yearOnYearGrowthRate; +} \ No newline at end of file diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/CardMetricsVo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/CardMetricsVo.java new file mode 100644 index 0000000..291eaf9 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/CardMetricsVo.java @@ -0,0 +1,13 @@ +package com.ruoyi.oa.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class CardMetricsVo { + private BigDecimal totalSalaryExpenditure; + private BigDecimal totalCompanyExpenditure; + private Integer employeeCount; + private Integer departmentCount; +} \ No newline at end of file diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/ChartDataVo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/ChartDataVo.java new file mode 100644 index 0000000..1a3c969 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/ChartDataVo.java @@ -0,0 +1,12 @@ +package com.ruoyi.oa.domain.vo; + +import lombok.Data; + +import java.util.List; + +@Data +public class ChartDataVo { + private List monthlyExpenditures; + private List salaryComponents; + private LineChartVo lineChartData; +} \ No newline at end of file diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/DepartmentStatVo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/DepartmentStatVo.java new file mode 100644 index 0000000..1396d49 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/DepartmentStatVo.java @@ -0,0 +1,15 @@ +package com.ruoyi.oa.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class DepartmentStatVo { + private Long deptId; + private String deptName; + private BigDecimal totalExpenditure; + private BigDecimal avgSalary; + private Integer employeeCount; + private BigDecimal yearOnYearGrowthRate; +} \ No newline at end of file diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/LineChartPointVo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/LineChartPointVo.java new file mode 100644 index 0000000..68cb5db --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/LineChartPointVo.java @@ -0,0 +1,12 @@ +package com.ruoyi.oa.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class LineChartPointVo { + private Integer month; + private BigDecimal totalExpenditure; + private BigDecimal avgSalary; +} \ No newline at end of file diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/LineChartVo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/LineChartVo.java new file mode 100644 index 0000000..d22571c --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/LineChartVo.java @@ -0,0 +1,26 @@ +package com.ruoyi.oa.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.List; + +@Data +public class LineChartVo { + private List months; + private List totalExpenditures; + private List avgSalaries; + + public LineChartVo(List points) { + this.months = new ArrayList<>(); + this.totalExpenditures = new ArrayList<>(); + this.avgSalaries = new ArrayList<>(); + + for (LineChartPointVo point : points) { + this.months.add(point.getMonth()); + this.totalExpenditures.add(point.getTotalExpenditure()); + this.avgSalaries.add(point.getAvgSalary()); + } + } +} \ No newline at end of file diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/MonthlyExpenditureVo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/MonthlyExpenditureVo.java new file mode 100644 index 0000000..2360980 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/MonthlyExpenditureVo.java @@ -0,0 +1,11 @@ +package com.ruoyi.oa.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class MonthlyExpenditureVo { + private Integer month; + private BigDecimal totalExpenditure; +} \ No newline at end of file diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/OaEmployeeVo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/OaEmployeeVo.java index 3037e58..97000c1 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/OaEmployeeVo.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/OaEmployeeVo.java @@ -44,6 +44,16 @@ public class OaEmployeeVo { */ @ExcelProperty(value = "备注") private String remark; + /** + * 部门ID + */ + @ExcelProperty(value = "部门ID") + private Long deptId; + /** + * 部门名称 + */ + @ExcelProperty(value = "部门名称") + private String deptName; } diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/SalaryComponentVo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/SalaryComponentVo.java new file mode 100644 index 0000000..a2ad037 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/SalaryComponentVo.java @@ -0,0 +1,11 @@ +package com.ruoyi.oa.domain.vo; + +import lombok.Data; + +import java.math.BigDecimal; + +@Data +public class SalaryComponentVo { + private String itemName; + private BigDecimal totalAmount; +} \ No newline at end of file diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/SalaryDashboardVo.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/SalaryDashboardVo.java new file mode 100644 index 0000000..a97c510 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/domain/vo/SalaryDashboardVo.java @@ -0,0 +1,11 @@ +package com.ruoyi.oa.domain.vo; + +import com.ruoyi.common.core.page.TableDataInfo; +import lombok.Data; + +@Data +public class SalaryDashboardVo { + private CardDataVo cardData; + private ChartDataVo chartData; + private TableDataInfo departmentStats; +} \ No newline at end of file diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/mapper/SalaryDashboardMapper.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/mapper/SalaryDashboardMapper.java new file mode 100644 index 0000000..3777da3 --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/mapper/SalaryDashboardMapper.java @@ -0,0 +1,21 @@ +package com.ruoyi.oa.mapper; + +import com.ruoyi.oa.domain.vo.*; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; + +import java.util.List; + +@Mapper +public interface SalaryDashboardMapper { + + CardMetricsVo queryCardMetrics(@Param("year") Long year, @Param("month") Long month); + + List queryMonthlyExpenditures(@Param("year") Long year); + + List querySalaryComponents(@Param("year") Long year, @Param("month") Long month); + + List queryLineChartData(@Param("year") Long year); + + List queryDepartmentStats(@Param("year") Long year, @Param("month") Long month); +} \ No newline at end of file diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/ISalaryDashboardService.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/ISalaryDashboardService.java new file mode 100644 index 0000000..d5d540f --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/ISalaryDashboardService.java @@ -0,0 +1,8 @@ +package com.ruoyi.oa.service; + +import com.ruoyi.common.core.domain.PageQuery; +import com.ruoyi.oa.domain.vo.SalaryDashboardVo; + +public interface ISalaryDashboardService { + SalaryDashboardVo getDashboardData(Long payYear, Long payMonth); +} diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaEmployeeServiceImpl.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaEmployeeServiceImpl.java index 74c8da3..eb1d5fe 100644 --- a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaEmployeeServiceImpl.java +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/OaEmployeeServiceImpl.java @@ -1,12 +1,15 @@ package com.ruoyi.oa.service.impl; import cn.hutool.core.bean.BeanUtil; +import com.baomidou.mybatisplus.core.toolkit.CollectionUtils; +import com.ruoyi.common.core.domain.entity.SysDept; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.common.core.page.TableDataInfo; import com.ruoyi.common.core.domain.PageQuery; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; +import com.ruoyi.system.mapper.SysDeptMapper; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import com.ruoyi.oa.domain.bo.OaEmployeeBo; @@ -18,6 +21,8 @@ import com.ruoyi.oa.service.IOaEmployeeService; import java.util.List; import java.util.Map; import java.util.Collection; +import java.util.Objects; +import java.util.stream.Collectors; /** * 员工基础信息Service业务层处理 @@ -30,6 +35,7 @@ import java.util.Collection; public class OaEmployeeServiceImpl implements IOaEmployeeService { private final OaEmployeeMapper baseMapper; + private final SysDeptMapper deptMapper; /** * 查询员工基础信息 @@ -46,6 +52,35 @@ public class OaEmployeeServiceImpl implements IOaEmployeeService { public TableDataInfo queryPageList(OaEmployeeBo bo, PageQuery pageQuery) { LambdaQueryWrapper lqw = buildQueryWrapper(bo); Page result = baseMapper.selectVoPage(pageQuery.build(), lqw); + // 根据部门id查询部门名称 + if (CollectionUtils.isNotEmpty(result.getRecords())) { + // 提取所有部门ID + List deptIds = result.getRecords().stream() + .map(OaEmployeeVo::getDeptId) + .filter(Objects::nonNull) + .distinct() + .collect(Collectors.toList()); + + // 批量查询部门信息 + if (!deptIds.isEmpty()) { + LambdaQueryWrapper queryWrapper = Wrappers.lambdaQuery(); + queryWrapper.in(SysDept::getDeptId, deptIds) + .eq(SysDept::getDelFlag, '0'); // 只查询未删除的部门 + + List deptList = deptMapper.selectList(queryWrapper); + Map deptNameMap = deptList.stream() + .collect(Collectors.toMap(SysDept::getDeptId, SysDept::getDeptName, (k1, k2) -> k1)); + + // 将部门名称设置到对应的VO对象中 + for (OaEmployeeVo vo : result.getRecords()) { + if (vo.getDeptId() != null) { + vo.setDeptName(deptNameMap.getOrDefault(vo.getDeptId(), "未知部门")); + } + } + } + } + + return TableDataInfo.build(result); } diff --git a/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/SalaryDashboardServiceImpl.java b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/SalaryDashboardServiceImpl.java new file mode 100644 index 0000000..ad541ca --- /dev/null +++ b/ruoyi-oa/src/main/java/com/ruoyi/oa/service/impl/SalaryDashboardServiceImpl.java @@ -0,0 +1,116 @@ +package com.ruoyi.oa.service.impl; + +import com.ruoyi.common.core.page.TableDataInfo; +import com.ruoyi.oa.domain.vo.*; +import com.ruoyi.oa.mapper.SalaryDashboardMapper; +import com.ruoyi.oa.service.ISalaryDashboardService; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; + +import java.math.BigDecimal; +import java.math.RoundingMode; +import java.util.List; + +@Service +public class SalaryDashboardServiceImpl implements ISalaryDashboardService { + + @Autowired + private SalaryDashboardMapper dashboardMapper; + + @Override + public SalaryDashboardVo getDashboardData(Long payYear, Long payMonth) { + SalaryDashboardVo dashboardVo = new SalaryDashboardVo(); + + // 1. 获取卡片数据 + dashboardVo.setCardData(getCardData(payYear, payMonth)); + + // 2. 获取图表数据 + dashboardVo.setChartData(getChartData(payYear, payMonth)); + + // 3. 获取部门统计表格数据 (带分页) +// PageHelper.startPage(pageNum, pageSize); + List deptStats = dashboardMapper.queryDepartmentStats(payYear, payMonth); + TableDataInfo tableData = new TableDataInfo<>(deptStats, deptStats.size()); + dashboardVo.setDepartmentStats(tableData); + return dashboardVo; + } + + private CardDataVo getCardData(Long payYear, Long payMonth) { + CardDataVo cardData = new CardDataVo(); + + // 获取当月数据 + CardMetricsVo currentMonthMetrics = dashboardMapper.queryCardMetrics(payYear, payMonth); + + // 获取上月数据 (处理1月的情况) + Long lastMonth = payMonth == 1 ? 12 : payMonth - 1; + Long lastYear = payMonth == 1 ? payYear - 1 : payYear; + CardMetricsVo lastMonthMetrics = dashboardMapper.queryCardMetrics(lastYear, lastMonth); + + // 获取去年同期数据 + CardMetricsVo lastYearMetrics = dashboardMapper.queryCardMetrics(payYear - 1, payMonth); + + // 计算各项指标 + cardData.setTotalSalaryExpenditure(currentMonthMetrics.getTotalSalaryExpenditure()); + + // 计算较上月增长率 + if (lastMonthMetrics.getTotalSalaryExpenditure().compareTo(BigDecimal.ZERO) != 0) { + BigDecimal monthGrowth = currentMonthMetrics.getTotalSalaryExpenditure() + .subtract(lastMonthMetrics.getTotalSalaryExpenditure()) + .divide(lastMonthMetrics.getTotalSalaryExpenditure(), 4, RoundingMode.HALF_UP) + .multiply(new BigDecimal("100")); + cardData.setLastMonthTotalSalaryExpenditureRate(monthGrowth); + } + + // 计算部门平均支出 + if (currentMonthMetrics.getDepartmentCount() > 0) { + cardData.setAvgDepartmentExpenditure( + currentMonthMetrics.getTotalCompanyExpenditure() + .divide(new BigDecimal(currentMonthMetrics.getDepartmentCount()), 2, RoundingMode.HALF_UP) + ); + } + + // 计算人均实发工资 + if (currentMonthMetrics.getEmployeeCount() > 0) { + cardData.setAvgPersonSalary( + currentMonthMetrics.getTotalSalaryExpenditure() + .divide(new BigDecimal(currentMonthMetrics.getEmployeeCount()), 2, RoundingMode.HALF_UP) + ); + } + + // 计算同比增长率 + if (lastYearMetrics.getEmployeeCount() > 0) { + BigDecimal lastYearAvgSalary = lastYearMetrics.getTotalSalaryExpenditure() + .divide(new BigDecimal(lastYearMetrics.getEmployeeCount()), 2, RoundingMode.HALF_UP); + if (lastYearAvgSalary.compareTo(BigDecimal.ZERO) != 0) { + BigDecimal yearGrowth = cardData.getAvgPersonSalary() + .subtract(lastYearAvgSalary) + .divide(lastYearAvgSalary, 4, RoundingMode.HALF_UP) + .multiply(new BigDecimal("100")); + cardData.setYearOnYearGrowthRate(yearGrowth); + } + } + + return cardData; + } + + private ChartDataVo getChartData(Long payYear, Long payMonth) { + ChartDataVo chartData = new ChartDataVo(); + + // 1. 获取柱状图数据 (年度每月总支出) + List monthlyExpenditures = + dashboardMapper.queryMonthlyExpenditures(payYear); + chartData.setMonthlyExpenditures(monthlyExpenditures); + + // 2. 获取饼图数据 (薪资构成) + List salaryComponents = + dashboardMapper.querySalaryComponents(payYear, payMonth); + chartData.setSalaryComponents(salaryComponents); + + // 3. 获取折线图数据 (总支出 vs 平均工资) + List lineChartData = + dashboardMapper.queryLineChartData(payYear); + chartData.setLineChartData(new LineChartVo(lineChartData)); + + return chartData; + } +} \ No newline at end of file diff --git a/ruoyi-oa/src/main/resources/mapper/oa/SalaryDashboardMapper.xml b/ruoyi-oa/src/main/resources/mapper/oa/SalaryDashboardMapper.xml new file mode 100644 index 0000000..0135a65 --- /dev/null +++ b/ruoyi-oa/src/main/resources/mapper/oa/SalaryDashboardMapper.xml @@ -0,0 +1,143 @@ + + + + + + + + + + + + + + + + + + + \ No newline at end of file