feat(erp/timesheet): 新增工时填报管理模块

dev
Yangk 2 months ago
parent 26954f9bf7
commit 3ed1dd0829

@ -0,0 +1,116 @@
package org.dromara.oa.erp.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.oa.erp.domain.vo.ErpTimesheetDeptVo;
import org.dromara.oa.erp.domain.bo.ErpTimesheetDeptBo;
import org.dromara.oa.erp.service.IErpTimesheetDeptService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
*
* 访:/oa/erp/timesheetDept
*
* @author Yangk
* @date 2025-12-09
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/erp/timesheetDept")
public class ErpTimesheetDeptController extends BaseController {
private final IErpTimesheetDeptService erpTimesheetDeptService;
/**
*
*/
@SaCheckPermission("oa/erp:timesheetDept:list")
@GetMapping("/list")
public TableDataInfo<ErpTimesheetDeptVo> list(ErpTimesheetDeptBo bo, PageQuery pageQuery) {
return erpTimesheetDeptService.queryPageList(bo, pageQuery);
}
/**
*
*/
@SaCheckPermission("oa/erp:timesheetDept:export")
@Log(title = "部门工作明细", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(ErpTimesheetDeptBo bo, HttpServletResponse response) {
List<ErpTimesheetDeptVo> list = erpTimesheetDeptService.queryList(bo);
ExcelUtil.exportExcel(list, "部门工作明细", ErpTimesheetDeptVo.class, response);
}
/**
*
*
* @param timesheetDeptId
*/
@SaCheckPermission("oa/erp:timesheetDept:query")
@GetMapping("/{timesheetDeptId}")
public R<ErpTimesheetDeptVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable("timesheetDeptId") Long timesheetDeptId) {
return R.ok(erpTimesheetDeptService.queryById(timesheetDeptId));
}
/**
*
*/
@SaCheckPermission("oa/erp:timesheetDept:add")
@Log(title = "部门工作明细", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody ErpTimesheetDeptBo bo) {
return toAjax(erpTimesheetDeptService.insertByBo(bo));
}
/**
*
*/
@SaCheckPermission("oa/erp:timesheetDept:edit")
@Log(title = "部门工作明细", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ErpTimesheetDeptBo bo) {
return toAjax(erpTimesheetDeptService.updateByBo(bo));
}
/**
*
*
* @param timesheetDeptIds
*/
@SaCheckPermission("oa/erp:timesheetDept:remove")
@Log(title = "部门工作明细", businessType = BusinessType.DELETE)
@DeleteMapping("/{timesheetDeptIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable("timesheetDeptIds") Long[] timesheetDeptIds) {
return toAjax(erpTimesheetDeptService.deleteWithValidByIds(List.of(timesheetDeptIds), true));
}
/**
*
*/
@GetMapping("/getErpTimesheetDeptList")
public R<List<ErpTimesheetDeptVo>> getErpTimesheetDeptList(ErpTimesheetDeptBo bo) {
List<ErpTimesheetDeptVo> list = erpTimesheetDeptService.queryList(bo);
return R.ok(list);
}
}

@ -0,0 +1,116 @@
package org.dromara.oa.erp.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.oa.erp.domain.vo.ErpTimesheetInfoVo;
import org.dromara.oa.erp.domain.bo.ErpTimesheetInfoBo;
import org.dromara.oa.erp.service.IErpTimesheetInfoService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
*
* 访:/oa/erp/timesheetInfo
*
* @author Yangk
* @date 2025-12-09
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/erp/timesheetInfo")
public class ErpTimesheetInfoController extends BaseController {
private final IErpTimesheetInfoService erpTimesheetInfoService;
/**
*
*/
@SaCheckPermission("oa/erp:timesheetInfo:list")
@GetMapping("/list")
public TableDataInfo<ErpTimesheetInfoVo> list(ErpTimesheetInfoBo bo, PageQuery pageQuery) {
return erpTimesheetInfoService.queryPageList(bo, pageQuery);
}
/**
*
*/
@SaCheckPermission("oa/erp:timesheetInfo:export")
@Log(title = "工时填报", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(ErpTimesheetInfoBo bo, HttpServletResponse response) {
List<ErpTimesheetInfoVo> list = erpTimesheetInfoService.queryList(bo);
ExcelUtil.exportExcel(list, "工时填报", ErpTimesheetInfoVo.class, response);
}
/**
*
*
* @param timesheetId
*/
@SaCheckPermission("oa/erp:timesheetInfo:query")
@GetMapping("/{timesheetId}")
public R<ErpTimesheetInfoVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable("timesheetId") Long timesheetId) {
return R.ok(erpTimesheetInfoService.queryById(timesheetId));
}
/**
*
*/
@SaCheckPermission("oa/erp:timesheetInfo:add")
@Log(title = "工时填报", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody ErpTimesheetInfoBo bo) {
return toAjax(erpTimesheetInfoService.insertByBo(bo));
}
/**
*
*/
@SaCheckPermission("oa/erp:timesheetInfo:edit")
@Log(title = "工时填报", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ErpTimesheetInfoBo bo) {
return toAjax(erpTimesheetInfoService.updateByBo(bo));
}
/**
*
*
* @param timesheetIds
*/
@SaCheckPermission("oa/erp:timesheetInfo:remove")
@Log(title = "工时填报", businessType = BusinessType.DELETE)
@DeleteMapping("/{timesheetIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable("timesheetIds") Long[] timesheetIds) {
return toAjax(erpTimesheetInfoService.deleteWithValidByIds(List.of(timesheetIds), true));
}
/**
*
*/
@GetMapping("/getErpTimesheetInfoList")
public R<List<ErpTimesheetInfoVo>> getErpTimesheetInfoList(ErpTimesheetInfoBo bo) {
List<ErpTimesheetInfoVo> list = erpTimesheetInfoService.queryList(bo);
return R.ok(list);
}
}

