feat(erp/TimesheetReport): 新增项目工时人员统计报表功能

dev
Yangk 1 day ago
parent bb59628452
commit 0f42d6ac3d

@ -3,6 +3,7 @@ package org.dromara.oa.erp.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor;
import org.dromara.common.core.domain.R;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.TableDataInfo;
@ -22,6 +23,10 @@ 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 org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.CellStyle;
import org.apache.poi.ss.usermodel.IndexedColors;
import org.apache.poi.ss.usermodel.FillPatternType;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
@ -31,6 +36,9 @@ import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import org.dromara.oa.erp.domain.vo.ProjectPersonnelReportVo;
import org.springframework.web.bind.annotation.RequestParam;
/**
*
*
@ -55,10 +63,23 @@ public class ErpTimesheetReportController {
return reportService.queryProjectManHourList(bo, startTime, endTime);
}
/**
*
*/
@SaCheckPermission("oa:erp:timesheetReport:projectPersonnel:list")
@GetMapping("/projectPersonnelReport")
public R<ProjectPersonnelReportVo> getProjectPersonnelReport(@RequestParam(required = true) Long deptId,
String startTime, String endTime) {
if (deptId == null) {
return R.fail("部门ID不能为空");
}
return R.ok(reportService.queryProjectPersonnelReport(deptId, startTime, endTime));
}
/**
*
*/
@SaCheckPermission("oa:erp:timesheetReport:projectManHour:export")
@SaCheckPermission("oa/erp:timesheetInfo:export")
@Log(title = "项目工时统计报表", businessType = BusinessType.EXPORT)
@PostMapping("/exportProjectManHour")
public void exportProjectManHour(ProjectManHourReportVo bo, String startTime, String endTime,
@ -113,6 +134,7 @@ public class ErpTimesheetReportController {
.head(heads)
.sheet("项目工时统计报表")
.registerWriteHandler(new ProjectCellMergeStrategy(list))
.registerWriteHandler(new ProjectRowHighlightStrategy(list))
.doWrite(list);
} catch (Exception e) {
e.printStackTrace();
@ -120,6 +142,106 @@ public class ErpTimesheetReportController {
}
}
/**
*
*/
@SaCheckPermission("oa/erp:timesheetInfo:export")
@Log(title = "项目人员工时统计报表", businessType = BusinessType.EXPORT)
@PostMapping("/exportProjectPersonnel")
public void exportProjectPersonnel(Long deptId, String startTime, String endTime, HttpServletResponse response) {
try {
ProjectPersonnelReportVo result = reportService.queryProjectPersonnelReport(deptId, startTime, endTime);
List<Map<String, Object>> rows = result.getRows();
List<ProjectPersonnelReportVo.ReportColumn> columns = result.getColumns();
// 1. 构建表头
List<List<String>> heads = new ArrayList<>();
heads.add(Collections.singletonList("部门"));
heads.add(Collections.singletonList("项目名称"));
heads.add(Collections.singletonList("项目编号"));
heads.add(Collections.singletonList("汇总"));
// 动态人员列
List<String> userKeys = new ArrayList<>();
if (columns != null) {
for (ProjectPersonnelReportVo.ReportColumn col : columns) {
heads.add(Collections.singletonList(col.getLabel()));
userKeys.add(col.getProp());
}
}
heads.add(Collections.singletonList("备注"));
// 2. 构建数据行
List<List<Object>> dataList = new ArrayList<>();
if (rows != null) {
for (Map<String, Object> row : rows) {
List<Object> dataRow = new ArrayList<>();
dataRow.add(row.get("deptName"));
dataRow.add(row.get("projectName"));
dataRow.add(row.get("projectCode"));
dataRow.add(row.get("rowTotal")); // 对应“汇总”列
// 人员列数据
for (String key : userKeys) {
Object hours = row.get(key);
dataRow.add(hours != null ? hours : "");
}
dataRow.add(row.get("remark"));
dataList.add(dataRow);
}
}
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");
// 3. 导出 (注册自定义样式策略)
FastExcel.write(response.getOutputStream())
.head(heads)
.sheet("项目人员工时统计")
.registerWriteHandler(new MapRowHighlightStrategy(rows))
.doWrite(dataList);
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("导出失败");
}
}
/**
* Map
*/
public static class MapRowHighlightStrategy implements CellWriteHandler {
private final List<Map<String, Object>> dataList;
public MapRowHighlightStrategy(List<Map<String, Object>> dataList) {
this.dataList = dataList;
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
if (isHead || relativeRowIndex == null)
return;
if (dataList != null && relativeRowIndex < dataList.size()) {
Map<String, Object> rowData = dataList.get(relativeRowIndex);
Boolean isCrossDept = (Boolean) rowData.get("isCrossDept");
if (Boolean.TRUE.equals(isCrossDept)) {
Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
CellStyle cellStyle = workbook.createCellStyle();
cellStyle.cloneStyleFrom(cell.getCellStyle());
cellStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
cell.setCellStyle(cellStyle);
}
}
}
}
/**
* ID
*/
@ -176,4 +298,38 @@ public class ErpTimesheetReportController {
}
}
}
/**
*
*/
public static class ProjectRowHighlightStrategy implements CellWriteHandler {
private final List<ProjectManHourReportVo> list;
// 黄色背景样式
private final static short YELLOW_COLOR_INDEX = org.apache.poi.ss.usermodel.IndexedColors.LIGHT_YELLOW
.getIndex();
public ProjectRowHighlightStrategy(List<ProjectManHourReportVo> list) {
this.list = list;
}
@Override
public void afterCellDispose(WriteSheetHolder writeSheetHolder, WriteTableHolder writeTableHolder,
List<WriteCellData<?>> cellDataList, Cell cell, Head head, Integer relativeRowIndex, Boolean isHead) {
if (isHead || relativeRowIndex == null || relativeRowIndex >= list.size()) {
return;
}
ProjectManHourReportVo vo = list.get(relativeRowIndex);
// 如果 crossDeptHours 不为空(说明有跨部门工时),则整行标黄
// 注意:前面的逻辑保证了只有不相等时 crossDeptHours 才不为空
if (vo.getCrossDeptHours() != null) {
org.apache.poi.ss.usermodel.Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
org.apache.poi.ss.usermodel.CellStyle cellStyle = workbook.createCellStyle();
cellStyle.cloneStyleFrom(cell.getCellStyle());
cellStyle.setFillForegroundColor(YELLOW_COLOR_INDEX);
cellStyle.setFillPattern(org.apache.poi.ss.usermodel.FillPatternType.SOLID_FOREGROUND);
cell.setCellStyle(cellStyle);
}
}
}
}

