Merge remote-tracking branch 'origin/dev' into dev

This commit is contained in:
2025-09-15 20:14:41 +08:00
19 changed files with 1006 additions and 81 deletions

View File

@ -11,12 +11,18 @@ import org.dromara.common.log.enums.BusinessType;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.web.core.BaseController; import org.dromara.common.web.core.BaseController;
import org.dromara.contractor.domain.SubUserSalaryDetail;
import org.dromara.contractor.domain.dto.constructionuser.SubConstructionUserQueryReq;
import org.dromara.contractor.domain.dto.usersalarydetail.SubUserSalaryDetailQueryReq; import org.dromara.contractor.domain.dto.usersalarydetail.SubUserSalaryDetailQueryReq;
import org.dromara.contractor.domain.dto.usersalaryperiod.SubConstructionUserSalaryDto;
import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserVo;
import org.dromara.contractor.domain.vo.usersalarydetail.SubUserSalaryDetailVo; import org.dromara.contractor.domain.vo.usersalarydetail.SubUserSalaryDetailVo;
import org.dromara.contractor.domain.vo.usersalaryperiod.SubConstructionUserSalaryVo;
import org.dromara.contractor.service.ISubUserSalaryDetailService; import org.dromara.contractor.service.ISubUserSalaryDetailService;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.io.IOException;
import java.util.List; import java.util.List;
/** /**
@ -65,4 +71,19 @@ public class SubUserSalaryDetailController extends BaseController {
return R.ok(subUserSalaryDetailService.queryById(id)); return R.ok(subUserSalaryDetailService.queryById(id));
} }
/**
* 工资计算与导出
*/
@GetMapping("/salaryPageList")
public TableDataInfo<SubConstructionUserSalaryVo> salaryPageList(@Validated SubConstructionUserSalaryDto dto, PageQuery pageQuery) {
return subUserSalaryDetailService.salaryPageList(dto, pageQuery);
}
@GetMapping("/export")
public void export(HttpServletResponse response, SubConstructionUserSalaryDto dto) throws IOException {
subUserSalaryDetailService.export(response, dto);
}
} }

View File

@ -0,0 +1,31 @@
package org.dromara.contractor.domain.dto.usersalaryperiod;
import com.alibaba.excel.annotation.ExcelProperty;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;
@Data
public class SubConstructionUserSalaryDto {
/**
* 人员姓名
*/
private String userName;
/**
* 项目id
*/
@NotBlank(message = "项目id不能为空")
private Long projectId;
/**
* 班组id
*/
private Long teamId;
/**
* 时间
*/
@NotBlank(message = "时间不能为空")
private String time;
}

View File

@ -0,0 +1,83 @@
package org.dromara.contractor.domain.vo.usersalaryperiod;
import com.alibaba.excel.annotation.ExcelIgnoreUnannotated;
import com.alibaba.excel.annotation.ExcelProperty;
import io.github.linpeilie.annotations.AutoMapper;
import lombok.Data;
import org.dromara.common.excel.annotation.ExcelDictFormat;
import org.dromara.common.excel.convert.ExcelDictConvert;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.constant.TransConstant;
import org.dromara.contractor.domain.SubConstructionUser;
import java.io.Serial;
import java.io.Serializable;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.util.Date;
/**
* 施工人员视图对象 bus_construction_user
*
* @author lilemy
* @date 2025-03-07
*/
@Data
@ExcelIgnoreUnannotated
@AutoMapper(target = SubConstructionUser.class)
public class SubConstructionUserSalaryVo implements Serializable {
@Serial
private static final long serialVersionUID = 1L;
/**
* 主键id
*/
private Long id;
/**
* 人员姓名
*/
private String userName;
/**
* 项目id
*/
private Long projectId;
/**
* 身份证号码
*/
private String sfzNumber;
/**
* 银行卡号
*/
private String yhkNumber;
/**
* 开户行
*/
private String yhkOpeningBank;
private BigDecimal totalSalary;
/**
* 用户Id
*/
private Long sysUserId;
private Double workDay;
private BigDecimal salary;
private String time;
private Integer order;
private String blank;
}

View File

@ -3,12 +3,17 @@ package org.dromara.contractor.service;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.IService; import com.baomidou.mybatisplus.extension.service.IService;
import jakarta.servlet.http.HttpServletResponse;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.contractor.domain.SubUserSalaryDetail; import org.dromara.contractor.domain.SubUserSalaryDetail;
import org.dromara.contractor.domain.dto.usersalarydetail.SubUserSalaryDetailQueryReq; import org.dromara.contractor.domain.dto.usersalarydetail.SubUserSalaryDetailQueryReq;
import org.dromara.contractor.domain.dto.usersalaryperiod.SubConstructionUserSalaryDto;
import org.dromara.contractor.domain.vo.usersalarydetail.SubUserSalaryDetailVo; import org.dromara.contractor.domain.vo.usersalarydetail.SubUserSalaryDetailVo;
import org.dromara.contractor.domain.vo.usersalaryperiod.SubConstructionUserSalaryVo;
import org.springframework.validation.annotation.Validated;
import java.io.IOException;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.util.List;
@ -52,7 +57,7 @@ public interface ISubUserSalaryDetailService extends IService<SubUserSalaryDetai
* @param reportDate 日报日期 * @param reportDate 日报日期
* @return 是否新增成功 * @return 是否新增成功
*/ */
Boolean insertByAttendance(Long userId, LocalDate reportDate); void insertByAttendance(Long userId, LocalDate reportDate);
/** /**
* 获取员工每日工资对视图 * 获取员工每日工资对视图
@ -78,4 +83,18 @@ public interface ISubUserSalaryDetailService extends IService<SubUserSalaryDetai
*/ */
Page<SubUserSalaryDetailVo> getVoPage(Page<SubUserSalaryDetail> userSalaryDetailPage); Page<SubUserSalaryDetailVo> getVoPage(Page<SubUserSalaryDetail> userSalaryDetailPage);
/**
* 工资计算与导出
*/
TableDataInfo<SubConstructionUserSalaryVo> salaryPageList(@Validated SubConstructionUserSalaryDto dto, PageQuery pageQuery);
/**
* 导出
*/
void export(HttpServletResponse response, SubConstructionUserSalaryDto dto) throws IOException;
} }

View File

