From 9dcbb9e3ee6f0e9c5c7ec643202a82a84b735b78 Mon Sep 17 00:00:00 2001 From: Yangk Date: Tue, 30 Dec 2025 10:36:23 +0800 Subject: [PATCH] =?UTF-8?q?feat(erp/TimesheetInfo):=20=E6=B7=BB=E5=8A=A0?= =?UTF-8?q?=E5=B7=A5=E6=97=B6=E5=A1=AB=E6=8A=A5=E4=B8=AD=E7=9A=84=E9=83=A8?= =?UTF-8?q?=E9=97=A8=E4=BF=A1=E6=81=AF=E6=98=BE=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 在工时填报查询中增加部门名称字段 - 修改数据库查询语句添加部门表关联 - 更新视图对象添加部门名称属性 - 配置部门名称导出到Excel功能 --- .../ErpTimesheetReportController.java | 179 ++++++++++++++++++ .../erp/domain/vo/ProjectManHourReportVo.java | 75 ++++++++ .../erp/mapper/ErpTimesheetReportMapper.java | 29 +++ .../service/IErpTimesheetReportService.java | 36 ++++ .../impl/ErpTimesheetReportServiceImpl.java | 39 ++++ .../oa/erp/ErpTimesheetReportMapper.xml | 60 ++++++ 6 files changed, 418 insertions(+) create mode 100644 ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpTimesheetReportController.java create mode 100644 ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ProjectManHourReportVo.java create mode 100644 ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/mapper/ErpTimesheetReportMapper.java create mode 100644 ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/IErpTimesheetReportService.java create mode 100644 ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpTimesheetReportServiceImpl.java create mode 100644 ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpTimesheetReportMapper.xml diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpTimesheetReportController.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpTimesheetReportController.java new file mode 100644 index 00000000..989a7847 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/controller/ErpTimesheetReportController.java @@ -0,0 +1,179 @@ +package org.dromara.oa.erp.controller; + +import cn.dev33.satoken.annotation.SaCheckPermission; +import jakarta.servlet.http.HttpServletResponse; +import lombok.RequiredArgsConstructor; +import org.dromara.common.log.annotation.Log; +import org.dromara.common.log.enums.BusinessType; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.oa.erp.domain.vo.ProjectManHourReportVo; +import org.dromara.oa.erp.service.IErpTimesheetReportService; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import cn.idev.excel.FastExcel; +import cn.idev.excel.write.handler.CellWriteHandler; +import cn.idev.excel.write.metadata.holder.WriteSheetHolder; +import cn.idev.excel.write.metadata.holder.WriteTableHolder; +import cn.idev.excel.metadata.data.WriteCellData; +import cn.idev.excel.metadata.Head; +import org.apache.poi.ss.usermodel.Cell; +import org.apache.poi.ss.util.CellRangeAddress; +import java.net.URLEncoder; +import java.util.List; +import java.util.Map; +import java.util.HashMap; +import java.math.BigDecimal; + +import java.util.ArrayList; +import java.util.Collections; + +/** + * 工时报表 + * + * @author Yangk + * @date 2025-12-26 + */ +@Validated +@RequiredArgsConstructor +@RestController +@RequestMapping("/erp/timesheetReport") +public class ErpTimesheetReportController { + + private final IErpTimesheetReportService reportService; + + /** + * 获取项目工时统计列表 + */ + @SaCheckPermission("oa:erp:timesheetReport:projectManHour:list") + @GetMapping("/projectManHourList") + public TableDataInfo getProjectManHourList(ProjectManHourReportVo bo, String startTime, + String endTime) { + return reportService.queryProjectManHourList(bo, startTime, endTime); + } + + /** + * 导出项目工时统计列表 + */ + @SaCheckPermission("oa:erp:timesheetReport:projectManHour:export") + @Log(title = "项目工时统计报表", businessType = BusinessType.EXPORT) + @PostMapping("/exportProjectManHour") + public void exportProjectManHour(ProjectManHourReportVo bo, String startTime, String endTime, + HttpServletResponse response) { + try { + List list = reportService.queryProjectManHourAll(bo, startTime, endTime); + + // 处理数据:如果跨部门工时 等于 当月工时(说明没有跨部门协作),则置空 + BigDecimal totalHoursSum = BigDecimal.ZERO; + if (list != null) { + for (ProjectManHourReportVo vo : list) { + // 累加总工时 + if (vo.getTotalHours() != null) { + totalHoursSum = totalHoursSum.add(vo.getTotalHours()); + } + + if (vo.getTotalHours() != null && vo.getCrossDeptHours() != null + && vo.getTotalHours().compareTo(vo.getCrossDeptHours()) == 0) { + vo.setCrossDeptHours(null); + } + } + + // 添加“合计”行 + ProjectManHourReportVo totalVo = new ProjectManHourReportVo(); + totalVo.setDeptName("总计"); + totalVo.setTotalHours(totalHoursSum); + list.add(totalVo); + } + + // 构建动态表头 + List> heads = new ArrayList<>(); + heads.add(Collections.singletonList("部门")); + heads.add(Collections.singletonList("项目经理")); + heads.add(Collections.singletonList("项目名称")); + heads.add(Collections.singletonList("项目编号")); + heads.add(Collections.singletonList("项目类别")); + + String totalHoursTitle = "当月工时"; + if (startTime != null && !startTime.isEmpty() && endTime != null && !endTime.isEmpty()) { + totalHoursTitle = totalHoursTitle + " (" + startTime + " 至 " + endTime + ")"; + } + heads.add(Collections.singletonList(totalHoursTitle)); + + heads.add(Collections.singletonList("跨部门工时")); + + response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"); + response.setCharacterEncoding("utf-8"); + String fileName = URLEncoder.encode("项目工时统计报表", "UTF-8").replaceAll("\\+", "%20"); + response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx"); + + FastExcel.write(response.getOutputStream(), ProjectManHourReportVo.class) + .head(heads) + .sheet("项目工时统计报表") + .registerWriteHandler(new ProjectCellMergeStrategy(list)) + .doWrite(list); + } catch (Exception e) { + e.printStackTrace(); + throw new RuntimeException("导出失败"); + } + } + + /** + * 自定义单元格合并策略(按项目ID合并跨部门工时列) + */ + public static class ProjectCellMergeStrategy implements CellWriteHandler { + private final List list; + private final Map mergeMap = new HashMap<>(); + + public ProjectCellMergeStrategy(List list) { + this.list = list; + calculateMerge(); + } + + private void calculateMerge() { + if (list == null || list.isEmpty()) + return; + for (int i = 0; i < list.size();) { + Long currentId = list.get(i).getProjectId(); + int count = 1; + for (int j = i + 1; j < list.size(); j++) { + Long nextId = list.get(j).getProjectId(); + if (currentId != null && currentId.equals(nextId)) { + count++; + } else { + break; + } + } + if (count > 1) { + mergeMap.put(i, count); + } + i += count; + } + } + + @Override + public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder, + List> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) { + if (isHead || relativeRowIndex == null) + return; + + // 仅合并“跨部门工时”列 + if (head.getHeadNameList().contains("跨部门工时")) { + if (mergeMap.containsKey(relativeRowIndex)) { + Integer rowspan = mergeMap.get(relativeRowIndex); + if (rowspan > 1) { + // 合并单元格: firstRow, lastRow, firstCol, lastCol + CellRangeAddress cellRangeAddress = new CellRangeAddress( + cell.getRowIndex(), + cell.getRowIndex() + rowspan - 1, + cell.getColumnIndex(), + cell.getColumnIndex()); + writeSheetHolder.getSheet().addMergedRegionUnsafe(cellRangeAddress); + } + } + } + } + } +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ProjectManHourReportVo.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ProjectManHourReportVo.java new file mode 100644 index 00000000..71fb0ced --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/domain/vo/ProjectManHourReportVo.java @@ -0,0 +1,75 @@ +package org.dromara.oa.erp.domain.vo; + +import cn.idev.excel.annotation.ExcelIgnoreUnannotated; +import cn.idev.excel.annotation.ExcelProperty; +import cn.idev.excel.annotation.write.style.ColumnWidth; +import org.dromara.common.excel.annotation.ExcelDictFormat; +import org.dromara.common.excel.convert.ExcelDictConvert; +import lombok.Data; +import java.io.Serial; +import java.io.Serializable; +import java.math.BigDecimal; + +/** + * 项目工时统计报表 VO + * + * @author Yangk + * @date 2025-12-26 + */ +@Data +@ColumnWidth(20) +@ExcelIgnoreUnannotated +public class ProjectManHourReportVo implements Serializable { + + @Serial + private static final long serialVersionUID = 1L; + + /** + * 项目ID + */ + private Long projectId; + + /** + * 部门名称 + */ + @ExcelProperty(value = "部门") + private String deptName; + + /** + * 项目经理姓名 + */ + @ExcelProperty(value = "项目经理") + private String managerName; + + /** + * 项目名称 + */ + @ExcelProperty(value = "项目名称") + private String projectName; + + /** + * 项目编号 + */ + @ExcelProperty(value = "项目编号") + private String projectCode; + + /** + * 项目类别 + */ + @ExcelProperty(value = "项目类别", converter = ExcelDictConvert.class) + @ExcelDictFormat(dictType = "project_category") + private String projectCategory; + + /** + * 当月/指定时间段工时 + */ + @ExcelProperty(value = "当月工时") + private BigDecimal totalHours; + + /** + * 跨部门工时 + */ + @ExcelProperty(value = "跨部门工时") + private BigDecimal crossDeptHours; + +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/mapper/ErpTimesheetReportMapper.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/mapper/ErpTimesheetReportMapper.java new file mode 100644 index 00000000..4bad76b3 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/mapper/ErpTimesheetReportMapper.java @@ -0,0 +1,29 @@ +package org.dromara.oa.erp.mapper; + +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +import org.dromara.oa.erp.domain.vo.ProjectManHourReportVo; + +import java.util.List; + +/** + * 项目工时统计报表 Mapper + * + * @author Yangk + * @date 2025-12-26 + */ +@Mapper +public interface ErpTimesheetReportMapper { + + /** + * 查询项目工时统计列表 + * + * @param bo 查询条件 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 列表 + */ + List selectProjectManHourList(@Param("bo") ProjectManHourReportVo bo, + @Param("startTime") String startTime, + @Param("endTime") String endTime); +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/IErpTimesheetReportService.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/IErpTimesheetReportService.java new file mode 100644 index 00000000..17ed1421 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/IErpTimesheetReportService.java @@ -0,0 +1,36 @@ +package org.dromara.oa.erp.service; + +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.oa.erp.domain.vo.ProjectManHourReportVo; + +import java.util.List; + +/** + * 工时报表 Service 接口 + * + * @author Yangk + * @date 2025-12-26 + */ +public interface IErpTimesheetReportService { + + /** + * 查询项目工时统计列表 + * + * @param bo 查询条件 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 列表 + */ + TableDataInfo queryProjectManHourList(ProjectManHourReportVo bo, String startTime, + String endTime); + + /** + * 查询所有项目工时统计 (导出用) + * + * @param bo 查询条件 + * @param startTime 开始时间 + * @param endTime 结束时间 + * @return 列表 + */ + List queryProjectManHourAll(ProjectManHourReportVo bo, String startTime, String endTime); +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpTimesheetReportServiceImpl.java b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpTimesheetReportServiceImpl.java new file mode 100644 index 00000000..a0b372d6 --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/java/org/dromara/oa/erp/service/impl/ErpTimesheetReportServiceImpl.java @@ -0,0 +1,39 @@ +package org.dromara.oa.erp.service.impl; + +import com.github.yulichang.toolkit.JoinWrappers; +import com.github.yulichang.wrapper.MPJLambdaWrapper; +import lombok.RequiredArgsConstructor; +import org.dromara.common.mybatis.core.page.TableDataInfo; +import org.dromara.oa.erp.domain.vo.ProjectManHourReportVo; +import org.dromara.oa.erp.mapper.ErpTimesheetReportMapper; +import org.dromara.oa.erp.service.IErpTimesheetReportService; +import org.springframework.stereotype.Service; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; + +import java.util.List; + +/** + * 工时报表 Service 实现 + * + * @author Yangk + * @date 2025-12-26 + */ +@RequiredArgsConstructor +@Service +public class ErpTimesheetReportServiceImpl implements IErpTimesheetReportService { + + private final ErpTimesheetReportMapper reportMapper; + + @Override + public TableDataInfo queryProjectManHourList(ProjectManHourReportVo bo, String startTime, + String endTime) { + List list = reportMapper.selectProjectManHourList(bo, startTime, endTime); + return TableDataInfo.build(list); + } + + @Override + public List queryProjectManHourAll(ProjectManHourReportVo bo, String startTime, + String endTime) { + return reportMapper.selectProjectManHourList(bo, startTime, endTime); + } +} diff --git a/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpTimesheetReportMapper.xml b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpTimesheetReportMapper.xml new file mode 100644 index 00000000..c8c6579b --- /dev/null +++ b/ruoyi-modules/ruoyi-oa/src/main/resources/mapper/oa/erp/ErpTimesheetReportMapper.xml @@ -0,0 +1,60 @@ + + + + + +