This commit is contained in:
aikai 2024-04-26 17:58:09 +08:00
parent be4f4278ba
commit cafd198037
45 changed files with 1405 additions and 95 deletions

View File

@ -0,0 +1,43 @@
package cn.iocoder.yudao.framework.common;
import java.time.format.DateTimeFormatter;
public class Constants {
/**
* 空字符串
*/
public static final String EMPTY_STRING = "";
/**
* 下划线
*/
public static final String UNDERLINE = "_";
/**
* 考勤前缀
*/
public static final String ATTENDANCE = "attendance";
/**
* 排班制前缀
*/
public static final String SCHEDULING = "scheduling";
/**
*
*/
public static final Integer ZERO = 0;
/**
*
*/
public static final Integer ONE = 1;
/**
* 中文1
*/
public static final String ONE_STR = "1";
/**
* yyyy-MM-dd格式
*/
public static final DateTimeFormatter REPO_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
}

View File

@ -183,6 +183,12 @@ public interface ErrorCodeConstants {
ErrorCode GROUP_SHIFT_NOT_EXISTS = new ErrorCode(1_003_004_000, "考勤组班次不存在"); ErrorCode GROUP_SHIFT_NOT_EXISTS = new ErrorCode(1_003_004_000, "考勤组班次不存在");
ErrorCode GROUP_NOT_EXISTS = new ErrorCode(1_003_005_000, "考勤组不存在"); ErrorCode GROUP_NOT_EXISTS = new ErrorCode(1_003_005_000, "考勤组不存在");
ErrorCode FIXED_NOT_EXISTS = new ErrorCode(1_003_006_000, "固定班制考勤设置"); ErrorCode FIXED_NOT_EXISTS = new ErrorCode(1_003_006_000, "固定班制考勤设置");
ErrorCode NOT_IN_THE_ATTENDANCE_GROUP = new ErrorCode(1_003_007_000, "未在考勤组内");
ErrorCode NO_ATTENDANCE_REQUIRED_ON_THE_DAY = new ErrorCode(1_003_008_000, "当天不需要考勤");
ErrorCode DID_NOT_ENTER_THE_ATTENDANCE_RANGE = new ErrorCode(1_003_009_000, "未进入到考勤范围");
ErrorCode ATTENDANCE_TIME_NOT_ARRIVED = new ErrorCode(1_003_010_000, "未到考勤时间");
ErrorCode LOG_FORM_NOT_USE = new ErrorCode(1_009_010_004, "你不用使用该日志模板"); ErrorCode LOG_FORM_NOT_USE = new ErrorCode(1_009_010_004, "你不用使用该日志模板");

View File

@ -0,0 +1,93 @@
package cn.iocoder.yudao.module.system.controller.admin.attendance;
import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.system.controller.admin.attendance.vo.AttendanceStatisticsPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.attendance.vo.AttendanceStatisticsRespVO;
import cn.iocoder.yudao.module.system.controller.admin.attendance.vo.AttendanceStatisticsSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.statistics.AttendanceStatisticsDO;
import cn.iocoder.yudao.module.system.service.attendance.statistics.AttendanceStatisticsService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;
import java.io.IOException;
import java.util.List;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - 考勤统计")
@RestController
@RequestMapping("/system.attendance/attendance-statistics")
@Validated
public class AttendanceStatisticsController {
@Resource
private AttendanceStatisticsService attendanceStatisticsService;
@PostMapping("/create")
@Operation(summary = "创建考勤统计")
@PreAuthorize("@ss.hasPermission('system.attendance:attendance-statistics:create')")
public CommonResult<Long> createAttendanceStatistics(@Valid @RequestBody AttendanceStatisticsSaveReqVO createReqVO) {
return success(attendanceStatisticsService.createAttendanceStatistics(createReqVO));
}
@PutMapping("/update")
@Operation(summary = "更新考勤统计")
@PreAuthorize("@ss.hasPermission('system.attendance:attendance-statistics:update')")
public CommonResult<Boolean> updateAttendanceStatistics(@Valid @RequestBody AttendanceStatisticsSaveReqVO updateReqVO) {
attendanceStatisticsService.updateAttendanceStatistics(updateReqVO);
return success(true);
}
@DeleteMapping("/delete")
@Operation(summary = "删除考勤统计")
@Parameter(name = "id", description = "编号", required = true)
@PreAuthorize("@ss.hasPermission('system.attendance:attendance-statistics:delete')")
public CommonResult<Boolean> deleteAttendanceStatistics(@RequestParam("id") Long id) {
attendanceStatisticsService.deleteAttendanceStatistics(id);
return success(true);
}
@GetMapping("/get")
@Operation(summary = "获得考勤统计")
@Parameter(name = "id", description = "编号", required = true, example = "1024")
@PreAuthorize("@ss.hasPermission('system.attendance:attendance-statistics:query')")
public CommonResult<AttendanceStatisticsRespVO> getAttendanceStatistics(@RequestParam("id") Long id) {
AttendanceStatisticsDO attendanceStatistics = attendanceStatisticsService.getAttendanceStatistics(id);
return success(BeanUtils.toBean(attendanceStatistics, AttendanceStatisticsRespVO.class));
}
@GetMapping("/page")
@Operation(summary = "获得考勤统计分页")
@PreAuthorize("@ss.hasPermission('system.attendance:attendance-statistics:query')")
public CommonResult<PageResult<AttendanceStatisticsRespVO>> getAttendanceStatisticsPage(@Valid AttendanceStatisticsPageReqVO pageReqVO) {
PageResult<AttendanceStatisticsDO> pageResult = attendanceStatisticsService.getAttendanceStatisticsPage(pageReqVO);
return success(BeanUtils.toBean(pageResult, AttendanceStatisticsRespVO.class));
}
@GetMapping("/export-excel")
@Operation(summary = "导出考勤统计 Excel")
@PreAuthorize("@ss.hasPermission('system.attendance:attendance-statistics:export')")
@OperateLog(type = EXPORT)
public void exportAttendanceStatisticsExcel(@Valid AttendanceStatisticsPageReqVO pageReqVO,
HttpServletResponse response) throws IOException {
pageReqVO.setPageSize(PageParam.PAGE_SIZE_NONE);
List<AttendanceStatisticsDO> list = attendanceStatisticsService.getAttendanceStatisticsPage(pageReqVO).getList();
// 导出 Excel
ExcelUtils.write(response, "考勤统计.xls", "数据", AttendanceStatisticsRespVO.class,
BeanUtils.toBean(list, AttendanceStatisticsRespVO.class));
}
}

View File

@ -0,0 +1,47 @@
package cn.iocoder.yudao.module.system.controller.admin.attendance.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@Schema(description = "管理后台 - 考勤统计分页 Request VO")
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
public class AttendanceStatisticsPageReqVO extends PageParam {
@Schema(description = "考勤用户id", example = "7104")
private Long userId;
@Schema(description = "考勤组id", example = "14983")
private Long attendanceGroupId;
@Schema(description = "考勤组名称", example = "赵六")
private String attendanceGroupName;
@Schema(description = "班次id", example = "14364")
private Long attendanceGroupShiftId;
@Schema(description = "班次名称", example = "李四")
private String attendanceGroupShiftName;
@Schema(description = "日期yyyy-MM-dd格式")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private String[] dayTime;
@Schema(description = "是否异常 0否 1是")
private Integer abnormalFlag;
@Schema(description = "工时(时间戳)")
private Long workingHours;
@Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime;
}

View File

@ -0,0 +1,56 @@
package cn.iocoder.yudao.module.system.controller.admin.attendance.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime;
import com.alibaba.excel.annotation.*;
@Schema(description = "管理后台 - 考勤统计 Response VO")
@Data
@ExcelIgnoreUnannotated
public class AttendanceStatisticsRespVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24107")
@ExcelProperty("编号")
private Long id;
@Schema(description = "考勤用户id", example = "7104")
@ExcelProperty("考勤用户id")
private Long userId;
@Schema(description = "考勤组id", example = "14983")
@ExcelProperty("考勤组id")
private Long attendanceGroupId;
@Schema(description = "考勤组名称", example = "赵六")
@ExcelProperty("考勤组名称")
private String attendanceGroupName;
@Schema(description = "班次id", example = "14364")
@ExcelProperty("班次id")
private Long attendanceGroupShiftId;
@Schema(description = "班次名称", example = "李四")
@ExcelProperty("班次名称")
private String attendanceGroupShiftName;
@Schema(description = "日期yyyy-MM-dd格式")
@ExcelProperty("日期yyyy-MM-dd格式")
private String dayTime;
@Schema(description = "是否异常 0否 1是")
@ExcelProperty("是否异常 0否 1是")
private Integer abnormalFlag;
@Schema(description = "工时(时间戳)")
@ExcelProperty("工时(时间戳)")
private Long workingHours;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间")
private LocalDateTime createTime;
}

View File

@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.system.controller.admin.attendance.vo;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;
import java.util.*;
import javax.validation.constraints.*;
import java.util.*;
@Schema(description = "管理后台 - 考勤统计新增/修改 Request VO")
@Data
public class AttendanceStatisticsSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "24107")
private Long id;
@Schema(description = "考勤用户id", example = "7104")
private Long userId;
@Schema(description = "考勤组id", example = "14983")
private Long attendanceGroupId;
@Schema(description = "考勤组名称", example = "赵六")
private String attendanceGroupName;
@Schema(description = "班次id", example = "14364")
private Long attendanceGroupShiftId;
@Schema(description = "班次名称", example = "李四")
private String attendanceGroupShiftName;
@Schema(description = "日期yyyy-MM-dd格式")
private String dayTime;
@Schema(description = "是否异常 0否 1是")
private Integer abnormalFlag;
@Schema(description = "工时(时间戳)")
private Long workingHours;
}

View File