@ -918,12 +918,11 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
Long projectId = req.getProjectId(); Long projectId = req.getProjectId();
Long teamId = req.getTeamId(); Long teamId = req.getTeamId();
String typeOfWork = req.getTypeOfWork(); String typeOfWork = req.getTypeOfWork();
String clockDate = req.getClockDate(); String clockMonth = req.getClockDate();
// 联表查询 // 联表查询
LambdaQueryWrapper<BusAttendance> attendanceLqw = Wrappers.lambdaQuery(BusAttendance.class) LambdaQueryWrapper<BusAttendance> attendanceLqw = Wrappers.lambdaQuery(BusAttendance.class)
.eq(BusAttendance::getProjectId, projectId); .eq(BusAttendance::getProjectId, projectId);
if (ObjectUtils.isNotEmpty(clockDate)) { if (ObjectUtils.isNotEmpty(clockMonth)) {
String clockMonth = clockDate.substring(0, 7);
// 校验月份格式 // 校验月份格式
if (!DateConstant.YEAR_MONTH_PATTERN.matcher(clockMonth).matches()) { if (!DateConstant.YEAR_MONTH_PATTERN.matcher(clockMonth).matches()) {
throw new ServiceException("月份格式不正确", HttpStatus.BAD_REQUEST); throw new ServiceException("月份格式不正确", HttpStatus.BAD_REQUEST);
@ -935,13 +934,13 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
throw new ServiceException("不能查看大于当前月份的记录", HttpStatus.BAD_REQUEST); throw new ServiceException("不能查看大于当前月份的记录", HttpStatus.BAD_REQUEST);
} }
// 计算当月第一天 / 最后一天 // 计算当月第一天 / 最后一天
Date start = DateUtils.toDate(yearMonth.atDay(1)); LocalDate start = yearMonth.atDay(1);
Date end = DateUtils.toDate(yearMonth.atEndOfMonth()); LocalDate end = yearMonth.atEndOfMonth();
attendanceLqw.between(BusAttendance::getClockDate, start, end); attendanceLqw.between(BusAttendance::getClockDate, start, end);
List<Long> userIdList = attendanceService.list(attendanceLqw) List<Long> userIdList = attendanceService.list(attendanceLqw)
.stream().map(BusAttendance::getUserId).toList(); .stream().map(BusAttendance::getUserId).toList();
if (CollUtil.isNotEmpty(userIdList)) { if (CollUtil.isNotEmpty(userIdList)) {
lqw.in(SubConstructionUser::getId, userIdList); lqw.in(SubConstructionUser::getSysUserId, userIdList);
} }
} }
// 模糊查询 // 模糊查询
@ -967,7 +966,7 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
return constructionUserAttendanceTotalPage; return constructionUserAttendanceTotalPage;
} }
// 获取施工人员id列表 // 获取施工人员id列表
List<Long> userIdList = constructionUserList.stream().map(SubConstructionUser::getId).toList(); List<Long> userIdList = constructionUserList.stream().map(SubConstructionUser::getSysUserId).toList();
// 关联查询施工人员考勤列表 // 关联查询施工人员考勤列表
attendanceLqw.in(BusAttendance::getUserId, userIdList); attendanceLqw.in(BusAttendance::getUserId, userIdList);
Map<Long, List<BusAttendance>> userIdBusAttendanceListMap = attendanceService.list(attendanceLqw) Map<Long, List<BusAttendance>> userIdBusAttendanceListMap = attendanceService.list(attendanceLqw)
@ -975,7 +974,7 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
// 填充信息 // 填充信息
List<SubConstructionUserAttendanceTotalVo> userAttendanceTotalList = constructionUserList.stream().map(constructionUser -> { List<SubConstructionUserAttendanceTotalVo> userAttendanceTotalList = constructionUserList.stream().map(constructionUser -> {
SubConstructionUserAttendanceTotalVo constructionUserAttendanceTotalResp = new SubConstructionUserAttendanceTotalVo(); SubConstructionUserAttendanceTotalVo constructionUserAttendanceTotalResp = new SubConstructionUserAttendanceTotalVo();
Long id = constructionUser.getId(); Long id = constructionUser.getSysUserId();
BeanUtils.copyProperties(constructionUser, constructionUserAttendanceTotalResp); BeanUtils.copyProperties(constructionUser, constructionUserAttendanceTotalResp);
// 关联施工人员考勤信息 // 关联施工人员考勤信息
int attendanceDays = 0; int attendanceDays = 0;
@ -1036,6 +1035,9 @@ public class SubConstructionUserServiceImpl extends ServiceImpl<SubConstructionU
return constructionUserAttendanceTotalPage; return constructionUserAttendanceTotalPage;
} }
/** /**
* 获取施工人员身份证信息 * 获取施工人员身份证信息
* *

View File

@ -1,34 +1,70 @@
package org.dromara.contractor.service.impl; package org.dromara.contractor.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource; import jakarta.annotation.Resource;
import jakarta.servlet.ServletException;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.apache.pdfbox.io.IOUtils;
import org.apache.poi.ss.usermodel.Sheet;
import org.apache.poi.ss.usermodel.Workbook;
import org.apache.poi.ss.usermodel.WorkbookFactory;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.ObjectUtils; import org.dromara.common.core.utils.ObjectUtils;
import org.dromara.common.core.utils.StringUtils; import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.utils.IdCardEncryptorUtil;
import org.dromara.contractor.domain.SubConstructionUser; import org.dromara.contractor.domain.SubConstructionUser;
import org.dromara.contractor.domain.SubUserSalaryDetail; import org.dromara.contractor.domain.SubUserSalaryDetail;
import org.dromara.contractor.domain.SubUserSalaryPeriod; import org.dromara.contractor.domain.SubUserSalaryPeriod;
import org.dromara.contractor.domain.dto.constructionuser.SubConstructionUserQueryReq;
import org.dromara.contractor.domain.dto.usersalarydetail.SubUserSalaryDetailQueryReq; import org.dromara.contractor.domain.dto.usersalarydetail.SubUserSalaryDetailQueryReq;
import org.dromara.contractor.domain.dto.usersalaryperiod.SubConstructionUserSalaryDto;
import org.dromara.contractor.domain.exportvo.BusConstructionUserExportVo;
import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserVo;
import org.dromara.contractor.domain.vo.usersalarydetail.SubUserSalaryDetailVo; import org.dromara.contractor.domain.vo.usersalarydetail.SubUserSalaryDetailVo;
import org.dromara.contractor.domain.vo.usersalaryperiod.SubConstructionUserSalaryVo;
import org.dromara.contractor.mapper.SubUserSalaryDetailMapper; import org.dromara.contractor.mapper.SubUserSalaryDetailMapper;
import org.dromara.contractor.service.ISubConstructionUserService; import org.dromara.contractor.service.ISubConstructionUserService;
import org.dromara.contractor.service.ISubUserSalaryDetailService; import org.dromara.contractor.service.ISubUserSalaryDetailService;
import org.dromara.project.domain.BusAttendance;
import org.dromara.project.domain.BusProject;
import org.dromara.project.domain.BusProjectTeam;
import org.dromara.project.domain.BusWorkWage; import org.dromara.project.domain.BusWorkWage;
import org.dromara.project.service.IBusAttendanceService;
import org.dromara.project.service.IBusProjectService;
import org.dromara.project.service.IBusProjectTeamService;
import org.dromara.project.service.IBusWorkWageService; import org.dromara.project.service.IBusWorkWageService;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.context.annotation.Lazy;
import org.springframework.scheduling.annotation.Async; import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import software.amazon.awssdk.utils.CollectionUtils;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.math.BigDecimal; import java.math.BigDecimal;
import java.math.RoundingMode;
import java.net.URLEncoder;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.List; import java.time.YearMonth;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.stream.Collectors;
/** /**
* 员工每日工资Service业务层处理 * 员工每日工资Service业务层处理
@ -47,6 +83,21 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
@Resource @Resource
private ISubConstructionUserService constructionUserService; private ISubConstructionUserService constructionUserService;
@Resource
private IdCardEncryptorUtil idCardEncryptorUtil;
@Resource
@Lazy
private IBusAttendanceService attendanceService;
@Resource
@Lazy
private IBusProjectService projectService;
@Resource
@Lazy
private IBusProjectTeamService projectTeamService;
/** /**
* 查询员工每日工资 * 查询员工每日工资
* *
@ -97,13 +148,14 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
*/ */
@Async @Async
@Override @Override
public Boolean insertByAttendance(Long userId, LocalDate reportDate) { public void insertByAttendance(Long userId, LocalDate reportDate) {
//查询当天工资,已存在则跳过 //打一次卡 半天工资 如果已存在直接X2
List<SubUserSalaryDetail> list = this.list(new LambdaQueryWrapper<SubUserSalaryDetail>() SubUserSalaryDetail one = this.getOne(new LambdaQueryWrapper<SubUserSalaryDetail>()
.eq(SubUserSalaryDetail::getUserId, userId) .eq(SubUserSalaryDetail::getUserId, userId)
.eq(SubUserSalaryDetail::getReportDate, reportDate)); .eq(SubUserSalaryDetail::getReportDate, reportDate));
if (CollUtil.isNotEmpty(list)) { if (ObjectUtils.isNotEmpty(one)) {
return true; one.setDailySalary(one.getDailySalary().multiply(new BigDecimal("2")));
this.updateById(one);
} }
SubConstructionUser constructionUser = constructionUserService.getBySysUserId(userId); SubConstructionUser constructionUser = constructionUserService.getBySysUserId(userId);
@ -127,16 +179,17 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
salary = BigDecimal.ZERO; salary = BigDecimal.ZERO;
} }
} }
// 保存当日工资
// 保存当日一半工资
SubUserSalaryDetail save = new SubUserSalaryDetail(); SubUserSalaryDetail save = new SubUserSalaryDetail();
save.setProjectId(constructionUser.getProjectId()); save.setProjectId(constructionUser.getProjectId());
save.setTeamId(constructionUser.getTeamId()); save.setTeamId(constructionUser.getTeamId());
save.setUserId(userId); save.setUserId(userId);
save.setUserName(constructionUser.getUserName()); save.setUserName(constructionUser.getUserName());
save.setReportDate(reportDate); save.setReportDate(reportDate);
save.setDailySalary(salary); save.setDailySalary(salary.divide(new BigDecimal(2),2, RoundingMode.HALF_UP));
save.setRemark(salary.compareTo(BigDecimal.ZERO) == 0 ? "当前人员未设置工资" : null); save.setRemark(salary.compareTo(BigDecimal.ZERO) == 0 ? "当前人员未设置工资" : null);
return this.save(save); this.save(save);
} }
/** /**
@ -207,5 +260,276 @@ public class SubUserSalaryDetailServiceImpl extends ServiceImpl<SubUserSalaryDet
return userSalaryDetailVoPage; return userSalaryDetailVoPage;
} }
@Override
public TableDataInfo<SubConstructionUserSalaryVo> salaryPageList(SubConstructionUserSalaryDto dto, PageQuery pageQuery) {
SubConstructionUserQueryReq req = new SubConstructionUserQueryReq();
req.setProjectId(dto.getProjectId());
req.setTeamId(dto.getTeamId());
req.setUserName(dto.getUserName());
TableDataInfo<SubConstructionUserVo> subConstructionUserVoTableDataInfo = constructionUserService.queryPageList(req, pageQuery);
List<SubConstructionUserVo> rows = subConstructionUserVoTableDataInfo.getRows();
if(CollectionUtil.isEmpty(rows)){
return TableDataInfo.build();
}
String time = dto.getTime();
YearMonth parse = YearMonth.parse(time, DateTimeFormatter.ofPattern("yyyy-MM"));
LocalDate start = parse.atDay(1);
LocalDate end = parse.atEndOfMonth();
List<Long> userIds = rows.stream().map(SubConstructionUserVo::getSysUserId).toList();
//考勤数据
List<BusAttendance> attendanceList = attendanceService.lambdaQuery()
.eq(BusAttendance::getProjectId, dto.getProjectId())
.in(BusAttendance::getUserId, userIds)
.between(BusAttendance::getClockDate, start, end)
.list();
//工资数据
List<SubUserSalaryDetail> salaryDetailList = this.lambdaQuery()
.eq(SubUserSalaryDetail::getProjectId, dto.getProjectId())
.in(SubUserSalaryDetail::getUserId, userIds)
.between(SubUserSalaryDetail::getReportDate, start, end)
.list();
ArrayList<SubConstructionUserSalaryVo> vos = new ArrayList<>();
for (SubConstructionUserVo row : rows) {
SubConstructionUserSalaryVo vo = new SubConstructionUserSalaryVo();
BeanUtil.copyProperties(row,vo);
vo.setSfzNumber(idCardEncryptorUtil.decrypt(vo.getSfzNumber()));
vo.setTime(dto.getTime());
// 获取工资
BigDecimal salary = row.getSalary();
if (salary == null || salary.compareTo(BigDecimal.ZERO) == 0) {
String typeOfWork = row.getTypeOfWork();
String wageMeasureUnit = row.getWageMeasureUnit();
if (StringUtils.isNotEmpty(typeOfWork) && StringUtils.isNotEmpty(wageMeasureUnit)) {
BusWorkWage workWage = workWageService.lambdaQuery()
.eq(BusWorkWage::getProjectId, row.getProjectId())
.eq(BusWorkWage::getWorkType, typeOfWork)
.eq(BusWorkWage::getWageMeasureUnit, wageMeasureUnit)
.one();
if (workWage != null) {
salary = workWage.getWage();
} else {
salary = BigDecimal.ZERO;
}
} else {
salary = BigDecimal.ZERO;
}
}
vo.setSalary(salary);
vo.setTotalSalary(salaryDetailList.stream().filter(detail -> detail.getUserId().equals(row.getSysUserId()))
.map(SubUserSalaryDetail::getDailySalary)
.reduce(BigDecimal.ZERO, BigDecimal::add)
);
List<BusAttendance> list = attendanceList.stream().filter(attendance -> attendance.getUserId().equals(row.getSysUserId())
&& Arrays.asList("1", "2", "3", "5").contains(attendance.getClockStatus())
).toList();
vo.setWorkDay(list.size()*0.5);
vos.add(vo);
}
return new TableDataInfo<>(vos,subConstructionUserVoTableDataInfo.getTotal());
}
@Override
public void export(HttpServletResponse response, SubConstructionUserSalaryDto dto) throws IOException {
// 1. 查询项目和人员数据
BusProject project = projectService.getById(dto.getProjectId());
if (project == null) {
throw new ServiceException("项目不存在");
}
List<SubConstructionUser> userList = constructionUserService.lambdaQuery()
.eq(SubConstructionUser::getProjectId, dto.getProjectId())
.eq(dto.getTeamId() != null, SubConstructionUser::getTeamId, dto.getTeamId())
.like(StringUtils.isNotBlank(dto.getUserName()), SubConstructionUser::getUserName, dto.getUserName())
.list();
if (userList.isEmpty()) {
throw new ServiceException("暂无数据");
}
// 2. 解析年月和查询薪资明细
YearMonth yearMonth = YearMonth.parse(dto.getTime(), DateTimeFormatter.ofPattern("yyyy-MM"));
LocalDate start = yearMonth.atDay(1);
LocalDate end = yearMonth.atEndOfMonth();
List<Long> userIds = userList.stream().map(SubConstructionUser::getSysUserId).toList();
List<SubUserSalaryDetail> salaryDetails = this.lambdaQuery()
.eq(SubUserSalaryDetail::getProjectId, dto.getProjectId())
.in(SubUserSalaryDetail::getUserId, userIds)
.between(SubUserSalaryDetail::getReportDate, start, end)
.list();
// 3. 设置响应头(下载文件配置)
setResponseHeader(response, yearMonth.getYear(), yearMonth.getMonthValue());
// 5. 读取模板文件到字节数组(复用模板)
byte[] templateBytes;
try (InputStream templateStream = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("excelTemplate/salary_template.xlsx")) {
if (templateStream == null) {
throw new ServiceException("模板文件不存在路径excelTemplate/salary_template.xlsx");
}
templateBytes = IOUtils.toByteArray(templateStream);
}
// 6. POI 处理多 sheet核心克隆模板 + 命名)
// 存储 <sheet名称, sheet对象> 映射,方便后续 EasyExcel 匹配
Map<String, Sheet> sheetMap = new LinkedHashMap<>();
try (Workbook workbook = WorkbookFactory.create(new ByteArrayInputStream(templateBytes));
ByteArrayOutputStream bos = new ByteArrayOutputStream()) {
// 校验模板是否有有效 sheet
if (workbook.getNumberOfSheets() == 0) {
throw new ServiceException("模板文件中无任何 sheet 页,请检查模板");
}
// 6.1 预处理班组信息获取班组ID + 班组名称映射)
List<Long> teamIds = userList.stream()
.map(SubConstructionUser::getTeamId)
.filter(Objects::nonNull)
.distinct()
.collect(Collectors.toList());
Map<Long, String> teamNameMap = new HashMap<>();
if (!teamIds.isEmpty()) {
List<BusProjectTeam> teams = projectTeamService.lambdaQuery()
.in(BusProjectTeam::getId, teamIds)
.list();
teamNameMap = teams.stream()
.collect(Collectors.toMap(
BusProjectTeam::getId,
BusProjectTeam::getTeamName,
(oldVal, newVal) -> newVal // 避免重复ID冲突
));
}
// 6.2 克隆模板:为每个班组生成 sheet关键修正
for (Long teamId : teamIds) {
// ① 克隆原始模板 sheet返回克隆后的 Sheet 对象)
Sheet newSheet = workbook.cloneSheet(0);
// ② 生成班组名称(避免空名称)
String teamName = teamNameMap.getOrDefault(teamId, "未知班组_" + teamId);
// ③ 给新 sheet 命名(通过 sheet 对象获取索引)
int newSheetIndex = workbook.getSheetIndex(newSheet);
workbook.setSheetName(newSheetIndex, teamName);
// ④ 存入映射,方便后续填充
sheetMap.put(teamName, newSheet);
}
// 6.3 克隆模板:处理“无班组”数据(如果需要)
boolean needNoTeamSheet = dto.getTeamId() == null;
if (needNoTeamSheet) {
Sheet noTeamSheet = workbook.cloneSheet(0);
String noTeamName = "无班组";
int noTeamSheetIndex = workbook.getSheetIndex(noTeamSheet);
workbook.setSheetName(noTeamSheetIndex, noTeamName);
sheetMap.put(noTeamName, noTeamSheet);
}
// 6.4 删除原始模板 sheet避免最终文件保留空模板
workbook.removeSheetAt(0);
// 6.5 将 POI 处理后的 workbook 写入字节数组,供 EasyExcel 使用
workbook.write(bos);
byte[] workbookBytes = bos.toByteArray();
// 7. EasyExcel 填充数据(按 sheet 映射顺序填充)
try (InputStream workbookStream = new ByteArrayInputStream(workbookBytes);
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
.withTemplate(workbookStream)
.build()) {
// 7.1 填充“班组”sheet 数据
for (Map.Entry<String, Sheet> entry : sheetMap.entrySet()) {
String sheetName = entry.getKey();
// 跳过“无班组”sheet后续单独处理
if ("无班组".equals(sheetName)) {
continue;
}
// 根据 sheet 名称反查班组ID用于过滤数据
Long teamId = teamNameMap.entrySet().stream()
.filter(e -> sheetName.equals(e.getValue()))
.map(Map.Entry::getKey)
.findFirst()
.orElse(null);
// 创建 EasyExcel 的 WriteSheet通过名称匹配 sheet
WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).build();
FillConfig fillConfig = FillConfig.builder()
.forceNewRow(true) // 强制新增行,不覆盖模板内容
.build();
// 填充列表数据和汇总数据
excelWriter.fill(getTeamData(userList, salaryDetails, teamId), fillConfig, writeSheet);
excelWriter.fill(getTeamSummary(project, teamNameMap.get(teamId), yearMonth), writeSheet);
}
// 7.2 填充“无班组”sheet 数据
if (needNoTeamSheet) {
WriteSheet noTeamSheet = EasyExcel.writerSheet("无班组").build();
FillConfig fillConfig = FillConfig.builder().forceNewRow(true).build();
excelWriter.fill(getTeamData(userList, salaryDetails, null), fillConfig, noTeamSheet);
excelWriter.fill(getTeamSummary(project, null, yearMonth), noTeamSheet);
}
}
}
}
/**
* 设置响应头信息
*/
private void setResponseHeader(HttpServletResponse response, int year, int month) throws IOException {
// 文件名编码,解决中文乱码
String fileName = URLEncoder.encode(year + "" + month + "月多部门考勤表.xlsx", "UTF-8");
// 设置内容类型
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
// 设置下载文件名
response.setHeader("Content-Disposition", "attachment;filename*=UTF-8''" + fileName);
// 禁止缓存
response.setHeader("Pragma", "no-cache");
response.setHeader("Cache-Control", "no-cache");
response.setDateHeader("Expires", 0);
}
private List<SubConstructionUserSalaryVo> getTeamData(List<SubConstructionUser> rows,
List<SubUserSalaryDetail> salaryDetailList,Long teamId){
List<SubConstructionUser> list1 ;
if(teamId == null){
list1 = rows.stream().filter(row -> row.getTeamId() == null && Arrays.asList("1","2").contains(row.getExitStatus())).toList();
}else {
list1 = rows.stream().filter(row -> teamId.equals(row.getTeamId())).toList();
}
ArrayList<SubConstructionUserSalaryVo> vos = new ArrayList<>();
int i =1;
for (SubConstructionUser row : list1) {
SubConstructionUserSalaryVo vo = new SubConstructionUserSalaryVo();
BeanUtil.copyProperties(row,vo);
vo.setOrder(i);
vo.setTotalSalary(salaryDetailList.stream().filter(detail -> detail.getUserId().equals(row.getSysUserId()))
.map(SubUserSalaryDetail::getDailySalary)
.reduce(BigDecimal.ZERO, BigDecimal::add)
);
vos.add(vo);
i++;
}
return vos;
}
private Map<String, Object> getTeamSummary(BusProject project,String teamName,YearMonth parse) {
// 模拟汇总数据
return Map.of(
"time", parse.format(DateTimeFormatter.ofPattern("yyyy年MM月")),
"projectName", project.getProjectName(),
"teamName", teamName == null? "无班组":teamName
);
}
} }

