Merge branch 'dev' into frx
# Conflicts: # yudao-module-bpm/yudao-module-bpm-biz/src/main/java/cn/iocoder/yudao/module/bpm/service/task/BpmProcessInstanceServiceImpl.java
This commit is contained in:
commit
f057d4310e
@ -49,4 +49,8 @@ public class Constants {
|
||||
* yyyy-MM-dd格式
|
||||
*/
|
||||
public static final DateTimeFormatter REPO_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||
/**
|
||||
* yyyy-MM-dd HH:mm:ss格式
|
||||
*/
|
||||
public static final DateTimeFormatter FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
|
||||
}
|
||||
|
File diff suppressed because one or more lines are too long
@ -77,15 +77,15 @@ public class AttendanceController {
|
||||
|
||||
@GetMapping("/getTheAttendanceGroupToWhichTheCurrentlyLoggedInUserBelongsAndWhetherTheyHaveAdministratorRights")
|
||||
@Operation(summary = "获取当前登陆用户所在考勤组并且是否有管理员权限")
|
||||
public CommonResult<List<AttendanceGroupSystemVO>> getTheAttendanceGroupToWhichTheCurrentlyLoggedInUserBelongsAndWhetherTheyHaveAdministratorRights() {
|
||||
List<AttendanceGroupSystemVO> vo = attendanceService.getTheAttendanceGroupToWhichTheCurrentlyLoggedInUserBelongsAndWhetherTheyHaveAdministratorRights();
|
||||
public CommonResult<List<AttendanceGroupSystemVO>> getTheAttendanceGroupToWhichTheCurrentlyLoggedInUserBelongsAndWhetherTheyHaveAdministratorRights(@RequestParam(name = "allFlag", required = false, defaultValue = "false") Boolean allFlag) {
|
||||
List<AttendanceGroupSystemVO> vo = attendanceService.getTheAttendanceGroupToWhichTheCurrentlyLoggedInUserBelongsAndWhetherTheyHaveAdministratorRights(allFlag);
|
||||
return success(vo);
|
||||
}
|
||||
|
||||
|
||||
@GetMapping("/export-excel")
|
||||
@PostMapping("/export-excel")
|
||||
@Operation(summary = "导出考勤报表 Excel")
|
||||
public void exportAttendanceExcel(@Valid ExportAttendanceExcelDTO dto,
|
||||
public void exportAttendanceExcel(@RequestBody ExportAttendanceExcelDTO dto,
|
||||
HttpServletResponse response) {
|
||||
attendanceService.exportAttendanceExcel(response, dto);
|
||||
}
|
||||
|
@ -17,7 +17,7 @@ import static cn.iocoder.yudao.framework.common.util.date.DateUtils.TIME_ZONE_DE
|
||||
@Accessors(chain = true)
|
||||
public class ExportAttendanceExcelDTO {
|
||||
|
||||
@Schema(description = "报表类型 1月度统计 2每日统计 3打卡记录")
|
||||
@Schema(description = "报表类型 1月度统计 2每日统计")
|
||||
private Integer type;
|
||||
|
||||
@DateTimeFormat(pattern = FORMAT_YEAR_MONTH_DAY)
|
||||
|
@ -7,6 +7,8 @@ import lombok.Data;
|
||||
public class CalculateNum {
|
||||
@Schema(description = "总考勤时间")
|
||||
long totalWorkingHours = 0L;
|
||||
@Schema(description = "总考勤时间中文")
|
||||
String totalWorkingHoursStr;
|
||||
@Schema(description = "总出勤天数")
|
||||
int totalAttendanceDays = 0;
|
||||
@Schema(description = "总休息天数")
|
||||
@ -25,6 +27,10 @@ public class CalculateNum {
|
||||
String totalEarlyDeparturesTimeStr;
|
||||
@Schema(description = "缺卡总次数")
|
||||
int totalMissingCardsNumber = 0;
|
||||
@Schema(description = "上班缺卡总次数")
|
||||
int totalUpMissingCardsNumber = 0;
|
||||
@Schema(description = "下班缺卡总次数")
|
||||
int totalDownMissingCardsNumber = 0;
|
||||
@Schema(description = "矿工总天数")
|
||||
int totalMinerDays = 0;
|
||||
@Schema(description = "外勤次数")
|
||||
|
@ -10,6 +10,7 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
|
||||
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.user.dto.UserPageDTO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.*;
|
||||
import cn.iocoder.yudao.module.system.convert.user.UserConvert;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
|
||||
@ -18,6 +19,7 @@ import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import cn.iocoder.yudao.module.system.service.dept.DeptService;
|
||||
import cn.iocoder.yudao.module.system.service.dept.PostService;
|
||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.Parameter;
|
||||
import io.swagger.v3.oas.annotations.Parameters;
|
||||
@ -130,6 +132,15 @@ public class UserController {
|
||||
return success(UserConvert.INSTANCE.convertSimpleList(list, deptMap));
|
||||
}
|
||||
|
||||
|
||||
@PostMapping({"/list-all"})
|
||||
@Operation(summary = "获取用户精简信息列表", description = "只包含被开启的用户,主要用于前端的下拉选项,无数据权限")
|
||||
@DataPermission(enable = false)
|
||||
public CommonResult<IPage<UserSimpleRespVO>> getAllUserListByGroupIds(@RequestBody UserPageDTO dto) {
|
||||
IPage<UserSimpleRespVO> pageResult = userService.getAllUserListByGroupIds(dto);
|
||||
return success(pageResult);
|
||||
}
|
||||
|
||||
@GetMapping("/get")
|
||||
@Operation(summary = "获得用户详情")
|
||||
@Parameter(name = "id", description = "编号", required = true, example = "1024")
|
||||
|
@ -0,0 +1,17 @@
|
||||
package cn.iocoder.yudao.module.system.controller.admin.user.dto;
|
||||
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageParam;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Data;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Data
|
||||
public class UserPageDTO extends PageParam {
|
||||
@Schema(description = "考勤组ids")
|
||||
private List<Long> groupIds;
|
||||
@Schema(description = "用户名称")
|
||||
private String name;
|
||||
@Schema(description = "状态(0正常 1停用)不传则全部", example = "1")
|
||||
private Integer status;
|
||||
}
|
@ -16,6 +16,8 @@ public class UserSimpleRespVO {
|
||||
|
||||
@Schema(description = "用户昵称", requiredMode = Schema.RequiredMode.REQUIRED, example = "芋道")
|
||||
private String nickname;
|
||||
@Schema(description = "用户头像", example = "https://www.iocoder.cn/xxx.png")
|
||||
private String avatar;
|
||||
|
||||
@Schema(description = "部门ID", example = "我是一个用户")
|
||||
private Long deptId;
|
||||
|
@ -2,6 +2,7 @@ package cn.iocoder.yudao.module.system.dal.dataobject.attendance.punchrecord;
|
||||
|
||||
import cn.iocoder.yudao.framework.mybatis.core.dataobject.BaseDO;
|
||||
import com.baomidou.mybatisplus.annotation.KeySequence;
|
||||
import com.baomidou.mybatisplus.annotation.TableField;
|
||||
import com.baomidou.mybatisplus.annotation.TableId;
|
||||
import com.baomidou.mybatisplus.annotation.TableName;
|
||||
import lombok.*;
|
||||
@ -138,4 +139,7 @@ public class AttendancePunchRecordDO extends BaseDO {
|
||||
* 是否已提醒 0否 1是
|
||||
*/
|
||||
private Integer remindFlag;
|
||||
|
||||
@TableField(exist = false)
|
||||
private String deptName;
|
||||
}
|
@ -5,8 +5,12 @@ 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.punchrecord.vo.AttendancePunchRecordPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.attendance.punchrecord.AttendancePunchRecordDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import cn.iocoder.yudao.module.system.service.attendance.punch.dto.AttendanceOnTheDayDTO;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 用户打卡记录 Mapper
|
||||
@ -35,4 +39,12 @@ public interface AttendancePunchRecordMapper extends BaseMapperX<AttendancePunch
|
||||
.orderByDesc(AttendancePunchRecordDO::getId));
|
||||
}
|
||||
|
||||
/**
|
||||
* 统计
|
||||
*
|
||||
* @param userList
|
||||
* @param dateList
|
||||
* @return
|
||||
*/
|
||||
List<AttendancePunchRecordDO> statistics(@Param("userList") List<Long> userList, @Param("dateList") List<String> dateList);
|
||||
}
|
@ -3,10 +3,13 @@ package cn.iocoder.yudao.module.system.dal.mysql.user;
|
||||
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.user.dto.UserPageDTO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.factoryUser.FactoryUserPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserRespVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSimpleRespVO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
|
||||
@ -92,4 +95,13 @@ public interface AdminUserMapper extends BaseMapperX<AdminUserDO> {
|
||||
List<UserRespVO> selectByDeptIdsFilterGroupUser(@Param("deptIds") Collection<Long> deptIds, @Param("groupId") Long groupId);
|
||||
|
||||
List<Long> selectUserByBoss();
|
||||
|
||||
/**
|
||||
* 获取考勤组里所有的用户
|
||||
*
|
||||
* @param mpPage
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
IPage<UserSimpleRespVO> getAllUserListByGroupIds(@Param("mpPage") IPage mpPage, @Param("dto") UserPageDTO dto);
|
||||
}
|
||||
|
@ -89,7 +89,7 @@ public interface AttendanceService {
|
||||
*
|
||||
* @return
|
||||
*/
|
||||
List<AttendanceGroupSystemVO> getTheAttendanceGroupToWhichTheCurrentlyLoggedInUserBelongsAndWhetherTheyHaveAdministratorRights();
|
||||
List<AttendanceGroupSystemVO> getTheAttendanceGroupToWhichTheCurrentlyLoggedInUserBelongsAndWhetherTheyHaveAdministratorRights(Boolean allFlag);
|
||||
|
||||
/**
|
||||
* 获取考勤规则
|
||||
|
@ -21,9 +21,11 @@ import cn.iocoder.yudao.module.system.dal.dataobject.attendance.group.Attendance
|
||||
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.dal.dataobject.attendance.punchrecord.AttendancePunchRecordDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.dept.PostDO;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.attendance.group.AttendanceGroupMapper;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.attendance.punchrecord.AttendancePunchRecordMapper;
|
||||
import cn.iocoder.yudao.module.system.dal.mysql.dept.PostMapper;
|
||||
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.groupshift.AttendanceGroupShiftService;
|
||||
@ -34,6 +36,7 @@ import cn.iocoder.yudao.module.system.service.attendance.punch.dto.AttendanceOnT
|
||||
import cn.iocoder.yudao.module.system.service.attendance.punchrecord.AttendancePunchRecordService;
|
||||
import cn.iocoder.yudao.module.system.service.user.AdminUserService;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.support.ExcelTypeEnum;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -44,6 +47,9 @@ import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import javax.annotation.Resource;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.time.LocalDateTime;
|
||||
import java.time.ZoneId;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
@ -85,6 +91,8 @@ public class AttendanceServiceImpl implements AttendanceService {
|
||||
private AttendanceGroupMapper attendanceGroupMapper;
|
||||
@Resource
|
||||
private AttendanceGroupUserService attendanceGroupUserService;
|
||||
@Resource
|
||||
private PostMapper postMapper;
|
||||
|
||||
|
||||
// 定义一些常量以提高代码的可读性和可维护性
|
||||
@ -608,7 +616,6 @@ public class AttendanceServiceImpl implements AttendanceService {
|
||||
@Override
|
||||
public TeamAttendanceStatisticsByCycleVO tesmStatisticsByCycle(TeamAttendanceStatisticsByCycleDTO dto) {
|
||||
TeamAttendanceStatisticsByCycleVO vo = new TeamAttendanceStatisticsByCycleVO();
|
||||
TeamAttendanceStatisticsByCycleVO.TeamAttendanceStatisticsNumVO teamAttendanceStatisticsNumVO = new TeamAttendanceStatisticsByCycleVO.TeamAttendanceStatisticsNumVO();
|
||||
//查询考勤组
|
||||
AttendanceGroupDO attendanceGroupDO = attendanceGroupService.getGroup(dto.getGroupId());
|
||||
// - 判断当前用户是否有权限查看
|
||||
@ -750,11 +757,11 @@ public class AttendanceServiceImpl implements AttendanceService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<AttendanceGroupSystemVO> getTheAttendanceGroupToWhichTheCurrentlyLoggedInUserBelongsAndWhetherTheyHaveAdministratorRights() {
|
||||
public List<AttendanceGroupSystemVO> getTheAttendanceGroupToWhichTheCurrentlyLoggedInUserBelongsAndWhetherTheyHaveAdministratorRights(Boolean allFlag) {
|
||||
Long userId = getLoginUserId();
|
||||
List<AttendanceGroupSystemVO> attendanceGroupSystemVOS = new ArrayList<>();
|
||||
List<AttendanceGroupDO> attendanceGroupDOS = attendanceGroupMapper.selectList(new LambdaQueryWrapper<AttendanceGroupDO>()
|
||||
.eq(AttendanceGroupDO::getUserId, userId));
|
||||
.eq(!allFlag, AttendanceGroupDO::getUserId, userId));
|
||||
if (CollectionUtil.isEmpty(attendanceGroupDOS)) {
|
||||
return attendanceGroupSystemVOS;
|
||||
}
|
||||
@ -792,11 +799,189 @@ public class AttendanceServiceImpl implements AttendanceService {
|
||||
} else {
|
||||
userList = adminUserService.getAllList(CommonStatusEnum.ENABLE.getStatus(), null, dto.getTargetIds());
|
||||
}
|
||||
|
||||
// -- 统计
|
||||
List<String> dateList = DateUtils.betweenDayList(dto.getStartTime(), dto.getEndTime());
|
||||
if (dto.getType() == 1) {
|
||||
this.monthlyStatistics(response, userList, dateList);
|
||||
String hh_mm = LocalDateTime.now().format(DateTimeFormatter.ofPattern("HH:mm"));
|
||||
String first = CollectionUtil.getFirst(dateList);
|
||||
String last = CollectionUtil.getLast(dateList);
|
||||
//查询出数据 -- 时间周期 - 用户列表
|
||||
List<AttendancePunchRecordDO> list = attendancePunchRecordMapper.statistics(userList.stream().map(AdminUserDO::getId).collect(Collectors.toList()), dateList);
|
||||
//获取用户列表
|
||||
Map<Long, AdminUserDO> userMap = new HashMap<>();
|
||||
if (CollectionUtil.isNotEmpty(userList)) {
|
||||
userMap = userList.stream().collect(Collectors.toMap(AdminUserDO::getId, Function.identity()));
|
||||
}
|
||||
List<PostDO> userPostList = postMapper.selectList();
|
||||
// 根据id分组
|
||||
Map<Long, PostDO> postMap = userPostList.stream().collect(Collectors.toMap(PostDO::getId, Function.identity()));
|
||||
|
||||
if (dto.getType() == 1) {
|
||||
String headTitle = String.format("月度汇总 统计日期:%s 至 %s", first, last);
|
||||
String detailedHead = String.format("月度汇总 统计日期:%s 至 %s %s", first, last, hh_mm);
|
||||
this.monthlyStatistics(response, userList, dateList, headTitle, detailedHead, list);
|
||||
} else {
|
||||
String headTitle = String.format("每日统计 统计日期:%s 至 %s", first, last);
|
||||
String detailedHead = String.format("报表生成时间:%s %s", last, hh_mm);
|
||||
this.dayStatistics(response, userMap, postMap, dateList, headTitle, detailedHead, list);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 按日导出
|
||||
*
|
||||
* @param response
|
||||
* @param userMap
|
||||
* @param postMap
|
||||
* @param dateList
|
||||
* @param headTitle
|
||||
* @param detailedHead
|
||||
* @param list
|
||||
*/
|
||||
private void dayStatistics(HttpServletResponse response, Map<Long, AdminUserDO> userMap, Map<Long, PostDO> postMap, List<String> dateList, String headTitle, String detailedHead, List<AttendancePunchRecordDO> list) {
|
||||
List<List<String>> data = new ArrayList<>();
|
||||
// -- 根据部门分组 - 根据考勤组分组
|
||||
Map<Long, List<AttendancePunchRecordDO>> userPunchMap = list.stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getUserId));
|
||||
|
||||
// -- 先计算下要循环几次 - 一个用户一个部门一个考勤组一个班次一个日期 最大的打卡次数 -
|
||||
Map<String, List<AttendancePunchRecordDO>> map = list.stream().collect(Collectors.groupingBy(
|
||||
a -> a.getUserId() + "_"
|
||||
+ a.getDeptId() + "_"
|
||||
+ a.getAttendanceGroupId() + "_"
|
||||
+ a.getAttendanceGroupShiftId() + "_"
|
||||
+ a.getDayTime()));
|
||||
|
||||
int maxSize = map.values().stream().mapToInt(List::size).max().orElse(0);
|
||||
// -- 如果膜2大于0
|
||||
maxSize = (maxSize % 2) > 0 ? maxSize / 2 + 1 : maxSize / 2;
|
||||
|
||||
//先根据人员分组 - 再根据部门分组 - 再根据考勤组分组 - 再根据日期分组
|
||||
long time = System.currentTimeMillis();
|
||||
for (Map.Entry<Long, List<AttendancePunchRecordDO>> entry : userPunchMap.entrySet()) {
|
||||
AdminUserDO adminUserDO = userMap.get(entry.getKey());
|
||||
List<String> postNames = this.getPostNames(adminUserDO, postMap);
|
||||
|
||||
|
||||
Map<Long, List<AttendancePunchRecordDO>> deptMap = entry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getDeptId));
|
||||
for (Map.Entry<Long, List<AttendancePunchRecordDO>> deptEntry : deptMap.entrySet()) {
|
||||
Map<Long, List<AttendancePunchRecordDO>> groupMap = deptEntry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getAttendanceGroupId));
|
||||
|
||||
for (Map.Entry<Long, List<AttendancePunchRecordDO>> groupEntry : groupMap.entrySet()) {
|
||||
//按日期分组
|
||||
Map<String, List<AttendancePunchRecordDO>> dayMap = groupEntry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getDayTime, TreeMap::new, Collectors.toList()));
|
||||
|
||||
for (String dateStr : dateList) {
|
||||
List<AttendancePunchRecordDO> items = dayMap.get(dateStr);
|
||||
Map<Long, List<AttendancePunchRecordDO>> groupShiftMap = new HashMap<>();
|
||||
if (CollectionUtil.isNotEmpty(items)) {
|
||||
groupShiftMap = items.stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getAttendanceGroupShiftId));
|
||||
} else {
|
||||
groupShiftMap.put(-1L, Collections.emptyList());
|
||||
}
|
||||
for (Map.Entry<Long, List<AttendancePunchRecordDO>> groupShiftEntry : groupShiftMap.entrySet()) {
|
||||
List<String> row = new ArrayList<>();
|
||||
row.add(adminUserDO.getNickname());
|
||||
row.add(groupEntry.getValue().get(0).getAttendanceGroupName());
|
||||
row.add(groupEntry.getValue().get(0).getDeptName());
|
||||
row.add(String.join("/", postNames));
|
||||
row.add(dateStr);
|
||||
row.add(CollectionUtil.isEmpty(groupShiftEntry.getValue()) ? "休息" : groupShiftEntry.getValue().get(0).getAttendanceGroupShiftName());
|
||||
Map<Long, List<AttendancePunchRecordDO>> workMap = groupShiftEntry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getAttendanceGroupShiftItemId, TreeMap::new, Collectors.toList()));
|
||||
// TODO: 2024/7/2 这里可能会有排序问题 具体看数据在调试
|
||||
for (Map.Entry<Long, List<AttendancePunchRecordDO>> groupShiftItemEntry : workMap.entrySet()) {
|
||||
for (AttendancePunchRecordDO attendancePunchRecordDO : groupShiftItemEntry.getValue()) {
|
||||
row.add(attendancePunchRecordDO.getPunchTime() == null ? "/" : attendancePunchRecordDO.getPunchTime().format(Constants.FORMAT_YEAR_MONTH_DAY_HOUR_MINUTE_SECOND));
|
||||
row.add(this.statusToStr(attendancePunchRecordDO.getStatus()));
|
||||
}
|
||||
}
|
||||
if (maxSize > workMap.entrySet().size()) {
|
||||
for (int i = 0; i < maxSize - workMap.entrySet().size(); i++) {
|
||||
row.add("/");
|
||||
row.add("/");
|
||||
row.add("/");
|
||||
row.add("/");
|
||||
}
|
||||
}
|
||||
|
||||
//工时
|
||||
String workHour = this.calculateAverageWorkingHour(workMap);
|
||||
row.add(workHour);
|
||||
//迟到时长
|
||||
String beLate = this.calculateBeLate(workMap);
|
||||
row.add(beLate);
|
||||
//早退时长
|
||||
String leaveEarly = this.calculateEarlyDepartures(workMap);
|
||||
row.add(leaveEarly);
|
||||
Map<Integer, Integer> missingCardsMap = this.calculateCommuteMissingCardsList(workMap);
|
||||
row.add(missingCardsMap.get(Constants.ZERO).toString());
|
||||
row.add(missingCardsMap.get(Constants.ONE).toString());
|
||||
data.add(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
log.info("考勤按日统计耗时:{}", (System.currentTimeMillis() - time));
|
||||
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
|
||||
|
||||
try {
|
||||
EasyExcel.write(response.getOutputStream())
|
||||
.head(generateDailyHead(headTitle, detailedHead, maxSize))
|
||||
.autoCloseStream(false)
|
||||
.excelType(ExcelTypeEnum.XLS)
|
||||
.registerWriteHandler(new CustomCellStyleHandler())
|
||||
.sheet("考勤统计按日导出")
|
||||
.doWrite(data);
|
||||
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("考勤统计", StandardCharsets.UTF_8.name()));
|
||||
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取岗位名称
|
||||
*
|
||||
* @param adminUserDO
|
||||
* @param postMap
|
||||
* @return
|
||||
*/
|
||||
private List<String> getPostNames(AdminUserDO adminUserDO, Map<Long, PostDO> postMap) {
|
||||
if (adminUserDO.getPostIds() != null) {
|
||||
return adminUserDO.getPostIds().stream()
|
||||
.map(postMap::get)
|
||||
.filter(Objects::nonNull)
|
||||
.map(PostDO::getName)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
return Collections.emptyList();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 状态转换
|
||||
*
|
||||
* @param status
|
||||
* @return
|
||||
*/
|
||||
private String statusToStr(int status) {
|
||||
if (status == 0) {
|
||||
return "正常";
|
||||
} else if (status == 1) {
|
||||
return "迟到";
|
||||
} else if (status == 2) {
|
||||
return "早退";
|
||||
} else if (status == 3) {
|
||||
return "缺卡";
|
||||
} else if (status == 4) {
|
||||
return "未打卡";
|
||||
} else if (status == 5) {
|
||||
return "补卡";
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
/**
|
||||
@ -805,37 +990,104 @@ public class AttendanceServiceImpl implements AttendanceService {
|
||||
* @param response
|
||||
* @param userList
|
||||
* @param dateList
|
||||
* @param headTitle
|
||||
* @param detailedHead
|
||||
* @param list
|
||||
*/
|
||||
private void monthlyStatistics(HttpServletResponse response, List<AdminUserDO> userList, List<String> dateList) {
|
||||
//查询出数据 -- 时间周期 - 用户列表
|
||||
List<AttendancePunchRecordDO> list = attendancePunchRecordMapper.selectList(new LambdaQueryWrapper<AttendancePunchRecordDO>()
|
||||
.in(AttendancePunchRecordDO::getUserId, userList)
|
||||
.in(AttendancePunchRecordDO::getDayTime, dateList));
|
||||
private void monthlyStatistics(HttpServletResponse response, List<AdminUserDO> userList, List<String> dateList, String headTitle, String detailedHead, List<AttendancePunchRecordDO> list) {
|
||||
List<List<String>> data = new ArrayList<>();
|
||||
|
||||
// -- 根据部门分组 - 根据考勤组分组
|
||||
Map<Long, List<AttendancePunchRecordDO>> userPunchMap = list.stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getUserId));
|
||||
|
||||
//获取用户列表
|
||||
Map<Long, AdminUserDO> userMap = new HashMap<>();
|
||||
if (CollectionUtil.isNotEmpty(userList)) {
|
||||
userMap = userList.stream().collect(Collectors.toMap(AdminUserDO::getId, Function.identity()));
|
||||
}
|
||||
List<PostDO> userPostList = postMapper.selectList();
|
||||
// 根据id分组
|
||||
Map<Long, PostDO> postMap = userPostList.stream().collect(Collectors.toMap(PostDO::getId, Function.identity()));
|
||||
|
||||
//先根据人员分组 - 再根据部门分组 - 再根据考勤组分组 - 再根据日期分组
|
||||
for (Map.Entry<Long, List<AttendancePunchRecordDO>> entry : userPunchMap.entrySet()) {
|
||||
AdminUserDO adminUserDO = userMap.get(entry.getKey());
|
||||
List<String> postNames = this.getPostNames(adminUserDO, postMap);
|
||||
Map<Long, List<AttendancePunchRecordDO>> deptMap = entry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getDeptId));
|
||||
for (Map.Entry<Long, List<AttendancePunchRecordDO>> deptEntry : deptMap.entrySet()) {
|
||||
Map<Long, List<AttendancePunchRecordDO>> groupMap = deptEntry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getAttendanceGroupId));
|
||||
|
||||
for (Map.Entry<Long, List<AttendancePunchRecordDO>> groupEntry : groupMap.entrySet()) {
|
||||
CalculateNum calculateNum = new CalculateNum();
|
||||
//按日期分组
|
||||
Map<String, List<AttendancePunchRecordDO>> dayMap = groupEntry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getDayTime));
|
||||
for (Map.Entry<String, List<AttendancePunchRecordDO>> dayEntry : dayMap.entrySet()) {
|
||||
// -- 按照班次子表分组
|
||||
Map<Long, List<AttendancePunchRecordDO>> workMap = dayEntry.getValue().stream().collect(Collectors.groupingBy(AttendancePunchRecordDO::getAttendanceGroupShiftItemId));
|
||||
this.calculateAverageWorkingHour(workMap, calculateNum);
|
||||
// -- 出考勤天数
|
||||
this.calculateAttendanceDays(dayEntry.getValue(), calculateNum);
|
||||
// -- 迟到
|
||||
this.calculateBeLate(workMap, calculateNum);
|
||||
// -- 早退
|
||||
this.calculateEarlyDepartures(workMap, calculateNum);
|
||||
// -- 缺卡
|
||||
this.calculateCommuteMissingCardsList(workMap, calculateNum);
|
||||
// -- 旷工
|
||||
this.calculateMiner(dayEntry.getValue(), calculateNum);
|
||||
}
|
||||
//休息天数
|
||||
this.calculateRestDay(dateList, dayMap.keySet().stream().distinct().collect(Collectors.toList()), calculateNum);
|
||||
//考勤总时间中文
|
||||
calculateNum.setTotalWorkingHoursStr(DateUtil.formatBetween(calculateNum.getTotalWorkingHours(), BetweenFormatter.Level.MINUTE));
|
||||
//迟到总时间中文
|
||||
calculateNum.setTotalLateArrivalsTimeStr(DateUtil.formatBetween(calculateNum.getTotalLateArrivalsTime(), BetweenFormatter.Level.MINUTE));
|
||||
//早退总时间中文
|
||||
calculateNum.setTotalEarlyDeparturesTimeStr(DateUtil.formatBetween(calculateNum.getTotalEarlyDeparturesTime(), BetweenFormatter.Level.MINUTE));
|
||||
|
||||
List<String> row = new ArrayList<>();
|
||||
row.add(adminUserDO.getNickname());
|
||||
row.add(groupEntry.getValue().get(0).getAttendanceGroupName());
|
||||
row.add(groupEntry.getValue().get(0).getDeptName());
|
||||
row.add(String.join("/", postNames));
|
||||
row.add(String.valueOf(calculateNum.getTotalAttendanceDays()));
|
||||
row.add(String.valueOf(calculateNum.getTotalRestDays()));
|
||||
row.add(String.valueOf(calculateNum.getTotalWorkingHoursStr()));
|
||||
row.add(String.valueOf(calculateNum.getTotalLateArrivalsNumber()));
|
||||
row.add(String.valueOf(calculateNum.getTotalLateArrivalsTimeStr()));
|
||||
|
||||
row.add(String.valueOf(calculateNum.getTotalEarlyDeparturesNumber()));
|
||||
row.add(String.valueOf(calculateNum.getTotalEarlyDeparturesTimeStr()));
|
||||
row.add(String.valueOf(calculateNum.getTotalUpMissingCardsNumber()));
|
||||
row.add(String.valueOf(calculateNum.getTotalDownMissingCardsNumber()));
|
||||
row.add(String.valueOf(calculateNum.getTotalMinerDays()));
|
||||
data.add(row);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
EasyExcel.write(response.getOutputStream())
|
||||
.head(generateHead(headTitle, detailedHead))
|
||||
.autoCloseStream(false)
|
||||
.excelType(ExcelTypeEnum.XLS)
|
||||
.registerWriteHandler(new CustomCellStyleHandler())
|
||||
.sheet("考勤统计按月导出")
|
||||
.doWrite(data);
|
||||
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("考勤统计", StandardCharsets.UTF_8.name()));
|
||||
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
|
||||
|
||||
} catch (IOException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String fileName = "/Users/aikai/Downloads/" + System.currentTimeMillis() + ".xlsx";
|
||||
// 这里 需要指定写用哪个class去写,然后写到第一个sheet,名字为模板 然后文件流会自动关闭
|
||||
EasyExcel.write(fileName)
|
||||
.head(generateHead())
|
||||
.registerWriteHandler(new CustomCellStyleHandler())
|
||||
// .registerWriteHandler(new LoopMergeStrategy())
|
||||
// .registerWriteHandler(loopMergeStrategy)
|
||||
.sheet("模板")
|
||||
.doWrite(generateData());
|
||||
}
|
||||
|
||||
public static List<List<String>> generateHead() {
|
||||
public static List<List<String>> generateHead(String headTitle, String detailedHead) {
|
||||
List<List<String>> head = new ArrayList<>();
|
||||
String headTitle = "月度汇总 统计日期:2024-06-01 至 2024-06-13";
|
||||
String detailedHead = "月度汇总 统计日期:2024-06-01 至 2024-06-13 10:48";
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "姓名", "姓名"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤组", "考勤组"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "部门", "部门"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "工号", "工号"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "职位", "职位"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "出勤天数", "出勤天数"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "休息天数", "休息天数"));
|
||||
@ -853,70 +1105,48 @@ public class AttendanceServiceImpl implements AttendanceService {
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "加班时长-按加班规则计算", "工作日加班"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "加班时长-按加班规则计算", "休息日加班"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "加班时长-按加班规则计算", "节假日加班"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "六"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "日"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "1"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "2"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "3"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "4"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "5"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "六"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "日"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "六"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "日"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "1"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "2"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "3"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "4"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "5"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "六"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "考勤结果", "日"));
|
||||
// 添加更多表头
|
||||
return head;
|
||||
}
|
||||
|
||||
public static List<List<Object>> generateData() {
|
||||
List<List<Object>> data = new ArrayList<>();
|
||||
|
||||
List<Object> row1 = new ArrayList<>();
|
||||
row1.add("艾楷");
|
||||
row1.add("测试考勤");
|
||||
row1.add("");
|
||||
row1.add("");
|
||||
row1.add("");
|
||||
row1.add("22026829221062585");
|
||||
row1.add(1);
|
||||
row1.add(2);
|
||||
row1.add(3);
|
||||
row1.add(4);
|
||||
row1.add(5);
|
||||
row1.add(6);
|
||||
row1.add(7);
|
||||
row1.add(8);
|
||||
row1.add(9);
|
||||
row1.add(10);
|
||||
row1.add(10);
|
||||
row1.add(12);
|
||||
// 添加更多数据
|
||||
|
||||
List<Object> row2 = new ArrayList<>();
|
||||
row2.add("谢鸿飞");
|
||||
row2.add("测试考勤");
|
||||
row2.add("");
|
||||
row2.add("");
|
||||
row2.add("");
|
||||
row2.add("066557433135769889");
|
||||
row2.add(1);
|
||||
row2.add(2);
|
||||
row2.add(3);
|
||||
row2.add(4);
|
||||
row2.add(4);
|
||||
row2.add(4);
|
||||
row2.add(4);
|
||||
row2.add(4);
|
||||
row2.add(4);
|
||||
row2.add(4);
|
||||
row2.add(4);
|
||||
row2.add(4);
|
||||
// 添加更多数据
|
||||
|
||||
data.add(row1);
|
||||
data.add(row2);
|
||||
|
||||
return data;
|
||||
public static List<List<String>> generateDailyHead(String headTitle, String detailedHead, int size) {
|
||||
List<List<String>> head = new ArrayList<>();
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "姓名", "姓名"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "考勤组", "考勤组"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "部门", "部门"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "职位", "职位"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "日期", "日期"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "班次", "班次"));
|
||||
for (int i = 1; i <= size; i++) {
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "上班" + i + "打卡时间", "上班" + i + "打卡时间"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "上班" + i + "打卡结果", "上班" + i + "打卡结果"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "下班" + i + "打卡时间", "下班" + i + "打卡时间"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "下班" + i + "打卡结果", "下班" + i + "打卡结果"));
|
||||
}
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "关联的审批单", "关联的审批单"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "出勤天数", "出勤天数"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "休息天数", "休息天数"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "工作时长", "工作时长"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "迟到次数", "迟到次数"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "迟到时长", "迟到时长"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "早退次数", "早退次数"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "早退时长", "早退时长"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "上班缺卡次数", "上班缺卡次数"));
|
||||
head.add(Arrays.asList(headTitle, detailedHead, "下班缺卡次数", "下班缺卡次数"));
|
||||
// head.add(Arrays.asList(headTitle, detailedHead, "旷工天数", "旷工天数"));
|
||||
return head;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 外勤计算
|
||||
*
|
||||
@ -1012,6 +1242,18 @@ public class AttendanceServiceImpl implements AttendanceService {
|
||||
return list;
|
||||
}
|
||||
|
||||
/**
|
||||
* 休息时间计算
|
||||
*
|
||||
* @param dateList
|
||||
* @param attendanceTime
|
||||
* @param calculateNum
|
||||
* @return
|
||||
*/
|
||||
private void calculateRestDay(List<String> dateList, List<String> attendanceTime, CalculateNum calculateNum) {
|
||||
List<String> subtract = new ArrayList<>(CollectionUtil.subtract(dateList, attendanceTime));
|
||||
calculateNum.setTotalRestDays(subtract.size());
|
||||
}
|
||||
|
||||
/**
|
||||
* @param day
|
||||
@ -1057,6 +1299,51 @@ public class AttendanceServiceImpl implements AttendanceService {
|
||||
calculateNum.setTotalMissingCardsNumber(calculateNum.getTotalMissingCardsNumber() + todayMissingCardsNumber);
|
||||
}
|
||||
|
||||
private void calculateCommuteMissingCardsList(Map<Long, List<AttendancePunchRecordDO>> workMap, CalculateNum calculateNum) {
|
||||
int todayUpMissingCardsNumber = 0;
|
||||
int todayDownMissingCardsNumber = 0;
|
||||
for (Map.Entry<Long, List<AttendancePunchRecordDO>> workEntry : workMap.entrySet()) {
|
||||
if (CollectionUtil.isNotEmpty(workEntry.getValue())) {
|
||||
// -- 缺卡计算
|
||||
List<AttendancePunchRecordDO> missingCardsList = workEntry.getValue().stream().filter(a -> AttendanceOnTheDayDTO.PUNCH_STATUS_MISS.equals(a.getStatus())).collect(Collectors.toList());
|
||||
if (CollectionUtil.isNotEmpty(missingCardsList)) {
|
||||
long upNum = missingCardsList.stream().filter(a -> Constants.ZERO.equals(a.getWorkType())).count();
|
||||
long downNum = missingCardsList.stream().filter(a -> Constants.ONE.equals(a.getWorkType())).count();
|
||||
todayUpMissingCardsNumber += upNum;
|
||||
todayDownMissingCardsNumber += downNum;
|
||||
}
|
||||
}
|
||||
}
|
||||
calculateNum.setTotalUpMissingCardsNumber(calculateNum.getTotalUpMissingCardsNumber() + todayUpMissingCardsNumber);
|
||||
calculateNum.setTotalDownMissingCardsNumber(calculateNum.getTotalDownMissingCardsNumber() + todayDownMissingCardsNumber);
|
||||
}
|
||||
|
||||
/**
|
||||
* 迟到次数
|
||||
*
|
||||
* @param workMap
|
||||
*/
|
||||
private Map<Integer, Integer> calculateCommuteMissingCardsList(Map<Long, List<AttendancePunchRecordDO>> workMap) {
|
||||
int todayUpMissingCardsNumber = 0;
|
||||
int todayDownMissingCardsNumber = 0;
|
||||
for (Map.Entry<Long, List<AttendancePunchRecordDO>> workEntry : workMap.entrySet()) {
|
||||
if (CollectionUtil.isNotEmpty(workEntry.getValue())) {
|
||||
// -- 缺卡计算
|
||||
List<AttendancePunchRecordDO> missingCardsList = workEntry.getValue().stream().filter(a -> AttendanceOnTheDayDTO.PUNCH_STATUS_MISS.equals(a.getStatus())).collect(Collectors.toList());
|
||||
if (CollectionUtil.isNotEmpty(missingCardsList)) {
|
||||
long upNum = missingCardsList.stream().filter(a -> Constants.ZERO.equals(a.getWorkType())).count();
|
||||
long downNum = missingCardsList.stream().filter(a -> Constants.ONE.equals(a.getWorkType())).count();
|
||||
todayUpMissingCardsNumber += upNum;
|
||||
todayDownMissingCardsNumber += downNum;
|
||||
}
|
||||
}
|
||||
}
|
||||
Map<Integer, Integer> map = new HashMap<>();
|
||||
map.put(Constants.ZERO, todayUpMissingCardsNumber);
|
||||
map.put(Constants.ONE, todayDownMissingCardsNumber);
|
||||
return map;
|
||||
}
|
||||
|
||||
/**
|
||||
* 早退计算
|
||||
*
|
||||
@ -1111,6 +1398,20 @@ public class AttendanceServiceImpl implements AttendanceService {
|
||||
calculateNum.setTotalEarlyDeparturesTime(calculateNum.getTotalEarlyDeparturesTime() + todayEarlyDeparturesTime);
|
||||
}
|
||||
|
||||
private String calculateEarlyDepartures(Map<Long, List<AttendancePunchRecordDO>> workMap) {
|
||||
long todayEarlyDeparturesTime = 0L;
|
||||
for (Map.Entry<Long, List<AttendancePunchRecordDO>> workEntry : workMap.entrySet()) {
|
||||
if (CollectionUtil.isNotEmpty(workEntry.getValue())) {
|
||||
// -- 早退计算
|
||||
List<AttendancePunchRecordDO> earlyDeparturesList = workEntry.getValue().stream().filter(a -> AttendanceOnTheDayDTO.PUNCH_STATUS_LEAVE_EARLY.equals(a.getStatus())).collect(Collectors.toList());
|
||||
if (CollectionUtil.isNotEmpty(earlyDeparturesList)) {
|
||||
todayEarlyDeparturesTime += earlyDeparturesList.stream().mapToLong(AttendancePunchRecordDO::getLeaveEarlyTime).sum();
|
||||
}
|
||||
}
|
||||
}
|
||||
return DateUtil.formatBetween(todayEarlyDeparturesTime, BetweenFormatter.Level.MINUTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算迟到
|
||||
*
|
||||
@ -1166,6 +1467,26 @@ public class AttendanceServiceImpl implements AttendanceService {
|
||||
calculateNum.setTotalLateArrivalsTime(calculateNum.getTotalLateArrivalsTime() + todayLateArrivalsTime);
|
||||
}
|
||||
|
||||
/**
|
||||
* 迟到时长
|
||||
*
|
||||
* @param workMap
|
||||
* @return
|
||||
*/
|
||||
private String calculateBeLate(Map<Long, List<AttendancePunchRecordDO>> workMap) {
|
||||
long todayLateArrivalsTime = 0L;
|
||||
for (Map.Entry<Long, List<AttendancePunchRecordDO>> workEntry : workMap.entrySet()) {
|
||||
if (CollectionUtil.isNotEmpty(workEntry.getValue())) {
|
||||
// -- 迟到计算
|
||||
List<AttendancePunchRecordDO> beLateList = workEntry.getValue().stream().filter(a -> AttendanceOnTheDayDTO.PUNCH_STATUS_LATE.equals(a.getStatus())).collect(Collectors.toList());
|
||||
if (CollectionUtil.isNotEmpty(beLateList)) {
|
||||
todayLateArrivalsTime += beLateList.stream().mapToLong(AttendancePunchRecordDO::getLateTime).sum();
|
||||
}
|
||||
}
|
||||
}
|
||||
return DateUtil.formatBetween(todayLateArrivalsTime, BetweenFormatter.Level.MINUTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算考勤工时
|
||||
*
|
||||
@ -1184,6 +1505,17 @@ public class AttendanceServiceImpl implements AttendanceService {
|
||||
return vo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算工时
|
||||
*
|
||||
* @param workMap
|
||||
* @return
|
||||
*/
|
||||
private String calculateAverageWorkingHour(Map<Long, List<AttendancePunchRecordDO>> workMap) {
|
||||
long todayWorkingHours = this.calculateAverageWorkingHourDay(workMap);
|
||||
return DateUtil.formatBetween(todayWorkingHours, BetweenFormatter.Level.MINUTE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算考勤工时
|
||||
*
|
||||
@ -1209,6 +1541,30 @@ public class AttendanceServiceImpl implements AttendanceService {
|
||||
return todayWorkingHours;
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算工时 - 当天
|
||||
*
|
||||
* @param workMap
|
||||
* @return
|
||||
*/
|
||||
private long calculateAverageWorkingHourDay(Map<Long, List<AttendancePunchRecordDO>> workMap) {
|
||||
long todayWorkingHours = 0L;
|
||||
for (Map.Entry<Long, List<AttendancePunchRecordDO>> workEntry : workMap.entrySet()) {
|
||||
if (CollectionUtil.isNotEmpty(workEntry.getValue())) {
|
||||
// -- 工时计算 - 有上班和下班两个卡 才计算到工时里面
|
||||
if (workEntry.getValue().size() >= 2) {
|
||||
AttendancePunchRecordDO first = CollectionUtil.getFirst(workEntry.getValue());
|
||||
AttendancePunchRecordDO last = CollectionUtil.getLast(workEntry.getValue());
|
||||
if (ObjectUtil.isNotEmpty(first.getPunchTime()) && ObjectUtil.isNotEmpty(last.getPunchTime())) {
|
||||
long time = Math.abs(LocalDateTimeUtil.between(first.getPunchTime(), last.getPunchTime(), ChronoUnit.MILLIS));
|
||||
todayWorkingHours += time;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return todayWorkingHours;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 计算出勤天数
|
||||
|
@ -25,7 +25,7 @@ public class CustomCellStyleHandler implements CellWriteHandler {
|
||||
|
||||
Workbook workbook = writeSheetHolder.getSheet().getWorkbook();
|
||||
CellStyle cellStyle = workbook.createCellStyle();
|
||||
writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), 5120);
|
||||
writeSheetHolder.getSheet().setColumnWidth(cell.getColumnIndex(), 7120);
|
||||
// 设置水平和垂直居中对齐
|
||||
cellStyle.setAlignment(HorizontalAlignment.CENTER);
|
||||
cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
|
||||
@ -44,12 +44,14 @@ public class CustomCellStyleHandler implements CellWriteHandler {
|
||||
// 头的策略
|
||||
WriteCellStyle headWriteCellStyle = new WriteCellStyle();
|
||||
// 背景设置为红色
|
||||
headWriteCellStyle.setFillForegroundColor(IndexedColors.YELLOW.getIndex());
|
||||
headWriteCellStyle.setFillForegroundColor(IndexedColors.LIGHT_YELLOW.getIndex());
|
||||
WriteFont headWriteFont = new WriteFont();
|
||||
headWriteFont.setFontHeightInPoints((short) 20);
|
||||
headWriteCellStyle.setWriteFont(headWriteFont);
|
||||
WriteCellStyle.merge(headWriteCellStyle, cellDataList.get(0).getOrCreateStyle());
|
||||
}
|
||||
} else {
|
||||
|
||||
}
|
||||
cell.setCellStyle(cellStyle);
|
||||
}
|
||||
|
@ -3,11 +3,13 @@ package cn.iocoder.yudao.module.system.service.user;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.collection.CollectionUtils;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.dto.UserPageDTO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.factoryUser.FactoryUserPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.*;
|
||||
import cn.iocoder.yudao.module.system.dal.dataobject.user.AdminUserDO;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
|
||||
import javax.validation.Valid;
|
||||
import java.io.InputStream;
|
||||
@ -296,4 +298,12 @@ public interface AdminUserService {
|
||||
* @return
|
||||
*/
|
||||
List<AdminUserDO> getAllList(Integer status, Integer type, List<Long> userIds);
|
||||
|
||||
/**
|
||||
* 获取所有人员列表根据考勤组
|
||||
*
|
||||
* @param dto
|
||||
* @return
|
||||
*/
|
||||
IPage<UserSimpleRespVO> getAllUserListByGroupIds(UserPageDTO dto);
|
||||
}
|
||||
|
@ -12,7 +12,9 @@ import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
||||
import cn.iocoder.yudao.framework.common.util.string.DTO.IdCardDO;
|
||||
import cn.iocoder.yudao.framework.common.util.string.StrUtils;
|
||||
import cn.iocoder.yudao.framework.datapermission.core.util.DataPermissionUtils;
|
||||
import cn.iocoder.yudao.framework.mybatis.core.util.MyBatisUtils;
|
||||
import cn.iocoder.yudao.module.infra.api.file.FileApi;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.dto.UserPageDTO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.factoryUser.FactoryUserPageReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdatePasswordReqVO;
|
||||
import cn.iocoder.yudao.module.system.controller.admin.user.vo.profile.UserProfileUpdateReqVO;
|
||||
@ -27,6 +29,7 @@ import cn.iocoder.yudao.module.system.service.dept.PostService;
|
||||
import cn.iocoder.yudao.module.system.service.permission.PermissionService;
|
||||
import cn.iocoder.yudao.module.system.service.tenant.TenantService;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.xingyuv.http.util.StringUtil;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@ -597,4 +600,9 @@ public class AdminUserServiceImpl implements AdminUserService {
|
||||
.in(CollectionUtil.isNotEmpty(userIds), AdminUserDO::getId, userIds));
|
||||
}
|
||||
|
||||
@Override
|
||||
public IPage<UserSimpleRespVO> getAllUserListByGroupIds(UserPageDTO dto) {
|
||||
return userMapper.getAllUserListByGroupIds(MyBatisUtils.buildPage(dto), dto);
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -4,6 +4,7 @@ import cn.hutool.core.bean.BeanUtil;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.collection.CollectionUtil;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
||||
import cn.iocoder.yudao.framework.common.util.date.DateUtils;
|
||||
@ -279,15 +280,19 @@ public class LogStatisticsServiceImpl implements LogStatisticsService {
|
||||
LogRuleDO logRuleDO = logRuleService.getLogRuleByFormId(dto.getFormId());
|
||||
// 判断开始时间是否当天 / 是否本周
|
||||
boolean flag = false;
|
||||
Date begin = DateUtil.parse(dto.getBeginTime(), "yyyy-MM-dd").toJdkDate();
|
||||
List<LogStatisticsDetailsVO> logStatisticsDOS = new ArrayList<>();
|
||||
Date thisDate = new Date();
|
||||
if (logRuleDO.getType() == 1) {
|
||||
flag = DateUtil.isSameDay(begin, thisDate);
|
||||
} else if (logRuleDO.getType() == 2) {
|
||||
flag = DateUtil.isSameWeek(begin, thisDate, true);
|
||||
List<String> dateList = new ArrayList<>();
|
||||
if (ObjectUtil.isNotEmpty(dto.getBeginTime())) {
|
||||
Date begin = DateUtil.parse(dto.getBeginTime(), "yyyy-MM-dd").toJdkDate();
|
||||
Date thisDate = new Date();
|
||||
if (logRuleDO.getType() == 1) {
|
||||
flag = DateUtil.isSameDay(begin, thisDate);
|
||||
} else if (logRuleDO.getType() == 2) {
|
||||
flag = DateUtil.isSameWeek(begin, thisDate, true);
|
||||
}
|
||||
dateList = DateUtils.betweenDayStrList(dto.getBeginTime(), dto.getEndTime());
|
||||
}
|
||||
List<String> dateList = DateUtils.betweenDayStrList(dto.getBeginTime(), dto.getEndTime());
|
||||
|
||||
// 这里将区间转换为具体的日期 因为mysql BETWEEN 会导致索引失效
|
||||
if (flag) {
|
||||
logStatisticsDOS = logStatisticsMapper.getCurrentStatistics(dto.getFormId(), dateList);
|
||||
|
@ -9,4 +9,27 @@
|
||||
文档可见:https://www.iocoder.cn/MyBatis/x-plugins/
|
||||
-->
|
||||
|
||||
<select id="statistics"
|
||||
resultType="cn.iocoder.yudao.module.system.dal.dataobject.attendance.punchrecord.AttendancePunchRecordDO">
|
||||
select
|
||||
a.*,
|
||||
b.name as deptName
|
||||
from kq_attendance_punch_record a
|
||||
left join system_dept as b on a.dept_id = b.id
|
||||
<where>
|
||||
a.deleted = 0
|
||||
<if test="userList != null and userList.size() > 0">
|
||||
and a.user_id in
|
||||
<foreach collection="userList" item="userId" separator="," open="(" close=")">
|
||||
#{userId}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="dateList != null and dateList.size() > 0">
|
||||
and a.day_time in
|
||||
<foreach collection="dateList" item="dayTime" separator="," open="(" close=")">
|
||||
#{dayTime}
|
||||
</foreach>
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
</mapper>
|
@ -54,4 +54,31 @@
|
||||
</foreach>
|
||||
and not exists(select id from kq_attendance_group_user where user_id = a.id and attendance_group_id != #{groupId} and deleted = 0)
|
||||
</select>
|
||||
|
||||
<select id="getAllUserListByGroupIds"
|
||||
resultType="cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserSimpleRespVO">
|
||||
select a.id,
|
||||
a.nickname,
|
||||
a.avatar,
|
||||
a.dept_id,
|
||||
c.name deptName
|
||||
from system_users as a
|
||||
left join kq_attendance_group_user b on a.id = b.user_id
|
||||
left join system_dept as c on a.dept_id = c.id
|
||||
<where>
|
||||
a.deleted = 0 and b.deleted = 0
|
||||
<if test="dto.groupIds != null and dto.groupIds.size() > 0">
|
||||
and b.attendance_group_id in
|
||||
<foreach collection="dto.groupIds" item="groupId" open="(" close=")" separator=",">
|
||||
#{groupId}
|
||||
</foreach>
|
||||
</if>
|
||||
<if test="dto.name != null and dto.name != ''">
|
||||
and a.nickname like concat('%', #{dto.name}, '%')
|
||||
</if>
|
||||
<if test="dto.status != null">
|
||||
and a.status = #{dto.status}
|
||||
</if>
|
||||
</where>
|
||||
</select>
|
||||
</mapper>
|
Loading…
Reference in New Issue
Block a user