feat(oa/erp): 新增预投工时分配明细导出功能

- 在控制器中添加 exportDetail 接口支持明细数据导出
- 创建 ErpTimesheetPreAllocDetailExportVo 专门用于导出的数据传输对象
- 实现服务层 queryExportDetailList 方法处理明细查询逻辑
- 支持按勾选分配单或过滤条件两种方式导出明细数据
- 使用 JoinWrappers 关联查询分配主表和明细表数据
- 添加相应的权限注解和日志记录功能
dev
yangk 1 month ago
parent 57aaca876e
commit ca8717f1c2

@ -19,6 +19,7 @@ import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.oa.erp.domain.vo.ErpProjectInfoVo;
import org.dromara.oa.erp.domain.vo.ErpTimesheetPreAllocVo;
import org.dromara.oa.erp.domain.vo.ErpTimesheetPreAllocDetailExportVo;
import org.dromara.oa.erp.domain.bo.ErpTimesheetPreAllocBo;
import org.dromara.oa.erp.domain.vo.PreAllocDetailVo;
import org.dromara.oa.erp.service.IErpTimesheetPreAllocService;
@ -59,6 +60,18 @@ public class ErpTimesheetPreAllocController extends BaseController {
ExcelUtil.exportExcel(list, "预投工时分配", ErpTimesheetPreAllocVo.class, response);
}
/**
*
*
*/
@SaCheckPermission("oa/erp:timesheetPreAlloc:export")
@Log(title = "预投工时分配明细", businessType = BusinessType.EXPORT)
@PostMapping("/exportDetail")
public void exportDetail(ErpTimesheetPreAllocBo bo, HttpServletResponse response) {
List<ErpTimesheetPreAllocDetailExportVo> list = erpTimesheetPreAllocService.queryExportDetailList(bo);
ExcelUtil.exportExcel(list, "预投工时分配明细", ErpTimesheetPreAllocDetailExportVo.class, response);
}
/**
*
*

@ -0,0 +1,67 @@
package org.dromara.oa.erp.domain.vo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
/**
*
* VO
*
* @author Yangk
* @date 2026-05-25
*/
@Data
@ExcelIgnoreUnannotated
public class ErpTimesheetPreAllocDetailExportVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
*
*/
@ExcelProperty(value = "分配单编号")
private String allocCode;
/**
*
*/
@ExcelProperty(value = "月 份")
private String monthCode;
/**
*
*/
@ExcelProperty(value = "原项目编码")
private String originalProjectCode;
/**
*
*/
@ExcelProperty(value = "原项目名称")
private String originalProjectName;
/**
*
*/
@ExcelProperty(value = "目标项目编码")
private String targetProjectCode;
/**
*
*/
@ExcelProperty(value = "目标项目名称")
private String targetProjectName;
/**
*
*/
@ExcelProperty(value = "分配工时")
private BigDecimal allocHours;
}