View File

@ -7,7 +7,8 @@ public enum FormalitiesStatusEnum {
TOSTART("待开始", "0"), TOSTART("待开始", "0"),
PROCESSING("处理中", "1"), PROCESSING("处理中", "1"),
DELETE("已完成", "2"); DELETE("已完成", "2"),
NOHANDLE("不需要办理", "3");
private final String text; private final String text;

View File

@ -252,7 +252,8 @@ public class BusFormalitiesAreConsolidatedServiceImpl extends ServiceImpl<BusFor
//TODO 做一些数据校验,如唯一约束 //TODO 做一些数据校验,如唯一约束
if (!FormalitiesStatusEnum.TOSTART.getText().equals(entity.getProcessingStatus()) if (!FormalitiesStatusEnum.TOSTART.getText().equals(entity.getProcessingStatus())
&& !FormalitiesStatusEnum.PROCESSING.getText().equals(entity.getProcessingStatus()) && !FormalitiesStatusEnum.PROCESSING.getText().equals(entity.getProcessingStatus())
&& !FormalitiesStatusEnum.DELETE.getText().equals(entity.getProcessingStatus())) { && !FormalitiesStatusEnum.DELETE.getText().equals(entity.getProcessingStatus())
&& !FormalitiesStatusEnum.NOHANDLE.getText().equals(entity.getProcessingStatus())) {
throw new ServiceException("办理状态错误!!"); throw new ServiceException("办理状态错误!!");
} }
} }
@ -265,9 +266,22 @@ public class BusFormalitiesAreConsolidatedServiceImpl extends ServiceImpl<BusFor
* @return 是否删除成功 * @return 是否删除成功
*/ */
@Override @Override
@Transactional(rollbackFor = Exception.class)
public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) { public Boolean deleteWithValidByIds(Collection<Long> ids, Boolean isValid) {
if (isValid) { if (isValid) {
//TODO 做一些业务上的校验,判断是否需要校验 //TODO 做一些业务上的校验,判断是否需要校验
for (Long id : ids) {
BusFormalitiesAreConsolidated selected = baseMapper.selectById(id);
if (selected == null || !FormalitiesStatusEnum.TOSTART.getText().equals(selected.getProcessingStatus())) {
throw new ServiceException("只能删除待开始数据");
}
Long count = busFormalitiesAnnexService.getBaseMapper().selectCount(new LambdaQueryWrapper<BusFormalitiesAnnex>()
.eq(BusFormalitiesAnnex::getFormalitiesId, id));
if (count > 0L) {
busFormalitiesAnnexService.getBaseMapper().delete(new LambdaQueryWrapper<BusFormalitiesAnnex>()
.eq(BusFormalitiesAnnex::getFormalitiesId, id));
}
}
} }
return baseMapper.deleteByIds(ids) > 0; return baseMapper.deleteByIds(ids) > 0;
} }
@ -282,6 +296,12 @@ public class BusFormalitiesAreConsolidatedServiceImpl extends ServiceImpl<BusFor
throw new ServiceException("数据不存在"); throw new ServiceException("数据不存在");
} }
if (FormalitiesStatusEnum.NOHANDLE.getText().equals(bo.getProcessingStatus())){
if (!FormalitiesStatusEnum.TOSTART.getText().equals(busFormalitiesAreConsolidated.getProcessingStatus())) {
throw new ServiceException("只有待开始数据能修改为’不需要办理‘状态");
}
}
busFormalitiesAreConsolidated.setProcessingStatus(bo.getProcessingStatus()); busFormalitiesAreConsolidated.setProcessingStatus(bo.getProcessingStatus());
if (FormalitiesStatusEnum.DELETE.getText().equals(busFormalitiesAreConsolidated.getProcessingStatus())) { if (FormalitiesStatusEnum.DELETE.getText().equals(busFormalitiesAreConsolidated.getProcessingStatus())) {
if (busFormalitiesAreConsolidated.getHead() == null) { if (busFormalitiesAreConsolidated.getHead() == null) {

View File

@ -6,6 +6,8 @@ import lombok.RequiredArgsConstructor;
import jakarta.servlet.http.HttpServletResponse; import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.*; import jakarta.validation.constraints.*;
import cn.dev33.satoken.annotation.SaCheckPermission; import cn.dev33.satoken.annotation.SaCheckPermission;
import org.dromara.project.domain.dto.attendance.AttendanceExportDto;
import org.dromara.project.domain.vo.attendance.BusAttendanceClockDateForTwoWeekVo;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.dromara.common.idempotent.annotation.RepeatSubmit; import org.dromara.common.idempotent.annotation.RepeatSubmit;
@ -102,4 +104,16 @@ public class BusAttendanceController extends BaseController {
@PathVariable Long[] ids) { @PathVariable Long[] ids) {
return toAjax(busAttendanceService.deleteWithValidByIds(List.of(ids), true)); return toAjax(busAttendanceService.deleteWithValidByIds(List.of(ids), true));
} }
@GetMapping("/list/clockDate/twoWeek")
public R<List<BusAttendanceClockDateForTwoWeekVo>> getClockDateForTwoWeekList(Long projectId) {
return R.ok(busAttendanceService.getClockDateForTwoWeekList(projectId));
}
@GetMapping("/exportList")
public void exportList(AttendanceExportDto dto, HttpServletResponse response) {
busAttendanceService.getExportList(dto, response);
}
} }

View File

@ -64,7 +64,7 @@ public class BusAttendanceAppController extends BaseController {
/** /**
* 人脸坐标打卡 * 人脸坐标打卡
*/ */
@RepeatSubmit(interval = 3, timeUnit = TimeUnit.SECONDS,message = "3分钟内禁止重复打卡") @RepeatSubmit(interval = 3, timeUnit = TimeUnit.MINUTES,message = "3分钟内禁止重复打卡")
@PostMapping("/punch/card/face") @PostMapping("/punch/card/face")
public R<Boolean> punchCardByFace(@RequestPart("file") MultipartFile file, BusAttendancePunchCardByFaceReq req) { public R<Boolean> punchCardByFace(@RequestPart("file") MultipartFile file, BusAttendancePunchCardByFaceReq req) {
return R.ok(attendanceService.punchCardByFace(file, req)); return R.ok(attendanceService.punchCardByFace(file, req));

View File

@ -0,0 +1,33 @@
package org.dromara.project.domain.dto.attendance;
import jakarta.validation.constraints.NotNull;
import lombok.Data;
@Data
public class AttendanceExportDto {
/**
* 人员姓名
*/
private String userName;
/**
* 项目id
*/
@NotNull(message = "项目id不能为空")
private Long projectId;
/**
* 班组id
*/
private Long teamId;
/**
* 工种
*/
private String typeOfWork;
/**
* 打卡月份
*/
private String clockDate;
}

View File

@ -5,6 +5,7 @@ import lombok.Data;
import java.io.Serial; import java.io.Serial;
import java.io.Serializable; import java.io.Serializable;
import java.time.LocalDate;
import java.util.Date; import java.util.Date;
/** /**
@ -20,10 +21,7 @@ public class BusAttendanceClockDateForTwoWeekVo implements Serializable {
/** /**
* 打卡日期 * 打卡日期
*/ */
@JsonFormat(shape = JsonFormat.Shape.STRING, private LocalDate clockDate;
pattern = "yyyy-MM-dd",
timezone = "GMT+8")
private Date clockDate;
/** /**
* 出勤人数 * 出勤人数

View File

@ -1,6 +1,9 @@
package org.dromara.project.mapper; package org.dromara.project.mapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.project.domain.BusAttendance; import org.dromara.project.domain.BusAttendance;
import org.dromara.project.domain.bo.BusAttendanceBo;
import org.dromara.project.domain.vo.BusAttendanceVo; import org.dromara.project.domain.vo.BusAttendanceVo;
import org.dromara.common.mybatis.core.mapper.BaseMapperPlus; import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
@ -12,4 +15,7 @@ import org.dromara.common.mybatis.core.mapper.BaseMapperPlus;
*/ */
public interface BusAttendanceMapper extends BaseMapperPlus<BusAttendance, BusAttendanceVo> { public interface BusAttendanceMapper extends BaseMapperPlus<BusAttendance, BusAttendanceVo> {
Page<BusAttendanceVo> queryPageList(BusAttendanceBo bo, PageQuery pageQuery);
} }

View File

@ -1,7 +1,10 @@
package org.dromara.project.service; package org.dromara.project.service;
import jakarta.servlet.http.HttpServletResponse;
import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.NotNull;
import org.dromara.common.core.domain.R;
import org.dromara.project.domain.dto.attendance.AttendanceCountDto; import org.dromara.project.domain.dto.attendance.AttendanceCountDto;
import org.dromara.project.domain.dto.attendance.AttendanceExportDto;
import org.dromara.project.domain.dto.attendance.AttendanceUserCountDto; import org.dromara.project.domain.dto.attendance.AttendanceUserCountDto;
import org.dromara.project.domain.dto.attendance.BusAttendancePunchCardByFaceReq; import org.dromara.project.domain.dto.attendance.BusAttendancePunchCardByFaceReq;
import org.dromara.project.domain.vo.BusAttendanceVo; import org.dromara.project.domain.vo.BusAttendanceVo;
@ -153,4 +156,16 @@ public interface IBusAttendanceService extends IService<BusAttendance>{
* 指定月份的打卡人员信息 * 指定月份的打卡人员信息
*/ */
AttendanceUserInfoVo getAttendanceUserInfo(AttendanceUserCountDto dto); AttendanceUserInfoVo getAttendanceUserInfo(AttendanceUserCountDto dto);
/**
* 近两周打卡统计
*/
List<BusAttendanceClockDateForTwoWeekVo> getClockDateForTwoWeekList(Long projectId);
/**
* 获取导出的考勤数据
*/
void getExportList(AttendanceExportDto dto,HttpServletResponse response);
} }

View File

@ -6,13 +6,16 @@ import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONArray; import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONUtil; import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import jakarta.annotation.Resource;
import jakarta.servlet.http.HttpServletResponse;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.formula.functions.T; import org.apache.poi.hssf.usermodel.HSSFWorkbook;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.dromara.common.core.constant.HttpStatus; import org.dromara.common.core.constant.HttpStatus;
import org.dromara.common.core.exception.ServiceException; import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.core.utils.MapstructUtils; import org.dromara.common.core.utils.MapstructUtils;
@ -21,14 +24,15 @@ import org.dromara.common.domain.GeoPoint;
import org.dromara.common.mybatis.core.page.PageQuery; import org.dromara.common.mybatis.core.page.PageQuery;
import org.dromara.common.mybatis.core.page.TableDataInfo; import org.dromara.common.mybatis.core.page.TableDataInfo;
import org.dromara.common.satoken.utils.LoginHelper; import org.dromara.common.satoken.utils.LoginHelper;
import org.dromara.common.utils.IdCardEncryptorUtil;
import org.dromara.common.utils.JSTUtil; import org.dromara.common.utils.JSTUtil;
import org.dromara.contractor.domain.SubConstructionUser; import org.dromara.contractor.domain.SubConstructionUser;
import org.dromara.contractor.domain.vo.constructionuser.SubConstructionUserVo;
import org.dromara.contractor.service.ISubConstructionUserService; import org.dromara.contractor.service.ISubConstructionUserService;
import org.dromara.contractor.service.ISubUserSalaryDetailService; import org.dromara.contractor.service.ISubUserSalaryDetailService;
import org.dromara.project.domain.*; import org.dromara.project.domain.*;
import org.dromara.project.domain.bo.BusAttendanceBo; import org.dromara.project.domain.bo.BusAttendanceBo;
import org.dromara.project.domain.dto.attendance.AttendanceCountDto; import org.dromara.project.domain.dto.attendance.AttendanceCountDto;
import org.dromara.project.domain.dto.attendance.AttendanceExportDto;
import org.dromara.project.domain.dto.attendance.AttendanceUserCountDto; import org.dromara.project.domain.dto.attendance.AttendanceUserCountDto;
import org.dromara.project.domain.dto.attendance.BusAttendancePunchCardByFaceReq; import org.dromara.project.domain.dto.attendance.BusAttendancePunchCardByFaceReq;
import org.dromara.project.domain.enums.BusAttendanceClockStatusEnum; import org.dromara.project.domain.enums.BusAttendanceClockStatusEnum;
@ -47,10 +51,10 @@ import org.dromara.websocket.ChatServerHandler;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile; import org.springframework.web.multipart.MultipartFile;
import java.time.Duration; import java.io.IOException;
import java.time.LocalDate; import java.io.OutputStream;
import java.time.LocalDateTime; import java.time.*;
import java.time.LocalTime; import java.time.format.DateTimeFormatter;
import java.util.*; import java.util.*;
import java.util.stream.Collectors; import java.util.stream.Collectors;
@ -98,6 +102,8 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
private final ISubUserSalaryDetailService userSalaryDetailService; private final ISubUserSalaryDetailService userSalaryDetailService;
@Resource
private IdCardEncryptorUtil idCardEncryptorUtil;
// 出勤状态(正常、迟到、早退) // 出勤状态(正常、迟到、早退)
@ -312,7 +318,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
// 上传人脸照 // 上传人脸照
SysOssVo upload = ossService.upload(file); SysOssVo upload = ossService.upload(file);
attendance.setFacePic(upload.getOssId().toString()); attendance.setFacePic(upload.getOssId().toString());
chatServerHandler.sendSystemMessageToUser(userId, "打卡成功","1"); chatServerHandler.sendSystemMessageToUser(userId, "打卡成功", "1");
boolean save = this.save(attendance); boolean save = this.save(attendance);
//插入工资 //插入工资
userSalaryDetailService.insertByAttendance(userId, attendance.getClockDate()); userSalaryDetailService.insertByAttendance(userId, attendance.getClockDate());
@ -356,7 +362,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
/** /**
* 计算实际打卡时间与规定时间的分钟差 * 计算实际打卡时间与规定时间的分钟差
* *
* @param actualTime 实际打卡时间 * @param actualTime 实际打卡时间
* @param expectedTime 规定打卡时间 * @param expectedTime 规定打卡时间
* @return 时间差(分钟) * @return 时间差(分钟)
*/ */
@ -367,7 +373,6 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
} }
@Override @Override
public List<String> getPunchRangeByProjectIdAndUserId(Long projectId, Long userId) { public List<String> getPunchRangeByProjectIdAndUserId(Long projectId, Long userId) {
BusUserProjectRelevancy relevancy = userProjectRelevancyService.getOne(Wrappers.lambdaQuery(BusUserProjectRelevancy.class) BusUserProjectRelevancy relevancy = userProjectRelevancyService.getOne(Wrappers.lambdaQuery(BusUserProjectRelevancy.class)
@ -433,7 +438,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
BusProjectTeam team = projectTeamService.getById(one.getTeamId()); BusProjectTeam team = projectTeamService.getById(one.getTeamId());
//需要考虑班组不设置考勤范围 //需要考虑班组不设置考勤范围
if("1".equals(team.getIsClockIn())){ if ("1".equals(team.getIsClockIn())) {
return true; return true;
} }
try { try {
@ -445,7 +450,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
} }
// 再获取项目的规则 // 再获取项目的规则
BusAttendanceRuleVo busAttendanceRuleVo = attendanceRuleService.queryByProjectId(projectId); BusAttendanceRuleVo busAttendanceRuleVo = attendanceRuleService.queryByProjectId(projectId);
if(busAttendanceRuleVo != null && "2".equals(busAttendanceRuleVo.getType())){ if (busAttendanceRuleVo != null && "2".equals(busAttendanceRuleVo.getType())) {
return true; return true;
} }
@ -458,7 +463,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
.toList(); .toList();
if (CollUtil.isEmpty(punchRangeList)) { if (CollUtil.isEmpty(punchRangeList)) {
throw new ServiceException(isConstruct?"班组":"项目"+"未配置考勤范围", HttpStatus.BAD_REQUEST); throw new ServiceException(isConstruct ? "班组" : "项目" + "未配置考勤范围", HttpStatus.BAD_REQUEST);
} }
List<GeoPoint> matchingRange = JSTUtil.findMatchingRange(req.getLat(), req.getLng(), punchRangeList); List<GeoPoint> matchingRange = JSTUtil.findMatchingRange(req.getLat(), req.getLng(), punchRangeList);
return matchingRange != null; return matchingRange != null;
@ -634,7 +639,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
List<BusAttendance> attendanceList = baseMapper.selectList(Wrappers.<BusAttendance>lambdaQuery() List<BusAttendance> attendanceList = baseMapper.selectList(Wrappers.<BusAttendance>lambdaQuery()
.eq(BusAttendance::getProjectId, dto.getProjectId()) .eq(BusAttendance::getProjectId, dto.getProjectId())
.in(BusAttendance::getClockStatus,Arrays.asList("1","2","3","5","7")) .in(BusAttendance::getClockStatus, Arrays.asList("1", "2", "3", "5", "7"))
.eq(BusAttendance::getClockDate, dto.getDate()) .eq(BusAttendance::getClockDate, dto.getDate())
); );
List<Long> attendanceUserIds = attendanceList.stream().map(BusAttendance::getUserId).toList(); List<Long> attendanceUserIds = attendanceList.stream().map(BusAttendance::getUserId).toList();
@ -642,7 +647,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
//总人数 //总人数
long count = constructionUserService.count(Wrappers.<SubConstructionUser>lambdaQuery() long count = constructionUserService.count(Wrappers.<SubConstructionUser>lambdaQuery()
.eq(SubConstructionUser::getProjectId, dto.getProjectId())); .eq(SubConstructionUser::getProjectId, dto.getProjectId()));
attendanceCountVo.setTotalCount((int)count); attendanceCountVo.setTotalCount((int) count);
//出勤人数 //出勤人数
HashSet<Long> longs = new HashSet<>(attendanceUserIds); HashSet<Long> longs = new HashSet<>(attendanceUserIds);
int size = longs.size(); int size = longs.size();
@ -656,15 +661,15 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
List<BusAttendance> attendanceList = baseMapper.selectList(Wrappers.<BusAttendance>lambdaQuery() List<BusAttendance> attendanceList = baseMapper.selectList(Wrappers.<BusAttendance>lambdaQuery()
.eq(BusAttendance::getProjectId, dto.getProjectId()) .eq(BusAttendance::getProjectId, dto.getProjectId())
.in(BusAttendance::getClockStatus,Arrays.asList("1","2","3","5","7")) .in(BusAttendance::getClockStatus, Arrays.asList("1", "2", "3", "5", "7"))
.eq(BusAttendance::getClockDate, dto.getDate()) .eq(BusAttendance::getClockDate, dto.getDate())
); );
List<Long> attendanceUserIds = attendanceList.stream().map(BusAttendance::getUserId).toList(); List<Long> attendanceUserIds = attendanceList.stream().map(BusAttendance::getUserId).toList();
LambdaQueryWrapper<SubConstructionUser> wrapper = new LambdaQueryWrapper<>(); LambdaQueryWrapper<SubConstructionUser> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SubConstructionUser::getProjectId, dto.getProjectId()); wrapper.eq(SubConstructionUser::getProjectId, dto.getProjectId());
if("1".equals(dto.getClockStatus()) && CollectionUtil.isEmpty(attendanceUserIds)){ if ("1".equals(dto.getClockStatus()) && CollectionUtil.isEmpty(attendanceUserIds)) {
TableDataInfo<AttendanceUserVo> rspData = new TableDataInfo<>(); TableDataInfo<AttendanceUserVo> rspData = new TableDataInfo<>();
rspData.setCode(cn.hutool.http.HttpStatus.HTTP_OK); rspData.setCode(cn.hutool.http.HttpStatus.HTTP_OK);
rspData.setMsg("查询成功"); rspData.setMsg("查询成功");
@ -673,10 +678,10 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
return rspData; return rspData;
} }
wrapper.in("1".equals(dto.getClockStatus()),SubConstructionUser::getSysUserId,attendanceUserIds); wrapper.in("1".equals(dto.getClockStatus()), SubConstructionUser::getSysUserId, attendanceUserIds);
wrapper.notIn("2".equals(dto.getClockStatus()) && CollectionUtil.isNotEmpty(attendanceUserIds),SubConstructionUser::getSysUserId,attendanceUserIds); wrapper.notIn("2".equals(dto.getClockStatus()) && CollectionUtil.isNotEmpty(attendanceUserIds), SubConstructionUser::getSysUserId, attendanceUserIds);
wrapper.eq(StrUtil.isNotBlank(dto.getTypeOfWork()),SubConstructionUser::getTypeOfWork,dto.getTypeOfWork()); wrapper.eq(StrUtil.isNotBlank(dto.getTypeOfWork()), SubConstructionUser::getTypeOfWork, dto.getTypeOfWork());
wrapper.eq(dto.getTeamId()!=null,SubConstructionUser::getTeamId,dto.getTeamId()); wrapper.eq(dto.getTeamId() != null, SubConstructionUser::getTeamId, dto.getTeamId());
Page<SubConstructionUser> result = constructionUserService.page(pageQuery.build(), wrapper); Page<SubConstructionUser> result = constructionUserService.page(pageQuery.build(), wrapper);
List<SubConstructionUser> records = result.getRecords(); List<SubConstructionUser> records = result.getRecords();
@ -688,14 +693,14 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
attendanceUserVo.setUserId(sysUserVo.getUserId()); attendanceUserVo.setUserId(sysUserVo.getUserId());
attendanceUserVo.setNickName(sysUserVo.getNickName()); attendanceUserVo.setNickName(sysUserVo.getNickName());
if (sysUserVo.getAvatar() != null) { if (sysUserVo.getAvatar() != null) {
SysOssVo byId = ossService.getById(sysUserVo.getAvatar()); SysOssVo byId = ossService.getById(sysUserVo.getAvatar());
attendanceUserVo.setAvatar(byId==null?null:byId.getUrl()); attendanceUserVo.setAvatar(byId == null ? null : byId.getUrl());
} }
attendanceUserVo.setTypeOfWork(attendanceUserVo.getTypeOfWork()); attendanceUserVo.setTypeOfWork(attendanceUserVo.getTypeOfWork());
attendanceUserVo.setTeamName(attendanceUserVo.getTeamName()); attendanceUserVo.setTeamName(attendanceUserVo.getTeamName());
if(attendanceUserIds.contains(constructionUser.getSysUserId())){ if (attendanceUserIds.contains(constructionUser.getSysUserId())) {
attendanceUserVo.setClockStatus("1"); attendanceUserVo.setClockStatus("1");
}else{ } else {
attendanceUserVo.setClockStatus("2"); attendanceUserVo.setClockStatus("2");
} }
list.add(attendanceUserVo); list.add(attendanceUserVo);
@ -746,12 +751,12 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
attendanceUserCountVo.setLateNum(lateList.size()); attendanceUserCountVo.setLateNum(lateList.size());
List<LocalDate> earlyList = attendanceList.stream().filter(a -> "3".equals(a.getClockStatus())) List<LocalDate> earlyList = attendanceList.stream().filter(a -> "3".equals(a.getClockStatus()))
.map(BusAttendanceVo::getClockDate) .map(BusAttendanceVo::getClockDate)
.toList(); .toList();
attendanceUserCountVo.setEarlyNum(earlyList.size()); attendanceUserCountVo.setEarlyNum(earlyList.size());
List<LocalDate> absentList = attendanceList.stream().filter(a -> "4".equals(a.getClockStatus())) List<LocalDate> absentList = attendanceList.stream().filter(a -> "4".equals(a.getClockStatus()))
.map(BusAttendanceVo::getClockDate) .map(BusAttendanceVo::getClockDate)
.toList(); .toList();
attendanceUserCountVo.setAbsentNum(absentList.size()); attendanceUserCountVo.setAbsentNum(absentList.size());
@ -811,6 +816,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
/** /**
* 将BusAttendanceVo转换为AttendanceUserDataDetailVo * 将BusAttendanceVo转换为AttendanceUserDataDetailVo
*
* @param attendance 考勤记录 * @param attendance 考勤记录
* @return 考勤详情VO * @return 考勤详情VO
*/ */
@ -829,10 +835,10 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
AttendanceUserInfoVo attendanceUserInfoVo = new AttendanceUserInfoVo(); AttendanceUserInfoVo attendanceUserInfoVo = new AttendanceUserInfoVo();
SysUserVo sysUserVo = userService.selectUserById(dto.getUserId()); SysUserVo sysUserVo = userService.selectUserById(dto.getUserId());
if(sysUserVo == null){ if (sysUserVo == null) {
throw new ServiceException("用户信息不存在"); throw new ServiceException("用户信息不存在");
} }
if(sysUserVo.getAvatar() != null){ if (sysUserVo.getAvatar() != null) {
SysOssVo byId = ossService.getById(sysUserVo.getAvatar()); SysOssVo byId = ossService.getById(sysUserVo.getAvatar());
attendanceUserInfoVo.setAvatar(byId.getUrl()); attendanceUserInfoVo.setAvatar(byId.getUrl());
} }
@ -840,7 +846,7 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
attendanceUserInfoVo.setNickName(sysUserVo.getNickName()); attendanceUserInfoVo.setNickName(sysUserVo.getNickName());
SubConstructionUser constructionUser = constructionUserService.getBySysUserId(dto.getUserId()); SubConstructionUser constructionUser = constructionUserService.getBySysUserId(dto.getUserId());
if(constructionUser == null){ if (constructionUser == null) {
throw new ServiceException("施工人员信息不存在"); throw new ServiceException("施工人员信息不存在");
} }
attendanceUserInfoVo.setLeaveDate(constructionUser.getLeaveDate()); attendanceUserInfoVo.setLeaveDate(constructionUser.getLeaveDate());
@ -849,4 +855,331 @@ public class BusAttendanceServiceImpl extends ServiceImpl<BusAttendanceMapper, B
return attendanceUserInfoVo; return attendanceUserInfoVo;
} }
@Override
public List<BusAttendanceClockDateForTwoWeekVo> getClockDateForTwoWeekList(Long projectId) {
LocalDate now = LocalDate.now();
//往前14天包含今天
LocalDate startDate = now.minusDays(13);
List<BusAttendance> list = list(Wrappers.<BusAttendance>lambdaQuery().eq(BusAttendance::getProjectId, projectId)
.between(BusAttendance::getClockDate, startDate, now)
);
// 按日期分组
Map<LocalDate, List<BusAttendance>> groupedByDate = list.stream()
.collect(Collectors.groupingBy(BusAttendance::getClockDate));
// 按日期和用户ID分组
Map<LocalDate, Map<Long, List<BusAttendance>>> dateUserMap = new HashMap<>();
for (Map.Entry<LocalDate, List<BusAttendance>> entry : groupedByDate.entrySet()) {
LocalDate date = entry.getKey();
Map<Long, List<BusAttendance>> userAttendanceMap = entry.getValue().stream()
.collect(Collectors.groupingBy(BusAttendance::getUserId));
dateUserMap.put(date, userAttendanceMap);
}
// 统计全勤、半勤、缺卡人数
ArrayList<BusAttendanceClockDateForTwoWeekVo> busAttendanceClockDateForTwoWeekVos = new ArrayList<>();
List<String> list1 = Arrays.asList("1", "2", "3", "5");
for (Map.Entry<LocalDate, Map<Long, List<BusAttendance>>> dateEntry : dateUserMap.entrySet()) {
LocalDate date = dateEntry.getKey();
Map<Long, List<BusAttendance>> userAttendanceMap = dateEntry.getValue();
int full = 0, half = 0, absent = 0;
for (Map.Entry<Long, List<BusAttendance>> userEntry : userAttendanceMap.entrySet()) {
List<BusAttendance> records = userEntry.getValue();
int a = 0;
for (BusAttendance record : records) {
if (list1.contains(record.getClockStatus())) {
a += 1;
}
}
if (a >= 2) {
full++;
} else if (a == 1) {
half++;
} else {
absent++;
}
}
BusAttendanceClockDateForTwoWeekVo busAttendanceClockDateForTwoWeekVo = new BusAttendanceClockDateForTwoWeekVo();
busAttendanceClockDateForTwoWeekVo.setClockDate(date);
busAttendanceClockDateForTwoWeekVo.setAttendance(full);
busAttendanceClockDateForTwoWeekVo.setHalfAttendance(half);
busAttendanceClockDateForTwoWeekVo.setAbsenteeism(absent);
busAttendanceClockDateForTwoWeekVos.add(busAttendanceClockDateForTwoWeekVo);
}
return busAttendanceClockDateForTwoWeekVos;
}
@Override
public void getExportList(AttendanceExportDto dto, HttpServletResponse response) {
BusProject project = projectService.getById(dto.getProjectId());
if (project == null) {
throw new ServiceException("项目不存在");
}
List<SubConstructionUser> list = constructionUserService.list(Wrappers.lambdaQuery(SubConstructionUser.class)
.eq(SubConstructionUser::getProjectId, dto.getProjectId())
.eq(dto.getTeamId() != null, SubConstructionUser::getTeamId, dto.getTeamId())
.eq(StrUtil.isNotBlank(dto.getTypeOfWork()), SubConstructionUser::getTypeOfWork, dto.getTypeOfWork())
.like(StringUtils.isNotBlank(dto.getUserName()), SubConstructionUser::getUserName, dto.getUserName())
);
if (list.isEmpty()) {
throw new ServiceException("未查询到相关人员");
}
List<Long> userIds = list.stream().map(SubConstructionUser::getSysUserId).toList();
String clockDate = dto.getClockDate();
YearMonth yearMonth = YearMonth.parse(clockDate, DateTimeFormatter.ofPattern("yyyy-MM"));
LocalDate start = yearMonth.atDay(1);
LocalDate end = yearMonth.atEndOfMonth();
int daysInMonth = yearMonth.lengthOfMonth(); // 动态获取天数
List<BusAttendance> attendanceList = list(Wrappers.lambdaQuery(BusAttendance.class)
.in(BusAttendance::getUserId, userIds)
.between(BusAttendance::getClockDate, start, end)
);
Map<Long, List<SubConstructionUser>> teamUserMap = list.stream()
.filter(user -> user.getTeamId() != null)
.collect(Collectors.groupingBy(SubConstructionUser::getTeamId));
Workbook workbook = new HSSFWorkbook();
for (Map.Entry<Long, List<SubConstructionUser>> entry : teamUserMap.entrySet()) {
Long teamId = entry.getKey();
List<SubConstructionUser> users = entry.getValue();
SubConstructionUser constructionUser = users.getFirst();
String teamName = constructionUser.getTeamName();
Sheet sheet = workbook.createSheet(teamName);
// ==================== 设置列宽 ====================
sheet.setColumnWidth(0, 5* 256); // 序号
sheet.setColumnWidth(1, 10* 256); // 姓名/日期
sheet.setColumnWidth(2, 30* 256); // 身份证号
// 日期列(每天一列)
for (int i = 3; i < 3 + daysInMonth; i++) {
sheet.setColumnWidth(i, 5* 256); // 每天列宽 15 字符
}
sheet.setColumnWidth(3 + daysInMonth, 5* 256); // 合计
sheet.setColumnWidth(3 + daysInMonth + 1, 10* 256); // 是否离场
sheet.setColumnWidth(3 + daysInMonth + 2, 15* 256); // 签字
// ==================== 表头部分 ====================
Row titleRow = sheet.createRow(0);
Cell titleCell = titleRow.createCell(0);
titleCell.setCellValue("建筑施工企业现场人员考勤表(" + clockDate + "月)");
titleCell.setCellStyle(createTitleCellStyle(workbook));
// 合并标题行,横跨所有列
int totalColumns = 3 + daysInMonth + 3; // 总列数
sheet.addMergedRegion(new CellRangeAddress(0, 0, 0, totalColumns - 1));
// ==================== 第二行:项目名称和班组类别 ====================
Row projectRow = sheet.createRow(1);
// 设置项目名称(左对齐,占据前半部分)
Cell projectNameCell = projectRow.createCell(0);
projectNameCell.setCellValue("项目名称:" + project.getProjectName());
projectNameCell.setCellStyle(createProjectCellStyle(workbook)); // 可选:设置样式
int midColumn = (totalColumns / 2) - 1;
// 设置班组类别(右对齐,占据后半部分)
int teamCol = midColumn +1; //
Cell teamNameCell = projectRow.createCell(teamCol);
teamNameCell.setCellValue("班组类别:" + teamName);
teamNameCell.setCellStyle(createProjectCellStyle(workbook));
// 合并项目名称区域(从第 0 列到中间)
if (midColumn >= 0) {
sheet.addMergedRegion(new CellRangeAddress(1, 1, 0, midColumn));
}
// 合并班组类别区域(从 teamCol 开始,到最后一列)
if (teamCol < totalColumns) {
sheet.addMergedRegion(new CellRangeAddress(1, 1, teamCol, totalColumns - 1));
}
// ==================== 数据表头 ====================
Row headerRow = sheet.createRow(2);
writeHeaderRow(headerRow, daysInMonth);
// ==================== 数据行 ====================
int rowIndex = 3;
for (SubConstructionUser user : users) {
Row row = sheet.createRow(rowIndex++);
writeDataRow(row, user, attendanceList, start, end, daysInMonth);
}
// ==================== 表尾部分 ====================
sheet.createRow(rowIndex++);
Row footerRow1 = sheet.createRow(rowIndex++);
int allTotalColumns = 3 + daysInMonth + 3;
int quarter = allTotalColumns / 4;
int[] colSpans = {
quarter - 3, // 第一列减少3列
quarter + 2, // 第二列增加2列
quarter + 2, // 第三列增加2列
allTotalColumns - (quarter - 3 + quarter + 2 + quarter + 2) // 自动计算最后一列
};
int startCol = 0;
String[] footerLabels = {"制表人:", "班组负责人:", "项目负责人:", "制表日期:"};
for (int i = 0; i < 4; i++) {
int endCol = startCol + colSpans[i] - 1;
// 合并单元格
sheet.addMergedRegion(new CellRangeAddress(rowIndex - 1, rowIndex - 1, startCol, endCol));
// 创建单元格并设置内容
Cell cell = footerRow1.createCell(startCol);
cell.setCellValue(footerLabels[i]);
cell.setCellStyle(createLeftAlignedCellStyle(workbook)); // 左对齐
startCol = endCol + 1;
}
Row footerRow2 = sheet.createRow(rowIndex++);
footerRow2.createCell(0).setCellValue("注:");
footerRow2.createCell(1).setCellValue("1、本考勤表必须按照每日实际工时进行填写");
Row footerRow3 = sheet.createRow(rowIndex++);
footerRow3.createCell(0).setCellValue("");
footerRow3.createCell(1).setCellValue("2、本考勤表作为工资计发的重要依据。");
}
try (OutputStream outputStream = response.getOutputStream()) {
workbook.write(outputStream);
workbook.close();
} catch (IOException e) {
log.error("导出失败", e);
throw new ServiceException("导出失败");
}
}
private CellStyle createProjectCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontHeightInPoints((short) 10);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
return style;
}
private CellStyle createTitleCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
Font font = workbook.createFont();
font.setFontHeightInPoints((short) 16);
font.setBold(true);
style.setFont(font);
style.setAlignment(HorizontalAlignment.CENTER);
style.setVerticalAlignment(VerticalAlignment.CENTER);
return style;
}
private void writeHeaderRow(Row row, int daysInMonth) {
row.createCell(0).setCellValue("序号");
row.createCell(1).setCellValue("姓名/日期");
row.createCell(2).setCellValue("身份证号");
for (int i = 1; i <= daysInMonth; i++) {
row.createCell(2 + i).setCellValue(String.valueOf(i));
}
row.createCell(2 + daysInMonth + 1).setCellValue("合计");
row.createCell(2 + daysInMonth + 2).setCellValue("是否离场");
row.createCell(2 + daysInMonth + 3).setCellValue("签字");
}
private void writeDataRow(Row row, SubConstructionUser user, List<BusAttendance> attendanceList, LocalDate start, LocalDate end, int daysInMonth) {
int index = row.getRowNum();
row.createCell(0).setCellValue(index);
row.createCell(1).setCellValue(user.getUserName());
row.createCell(2).setCellValue(idCardEncryptorUtil.decrypt(user.getSfzNumber()));
for (int i = 1; i <= daysInMonth; i++) {
LocalDate date = start.plusDays(i - 1);
String value = getAttendanceValue(user.getSysUserId(), date, attendanceList);
row.createCell(2 + i).setCellValue(value);
}
double total = countAttendance(user.getSysUserId(), attendanceList, start, end);
row.createCell(2 + daysInMonth + 1).setCellValue(total);
String leaveStatus = "0".equals(user.getExitStatus()) ? "未离场" : "已离场";
row.createCell(2 + daysInMonth + 2).setCellValue(leaveStatus);
row.createCell(2 + daysInMonth + 3).setCellValue("");
}
private String getAttendanceValue(Long userId, LocalDate date, List<BusAttendance> attendanceList) {
List<BusAttendance> validRecords = attendanceList.stream()
.filter(a -> a.getUserId().equals(userId) && a.getClockDate().equals(date))
.filter(a -> !a.getClockStatus().equals("4")) // 排除缺卡记录
.collect(Collectors.toList());
if (validRecords.isEmpty()) {
return "0"; // 无有效打卡
}
// 检查是否有有效出勤状态1,2,3,5
boolean hasValidStatus = validRecords.stream()
.anyMatch(a -> Arrays.asList("1", "2", "3", "5").contains(a.getClockStatus()));
if (!hasValidStatus) {
return "0"; // 状态无效,如补卡、其他异常
}
// 判断是否为半勤(仅一次有效打卡)
if (validRecords.size() == 1) {
return "0.5";
}
// 正常出勤(两次有效打卡)
return "1";
}
private double countAttendance(Long userId, List<BusAttendance> attendanceList, LocalDate start, LocalDate end) {
double total = 0.0;
LocalDate current = start;
while (!current.isAfter(end)) {
String value = getAttendanceValue(userId, current, attendanceList);
if ("1".equals(value)) {
total += 1.0;
} else if ("0.5".equals(value)) {
total += 0.5;
}
current = current.plusDays(1);
}
return total;
}
private CellStyle createLeftAlignedCellStyle(Workbook workbook) {
CellStyle style = workbook.createCellStyle();
style.setAlignment(HorizontalAlignment.LEFT); // 左对齐
style.setVerticalAlignment(VerticalAlignment.CENTER);
return style;
}
} }

