feat(system): 增加工资条导入功能并优化相关服务- 新增工资条导入相关的错误码常量

- 修改考勤统计导出功能,增加行高设置
- 优化工资条控制器和服务实现,支持导入功能- 更新数据库实体和相关VO,增加公司名称字段
- 重构工资条导出功能,优化数据处理和样式设置
This commit is contained in:
furongxin 2024-10-30 09:28:18 +08:00
parent 3001531471
commit ac47a0a252
9 changed files with 67 additions and 14 deletions

View File

@ -269,4 +269,5 @@ public interface ErrorCodeConstants {
ErrorCode PAYSLIP_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_012_002_006, "导入数据不能为空!"); ErrorCode PAYSLIP_IMPORT_LIST_IS_EMPTY = new ErrorCode(1_012_002_006, "导入数据不能为空!");
ErrorCode PAYSLIP_EXISTS = new ErrorCode(1_012_002_007, "所选公司在当前月份已上传工资条!如需修改,请勾选覆盖导入。"); ErrorCode PAYSLIP_EXISTS = new ErrorCode(1_012_002_007, "所选公司在当前月份已上传工资条!如需修改,请勾选覆盖导入。");
ErrorCode PAYSLIP_ID_CARD_ERROR = new ErrorCode(1_012_002_008, "【{}】身份证输入有误,请核对后导入!"); ErrorCode PAYSLIP_ID_CARD_ERROR = new ErrorCode(1_012_002_008, "【{}】身份证输入有误,请核对后导入!");
ErrorCode PAYSLIP_ISSUED = new ErrorCode(1_012_002_009, "所选公司在当前月份的工资条已下发,不能修改!");
} }

View File