@ -21,6 +21,9 @@ public class AttendanceGroupShiftPageReqVO extends PageParam {
@Schema(description = "班次名称", example = "张三") @Schema(description = "班次名称", example = "张三")
private String name; private String name;
@Schema(description = "是否次日(开始时间 大于 结束时间)跨天 0否 1是")
private Integer nextDayFlag;
@Schema(description = "创建时间") @Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime; private LocalDateTime[] createTime;

View File

@ -25,6 +25,10 @@ public class AttendanceGroupShiftRespVO {
@ExcelProperty("班次名称") @ExcelProperty("班次名称")
private String name; private String name;
@Schema(description = "是否次日(开始时间 大于 结束时间)跨天 0否 1是")
@ExcelProperty("是否次日(开始时间 大于 结束时间)跨天 0否 1是")
private Integer nextDayFlag;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间") @ExcelProperty("创建时间")
private LocalDateTime createTime; private LocalDateTime createTime;

View File

@ -19,4 +19,7 @@ public class AttendanceGroupShiftSaveReqVO {
@Schema(description = "班次名称", example = "张三") @Schema(description = "班次名称", example = "张三")
private String name; private String name;
@Schema(description = "是否次日(开始时间 大于 结束时间)跨天 0否 1是")
private Integer nextDayFlag;
} }

View File

@ -35,6 +35,9 @@ public class AttendanceGroupShiftItemPageReqVO extends PageParam {
@Schema(description = "是否必须 0否 1是 (如果不是必须系统会自动打卡)") @Schema(description = "是否必须 0否 1是 (如果不是必须系统会自动打卡)")
private Integer mustFlag; private Integer mustFlag;
@Schema(description = "是否次日(开始时间 大于 结束时间)跨天 0否 1是")
private Integer nextDayFlag;
@Schema(description = "上班前打卡时间(分钟) 默认 120分钟") @Schema(description = "上班前打卡时间(分钟) 默认 120分钟")
private Integer beforePunchTimeUpWork; private Integer beforePunchTimeUpWork;

View File

@ -41,6 +41,10 @@ public class AttendanceGroupShiftItemRespVO {
@ExcelProperty("是否必须 0否 1是 (如果不是必须系统会自动打卡)") @ExcelProperty("是否必须 0否 1是 (如果不是必须系统会自动打卡)")
private Integer mustFlag; private Integer mustFlag;
@Schema(description = "是否次日(开始时间 大于 结束时间)跨天 0否 1是")
@ExcelProperty("是否次日(开始时间 大于 结束时间)跨天 0否 1是")
private Integer nextDayFlag;
@Schema(description = "上班前打卡时间(分钟) 默认 120分钟") @Schema(description = "上班前打卡时间(分钟) 默认 120分钟")
@ExcelProperty("上班前打卡时间(分钟) 默认 120分钟") @ExcelProperty("上班前打卡时间(分钟) 默认 120分钟")
private Integer beforePunchTimeUpWork; private Integer beforePunchTimeUpWork;

View File

@ -1,5 +1,6 @@
package cn.iocoder.yudao.module.system.controller.admin.groupshiftitem.vo; package cn.iocoder.yudao.module.system.controller.admin.groupshiftitem.vo;
import com.alibaba.excel.annotation.ExcelProperty;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*; import lombok.*;
import java.util.*; import java.util.*;
@ -31,6 +32,9 @@ public class AttendanceGroupShiftItemSaveReqVO {
@Schema(description = "是否必须 0否 1是 (如果不是必须系统会自动打卡)") @Schema(description = "是否必须 0否 1是 (如果不是必须系统会自动打卡)")
private Integer mustFlag; private Integer mustFlag;
@Schema(description = "是否次日(开始时间 大于 结束时间)跨天 0否 1是")
private Integer nextDayFlag;
@Schema(description = "上班前打卡时间(分钟) 默认 120分钟") @Schema(description = "上班前打卡时间(分钟) 默认 120分钟")
private Integer beforePunchTimeUpWork; private Integer beforePunchTimeUpWork;

View File

@ -1,10 +1,12 @@
package cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo; package cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo;
import lombok.*;
import java.util.*;
import io.swagger.v3.oas.annotations.media.Schema;
import cn.iocoder.yudao.framework.common.pojo.PageParam; import cn.iocoder.yudao.framework.common.pojo.PageParam;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND; import static cn.iocoder.yudao.framework.common.util.date.DateUtils.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND;
@ -24,18 +26,27 @@ public class AttendancePunchRecordPageReqVO extends PageParam {
@Schema(description = "班次id", example = "10780") @Schema(description = "班次id", example = "10780")
private Long attendanceGroupShiftId; private Long attendanceGroupShiftId;
@Schema(description = "班次子表id", example = "10780")
private Long attendanceGroupShiftItemId;
@Schema(description = "考勤类型 1固定班制 2排班制", example = "2") @Schema(description = "考勤类型 1固定班制 2排班制", example = "2")
private Integer type; private Integer type;
@Schema(description = "打卡类型 1考勤机 2小程序范围打卡", example = "2") @Schema(description = "打卡类型 1考勤机 2小程序范围打卡", example = "2")
private Integer punchType; private Integer punchType;
@Schema(description = "班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}]") @Schema(description = "上下班类型 0上班 1下班", example = "1")
private String attendanceTimeJson; private Integer workType;
@Schema(description = "打卡状态 0正常 1迟到 2缺卡 3外勤 4补卡", example = "1") @Schema(description = "级别 1到", example = "1")
private Integer level;
@Schema(description = "打卡状态 0正常 1迟到 2早退 3缺卡 4补卡", example = "1")
private Integer status; private Integer status;
@Schema(description = "是否外勤 0否 1是", example = "1")
private Integer fieldServiceFlag;
@Schema(description = "日期yyyy-MM-dd格式") @Schema(description = "日期yyyy-MM-dd格式")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private String[] dayTime; private String[] dayTime;
@ -50,6 +61,15 @@ public class AttendancePunchRecordPageReqVO extends PageParam {
@Schema(description = "图片") @Schema(description = "图片")
private String image; private String image;
@Schema(description = "用户打卡地点")
private String punchAddress;
@Schema(description = "迟到时长时间戳")
private Long lateTime;
@Schema(description = "早退时长时间戳")
private Long leaveEarlyTime;
@Schema(description = "创建时间") @Schema(description = "创建时间")
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND) @DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND)
private LocalDateTime[] createTime; private LocalDateTime[] createTime;

View File

@ -29,6 +29,10 @@ public class AttendancePunchRecordRespVO {
@ExcelProperty("班次id") @ExcelProperty("班次id")
private Long attendanceGroupShiftId; private Long attendanceGroupShiftId;
@Schema(description = "班次子表id", example = "10780")
@ExcelProperty("班次子表id")
private Long attendanceGroupShiftItemId;
@Schema(description = "考勤类型 1固定班制 2排班制", example = "2") @Schema(description = "考勤类型 1固定班制 2排班制", example = "2")
@ExcelProperty("考勤类型 1固定班制 2排班制") @ExcelProperty("考勤类型 1固定班制 2排班制")
private Integer type; private Integer type;
@ -37,14 +41,22 @@ public class AttendancePunchRecordRespVO {
@ExcelProperty("打卡类型 1考勤机 2小程序范围打卡") @ExcelProperty("打卡类型 1考勤机 2小程序范围打卡")
private Integer punchType; private Integer punchType;
@Schema(description = "班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}]") @Schema(description = "上下班类型 0上班 1下班", example = "1")
@ExcelProperty("班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}]") @ExcelProperty("上下班类型 0上班 1下班")
private String attendanceTimeJson; private Integer workType;
@Schema(description = "打卡状态 0正常 1迟到 2缺卡 3外勤 4补卡", example = "1") @Schema(description = "级别 1到", example = "1")
@ExcelProperty("打卡状态 0正常 1迟到 2缺卡 3外勤 4补卡") @ExcelProperty("级别 1到")
private Integer level;
@Schema(description = "打卡状态 0正常 1迟到 2早退 3缺卡 4补卡", example = "1")
@ExcelProperty("打卡状态 0正常 1迟到 2早退 3缺卡 4补卡")
private Integer status; private Integer status;
@Schema(description = "是否外勤 0否 1是", example = "1")
@ExcelProperty("是否外勤 0否 1是")
private Integer fieldServiceFlag;
@Schema(description = "日期yyyy-MM-dd格式") @Schema(description = "日期yyyy-MM-dd格式")
@ExcelProperty("日期yyyy-MM-dd格式") @ExcelProperty("日期yyyy-MM-dd格式")
private String dayTime; private String dayTime;
@ -61,6 +73,17 @@ public class AttendancePunchRecordRespVO {
@ExcelProperty("图片") @ExcelProperty("图片")
private String image; private String image;
@Schema(description = "用户打卡地点")
private String punchAddress;
@Schema(description = "迟到时长时间戳")
@ExcelProperty("迟到时长时间戳")
private Long lateTime;
@Schema(description = "早退时长时间戳")
@ExcelProperty("早退时长时间戳")
private Long leaveEarlyTime;
@Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED) @Schema(description = "创建时间", requiredMode = Schema.RequiredMode.REQUIRED)
@ExcelProperty("创建时间") @ExcelProperty("创建时间")
private LocalDateTime createTime; private LocalDateTime createTime;

View File

@ -1,17 +1,15 @@
package cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo; package cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo;
import cn.hutool.core.date.LocalDateTimeUtil;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*; import lombok.Data;
import java.util.*;
import javax.validation.constraints.*;
import java.util.*;
import org.springframework.format.annotation.DateTimeFormat;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
@Schema(description = "管理后台 - 用户打卡记录新增/修改 Request VO") @Schema(description = "管理后台 - 用户打卡记录新增/修改 Request VO")
@Data @Data
public class AttendancePunchRecordSaveReqVO { public class AttendancePunchRecordSaveReqVO {
@Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "65") @Schema(description = "编号", requiredMode = Schema.RequiredMode.REQUIRED, example = "65")
private Long id; private Long id;
@ -24,21 +22,33 @@ public class AttendancePunchRecordSaveReqVO {
@Schema(description = "班次id", example = "10780") @Schema(description = "班次id", example = "10780")
private Long attendanceGroupShiftId; private Long attendanceGroupShiftId;
@Schema(description = "班次子表id", example = "10780")
private Long attendanceGroupShiftItemId;
@Schema(description = "考勤类型 1固定班制 2排班制", example = "2") @Schema(description = "考勤类型 1固定班制 2排班制", example = "2")
private Integer type; private Integer type;
@Schema(description = "打卡类型 1考勤机 2小程序范围打卡", example = "2") @Schema(description = "打卡类型 1考勤机 2小程序范围打卡", example = "2")
private Integer punchType; private Integer punchType;
@Schema(description = "班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}]") @Schema(description = "上下班类型 0上班 1下班", example = "1")
private String attendanceTimeJson; private Integer workType;
@Schema(description = "打卡状态 0正常 1迟到 2缺卡 3外勤 4补卡", example = "1") @Schema(description = "级别 1到", example = "1")
private Integer level;
@Schema(description = "打卡状态 0正常 1迟到 2早退 3缺卡 4补卡", example = "1")
private Integer status; private Integer status;
@Schema(description = "是否外勤 0否 1是", example = "1")
private Integer fieldServiceFlag;
@Schema(description = "日期yyyy-MM-dd格式") @Schema(description = "日期yyyy-MM-dd格式")
private String dayTime; private String dayTime;
@Schema(description = "应打卡时间")
private LocalDateTime shouldPunchTime;
@Schema(description = "打卡时间") @Schema(description = "打卡时间")
private LocalDateTime punchTime; private LocalDateTime punchTime;
@ -48,4 +58,32 @@ public class AttendancePunchRecordSaveReqVO {
@Schema(description = "图片") @Schema(description = "图片")
private String image; private String image;
@Schema(description = "用户打卡地点")
private String punchAddress;
@Schema(description = "迟到时长时间戳")
private Long lateTime = (status == 1 ? LocalDateTimeUtil.between(shouldPunchTime, punchTime, ChronoUnit.MILLIS) : 0L);
@Schema(description = "早退时长时间戳")
private Long leaveEarlyTime = (status == 2 ? LocalDateTimeUtil.between(shouldPunchTime, punchTime, ChronoUnit.MILLIS) : 0L);
/**
* 打卡类型(0上班卡 1下班卡 2迟到卡 3早退卡 4未到打卡时间) 打卡状态 0正常 1迟到 2早退 3缺卡 4补卡
*
* @param punchType
* @return
*/
public static Integer convertStatus(Integer punchType) {
if (punchType == 0 || punchType == 1) {
return 0;
} else if (punchType == 2) {
return 1;
} else if (punchType == 3) {
return 2;
}
return null;
}
} }

View File

@ -87,7 +87,6 @@ public class LogFormController {
@GetMapping("/page") @GetMapping("/page")
@Operation(summary = "获得动态模板分页") @Operation(summary = "获得动态模板分页")
@PreAuthorize("@ss.hasPermission('worklog:form:query')")
public CommonResult<PageResult<LogFormRespVO>> getFormPage(@Valid LogFormPageReqVO pageVO) { public CommonResult<PageResult<LogFormRespVO>> getFormPage(@Valid LogFormPageReqVO pageVO) {
PageResult<LogFormDO> pageResult = formService.getFormPage(pageVO); PageResult<LogFormDO> pageResult = formService.getFormPage(pageVO);
return success(LogFormConvert.INSTANCE.convertPage(pageResult)); return success(LogFormConvert.INSTANCE.convertPage(pageResult));
@ -95,7 +94,6 @@ public class LogFormController {
@GetMapping("/page-by-rule") @GetMapping("/page-by-rule")
@Operation(summary = "获得我可以使用的模板分页") @Operation(summary = "获得我可以使用的模板分页")
@PreAuthorize("@ss.hasPermission('worklog:form:query')")
public CommonResult<PageResult<LogFormRespVO>> getFormPageByRule(@Valid LogFormPageReqVO pageVO) { public CommonResult<PageResult<LogFormRespVO>> getFormPageByRule(@Valid LogFormPageReqVO pageVO) {
PageResult<LogFormDO> pageResult = formService.getFormPageByRule(pageVO); PageResult<LogFormDO> pageResult = formService.getFormPageByRule(pageVO);

View File

@ -1,17 +1,15 @@
package cn.iocoder.yudao.module.system.controller.app.attendance; package cn.iocoder.yudao.module.system.controller.app.attendance;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils; import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchDTO;
import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchPageDTO; import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchPageDTO;
import cn.iocoder.yudao.module.system.controller.app.attendance.vo.AttendancePunchPageVO; import cn.iocoder.yudao.module.system.controller.app.attendance.vo.AttendancePunchPageVO;
import cn.iocoder.yudao.module.system.controller.app.attendance.vo.AttendancePunchVO;
import cn.iocoder.yudao.module.system.service.attendance.AttendanceService; import cn.iocoder.yudao.module.system.service.attendance.AttendanceService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.*;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource; import javax.annotation.Resource;
@ -22,15 +20,21 @@ import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@RequestMapping("/system/attendance") @RequestMapping("/system/attendance")
@Validated @Validated
public class AttendanceController { public class AttendanceController {
@Resource @Resource
private AttendanceService attendanceService; private AttendanceService attendanceService;
@GetMapping("/getPunchPage") @GetMapping("/getPunchPage")
@Operation(summary = "获取考勤页面") @Operation(summary = "获取考勤页面")
public CommonResult<AttendancePunchPageVO> getPunchPage(@ModelAttribute AttendancePunchPageDTO dto) { public CommonResult<AttendancePunchPageVO> getPunchPage(@ModelAttribute AttendancePunchPageDTO dto) {
Long userId = WebFrameworkUtils.getLoginUserId(); AttendancePunchPageVO vo = attendanceService.getPunchPage(dto);
AttendancePunchPageVO vo = attendanceService.getPunchPage(dto.setUserId(userId)); return success(vo);
}
@PostMapping("/punch")
@Operation(summary = "打卡")
public CommonResult<AttendancePunchVO> punch(@RequestBody AttendancePunchDTO dto) {
AttendancePunchVO vo = attendanceService.punch(dto);
return success(vo); return success(vo);
} }

View File

@ -0,0 +1,40 @@
package cn.iocoder.yudao.module.system.controller.app.attendance.dto;
import cn.iocoder.yudao.framework.web.core.util.WebFrameworkUtils;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.AttendanceGroupDO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Data
@Accessors(chain = true)
public class AttendancePunchDTO {
@Schema(description = "经度")
private String longitude;
@Schema(description = "纬度")
private String latitude;
@Schema(description = "用户打卡地点")
private String punchAddress;
@Schema(description = "照片")
private String image;
@Schema(description = "备注")
private String remark;
/**
* 当前用户id
*/
private Long userId = getLoginUserId();
/**
* 当前时间
*/
private LocalDateTime localDateTime = LocalDateTime.now();
}

View File

@ -5,6 +5,10 @@ import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import static cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils.getLoginUserId;
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class AttendancePunchPageDTO { public class AttendancePunchPageDTO {
@ -18,10 +22,18 @@ public class AttendancePunchPageDTO {
/** /**
* 当前用户id * 当前用户id
*/ */
private Long userId; private Long userId = getLoginUserId();
/**
* 是否返回考勤组考勤班次信息
*/
private Boolean flag = false;
/** /**
* 考勤组 * 考勤组
*/ */
private AttendanceGroupDO activationGroup; private AttendanceGroupDO activationGroup;
/**
* 当前时间
*/
private LocalDateTime localDateTime = LocalDateTime.now();
} }

View File

@ -1,9 +1,15 @@
package cn.iocoder.yudao.module.system.controller.app.attendance.vo; package cn.iocoder.yudao.module.system.controller.app.attendance.vo;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.AttendanceGroupDO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshift.AttendanceGroupShiftDO;
import cn.iocoder.yudao.module.system.service.attendance.punch.dto.AttendanceOnTheDayDTO;
import io.swagger.v3.oas.annotations.media.Schema; import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data; import lombok.Data;
import lombok.experimental.Accessors; import lombok.experimental.Accessors;
import java.time.LocalDateTime;
import java.util.List;
@Data @Data
@Accessors(chain = true) @Accessors(chain = true)
public class AttendancePunchPageVO { public class AttendancePunchPageVO {
@ -14,12 +20,40 @@ public class AttendancePunchPageVO {
@Schema(description = "是否允许外勤打卡 0否 1是") @Schema(description = "是否允许外勤打卡 0否 1是")
private Integer fieldworkFlag; private Integer fieldworkFlag;
@Schema(description = "是否在打卡点 0否 1是") @Schema(description = "是否在打卡点 0否 1是")
private Integer checkInPoint; private Integer punchPoint;
@Schema(description = "打卡页面顶部 当天打卡记录 已打卡 0/4") @Schema(description = "打卡类型(0上班卡 1下班卡 2迟到卡 3早退卡 4未到打卡时间)")
private String checkInRecordToday; private Integer punchType = 4;
@Schema(description = "打卡类型(0上班卡 1下班卡 2迟到卡 3外勤卡)") @Schema(description = "当天考勤列表")
private String punchType; private List<AttendanceOnTheDayDTO> attendanceOnTheDayDTOS;
//打卡状态 0正常 1迟到 2缺卡 3外勤 4补卡 5早退
@Schema(description = "考勤规则/打卡记录 JSON格式 [{name:上班 09:00,punchTime:yyyy-MM-dd HH:mm:ss,status:1},{name:下班 12:00,punchTime:yyyy-MM-dd HH:mm:ss,status:2}]") /**
private String punchJson; * redisKey
*/
private String redisKey;
/**
* 打卡数组当前下标
*/
private Integer index;
/**
* 类型 0上班 1下班
*/
private Integer type;
/**
* 应打卡时间
*/
private LocalDateTime shouldPunchTime;
/**
* 考勤组
*/
private AttendanceGroupDO activationGroup;
/**
* 考勤组班次
*/
private AttendanceGroupShiftDO attendanceGroupShiftDO;
/**
* 考勤组班次子表
*/
private Long attendanceGroupShiftItemId;
} }

View File

@ -0,0 +1,23 @@
package cn.iocoder.yudao.module.system.controller.app.attendance.vo;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.AttendanceGroupDO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshift.AttendanceGroupShiftDO;
import cn.iocoder.yudao.module.system.service.attendance.punch.dto.AttendanceOnTheDayDTO;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import lombok.experimental.Accessors;
import java.util.List;
@Data
@Accessors(chain = true)
public class AttendancePunchVO {
@Schema(description = "考勤组")
private AttendanceGroupDO activationGroup;
@Schema(description = "考勤组班次")
private AttendanceGroupShiftDO attendanceGroupShiftDO;
@Schema(description = "当天考勤列表")
private List<AttendanceOnTheDayDTO> attendanceOnTheDayDTOS;
}

View File

@ -25,6 +25,12 @@ public class AttendanceGroupDO extends BaseDO {
*/ */
public static final Integer FALSE = 0; public static final Integer FALSE = 0;
public static final Integer TRUE = 1; public static final Integer TRUE = 1;
/**
* 打卡类型 1考勤机 2小程序范围打卡
*/
public static final Integer PUNCH_TYPE_ATTENDANCE_MACHINE = 1;
public static final Integer PUNCH_TYPE_MI_NI_APP = 2;
/** /**
* 编号 * 编号
*/ */

View File

@ -35,4 +35,8 @@ public class AttendanceGroupShiftDO extends BaseDO {
* 班次名称 * 班次名称
*/ */
private String name; private String name;
/**
* 是否次日(开始时间 大于 结束时间)跨天 0否 1是
*/
private Integer nextDayFlag;
} }

View File

@ -1,11 +1,10 @@
package cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshiftitem; package cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshiftitem;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
import com.baomidou.mybatisplus.annotation.KeySequence;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.*;
/** /**
* 考勤组班次子表 DO * 考勤组班次子表 DO
@ -40,17 +39,21 @@ public class AttendanceGroupShiftItemDO extends BaseDO {
*/ */
private Integer level; private Integer level;
/** /**
* 开始打卡时间 * 开始打卡时间 HH:mm
*/ */
private String beginTime; private String beginTime;
/** /**
* 结束打卡时间 * 结束打卡时间 HH:mm
*/ */
private String endTime; private String endTime;
/** /**
* 是否必须 0否 1是 (如果不是必须系统会自动打卡) * 是否必须 0否 1是 (如果不是必须系统会自动打卡)
*/ */
private Integer mustFlag; private Integer mustFlag;
/**
* 是否次日(开始时间 大于 结束时间)跨天 0否 1是
*/
private Integer nextDayFlag;
/** /**
* 上班前打卡时间分钟 默认 120分钟 * 上班前打卡时间分钟 默认 120分钟
*/ */

View File

@ -1,10 +1,13 @@
package cn.iocoder.yudao.module.system.dal.dataobject.attendance.punchrecord; package cn.iocoder.yudao.module.system.dal.dataobject.attendance.punchrecord;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*; import lombok.*;
import java.util.*; import java.util.*;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import java.time.LocalDateTime; import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*; import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO; import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
@ -40,6 +43,10 @@ public class AttendancePunchRecordDO extends BaseDO {
* 班次id * 班次id
*/ */
private Long attendanceGroupShiftId; private Long attendanceGroupShiftId;
/**
* 班次子表id
*/
private Long attendanceGroupShiftItemId;
/** /**
* 考勤类型 1固定班制 2排班制 * 考勤类型 1固定班制 2排班制
*/ */
@ -48,14 +55,25 @@ public class AttendancePunchRecordDO extends BaseDO {
* 打卡类型 1考勤机 2小程序范围打卡 * 打卡类型 1考勤机 2小程序范围打卡
*/ */
private Integer punchType; private Integer punchType;
/** /**
* 班次考勤时间json[{start_tiem:HH:mm,start_check_flag:true,end_time:HH:mm,end_check_flah: true}] * 上下班类型 0上班 1下班
*/ */
private String attendanceTimeJson; private Integer workType;
/** /**
* 打卡状态 0正常 1迟到 2缺卡 3外勤 4补卡 * 级别 1到
*/
private Integer level;
/**
* 打卡状态 0正常 1迟到 2早退 3缺卡 4补卡
*/ */
private Integer status; private Integer status;
/**
* 是否外勤 0否 1是
*/
private Integer fieldServiceFlag;
/** /**
* 日期yyyy-MM-dd格式 * 日期yyyy-MM-dd格式
*/ */
@ -73,4 +91,18 @@ public class AttendancePunchRecordDO extends BaseDO {
*/ */
private String image; private String image;
/**
* 用户打卡地点
*/
private String punchAddress;
/**
* 迟到时长
*/
private String lateTime;
/**
* 早退时长
*/
private String leaveEarlyTime;
} }

View File

@ -0,0 +1,63 @@
package cn.iocoder.yudao.module.system.dal.dataobject.attendance.statistics;
import lombok.*;
import java.util.*;
import java.time.LocalDateTime;
import java.time.LocalDateTime;
import com.baomidou.mybatisplus.annotation.*;
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
/**
* 考勤统计 DO
*
* @author 艾楷
*/
@TableName("kq_attendance_statistics")
@KeySequence("kq_attendance_statistics_seq") // 用于 OraclePostgreSQLKingbaseDB2H2 数据库的主键自增如果是 MySQL 等数据库可不写
@Data
@EqualsAndHashCode(callSuper = true)
@ToString(callSuper = true)
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AttendanceStatisticsDO extends BaseDO {
/**
* 编号
*/
@TableId
private Long id;
/**
* 考勤用户id
*/
private Long userId;
/**
* 考勤组id
*/
private Long attendanceGroupId;
/**
* 考勤组名称
*/
private String attendanceGroupName;
/**
* 班次id
*/
private Long attendanceGroupShiftId;
/**
* 班次名称
*/
private String attendanceGroupShiftName;
/**
* 日期yyyy-MM-dd格式
*/
private String dayTime;
/**
* 是否异常 0否 1是
*/
private Integer abnormalFlag;
/**
* 工时时间戳
*/
private Long workingHours;
}

View File

@ -22,8 +22,8 @@ public interface AttendancePunchRecordMapper extends BaseMapperX<AttendancePunch
.eqIfPresent(AttendancePunchRecordDO::getAttendanceGroupShiftId, reqVO.getAttendanceGroupShiftId()) .eqIfPresent(AttendancePunchRecordDO::getAttendanceGroupShiftId, reqVO.getAttendanceGroupShiftId())
.eqIfPresent(AttendancePunchRecordDO::getType, reqVO.getType()) .eqIfPresent(AttendancePunchRecordDO::getType, reqVO.getType())
.eqIfPresent(AttendancePunchRecordDO::getPunchType, reqVO.getPunchType()) .eqIfPresent(AttendancePunchRecordDO::getPunchType, reqVO.getPunchType())
.eqIfPresent(AttendancePunchRecordDO::getAttendanceTimeJson, reqVO.getAttendanceTimeJson())
.eqIfPresent(AttendancePunchRecordDO::getStatus, reqVO.getStatus()) .eqIfPresent(AttendancePunchRecordDO::getStatus, reqVO.getStatus())
.eqIfPresent(AttendancePunchRecordDO::getFieldServiceFlag, reqVO.getFieldServiceFlag())
.betweenIfPresent(AttendancePunchRecordDO::getDayTime, reqVO.getDayTime()) .betweenIfPresent(AttendancePunchRecordDO::getDayTime, reqVO.getDayTime())
.betweenIfPresent(AttendancePunchRecordDO::getPunchTime, reqVO.getPunchTime()) .betweenIfPresent(AttendancePunchRecordDO::getPunchTime, reqVO.getPunchTime())
.eqIfPresent(AttendancePunchRecordDO::getRemark, reqVO.getRemark()) .eqIfPresent(AttendancePunchRecordDO::getRemark, reqVO.getRemark())

View File

@ -4,9 +4,12 @@ import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX; import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.scheduling.AttendanceSchedulingDO; import cn.iocoder.yudao.module.system.dal.dataobject.attendance.scheduling.AttendanceSchedulingDO;
import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.*; import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.*;
import java.util.Map;
/** /**
* 排班制考勤设置 Mapper * 排班制考勤设置 Mapper
* *
@ -24,5 +27,6 @@ public interface AttendanceSchedulingMapper extends BaseMapperX<AttendanceSchedu
.betweenIfPresent(AttendanceSchedulingDO::getCreateTime, reqVO.getCreateTime()) .betweenIfPresent(AttendanceSchedulingDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(AttendanceSchedulingDO::getId)); .orderByDesc(AttendanceSchedulingDO::getId));
} }
@MapKey("id")
Map<Long, Integer> countGroupId();
} }

View File

@ -0,0 +1,32 @@
package cn.iocoder.yudao.module.system.dal.mysql.attendance.statistics;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.mybatis.core.mapper.BaseMapperX;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.system.controller.admin.attendance.vo.AttendanceStatisticsPageReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.statistics.AttendanceStatisticsDO;
import org.apache.ibatis.annotations.Mapper;
/**
* 考勤统计 Mapper
*
* @author 艾楷
*/
@Mapper
public interface AttendanceStatisticsMapper extends BaseMapperX<AttendanceStatisticsDO> {
default PageResult<AttendanceStatisticsDO> selectPage(AttendanceStatisticsPageReqVO reqVO) {
return selectPage(reqVO, new LambdaQueryWrapperX<AttendanceStatisticsDO>()
.eqIfPresent(AttendanceStatisticsDO::getUserId, reqVO.getUserId())
.eqIfPresent(AttendanceStatisticsDO::getAttendanceGroupId, reqVO.getAttendanceGroupId())
.likeIfPresent(AttendanceStatisticsDO::getAttendanceGroupName, reqVO.getAttendanceGroupName())
.eqIfPresent(AttendanceStatisticsDO::getAttendanceGroupShiftId, reqVO.getAttendanceGroupShiftId())
.likeIfPresent(AttendanceStatisticsDO::getAttendanceGroupShiftName, reqVO.getAttendanceGroupShiftName())
.betweenIfPresent(AttendanceStatisticsDO::getDayTime, reqVO.getDayTime())
.eqIfPresent(AttendanceStatisticsDO::getAbnormalFlag, reqVO.getAbnormalFlag())
.eqIfPresent(AttendanceStatisticsDO::getWorkingHours, reqVO.getWorkingHours())
.betweenIfPresent(AttendanceStatisticsDO::getCreateTime, reqVO.getCreateTime())
.orderByDesc(AttendanceStatisticsDO::getId));
}
}

View File

@ -0,0 +1,28 @@
package cn.iocoder.yudao.module.system.job.attendance;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import cn.iocoder.yudao.module.system.dal.mysql.attendance.statistics.AttendanceStatisticsMapper;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
@Component
@Slf4j
public class AttendanceStatisticsJob {
@Resource
private AttendanceStatisticsMapper attendanceStatisticsMapper;
// TODO: 2024/4/22 - 每天晚上24点执行
@XxlJob("attendanceStatisticsJob")
@TenantJob // --- 这个注解 会将租户列表拉出来 完了后逐个租户执行 定时任务需要注意
public ReturnT<String> execute() throws Exception {
// -- 获取所有考勤内人员 -
// 返回执行成功
return ReturnT.SUCCESS;
}
}

View File

@ -0,0 +1,47 @@
package cn.iocoder.yudao.module.system.job.attendance;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.Constants;
import cn.iocoder.yudao.framework.tenant.core.job.TenantJob;
import cn.iocoder.yudao.module.system.service.attendance.scheduling.AttendanceSchedulingService;
import com.xxl.job.core.biz.model.ReturnT;
import com.xxl.job.core.handler.annotation.XxlJob;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.Map;
@Component
@Slf4j
public class SchedulingJob {
@Resource
private AttendanceSchedulingService attendanceSchedulingService;
@Resource
private StringRedisTemplate stringRedisTemplate;
// TODO: 2024/4/22 - 每天晚上24点执行
@XxlJob("schedulingCycleIncrementJob")
@TenantJob // --- 这个注解 会将租户列表拉出来 完了后逐个租户执行 定时任务需要注意
public ReturnT<String> execute() throws Exception {
// -- 获取每个排班考勤组 有多少数量(也就是周期多少日)
Map<Long, Integer> map = attendanceSchedulingService.countGroupId();
if (MapUtil.isEmpty(map)) {
return ReturnT.SUCCESS;
}
for (Map.Entry<Long, Integer> entry : map.entrySet()) {
String schedulingKey = Constants.SCHEDULING + Constants.UNDERLINE + entry.getKey();
String value = stringRedisTemplate.opsForValue().get(schedulingKey);
// -- 如果大于等于周期数 则重置为1
if (StrUtil.isNotEmpty(value) && Integer.parseInt(value) >= entry.getValue()) {
stringRedisTemplate.opsForValue().set(schedulingKey, "1");
} else {
stringRedisTemplate.opsForValue().increment(schedulingKey);
}
}
// 返回执行成功
return ReturnT.SUCCESS;
}
}

View File

@ -1,8 +1,11 @@
package cn.iocoder.yudao.module.system.service.attendance; package cn.iocoder.yudao.module.system.service.attendance;
import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchDTO;
import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchPageDTO; import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchPageDTO;
import cn.iocoder.yudao.module.system.controller.app.attendance.vo.AttendancePunchPageVO; import cn.iocoder.yudao.module.system.controller.app.attendance.vo.AttendancePunchPageVO;
import cn.iocoder.yudao.module.system.controller.app.attendance.vo.AttendancePunchVO;
import java.time.LocalDateTime;
import java.util.Date; import java.util.Date;
/** /**
@ -12,15 +15,36 @@ import java.util.Date;
*/ */
public interface AttendanceService { public interface AttendanceService {
/**
* 获取打卡页面
*
* @param attendancePunchPageDTO
* @return
*/
AttendancePunchPageVO getPunchPage(AttendancePunchPageDTO attendancePunchPageDTO); AttendancePunchPageVO getPunchPage(AttendancePunchPageDTO attendancePunchPageDTO);
/** /**
* 判断当前传如时间是否是 节假日(节假日分 补班/放假) 补班的话返回false 放假的话返回 true * 判断当前传如时间是否是 节假日(节假日分 补班/放假) 补班的话返回false 放假的话返回 true
* 如果不是节假日的话返回null * 如果不是节假日的话返回null
* *
* @param date * @param localDateTime
* @return * @return
*/ */
Boolean isHoliday(Date date); Boolean isHoliday(LocalDateTime localDateTime);
/**
* 计算考勤信息
*
* @param vo
* @param dto
*/
void calculatePunch(AttendancePunchPageDTO dto, AttendancePunchPageVO vo);
/**
* 打卡
*
* @param attendancePunchDTO
* @return
*/
AttendancePunchVO punch(AttendancePunchDTO attendancePunchDTO);
} }

View File

@ -1,25 +1,48 @@
package cn.iocoder.yudao.module.system.service.attendance; package cn.iocoder.yudao.module.system.service.attendance;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.http.HttpUtil; import cn.hutool.http.HttpUtil;
import cn.hutool.json.JSONObject; import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import cn.iocoder.yudao.framework.common.Constants;
import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.distance.GeoUtil;
import cn.iocoder.yudao.module.system.controller.admin.punchrecord.vo.AttendancePunchRecordSaveReqVO;
import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchDTO;
import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchPageDTO; import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchPageDTO;
import cn.iocoder.yudao.module.system.controller.app.attendance.vo.AttendancePunchPageVO; import cn.iocoder.yudao.module.system.controller.app.attendance.vo.AttendancePunchPageVO;
import cn.iocoder.yudao.module.system.controller.app.attendance.vo.AttendancePunchVO;
import cn.iocoder.yudao.module.system.controller.app.attendance.vo.HolidayVO; import cn.iocoder.yudao.module.system.controller.app.attendance.vo.HolidayVO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.AttendanceGroupDO; import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.AttendanceGroupDO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshift.AttendanceGroupShiftDO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshiftitem.AttendanceGroupShiftItemDO;
import cn.iocoder.yudao.module.system.handler.PunchHandler; import cn.iocoder.yudao.module.system.handler.PunchHandler;
import cn.iocoder.yudao.module.system.service.attendance.group.AttendanceGroupService; import cn.iocoder.yudao.module.system.service.attendance.group.AttendanceGroupService;
import cn.iocoder.yudao.module.system.service.attendance.groupshift.AttendanceGroupShiftService;
import cn.iocoder.yudao.module.system.service.attendance.groupshiftitem.AttendanceGroupShiftItemService;
import cn.iocoder.yudao.module.system.service.attendance.punch.PunchService; import cn.iocoder.yudao.module.system.service.attendance.punch.PunchService;
import cn.iocoder.yudao.module.system.service.attendance.punch.dto.AttendanceOnTheDayDTO;
import cn.iocoder.yudao.module.system.service.attendance.punchrecord.AttendancePunchRecordService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.Date; import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.*;
/** /**
* 考勤 实现类 * 考勤 实现类
*/ */
@ -32,6 +55,29 @@ public class AttendanceServiceImpl implements AttendanceService {
private AttendanceGroupService attendanceGroupService; private AttendanceGroupService attendanceGroupService;
@Resource @Resource
private StringRedisTemplate stringRedisTemplate; private StringRedisTemplate stringRedisTemplate;
@Resource
private AttendanceGroupShiftService attendanceGroupShiftService;
@Resource
private AttendanceGroupShiftItemService attendanceGroupShiftItemService;
@Resource
private AttendancePunchRecordService attendancePunchRecordService;
// 定义一些常量以提高代码的可读性和可维护性
/**
* 打卡类型(0上班卡 1下班卡 2迟到卡 3早退卡 4未到打卡时间)
*/
private static final Integer PUNCH_TYPE_UP_WORK = 0;
private static final Integer PUNCH_TYPE_DOWN_WORK = 1;
private static final Integer PUNCH_TYPE_BE_LATE = 2;
private static final Integer PUNCH_TYPE_LEAVE_EARLY = 3;
private static final Integer PUNCH_TYPE_NOT_COME_YET = 4;
/**
* 上下班标识 0上班 1下班
*/
private static final Integer UP_WORK = 0;
private static final Integer DOWN_WORK = 1;
@Override @Override
public AttendancePunchPageVO getPunchPage(AttendancePunchPageDTO dto) { public AttendancePunchPageVO getPunchPage(AttendancePunchPageDTO dto) {
@ -43,14 +89,79 @@ public class AttendanceServiceImpl implements AttendanceService {
vo.setInGroup(AttendanceGroupDO.FALSE); vo.setInGroup(AttendanceGroupDO.FALSE);
return vo; return vo;
} }
if (dto.getFlag()) {
vo.setActivationGroup(activationGroup);
}
// 策略模式 - 将不同考勤类型的 分散开逻辑 - 后续好拓展 // 策略模式 - 将不同考勤类型的 分散开逻辑 - 后续好拓展
PunchService punchService = punchHandler.getResource(AttendanceGroupDO.getCodeByType(activationGroup.getType())); PunchService punchService = punchHandler.getResource(AttendanceGroupDO.getCodeByType(activationGroup.getType()));
return punchService.getPunchPage(dto.setActivationGroup(activationGroup)); return punchService.getPunchPage(dto.setActivationGroup(activationGroup));
} }
@Override @Override
public Boolean isHoliday(Date date) { @Transactional(rollbackFor = Exception.class)
String dateStr = DateUtils.dateFormat(date, "yyyy-MM-dd"); public AttendancePunchVO punch(AttendancePunchDTO dto) {
AttendancePunchPageVO pageVO = this.getPunchPage(new AttendancePunchPageDTO().setLongitude(dto.getLongitude())
.setLatitude(dto.getLatitude()).setFlag(true).setLocalDateTime(dto.getLocalDateTime()).setUserId(dto.getUserId()));
// -- 获取当天的考勤记录
if (AttendanceGroupDO.FALSE.equals(pageVO.getInGroup())) {
throw exception(NOT_IN_THE_ATTENDANCE_GROUP);
}
if (AttendanceGroupDO.FALSE.equals(pageVO.getTodayNeedAttendance())) {
throw exception(NO_ATTENDANCE_REQUIRED_ON_THE_DAY);
}
if (AttendanceGroupDO.FALSE.equals(pageVO.getFieldworkFlag()) && AttendanceGroupDO.FALSE.equals(pageVO.getPunchPoint())) {
throw exception(DID_NOT_ENTER_THE_ATTENDANCE_RANGE);
}
if (PUNCH_TYPE_NOT_COME_YET.equals(pageVO.getPunchType())) {
throw exception(ATTENDANCE_TIME_NOT_ARRIVED);
}
AttendancePunchVO vo = new AttendancePunchVO();
// -- 插入数据到数据库中 - 如果插入成功 更新redis
List<AttendanceOnTheDayDTO> attendanceOnTheDayDTOS = pageVO.getAttendanceOnTheDayDTOS();
AttendanceOnTheDayDTO dayDTO = attendanceOnTheDayDTOS.get(pageVO.getIndex());
AttendancePunchRecordSaveReqVO attendancePunchRecordSaveReqVO = new AttendancePunchRecordSaveReqVO()
.setUserId(dto.getUserId())
.setAttendanceGroupId(pageVO.getActivationGroup().getId())
.setAttendanceGroupShiftId(pageVO.getAttendanceGroupShiftDO().getId())
.setAttendanceGroupShiftItemId(pageVO.getAttendanceGroupShiftItemId())
.setType(pageVO.getActivationGroup().getType())
.setPunchType(AttendanceGroupDO.PUNCH_TYPE_MI_NI_APP)
.setWorkType(pageVO.getType())
.setLevel(dayDTO.getLevel())
.setStatus(AttendancePunchRecordSaveReqVO.convertStatus(pageVO.getPunchType()))
.setFieldServiceFlag(
(AttendanceGroupDO.TRUE.equals(pageVO.getFieldworkFlag()) && AttendanceGroupDO.FALSE.equals(pageVO.getPunchPoint()))
? AttendanceGroupDO.TRUE : AttendanceGroupDO.FALSE)
.setDayTime(dto.getLocalDateTime().format(Constants.REPO_DATE_FORMAT))
.setShouldPunchTime(pageVO.getShouldPunchTime())
.setPunchTime(dto.getLocalDateTime())
.setRemark(dto.getRemark())
.setImage(dto.getImage())
.setPunchAddress(dto.getPunchAddress());
Long id = attendancePunchRecordService.createPunchRecord(attendancePunchRecordSaveReqVO);
// -- 新增成功后更新redis
if (id != null) {
dayDTO.setPunchTime(dto.getLocalDateTime().format(Constants.REPO_DATE_FORMAT));
dayDTO.setPunchStatus(attendancePunchRecordSaveReqVO.getStatus());
dayDTO.setFieldServiceFlag(attendancePunchRecordSaveReqVO.getFieldServiceFlag());
dayDTO.setPunchAddress(dto.getPunchAddress());
dayDTO.setRemark(dto.getRemark());
dayDTO.setImage(dto.getImage());
dayDTO.setShouldPunchTime(pageVO.getShouldPunchTime());
dayDTO.setPunchLocalDateTime(dto.getLocalDateTime());
String mapKey = dto.getUserId().toString();
stringRedisTemplate.opsForHash().put(pageVO.getRedisKey(), mapKey, JSONUtil.toJsonStr(attendanceOnTheDayDTOS));
}
vo.setActivationGroup(pageVO.getActivationGroup());
vo.setAttendanceGroupShiftDO(pageVO.getAttendanceGroupShiftDO());
vo.setAttendanceOnTheDayDTOS(attendanceOnTheDayDTOS);
return vo;
}
@Override
public Boolean isHoliday(LocalDateTime localDateTime) {
String dateStr = localDateTime.format(Constants.REPO_DATE_FORMAT);
String year = dateStr.substring(0, 4); String year = dateStr.substring(0, 4);
String key = "holiday_" + year; String key = "holiday_" + year;
Boolean flag = stringRedisTemplate.hasKey(key); Boolean flag = stringRedisTemplate.hasKey(key);
@ -65,7 +176,179 @@ public class AttendanceServiceImpl implements AttendanceService {
String lastKey = "holiday_" + (Integer.parseInt(year) - 1); String lastKey = "holiday_" + (Integer.parseInt(year) - 1);
stringRedisTemplate.delete(lastKey); stringRedisTemplate.delete(lastKey);
} }
return (Boolean) stringRedisTemplate.opsForHash().get(key, date); return (Boolean) stringRedisTemplate.opsForHash().get(key, dateStr);
} }
@Override
public void calculatePunch(AttendancePunchPageDTO dto, AttendancePunchPageVO vo) {
LocalDateTime localDateTime = dto.getLocalDateTime();
AttendanceGroupDO activationGroup = dto.getActivationGroup();
vo.setFieldworkFlag(activationGroup.getFieldworkFlag());
// - 根据经纬度判断是否在对应班组的打卡点上
vo.setPunchPoint(GeoUtil.distance(Double.parseDouble(dto.getLatitude()), Double.parseDouble(dto.getLongitude())
, Double.parseDouble(activationGroup.getLatitude()), Double.parseDouble(activationGroup.getLongitude()), activationGroup.getScope()));
// -- 获取班次
AttendanceGroupShiftDO attendanceGroupShiftDO = attendanceGroupShiftService.getGroupShift(dto.getActivationGroup().getId());
// -- 获取班次item
List<AttendanceGroupShiftItemDO> attendanceGroupShiftItemDOList = attendanceGroupShiftItemService.getGroupShiftItemListByShiftId(dto.getActivationGroup().getId());
String key = Constants.ATTENDANCE + Constants.UNDERLINE + activationGroup.getId() + Constants.UNDERLINE; // + 时间
String mapKey = dto.getUserId().toString();
String yesterdayStr = LocalDateTimeUtil.offset(localDateTime, -1, ChronoUnit.DAYS).format(Constants.REPO_DATE_FORMAT);
String toDayStr = localDateTime.format(Constants.REPO_DATE_FORMAT);
String targetDayStr = null;
if (attendanceGroupShiftDO.getNextDayFlag() == null || AttendanceGroupDO.FALSE.equals(attendanceGroupShiftDO.getNextDayFlag())) {
targetDayStr = toDayStr;
} else {
// 如果跨天的话 所以不能够直接通过判断是否跨天来取redis 数据 - 而是应该获取最晚打下班卡时间判断当前时间是否在最晚打卡下班时间之后 - 如果是的话 插入今天的数据
targetDayStr = yesterdayStr;
// -- 获取最后一个元素
AttendanceGroupShiftItemDO attendanceOnTheDayDTO = CollUtil.getLast(attendanceGroupShiftItemDOList);
// -- 获取最晚下班打卡时间
LocalDateTime endTime = LocalDateTime.ofInstant(DateUtils.buildHHmmTime(attendanceOnTheDayDTO.getEndTime()).toInstant(), ZoneId.systemDefault()).plusMinutes(attendanceOnTheDayDTO.getAfterPunchTimeDownWork());
if (localDateTime.isAfter(endTime)) {
targetDayStr = toDayStr;
}
}
List<AttendanceOnTheDayDTO> attendanceOnTheDayDTOS = this.getAttendanceOnTheDay(key, mapKey, targetDayStr, attendanceGroupShiftItemDOList);
// -- 这里没有的情况 只可能是redis 数据库崩了/或者班次子表没数据 - 暂时先返回不需要考勤
if (attendanceOnTheDayDTOS.isEmpty()) {
vo.setTodayNeedAttendance(AttendanceGroupDO.FALSE);
return;
}
// 定义是否需要更新redis - 默认不需要更新
boolean updateRedis = false;
int index = 0;
for (AttendanceOnTheDayDTO attendanceOnTheDayDTO : attendanceOnTheDayDTOS) {
if (AttendanceOnTheDayDTO.PUNCH_STATUS_UN_PUNCH.equals(attendanceOnTheDayDTO.getPunchStatus())) {
LocalDateTime time = LocalDateTime.ofInstant(DateUtils.buildHHmmTime(attendanceOnTheDayDTO.getTime()).toInstant(), ZoneId.systemDefault());
LocalDateTime lastTime = time.minusMinutes(attendanceOnTheDayDTO.getBeforePunchTime());
LocalDateTime endTime = time.plusMinutes(attendanceOnTheDayDTO.getAfterPunchTime());
// 确保beforePunchTime/AfterPunchTime是正数
if (attendanceOnTheDayDTO.getBeforePunchTime() <= Constants.ZERO || attendanceOnTheDayDTO.getAfterPunchTime() <= Constants.ZERO) {
continue; // 跳过当前循环避免错误 - 如果所有的都错了话 那么返回默认的 未到打卡时间
}
if (LocalDateTimeUtil.isIn(localDateTime, lastTime, endTime)) {
int punchType = this.determinePunchType(localDateTime, time, UP_WORK.equals(attendanceOnTheDayDTO.getType()));
vo.setPunchType(punchType);
if (dto.getFlag()) {
vo.setType(attendanceOnTheDayDTO.getType());
vo.setShouldPunchTime(time);
vo.setAttendanceGroupShiftItemId(attendanceOnTheDayDTO.getId());
vo.setIndex(index);
}
break;
} else {
// 如果是未打卡 - 判断当前时间是否在打卡区间内 - 如果不在的话直接set缺卡
// - 定时任务还没来得及刷新redis 数据 - 这里需要手动刷新
updateRedis = true;
attendanceOnTheDayDTO.setPunchStatus(AttendanceOnTheDayDTO.PUNCH_STATUS_MISS);
}
}
index++;
}
vo.setAttendanceOnTheDayDTOS(attendanceOnTheDayDTOS);
if (dto.getFlag()) {
vo.setRedisKey(key + targetDayStr);
vo.setAttendanceGroupShiftDO(attendanceGroupShiftDO);
}
// 更新redis
if (updateRedis) {
stringRedisTemplate.opsForHash().put(key + targetDayStr, mapKey, JSONUtil.toJsonStr(attendanceOnTheDayDTOS));
}
}
/**
* 确定打卡类型 - 上班打卡 忽略秒 比如 8点上班 8点0分59秒打卡不算迟到
*
* @param localDateTime
* @param time
* @param isUpWork
* @return
*/
private int determinePunchType(LocalDateTime localDateTime, LocalDateTime time, boolean isUpWork) {
if (localDateTime.isAfter(time.plusMinutes(Constants.ONE)) && isUpWork) {
return PUNCH_TYPE_BE_LATE;
} else if (time.plusMinutes(1).isAfter(localDateTime) && isUpWork) {
return PUNCH_TYPE_UP_WORK;
} else if (localDateTime.isAfter(time) && !isUpWork) {
return PUNCH_TYPE_DOWN_WORK;
} else {
return PUNCH_TYPE_LEAVE_EARLY;
}
}
private List<AttendanceOnTheDayDTO> getAttendanceOnTheDay(String key, String mapKey, String targetDayStr, List<AttendanceGroupShiftItemDO> attendanceGroupShiftItemDOList) {
List<AttendanceOnTheDayDTO> attendanceOnTheDayDTOS = new ArrayList<>();
// 尝试从Redis获取数据
try {
attendanceOnTheDayDTOS = this.loadFromRedis(key, targetDayStr, mapKey);
} catch (Exception e) {
// Redis操作异常处理逻辑
log.error("Load from Redis failed.", e);
}
// 如果Redis中没有则从数据库加载并存入Redis
if (attendanceOnTheDayDTOS.isEmpty()) {
attendanceOnTheDayDTOS = this.buildAttendanceOnTheDay(attendanceGroupShiftItemDOList);
this.saveToRedis(key, targetDayStr, mapKey, attendanceOnTheDayDTOS);
}
return attendanceOnTheDayDTOS;
}
private List<AttendanceOnTheDayDTO> loadFromRedis(String key, String targetDayStr, String mapKey) {
Object o = stringRedisTemplate.opsForHash().get(key + targetDayStr, mapKey);
return o != null ? JSONUtil.toList(o.toString(), AttendanceOnTheDayDTO.class) : new ArrayList<>();
}
/**
* 考勤数据设置两天过期
*
* @param key
* @param targetDayStr
* @param mapKey
* @param attendanceOnTheDayDTOS
*/
private void saveToRedis(String key, String targetDayStr, String mapKey, List<AttendanceOnTheDayDTO> attendanceOnTheDayDTOS) {
stringRedisTemplate.opsForHash().put(key + targetDayStr, mapKey, JSONUtil.toJsonStr(attendanceOnTheDayDTOS));
//设置缓存 2天
stringRedisTemplate.expire(key + targetDayStr, 2, TimeUnit.DAYS);
}
private List<AttendanceOnTheDayDTO> buildAttendanceOnTheDay(List<AttendanceGroupShiftItemDO> attendanceGroupShiftItemDOList) {
List<AttendanceOnTheDayDTO> attendanceOnTheDayDTOS = new ArrayList<>();
//上下班时间
for (AttendanceGroupShiftItemDO attendanceGroupShiftItemDO : attendanceGroupShiftItemDOList) {
AttendanceOnTheDayDTO dto = new AttendanceOnTheDayDTO();
dto.setId(attendanceGroupShiftItemDO.getId());
dto.setTime(attendanceGroupShiftItemDO.getBeginTime());
dto.setPunchTime(StringUtils.EMPTY);
dto.setType(UP_WORK);
dto.setLevel(attendanceGroupShiftItemDO.getLevel());
dto.setBeforePunchTime(attendanceGroupShiftItemDO.getBeforePunchTimeUpWork());
dto.setAfterPunchTime(attendanceGroupShiftItemDO.getAfterPunchTimeUpWork());
dto.setPunchStatus(AttendanceOnTheDayDTO.PUNCH_STATUS_UN_PUNCH);
dto.setFieldServiceFlag(AttendanceGroupDO.FALSE);
attendanceOnTheDayDTOS.add(dto);
dto = new AttendanceOnTheDayDTO();
dto.setId(attendanceGroupShiftItemDO.getId());
dto.setTime(attendanceGroupShiftItemDO.getEndTime());
dto.setPunchTime(StringUtils.EMPTY);
dto.setType(DOWN_WORK);
dto.setLevel(attendanceGroupShiftItemDO.getLevel());
dto.setBeforePunchTime(attendanceGroupShiftItemDO.getBeforePunchTimeDownWork());
dto.setAfterPunchTime(attendanceGroupShiftItemDO.getAfterPunchTimeDownWork());
dto.setPunchStatus(AttendanceOnTheDayDTO.PUNCH_STATUS_UN_PUNCH);
dto.setFieldServiceFlag(AttendanceGroupDO.FALSE);
attendanceOnTheDayDTOS.add(dto);
}
return attendanceOnTheDayDTOS;
}
} }

View File

@ -2,7 +2,6 @@ package cn.iocoder.yudao.module.system.service.attendance.fixed;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.date.DateUtils; import cn.iocoder.yudao.framework.common.util.date.DateUtils;
import cn.iocoder.yudao.framework.common.util.distance.GeoUtil;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.controller.admin.fixed.vo.AttendanceFixedPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.fixed.vo.AttendanceFixedPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.fixed.vo.AttendanceFixedSaveReqVO; import cn.iocoder.yudao.module.system.controller.admin.fixed.vo.AttendanceFixedSaveReqVO;
@ -12,14 +11,18 @@ import cn.iocoder.yudao.module.system.dal.dataobject.attendance.fixed.Attendance
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.AttendanceGroupDO; import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.AttendanceGroupDO;
import cn.iocoder.yudao.module.system.dal.mysql.attendance.fixed.AttendanceFixedMapper; import cn.iocoder.yudao.module.system.dal.mysql.attendance.fixed.AttendanceFixedMapper;
import cn.iocoder.yudao.module.system.service.attendance.AttendanceService; import cn.iocoder.yudao.module.system.service.attendance.AttendanceService;
import cn.iocoder.yudao.module.system.service.attendance.groupshift.AttendanceGroupShiftService;
import cn.iocoder.yudao.module.system.service.attendance.groupshiftitem.AttendanceGroupShiftItemService;
import cn.iocoder.yudao.module.system.service.attendance.punch.PunchService; import cn.iocoder.yudao.module.system.service.attendance.punch.PunchService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Lazy; import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.time.LocalDateTime;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
@ -33,20 +36,19 @@ import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.FIXED_NOT_
*/ */
@Service("fixed") @Service("fixed")
@Validated @Validated
@Slf4j
public class AttendanceFixedServiceImpl implements AttendanceFixedService, PunchService { public class AttendanceFixedServiceImpl implements AttendanceFixedService, PunchService {
@Resource @Resource
private AttendanceFixedMapper fixedMapper; private AttendanceFixedMapper attendanceFixedMapper;
@Resource @Resource
@Lazy // 避免依赖循环 @Lazy // 避免依赖循环
private AttendanceService attendanceService; private AttendanceService attendanceService;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Override @Override
public Long createFixed(AttendanceFixedSaveReqVO createReqVO) { public Long createFixed(AttendanceFixedSaveReqVO createReqVO) {
// 插入 // 插入
AttendanceFixedDO fixed = BeanUtils.toBean(createReqVO, AttendanceFixedDO.class); AttendanceFixedDO fixed = BeanUtils.toBean(createReqVO, AttendanceFixedDO.class);
fixedMapper.insert(fixed); attendanceFixedMapper.insert(fixed);
// 返回 // 返回
return fixed.getId(); return fixed.getId();
} }
@ -57,7 +59,7 @@ public class AttendanceFixedServiceImpl implements AttendanceFixedService, Punch
validateFixedExists(updateReqVO.getId()); validateFixedExists(updateReqVO.getId());
// 更新 // 更新
AttendanceFixedDO updateObj = BeanUtils.toBean(updateReqVO, AttendanceFixedDO.class); AttendanceFixedDO updateObj = BeanUtils.toBean(updateReqVO, AttendanceFixedDO.class);
fixedMapper.updateById(updateObj); attendanceFixedMapper.updateById(updateObj);
} }
@Override @Override
@ -65,28 +67,28 @@ public class AttendanceFixedServiceImpl implements AttendanceFixedService, Punch
// 校验存在 // 校验存在
validateFixedExists(id); validateFixedExists(id);
// 删除 // 删除
fixedMapper.deleteById(id); attendanceFixedMapper.deleteById(id);
} }
private void validateFixedExists(Long id) { private void validateFixedExists(Long id) {
if (fixedMapper.selectById(id) == null) { if (attendanceFixedMapper.selectById(id) == null) {
throw exception(FIXED_NOT_EXISTS); throw exception(FIXED_NOT_EXISTS);
} }
} }
@Override @Override
public AttendanceFixedDO getFixed(Long id) { public AttendanceFixedDO getFixed(Long id) {
return fixedMapper.selectById(id); return attendanceFixedMapper.selectById(id);
} }
@Override @Override
public PageResult<AttendanceFixedDO> getFixedPage(AttendanceFixedPageReqVO pageReqVO) { public PageResult<AttendanceFixedDO> getFixedPage(AttendanceFixedPageReqVO pageReqVO) {
return fixedMapper.selectPage(pageReqVO); return attendanceFixedMapper.selectPage(pageReqVO);
} }
@Override @Override
public AttendanceFixedDO getByGroupIdAndWeek(Long attendanceGroupId, Integer week) { public AttendanceFixedDO getByGroupIdAndWeek(Long attendanceGroupId, Integer week) {
List<AttendanceFixedDO> list = fixedMapper.selectList(new LambdaQueryWrapper<AttendanceFixedDO>() List<AttendanceFixedDO> list = attendanceFixedMapper.selectList(new LambdaQueryWrapper<AttendanceFixedDO>()
.eq(attendanceGroupId != null, AttendanceFixedDO::getAttendanceGroupId, attendanceGroupId) .eq(attendanceGroupId != null, AttendanceFixedDO::getAttendanceGroupId, attendanceGroupId)
.eq(week != null, AttendanceFixedDO::getWeekTime, week)); .eq(week != null, AttendanceFixedDO::getWeekTime, week));
if (!list.isEmpty()) { if (!list.isEmpty()) {
@ -98,39 +100,23 @@ public class AttendanceFixedServiceImpl implements AttendanceFixedService, Punch
@Override @Override
public AttendancePunchPageVO getPunchPage(AttendancePunchPageDTO dto) { public AttendancePunchPageVO getPunchPage(AttendancePunchPageDTO dto) {
AttendancePunchPageVO vo = new AttendancePunchPageVO(); AttendancePunchPageVO vo = new AttendancePunchPageVO();
Date thisDate = new Date();
AttendanceGroupDO activationGroup = dto.getActivationGroup(); AttendanceGroupDO activationGroup = dto.getActivationGroup();
// -- 判断是否根据节假日自动排班 - 如果是的话 - 根据排班的来 // -- 判断是否根据节假日自动排班 - 如果是的话 - 根据排班的来
Boolean isHolidayFlag = AttendanceGroupDO.TRUE.equals(activationGroup.getAutoHolidaysFlag()) ? Boolean isHolidayFlag = AttendanceGroupDO.TRUE.equals(activationGroup.getAutoHolidaysFlag()) ?
attendanceService.isHoliday(thisDate) : null; attendanceService.isHoliday(dto.getLocalDateTime()) : null;
// -- 当前是节假日 并且是放假 // -- 当前是节假日 并且是放假
if (isHolidayFlag != null && isHolidayFlag) { if (isHolidayFlag != null && isHolidayFlag) {
return vo.setTodayNeedAttendance(AttendanceGroupDO.FALSE); return vo.setTodayNeedAttendance(AttendanceGroupDO.FALSE);
} }
//获取到当天是周几 //获取到当天是周几
int week = DateUtils.dayOfWeek(thisDate); int week = dto.getLocalDateTime().getDayOfWeek().getValue();
AttendanceFixedDO attendanceFixedDO = this.getByGroupIdAndWeek(activationGroup.getId(), week); AttendanceFixedDO attendanceFixedDO = this.getByGroupIdAndWeek(activationGroup.getId(), week);
// -- 当前没有班次 - 不需要考勤 // -- 当前没有班次 - 不需要考勤
if (attendanceFixedDO == null || attendanceFixedDO.getAttendanceGroupShiftId() == null) { if (attendanceFixedDO == null || attendanceFixedDO.getAttendanceGroupShiftId() == null) {
return vo.setTodayNeedAttendance(AttendanceGroupDO.FALSE); return vo.setTodayNeedAttendance(AttendanceGroupDO.FALSE);
} }
vo.setFieldworkFlag(activationGroup.getFieldworkFlag()); attendanceService.calculatePunch(dto, vo);
// - 根据经纬度判断是否在对应班组的打卡点上 return vo;
vo.setCheckInPoint(GeoUtil.distance(Double.parseDouble(dto.getLatitude()), Double.parseDouble(dto.getLongitude())
, Double.parseDouble(activationGroup.getLatitude()), Double.parseDouble(activationGroup.getLongitude()), activationGroup.getScope()));
String key = "attendance" + "_" + activationGroup.getId();
// -- 这里的key 有个情况 - 就是上班时间和下班时间夸天了 - 所以 这里要判断下上下班时间 -
// 是否夸天 - 如果是的话 判断当前是上班卡还是下班卡 - 如果是下班卡的话 当前时间需要减去一天
String mapKey = dto.getUserId() + "_" + DateUtils.dateFormat(thisDate, DateUtils.FORMAT_YEAR_MONTH_DAY);
Object value = stringRedisTemplate.opsForHash().get(key, mapKey);
if (value != null) {
}
return null;
} }

View File

@ -49,5 +49,4 @@ public interface AttendanceGroupShiftService {
* @return 考勤组班次分页 * @return 考勤组班次分页
*/ */
PageResult<AttendanceGroupShiftDO> getGroupShiftPage(AttendanceGroupShiftPageReqVO pageReqVO); PageResult<AttendanceGroupShiftDO> getGroupShiftPage(AttendanceGroupShiftPageReqVO pageReqVO);
} }

View File

@ -6,6 +6,7 @@ import cn.iocoder.yudao.module.system.controller.admin.groupshiftitem.vo.Attenda
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshiftitem.AttendanceGroupShiftItemDO; import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshiftitem.AttendanceGroupShiftItemDO;
import javax.validation.Valid; import javax.validation.Valid;
import java.util.List;
/** /**
* 考勤组班次子表 Service 接口 * 考勤组班次子表 Service 接口
@ -52,4 +53,11 @@ public interface AttendanceGroupShiftItemService {
*/ */
PageResult<AttendanceGroupShiftItemDO> getAttendanceGroupShiftItemPage(AttendanceGroupShiftItemPageReqVO pageReqVO); PageResult<AttendanceGroupShiftItemDO> getAttendanceGroupShiftItemPage(AttendanceGroupShiftItemPageReqVO pageReqVO);
/**
* 通过班次id获取班次列表 (按级别升序)
*
* @param attendanceGroupShiftId
* @return
*/
List<AttendanceGroupShiftItemDO> getGroupShiftItemListByShiftId(Long attendanceGroupShiftId);
} }

View File

@ -6,10 +6,12 @@ import cn.iocoder.yudao.module.system.controller.admin.groupshiftitem.vo.Attenda
import cn.iocoder.yudao.module.system.controller.admin.groupshiftitem.vo.AttendanceGroupShiftItemSaveReqVO; import cn.iocoder.yudao.module.system.controller.admin.groupshiftitem.vo.AttendanceGroupShiftItemSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshiftitem.AttendanceGroupShiftItemDO; import cn.iocoder.yudao.module.system.dal.dataobject.attendance.groupshiftitem.AttendanceGroupShiftItemDO;
import cn.iocoder.yudao.module.system.dal.mysql.attendance.groupshiftitem.AttendanceGroupShiftItemMapper; import cn.iocoder.yudao.module.system.dal.mysql.attendance.groupshiftitem.AttendanceGroupShiftItemMapper;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List;
/** /**
* 考勤组班次子表 Service 实现类 * 考勤组班次子表 Service 实现类
@ -56,4 +58,11 @@ public class AttendanceGroupShiftItemServiceImpl implements AttendanceGroupShift
return attendanceGroupShiftItemMapper.selectPage(pageReqVO); return attendanceGroupShiftItemMapper.selectPage(pageReqVO);
} }
@Override
public List<AttendanceGroupShiftItemDO> getGroupShiftItemListByShiftId(Long attendanceGroupShiftId) {
return attendanceGroupShiftItemMapper.selectList(new LambdaQueryWrapper<AttendanceGroupShiftItemDO>()
.eq(AttendanceGroupShiftItemDO::getKqAttendanceGroupShiftId, attendanceGroupShiftId)
.orderByAsc(AttendanceGroupShiftItemDO::getLevel));
}
} }

View File

@ -0,0 +1,71 @@
package cn.iocoder.yudao.module.system.service.attendance.punch.dto;
import cn.hutool.core.date.LocalDateTimeUtil;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.Data;
import java.time.LocalDateTime;
import java.time.temporal.ChronoUnit;
@Data
public class AttendanceOnTheDayDTO {
/**
* 打卡状态 0正常 1迟到 2早退 3缺卡 4未打卡(还没到打卡时间)
*/
public static final Integer PUNCH_STATUS_NORMAL = 0;
public static final Integer PUNCH_STATUS_LATE = 1;
public static final Integer PUNCH_STATUS_LEAVE_EARLY = 2;
public static final Integer PUNCH_STATUS_MISS = 3;
public static final Integer PUNCH_STATUS_UN_PUNCH = 4;
@Schema(description = "子表id")
private Long id;
@Schema(description = "类型 0上班 1下班")
private Integer type;
@Schema(description = "级别 从1到 排序用")
private Integer level;
@Schema(description = "应该打卡时间 HH:mm")
private String time;
@Schema(description = "打卡时间 HH:mm")
private String punchTime;
@Schema(description = "前打卡时间(分钟) 默认 120分钟")
private Integer beforePunchTime;
@Schema(description = "后打卡时间(分钟) 默认 120分钟")
private Integer afterPunchTime;
@Schema(description = "打卡状态 0正常 1迟到 2早退 3缺卡 4未打卡(还没到打卡时间)")
private Integer punchStatus;
@Schema(description = "是否外勤 0否 1是", example = "1")
private Integer fieldServiceFlag;
@Schema(description = "用户打卡地点")
private String punchAddress;
@Schema(description = "打卡备注", example = "你说的对")
private String remark;
@Schema(description = "图片")
private String image;
@Schema(description = "应打卡时间")
private LocalDateTime shouldPunchTime;
@Schema(description = "打卡时间")
private LocalDateTime punchLocalDateTime;
@Schema(description = "迟到时长时间戳")
private Long lateTime = (punchStatus == 1 ? LocalDateTimeUtil.between(shouldPunchTime, punchLocalDateTime, ChronoUnit.MILLIS) : 0L);
@Schema(description = "早退时长时间戳")
private Long leaveEarlyTime = (punchStatus == 2 ? LocalDateTimeUtil.between(shouldPunchTime, punchLocalDateTime, ChronoUnit.MILLIS) : 0L);
}

View File

@ -1,9 +1,12 @@
package cn.iocoder.yudao.module.system.service.attendance.scheduling; package cn.iocoder.yudao.module.system.service.attendance.scheduling;
import javax.validation.*;
import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.*;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.scheduling.AttendanceSchedulingDO;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.AttendanceSchedulingPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.AttendanceSchedulingSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.scheduling.AttendanceSchedulingDO;
import javax.validation.Valid;
import java.util.Map;
/** /**
* 排班制考勤设置 Service 接口 * 排班制考勤设置 Service 接口
@ -50,4 +53,21 @@ public interface AttendanceSchedulingService {
*/ */
PageResult<AttendanceSchedulingDO> getSchedulingPage(AttendanceSchedulingPageReqVO pageReqVO); PageResult<AttendanceSchedulingDO> getSchedulingPage(AttendanceSchedulingPageReqVO pageReqVO);
/**
* 通过班次id和第几天获取排班制考勤设置
*
* @param attendanceGroupId
* @param indexDay
* @return
*/
AttendanceSchedulingDO getSchedulingByIndexDay(Long attendanceGroupId, Integer indexDay);
Integer getCycleDay(Long attendanceGroupId);
/**
* 获取每个排班考勤组 有多少数量(也就是周期多少日)
*
* @return
*/
Map<Long, Integer> countGroupId();
} }

View File

@ -1,18 +1,29 @@
package cn.iocoder.yudao.module.system.service.attendance.scheduling; package cn.iocoder.yudao.module.system.service.attendance.scheduling;
import cn.hutool.core.util.StrUtil;
import cn.iocoder.yudao.framework.common.Constants;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.AttendanceSchedulingPageReqVO; import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.AttendanceSchedulingPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.AttendanceSchedulingSaveReqVO; import cn.iocoder.yudao.module.system.controller.admin.scheduling.vo.AttendanceSchedulingSaveReqVO;
import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchPageDTO; import cn.iocoder.yudao.module.system.controller.app.attendance.dto.AttendancePunchPageDTO;
import cn.iocoder.yudao.module.system.controller.app.attendance.vo.AttendancePunchPageVO; import cn.iocoder.yudao.module.system.controller.app.attendance.vo.AttendancePunchPageVO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.AttendanceGroupDO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.scheduling.AttendanceSchedulingDO; import cn.iocoder.yudao.module.system.dal.dataobject.attendance.scheduling.AttendanceSchedulingDO;
import cn.iocoder.yudao.module.system.dal.mysql.attendance.scheduling.AttendanceSchedulingMapper; import cn.iocoder.yudao.module.system.dal.mysql.attendance.scheduling.AttendanceSchedulingMapper;
import cn.iocoder.yudao.module.system.service.attendance.AttendanceService;
import cn.iocoder.yudao.module.system.service.attendance.groupshift.AttendanceGroupShiftService;
import cn.iocoder.yudao.module.system.service.attendance.groupshiftitem.AttendanceGroupShiftItemService;
import cn.iocoder.yudao.module.system.service.attendance.punch.PunchService; import cn.iocoder.yudao.module.system.service.attendance.punch.PunchService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import org.springframework.context.annotation.Lazy;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource; import javax.annotation.Resource;
import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception; import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SCHEDULING_NOT_EXISTS; import static cn.iocoder.yudao.module.system.enums.ErrorCodeConstants.SCHEDULING_NOT_EXISTS;
@ -28,6 +39,15 @@ public class AttendanceSchedulingServiceImpl implements AttendanceSchedulingServ
@Resource @Resource
private AttendanceSchedulingMapper schedulingMapper; private AttendanceSchedulingMapper schedulingMapper;
@Resource
private StringRedisTemplate stringRedisTemplate;
@Resource
private AttendanceGroupShiftService attendanceGroupShiftService;
@Resource
private AttendanceGroupShiftItemService attendanceGroupShiftItemService;
@Resource
@Lazy
private AttendanceService attendanceService;
@Override @Override
public Long createScheduling(AttendanceSchedulingSaveReqVO createReqVO) { public Long createScheduling(AttendanceSchedulingSaveReqVO createReqVO) {
@ -72,7 +92,47 @@ public class AttendanceSchedulingServiceImpl implements AttendanceSchedulingServ
} }
@Override @Override
public AttendancePunchPageVO getPunchPage(AttendancePunchPageDTO dto) { public AttendanceSchedulingDO getSchedulingByIndexDay(Long attendanceGroupId, Integer indexDay) {
List<AttendanceSchedulingDO> list = schedulingMapper.selectList(new LambdaQueryWrapper<AttendanceSchedulingDO>()
.eq(AttendanceSchedulingDO::getAttendanceGroupShiftId, attendanceGroupId)
.eq(AttendanceSchedulingDO::getIndexDay, indexDay));
if (!list.isEmpty()) {
return list.get(0);
}
return null; return null;
} }
@Override
public Integer getCycleDay(Long attendanceGroupId) {
return Math.toIntExact(schedulingMapper.selectCount(new LambdaQueryWrapper<AttendanceSchedulingDO>()
.eq(AttendanceSchedulingDO::getAttendanceGroupShiftId, attendanceGroupId)));
}
@Override
public Map<Long, Integer> countGroupId() {
return schedulingMapper.countGroupId();
}
@Override
public AttendancePunchPageVO getPunchPage(AttendancePunchPageDTO dto) {
AttendancePunchPageVO vo = new AttendancePunchPageVO();
AttendanceGroupDO activationGroup = dto.getActivationGroup();
//获取到当天是第几天
String schedulingKey = Constants.SCHEDULING + Constants.UNDERLINE + activationGroup.getId();
String indexDayObj = stringRedisTemplate.opsForValue().get(schedulingKey);
if (StrUtil.isEmpty(indexDayObj)) {
// 当作第一天来
indexDayObj = Constants.ONE_STR;
}
Integer indexDay = Integer.valueOf(indexDayObj);
AttendanceSchedulingDO attendanceSchedulingDO = this.getSchedulingByIndexDay(activationGroup.getId(), indexDay);
// -- 当前没有班次 - 不需要考勤
if (attendanceSchedulingDO == null || Constants.ONE.equals(attendanceSchedulingDO.getRestFlag()) || attendanceSchedulingDO.getAttendanceGroupShiftId() == null) {
return vo.setTodayNeedAttendance(AttendanceGroupDO.FALSE);
}
attendanceService.calculatePunch(dto, vo);
return vo;
}
} }

View File

@ -0,0 +1,55 @@
package cn.iocoder.yudao.module.system.service.attendance.statistics;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.module.system.controller.admin.attendance.vo.AttendanceStatisticsPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.attendance.vo.AttendanceStatisticsSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.statistics.AttendanceStatisticsDO;
import javax.validation.Valid;
/**
* 考勤统计 Service 接口
*
* @author 艾楷
*/
public interface AttendanceStatisticsService {
/**
* 创建考勤统计
*
* @param createReqVO 创建信息
* @return 编号
*/
Long createAttendanceStatistics(@Valid AttendanceStatisticsSaveReqVO createReqVO);
/**
* 更新考勤统计
*
* @param updateReqVO 更新信息
*/
void updateAttendanceStatistics(@Valid AttendanceStatisticsSaveReqVO updateReqVO);
/**
* 删除考勤统计
*
* @param id 编号
*/
void deleteAttendanceStatistics(Long id);
/**
* 获得考勤统计
*
* @param id 编号
* @return 考勤统计
*/
AttendanceStatisticsDO getAttendanceStatistics(Long id);
/**
* 获得考勤统计分页
*
* @param pageReqVO 分页查询
* @return 考勤统计分页
*/
PageResult<AttendanceStatisticsDO> getAttendanceStatisticsPage(AttendanceStatisticsPageReqVO pageReqVO);
}

View File

@ -0,0 +1,59 @@
package cn.iocoder.yudao.module.system.service.attendance.statistics;
import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.system.controller.admin.attendance.vo.AttendanceStatisticsPageReqVO;
import cn.iocoder.yudao.module.system.controller.admin.attendance.vo.AttendanceStatisticsSaveReqVO;
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.statistics.AttendanceStatisticsDO;
import cn.iocoder.yudao.module.system.dal.mysql.attendance.statistics.AttendanceStatisticsMapper;
import org.springframework.stereotype.Service;
import org.springframework.validation.annotation.Validated;
import javax.annotation.Resource;
/**
* 考勤统计 Service 实现类
*
* @author 艾楷
*/
@Service
@Validated
public class AttendanceStatisticsServiceImpl implements AttendanceStatisticsService {
@Resource
private AttendanceStatisticsMapper attendanceStatisticsMapper;
@Override
public Long createAttendanceStatistics(AttendanceStatisticsSaveReqVO createReqVO) {
// 插入
AttendanceStatisticsDO attendanceStatistics = BeanUtils.toBean(createReqVO, AttendanceStatisticsDO.class);
attendanceStatisticsMapper.insert(attendanceStatistics);
// 返回
return attendanceStatistics.getId();
}
@Override
public void updateAttendanceStatistics(AttendanceStatisticsSaveReqVO updateReqVO) {
// 更新
AttendanceStatisticsDO updateObj = BeanUtils.toBean(updateReqVO, AttendanceStatisticsDO.class);
attendanceStatisticsMapper.updateById(updateObj);
}
@Override
public void deleteAttendanceStatistics(Long id) {
// 删除
attendanceStatisticsMapper.deleteById(id);
}
@Override
public AttendanceStatisticsDO getAttendanceStatistics(Long id) {
return attendanceStatisticsMapper.selectById(id);
}
@Override
public PageResult<AttendanceStatisticsDO> getAttendanceStatisticsPage(AttendanceStatisticsPageReqVO pageReqVO) {
return attendanceStatisticsMapper.selectPage(pageReqVO);
}
}

View File

@ -80,7 +80,7 @@ xxl:
job: job:
enabled: true # 是否开启调度中心,默认为 true 开启 enabled: true # 是否开启调度中心,默认为 true 开启
admin: admin:
addresses: http://127.0.0.1:9090/xxl-job-admin # 调度中心部署跟地址 addresses: http://192.168.1.105:9090/xxl-job-admin # 调度中心部署跟地址
executor: executor:
appname: ${spring.application.name} # 执行器 AppName appname: ${spring.application.name} # 执行器 AppName
ip: # 执行器IP [选填]默认为空表示自动获取IP多网卡时可手动设置指定IP该IP不会绑定Host仅作为通讯实用地址信息用于 "执行器注册" 和 "调度中心请求并触发任务" ip: # 执行器IP [选填]默认为空表示自动获取IP多网卡时可手动设置指定IP该IP不会绑定Host仅作为通讯实用地址信息用于 "执行器注册" 和 "调度中心请求并触发任务"

View File

@ -9,4 +9,11 @@
文档可见https://www.iocoder.cn/MyBatis/x-plugins/ 文档可见https://www.iocoder.cn/MyBatis/x-plugins/
--> -->
<select id="countGroupId" resultType="java.util.Map">
select
attendance_group_id as id,
count(id) as num
from kq_attendance_scheduling
group by attendance_group_id
</select>
</mapper> </mapper>

View File

@ -0,0 +1,12 @@
<?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="cn.iocoder.yudao.module.system.dal.mysql.attendance.statistics.AttendanceStatisticsMapper">
<!--
一般情况下,尽可能使用 Mapper 进行 CRUD 增删改查即可。
无法满足的场景,例如说多表关联查询,才使用 XML 编写 SQL。
代码生成器暂时只生成 Mapper XML 文件本身,更多推荐 MybatisX 快速开发插件来生成查询。
文档可见https://www.iocoder.cn/MyBatis/x-plugins/
-->
</mapper>