View File

@ -31,17 +31,16 @@ import org.dromara.project.domain.vo.projectteammember.BusProjectTeamMemberVo;
import org.dromara.project.mapper.BusProjectTeamMemberMapper; import org.dromara.project.mapper.BusProjectTeamMemberMapper;
import org.dromara.project.service.*; import org.dromara.project.service.*;
import org.dromara.system.domain.SysUser; import org.dromara.system.domain.SysUser;
import org.dromara.system.domain.SysUserRole;
import org.dromara.system.domain.vo.SysUserVo; import org.dromara.system.domain.vo.SysUserVo;
import org.dromara.system.mapper.SysUserRoleMapper;
import org.dromara.system.service.ISysUserService; import org.dromara.system.service.ISysUserService;
import org.springframework.beans.BeanUtils; import org.springframework.beans.BeanUtils;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDate; import java.time.LocalDate;
import java.util.Date; import java.util.*;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors; import java.util.stream.Collectors;
/** /**
@ -77,6 +76,9 @@ public class BusProjectTeamMemberServiceImpl extends ServiceImpl<BusProjectTeamM
@Resource @Resource
private IBusUserProjectRelevancyService userProjectRelevancyService; private IBusUserProjectRelevancyService userProjectRelevancyService;
@Resource
private SysUserRoleMapper userRoleMapper;
/** /**
* 查询项目班组下的成员 * 查询项目班组下的成员
@ -180,7 +182,29 @@ public class BusProjectTeamMemberServiceImpl extends ServiceImpl<BusProjectTeamM
relevancy.setProjectId(req.getProjectId()); relevancy.setProjectId(req.getProjectId());
userProjectRelevancyService.save(relevancy); userProjectRelevancyService.save(relevancy);
} }
//设置基础角色
List<SysUserRole> sysUserRoles = userRoleMapper.selectList(Wrappers.<SysUserRole>lambdaQuery()
.eq(SysUserRole::getUserId, constructionUser.getSysUserId())
.eq(SysUserRole::getProjectId, req.getProjectId())
.in(SysUserRole::getRoleId, Arrays.asList(2L, 3L))
);
if (CollUtil.isEmpty(sysUserRoles)) {
SysUserRole sysUserRole = new SysUserRole();
sysUserRole.setUserId(constructionUser.getSysUserId());
sysUserRole.setRoleId("0".equals(req.getPostId()) ? 2L : 3L);
sysUserRole.setProjectId(req.getProjectId());
userRoleMapper.insert(sysUserRole);
} else {
Long roleId = "0".equals(req.getPostId()) ? 2L : 3L;
List<Long> list1 = sysUserRoles.stream().map(SysUserRole::getRoleId).toList();
if (!list1.contains(roleId)){
SysUserRole sysUserRole = new SysUserRole();
sysUserRole.setUserId(constructionUser.getSysUserId());
sysUserRole.setRoleId(roleId);
sysUserRole.setProjectId(req.getProjectId());
userRoleMapper.insert(sysUserRole);
}
}
return projectTeamMember.getId(); return projectTeamMember.getId();
} }

View File

@ -39,30 +39,26 @@ import java.util.concurrent.ConcurrentHashMap;
public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> { public class ChatServerHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
// 移除 @Autowired 注解 // 移除 @Autowired 注解
private static ChatHistoryServiceImpl chatHistoryService;
private static ChatGroupServiceImpl chatGroupService;
private static SysUserServiceImpl sysUserService;
private static SysOssServiceImpl sysOssService;
@Autowired @Autowired
private ChatHistoryServiceImpl chatHistoryService; public void setChatHistoryService(ChatHistoryServiceImpl service) {
chatHistoryService = service;
}
@Autowired @Autowired
private ChatGroupServiceImpl chatGroupService; public void setChatGroupService(ChatGroupServiceImpl service){
chatGroupService = service;
}
@Autowired @Autowired
private SysUserServiceImpl sysUserService; public void setSysUserService(SysUserServiceImpl service){
sysUserService = service;
}
@Autowired @Autowired
private SysOssServiceImpl sysOssService; public void setSysOssService(SysOssServiceImpl service){
// @Autowired sysOssService = service;
// public void setChatHistoryService(ChatHistoryServiceImpl service) { }
// chatHistoryService = service;
// }
// @Autowired
// public void setChatGroupService(ChatGroupServiceImpl service){
// chatGroupService = service;
// }
// @Autowired
// public void setSysUserService(SysUserServiceImpl service){
// sysUserService = service;
// }
// @Autowired
// public void setSysOssService(SysOssServiceImpl service){
// sysOssService = service;
// }
// 存储所有连接的客户端Channel // 存储所有连接的客户端Channel
private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE); private static ChannelGroup channelGroup = new DefaultChannelGroup(GlobalEventExecutor.INSTANCE);

View File

@ -156,6 +156,11 @@ public class ChatGroupController {
return R.fail(); return R.fail();
} }
@GetMapping("/testSys")
public void testSys(Long userId, String message){
chatServerHandler.sendSystemMessageToUser(userId, message,"1");
}
/*** /***
* 获取房间聊天记录 传递房间ID 获取发送给该房间的所有聊天记录 * 获取房间聊天记录 传递房间ID 获取发送给该房间的所有聊天记录
*/ */