@ -6,6 +6,7 @@ import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.oa.erp.domain.vo.ErpProjectInfoVo;
import org.dromara.oa.erp.domain.vo.PreAllocDetailVo;
import org.dromara.oa.erp.domain.vo.ErpTimesheetPreAllocDetailExportVo;
import java.util.Collection;
import java.util.List;
@ -51,6 +52,14 @@ public interface IErpTimesheetPreAllocService {
*/
List<ErpTimesheetPreAllocVo> queryList(ErpTimesheetPreAllocBo bo);
/**
*
*
* @param bo
* @return
*/
List<ErpTimesheetPreAllocDetailExportVo> queryExportDetailList(ErpTimesheetPreAllocBo bo);
/**
*
*

@ -21,6 +21,7 @@ import org.dromara.oa.erp.domain.vo.ErpTimesheetPreAllocVo;
import org.dromara.oa.erp.domain.vo.PreAllocDetailVo;
import org.dromara.oa.erp.domain.vo.PreAllocSourceStatVo;
import org.dromara.oa.erp.domain.vo.PreAllocTargetVo;
import org.dromara.oa.erp.domain.vo.ErpTimesheetPreAllocDetailExportVo;
import org.dromara.oa.erp.mapper.ErpProjectInfoMapper;
import org.dromara.oa.erp.mapper.ErpTimesheetPreAllocDetailMapper;
import org.dromara.oa.erp.mapper.ErpTimesheetPreAllocMapper;
@ -123,21 +124,60 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
return baseMapper.selectVoList(lqw);
}
/**
*
*
* @param bo
* @return
*/
@Override
public List<ErpTimesheetPreAllocDetailExportVo> queryExportDetailList(ErpTimesheetPreAllocBo bo) {
List<Long> allocIds = null;
if (StringUtils.isNotBlank(bo.getAllocIds())) {
// 如果用户在前端勾选了特定的分配单,则仅导出这些分配单的明细
allocIds = java.util.stream.Stream.of(bo.getAllocIds().split(",")).map(Long::valueOf).toList();
} else {
// 若用户未勾选,则依据当前列表筛选条件,反查出符合过滤条件的所有分配单,再进行明细关联
List<ErpTimesheetPreAllocVo> list = this.queryList(bo);
allocIds = list.stream().map(ErpTimesheetPreAllocVo::getAllocId).toList();
}
if (cn.hutool.core.collection.CollUtil.isEmpty(allocIds)) {
return List.of();
}
return preAllocDetailMapper.selectJoinList(
ErpTimesheetPreAllocDetailExportVo.class,
JoinWrappers.lambda(ErpTimesheetPreAllocDetail.class)
.selectAs(ErpTimesheetPreAlloc::getAllocCode, "allocCode")
.selectAs(ErpTimesheetPreAlloc::getMonthCode, "monthCode")
.select(ErpTimesheetPreAllocDetail::getOriginalProjectCode,
ErpTimesheetPreAllocDetail::getOriginalProjectName,
ErpTimesheetPreAllocDetail::getTargetProjectCode,
ErpTimesheetPreAllocDetail::getTargetProjectName,
ErpTimesheetPreAllocDetail::getAllocHours)
.leftJoin(ErpTimesheetPreAlloc.class, ErpTimesheetPreAlloc::getAllocId,
ErpTimesheetPreAllocDetail::getAllocId)
.in(ErpTimesheetPreAllocDetail::getAllocId, allocIds)
.eq(ErpTimesheetPreAllocDetail::getDelFlag, "0"));
}
private MPJLambdaWrapper<ErpTimesheetPreAlloc> buildQueryWrapper(ErpTimesheetPreAllocBo bo) {
Long deptId = LoginHelper.getDeptId();
return JoinWrappers.lambda(ErpTimesheetPreAlloc.class)
.selectAll(ErpTimesheetPreAlloc.class)
.eq(ErpTimesheetPreAlloc::getDelFlag, DEL_FLAG_NORMAL)
.eq(deptId != null, ErpTimesheetPreAlloc::getCreateDept, deptId)
.eq(StringUtils.isNotBlank(bo.getAllocCode()), ErpTimesheetPreAlloc::getAllocCode, bo.getAllocCode())
.eq(StringUtils.isNotBlank(bo.getMonthCode()), ErpTimesheetPreAlloc::getMonthCode, bo.getMonthCode())
.eq(bo.getProjectId() != null, ErpTimesheetPreAlloc::getProjectId, bo.getProjectId())
.eq(StringUtils.isNotBlank(bo.getProjectCode()), ErpTimesheetPreAlloc::getProjectCode, bo.getProjectCode())
.like(StringUtils.isNotBlank(bo.getProjectName()), ErpTimesheetPreAlloc::getProjectName, bo.getProjectName())
.eq(StringUtils.isNotBlank(bo.getAllocStatus()), ErpTimesheetPreAlloc::getAllocStatus, bo.getAllocStatus())
.in(StringUtils.isNotBlank(bo.getAllocIds()), ErpTimesheetPreAlloc::getAllocId,
StringUtils.isNotBlank(bo.getAllocIds()) ? List.of(bo.getAllocIds().split(",")) : null)
.orderByDesc(ErpTimesheetPreAlloc::getAllocId);
.selectAll(ErpTimesheetPreAlloc.class)
.eq(ErpTimesheetPreAlloc::getDelFlag, DEL_FLAG_NORMAL)
.eq(deptId != null, ErpTimesheetPreAlloc::getCreateDept, deptId)
.eq(StringUtils.isNotBlank(bo.getAllocCode()), ErpTimesheetPreAlloc::getAllocCode, bo.getAllocCode())
.eq(StringUtils.isNotBlank(bo.getMonthCode()), ErpTimesheetPreAlloc::getMonthCode, bo.getMonthCode())
.eq(bo.getProjectId() != null, ErpTimesheetPreAlloc::getProjectId, bo.getProjectId())
.eq(StringUtils.isNotBlank(bo.getProjectCode()), ErpTimesheetPreAlloc::getProjectCode,
bo.getProjectCode())
.like(StringUtils.isNotBlank(bo.getProjectName()), ErpTimesheetPreAlloc::getProjectName,
bo.getProjectName())
.eq(StringUtils.isNotBlank(bo.getAllocStatus()), ErpTimesheetPreAlloc::getAllocStatus,
bo.getAllocStatus())
.in(StringUtils.isNotBlank(bo.getAllocIds()), ErpTimesheetPreAlloc::getAllocId,
StringUtils.isNotBlank(bo.getAllocIds()) ? List.of(bo.getAllocIds().split(",")) : null)
.orderByDesc(ErpTimesheetPreAlloc::getAllocId);
}
/**
@ -161,8 +201,8 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
ErpTimesheetPreAlloc alloc = queryActiveAlloc(monthCode, projectId, deptId);
List<PreAllocTargetVo> allocItems = buildTargetVos(alloc == null ? null : alloc.getAllocId());
BigDecimal allocatedTotal = allocItems.stream()
.map(item -> nvl(item.getAllocHours()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
.map(item -> nvl(item.getAllocHours()))
.reduce(BigDecimal.ZERO, BigDecimal::add);
BigDecimal sourceTotal = nvl(sourceStat.getSourceTotalHours());
PreAllocDetailVo detailVo = new PreAllocDetailVo();
@ -245,7 +285,8 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
}
AllocationResult allocationResult = buildAllocationResult(bo.getAllocItems(), sourceTotal);
ErpTimesheetPreAlloc alloc = resolveAlloc(bo.getAllocId(), bo.getMonthCode(), sourceProject.getProjectId(), deptId);
ErpTimesheetPreAlloc alloc = resolveAlloc(bo.getAllocId(), bo.getMonthCode(), sourceProject.getProjectId(),
deptId);
boolean isNew = alloc == null;
if (isNew) {
alloc = new ErpTimesheetPreAlloc();
@ -281,8 +322,8 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
return false;
}
List<ErpTimesheetPreAlloc> allocList = baseMapper.selectList(Wrappers.<ErpTimesheetPreAlloc>lambdaQuery()
.in(ErpTimesheetPreAlloc::getAllocId, ids)
.eq(ErpTimesheetPreAlloc::getDelFlag, DEL_FLAG_NORMAL));
.in(ErpTimesheetPreAlloc::getAllocId, ids)
.eq(ErpTimesheetPreAlloc::getDelFlag, DEL_FLAG_NORMAL));
if (allocList.size() != ids.size()) {
throw new ServiceException("部分预投工时分配单不存在或已删除");
}
@ -293,7 +334,7 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
}
}
preAllocDetailMapper.delete(Wrappers.<ErpTimesheetPreAllocDetail>lambdaQuery()
.in(ErpTimesheetPreAllocDetail::getAllocId, ids));
.in(ErpTimesheetPreAllocDetail::getAllocId, ids));
return baseMapper.deleteByIds(ids) > 0;
}
@ -317,14 +358,15 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
private MonthRange monthRange(String monthCode) {
YearMonth yearMonth = YearMonth.of(
Integer.parseInt(monthCode.substring(0, 4)),
Integer.parseInt(monthCode.substring(4, 6)));
Integer.parseInt(monthCode.substring(0, 4)),
Integer.parseInt(monthCode.substring(4, 6)));
return new MonthRange(Date.valueOf(yearMonth.atDay(1)), Date.valueOf(yearMonth.atEndOfMonth()));
}
private PreAllocSourceStatVo loadSourceStat(String monthCode, Long deptId, Long projectId) {
MonthRange range = monthRange(monthCode);
PreAllocSourceStatVo sourceStat = baseMapper.selectSourceStat(deptId, range.startDate(), range.endDate(), projectId);
PreAllocSourceStatVo sourceStat = baseMapper.selectSourceStat(deptId, range.startDate(), range.endDate(),
projectId);
if (sourceStat == null) {
sourceStat = new PreAllocSourceStatVo();
}
@ -355,10 +397,10 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
private ErpTimesheetPreAlloc queryActiveAlloc(String monthCode, Long projectId, Long deptId) {
List<ErpTimesheetPreAlloc> allocList = baseMapper.selectList(Wrappers.<ErpTimesheetPreAlloc>lambdaQuery()
.eq(ErpTimesheetPreAlloc::getMonthCode, monthCode)
.eq(ErpTimesheetPreAlloc::getProjectId, projectId)
.eq(ErpTimesheetPreAlloc::getCreateDept, deptId)
.eq(ErpTimesheetPreAlloc::getDelFlag, DEL_FLAG_NORMAL));
.eq(ErpTimesheetPreAlloc::getMonthCode, monthCode)
.eq(ErpTimesheetPreAlloc::getProjectId, projectId)
.eq(ErpTimesheetPreAlloc::getCreateDept, deptId)
.eq(ErpTimesheetPreAlloc::getDelFlag, DEL_FLAG_NORMAL));
if (allocList.size() > 1) {
throw new ServiceException("当前部门该月份该预投项目存在多张有效分配单,请先修正数据");
}
@ -422,11 +464,11 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
}
private void fillAlloc(ErpTimesheetPreAlloc alloc,
ErpTimesheetPreAllocBo bo,
ErpProjectInfo sourceProject,
PreAllocSourceStatVo sourceStat,
AllocationResult allocationResult,
Long deptId) {
ErpTimesheetPreAllocBo bo,
ErpProjectInfo sourceProject,
PreAllocSourceStatVo sourceStat,
AllocationResult allocationResult,
Long deptId) {
BigDecimal sourceTotal = nvl(sourceStat.getSourceTotalHours());
alloc.setMonthCode(bo.getMonthCode());
alloc.setStandardMonthId(null);
@ -442,10 +484,10 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
}
private void rewritePreAllocDetails(ErpTimesheetPreAlloc alloc,
ErpProjectInfo sourceProject,
List<TargetAllocation> targetAllocations) {
ErpProjectInfo sourceProject,
List<TargetAllocation> targetAllocations) {
preAllocDetailMapper.delete(Wrappers.<ErpTimesheetPreAllocDetail>lambdaQuery()
.eq(ErpTimesheetPreAllocDetail::getAllocId, alloc.getAllocId()));
.eq(ErpTimesheetPreAllocDetail::getAllocId, alloc.getAllocId()));
if (targetAllocations.isEmpty()) {
return;
}
@ -471,11 +513,12 @@ public class ErpTimesheetPreAllocServiceImpl implements IErpTimesheetPreAllocSer
if (allocId == null) {
return Collections.emptyList();
}
List<ErpTimesheetPreAllocDetail> detailList = preAllocDetailMapper.selectList(Wrappers.<ErpTimesheetPreAllocDetail>lambdaQuery()
.eq(ErpTimesheetPreAllocDetail::getAllocId, allocId)
.eq(ErpTimesheetPreAllocDetail::getDelFlag, DEL_FLAG_NORMAL)
.orderByAsc(ErpTimesheetPreAllocDetail::getSortOrder)
.orderByAsc(ErpTimesheetPreAllocDetail::getAllocDetailId));
List<ErpTimesheetPreAllocDetail> detailList = preAllocDetailMapper
.selectList(Wrappers.<ErpTimesheetPreAllocDetail>lambdaQuery()
.eq(ErpTimesheetPreAllocDetail::getAllocId, allocId)
.eq(ErpTimesheetPreAllocDetail::getDelFlag, DEL_FLAG_NORMAL)
.orderByAsc(ErpTimesheetPreAllocDetail::getSortOrder)
.orderByAsc(ErpTimesheetPreAllocDetail::getAllocDetailId));
List<PreAllocTargetVo> targetVos = new ArrayList<>();
for (ErpTimesheetPreAllocDetail detail : detailList) {
PreAllocTargetVo targetVo = new PreAllocTargetVo();

Loading…
Cancel
Save