@ -23,8 +23,10 @@ import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid; import javax.validation.Valid;
import java.io.IOException; import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
import static cn.iocoder.yudao.framework.common.util.collection.CollectionUtils.convertList;
import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT; import static cn.iocoder.yudao.framework.operatelog.core.enums.OperateTypeEnum.EXPORT;
@Tag(name = "管理后台 - 工资条") @Tag(name = "管理后台 - 工资条")
@ -162,6 +164,10 @@ public class PayslipController {
public CommonResult<PageResult<PayslipRespVO>> getMyPage(@RequestBody PayslipPageReqVO pageReqVO) { public CommonResult<PageResult<PayslipRespVO>> getMyPage(@RequestBody PayslipPageReqVO pageReqVO) {
PageResult<PayslipRespVO> pageResult = BeanUtils.toBean(payslipService.getMyPage(pageReqVO), PayslipRespVO.class); PageResult<PayslipRespVO> pageResult = BeanUtils.toBean(payslipService.getMyPage(pageReqVO), PayslipRespVO.class);
// 获取电子签名Map
Map<Long, String> signImgPathMap = fileApi.getUserSignImgPathMap(convertList(pageResult.getList(), PayslipRespVO::getUserId)).getCheckedData();
pageResult.getList().forEach(data -> { pageResult.getList().forEach(data -> {
List<PayslipDetail> details = data.getDetails(); List<PayslipDetail> details = data.getDetails();
// 设置实发工资 // 设置实发工资
@ -189,7 +195,7 @@ public class PayslipController {
.setValue(data.getRemark())); .setValue(data.getRemark()));
if (data.getIsConfirm() == 1) { if (data.getIsConfirm() == 1) {
data.setSignURL(fileApi.getUserSignImgPath(data.getUserId()).getData()); data.setSignURL(signImgPathMap.get(data.getUserId()));
} }
// 设置签名 // 设置签名
@ -208,12 +214,14 @@ public class PayslipController {
@Parameter(name = "file", description = "Excel 文件", required = true), @Parameter(name = "file", description = "Excel 文件", required = true),
@Parameter(name = "date", description = "日期", required = true), @Parameter(name = "date", description = "日期", required = true),
@Parameter(name = "deptId", description = "公司编号", required = true), @Parameter(name = "deptId", description = "公司编号", required = true),
@Parameter(name = "companyName", description = "公司名称", required = true),
@Parameter(name = "isUpdate", description = "是否允许覆盖", required = true) @Parameter(name = "isUpdate", description = "是否允许覆盖", required = true)
}) })
@PreAuthorize("@ss.hasPermission('system:hr:payslip:import')") @PreAuthorize("@ss.hasPermission('system:hr:payslip:import')")
public CommonResult<Boolean> importExcel(@RequestParam("file") MultipartFile file, public CommonResult<Boolean> importExcel(@RequestParam("file") MultipartFile file,
@RequestParam("date") String date, @RequestParam("date") String date,
@RequestParam("deptId") Long deptId, @RequestParam("deptId") Long deptId,
@RequestParam("companyName") String companyName,
@RequestParam("isUpdate") Boolean isUpdate) throws Exception { @RequestParam("isUpdate") Boolean isUpdate) throws Exception {
List<PayslipImportExcelVO> list = EasyExcel.read(file.getInputStream(), PayslipImportExcelVO.class, null) List<PayslipImportExcelVO> list = EasyExcel.read(file.getInputStream(), PayslipImportExcelVO.class, null)
@ -221,7 +229,7 @@ public class PayslipController {
.autoCloseStream(false) // 不要自动关闭交给 Servlet 自己处理 .autoCloseStream(false) // 不要自动关闭交给 Servlet 自己处理
.doReadAllSync(); .doReadAllSync();
// 导入数据 // 导入数据
payslipService.importPayslip(list, date, deptId, isUpdate); payslipService.importPayslip(list, date, deptId, companyName, isUpdate);
return success(true); return success(true);
} }

View File

@ -25,6 +25,9 @@ public class PayslipCreateReqVO {
@Schema(description = "所属公司编号") @Schema(description = "所属公司编号")
private Long companyDeptId; private Long companyDeptId;
@Schema(description = "所属公司名称")
private String companyName;
@Schema(description = "银行开户名") @Schema(description = "银行开户名")
private String bankName; private String bankName;

View File

@ -28,6 +28,9 @@ public class PayslipRespVO {
@Schema(description = "所属公司编号") @Schema(description = "所属公司编号")
private Long companyDeptId; private Long companyDeptId;
@Schema(description = "公司名称", example = "芋道源码")
private String companyName;
@Schema(description = "银行开户名") @Schema(description = "银行开户名")
private String bankName; private String bankName;

View File

@ -40,6 +40,11 @@ public class PayslipDO extends BaseDO {
*/ */
private Long companyDeptId; private Long companyDeptId;
/**
* 所属公司名称
*/
private String companyName;
/** /**
* 薪资日期 YYYY-MM * 薪资日期 YYYY-MM
*/ */

View File

@ -1252,7 +1252,7 @@ public class AttendanceServiceImpl implements AttendanceService {
.head(generateDailyHead(headTitle, detailedHead, maxSize)) .head(generateDailyHead(headTitle, detailedHead, maxSize))
.autoCloseStream(false) .autoCloseStream(false)
.excelType(ExcelTypeEnum.XLS) .excelType(ExcelTypeEnum.XLS)
.registerWriteHandler(new CustomCellStyleHandler()) .registerWriteHandler(new CustomCellStyleHandler(null))
.sheet("考勤统计按日导出") .sheet("考勤统计按日导出")
.doWrite(data); .doWrite(data);
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("考勤统计", StandardCharsets.UTF_8.name())); response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("考勤统计", StandardCharsets.UTF_8.name()));
@ -1399,7 +1399,7 @@ public class AttendanceServiceImpl implements AttendanceService {
.head(generateHead(headTitle, detailedHead)) .head(generateHead(headTitle, detailedHead))
.autoCloseStream(false) .autoCloseStream(false)
.excelType(ExcelTypeEnum.XLS) .excelType(ExcelTypeEnum.XLS)
.registerWriteHandler(new CustomCellStyleHandler()) .registerWriteHandler(new CustomCellStyleHandler(null))
.sheet("考勤统计按月导出") .sheet("考勤统计按月导出")
.doWrite(data); .doWrite(data);
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("考勤统计", StandardCharsets.UTF_8.name())); response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("考勤统计", StandardCharsets.UTF_8.name()));

View File

@ -14,6 +14,11 @@ import java.util.Map;
public class CustomCellStyleHandler implements CellWriteHandler { public class CustomCellStyleHandler implements CellWriteHandler {
private final Map<String, CellStyle> styleCache = new HashMap<>(); private final Map<String, CellStyle> styleCache = new HashMap<>();
private final List<String> nameList;
public CustomCellStyleHandler(List<String> nameList) {
this.nameList = nameList;
}
@Override @Override
public void afterCellDispose( public void afterCellDispose(
@ -32,6 +37,10 @@ public class CustomCellStyleHandler implements CellWriteHandler {
CellStyle cellStyle = getOrCreateCellStyle(workbook, isHead, relativeRowIndex, cellDataList); CellStyle cellStyle = getOrCreateCellStyle(workbook, isHead, relativeRowIndex, cellDataList);
cell.setCellStyle(cellStyle); cell.setCellStyle(cellStyle);
if (nameList != null && nameList.contains(cell.getStringCellValue())) {
cell.getRow().setHeightInPoints(150);
}
} }
private CellStyle getOrCreateCellStyle(Workbook workbook, Boolean isHead, Integer relativeRowIndex, List<WriteCellData<?>> cellDataList) { private CellStyle getOrCreateCellStyle(Workbook workbook, Boolean isHead, Integer relativeRowIndex, List<WriteCellData<?>> cellDataList) {

View File

@ -53,7 +53,7 @@ public interface PayslipService {
* 导入工资条 * 导入工资条
* @param list 导入工资条数据 * @param list 导入工资条数据
*/ */
void importPayslip(List<PayslipImportExcelVO> list, String date, Long deptId, Boolean isUpdate); void importPayslip(List<PayslipImportExcelVO> list, String date, Long deptId, String companyName, Boolean isUpdate);
/** /**
* 下发工资条 * 下发工资条

View File

@ -4,6 +4,7 @@ import cn.hutool.core.collection.CollUtil;
import cn.iocoder.yudao.framework.common.pojo.PageResult; import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX; import cn.iocoder.yudao.framework.mybatis.core.query.LambdaQueryWrapperX;
import cn.iocoder.yudao.module.infra.api.file.FileApi;
import cn.iocoder.yudao.module.system.controller.admin.hr.vo.payslip.*; import cn.iocoder.yudao.module.system.controller.admin.hr.vo.payslip.*;
import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO; import cn.iocoder.yudao.module.system.dal.dataobject.dept.DeptDO;
import cn.iocoder.yudao.module.system.dal.dataobject.hr.PayslipDO; import cn.iocoder.yudao.module.system.dal.dataobject.hr.PayslipDO;
@ -20,6 +21,7 @@ import org.springframework.transaction.annotation.Transactional;
import javax.annotation.Resource; import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import java.io.IOException; import java.io.IOException;
import java.net.URL;
import java.net.URLEncoder; import java.net.URLEncoder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
@ -51,6 +53,9 @@ public class PayslipServiceImpl implements PayslipService{
@Resource @Resource
private AdminUserService userService; private AdminUserService userService;
@Resource
private FileApi fileApi;
@Override @Override
public void createPayslip(List<PayslipCreateReqVO> createReqVO) { public void createPayslip(List<PayslipCreateReqVO> createReqVO) {
@ -87,12 +92,20 @@ public class PayslipServiceImpl implements PayslipService{
@Override @Override
@Transactional(rollbackFor = Exception.class) // 添加事务异常则回滚所有导入 @Transactional(rollbackFor = Exception.class) // 添加事务异常则回滚所有导入
public void importPayslip(List<PayslipImportExcelVO> importList, String date, Long deptId, Boolean isUpdate) { public void importPayslip(List<PayslipImportExcelVO> importList, String date, Long deptId, String companyName, Boolean isUpdate) {
if (CollUtil.isEmpty(importList)) { if (CollUtil.isEmpty(importList)) {
throw exception(PAYSLIP_IMPORT_LIST_IS_EMPTY); throw exception(PAYSLIP_IMPORT_LIST_IS_EMPTY);
} }
if (payslipMapper.selectCount(new LambdaQueryWrapperX<PayslipDO>()
.eq(PayslipDO::getSalaryDate, date)
.eqIfPresent(PayslipDO::getCompanyDeptId, deptId)
.eq(PayslipDO::getIsConfirm, 0)) > 0L) {
throw exception(PAYSLIP_ISSUED);
}
// 判断如果选择覆盖 则删除原有数据重新插入 // 判断如果选择覆盖 则删除原有数据重新插入
if (isUpdate) { if (isUpdate) {
payslipMapper.deletePayslip(date, deptId); payslipMapper.deletePayslip(date, deptId);
@ -125,6 +138,7 @@ public class PayslipServiceImpl implements PayslipService{
payslipDO.setBankName(data.getBankName()); payslipDO.setBankName(data.getBankName());
payslipDO.setBankNo(data.getBankNo()); payslipDO.setBankNo(data.getBankNo());
payslipDO.setRemark(data.getRemark()); payslipDO.setRemark(data.getRemark());
payslipDO.setCompanyName(companyName);
/* begin 设置明细数据 */ /* begin 设置明细数据 */
List<PayslipDetail> details = new ArrayList<>(); List<PayslipDetail> details = new ArrayList<>();
@ -146,7 +160,7 @@ public class PayslipServiceImpl implements PayslipService{
// 误餐补助 // 误餐补助
detailList.add(new PayslipDetail() detailList.add(new PayslipDetail()
.setCode("wcbz") .setCode("wcbz")
.setName("防暑降温费") .setName("误餐补助")
.setValue(data.getWcbz())); .setValue(data.getWcbz()));
// 福利费 // 福利费
detailList.add(new PayslipDetail() detailList.add(new PayslipDetail()
@ -258,11 +272,15 @@ public class PayslipServiceImpl implements PayslipService{
List<Long> userIds = convertList(list, PayslipRespVO::getUserId); List<Long> userIds = convertList(list, PayslipRespVO::getUserId);
Map<Long, AdminUserDO> userMap = convertMap(userService.getUserList(userIds), AdminUserDO::getId); Map<Long, AdminUserDO> userMap = convertMap(userService.getUserList(userIds), AdminUserDO::getId);
List<List<String>> data = new ArrayList<>(); // 获取电子签名Map
Map<Long, String> signImgPathMap = fileApi.getUserSignImgPathMap(userIds).getCheckedData();
List<String> nameList = new ArrayList<>();
List<List<Object>> data = new ArrayList<>();
int count = 1; int count = 1;
for (PayslipRespVO payslipRespVO : list) { for (PayslipRespVO payslipRespVO : list) {
List<String> row = new ArrayList<>(); List<Object> row = new ArrayList<>();
row.add(String.valueOf(count++)); row.add(String.valueOf(count++));
row.add(payslipRespVO.getUserName()); row.add(payslipRespVO.getUserName());
row.add(userMap.get(payslipRespVO.getUserId()).getIdcard()); row.add(userMap.get(payslipRespVO.getUserId()).getIdcard());
@ -275,14 +293,15 @@ public class PayslipServiceImpl implements PayslipService{
}else { }else {
children.add(item); children.add(item);
} }
return children.stream().collect(Collectors.toMap(PayslipDetail::getCode, PayslipDetail::getValue)); return children.stream().collect(Collectors.toMap(PayslipDetail::getCode,
value -> value.getValue() == null ? 0 : value.getValue()));
}) })
.flatMap(map -> map.entrySet().stream()) // 将每个 Map 转换为 Entry .flatMap(map -> map.entrySet().stream()) // 将每个 Map 转换为 Entry
.collect(Collectors.toMap( .collect(Collectors.toMap(
Map.Entry::getKey, // 使用 Entry key Map.Entry::getKey, // 使用 Entry key
Map.Entry::getValue, // 使用 Entry value Map.Entry::getValue, // 使用 Entry value
(existing, replacement) -> existing // 处理键冲突保留现有值 (existing, replacement) -> existing // 处理键冲突保留现有值
));; ));
row.add(detailMap.get("jbgz").toString()); row.add(detailMap.get("jbgz").toString());
row.add(detailMap.get("fsjwf").toString()); row.add(detailMap.get("fsjwf").toString());
@ -302,20 +321,25 @@ public class PayslipServiceImpl implements PayslipService{
row.add(payslipRespVO.getBankName()); row.add(payslipRespVO.getBankName());
row.add(payslipRespVO.getBankNo()); row.add(payslipRespVO.getBankNo());
row.add(payslipRespVO.getRemark()); row.add(payslipRespVO.getRemark());
row.add(payslipRespVO.getSignURL()); if (payslipRespVO.getIsConfirm() == 1) {
row.add(new URL(signImgPathMap.get(payslipRespVO.getUserId())));
nameList.add(payslipRespVO.getUserName());
}else {
row.add("");
}
data.add(row); data.add(row);
} }
// 获取公司信息 // 获取公司信息
DeptDO deptDO = deptService.getDept(exportReqVO.getDeptId()); DeptDO deptDO = deptService.getDept(exportReqVO.getDeptId());
String headTitle = deptDO.getName() + exportReqVO.getDate() + "工资条"; String headTitle = deptDO.getName() + exportReqVO.getDate() + "工资条";
EasyExcel.write(response.getOutputStream()) EasyExcel.write(response.getOutputStream())
.head(getHead(headTitle, list.get(0))) .head(getHead(headTitle, list.get(0)))
.autoCloseStream(false) .autoCloseStream(false)
.excelType(ExcelTypeEnum.XLS) .excelType(ExcelTypeEnum.XLS)
.registerWriteHandler(new CustomCellStyleHandler()) .registerWriteHandler(new CustomCellStyleHandler(nameList))
.sheet("工资条") .sheet("工资条")
.doWrite(data); .doWrite(data);
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("工资条", StandardCharsets.UTF_8.name())); response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("工资条", StandardCharsets.UTF_8.name()));