feat(oa): 新增工资数据看板功能

- 添加新工资看板相关的 VO 类
- 实现新工资看板的数据获取接口和业务逻辑
- 优化原有工资看板的代码结构
- 新增月度支出、工资构成、趋势分析等图表数据查询- 增加社保公积金统计和单位、部门统计功能
This commit is contained in:
2025-09-08 15:33:33 +08:00
parent fdd7489102
commit 655540c20b
16 changed files with 993 additions and 3 deletions

View File

@@ -1,10 +1,10 @@
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.domain.vo.dashboard.NewSalaryDashboardVo;
import com.ruoyi.oa.service.ISalaryDashboardService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
@@ -12,6 +12,9 @@ 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 {
@@ -19,8 +22,11 @@ public class SalaryDashboardController extends BaseController {
@Autowired
private ISalaryDashboardService salaryDashboardService;
/**
* 获取原有工资数据看板数据
*/
@GetMapping
public R<SalaryDashboardVo>getDashboardData(
public R<SalaryDashboardVo> getDashboardData(
@RequestParam(required = true) Long payYear,
@RequestParam(required = true) Long payMonth
) {
@@ -28,4 +34,17 @@ public class SalaryDashboardController extends BaseController {
salaryDashboardService.getDashboardData(payYear, payMonth)
);
}
/**
* 获取新工资数据看板数据(基于新的工资表结构)
*/
@GetMapping("/new")
public R<NewSalaryDashboardVo> getNewDashboardData(
@RequestParam(required = true) String salaryPeriod
) {
return R.ok(
salaryDashboardService.getNewDashboardData(salaryPeriod)
);
}
}

View File

@@ -0,0 +1,46 @@
package com.ruoyi.oa.domain.vo.dashboard;
import lombok.Data;
import java.math.BigDecimal;
/**
* 部门工资统计VO
*/
@Data
public class DeptSalaryStatVo {
/**
* 部门名称
*/
private String deptName;
/**
* 员工数量
*/
private Integer employeeCount;
/**
* 总实发工资
*/
private BigDecimal totalNetSalary;
/**
* 总应发工资
*/
private BigDecimal totalGrossSalary;
/**
* 人均实发工资
*/
private BigDecimal avgNetSalary;
/**
* 人均应发工资
*/
private BigDecimal avgGrossSalary;
/**
* 同比增长率
*/
private BigDecimal yearOnYearGrowthRate;
}

View File

@@ -0,0 +1,31 @@
package com.ruoyi.oa.domain.vo.dashboard;
import lombok.Data;
import java.math.BigDecimal;
/**
* 社保公积金统计VO
*/
@Data
public class InsuranceStatVo {
/**
* 项目名称
*/
private String itemName;
/**
* 个人缴纳总额
*/
private BigDecimal personalTotal;
/**
* 企业缴纳总额
*/
private BigDecimal enterpriseTotal;
/**
* 合计
*/
private BigDecimal total;
}

View File

@@ -0,0 +1,31 @@
package com.ruoyi.oa.domain.vo.dashboard;
import lombok.Data;
import java.math.BigDecimal;
/**
* 月度支出VO
*/
@Data
public class MonthlyExpenseVo {
/**
* 月份
*/
private Integer month;
/**
* 实发工资总额
*/
private BigDecimal totalNetSalary;
/**
* 应发工资总额
*/
private BigDecimal totalGrossSalary;
/**
* 单位总支出
*/
private BigDecimal totalUnitExpense;
}

View File

@@ -0,0 +1,66 @@
package com.ruoyi.oa.domain.vo.dashboard;
import lombok.Data;
import java.math.BigDecimal;
/**
* 新工资看板卡片数据VO
*/
@Data
public class NewCardDataVo {
/**
* 总实发工资
*/
private BigDecimal totalNetSalary;
/**
* 较上月总实发工资增长率
*/
private BigDecimal lastMonthNetSalaryRate;
/**
* 总应发工资
*/
private BigDecimal totalGrossSalary;
/**
* 较上月总应发工资增长率
*/
private BigDecimal lastMonthGrossSalaryRate;
/**
* 单位总支出
*/
private BigDecimal totalUnitExpense;
/**
* 较上月单位总支出增长率
*/
private BigDecimal lastMonthUnitExpenseRate;
/**
* 人均实发工资
*/
private BigDecimal avgNetSalary;
/**
* 较上月人均实发工资增长率
*/
private BigDecimal lastMonthAvgNetSalaryRate;
/**
* 同比增长率(与去年同期相比)
*/
private BigDecimal yearOnYearGrowthRate;
/**
* 员工总数
*/
private Integer totalEmployeeCount;
/**
* 单位数量
*/
private Integer unitCount;
}

View File

@@ -0,0 +1,36 @@
package com.ruoyi.oa.domain.vo.dashboard;
import lombok.Data;
import java.math.BigDecimal;
/**
* 新工资看板卡片指标VO
*/
@Data
public class NewCardMetricsVo {
/**
* 总实发工资
*/
private BigDecimal totalNetSalary;
/**
* 总应发工资
*/
private BigDecimal totalGrossSalary;
/**
* 单位总支出
*/
private BigDecimal totalUnitExpense;
/**
* 员工数量
*/
private Integer employeeCount;
/**
* 单位数量
*/
private Integer unitCount;
}

View File

@@ -0,0 +1,31 @@
package com.ruoyi.oa.domain.vo.dashboard;
import lombok.Data;
import java.util.List;
/**
* 新工资看板图表数据VO
*/
@Data
public class NewChartDataVo {
/**
* 月度支出数据(柱状图)
*/
private List<MonthlyExpenseVo> monthlyExpenses;
/**
* 工资构成分析(饼图)
*/
private List<SalaryStructureVo> salaryStructures;
/**
* 趋势分析(折线图)
*/
private SalaryTrendVo trendData;
/**
* 社保公积金统计(柱状图)
*/
private List<InsuranceStatVo> insuranceStats;
}

View File

@@ -0,0 +1,30 @@
package com.ruoyi.oa.domain.vo.dashboard;
import com.ruoyi.common.core.page.TableDataInfo;
import lombok.Data;
/**
* 新工资数据看板VO
*/
@Data
public class NewSalaryDashboardVo {
/**
* 卡片数据
*/
private NewCardDataVo cardData;
/**
* 图表数据
*/
private NewChartDataVo chartData;
/**
* 单位统计表格数据
*/
private TableDataInfo<UnitStatVo> unitStats;
/**
* 部门统计表格数据
*/
private TableDataInfo<DeptSalaryStatVo> deptStats;
}

View File

@@ -0,0 +1,26 @@
package com.ruoyi.oa.domain.vo.dashboard;
import lombok.Data;
import java.math.BigDecimal;
/**
* 工资构成VO
*/
@Data
public class SalaryStructureVo {
/**
* 项目名称
*/
private String itemName;
/**
* 总金额
*/
private BigDecimal totalAmount;
/**
* 占比
*/
private BigDecimal percentage;
}

View File

@@ -0,0 +1,31 @@
package com.ruoyi.oa.domain.vo.dashboard;
import lombok.Data;
import java.math.BigDecimal;
/**
* 工资趋势点VO
*/
@Data
public class SalaryTrendPointVo {
/**
* 月份
*/
private Integer month;
/**
* 总实发工资
*/
private BigDecimal totalNetSalary;
/**
* 总应发工资
*/
private BigDecimal totalGrossSalary;
/**
* 人均实发工资
*/
private BigDecimal avgNetSalary;
}

View File

@@ -0,0 +1,47 @@
package com.ruoyi.oa.domain.vo.dashboard;
import lombok.Data;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
/**
* 工资趋势VO
*/
@Data
public class SalaryTrendVo {
/**
* 月份列表
*/
private List<Integer> months;
/**
* 实发工资趋势
*/
private List<BigDecimal> netSalaryTrend;
/**
* 应发工资趋势
*/
private List<BigDecimal> grossSalaryTrend;
/**
* 人均实发工资趋势
*/
private List<BigDecimal> avgNetSalaryTrend;
public SalaryTrendVo(List<SalaryTrendPointVo> points) {
this.months = new ArrayList<>();
this.netSalaryTrend = new ArrayList<>();
this.grossSalaryTrend = new ArrayList<>();
this.avgNetSalaryTrend = new ArrayList<>();
for (SalaryTrendPointVo point : points) {
this.months.add(point.getMonth());
this.netSalaryTrend.add(point.getTotalNetSalary());
this.grossSalaryTrend.add(point.getTotalGrossSalary());
this.avgNetSalaryTrend.add(point.getAvgNetSalary());
}
}
}

View File

@@ -0,0 +1,51 @@
package com.ruoyi.oa.domain.vo.dashboard;
import lombok.Data;
import java.math.BigDecimal;
/**
* 单位统计VO
*/
@Data
public class UnitStatVo {
/**
* 单位名称
*/
private String unitName;
/**
* 员工数量
*/
private Integer employeeCount;
/**
* 总实发工资
*/
private BigDecimal totalNetSalary;
/**
* 总应发工资
*/
private BigDecimal totalGrossSalary;
/**
* 单位总支出
*/
private BigDecimal totalUnitExpense;
/**
* 人均实发工资
*/
private BigDecimal avgNetSalary;
/**
* 同比增长率
*/
private BigDecimal yearOnYearGrowthRate;
/**
* 工资期间
*/
private String salaryPeriod;
}

View File

@@ -1,6 +1,7 @@
package com.ruoyi.oa.mapper;
import com.ruoyi.oa.domain.vo.*;
import com.ruoyi.oa.domain.vo.dashboard.*;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
@@ -18,4 +19,59 @@ public interface SalaryDashboardMapper {
List<LineChartPointVo> queryLineChartData(@Param("year") Long year);
List<DepartmentStatVo> queryDepartmentStats(@Param("year") Long year, @Param("month") Long month);
/**
* 查询卡片指标数据
*
* @param salaryPeriod 工资期间
* @return 卡片指标数据
*/
NewCardMetricsVo queryNewCardMetrics(@Param("salaryPeriod") String salaryPeriod);
/**
* 查询月度支出数据
*
* @param year 年份
* @return 月度支出列表
*/
List<MonthlyExpenseVo> queryMonthlyExpenses(@Param("year") Integer year);
/**
* 查询工资构成数据
*
* @param salaryPeriod 工资期间
* @return 工资构成列表
*/
List<SalaryStructureVo> querySalaryStructures(@Param("salaryPeriod") String salaryPeriod);
/**
* 查询工资趋势数据
*
* @param year 年份
* @return 工资趋势点列表
*/
List<SalaryTrendPointVo> querySalaryTrendData(@Param("year") Integer year);
/**
* 查询社保公积金统计数据
*
* @param salaryPeriod 工资期间
* @return 社保公积金统计列表
*/
List<InsuranceStatVo> queryInsuranceStats(@Param("salaryPeriod") String salaryPeriod);
/**
* 查询单位统计数据
*
* @param salaryPeriod 工资期间
* @return 单位统计列表
*/
List<UnitStatVo> queryUnitStats(@Param("salaryPeriod") String salaryPeriod);
/**
* 查询部门统计数据
*
* @param salaryPeriod 工资期间
* @return 部门统计列表
*/
List<DeptSalaryStatVo> queryDeptSalaryStats(@Param("salaryPeriod") String salaryPeriod);
}

View File

@@ -1,8 +1,17 @@
package com.ruoyi.oa.service;
import com.ruoyi.common.core.domain.PageQuery;
import com.ruoyi.oa.domain.vo.SalaryDashboardVo;
import com.ruoyi.oa.domain.vo.dashboard.NewSalaryDashboardVo;
public interface ISalaryDashboardService {
SalaryDashboardVo getDashboardData(Long payYear, Long payMonth);
/**
* 获取新工资数据看板数据
*
* @param salaryPeriod 工资期间格式如2023-08
* @return 数据看板VO
*/
NewSalaryDashboardVo getNewDashboardData(String salaryPeriod);
}

View File

@@ -2,6 +2,7 @@ package com.ruoyi.oa.service.impl;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.oa.domain.vo.*;
import com.ruoyi.oa.domain.vo.dashboard.*;
import com.ruoyi.oa.mapper.SalaryDashboardMapper;
import com.ruoyi.oa.service.ISalaryDashboardService;
import org.springframework.beans.factory.annotation.Autowired;
@@ -154,4 +155,210 @@ public class SalaryDashboardServiceImpl implements ISalaryDashboardService {
return chartData;
}
@Override
public NewSalaryDashboardVo getNewDashboardData(String salaryPeriod) {
NewSalaryDashboardVo dashboardVo = new NewSalaryDashboardVo();
// 1. 获取卡片数据
dashboardVo.setCardData(getNewCardData(salaryPeriod));
// 2. 获取图表数据
String[] periodParts = salaryPeriod.split("-");
Integer year = Integer.valueOf(periodParts[0]);
dashboardVo.setChartData(getNewChartData(salaryPeriod, year));
// 3. 获取单位统计表格数据
List<UnitStatVo> unitStats = dashboardMapper.queryUnitStats(salaryPeriod);
TableDataInfo<UnitStatVo> unitTableData = new TableDataInfo<>(unitStats, unitStats.size());
dashboardVo.setUnitStats(unitTableData);
// 4. 获取部门统计表格数据
List<DeptSalaryStatVo> deptStats = dashboardMapper.queryDeptSalaryStats(salaryPeriod);
TableDataInfo<DeptSalaryStatVo> deptTableData = new TableDataInfo<>(deptStats, deptStats.size());
dashboardVo.setDeptStats(deptTableData);
return dashboardVo;
}
/**
* 获取新卡片数据
*/
private NewCardDataVo getNewCardData(String salaryPeriod) {
NewCardDataVo cardData = new NewCardDataVo();
// 初始化所有数值为0避免NPE
cardData.setTotalNetSalary(BigDecimal.ZERO);
cardData.setLastMonthNetSalaryRate(BigDecimal.ZERO);
cardData.setTotalGrossSalary(BigDecimal.ZERO);
cardData.setLastMonthGrossSalaryRate(BigDecimal.ZERO);
cardData.setTotalUnitExpense(BigDecimal.ZERO);
cardData.setLastMonthUnitExpenseRate(BigDecimal.ZERO);
cardData.setAvgNetSalary(BigDecimal.ZERO);
cardData.setLastMonthAvgNetSalaryRate(BigDecimal.ZERO);
cardData.setYearOnYearGrowthRate(BigDecimal.ZERO);
cardData.setTotalEmployeeCount(0);
cardData.setUnitCount(0);
// 获取当月数据
NewCardMetricsVo currentMonthMetrics = dashboardMapper.queryNewCardMetrics(salaryPeriod);
if (currentMonthMetrics == null) {
return cardData; // 如果当月没有数据,直接返回零值
}
// 设置当月数据
cardData.setTotalNetSalary(currentMonthMetrics.getTotalNetSalary() != null ?
currentMonthMetrics.getTotalNetSalary() : BigDecimal.ZERO);
cardData.setTotalGrossSalary(currentMonthMetrics.getTotalGrossSalary() != null ?
currentMonthMetrics.getTotalGrossSalary() : BigDecimal.ZERO);
cardData.setTotalUnitExpense(currentMonthMetrics.getTotalUnitExpense() != null ?
currentMonthMetrics.getTotalUnitExpense() : BigDecimal.ZERO);
cardData.setTotalEmployeeCount(currentMonthMetrics.getEmployeeCount() != null ?
currentMonthMetrics.getEmployeeCount() : 0);
cardData.setUnitCount(currentMonthMetrics.getUnitCount() != null ?
currentMonthMetrics.getUnitCount() : 0);
// 计算人均实发工资
if (currentMonthMetrics.getEmployeeCount() != null &&
currentMonthMetrics.getEmployeeCount() > 0 &&
currentMonthMetrics.getTotalNetSalary() != null) {
try {
BigDecimal avgNetSalary = currentMonthMetrics.getTotalNetSalary()
.divide(new BigDecimal(currentMonthMetrics.getEmployeeCount()), 2, RoundingMode.HALF_UP);
cardData.setAvgNetSalary(avgNetSalary);
} catch (Exception e) {
cardData.setAvgNetSalary(BigDecimal.ZERO);
}
}
// 计算上月对比数据
String lastMonthPeriod = getLastMonthPeriod(salaryPeriod);
NewCardMetricsVo lastMonthMetrics = dashboardMapper.queryNewCardMetrics(lastMonthPeriod);
if (lastMonthMetrics != null) {
// 计算实发工资增长率
if (lastMonthMetrics.getTotalNetSalary() != null &&
lastMonthMetrics.getTotalNetSalary().compareTo(BigDecimal.ZERO) != 0) {
try {
BigDecimal netSalaryGrowth = currentMonthMetrics.getTotalNetSalary()
.subtract(lastMonthMetrics.getTotalNetSalary())
.divide(lastMonthMetrics.getTotalNetSalary(), 4, RoundingMode.HALF_UP)
.multiply(new BigDecimal("100"));
cardData.setLastMonthNetSalaryRate(netSalaryGrowth);
} catch (Exception e) {
cardData.setLastMonthNetSalaryRate(BigDecimal.ZERO);
}
}
// 计算应发工资增长率
if (lastMonthMetrics.getTotalGrossSalary() != null &&
lastMonthMetrics.getTotalGrossSalary().compareTo(BigDecimal.ZERO) != 0) {
try {
BigDecimal grossSalaryGrowth = currentMonthMetrics.getTotalGrossSalary()
.subtract(lastMonthMetrics.getTotalGrossSalary())
.divide(lastMonthMetrics.getTotalGrossSalary(), 4, RoundingMode.HALF_UP)
.multiply(new BigDecimal("100"));
cardData.setLastMonthGrossSalaryRate(grossSalaryGrowth);
} catch (Exception e) {
cardData.setLastMonthGrossSalaryRate(BigDecimal.ZERO);
}
}
// 计算单位总支出增长率
if (lastMonthMetrics.getTotalUnitExpense() != null &&
lastMonthMetrics.getTotalUnitExpense().compareTo(BigDecimal.ZERO) != 0) {
try {
BigDecimal unitExpenseGrowth = currentMonthMetrics.getTotalUnitExpense()
.subtract(lastMonthMetrics.getTotalUnitExpense())
.divide(lastMonthMetrics.getTotalUnitExpense(), 4, RoundingMode.HALF_UP)
.multiply(new BigDecimal("100"));
cardData.setLastMonthUnitExpenseRate(unitExpenseGrowth);
} catch (Exception e) {
cardData.setLastMonthUnitExpenseRate(BigDecimal.ZERO);
}
}
}
// 计算同比增长率(与去年同期相比)
String lastYearPeriod = getLastYearPeriod(salaryPeriod);
NewCardMetricsVo lastYearMetrics = dashboardMapper.queryNewCardMetrics(lastYearPeriod);
if (lastYearMetrics != null &&
lastYearMetrics.getEmployeeCount() != null &&
lastYearMetrics.getEmployeeCount() > 0 &&
lastYearMetrics.getTotalNetSalary() != null) {
try {
BigDecimal lastYearAvgSalary = lastYearMetrics.getTotalNetSalary()
.divide(new BigDecimal(lastYearMetrics.getEmployeeCount()), 2, RoundingMode.HALF_UP);
if (lastYearAvgSalary.compareTo(BigDecimal.ZERO) != 0 &&
cardData.getAvgNetSalary() != null) {
BigDecimal yearGrowth = cardData.getAvgNetSalary()
.subtract(lastYearAvgSalary)
.divide(lastYearAvgSalary, 4, RoundingMode.HALF_UP)
.multiply(new BigDecimal("100"));
cardData.setYearOnYearGrowthRate(yearGrowth);
}
} catch (Exception e) {
cardData.setYearOnYearGrowthRate(BigDecimal.ZERO);
}
}
return cardData;
}
/**
* 获取新图表数据
*/
private NewChartDataVo getNewChartData(String salaryPeriod, Integer year) {
NewChartDataVo chartData = new NewChartDataVo();
// 1. 获取月度支出数据
List<MonthlyExpenseVo> monthlyExpenses = dashboardMapper.queryMonthlyExpenses(year);
chartData.setMonthlyExpenses(monthlyExpenses != null ? monthlyExpenses : new ArrayList<>());
// 2. 获取工资构成数据
List<SalaryStructureVo> salaryStructures = dashboardMapper.querySalaryStructures(salaryPeriod);
chartData.setSalaryStructures(salaryStructures != null ? salaryStructures : new ArrayList<>());
// 3. 获取趋势数据
List<SalaryTrendPointVo> trendData = dashboardMapper.querySalaryTrendData(year);
chartData.setTrendData(new SalaryTrendVo(trendData != null ? trendData : new ArrayList<>()));
// 4. 获取社保公积金统计数据
List<InsuranceStatVo> insuranceStats = dashboardMapper.queryInsuranceStats(salaryPeriod);
chartData.setInsuranceStats(insuranceStats != null ? insuranceStats : new ArrayList<>());
return chartData;
}
/**
* 获取上月期间
*/
private String getLastMonthPeriod(String salaryPeriod) {
String[] parts = salaryPeriod.split("-");
int year = Integer.parseInt(parts[0]);
int month = Integer.parseInt(parts[1]);
if (month == 1) {
year--;
month = 12;
} else {
month--;
}
return String.format("%d-%02d", year, month);
}
/**
* 获取去年同期
*/
private String getLastYearPeriod(String salaryPeriod) {
String[] parts = salaryPeriod.split("-");
int year = Integer.parseInt(parts[0]) - 1;
int month = Integer.parseInt(parts[1]);
return String.format("%d-%02d", year, month);
}
}

View File

@@ -140,4 +140,277 @@
ORDER BY
totalExpenditure DESC
</select>
<!-- 查询卡片指标数据 -->
<select id="queryNewCardMetrics" resultType="com.ruoyi.oa.domain.vo.dashboard.NewCardMetricsVo">
SELECT
COALESCE(SUM(d.net_salary), 0) as totalNetSalary,
COALESCE(SUM(d.gross_salary), 0) as totalGrossSalary,
COALESCE(SUM(d.unit_total_expense), 0) as totalUnitExpense,
COUNT(DISTINCT d.detail_id) as employeeCount,
COUNT(DISTINCT m.unit_name) as unitCount
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0
AND d.del_flag = 0
</select>
<!-- 查询月度支出数据 -->
<select id="queryMonthlyExpenses" resultType="com.ruoyi.oa.domain.vo.dashboard.MonthlyExpenseVo">
SELECT
CAST(SUBSTRING(m.salary_period, 6, 2) AS UNSIGNED) as month,
COALESCE(SUM(d.net_salary), 0) as totalNetSalary,
COALESCE(SUM(d.gross_salary), 0) as totalGrossSalary,
COALESCE(SUM(d.unit_total_expense), 0) as totalUnitExpense
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE SUBSTRING(m.salary_period, 1, 4) = #{year}
AND m.del_flag = 0
AND d.del_flag = 0
GROUP BY CAST(SUBSTRING(m.salary_period, 6, 2) AS UNSIGNED)
ORDER BY month
</select>
<!-- 查询工资构成数据 -->
<select id="querySalaryStructures" resultType="com.ruoyi.oa.domain.vo.dashboard.SalaryStructureVo">
SELECT
'基本工资' as itemName,
COALESCE(SUM(d.basic_salary), 0) as totalAmount,
CASE
WHEN SUM(d.gross_salary) > 0 THEN ROUND((SUM(d.basic_salary) / SUM(d.gross_salary)) * 100, 2)
ELSE 0
END as percentage
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0 AND d.del_flag = 0
UNION ALL
SELECT
'岗位工资' as itemName,
COALESCE(SUM(d.post_salary), 0) as totalAmount,
CASE
WHEN SUM(d.gross_salary) > 0 THEN ROUND((SUM(d.post_salary) / SUM(d.gross_salary)) * 100, 2)
ELSE 0
END as percentage
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0 AND d.del_flag = 0
UNION ALL
SELECT
'餐补' as itemName,
COALESCE(SUM(d.meal_allowance), 0) as totalAmount,
CASE
WHEN SUM(d.gross_salary) > 0 THEN ROUND((SUM(d.meal_allowance) / SUM(d.gross_salary)) * 100, 2)
ELSE 0
END as percentage
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0 AND d.del_flag = 0
UNION ALL
SELECT
'住房补贴' as itemName,
COALESCE(SUM(d.housing_allowance), 0) as totalAmount,
CASE
WHEN SUM(d.gross_salary) > 0 THEN ROUND((SUM(d.housing_allowance) / SUM(d.gross_salary)) * 100, 2)
ELSE 0
END as percentage
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0 AND d.del_flag = 0
UNION ALL
SELECT
'公交补贴' as itemName,
COALESCE(SUM(d.bus_allowance), 0) as totalAmount,
CASE
WHEN SUM(d.gross_salary) > 0 THEN ROUND((SUM(d.bus_allowance) / SUM(d.gross_salary)) * 100, 2)
ELSE 0
END as percentage
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0 AND d.del_flag = 0
UNION ALL
SELECT
'出差补助' as itemName,
COALESCE(SUM(d.business_allowance), 0) as totalAmount,
CASE
WHEN SUM(d.gross_salary) > 0 THEN ROUND((SUM(d.business_allowance) / SUM(d.gross_salary)) * 100, 2)
ELSE 0
END as percentage
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0 AND d.del_flag = 0
UNION ALL
SELECT
'加班工资' as itemName,
COALESCE(SUM(d.overtime_total), 0) as totalAmount,
CASE
WHEN SUM(d.gross_salary) > 0 THEN ROUND((SUM(d.overtime_total) / SUM(d.gross_salary)) * 100, 2)
ELSE 0
END as percentage
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0 AND d.del_flag = 0
ORDER BY totalAmount DESC
</select>
<!-- 查询工资趋势数据 -->
<select id="querySalaryTrendData" resultType="com.ruoyi.oa.domain.vo.dashboard.SalaryTrendPointVo">
SELECT
CAST(SUBSTRING(m.salary_period, 6, 2) AS UNSIGNED) as month,
COALESCE(SUM(d.net_salary), 0) as totalNetSalary,
COALESCE(SUM(d.gross_salary), 0) as totalGrossSalary,
CASE
WHEN COUNT(DISTINCT d.detail_id) > 0 THEN ROUND(SUM(d.net_salary) / COUNT(DISTINCT d.detail_id), 2)
ELSE 0
END as avgNetSalary
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE SUBSTRING(m.salary_period, 1, 4) = #{year}
AND m.del_flag = 0
AND d.del_flag = 0
GROUP BY CAST(SUBSTRING(m.salary_period, 6, 2) AS UNSIGNED)
ORDER BY month
</select>
<!-- 查询社保公积金统计数据 -->
<select id="queryInsuranceStats" resultType="com.ruoyi.oa.domain.vo.dashboard.InsuranceStatVo">
SELECT
'养老保险' as itemName,
COALESCE(SUM(d.personal_pension), 0) as personalTotal,
COALESCE(SUM(d.enterprise_pension), 0) as enterpriseTotal,
COALESCE(SUM(d.personal_pension) + SUM(d.enterprise_pension), 0) as total
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0 AND d.del_flag = 0
UNION ALL
SELECT
'医疗保险' as itemName,
COALESCE(SUM(d.personal_medical), 0) as personalTotal,
COALESCE(SUM(d.enterprise_medical), 0) as enterpriseTotal,
COALESCE(SUM(d.personal_medical) + SUM(d.enterprise_medical), 0) as total
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0 AND d.del_flag = 0
UNION ALL
SELECT
'失业保险' as itemName,
COALESCE(SUM(d.personal_unemployment), 0) as personalTotal,
COALESCE(SUM(d.enterprise_unemployment), 0) as enterpriseTotal,
COALESCE(SUM(d.personal_unemployment) + SUM(d.enterprise_unemployment), 0) as total
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0 AND d.del_flag = 0
UNION ALL
SELECT
'住房公积金' as itemName,
COALESCE(SUM(d.personal_housing_fund), 0) as personalTotal,
COALESCE(SUM(d.enterprise_housing_fund), 0) as enterpriseTotal,
COALESCE(SUM(d.personal_housing_fund) + SUM(d.enterprise_housing_fund), 0) as total
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0 AND d.del_flag = 0
UNION ALL
SELECT
'工伤保险' as itemName,
0 as personalTotal,
COALESCE(SUM(d.enterprise_injury), 0) as enterpriseTotal,
COALESCE(SUM(d.enterprise_injury), 0) as total
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0 AND d.del_flag = 0
UNION ALL
SELECT
'生育保险' as itemName,
0 as personalTotal,
COALESCE(SUM(d.enterprise_maternity), 0) as enterpriseTotal,
COALESCE(SUM(d.enterprise_maternity), 0) as total
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0 AND d.del_flag = 0
ORDER BY total DESC
</select>
<!-- 查询单位统计数据 -->
<select id="queryUnitStats" resultType="com.ruoyi.oa.domain.vo.dashboard.UnitStatVo">
SELECT
m.unit_name as unitName,
COUNT(DISTINCT d.detail_id) as employeeCount,
COALESCE(SUM(d.net_salary), 0) as totalNetSalary,
COALESCE(SUM(d.gross_salary), 0) as totalGrossSalary,
COALESCE(SUM(d.unit_total_expense), 0) as totalUnitExpense,
CASE
WHEN COUNT(DISTINCT d.detail_id) > 0 THEN ROUND(SUM(d.net_salary) / COUNT(DISTINCT d.detail_id), 2)
ELSE 0
END as avgNetSalary,
m.salary_period as salaryPeriod,
0 as yearOnYearGrowthRate
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0
AND d.del_flag = 0
GROUP BY m.unit_name, m.salary_period
ORDER BY totalNetSalary DESC
</select>
<!-- 查询部门统计数据 -->
<select id="queryDeptSalaryStats" resultType="com.ruoyi.oa.domain.vo.dashboard.DeptSalaryStatVo">
SELECT
COALESCE(d.dept, '未分配部门') as deptName,
COUNT(DISTINCT d.detail_id) as employeeCount,
COALESCE(SUM(d.net_salary), 0) as totalNetSalary,
COALESCE(SUM(d.gross_salary), 0) as totalGrossSalary,
CASE
WHEN COUNT(DISTINCT d.detail_id) > 0 THEN ROUND(SUM(d.net_salary) / COUNT(DISTINCT d.detail_id), 2)
ELSE 0
END as avgNetSalary,
CASE
WHEN COUNT(DISTINCT d.detail_id) > 0 THEN ROUND(SUM(d.gross_salary) / COUNT(DISTINCT d.detail_id), 2)
ELSE 0
END as avgGrossSalary,
0 as yearOnYearGrowthRate
FROM oa_salary_master m
LEFT JOIN oa_salary_detail d ON m.master_id = d.main_id
WHERE m.salary_period = #{salaryPeriod}
AND m.del_flag = 0
AND d.del_flag = 0
GROUP BY d.dept
ORDER BY totalNetSalary DESC
</select>
</mapper>