@ -0,0 +1,116 @@
package org.dromara.oa.erp.controller;
import java.util.List;
import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission;
import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit;
import org.dromara.common.log.annotation.Log;
import org.dromara.common.web.core.BaseController;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.core.domain.R;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.excel.utils.ExcelUtil;
import org.dromara.oa.erp.domain.vo.ErpTimesheetProjectVo;
import org.dromara.oa.erp.domain.bo.ErpTimesheetProjectBo;
import org.dromara.oa.erp.service.IErpTimesheetProjectService;
import org.dromara.common.mybatis.core.page.TableDataInfo;
/**
*
* 访:/oa/erp/timesheetProject
*
* @author Yangk
* @date 2025-12-09
*/
@Validated
@RequiredArgsConstructor
@RestController
@RequestMapping("/erp/timesheetProject")
public class ErpTimesheetProjectController extends BaseController {
private final IErpTimesheetProjectService erpTimesheetProjectService;
/**
*
*/
@SaCheckPermission("oa/erp:timesheetProject:list")
@GetMapping("/list")
public TableDataInfo<ErpTimesheetProjectVo> list(ErpTimesheetProjectBo bo, PageQuery pageQuery) {
return erpTimesheetProjectService.queryPageList(bo, pageQuery);
}
/**
*
*/
@SaCheckPermission("oa/erp:timesheetProject:export")
@Log(title = "项目工作明细", businessType = BusinessType.EXPORT)
@PostMapping("/export")
public void export(ErpTimesheetProjectBo bo, HttpServletResponse response) {
List<ErpTimesheetProjectVo> list = erpTimesheetProjectService.queryList(bo);
ExcelUtil.exportExcel(list, "项目工作明细", ErpTimesheetProjectVo.class, response);
}
/**
*
*
* @param timesheetProjectId
*/
@SaCheckPermission("oa/erp:timesheetProject:query")
@GetMapping("/{timesheetProjectId}")
public R<ErpTimesheetProjectVo> getInfo(@NotNull(message = "主键不能为空")
@PathVariable("timesheetProjectId") Long timesheetProjectId) {
return R.ok(erpTimesheetProjectService.queryById(timesheetProjectId));
}
/**
*
*/
@SaCheckPermission("oa/erp:timesheetProject:add")
@Log(title = "项目工作明细", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping()
public R<Void> add(@Validated(AddGroup.class) @RequestBody ErpTimesheetProjectBo bo) {
return toAjax(erpTimesheetProjectService.insertByBo(bo));
}
/**
*
*/
@SaCheckPermission("oa/erp:timesheetProject:edit")
@Log(title = "项目工作明细", businessType = BusinessType.UPDATE)
@RepeatSubmit()
@PutMapping()
public R<Void> edit(@Validated(EditGroup.class) @RequestBody ErpTimesheetProjectBo bo) {
return toAjax(erpTimesheetProjectService.updateByBo(bo));
}
/**
*
*
* @param timesheetProjectIds
*/
@SaCheckPermission("oa/erp:timesheetProject:remove")
@Log(title = "项目工作明细", businessType = BusinessType.DELETE)
@DeleteMapping("/{timesheetProjectIds}")
public R<Void> remove(@NotEmpty(message = "主键不能为空")
@PathVariable("timesheetProjectIds") Long[] timesheetProjectIds) {
return toAjax(erpTimesheetProjectService.deleteWithValidByIds(List.of(timesheetProjectIds), true));
}
/**
*
*/
@GetMapping("/getErpTimesheetProjectList")
public R<List<ErpTimesheetProjectVo>> getErpTimesheetProjectList(ErpTimesheetProjectBo bo) {
List<ErpTimesheetProjectVo> list = erpTimesheetProjectService.queryList(bo);
return R.ok(list);
}
}

@ -0,0 +1,68 @@
package org.dromara.oa.erp.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.math.BigDecimal;
/**
* erp_timesheet_dept
*
* @author Yangk
* @date 2025-12-09
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("erp_timesheet_dept")
public class ErpTimesheetDept extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(value = "timesheet_dept_id", type = IdType.ASSIGN_ID)
private Long timesheetDeptId;
/**
* ID
*/
private Long timesheetId;
/**
*
*/
private Long sortOrder;
/**
*
*/
private String workDescription;
/**
* ID
*/
private Long deptId;
/**
* 线ID
*/
private Long deptManagerId;
/**
*
*/
private BigDecimal hours;
/**
* 0 1
*/
@TableLogic
private String delFlag;
}

@ -0,0 +1,96 @@
package org.dromara.oa.erp.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.math.BigDecimal;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import java.io.Serial;
/**
* erp_timesheet_info
*
* @author Yangk
* @date 2025-12-09
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("erp_timesheet_info")
public class ErpTimesheetInfo extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(value = "timesheet_id", type = IdType.ASSIGN_ID)
private Long timesheetId;
/**
*
*/
private String timesheetCode;
/**
* ID
*/
private Long userId;
/**
* ID
*/
private Long deptId;
/**
*
*/
private Date startTime;
/**
*
*/
private Date endTime;
/**
*
*/
private BigDecimal totalHours;
/**
*
*/
private BigDecimal deptHours;
/**
*
*/
private BigDecimal projectHours;
/**
* 1 2 3 4
*/
private String timesheetStatus;
/**
*
*/
private String flowStatus;
/**
*
*/
private String remark;
/**
* 0 1
*/
@TableLogic
private String delFlag;
}

@ -0,0 +1,78 @@
package org.dromara.oa.erp.domain;
import org.dromara.common.tenant.core.TenantEntity;
import com.baomidou.mybatisplus.annotation.*;
import lombok.Data;
import lombok.EqualsAndHashCode;
import java.io.Serial;
import java.math.BigDecimal;
/**
* erp_timesheet_project
*
* @author Yangk
* @date 2025-12-09
*/
@Data
@EqualsAndHashCode(callSuper = true)
@TableName("erp_timesheet_project")
public class ErpTimesheetProject extends TenantEntity {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@TableId(value = "timesheet_project_id", type = IdType.ASSIGN_ID)
private Long timesheetProjectId;
/**
* ID
*/
private Long timesheetId;
/**
*
*/
private Long sortOrder;
/**
* ID
*/
private Long projectId;
/**
*
*/
private String projectCode;
/**
*
*/
private String projectName;
/**
* ID
*/
private Long projectManagerId;
/**
* ID
*/
private Long deptId;
/**
*
*/
private BigDecimal hours;
/**
* 0 1
*/
@TableLogic
private String delFlag;
}