@ -0,0 +1,60 @@
package org.dromara.oa.erp.domain.vo;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
/**
* VO
*
* @author Yangk
* @date 2025-12-30
*/
@Data
public class ProjectPersonnelReportVo implements Serializable {
/**
*
*/
private List<ReportColumn> columns;
/**
* (Map key column.prop)
*/
private List<Map<String, Object>> rows;
/**
*
*/
private ReportFooter footer;
@Data
public static class ReportColumn implements Serializable {
private String label;
private String prop;
// userId (可选,用于后续可能的操作)
private Long userId;
public ReportColumn(String label, String prop) {
this.label = label;
this.prop = prop;
}
public ReportColumn(String label, String prop, Long userId) {
this.label = label;
this.prop = prop;
this.userId = userId;
}
}
@Data
public static class ReportFooter implements Serializable {
// 当月天数
private Integer totalDays;
// 部门人数
private Integer deptHeadCount;
// 部门总人天
private Integer totalManDays;
}
}

@ -5,6 +5,7 @@ import org.apache.ibatis.annotations.Param;
import org.dromara.oa.erp.domain.vo.ProjectManHourReportVo;
import java.util.List;
import java.util.Map;
/**
* Mapper
@ -15,15 +16,32 @@ import java.util.List;
@Mapper
public interface ErpTimesheetReportMapper {
/**
*
*
* @param bo
* @param startTime
* @param endTime
* @return
*/
List<ProjectManHourReportVo> selectProjectManHourList(@Param("bo") ProjectManHourReportVo bo,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
/**
*
*
* @param bo
* @param startTime
* @param endTime
* @return
*/
List<ProjectManHourReportVo> selectProjectManHourList(@Param("bo") ProjectManHourReportVo bo,
@Param("startTime") String startTime,
@Param("endTime") String endTime);
/**
* -
*/
List<Map<String, Object>> selectProjectPersonnelHours(@Param("deptId") Long deptId,
@Param("startTime") String startTime, @Param("endTime") String endTime);
/**
*
*/
List<Map<String, Object>> selectProjectTotalHours(@Param("deptId") Long deptId,
@Param("startTime") String startTime, @Param("endTime") String endTime);
/**
*
*/
List<Map<String, Object>> selectDeptUsers(@Param("deptId") Long deptId);
}

