feat(新增加班申请功能): 新增申请功能,可在工作台进行审核。
fix(dict_data): 字典数据的颜色类型字段不允许null更新。
This commit is contained in:
@@ -227,4 +227,17 @@ public interface ErrorCodeConstants {
|
||||
ErrorCode PERSONAL_ITEM_STATUS_NOT_ALLOW_EDIT = new ErrorCode(1_008_008_007, "当前个人事项状态不允许编辑");
|
||||
ErrorCode PERSONAL_ITEM_NOT_ALLOW_DELETE = new ErrorCode(1_008_008_008, "仅初始态(待开始)的个人事项允许删除");
|
||||
ErrorCode PERSONAL_ITEM_WRITE_FORBIDDEN = new ErrorCode(1_008_008_009, "无权修改个人事项");
|
||||
|
||||
// ========== 加班申请 1_008_009_xxx ==========
|
||||
ErrorCode OVERTIME_APPLICATION_NOT_EXISTS = new ErrorCode(1_008_009_001, "加班申请不存在");
|
||||
ErrorCode OVERTIME_APPLICATION_STATUS_MODEL_NOT_EXISTS_OR_DISABLED = new ErrorCode(1_008_009_002, "加班申请状态定义不存在或已停用");
|
||||
ErrorCode OVERTIME_APPLICATION_STATUS_ACTION_NOT_ALLOWED = new ErrorCode(1_008_009_003, "当前加班申请状态不支持动作【{}】");
|
||||
ErrorCode OVERTIME_APPLICATION_STATUS_ACTION_REASON_REQUIRED = new ErrorCode(1_008_009_004, "动作【{}】必须填写原因");
|
||||
ErrorCode OVERTIME_APPLICATION_STATUS_CONCURRENT_MODIFIED = new ErrorCode(1_008_009_005, "加班申请状态已发生变化,请刷新后重试");
|
||||
ErrorCode OVERTIME_APPLICATION_APPLICANT_ONLY = new ErrorCode(1_008_009_006, "仅申请人可执行该操作");
|
||||
ErrorCode OVERTIME_APPLICATION_APPROVER_ONLY = new ErrorCode(1_008_009_007, "仅当前审核人可执行该操作");
|
||||
ErrorCode OVERTIME_APPLICATION_APPROVER_INVALID = new ErrorCode(1_008_009_008, "审核人不是有效系统用户");
|
||||
ErrorCode OVERTIME_APPLICATION_APPROVER_SELF_FORBIDDEN = new ErrorCode(1_008_009_009, "审核人不能选择申请人本人");
|
||||
ErrorCode OVERTIME_APPLICATION_READ_FORBIDDEN = new ErrorCode(1_008_009_010, "无权查看该加班申请");
|
||||
ErrorCode OVERTIME_APPLICATION_DELETE_ONLY_CANCELLED = new ErrorCode(1_008_009_011, "仅已撤销的加班申请允许删除");
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.njcn.rdms.module.project.constant;
|
||||
|
||||
/**
|
||||
* 加班申请常量。
|
||||
*/
|
||||
public final class OvertimeApplicationConstants {
|
||||
|
||||
private OvertimeApplicationConstants() {
|
||||
}
|
||||
|
||||
public static final String BIZ_TYPE = "overtime_application";
|
||||
public static final String STATUS_OBJECT_TYPE = BIZ_TYPE;
|
||||
|
||||
public static final String STATUS_PENDING = "pending";
|
||||
public static final String STATUS_APPROVED = "approved";
|
||||
public static final String STATUS_REJECTED = "rejected";
|
||||
public static final String STATUS_CANCELLED = "cancelled";
|
||||
|
||||
public static final String ACTION_SUBMIT = "submit";
|
||||
public static final String ACTION_RESUBMIT = "resubmit";
|
||||
public static final String ACTION_APPROVE = "approve";
|
||||
public static final String ACTION_REJECT = "reject";
|
||||
public static final String ACTION_CANCEL = "cancel";
|
||||
public static final String ACTION_DELETE = "delete";
|
||||
|
||||
public static final String PERMISSION_QUERY = "project:overtime-application:query";
|
||||
public static final String PERMISSION_CREATE = "project:overtime-application:create";
|
||||
public static final String PERMISSION_UPDATE = "project:overtime-application:update";
|
||||
public static final String PERMISSION_DELETE = "project:overtime-application:delete";
|
||||
public static final String PERMISSION_APPROVE = "project:overtime-application:approve";
|
||||
public static final String PERMISSION_EXPORT = "project:overtime-application:export";
|
||||
|
||||
}
|
||||
@@ -0,0 +1,135 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime;
|
||||
|
||||
import com.njcn.rdms.framework.apilog.core.annotation.ApiAccessLog;
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.excel.core.util.ExcelUtils;
|
||||
import com.njcn.rdms.module.project.constant.OvertimeApplicationConstants;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationExportVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationPageReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationSaveReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationStatusActionReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationStatusLogRespVO;
|
||||
import com.njcn.rdms.module.project.service.overtime.OvertimeApplicationService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import jakarta.annotation.Resource;
|
||||
import jakarta.servlet.http.HttpServletResponse;
|
||||
import jakarta.validation.Valid;
|
||||
import org.springframework.security.access.prepost.PreAuthorize;
|
||||
import org.springframework.validation.annotation.Validated;
|
||||
import org.springframework.web.bind.annotation.DeleteMapping;
|
||||
import org.springframework.web.bind.annotation.GetMapping;
|
||||
import org.springframework.web.bind.annotation.PathVariable;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.PutMapping;
|
||||
import org.springframework.web.bind.annotation.RequestBody;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import static com.njcn.rdms.framework.apilog.core.enums.OperateTypeEnum.EXPORT;
|
||||
import static com.njcn.rdms.framework.common.pojo.CommonResult.success;
|
||||
|
||||
@Tag(name = "管理后台 - 加班申请")
|
||||
@RestController
|
||||
@RequestMapping("/project/overtime-applications")
|
||||
@Validated
|
||||
public class OvertimeApplicationController {
|
||||
|
||||
@Resource
|
||||
private OvertimeApplicationService overtimeApplicationService;
|
||||
|
||||
@PostMapping
|
||||
@Operation(summary = "新增加班申请并提交")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_CREATE + "')")
|
||||
public CommonResult<Long> create(@Valid @RequestBody OvertimeApplicationSaveReqVO reqVO) {
|
||||
return success(overtimeApplicationService.createApplication(reqVO));
|
||||
}
|
||||
|
||||
@PutMapping("/{id}")
|
||||
@Operation(summary = "退回后修改并重新提交加班申请")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_UPDATE + "')")
|
||||
public CommonResult<Boolean> updateRejected(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody OvertimeApplicationSaveReqVO reqVO) {
|
||||
overtimeApplicationService.updateRejectedApplication(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
@Operation(summary = "获取加班申请详情")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<OvertimeApplicationRespVO> get(@PathVariable("id") Long id) {
|
||||
return success(overtimeApplicationService.getApplication(id));
|
||||
}
|
||||
|
||||
@GetMapping("/page")
|
||||
@Operation(summary = "获取我的加班申请分页")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<PageResult<OvertimeApplicationRespVO>> page(@Valid OvertimeApplicationPageReqVO reqVO) {
|
||||
return success(overtimeApplicationService.getMyPage(reqVO));
|
||||
}
|
||||
|
||||
@GetMapping("/approval-page")
|
||||
@Operation(summary = "获取待我审批的加班申请分页")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<PageResult<OvertimeApplicationRespVO>> approvalPage(@Valid OvertimeApplicationPageReqVO reqVO) {
|
||||
return success(overtimeApplicationService.getApprovalPage(reqVO));
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/approve")
|
||||
@Operation(summary = "审核通过加班申请")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<Boolean> approve(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody OvertimeApplicationStatusActionReqVO reqVO) {
|
||||
overtimeApplicationService.approve(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/reject")
|
||||
@Operation(summary = "审核退回加班申请")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_APPROVE + "')")
|
||||
public CommonResult<Boolean> reject(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody OvertimeApplicationStatusActionReqVO reqVO) {
|
||||
overtimeApplicationService.reject(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@PostMapping("/{id}/cancel")
|
||||
@Operation(summary = "撤销加班申请")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_UPDATE + "')")
|
||||
public CommonResult<Boolean> cancel(@PathVariable("id") Long id,
|
||||
@Valid @RequestBody OvertimeApplicationStatusActionReqVO reqVO) {
|
||||
overtimeApplicationService.cancel(id, reqVO);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@DeleteMapping("/{id}")
|
||||
@Operation(summary = "删除已撤销的加班申请")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_DELETE + "')")
|
||||
public CommonResult<Boolean> delete(@PathVariable("id") Long id) {
|
||||
overtimeApplicationService.deleteApplication(id);
|
||||
return success(true);
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/status-logs")
|
||||
@Operation(summary = "获取加班申请状态日志")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_QUERY + "')")
|
||||
public CommonResult<List<OvertimeApplicationStatusLogRespVO>> statusLogs(@PathVariable("id") Long id) {
|
||||
return success(overtimeApplicationService.getStatusLogs(id));
|
||||
}
|
||||
|
||||
@GetMapping("/export")
|
||||
@Operation(summary = "导出我的加班申请")
|
||||
@PreAuthorize("@ss.hasPermission('" + OvertimeApplicationConstants.PERMISSION_EXPORT + "')")
|
||||
@ApiAccessLog(operateType = EXPORT)
|
||||
public void export(@Valid OvertimeApplicationPageReqVO reqVO, HttpServletResponse response) throws IOException {
|
||||
List<OvertimeApplicationExportVO> list = overtimeApplicationService.getExportList(reqVO);
|
||||
ExcelUtils.write(response, "加班申请_" + LocalDate.now() + ".xls", "加班申请",
|
||||
OvertimeApplicationExportVO.class, list);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.vo;
|
||||
|
||||
import cn.idev.excel.annotation.ExcelIgnoreUnannotated;
|
||||
import cn.idev.excel.annotation.ExcelProperty;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Data
|
||||
@ExcelIgnoreUnannotated
|
||||
public class OvertimeApplicationExportVO {
|
||||
|
||||
@ExcelProperty("申请人")
|
||||
private String applicantName;
|
||||
|
||||
@ExcelProperty("加班日期")
|
||||
private LocalDate overtimeDate;
|
||||
|
||||
@ExcelProperty("加班时长")
|
||||
private String overtimeDuration;
|
||||
|
||||
@ExcelProperty("加班原因")
|
||||
private String overtimeReason;
|
||||
|
||||
@ExcelProperty("加班内容")
|
||||
private String overtimeContent;
|
||||
|
||||
@ExcelProperty("状态")
|
||||
private String statusName;
|
||||
|
||||
@ExcelProperty("审核人")
|
||||
private String approverName;
|
||||
|
||||
@ExcelProperty("提交时间")
|
||||
private LocalDateTime submitTime;
|
||||
|
||||
@ExcelProperty("审核时间")
|
||||
private LocalDateTime approvalTime;
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.vo;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
import org.springframework.format.annotation.DateTimeFormat;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY;
|
||||
import static com.njcn.rdms.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
|
||||
|
||||
@Schema(description = "管理后台 - 加班申请分页 Request VO")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OvertimeApplicationPageReqVO extends PageParam {
|
||||
|
||||
@Schema(description = "关键词,匹配加班原因或加班内容", example = "上线")
|
||||
private String keyword;
|
||||
|
||||
@Schema(description = "申请人姓名,模糊匹配", example = "张三")
|
||||
private String applicantName;
|
||||
|
||||
@Schema(description = "审核人用户编号", example = "1001")
|
||||
private Long approverId;
|
||||
|
||||
@Schema(description = "审核人姓名,模糊匹配", example = "李四")
|
||||
private String approverName;
|
||||
|
||||
@Schema(description = "状态编码", example = "pending")
|
||||
@Size(max = 32, message = "状态编码长度不能超过32个字符")
|
||||
private String statusCode;
|
||||
|
||||
@Schema(description = "加班日期范围", example = "[2026-06-01, 2026-06-30]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
private LocalDate[] overtimeDate;
|
||||
|
||||
@Schema(description = "创建时间", example = "[2026-06-01 00:00:00, 2026-06-30 23:59:59]")
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
|
||||
private LocalDateTime[] createTime;
|
||||
}
|
||||
@@ -0,0 +1,66 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 加班申请 Response VO")
|
||||
@Data
|
||||
public class OvertimeApplicationRespVO {
|
||||
|
||||
@Schema(description = "加班申请编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "9001")
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "申请人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "3001")
|
||||
private Long applicantId;
|
||||
|
||||
@Schema(description = "申请人姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "张三")
|
||||
private String applicantName;
|
||||
|
||||
@Schema(description = "加班日期", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDate overtimeDate;
|
||||
|
||||
@Schema(description = "加班时长", requiredMode = Schema.RequiredMode.REQUIRED, example = "1天")
|
||||
private String overtimeDuration;
|
||||
|
||||
@Schema(description = "加班原因", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String overtimeReason;
|
||||
|
||||
@Schema(description = "加班内容", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String overtimeContent;
|
||||
|
||||
@Schema(description = "审核人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001")
|
||||
private Long approverId;
|
||||
|
||||
@Schema(description = "审核人姓名", requiredMode = Schema.RequiredMode.REQUIRED, example = "李四")
|
||||
private String approverName;
|
||||
|
||||
@Schema(description = "状态编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "pending")
|
||||
private String statusCode;
|
||||
|
||||
@Schema(description = "状态名称", example = "待审批")
|
||||
private String statusName;
|
||||
|
||||
@Schema(description = "当前状态是否允许编辑", example = "false")
|
||||
private Boolean allowEdit;
|
||||
|
||||
@Schema(description = "是否终态", example = "false")
|
||||
private Boolean terminal;
|
||||
|
||||
@Schema(description = "最近一次审核意见")
|
||||
private String approvalComment;
|
||||
|
||||
@Schema(description = "提交时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime submitTime;
|
||||
|
||||
@Schema(description = "最近一次审核时间")
|
||||
private LocalDateTime approvalTime;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@Schema(description = "更新时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime updateTime;
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Schema(description = "管理后台 - 加班申请保存 Request VO")
|
||||
@Data
|
||||
public class OvertimeApplicationSaveReqVO {
|
||||
|
||||
@Schema(description = "加班日期", requiredMode = Schema.RequiredMode.REQUIRED, example = "2026-06-01")
|
||||
@NotNull(message = "加班日期不能为空")
|
||||
private LocalDate overtimeDate;
|
||||
|
||||
@Schema(description = "加班时长", requiredMode = Schema.RequiredMode.REQUIRED, example = "1天")
|
||||
@NotBlank(message = "加班时长不能为空")
|
||||
@Size(max = 30, message = "加班时长长度不能超过30个字符")
|
||||
private String overtimeDuration;
|
||||
|
||||
@Schema(description = "加班原因", requiredMode = Schema.RequiredMode.REQUIRED, example = "版本上线保障")
|
||||
@NotBlank(message = "加班原因不能为空")
|
||||
@Size(max = 500, message = "加班原因长度不能超过500个字符")
|
||||
private String overtimeReason;
|
||||
|
||||
@Schema(description = "加班内容", requiredMode = Schema.RequiredMode.REQUIRED, example = "处理上线验证和问题修复")
|
||||
@NotBlank(message = "加班内容不能为空")
|
||||
@Size(max = 1000, message = "加班内容长度不能超过1000个字符")
|
||||
private String overtimeContent;
|
||||
|
||||
@Schema(description = "审核人用户编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "1001")
|
||||
@NotNull(message = "审核人不能为空")
|
||||
private Long approverId;
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import lombok.Data;
|
||||
|
||||
@Schema(description = "管理后台 - 加班申请状态动作 Request VO")
|
||||
@Data
|
||||
public class OvertimeApplicationStatusActionReqVO {
|
||||
|
||||
@Schema(description = "动作原因或审核意见。退回、撤销必填", example = "请补充加班内容")
|
||||
@Size(max = 1000, message = "动作原因长度不能超过1000个字符")
|
||||
private String reason;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.njcn.rdms.module.project.controller.admin.overtime.vo;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
@Schema(description = "管理后台 - 加班申请状态日志 Response VO")
|
||||
@Data
|
||||
public class OvertimeApplicationStatusLogRespVO {
|
||||
|
||||
@Schema(description = "日志编号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long id;
|
||||
|
||||
@Schema(description = "加班申请编号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long applicationId;
|
||||
|
||||
@Schema(description = "动作编码", requiredMode = Schema.RequiredMode.REQUIRED, example = "approve")
|
||||
private String actionType;
|
||||
|
||||
@Schema(description = "变更前状态", example = "pending")
|
||||
private String fromStatus;
|
||||
|
||||
@Schema(description = "变更后状态", requiredMode = Schema.RequiredMode.REQUIRED, example = "approved")
|
||||
private String toStatus;
|
||||
|
||||
@Schema(description = "原因或审核意见")
|
||||
private String reason;
|
||||
|
||||
@Schema(description = "操作人用户编号", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private Long operatorUserId;
|
||||
|
||||
@Schema(description = "操作人名称", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String operatorName;
|
||||
|
||||
@Schema(description = "申请人姓名快照", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String applicantNameSnapshot;
|
||||
|
||||
@Schema(description = "加班日期快照", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDate overtimeDateSnapshot;
|
||||
|
||||
@Schema(description = "加班时长快照", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private String overtimeDurationSnapshot;
|
||||
|
||||
@Schema(description = "备注")
|
||||
private String remark;
|
||||
|
||||
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
|
||||
private LocalDateTime createTime;
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
package com.njcn.rdms.module.project.dal.dataobject.overtime;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.FieldStrategy;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* 加班申请表。
|
||||
*/
|
||||
@TableName("rdms_overtime_application")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OvertimeApplicationDO extends BaseDO {
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
private Long applicantId;
|
||||
|
||||
private String applicantName;
|
||||
|
||||
private LocalDate overtimeDate;
|
||||
|
||||
private String overtimeDuration;
|
||||
|
||||
private String overtimeReason;
|
||||
|
||||
private String overtimeContent;
|
||||
|
||||
private Long approverId;
|
||||
|
||||
private String approverName;
|
||||
|
||||
private String statusCode;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private String approvalComment;
|
||||
|
||||
private LocalDateTime submitTime;
|
||||
|
||||
@TableField(updateStrategy = FieldStrategy.ALWAYS)
|
||||
private LocalDateTime approvalTime;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.njcn.rdms.module.project.dal.dataobject.overtime;
|
||||
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
|
||||
import lombok.Data;
|
||||
import lombok.EqualsAndHashCode;
|
||||
|
||||
import java.time.LocalDate;
|
||||
|
||||
/**
|
||||
* 加班申请状态日志表。
|
||||
*/
|
||||
@TableName("rdms_overtime_application_status_log")
|
||||
@Data
|
||||
@EqualsAndHashCode(callSuper = true)
|
||||
public class OvertimeApplicationStatusLogDO extends BaseDO {
|
||||
|
||||
@TableId
|
||||
private Long id;
|
||||
|
||||
private Long applicationId;
|
||||
|
||||
private String actionType;
|
||||
|
||||
private String fromStatus;
|
||||
|
||||
private String toStatus;
|
||||
|
||||
private String reason;
|
||||
|
||||
private Long operatorUserId;
|
||||
|
||||
private String operatorName;
|
||||
|
||||
private String applicantNameSnapshot;
|
||||
|
||||
private LocalDate overtimeDateSnapshot;
|
||||
|
||||
private String overtimeDurationSnapshot;
|
||||
|
||||
private String remark;
|
||||
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.njcn.rdms.module.project.dal.mysql.overtime;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationPageReqVO;
|
||||
import com.njcn.rdms.module.project.dal.dataobject.overtime.OvertimeApplicationDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.util.Collection;
|
||||
|
||||
@Mapper
|
||||
public interface OvertimeApplicationMapper extends BaseMapperX<OvertimeApplicationDO> {
|
||||
|
||||
default PageResult<OvertimeApplicationDO> selectMyPage(Long applicantId, OvertimeApplicationPageReqVO reqVO) {
|
||||
LambdaQueryWrapperX<OvertimeApplicationDO> queryWrapper = buildPageQuery(reqVO);
|
||||
queryWrapper.eq(OvertimeApplicationDO::getApplicantId, applicantId);
|
||||
queryWrapper.orderByDesc(OvertimeApplicationDO::getSubmitTime)
|
||||
.orderByDesc(OvertimeApplicationDO::getId);
|
||||
return selectPage(reqVO, queryWrapper);
|
||||
}
|
||||
|
||||
default PageResult<OvertimeApplicationDO> selectApprovalPage(Long approverId, OvertimeApplicationPageReqVO reqVO) {
|
||||
LambdaQueryWrapperX<OvertimeApplicationDO> queryWrapper = buildPageQuery(reqVO);
|
||||
queryWrapper.eq(OvertimeApplicationDO::getApproverId, approverId);
|
||||
queryWrapper.orderByDesc(OvertimeApplicationDO::getSubmitTime)
|
||||
.orderByDesc(OvertimeApplicationDO::getId);
|
||||
return selectPage(reqVO, queryWrapper);
|
||||
}
|
||||
|
||||
default OvertimeApplicationDO selectByIdAndApplicantId(Long id, Long applicantId) {
|
||||
return selectOne(new LambdaQueryWrapperX<OvertimeApplicationDO>()
|
||||
.eq(OvertimeApplicationDO::getId, id)
|
||||
.eq(OvertimeApplicationDO::getApplicantId, applicantId));
|
||||
}
|
||||
|
||||
default int updateByIdAndStatus(OvertimeApplicationDO update, Long id, String fromStatus) {
|
||||
return update(update, new LambdaQueryWrapperX<OvertimeApplicationDO>()
|
||||
.eq(OvertimeApplicationDO::getId, id)
|
||||
.eq(OvertimeApplicationDO::getStatusCode, fromStatus));
|
||||
}
|
||||
|
||||
default int updateByIdAndStatusAndApplicantId(OvertimeApplicationDO update, Long id, String fromStatus,
|
||||
Long applicantId) {
|
||||
return update(update, new LambdaQueryWrapperX<OvertimeApplicationDO>()
|
||||
.eq(OvertimeApplicationDO::getId, id)
|
||||
.eq(OvertimeApplicationDO::getStatusCode, fromStatus)
|
||||
.eq(OvertimeApplicationDO::getApplicantId, applicantId));
|
||||
}
|
||||
|
||||
default int updateByIdAndStatusesAndApplicantId(OvertimeApplicationDO update, Long id,
|
||||
Collection<String> fromStatuses, Long applicantId) {
|
||||
return update(update, new LambdaQueryWrapperX<OvertimeApplicationDO>()
|
||||
.eq(OvertimeApplicationDO::getId, id)
|
||||
.in(OvertimeApplicationDO::getStatusCode, fromStatuses)
|
||||
.eq(OvertimeApplicationDO::getApplicantId, applicantId));
|
||||
}
|
||||
|
||||
private LambdaQueryWrapperX<OvertimeApplicationDO> buildPageQuery(OvertimeApplicationPageReqVO reqVO) {
|
||||
LambdaQueryWrapperX<OvertimeApplicationDO> queryWrapper = new LambdaQueryWrapperX<>();
|
||||
queryWrapper.likeIfPresent(OvertimeApplicationDO::getApplicantName, reqVO.getApplicantName())
|
||||
.eqIfPresent(OvertimeApplicationDO::getApproverId, reqVO.getApproverId())
|
||||
.likeIfPresent(OvertimeApplicationDO::getApproverName, reqVO.getApproverName())
|
||||
.eqIfPresent(OvertimeApplicationDO::getStatusCode, reqVO.getStatusCode())
|
||||
.betweenIfPresent(OvertimeApplicationDO::getOvertimeDate, reqVO.getOvertimeDate())
|
||||
.betweenIfPresent(BaseDO::getCreateTime, reqVO.getCreateTime());
|
||||
if (StringUtils.hasText(reqVO.getKeyword())) {
|
||||
queryWrapper.and(wrapper -> wrapper.like(OvertimeApplicationDO::getOvertimeReason, reqVO.getKeyword())
|
||||
.or()
|
||||
.like(OvertimeApplicationDO::getOvertimeContent, reqVO.getKeyword()));
|
||||
}
|
||||
return queryWrapper;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.njcn.rdms.module.project.dal.mysql.overtime;
|
||||
|
||||
import com.njcn.rdms.framework.mybatis.core.mapper.BaseMapperX;
|
||||
import com.njcn.rdms.framework.mybatis.core.query.LambdaQueryWrapperX;
|
||||
import com.njcn.rdms.module.project.dal.dataobject.overtime.OvertimeApplicationStatusLogDO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Mapper
|
||||
public interface OvertimeApplicationStatusLogMapper extends BaseMapperX<OvertimeApplicationStatusLogDO> {
|
||||
|
||||
default List<OvertimeApplicationStatusLogDO> selectListByApplicationId(Long applicationId) {
|
||||
return selectList(new LambdaQueryWrapperX<OvertimeApplicationStatusLogDO>()
|
||||
.eq(OvertimeApplicationStatusLogDO::getApplicationId, applicationId)
|
||||
.orderByDesc(OvertimeApplicationStatusLogDO::getCreateTime)
|
||||
.orderByDesc(OvertimeApplicationStatusLogDO::getId));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
package com.njcn.rdms.module.project.service.overtime;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationExportVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationPageReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationSaveReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationStatusActionReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationStatusLogRespVO;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
public interface OvertimeApplicationService {
|
||||
|
||||
Long createApplication(OvertimeApplicationSaveReqVO reqVO);
|
||||
|
||||
void updateRejectedApplication(Long id, OvertimeApplicationSaveReqVO reqVO);
|
||||
|
||||
void approve(Long id, OvertimeApplicationStatusActionReqVO reqVO);
|
||||
|
||||
void reject(Long id, OvertimeApplicationStatusActionReqVO reqVO);
|
||||
|
||||
void cancel(Long id, OvertimeApplicationStatusActionReqVO reqVO);
|
||||
|
||||
void deleteApplication(Long id);
|
||||
|
||||
OvertimeApplicationRespVO getApplication(Long id);
|
||||
|
||||
PageResult<OvertimeApplicationRespVO> getMyPage(OvertimeApplicationPageReqVO reqVO);
|
||||
|
||||
PageResult<OvertimeApplicationRespVO> getApprovalPage(OvertimeApplicationPageReqVO reqVO);
|
||||
|
||||
List<OvertimeApplicationStatusLogRespVO> getStatusLogs(Long id);
|
||||
|
||||
List<OvertimeApplicationExportVO> getExportList(OvertimeApplicationPageReqVO reqVO);
|
||||
}
|
||||
@@ -0,0 +1,467 @@
|
||||
package com.njcn.rdms.module.project.service.overtime;
|
||||
|
||||
import com.njcn.rdms.framework.common.pojo.CommonResult;
|
||||
import com.njcn.rdms.framework.common.pojo.PageParam;
|
||||
import com.njcn.rdms.framework.common.pojo.PageResult;
|
||||
import com.njcn.rdms.framework.common.util.json.JsonUtils;
|
||||
import com.njcn.rdms.framework.common.util.object.BeanUtils;
|
||||
import com.njcn.rdms.framework.security.core.util.SecurityFrameworkUtils;
|
||||
import com.njcn.rdms.module.project.constant.OvertimeApplicationConstants;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationExportVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationPageReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationRespVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationSaveReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationStatusActionReqVO;
|
||||
import com.njcn.rdms.module.project.controller.admin.overtime.vo.OvertimeApplicationStatusLogRespVO;
|
||||
import com.njcn.rdms.module.project.dal.dataobject.audit.BizAuditLogDO;
|
||||
import com.njcn.rdms.module.project.dal.dataobject.overtime.OvertimeApplicationDO;
|
||||
import com.njcn.rdms.module.project.dal.dataobject.overtime.OvertimeApplicationStatusLogDO;
|
||||
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusModelDO;
|
||||
import com.njcn.rdms.module.project.dal.dataobject.status.ObjectStatusTransitionDO;
|
||||
import com.njcn.rdms.module.project.dal.mysql.audit.BizAuditLogMapper;
|
||||
import com.njcn.rdms.module.project.dal.mysql.overtime.OvertimeApplicationMapper;
|
||||
import com.njcn.rdms.module.project.dal.mysql.overtime.OvertimeApplicationStatusLogMapper;
|
||||
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusModelMapper;
|
||||
import com.njcn.rdms.module.project.dal.mysql.status.ObjectStatusTransitionMapper;
|
||||
import com.njcn.rdms.module.project.enums.ErrorCodeConstants;
|
||||
import com.njcn.rdms.module.system.api.user.AdminUserApi;
|
||||
import com.njcn.rdms.module.system.api.user.dto.AdminUserRespDTO;
|
||||
import jakarta.annotation.Resource;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
import org.springframework.util.StringUtils;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Collections;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.function.Function;
|
||||
|
||||
import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.exception;
|
||||
import static com.njcn.rdms.framework.common.exception.util.ServiceExceptionUtil.invalidParamException;
|
||||
|
||||
@Service
|
||||
public class OvertimeApplicationServiceImpl implements OvertimeApplicationService {
|
||||
|
||||
@Resource
|
||||
private OvertimeApplicationMapper overtimeApplicationMapper;
|
||||
@Resource
|
||||
private OvertimeApplicationStatusLogMapper overtimeApplicationStatusLogMapper;
|
||||
@Resource
|
||||
private BizAuditLogMapper bizAuditLogMapper;
|
||||
@Resource
|
||||
private ObjectStatusModelMapper objectStatusModelMapper;
|
||||
@Resource
|
||||
private ObjectStatusTransitionMapper objectStatusTransitionMapper;
|
||||
@Resource
|
||||
private AdminUserApi adminUserApi;
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public Long createApplication(OvertimeApplicationSaveReqVO reqVO) {
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
AdminUserRespDTO approver = validateApprover(reqVO.getApproverId());
|
||||
String initialStatus = getInitialStatusCode();
|
||||
|
||||
OvertimeApplicationDO application = new OvertimeApplicationDO();
|
||||
application.setApplicantId(loginUserId);
|
||||
application.setApplicantName(defaultText(SecurityFrameworkUtils.getLoginUserNickname()));
|
||||
applySaveFields(application, reqVO, approver);
|
||||
application.setStatusCode(initialStatus);
|
||||
application.setApprovalComment(null);
|
||||
application.setSubmitTime(LocalDateTime.now());
|
||||
application.setApprovalTime(null);
|
||||
overtimeApplicationMapper.insert(application);
|
||||
|
||||
writeStatusLog(application, OvertimeApplicationConstants.ACTION_SUBMIT, null, initialStatus, null);
|
||||
writeAuditLog(application, OvertimeApplicationConstants.ACTION_SUBMIT, null, initialStatus, null, null, null);
|
||||
return application.getId();
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void updateRejectedApplication(Long id, OvertimeApplicationSaveReqVO reqVO) {
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
OvertimeApplicationDO current = validateApplicationExists(id);
|
||||
if (!Objects.equals(current.getApplicantId(), loginUserId)) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_APPLICANT_ONLY);
|
||||
}
|
||||
String fromStatus = current.getStatusCode();
|
||||
ObjectStatusTransitionDO transition = validateTransition(fromStatus, OvertimeApplicationConstants.ACTION_RESUBMIT,
|
||||
null);
|
||||
AdminUserRespDTO approver = validateApprover(reqVO.getApproverId());
|
||||
|
||||
OvertimeApplicationDO before = cloneApplication(current);
|
||||
OvertimeApplicationDO update = new OvertimeApplicationDO();
|
||||
applySaveFields(update, reqVO, approver);
|
||||
update.setStatusCode(transition.getToStatusCode());
|
||||
update.setApprovalComment(null);
|
||||
update.setSubmitTime(LocalDateTime.now());
|
||||
update.setApprovalTime(null);
|
||||
|
||||
int updateCount = overtimeApplicationMapper.updateByIdAndStatusesAndApplicantId(update, id,
|
||||
List.of(OvertimeApplicationConstants.STATUS_PENDING, OvertimeApplicationConstants.STATUS_REJECTED),
|
||||
loginUserId);
|
||||
if (updateCount != 1) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_CONCURRENT_MODIFIED);
|
||||
}
|
||||
|
||||
OvertimeApplicationDO after = mergeUpdated(current, update);
|
||||
writeStatusLog(after, OvertimeApplicationConstants.ACTION_RESUBMIT, fromStatus, transition.getToStatusCode(),
|
||||
null);
|
||||
writeAuditLog(after, OvertimeApplicationConstants.ACTION_RESUBMIT, fromStatus, transition.getToStatusCode(),
|
||||
buildFieldChanges(before, after), null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void approve(Long id, OvertimeApplicationStatusActionReqVO reqVO) {
|
||||
processApprovalAction(id, OvertimeApplicationConstants.ACTION_APPROVE, reqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void reject(Long id, OvertimeApplicationStatusActionReqVO reqVO) {
|
||||
processApprovalAction(id, OvertimeApplicationConstants.ACTION_REJECT, reqVO);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void cancel(Long id, OvertimeApplicationStatusActionReqVO reqVO) {
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
OvertimeApplicationDO current = validateApplicationExists(id);
|
||||
if (!Objects.equals(current.getApplicantId(), loginUserId)) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_APPLICANT_ONLY);
|
||||
}
|
||||
String reason = normalizeNullableText(reqVO == null ? null : reqVO.getReason());
|
||||
String fromStatus = current.getStatusCode();
|
||||
ObjectStatusTransitionDO transition = validateTransition(fromStatus, OvertimeApplicationConstants.ACTION_CANCEL,
|
||||
reason);
|
||||
|
||||
OvertimeApplicationDO update = new OvertimeApplicationDO();
|
||||
update.setStatusCode(transition.getToStatusCode());
|
||||
update.setApprovalComment(reason);
|
||||
update.setApprovalTime(LocalDateTime.now());
|
||||
int updateCount = overtimeApplicationMapper.updateByIdAndStatusAndApplicantId(update, id, fromStatus,
|
||||
loginUserId);
|
||||
if (updateCount != 1) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_CONCURRENT_MODIFIED);
|
||||
}
|
||||
|
||||
OvertimeApplicationDO after = mergeUpdated(current, update);
|
||||
writeStatusLog(after, OvertimeApplicationConstants.ACTION_CANCEL, fromStatus, transition.getToStatusCode(),
|
||||
reason);
|
||||
writeAuditLog(after, OvertimeApplicationConstants.ACTION_CANCEL, fromStatus, transition.getToStatusCode(),
|
||||
null, reason, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
@Transactional(rollbackFor = Exception.class)
|
||||
public void deleteApplication(Long id) {
|
||||
OvertimeApplicationDO current = validateApplicationExists(id);
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
if (!Objects.equals(current.getApplicantId(), loginUserId)) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_APPLICANT_ONLY);
|
||||
}
|
||||
if (!OvertimeApplicationConstants.STATUS_CANCELLED.equals(current.getStatusCode())) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_DELETE_ONLY_CANCELLED);
|
||||
}
|
||||
overtimeApplicationMapper.deleteById(id);
|
||||
writeAuditLog(current, OvertimeApplicationConstants.ACTION_DELETE, current.getStatusCode(), null, null, null,
|
||||
null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public OvertimeApplicationRespVO getApplication(Long id) {
|
||||
OvertimeApplicationDO application = validateReadableApplication(id);
|
||||
return toRespVO(application);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<OvertimeApplicationRespVO> getMyPage(OvertimeApplicationPageReqVO reqVO) {
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
PageResult<OvertimeApplicationDO> page = overtimeApplicationMapper.selectMyPage(loginUserId, reqVO);
|
||||
return BeanUtils.toBean(page, OvertimeApplicationRespVO.class, this::applyStatusView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageResult<OvertimeApplicationRespVO> getApprovalPage(OvertimeApplicationPageReqVO reqVO) {
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
PageResult<OvertimeApplicationDO> page = overtimeApplicationMapper.selectApprovalPage(loginUserId, reqVO);
|
||||
return BeanUtils.toBean(page, OvertimeApplicationRespVO.class, this::applyStatusView);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OvertimeApplicationStatusLogRespVO> getStatusLogs(Long id) {
|
||||
validateReadableApplication(id);
|
||||
return BeanUtils.toBean(overtimeApplicationStatusLogMapper.selectListByApplicationId(id),
|
||||
OvertimeApplicationStatusLogRespVO.class);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<OvertimeApplicationExportVO> getExportList(OvertimeApplicationPageReqVO reqVO) {
|
||||
reqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
|
||||
PageResult<OvertimeApplicationRespVO> page = getMyPage(reqVO);
|
||||
return BeanUtils.toBean(page.getList(), OvertimeApplicationExportVO.class);
|
||||
}
|
||||
|
||||
private void processApprovalAction(Long id, String actionCode, OvertimeApplicationStatusActionReqVO reqVO) {
|
||||
OvertimeApplicationDO current = validateApplicationExists(id);
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
if (!Objects.equals(current.getApproverId(), loginUserId)) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_APPROVER_ONLY);
|
||||
}
|
||||
String reason = normalizeNullableText(reqVO == null ? null : reqVO.getReason());
|
||||
String fromStatus = current.getStatusCode();
|
||||
ObjectStatusTransitionDO transition = validateTransition(fromStatus, actionCode, reason);
|
||||
|
||||
OvertimeApplicationDO update = new OvertimeApplicationDO();
|
||||
update.setStatusCode(transition.getToStatusCode());
|
||||
update.setApprovalComment(reason);
|
||||
update.setApprovalTime(LocalDateTime.now());
|
||||
int updateCount = overtimeApplicationMapper.updateByIdAndStatus(update, id, fromStatus);
|
||||
if (updateCount != 1) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_CONCURRENT_MODIFIED);
|
||||
}
|
||||
|
||||
OvertimeApplicationDO after = mergeUpdated(current, update);
|
||||
writeStatusLog(after, actionCode, fromStatus, transition.getToStatusCode(), reason);
|
||||
writeAuditLog(after, actionCode, fromStatus, transition.getToStatusCode(), null, reason, null);
|
||||
}
|
||||
|
||||
private OvertimeApplicationDO validateApplicationExists(Long id) {
|
||||
OvertimeApplicationDO application = overtimeApplicationMapper.selectById(id);
|
||||
if (application == null) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_NOT_EXISTS);
|
||||
}
|
||||
return application;
|
||||
}
|
||||
|
||||
private OvertimeApplicationDO validateReadableApplication(Long id) {
|
||||
OvertimeApplicationDO application = validateApplicationExists(id);
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
if (!Objects.equals(application.getApplicantId(), loginUserId)
|
||||
&& !Objects.equals(application.getApproverId(), loginUserId)) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_READ_FORBIDDEN);
|
||||
}
|
||||
return application;
|
||||
}
|
||||
|
||||
private ObjectStatusTransitionDO validateTransition(String fromStatus, String actionCode, String reason) {
|
||||
ObjectStatusTransitionDO transition = objectStatusTransitionMapper
|
||||
.selectByObjectTypeAndFromStatusAndAction(OvertimeApplicationConstants.STATUS_OBJECT_TYPE, fromStatus,
|
||||
actionCode);
|
||||
if (transition == null) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_ACTION_NOT_ALLOWED, actionCode);
|
||||
}
|
||||
if (Boolean.TRUE.equals(transition.getNeedReason()) && !StringUtils.hasText(reason)) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_ACTION_REASON_REQUIRED, actionCode);
|
||||
}
|
||||
ObjectStatusModelDO toModel = objectStatusModelMapper.selectByObjectTypeAndStatusCodeEnabled(
|
||||
OvertimeApplicationConstants.STATUS_OBJECT_TYPE, transition.getToStatusCode());
|
||||
if (toModel == null) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_MODEL_NOT_EXISTS_OR_DISABLED);
|
||||
}
|
||||
return transition;
|
||||
}
|
||||
|
||||
private String getInitialStatusCode() {
|
||||
ObjectStatusModelDO statusModel = objectStatusModelMapper
|
||||
.selectInitialByObjectTypeEnabled(OvertimeApplicationConstants.STATUS_OBJECT_TYPE);
|
||||
if (statusModel == null || !StringUtils.hasText(statusModel.getStatusCode())) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_STATUS_MODEL_NOT_EXISTS_OR_DISABLED);
|
||||
}
|
||||
return statusModel.getStatusCode();
|
||||
}
|
||||
|
||||
private AdminUserRespDTO validateApprover(Long approverId) {
|
||||
Long loginUserId = SecurityFrameworkUtils.getLoginUserId();
|
||||
if (Objects.equals(approverId, loginUserId)) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_APPROVER_SELF_FORBIDDEN);
|
||||
}
|
||||
return loadUser(approverId);
|
||||
}
|
||||
|
||||
private AdminUserRespDTO loadUser(Long userId) {
|
||||
if (userId == null) {
|
||||
throw invalidParamException("用户编号不能为空");
|
||||
}
|
||||
adminUserApi.validateUserList(Collections.singleton(userId)).getCheckedData();
|
||||
CommonResult<AdminUserRespDTO> result = adminUserApi.getUser(userId);
|
||||
AdminUserRespDTO user = result.getCheckedData();
|
||||
if (user == null) {
|
||||
throw exception(ErrorCodeConstants.OVERTIME_APPLICATION_APPROVER_INVALID);
|
||||
}
|
||||
return user;
|
||||
}
|
||||
|
||||
private void applySaveFields(OvertimeApplicationDO target, OvertimeApplicationSaveReqVO reqVO,
|
||||
AdminUserRespDTO approver) {
|
||||
target.setOvertimeDate(reqVO.getOvertimeDate());
|
||||
target.setOvertimeDuration(normalizeRequiredText(reqVO.getOvertimeDuration(), "加班时长不能为空"));
|
||||
target.setOvertimeReason(normalizeRequiredText(reqVO.getOvertimeReason(), "加班原因不能为空"));
|
||||
target.setOvertimeContent(normalizeRequiredText(reqVO.getOvertimeContent(), "加班内容不能为空"));
|
||||
target.setApproverId(approver.getId());
|
||||
target.setApproverName(defaultText(approver.getNickname()));
|
||||
}
|
||||
|
||||
private OvertimeApplicationRespVO toRespVO(OvertimeApplicationDO application) {
|
||||
return BeanUtils.toBean(application, OvertimeApplicationRespVO.class, this::applyStatusView);
|
||||
}
|
||||
|
||||
private void applyStatusView(OvertimeApplicationRespVO respVO) {
|
||||
ObjectStatusModelDO statusModel = objectStatusModelMapper.selectByObjectTypeAndStatusCodeEnabled(
|
||||
OvertimeApplicationConstants.STATUS_OBJECT_TYPE, respVO.getStatusCode());
|
||||
if (statusModel == null) {
|
||||
respVO.setStatusName(respVO.getStatusCode());
|
||||
respVO.setAllowEdit(false);
|
||||
respVO.setTerminal(false);
|
||||
return;
|
||||
}
|
||||
respVO.setStatusName(statusModel.getStatusName());
|
||||
respVO.setAllowEdit(Boolean.TRUE.equals(statusModel.getAllowEdit()));
|
||||
respVO.setTerminal(Boolean.TRUE.equals(statusModel.getTerminalFlag()));
|
||||
}
|
||||
|
||||
private void writeStatusLog(OvertimeApplicationDO application, String actionType, String fromStatus,
|
||||
String toStatus, String reason) {
|
||||
OvertimeApplicationStatusLogDO log = new OvertimeApplicationStatusLogDO();
|
||||
log.setApplicationId(application.getId());
|
||||
log.setActionType(actionType);
|
||||
log.setFromStatus(fromStatus);
|
||||
log.setToStatus(toStatus);
|
||||
log.setReason(reason);
|
||||
log.setOperatorUserId(SecurityFrameworkUtils.getLoginUserId());
|
||||
log.setOperatorName(defaultText(SecurityFrameworkUtils.getLoginUserNickname()));
|
||||
log.setApplicantNameSnapshot(application.getApplicantName());
|
||||
log.setOvertimeDateSnapshot(application.getOvertimeDate());
|
||||
log.setOvertimeDurationSnapshot(application.getOvertimeDuration());
|
||||
log.setRemark(buildSnapshotRemark(application));
|
||||
overtimeApplicationStatusLogMapper.insert(log);
|
||||
}
|
||||
|
||||
private void writeAuditLog(OvertimeApplicationDO application, String actionType, String fromStatus,
|
||||
String toStatus, String fieldChanges, String reason, String remark) {
|
||||
BizAuditLogDO auditLog = new BizAuditLogDO();
|
||||
auditLog.setBizType(OvertimeApplicationConstants.BIZ_TYPE);
|
||||
auditLog.setBizId(application.getId());
|
||||
auditLog.setActionType(actionType);
|
||||
auditLog.setFromStatus(fromStatus);
|
||||
auditLog.setToStatus(toStatus);
|
||||
auditLog.setFieldChanges(fieldChanges);
|
||||
auditLog.setReason(reason);
|
||||
auditLog.setOperatorUserId(SecurityFrameworkUtils.getLoginUserId());
|
||||
auditLog.setOperatorName(defaultText(SecurityFrameworkUtils.getLoginUserNickname()));
|
||||
auditLog.setRemark(StringUtils.hasText(remark) ? remark : buildSnapshotRemark(application));
|
||||
bizAuditLogMapper.insert(auditLog);
|
||||
}
|
||||
|
||||
private String buildSnapshotRemark(OvertimeApplicationDO application) {
|
||||
return "申请人:" + defaultText(application.getApplicantName())
|
||||
+ ",加班日期:" + application.getOvertimeDate()
|
||||
+ ",加班时长:" + defaultText(application.getOvertimeDuration());
|
||||
}
|
||||
|
||||
private String buildFieldChanges(OvertimeApplicationDO before, OvertimeApplicationDO after) {
|
||||
Map<String, Object> fieldChanges = new LinkedHashMap<>();
|
||||
appendFieldChange(fieldChanges, "overtimeDate", valueOf(before, OvertimeApplicationDO::getOvertimeDate),
|
||||
valueOf(after, OvertimeApplicationDO::getOvertimeDate));
|
||||
appendFieldChange(fieldChanges, "overtimeDuration",
|
||||
valueOf(before, OvertimeApplicationDO::getOvertimeDuration),
|
||||
valueOf(after, OvertimeApplicationDO::getOvertimeDuration));
|
||||
appendFieldChange(fieldChanges, "overtimeReason", valueOf(before, OvertimeApplicationDO::getOvertimeReason),
|
||||
valueOf(after, OvertimeApplicationDO::getOvertimeReason));
|
||||
appendFieldChange(fieldChanges, "overtimeContent", valueOf(before, OvertimeApplicationDO::getOvertimeContent),
|
||||
valueOf(after, OvertimeApplicationDO::getOvertimeContent));
|
||||
appendFieldChange(fieldChanges, "approverId", valueOf(before, OvertimeApplicationDO::getApproverId),
|
||||
valueOf(after, OvertimeApplicationDO::getApproverId));
|
||||
appendFieldChange(fieldChanges, "approverName", valueOf(before, OvertimeApplicationDO::getApproverName),
|
||||
valueOf(after, OvertimeApplicationDO::getApproverName));
|
||||
appendFieldChange(fieldChanges, "statusCode", valueOf(before, OvertimeApplicationDO::getStatusCode),
|
||||
valueOf(after, OvertimeApplicationDO::getStatusCode));
|
||||
return fieldChanges.isEmpty() ? null : JsonUtils.toJsonString(fieldChanges);
|
||||
}
|
||||
|
||||
private void appendFieldChange(Map<String, Object> fieldChanges, String fieldName, Object before, Object after) {
|
||||
if (Objects.equals(before, after)) {
|
||||
return;
|
||||
}
|
||||
Map<String, Object> value = new LinkedHashMap<>();
|
||||
value.put("before", before);
|
||||
value.put("after", after);
|
||||
fieldChanges.put(fieldName, value);
|
||||
}
|
||||
|
||||
private <T> T valueOf(OvertimeApplicationDO application, Function<OvertimeApplicationDO, T> getter) {
|
||||
return application == null ? null : getter.apply(application);
|
||||
}
|
||||
|
||||
private OvertimeApplicationDO cloneApplication(OvertimeApplicationDO source) {
|
||||
OvertimeApplicationDO target = new OvertimeApplicationDO();
|
||||
target.setId(source.getId());
|
||||
target.setApplicantId(source.getApplicantId());
|
||||
target.setApplicantName(source.getApplicantName());
|
||||
target.setOvertimeDate(source.getOvertimeDate());
|
||||
target.setOvertimeDuration(source.getOvertimeDuration());
|
||||
target.setOvertimeReason(source.getOvertimeReason());
|
||||
target.setOvertimeContent(source.getOvertimeContent());
|
||||
target.setApproverId(source.getApproverId());
|
||||
target.setApproverName(source.getApproverName());
|
||||
target.setStatusCode(source.getStatusCode());
|
||||
target.setApprovalComment(source.getApprovalComment());
|
||||
target.setSubmitTime(source.getSubmitTime());
|
||||
target.setApprovalTime(source.getApprovalTime());
|
||||
return target;
|
||||
}
|
||||
|
||||
private OvertimeApplicationDO mergeUpdated(OvertimeApplicationDO current, OvertimeApplicationDO update) {
|
||||
OvertimeApplicationDO after = cloneApplication(current);
|
||||
if (update.getOvertimeDate() != null) {
|
||||
after.setOvertimeDate(update.getOvertimeDate());
|
||||
}
|
||||
if (update.getOvertimeDuration() != null) {
|
||||
after.setOvertimeDuration(update.getOvertimeDuration());
|
||||
}
|
||||
if (update.getOvertimeReason() != null) {
|
||||
after.setOvertimeReason(update.getOvertimeReason());
|
||||
}
|
||||
if (update.getOvertimeContent() != null) {
|
||||
after.setOvertimeContent(update.getOvertimeContent());
|
||||
}
|
||||
if (update.getApproverId() != null) {
|
||||
after.setApproverId(update.getApproverId());
|
||||
}
|
||||
if (update.getApproverName() != null) {
|
||||
after.setApproverName(update.getApproverName());
|
||||
}
|
||||
if (update.getStatusCode() != null) {
|
||||
after.setStatusCode(update.getStatusCode());
|
||||
}
|
||||
after.setApprovalComment(update.getApprovalComment());
|
||||
if (update.getSubmitTime() != null) {
|
||||
after.setSubmitTime(update.getSubmitTime());
|
||||
}
|
||||
after.setApprovalTime(update.getApprovalTime());
|
||||
return after;
|
||||
}
|
||||
|
||||
private String normalizeRequiredText(String value, String message) {
|
||||
if (!StringUtils.hasText(value)) {
|
||||
throw invalidParamException(message);
|
||||
}
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
private String normalizeNullableText(String value) {
|
||||
if (!StringUtils.hasText(value)) {
|
||||
return null;
|
||||
}
|
||||
return value.trim();
|
||||
}
|
||||
|
||||
private String defaultText(String value) {
|
||||
return StringUtils.hasText(value) ? value : "";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user