@ -172,13 +172,11 @@ public class ErpAfterSalesBo extends BaseEntity {
/** /**
* *
* JSON "laborCostsList": [...]
*/ */
private List<ErpAfterSalesLaborCostsBo> laborCostsList; private List<ErpAfterSalesLaborCostsBo> laborCostsList;
/** /**
* *
* JSON "materialCostsList": [...]
*/ */
private List<ErpAfterSalesMaterialCostsBo> materialCostsList; private List<ErpAfterSalesMaterialCostsBo> materialCostsList;
@ -188,7 +186,7 @@ public class ErpAfterSalesBo extends BaseEntity {
private String flowCode; private String flowCode;
/** /**
* {'entity': {}} *
*/ */
private Map<String, Object> variables; private Map<String, Object> variables;

@ -0,0 +1,61 @@
package org.dromara.oa.erp.domain.bo;
import org.dromara.oa.erp.domain.ErpTimesheetDept;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* erp_timesheet_dept
*
* @author Yangk
* @date 2025-12-09
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = ErpTimesheetDept.class, reverseConvertGenerate = false)
public class ErpTimesheetDeptBo extends BaseEntity {
/**
* ID
*/
@NotNull(message = "部门工作ID不能为空", groups = { EditGroup.class })
private Long timesheetDeptId;
/**
* ID
*/
@NotNull(message = "工时填报ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long timesheetId;
/**
*
*/
private Long sortOrder;
/**
*
*/
private String workDescription;
/**
* ID
*/
private Long deptId;
/**
* 线ID
*/
private Long deptManagerId;
/**
*
*/
private Long hours;
}

@ -0,0 +1,120 @@
package org.dromara.oa.erp.domain.bo;
import org.dromara.oa.erp.domain.ErpTimesheetInfo;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
import java.util.Date;
import java.util.List;
import java.util.Map;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.workflow.api.domain.RemoteFlowInstanceBizExt;
/**
* erp_timesheet_info
*
* @author Yangk
* @date 2025-12-09
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = ErpTimesheetInfo.class, reverseConvertGenerate = false)
public class ErpTimesheetInfoBo extends BaseEntity {
/**
* ID
*/
@NotNull(message = "工时填报ID不能为空", groups = { EditGroup.class })
private Long timesheetId;
/**
*
*/
@NotBlank(message = "工时填报编号不能为空", groups = { AddGroup.class, EditGroup.class })
private String timesheetCode;
/**
* ID
*/
@NotNull(message = "人员ID填报人不能为空", groups = { AddGroup.class, EditGroup.class })
private Long userId;
/**
* ID
*/
private Long deptId;
/**
*
*/
@NotNull(message = "起始时间(自然周的周一)不能为空", groups = { AddGroup.class, EditGroup.class })
private Date startTime;
/**
*
*/
@NotNull(message = "结束时间(自然周的周日)不能为空", groups = { AddGroup.class, EditGroup.class })
private Date endTime;
/**
*
*/
private Long totalHours;
/**
*
*/
private Long deptHours;
/**
*
*/
private Long projectHours;
/**
* 1 2 3 4
*/
private String timesheetStatus;
/**
*
*/
private String flowStatus;
/**
*
*/
private String remark;
/**
*
*/
private List<ErpTimesheetDeptBo> timesheetDeptList;
/**
*
*/
private List<ErpTimesheetProjectBo> timesheetProjectList;
/**
*
*/
private String flowCode;
/**
*
*/
private Map<String, Object> variables;
/**
*
*/
private RemoteFlowInstanceBizExt bizExt;
}

@ -0,0 +1,71 @@
package org.dromara.oa.erp.domain.bo;
import org.dromara.oa.erp.domain.ErpTimesheetProject;
import org.dromara.common.mybatis.core.domain.BaseEntity;
import org.dromara.common.core.validate.AddGroup;
import org.dromara.common.core.validate.EditGroup;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import lombok.EqualsAndHashCode;
import jakarta.validation.constraints.*;
/**
* erp_timesheet_project
*
* @author Yangk
* @date 2025-12-09
*/
@Data
@EqualsAndHashCode(callSuper = true)
@AutoMapper(target = ErpTimesheetProject.class, reverseConvertGenerate = false)
public class ErpTimesheetProjectBo extends BaseEntity {
/**
* ID
*/
@NotNull(message = "项目工作ID不能为空", groups = { EditGroup.class })
private Long timesheetProjectId;
/**
* ID
*/
@NotNull(message = "工时填报ID不能为空", groups = { AddGroup.class, EditGroup.class })
private Long timesheetId;
/**
*
*/
private Long sortOrder;
/**
* ID
*/
private Long projectId;
/**
*
*/
private String projectCode;
/**
*
*/
private String projectName;
/**
* ID
*/
private Long projectManagerId;
/**
* ID
*/
private Long deptId;
/**
*
*/
private Long hours;
}

@ -46,7 +46,7 @@ public class ErpAfterSalesVo implements Serializable {
private String afterSalesSubject; private String afterSalesSubject;
/** /**
* () *
*/ */
@ExcelProperty(value = "项目名称") @ExcelProperty(value = "项目名称")
private String projectName; private String projectName;
@ -55,7 +55,7 @@ public class ErpAfterSalesVo implements Serializable {
private String projectCode; private String projectCode;
/** /**
* () *
*/ */
@ExcelProperty(value = "客户名称") @ExcelProperty(value = "客户名称")
private String customerName; private String customerName;
@ -81,7 +81,7 @@ public class ErpAfterSalesVo implements Serializable {
private Date afterSalesDate; private Date afterSalesDate;
/** /**
* () *
*/ */
@ExcelProperty(value = "合同号") @ExcelProperty(value = "合同号")
private String contractCode; private String contractCode;
@ -101,7 +101,7 @@ public class ErpAfterSalesVo implements Serializable {
private String stakeholderId; private String stakeholderId;
/** /**
* () *
*/ */
@ExcelProperty(value = "客户干系人") @ExcelProperty(value = "客户干系人")
private String stakeholderName; private String stakeholderName;
@ -112,7 +112,7 @@ public class ErpAfterSalesVo implements Serializable {
private String handlerId; private String handlerId;
/** /**
* () *
*/ */
@ExcelProperty(value = "处理人") @ExcelProperty(value = "处理人")
private String handlerName; private String handlerName;

@ -0,0 +1,74 @@
package org.dromara.oa.erp.domain.vo;
import org.dromara.oa.erp.domain.ErpTimesheetDept;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* erp_timesheet_dept
*
* @author Yangk
* @date 2025-12-09
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = ErpTimesheetDept.class)
public class ErpTimesheetDeptVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@ExcelProperty(value = "部门工作ID")
private Long timesheetDeptId;
/**
* ID
*/
@ExcelProperty(value = "工时填报ID")
private Long timesheetId;
/**
*
*/
@ExcelProperty(value = "序号")
private Long sortOrder;
/**
*
*/
@ExcelProperty(value = "部门工作描述")
private String workDescription;
/**
* ID
*/
@ExcelProperty(value = "部门ID")
private Long deptId;
/**
* 线ID
*/
@ExcelProperty(value = "部门直线经理ID")
private Long deptManagerId;
/**
*
*/
@ExcelProperty(value = "工时")
private Long hours;
}

@ -0,0 +1,120 @@
package org.dromara.oa.erp.domain.vo;
import java.util.Date;
import com.fasterxml.jackson.annotation.JsonFormat;
import org.dromara.oa.erp.domain.ErpTimesheetInfo;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
import java.util.List;
/**
* erp_timesheet_info
*
* @author Yangk
* @date 2025-12-09
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = ErpTimesheetInfo.class)
public class ErpTimesheetInfoVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@ExcelProperty(value = "工时填报ID")
private Long timesheetId;
/**
*
*/
@ExcelProperty(value = "工时填报编号")
private String timesheetCode;
/**
* ID
*/
@ExcelProperty(value = "人员ID", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "填=报人")
private Long userId;
/**
* ID
*/
@ExcelProperty(value = "部门ID")
private Long deptId;
/**
*
*/
@ExcelProperty(value = "起始时间", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "自=然周的周一")
private Date startTime;
/**
*
*/
@ExcelProperty(value = "结束时间", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "自=然周的周日")
private Date endTime;
/**
*
*/
@ExcelProperty(value = "总工时", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "自=动累计")
private Long totalHours;
/**
*
*/
@ExcelProperty(value = "部门工时", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "根=据部门工作自动累计")
private Long deptHours;
/**
*
*/
@ExcelProperty(value = "项目工时", converter = ExcelDictConvert.class)
@ExcelDictFormat(readConverterExp = "根=据项目工作自动累计")
private Long projectHours;
/**
* 1 2 3 4
*/
@ExcelProperty(value = "工时填报状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "timesheet_status")
private String timesheetStatus;
/**
*
*/
@ExcelProperty(value = "流程状态", converter = ExcelDictConvert.class)
@ExcelDictFormat(dictType = "flow_status")
private String flowStatus;
/**
*
*/
@ExcelProperty(value = "备注")
private String remark;
/**
*
*/
private List<ErpTimesheetDeptVo> timesheetDeptList;
private List<ErpTimesheetProjectVo> timesheetProjectList;
}

@ -0,0 +1,86 @@
package org.dromara.oa.erp.domain.vo;
import org.dromara.oa.erp.domain.ErpTimesheetProject;
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
import cn.idev.excel.annotation.ExcelProperty;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import java.io.Serial;
import java.io.Serializable;
import java.util.Date;
/**
* erp_timesheet_project
*
* @author Yangk
* @date 2025-12-09
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = ErpTimesheetProject.class)
public class ErpTimesheetProjectVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* ID
*/
@ExcelProperty(value = "项目工作ID")
private Long timesheetProjectId;
/**
* ID
*/
@ExcelProperty(value = "工时填报ID")
private Long timesheetId;
/**
*
*/
@ExcelProperty(value = "序号")
private Long sortOrder;
/**
* ID
*/
@ExcelProperty(value = "项目ID")
private Long projectId;
/**
*
*/
@ExcelProperty(value = "项目号")
private String projectCode;
/**
*
*/
@ExcelProperty(value = "项目名称")
private String projectName;
/**
* ID
*/
@ExcelProperty(value = "项目经理ID")
private Long projectManagerId;
/**
* ID
*/
@ExcelProperty(value = "部门ID")
private Long deptId;
/**
*
*/
@ExcelProperty(value = "工时")
private Long hours;
}

@ -0,0 +1,37 @@
package org.dromara.oa.erp.mapper;
import java.util.List;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.oa.erp.domain.ErpTimesheetDept;
import org.dromara.oa.erp.domain.vo.ErpTimesheetDeptVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* Mapper
*
* @author Yangk
* @date 2025-12-09
*/
public interface ErpTimesheetDeptMapper extends BaseMapperPlus<ErpTimesheetDept, ErpTimesheetDeptVo> {
/**
*
*
* @param page
* @param queryWrapper
* @return
*/
public Page<ErpTimesheetDeptVo> selectCustomErpTimesheetDeptVoList(@Param("page") Page<ErpTimesheetDeptVo> page, @Param(Constants.WRAPPER) MPJLambdaWrapper<ErpTimesheetDept> queryWrapper);
/**
*
*
* @param queryWrapper
* @return
*/
public List<ErpTimesheetDeptVo> selectCustomErpTimesheetDeptVoList(@Param(Constants.WRAPPER) MPJLambdaWrapper<ErpTimesheetDept> queryWrapper);
}

@ -0,0 +1,37 @@
package org.dromara.oa.erp.mapper;
import java.util.List;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.oa.erp.domain.ErpTimesheetInfo;
import org.dromara.oa.erp.domain.vo.ErpTimesheetInfoVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* Mapper
*
* @author Yangk
* @date 2025-12-09
*/
public interface ErpTimesheetInfoMapper extends BaseMapperPlus<ErpTimesheetInfo, ErpTimesheetInfoVo> {
/**
*
*
* @param page
* @param queryWrapper
* @return
*/
public Page<ErpTimesheetInfoVo> selectCustomErpTimesheetInfoVoList(@Param("page") Page<ErpTimesheetInfoVo> page, @Param(Constants.WRAPPER) MPJLambdaWrapper<ErpTimesheetInfo> queryWrapper);
/**
*
*
* @param queryWrapper
* @return
*/
public List<ErpTimesheetInfoVo> selectCustomErpTimesheetInfoVoList(@Param(Constants.WRAPPER) MPJLambdaWrapper<ErpTimesheetInfo> queryWrapper);
}

@ -0,0 +1,37 @@
package org.dromara.oa.erp.mapper;
import java.util.List;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.baomidou.mybatisplus.core.toolkit.Constants;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import org.dromara.oa.erp.domain.ErpTimesheetProject;
import org.dromara.oa.erp.domain.vo.ErpTimesheetProjectVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
/**
* Mapper
*
* @author Yangk
* @date 2025-12-09
*/
public interface ErpTimesheetProjectMapper extends BaseMapperPlus<ErpTimesheetProject, ErpTimesheetProjectVo> {
/**
*
*
* @param page
* @param queryWrapper
* @return
*/
public Page<ErpTimesheetProjectVo> selectCustomErpTimesheetProjectVoList(@Param("page") Page<ErpTimesheetProjectVo> page, @Param(Constants.WRAPPER) MPJLambdaWrapper<ErpTimesheetProject> queryWrapper);
/**
*
*
* @param queryWrapper
* @return
*/
public List<ErpTimesheetProjectVo> selectCustomErpTimesheetProjectVoList(@Param(Constants.WRAPPER) MPJLambdaWrapper<ErpTimesheetProject> queryWrapper);
}

@ -0,0 +1,70 @@
package org.dromara.oa.erp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.oa.erp.domain.ErpTimesheetDept;
import org.dromara.oa.erp.domain.vo.ErpTimesheetDeptVo;
import org.dromara.oa.erp.domain.bo.ErpTimesheetDeptBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
* Service
*
* @author Yangk
* @date 2025-12-09
*/
public interface IErpTimesheetDeptService extends IService<ErpTimesheetDept> {
/**
*
*
* @param timesheetDeptId
* @return
*/
ErpTimesheetDeptVo queryById(Long timesheetDeptId);
/**
*
*
* @param bo
* @param pageQuery
* @return
*/
TableDataInfo<ErpTimesheetDeptVo> queryPageList(ErpTimesheetDeptBo bo, PageQuery pageQuery);
/**
*
*
* @param bo
* @return
*/
List<ErpTimesheetDeptVo> queryList(ErpTimesheetDeptBo bo);
/**
*
*
* @param bo
* @return
*/
Boolean insertByBo(ErpTimesheetDeptBo bo);
/**
*
*
* @param bo
* @return
*/
Boolean updateByBo(ErpTimesheetDeptBo bo);
/**
*
*
* @param ids
* @param isValid
* @return
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

@ -0,0 +1,79 @@
package org.dromara.oa.erp.service;
import org.dromara.oa.erp.domain.ErpTimesheetInfo;
import org.dromara.oa.erp.domain.bo.ErpAfterSalesBo;
import org.dromara.oa.erp.domain.vo.ErpAfterSalesVo;
import org.dromara.oa.erp.domain.vo.ErpTimesheetDeptVo;
import org.dromara.oa.erp.domain.vo.ErpTimesheetInfoVo;
import org.dromara.oa.erp.domain.bo.ErpTimesheetInfoBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
* Service
*
* @author Yangk
* @date 2025-12-09
*/
public interface IErpTimesheetInfoService {
/**
*
*
* @param timesheetId
* @return
*/
ErpTimesheetInfoVo queryById(Long timesheetId);
/**
*
*
* @param bo
* @param pageQuery
* @return
*/
TableDataInfo<ErpTimesheetInfoVo> queryPageList(ErpTimesheetInfoBo bo, PageQuery pageQuery);
/**
*
*
* @param bo
* @return
*/
List<ErpTimesheetInfoVo> queryList(ErpTimesheetInfoBo bo);
/**
*
*
* @param bo
* @return
*/
Boolean insertByBo(ErpTimesheetInfoBo bo);
/**
*
*
* @param bo
* @return
*/
Boolean updateByBo(ErpTimesheetInfoBo bo);
/**
*
*
* @param ids
* @param isValid
* @return
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
/**
*
* @param bo
* @return
*/
ErpTimesheetInfoVo submitAndFlowStart(ErpTimesheetInfoBo bo);
}

@ -0,0 +1,70 @@
package org.dromara.oa.erp.service;
import com.baomidou.mybatisplus.extension.service.IService;
import org.dromara.oa.erp.domain.ErpTimesheetProject;
import org.dromara.oa.erp.domain.vo.ErpTimesheetProjectVo;
import org.dromara.oa.erp.domain.bo.ErpTimesheetProjectBo;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import java.util.Collection;
import java.util.List;
/**
* Service
*
* @author Yangk
* @date 2025-12-09
*/
public interface IErpTimesheetProjectService extends IService<ErpTimesheetProject> {
/**
*
*
* @param timesheetProjectId
* @return
*/
ErpTimesheetProjectVo queryById(Long timesheetProjectId);
/**
*
*
* @param bo
* @param pageQuery
* @return
*/
TableDataInfo<ErpTimesheetProjectVo> queryPageList(ErpTimesheetProjectBo bo, PageQuery pageQuery);
/**
*
*
* @param bo
* @return
*/
List<ErpTimesheetProjectVo> queryList(ErpTimesheetProjectBo bo);
/**
*
*
* @param bo
* @return
*/
Boolean insertByBo(ErpTimesheetProjectBo bo);
/**
*
*
* @param bo
* @return
*/
Boolean updateByBo(ErpTimesheetProjectBo bo);
/**
*
*
* @param ids
* @param isValid
* @return
*/
Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid);
}

@ -159,7 +159,7 @@ public class ErpAfterSalesServiceImpl implements IErpAfterSalesService {
* @return * @return
*/ */
@Override @Override
@Transactional(rollbackFor = Exception.class) // 【重要】开启事务!如果子表保存失败,主表也要回滚,防止数据不一致 @Transactional(rollbackFor = Exception.class) // 开启事务!如果子表保存失败,主表也要回滚,防止数据不一致
public Boolean insertByBo(ErpAfterSalesBo bo) { public Boolean insertByBo(ErpAfterSalesBo bo) {
ErpAfterSales add = MapstructUtils.convert(bo, ErpAfterSales.class); ErpAfterSales add = MapstructUtils.convert(bo, ErpAfterSales.class);
@ -195,7 +195,7 @@ public class ErpAfterSalesServiceImpl implements IErpAfterSalesService {
* @return * @return
*/ */
@Override @Override
@Transactional(rollbackFor = Exception.class) // 【重要】开启事务 @Transactional(rollbackFor = Exception.class) // 开启事务
public Boolean updateByBo(ErpAfterSalesBo bo) { public Boolean updateByBo(ErpAfterSalesBo bo) {
ErpAfterSales update = MapstructUtils.convert(bo, ErpAfterSales.class); ErpAfterSales update = MapstructUtils.convert(bo, ErpAfterSales.class);
validEntityBeforeSave(update); validEntityBeforeSave(update);
@ -216,7 +216,7 @@ public class ErpAfterSalesServiceImpl implements IErpAfterSalesService {
// 遍历列表,清空 ID // 遍历列表,清空 ID
laborList.forEach(item -> { laborList.forEach(item -> {
item.setLaborCostsId(null); // 关键:清空ID让MP重新生成雪花ID避免和逻辑删除的数据冲突 item.setLaborCostsId(null); // 清空ID让MP重新生成雪花ID避免和逻辑删除的数据冲突
item.setAfterSalesId(afterSalesId); item.setAfterSalesId(afterSalesId);
}); });
@ -232,7 +232,7 @@ public class ErpAfterSalesServiceImpl implements IErpAfterSalesService {
List<ErpAfterSalesMaterialCosts> materialList = MapstructUtils.convert(materialBoList, ErpAfterSalesMaterialCosts.class); List<ErpAfterSalesMaterialCosts> materialList = MapstructUtils.convert(materialBoList, ErpAfterSalesMaterialCosts.class);
materialList.forEach(item -> { materialList.forEach(item -> {
item.setMaterialCostsId(null); // 关键清空ID item.setMaterialCostsId(null);
item.setAfterSalesId(afterSalesId); item.setAfterSalesId(afterSalesId);
}); });

@ -0,0 +1,141 @@
package org.dromara.oa.erp.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.dromara.oa.erp.domain.bo.ErpTimesheetDeptBo;
import org.dromara.oa.erp.domain.vo.ErpTimesheetDeptVo;
import org.dromara.oa.erp.domain.ErpTimesheetDept;
import org.dromara.oa.erp.mapper.ErpTimesheetDeptMapper;
import org.dromara.oa.erp.service.IErpTimesheetDeptService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
* Service
*
* @author Yangk
* @date 2025-12-09
*/
@RequiredArgsConstructor
@Service
public class ErpTimesheetDeptServiceImpl
extends ServiceImpl<ErpTimesheetDeptMapper, ErpTimesheetDept>
implements IErpTimesheetDeptService {
private final ErpTimesheetDeptMapper baseMapper;
/**
*
*
* @param timesheetDeptId
* @return
*/
@Override
public ErpTimesheetDeptVo queryById(Long timesheetDeptId){
return baseMapper.selectVoById(timesheetDeptId);
}
/**
*
*
* @param bo
* @param pageQuery
* @return
*/
@Override
public TableDataInfo<ErpTimesheetDeptVo> queryPageList(ErpTimesheetDeptBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<ErpTimesheetDept> lqw = buildQueryWrapper(bo);
Page<ErpTimesheetDeptVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
*
*
* @param bo
* @return
*/
@Override
public List<ErpTimesheetDeptVo> queryList(ErpTimesheetDeptBo bo) {
MPJLambdaWrapper<ErpTimesheetDept> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private MPJLambdaWrapper<ErpTimesheetDept> buildQueryWrapper(ErpTimesheetDeptBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<ErpTimesheetDept> lqw = JoinWrappers.lambda(ErpTimesheetDept.class)
.selectAll(ErpTimesheetDept.class)
.eq(ErpTimesheetDept::getDelFlag, "0")
.eq(bo.getTimesheetId() != null, ErpTimesheetDept::getTimesheetId, bo.getTimesheetId())
.eq(bo.getSortOrder() != null, ErpTimesheetDept::getSortOrder, bo.getSortOrder())
.eq(StringUtils.isNotBlank(bo.getWorkDescription()), ErpTimesheetDept::getWorkDescription, bo.getWorkDescription())
.eq(bo.getDeptId() != null, ErpTimesheetDept::getDeptId, bo.getDeptId())
.eq(bo.getDeptManagerId() != null, ErpTimesheetDept::getDeptManagerId, bo.getDeptManagerId())
.eq(bo.getHours() != null, ErpTimesheetDept::getHours, bo.getHours())
;
return lqw;
}
/**
*
*
* @param bo
* @return
*/
@Override
public Boolean insertByBo(ErpTimesheetDeptBo bo) {
ErpTimesheetDept add = MapstructUtils.convert(bo, ErpTimesheetDept.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setTimesheetDeptId(add.getTimesheetDeptId());
}
return flag;
}
/**
*
*
* @param bo
* @return
*/
@Override
public Boolean updateByBo(ErpTimesheetDeptBo bo) {
ErpTimesheetDept update = MapstructUtils.convert(bo, ErpTimesheetDept.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
*
*/
private void validEntityBeforeSave(ErpTimesheetDept entity){
//TODO 做一些数据校验,如唯一约束
}
/**
*
*
* @param ids
* @param isValid
* @return
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

@ -0,0 +1,281 @@
package org.dromara.oa.erp.service.impl;
import cn.hutool.core.collection.CollUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.apache.dubbo.config.annotation.DubboReference;
import org.apache.seata.spring.annotation.GlobalTransactional;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.dromara.oa.erp.domain.ErpTimesheetDept;
import org.dromara.oa.erp.domain.ErpTimesheetProject;
import org.dromara.oa.erp.domain.bo.ErpTimesheetDeptBo;
import org.dromara.oa.erp.domain.bo.ErpTimesheetProjectBo;
import org.dromara.oa.erp.domain.vo.ErpTimesheetDeptVo;
import org.dromara.oa.erp.domain.vo.ErpTimesheetProjectVo;
import org.dromara.oa.erp.service.IErpTimesheetDeptService;
import org.dromara.oa.erp.service.IErpTimesheetProjectService;
import org.dromara.workflow.api.RemoteWorkflowService;
import org.dromara.workflow.api.domain.RemoteStartProcess;
import org.springframework.stereotype.Service;
import org.dromara.oa.erp.domain.bo.ErpTimesheetInfoBo;
import org.dromara.oa.erp.domain.vo.ErpTimesheetInfoVo;
import org.dromara.oa.erp.domain.ErpTimesheetInfo;
import org.dromara.oa.erp.mapper.ErpTimesheetInfoMapper;
import org.dromara.oa.erp.service.IErpTimesheetInfoService;
import org.springframework.transaction.annotation.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
* Service
*
* @author Yangk
* @date 2025-12-09
*/
@RequiredArgsConstructor
@Service
public class ErpTimesheetInfoServiceImpl implements IErpTimesheetInfoService {
private final ErpTimesheetInfoMapper baseMapper;
@DubboReference(timeout = 30000)
private RemoteWorkflowService remoteWorkflowService;
private final IErpTimesheetDeptService timesheetDeptService;
private final IErpTimesheetProjectService timesheetProjectService;
/**
*
*
* @param timesheetId
* @return
*/
@Override
public ErpTimesheetInfoVo queryById(Long timesheetId) {
ErpTimesheetInfoVo vo = baseMapper.selectVoById(timesheetId);
if (vo != null) {
// 回显部门工时
List<ErpTimesheetDept> deptList = timesheetDeptService.list(
new LambdaQueryWrapper<ErpTimesheetDept>().eq(ErpTimesheetDept::getTimesheetId, timesheetId)
);
vo.setTimesheetDeptList(MapstructUtils.convert(deptList, ErpTimesheetDeptVo.class));
// 回显项目工时
List<ErpTimesheetProject> projectList = timesheetProjectService.list(
new LambdaQueryWrapper<ErpTimesheetProject>().eq(ErpTimesheetProject::getTimesheetId, timesheetId)
);
vo.setTimesheetProjectList(MapstructUtils.convert(projectList, ErpTimesheetProjectVo.class));
}
return vo;
}
/**
*
*
* @param bo
* @param pageQuery
* @return
*/
@Override
public TableDataInfo<ErpTimesheetInfoVo> queryPageList(ErpTimesheetInfoBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<ErpTimesheetInfo> lqw = buildQueryWrapper(bo);
Page<ErpTimesheetInfoVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
*
*
* @param bo
* @return
*/
@Override
public List<ErpTimesheetInfoVo> queryList(ErpTimesheetInfoBo bo) {
MPJLambdaWrapper<ErpTimesheetInfo> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private MPJLambdaWrapper<ErpTimesheetInfo> buildQueryWrapper(ErpTimesheetInfoBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<ErpTimesheetInfo> lqw = JoinWrappers.lambda(ErpTimesheetInfo.class)
.selectAll(ErpTimesheetInfo.class)
.eq(ErpTimesheetInfo::getDelFlag, "0")
.eq(StringUtils.isNotBlank(bo.getTimesheetCode()), ErpTimesheetInfo::getTimesheetCode, bo.getTimesheetCode())
.eq(bo.getUserId() != null, ErpTimesheetInfo::getUserId, bo.getUserId())
.eq(bo.getDeptId() != null, ErpTimesheetInfo::getDeptId, bo.getDeptId())
.eq(bo.getStartTime() != null, ErpTimesheetInfo::getStartTime, bo.getStartTime())
.eq(bo.getEndTime() != null, ErpTimesheetInfo::getEndTime, bo.getEndTime())
.eq(bo.getTotalHours() != null, ErpTimesheetInfo::getTotalHours, bo.getTotalHours())
.eq(bo.getDeptHours() != null, ErpTimesheetInfo::getDeptHours, bo.getDeptHours())
.eq(bo.getProjectHours() != null, ErpTimesheetInfo::getProjectHours, bo.getProjectHours())
.eq(StringUtils.isNotBlank(bo.getTimesheetStatus()), ErpTimesheetInfo::getTimesheetStatus, bo.getTimesheetStatus())
.eq(StringUtils.isNotBlank(bo.getFlowStatus()), ErpTimesheetInfo::getFlowStatus, bo.getFlowStatus())
;
return lqw;
}
/**
*
*
* @param bo
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean insertByBo(ErpTimesheetInfoBo bo) {
ErpTimesheetInfo add = MapstructUtils.convert(bo, ErpTimesheetInfo.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
// 回填 ID 给 BO
bo.setTimesheetId(add.getTimesheetId());
Long timesheetId = add.getTimesheetId();
// 保存部门工时子表
List<ErpTimesheetDeptBo> deptBoList = bo.getTimesheetDeptList();
if (CollUtil.isNotEmpty(deptBoList)) {
List<ErpTimesheetDept> list = MapstructUtils.convert(deptBoList, ErpTimesheetDept.class);
list.forEach(item -> item.setTimesheetId(timesheetId));
timesheetDeptService.saveBatch(list);
}
// 保存项目工时子表
List<ErpTimesheetProjectBo> projectBoList = bo.getTimesheetProjectList();
if (CollUtil.isNotEmpty(projectBoList)) {
List<ErpTimesheetProject> list = MapstructUtils.convert(projectBoList, ErpTimesheetProject.class);
list.forEach(item -> item.setTimesheetId(timesheetId));
timesheetProjectService.saveBatch(list);
}
}
return flag;
}
/**
*
*
* @param bo
* @return
*/
@Override
@Transactional(rollbackFor = Exception.class)
public Boolean updateByBo(ErpTimesheetInfoBo bo) {
ErpTimesheetInfo update = MapstructUtils.convert(bo, ErpTimesheetInfo.class);
validEntityBeforeSave(update);
int row = baseMapper.updateById(update);
if (row > 0) {
Long timesheetId = bo.getTimesheetId();
// 更新部门工时 (先删后增)
timesheetDeptService.remove(new LambdaQueryWrapper<ErpTimesheetDept>()
.eq(ErpTimesheetDept::getTimesheetId, timesheetId));
List<ErpTimesheetDeptBo> deptBoList = bo.getTimesheetDeptList();
if (CollUtil.isNotEmpty(deptBoList)) {
List<ErpTimesheetDept> list = MapstructUtils.convert(deptBoList, ErpTimesheetDept.class);
list.forEach(item -> {
item.setTimesheetDeptId(null); // 清空子表ID防止逻辑删除冲突
item.setTimesheetId(timesheetId);
});
timesheetDeptService.saveBatch(list);
}
// 更新项目工时 (先删后增)
timesheetProjectService.remove(new LambdaQueryWrapper<ErpTimesheetProject>()
.eq(ErpTimesheetProject::getTimesheetId, timesheetId));
List<ErpTimesheetProjectBo> projectBoList = bo.getTimesheetProjectList();
if (CollUtil.isNotEmpty(projectBoList)) {
List<ErpTimesheetProject> list = MapstructUtils.convert(projectBoList, ErpTimesheetProject.class);
list.forEach(item -> {
item.setTimesheetProjectId(null);
item.setTimesheetId(timesheetId);
});
timesheetProjectService.saveBatch(list);
}
}
return row > 0;
}
/**
*
*/
private void validEntityBeforeSave(ErpTimesheetInfo entity){
//TODO 做一些数据校验,如唯一约束
}
/**
*
*
* @param ids
* @param isValid
* @return
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
/**
*
*
* @param bo
* @return
*/
@Override
@GlobalTransactional(rollbackFor = Exception.class) // 开启全局事务
public ErpTimesheetInfoVo submitAndFlowStart(ErpTimesheetInfoBo bo) {
ErpTimesheetInfo add = MapstructUtils.convert(bo, ErpTimesheetInfo.class);
validEntityBeforeSave(add);
if (bo.getTimesheetId() == null) {
this.insertByBo(bo);
} else {
this.updateByBo(bo);
}
// 准备启动流程参数
// 后端发起需要忽略权限
if (bo.getVariables() == null) {
bo.setVariables(new HashMap<>());
}
bo.getVariables().put("ignore", true);
RemoteStartProcess startProcess = new RemoteStartProcess();
startProcess.setBusinessId(bo.getTimesheetId().toString());
startProcess.setFlowCode(bo.getFlowCode());
startProcess.setVariables(bo.getVariables());
startProcess.setBizExt(bo.getBizExt());
// 确保 BizExt 里也有 BusinessId
if (bo.getBizExt() != null) {
bo.getBizExt().setBusinessId(startProcess.getBusinessId());
}
// 调用远程服务启动流程
// startCompleteTask 表示“启动并自动完成第一个发起节点”,直接流转到下一个审批人
boolean flagOne = remoteWorkflowService.startCompleteTask(startProcess);
if (!flagOne) {
throw new ServiceException("流程发起异常");
}
return MapstructUtils.convert(add, ErpTimesheetInfoVo.class);
}
}

@ -0,0 +1,143 @@
package org.dromara.oa.erp.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.dromara.common.core.utils.MapstructUtils;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.mybatis.core.page.PageQuery;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.yulichang.toolkit.JoinWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.dromara.oa.erp.domain.bo.ErpTimesheetProjectBo;
import org.dromara.oa.erp.domain.vo.ErpTimesheetProjectVo;
import org.dromara.oa.erp.domain.ErpTimesheetProject;
import org.dromara.oa.erp.mapper.ErpTimesheetProjectMapper;
import org.dromara.oa.erp.service.IErpTimesheetProjectService;
import java.util.List;
import java.util.Map;
import java.util.Collection;
/**
* Service
*
* @author Yangk
* @date 2025-12-09
*/
@RequiredArgsConstructor
@Service
public class ErpTimesheetProjectServiceImpl
extends ServiceImpl<ErpTimesheetProjectMapper, ErpTimesheetProject>
implements IErpTimesheetProjectService {
private final ErpTimesheetProjectMapper baseMapper;
/**
*
*
* @param timesheetProjectId
* @return
*/
@Override
public ErpTimesheetProjectVo queryById(Long timesheetProjectId){
return baseMapper.selectVoById(timesheetProjectId);
}
/**
*
*
* @param bo
* @param pageQuery
* @return
*/
@Override
public TableDataInfo<ErpTimesheetProjectVo> queryPageList(ErpTimesheetProjectBo bo, PageQuery pageQuery) {
MPJLambdaWrapper<ErpTimesheetProject> lqw = buildQueryWrapper(bo);
Page<ErpTimesheetProjectVo> result = baseMapper.selectVoPage(pageQuery.build(), lqw);
return TableDataInfo.build(result);
}
/**
*
*
* @param bo
* @return
*/
@Override
public List<ErpTimesheetProjectVo> queryList(ErpTimesheetProjectBo bo) {
MPJLambdaWrapper<ErpTimesheetProject> lqw = buildQueryWrapper(bo);
return baseMapper.selectVoList(lqw);
}
private MPJLambdaWrapper<ErpTimesheetProject> buildQueryWrapper(ErpTimesheetProjectBo bo) {
Map<String, Object> params = bo.getParams();
MPJLambdaWrapper<ErpTimesheetProject> lqw = JoinWrappers.lambda(ErpTimesheetProject.class)
.selectAll(ErpTimesheetProject.class)
.eq(ErpTimesheetProject::getDelFlag, "0")
.eq(bo.getTimesheetId() != null, ErpTimesheetProject::getTimesheetId, bo.getTimesheetId())
.eq(bo.getSortOrder() != null, ErpTimesheetProject::getSortOrder, bo.getSortOrder())
.eq(bo.getProjectId() != null, ErpTimesheetProject::getProjectId, bo.getProjectId())
.eq(StringUtils.isNotBlank(bo.getProjectCode()), ErpTimesheetProject::getProjectCode, bo.getProjectCode())
.like(StringUtils.isNotBlank(bo.getProjectName()), ErpTimesheetProject::getProjectName, bo.getProjectName())
.eq(bo.getProjectManagerId() != null, ErpTimesheetProject::getProjectManagerId, bo.getProjectManagerId())
.eq(bo.getDeptId() != null, ErpTimesheetProject::getDeptId, bo.getDeptId())
.eq(bo.getHours() != null, ErpTimesheetProject::getHours, bo.getHours())
;
return lqw;
}
/**
*
*
* @param bo
* @return
*/
@Override
public Boolean insertByBo(ErpTimesheetProjectBo bo) {
ErpTimesheetProject add = MapstructUtils.convert(bo, ErpTimesheetProject.class);
validEntityBeforeSave(add);
boolean flag = baseMapper.insert(add) > 0;
if (flag) {
bo.setTimesheetProjectId(add.getTimesheetProjectId());
}
return flag;
}
/**
*
*
* @param bo
* @return
*/
@Override
public Boolean updateByBo(ErpTimesheetProjectBo bo) {
ErpTimesheetProject update = MapstructUtils.convert(bo, ErpTimesheetProject.class);
validEntityBeforeSave(update);
return baseMapper.updateById(update) > 0;
}
/**
*
*/
private void validEntityBeforeSave(ErpTimesheetProject entity){
//TODO 做一些数据校验,如唯一约束
}
/**
*
*
* @param ids
* @param isValid
* @return
*/
@Override
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if(isValid){
//TODO 做一些业务上的校验,判断是否需要校验
}
return baseMapper.deleteByIds(ids) > 0;
}
}

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.oa.erp.mapper.ErpTimesheetDeptMapper">
<resultMap type="org.dromara.oa.erp.domain.vo.ErpTimesheetDeptVo" id="ErpTimesheetDeptResult">
</resultMap>
<select id="selectCustomErpTimesheetDeptVoList" resultMap="ErpTimesheetDeptResult">
select timesheet_dept_id, tenant_id, timesheet_id, sort_order, work_description, dept_id, dept_manager_id, hours, del_flag, create_dept, create_by, create_time, update_by, update_time from erp_timesheet_dept t
${ew.getCustomSqlSegment}
</select>
</mapper>

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.oa.erp.mapper.ErpTimesheetInfoMapper">
<resultMap type="org.dromara.oa.erp.domain.vo.ErpTimesheetInfoVo" id="ErpTimesheetInfoResult">
</resultMap>
<select id="selectCustomErpTimesheetInfoVoList" resultMap="ErpTimesheetInfoResult">
select timesheet_id, tenant_id, timesheet_code, user_id, dept_id, start_time, end_time, total_hours, dept_hours, project_hours, timesheet_status, flow_status, remark, del_flag, create_dept, create_by, create_time, update_by, update_time from erp_timesheet_info t
${ew.getCustomSqlSegment}
</select>
</mapper>

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="org.dromara.oa.erp.mapper.ErpTimesheetProjectMapper">
<resultMap type="org.dromara.oa.erp.domain.vo.ErpTimesheetProjectVo" id="ErpTimesheetProjectResult">
</resultMap>
<select id="selectCustomErpTimesheetProjectVoList" resultMap="ErpTimesheetProjectResult">
select timesheet_project_id, tenant_id, timesheet_id, sort_order, project_id, project_code, project_name, project_manager_id, dept_id, hours, del_flag, create_dept, create_by, create_time, update_by, update_time from erp_timesheet_project t
${ew.getCustomSqlSegment}
</select>
</mapper>
Loading…
Cancel
Save