@ -72,8 +72,14 @@ public interface IErpTimesheetInfoService {
/**
*
*
* @param bo
* @return
*/
ErpTimesheetInfoVo submitAndFlowStart(ErpTimesheetInfoBo bo);
/**
*
*/
ErpTimesheetInfoVo processSubmitAndFlowStart(ErpTimesheetInfoBo bo);
}

@ -5,6 +5,8 @@ import org.dromara.oa.erp.domain.vo.ProjectManHourReportVo;
import java.util.List;
import org.dromara.oa.erp.domain.vo.ProjectPersonnelReportVo;
/**
* Service
*
@ -14,23 +16,23 @@ import java.util.List;
public interface IErpTimesheetReportService {
/**
*
*
* @param bo
* @param startTime
* @param endTime
* @return
*
*/
TableDataInfo<ProjectManHourReportVo> queryProjectManHourList(ProjectManHourReportVo bo, String startTime,
String endTime);
/**
* ()
*
* @param bo
* @param startTime
* @param endTime
* @return
*/
List<ProjectManHourReportVo> queryProjectManHourAll(ProjectManHourReportVo bo, String startTime, String endTime);
/**
*
*
* @param deptId ID
* @param startTime
* @param endTime
* @return
*/
ProjectPersonnelReportVo queryProjectPersonnelReport(Long deptId, String startTime, String endTime);
}

@ -29,6 +29,7 @@ import org.dromara.oa.erp.service.IErpTimesheetProjectService;
import org.dromara.system.api.RemoteCodeRuleService;
import org.dromara.workflow.api.RemoteWorkflowService;
import org.dromara.workflow.api.domain.RemoteStartProcess;
import org.dromara.workflow.api.domain.RemoteStartProcessReturn;
import org.dromara.workflow.api.event.ProcessEvent;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Service;
@ -64,6 +65,10 @@ public class ErpTimesheetInfoServiceImpl implements IErpTimesheetInfoService {
private final IErpTimesheetDeptService timesheetDeptService;
private final IErpTimesheetProjectService timesheetProjectService;
@jakarta.annotation.Resource
@org.springframework.context.annotation.Lazy
private IErpTimesheetInfoService self;
/**
*
*
@ -254,9 +259,23 @@ public class ErpTimesheetInfoServiceImpl implements IErpTimesheetInfoService {
* @param bo
* @return
*/
/**
*
*/
@Override
public ErpTimesheetInfoVo submitAndFlowStart(ErpTimesheetInfoBo bo) {
// 1. 如果是新增且没有编号,先生成编号
if (bo.getTimesheetId() == null && StringUtils.isBlank(bo.getTimesheetCode())) {
String timesheetCode = remoteCodeRuleService.selectCodeRuleCode("1015");
bo.setTimesheetCode(timesheetCode);
}
// 2. 调用实际处理方法 (开启全局事务)
return self.processSubmitAndFlowStart(bo);
}
@Override
@GlobalTransactional(rollbackFor = Exception.class) // 开启全局事务
public ErpTimesheetInfoVo submitAndFlowStart(ErpTimesheetInfoBo bo) {
public ErpTimesheetInfoVo processSubmitAndFlowStart(ErpTimesheetInfoBo bo) {
if (bo.getTimesheetId() == null) {
this.insertByBo(bo);
} else {
@ -309,9 +328,27 @@ public class ErpTimesheetInfoServiceImpl implements IErpTimesheetInfoService {
bo.getBizExt().setBusinessId(startProcess.getBusinessId());
}
boolean flag = remoteWorkflowService.startCompleteTask(startProcess);
if (!flag) {
throw new ServiceException("流程发起异常");
RemoteStartProcessReturn result = remoteWorkflowService.startWorkFlow(startProcess);
if (result == null) {
throw new ServiceException("流程发起失败");
}
// 如果返回了 taskId说明流程停留在第一个节点需要我们手动完成它 (对应 "startCompleteTask" 的逻辑)
if (result.getTaskId() != null) {
org.dromara.workflow.api.domain.RemoteCompleteTask completeTask = new org.dromara.workflow.api.domain.RemoteCompleteTask();
completeTask.setTaskId(result.getTaskId());
// 审批意见,提交时默认为 "提交申请"
completeTask.setMessage("提交申请");
completeTask.setVariables(variables);
boolean completeFlag = remoteWorkflowService.completeTask(completeTask);
if (!completeFlag) {
// 这里可能需要警告,但既然流程已启动,可能不一定非要抛异常,视业务而定
log.warn("流程启动成功,但自动完成第一个任务失败: taskId={}", result.getTaskId());
}
} else {
// taskId 为空,说明流程启动后直接跳过/已结束/进入下一个节点(可能因为自动完成)
// 这种情况下,我们不需要做任何事,直接视为成功
log.info("流程启动成功,首节点已自动完成或跳过: instanceId={}", result.getProcessInstanceId());
}
return MapstructUtils.convert(baseMapper.selectById(bo.getTimesheetId()), ErpTimesheetInfoVo.class);

@ -5,12 +5,18 @@ 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.domain.vo.ProjectPersonnelReportVo;
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;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.time.temporal.ChronoUnit;
import java.util.*;
import java.util.stream.Collectors;
/**
* Service
@ -36,4 +42,123 @@ public class ErpTimesheetReportServiceImpl implements IErpTimesheetReportService
String endTime) {
return reportMapper.selectProjectManHourList(bo, startTime, endTime);
}
@Override
public ProjectPersonnelReportVo queryProjectPersonnelReport(Long deptId, String startTime, String endTime) {
ProjectPersonnelReportVo result = new ProjectPersonnelReportVo();
// 1. 查询该部门所有人员 (作为所有的列)
List<Map<String, Object>> deptUsers = reportMapper.selectDeptUsers(deptId);
List<ProjectPersonnelReportVo.ReportColumn> columns = new ArrayList<>();
Set<Long> userIds = new HashSet<>();
// 记录部门名称 (随便取一个用户的,或者如果没人则为空)
String deptName = "";
if (deptUsers != null) {
for (Map<String, Object> u : deptUsers) {
Long uid = (Long) u.get("userId");
String nickName = (String) u.get("nickName");
if (deptName.isEmpty() && u.get("deptName") != null) {
deptName = (String) u.get("deptName");
}
if (uid != null && !userIds.contains(uid)) {
userIds.add(uid);
columns.add(new ProjectPersonnelReportVo.ReportColumn(nickName, "user_" + uid, uid));
}
}
}
// 按名字排序 (或者保持数据库查询顺序)
// columns.sort(Comparator.comparing(ProjectPersonnelReportVo.ReportColumn::getLabel));
result.setColumns(columns);
// 2. 查询工时明细数据 (projectId, userId, hours)
List<Map<String, Object>> rawData = reportMapper.selectProjectPersonnelHours(deptId, startTime, endTime);
// 3. 查询跨部门总工时 (projectId, totalHours)
List<Map<String, Object>> totalHoursData = reportMapper.selectProjectTotalHours(deptId, startTime, endTime);
Map<Long, BigDecimal> projectTotalHoursMap = new HashMap<>();
if (totalHoursData != null) {
for (Map<String, Object> map : totalHoursData) {
Long pid = (Long) map.get("projectId");
BigDecimal total = (BigDecimal) map.get("totalHours");
projectTotalHoursMap.put(pid, total);
}
}
// 4. 构建行 (项目)
Map<Long, Map<String, Object>> rowsMap = new LinkedHashMap<>();
if (rawData != null) {
for (Map<String, Object> map : rawData) {
Long pid = (Long) map.get("projectId");
Long uid = (Long) map.get("userId");
BigDecimal hours = (BigDecimal) map.get("hours");
Map<String, Object> row = rowsMap.computeIfAbsent(pid, k -> {
Map<String, Object> r = new HashMap<>();
r.put("projectId", pid);
r.put("projectDeptId", map.get("projectDeptId"));
r.put("projectName", map.get("projectName"));
r.put("projectCode", map.get("projectCode"));
r.put("rowTotal", BigDecimal.ZERO);
return r;
});
// 设置人员工时
row.put("user_" + uid, hours);
// 累加行汇总
BigDecimal currentTotal = (BigDecimal) row.get("rowTotal");
row.put("rowTotal", currentTotal.add(hours));
}
}
List<Map<String, Object>> rows = new ArrayList<>(rowsMap.values());
// 5. 跨部门逻辑 & 底部统计
int days = calculateDays(startTime, endTime);
int headCount = userIds.size();
for (Map<String, Object> row : rows) {
// 补充部门名称
row.put("deptName", deptName);
Long projectDeptId = (Long) row.get("projectDeptId");
// 如果项目所属部门 != 当前报表统计部门,则是跨部门项目
if (projectDeptId != null && !projectDeptId.equals(deptId)) {
row.put("isCrossDept", true);
row.put("remark", "跨部门"); // 备注列
} else {
row.put("isCrossDept", false);
row.put("remark", "");
}
}
result.setRows(rows);
// 6. 底部
ProjectPersonnelReportVo.ReportFooter footer = new ProjectPersonnelReportVo.ReportFooter();
footer.setTotalDays(days);
footer.setDeptHeadCount(headCount);
footer.setTotalManDays(days * headCount);
result.setFooter(footer);
return result;
}
private int calculateDays(String start, String end) {
if (start == null || end == null)
return 0;
try {
LocalDate d1 = LocalDate.parse(start, DateTimeFormatter.ISO_DATE);
LocalDate d2 = LocalDate.parse(end, DateTimeFormatter.ISO_DATE);
return (int) ChronoUnit.DAYS.between(d1, d2) + 1;
} catch (Exception e) {
return 0;
}
}
}

@ -13,8 +13,8 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
p.project_code,
p.project_category,
COALESCE(SUM(tp.hours), 0) AS total_hours,
<!-- 计算该项目的总工时,用于最后一列展示 -->
SUM(SUM(tp.hours)) OVER(PARTITION BY p.project_id) AS cross_dept_hours
<!-- 获取该项目在全公司的总工时(用于判断是否存在跨部门) -->
COALESCE(global_stats.total_hours, 0) AS cross_dept_hours
FROM
erp_project_info p
LEFT JOIN sys_user u ON p.manager_id = u.user_id
@ -26,6 +26,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
LEFT JOIN sys_dept t_dept ON ti.dept_id = t_dept.dept_id
<!-- 关联项目所属部门,用于部门筛选 -->
LEFT JOIN sys_dept p_dept ON p.dept_id = p_dept.dept_id
<!-- 关联全局统计 (独立子查询,不受主查询部门条件影响) -->
LEFT JOIN (
SELECT
tp2.project_id,
SUM(tp2.hours) AS total_hours
FROM erp_timesheet_project tp2
JOIN erp_timesheet_info ti2 ON tp2.timesheet_id = ti2.timesheet_id AND ti2.del_flag = '0'
WHERE tp2.del_flag = '0'
AND ti2.timesheet_status = '3'
<if test="startTime != null and startTime != ''">
AND ti2.start_time >= #{startTime}
</if>
<if test="endTime != null and endTime != ''">
AND ti2.end_time &lt;= #{endTime}
</if>
GROUP BY tp2.project_id
) global_stats ON p.project_id = global_stats.project_id
<where>
p.del_flag = '0'
<if test="bo.projectName != null and bo.projectName != ''">
@ -52,9 +69,85 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
AND ti.timesheet_status = '3'
</where>
<!-- 按项目ID 和 填报部门ID 分组 -->
GROUP BY p.project_id, ti.dept_id
GROUP BY p.project_id, ti.dept_id, global_stats.total_hours
<!-- 仅显示有工时的记录 -->
HAVING SUM(tp.hours) > 0
ORDER BY p.project_code, t_dept.dept_id
</select>
<!-- 查询项目-人员工时分布 -->
<select id="selectProjectPersonnelHours" resultType="java.util.Map">
SELECT
p.project_id AS projectId,
p.dept_id AS projectDeptId,
p.project_name AS projectName,
p.project_code AS projectCode,
u.user_id AS userId,
u.nick_name AS nickName,
COALESCE(SUM(tp.hours), 0) AS hours
FROM erp_timesheet_project tp
JOIN erp_timesheet_info ti ON tp.timesheet_id = ti.timesheet_id AND ti.del_flag = '0'
JOIN erp_project_info p ON tp.project_id = p.project_id AND p.del_flag = '0'
JOIN sys_user u ON ti.user_id = u.user_id AND u.del_flag = '0'
WHERE ti.timesheet_status = '3'
AND tp.del_flag = '0'
<if test="deptId != null">
AND ti.dept_id = #{deptId}
</if>
<if test="startTime != null and startTime != ''">
AND ti.start_time >= #{startTime}
</if>
<if test="endTime != null and endTime != ''">
AND ti.end_time &lt;= #{endTime}
</if>
GROUP BY p.project_id, u.user_id
</select>
<!-- 查询项目在全公司的总工时(用于跨部门判断) -->
<select id="selectProjectTotalHours" resultType="java.util.Map">
SELECT
tp.project_id AS projectId,
COALESCE(SUM(tp.hours), 0) AS totalHours
FROM erp_timesheet_project tp
JOIN erp_timesheet_info ti ON tp.timesheet_id = ti.timesheet_id AND ti.del_flag = '0'
WHERE ti.timesheet_status = '3'
AND tp.del_flag = '0'
<if test="startTime != null and startTime != ''">
AND ti.start_time >= #{startTime}
</if>
<if test="endTime != null and endTime != ''">
AND ti.end_time &lt;= #{endTime}
</if>
<!-- 仅统计涉及到的项目 -->
AND tp.project_id IN
(
SELECT DISTINCT tpp.project_id
FROM erp_timesheet_project tpp
JOIN erp_timesheet_info tii ON tpp.timesheet_id = tii.timesheet_id
WHERE tpp.del_flag = '0'
AND tii.dept_id = #{deptId}
AND tii.timesheet_status = '3'
<if test="startTime != null and startTime != ''">
AND tii.start_time >= #{startTime}
</if>
<if test="endTime != null and endTime != ''">
AND tii.end_time &lt;= #{endTime}
</if>
)
GROUP BY tp.project_id
</select>
<!-- 查询部门的所有人员 -->
<select id="selectDeptUsers" resultType="java.util.Map">
SELECT
u.user_id AS userId,
u.nick_name AS nickName,
d.dept_name AS deptName
FROM sys_user u
LEFT JOIN sys_dept d ON u.dept_id = d.dept_id
WHERE u.dept_id = #{deptId}
AND u.del_flag = '0'
AND u.status = '0'
ORDER BY u.user_id
</select>
</mapper>

Loading…
Cancel
Save