feat(oa): 新增工资明细批量插入接口支持字符串格式数字- 新增 BatchInsertSalaryStringDto 和 SalaryDetailStringDto 类用于处理字符串格式的工资数据

- 在 OaSalaryMasterController 中添加新的批量插入接口
- 实现 SalaryStringConverter 工具类用于字符串数据转换
- 优化了前端数据传输格式,支持驼峰命名的字符串数字
This commit is contained in:
2025-09-08 16:31:16 +08:00
parent f37e0eb53a
commit ec1e0be293
4 changed files with 575 additions and 0 deletions

View File

@@ -19,7 +19,9 @@ import com.ruoyi.common.enums.BusinessType;
import com.ruoyi.common.utils.poi.ExcelUtil;
import com.ruoyi.oa.domain.vo.OaSalaryMasterVo;
import com.ruoyi.oa.domain.bo.OaSalaryMasterBo;
import com.ruoyi.oa.domain.dto.BatchInsertSalaryStringDto;
import com.ruoyi.oa.service.IOaSalaryMasterService;
import com.ruoyi.oa.utils.SalaryStringConverter;
import com.ruoyi.common.core.page.TableDataInfo;
/**
@@ -109,4 +111,19 @@ public class OaSalaryMasterController extends BaseController {
return toAjax(iOaSalaryMasterService.batchInsertDetails(bo));
}
/**
* 批量插入工资明细(处理字符串数字格式)
* 专门用于处理前端传递的驼峰命名但数字为字符串格式的数据
*
* @param requestDto 字符串格式的请求DTO
*/
@Log(title = "批量插入工资明细", businessType = BusinessType.INSERT)
@RepeatSubmit()
@PostMapping("/batchInsertDetailsWithStringConvert")
public R<Void> batchInsertDetailsWithStringConvert(@Validated @RequestBody BatchInsertSalaryStringDto requestDto) {
// 将字符串DTO转换为后端BO
OaSalaryMasterBo bo = SalaryStringConverter.convertToMasterBo(requestDto);
return toAjax(iOaSalaryMasterService.batchInsertDetails(bo));
}
}

View File

@@ -0,0 +1,66 @@
package com.ruoyi.oa.domain.dto;
import lombok.Data;
import javax.validation.Valid;
import javax.validation.constraints.NotEmpty;
import javax.validation.constraints.NotBlank;
import java.util.List;
/**
* 批量插入工资明细请求DTO - 处理驼峰命名的字符串数字格式
*
* @author Joshi
* @date 2025-09-08
*/
@Data
public class BatchInsertSalaryStringDto {
/**
* 单位名称
*/
@NotBlank(message = "单位名称不能为空")
private String unitName;
/**
* 工资所属期间格式如2025-8
*/
@NotBlank(message = "工资期间不能为空")
private String salaryPeriod;
/**
* 总经理审批状态或姓名
*/
private String gmApproval;
/**
* 财务审核状态或姓名
*/
private String financeAudit;
/**
* 出纳姓名
*/
private String cashier;
/**
* 部门主管姓名
*/
private String deptManager;
/**
* 经办人姓名
*/
private String operator;
/**
* 备注
*/
private String remark;
/**
* 工资明细列表
*/
@Valid
@NotEmpty(message = "明细列表不能为空")
private List<SalaryDetailStringDto> salaryDetailList;
}

View File

@@ -0,0 +1,377 @@
package com.ruoyi.oa.domain.dto;
import com.fasterxml.jackson.annotation.JsonIgnore;
import lombok.Data;
import java.math.BigDecimal;
/**
* 工资明细字符串DTO - 用于接收前端字符串格式的数字字段
*
* @author Joshi
* @date 2025-09-08
*/
@Data
public class SalaryDetailStringDto {
/**
* 主键,自增
*/
private Long detailId;
/**
* 关联主表ID
*/
private Long mainId;
/**
* 序号(字符串格式)
*/
private String serialNumber;
/**
* 部门
*/
private String dept;
/**
* 员工姓名
*/
private String name;
/**
* 基本工资(字符串格式)
*/
private String basicSalary;
/**
* 岗位工资(字符串格式)
*/
private String postSalary;
/**
* 餐补(字符串格式)
*/
private String mealAllowance;
/**
* 住房补贴(字符串格式)
*/
private String housingAllowance;
/**
* 公交补贴(字符串格式)
*/
private String busAllowance;
/**
* 出差天数(字符串格式)
*/
private String businessDaysOther;
/**
* 出差补助(字符串格式)
*/
private String businessAllowance;
/**
* 社保补助(字符串格式)
*/
private String socialSecurityAllowance;
/**
* 加班时长(字符串格式)
*/
private String overtimeHours;
/**
* 加班标准(字符串格式)
*/
private String overtimeRate;
/**
* 加班工资总计(字符串格式)
*/
private String overtimeTotal;
/**
* 出勤天数(字符串格式)
*/
private String businessDays;
/**
* 请假扣款(字符串格式)
*/
private String leaveDeduction;
/**
* 其他扣款(字符串格式)
*/
private String otherDeduction;
/**
* 应发工资(字符串格式)
*/
private String grossSalary;
/**
* 个人缴纳养老保险(字符串格式)
*/
private String personalPension;
/**
* 个人缴纳医疗保险(字符串格式)
*/
private String personalMedical;
/**
* 个人缴纳失业保险(字符串格式)
*/
private String personalUnemployment;
/**
* 个人缴纳大病医疗(字符串格式)
*/
private String personalBigMedical;
/**
* 个人缴纳住房公积金(字符串格式)
*/
private String personalHousingFund;
/**
* 个人所得税(字符串格式)
*/
private String personalTax;
/**
* 实发工资(字符串格式)
*/
private String netSalary;
/**
* 企业缴纳养老保险(字符串格式)
*/
private String enterprisePension;
/**
* 企业缴纳医疗保险(字符串格式)
*/
private String enterpriseMedical;
/**
* 企业缴纳工伤保险(字符串格式)
*/
private String enterpriseInjury;
/**
* 企业缴纳失业保险(字符串格式)
*/
private String enterpriseUnemployment;
/**
* 企业缴纳生育保险(字符串格式)
*/
private String enterpriseMaternity;
/**
* 企业缴纳住房公积金(字符串格式)
*/
private String enterpriseHousingFund;
/**
* 企业缴纳大病医疗(字符串格式)
*/
private String enterpriseBigMedical;
/**
* 单位总支出(字符串格式)
*/
private String unitTotalExpense;
/**
* 备注
*/
private String remark;
// 辅助方法安全地将字符串转换为BigDecimal
@JsonIgnore
private BigDecimal parseDecimal(String value) {
if (value == null || value.trim().isEmpty()) {
return BigDecimal.ZERO;
}
try {
return new BigDecimal(value.trim());
} catch (NumberFormatException e) {
return BigDecimal.ZERO;
}
}
// 辅助方法安全地将字符串转换为Long
@JsonIgnore
private Long parseLong(String value) {
if (value == null || value.trim().isEmpty()) {
return 0L;
}
try {
return Long.parseLong(value.trim());
} catch (NumberFormatException e) {
return 0L;
}
}
// 转换方法,返回数值类型
@JsonIgnore
public Long getSerialNumberAsLong() {
return parseLong(serialNumber);
}
@JsonIgnore
public BigDecimal getBasicSalaryAsDecimal() {
return parseDecimal(basicSalary);
}
@JsonIgnore
public BigDecimal getPostSalaryAsDecimal() {
return parseDecimal(postSalary);
}
@JsonIgnore
public BigDecimal getMealAllowanceAsDecimal() {
return parseDecimal(mealAllowance);
}
@JsonIgnore
public BigDecimal getHousingAllowanceAsDecimal() {
return parseDecimal(housingAllowance);
}
@JsonIgnore
public BigDecimal getBusAllowanceAsDecimal() {
return parseDecimal(busAllowance);
}
@JsonIgnore
public Long getBusinessDaysOtherAsLong() {
return parseLong(businessDaysOther);
}
@JsonIgnore
public BigDecimal getBusinessAllowanceAsDecimal() {
return parseDecimal(businessAllowance);
}
@JsonIgnore
public BigDecimal getSocialSecurityAllowanceAsDecimal() {
return parseDecimal(socialSecurityAllowance);
}
@JsonIgnore
public BigDecimal getOvertimeHoursAsDecimal() {
return parseDecimal(overtimeHours);
}
@JsonIgnore
public BigDecimal getOvertimeRateAsDecimal() {
return parseDecimal(overtimeRate);
}
@JsonIgnore
public BigDecimal getOvertimeTotalAsDecimal() {
return parseDecimal(overtimeTotal);
}
@JsonIgnore
public Long getBusinessDaysAsLong() {
return parseLong(businessDays);
}
@JsonIgnore
public BigDecimal getLeaveDeductionAsDecimal() {
return parseDecimal(leaveDeduction);
}
@JsonIgnore
public BigDecimal getOtherDeductionAsDecimal() {
return parseDecimal(otherDeduction);
}
@JsonIgnore
public BigDecimal getGrossSalaryAsDecimal() {
return parseDecimal(grossSalary);
}
@JsonIgnore
public BigDecimal getPersonalPensionAsDecimal() {
return parseDecimal(personalPension);
}
@JsonIgnore
public BigDecimal getPersonalMedicalAsDecimal() {
return parseDecimal(personalMedical);
}
@JsonIgnore
public BigDecimal getPersonalUnemploymentAsDecimal() {
return parseDecimal(personalUnemployment);
}
@JsonIgnore
public BigDecimal getPersonalBigMedicalAsDecimal() {
return parseDecimal(personalBigMedical);
}
@JsonIgnore
public BigDecimal getPersonalHousingFundAsDecimal() {
return parseDecimal(personalHousingFund);
}
@JsonIgnore
public BigDecimal getPersonalTaxAsDecimal() {
return parseDecimal(personalTax);
}
@JsonIgnore
public BigDecimal getNetSalaryAsDecimal() {
return parseDecimal(netSalary);
}
@JsonIgnore
public BigDecimal getEnterprisePensionAsDecimal() {
return parseDecimal(enterprisePension);
}
@JsonIgnore
public BigDecimal getEnterpriseMedicalAsDecimal() {
return parseDecimal(enterpriseMedical);
}
@JsonIgnore
public BigDecimal getEnterpriseInjuryAsDecimal() {
return parseDecimal(enterpriseInjury);
}
@JsonIgnore
public BigDecimal getEnterpriseUnemploymentAsDecimal() {
return parseDecimal(enterpriseUnemployment);
}
@JsonIgnore
public BigDecimal getEnterpriseMaternityAsDecimal() {
return parseDecimal(enterpriseMaternity);
}
@JsonIgnore
public BigDecimal getEnterpriseHousingFundAsDecimal() {
return parseDecimal(enterpriseHousingFund);
}
@JsonIgnore
public BigDecimal getEnterpriseBigMedicalAsDecimal() {
return parseDecimal(enterpriseBigMedical);
}
@JsonIgnore
public BigDecimal getUnitTotalExpenseAsDecimal() {
return parseDecimal(unitTotalExpense);
}
}

View File

@@ -0,0 +1,115 @@
package com.ruoyi.oa.utils;
import com.ruoyi.oa.domain.bo.OaSalaryDetailBo;
import com.ruoyi.oa.domain.bo.OaSalaryMasterBo;
import com.ruoyi.oa.domain.dto.BatchInsertSalaryStringDto;
import com.ruoyi.oa.domain.dto.SalaryDetailStringDto;
import cn.hutool.core.collection.CollUtil;
import java.util.List;
import java.util.stream.Collectors;
/**
* 工资字符串数据转换工具类
*
* @author Joshi
* @date 2025-09-08
*/
public class SalaryStringConverter {
/**
* 将字符串格式的请求DTO转换为后端BO对象
*
* @param requestDto 字符串格式的请求DTO
* @return 后端BO对象
*/
public static OaSalaryMasterBo convertToMasterBo(BatchInsertSalaryStringDto requestDto) {
if (requestDto == null) {
return null;
}
OaSalaryMasterBo masterBo = new OaSalaryMasterBo();
masterBo.setUnitName(requestDto.getUnitName());
masterBo.setSalaryPeriod(requestDto.getSalaryPeriod());
masterBo.setGmApproval(requestDto.getGmApproval());
masterBo.setFinanceAudit(requestDto.getFinanceAudit());
masterBo.setCashier(requestDto.getCashier());
masterBo.setDeptManager(requestDto.getDeptManager());
masterBo.setOperator(requestDto.getOperator());
masterBo.setRemark(requestDto.getRemark());
// 转换明细列表
if (CollUtil.isNotEmpty(requestDto.getSalaryDetailList())) {
List<OaSalaryDetailBo> detailBoList = requestDto.getSalaryDetailList().stream()
.map(SalaryStringConverter::convertToDetailBo)
.collect(Collectors.toList());
masterBo.setSalaryDetailList(detailBoList);
}
return masterBo;
}
/**
* 将字符串格式的明细DTO转换为后端明细BO对象
*
* @param stringDto 字符串格式的明细DTO
* @return 后端明细BO对象
*/
public static OaSalaryDetailBo convertToDetailBo(SalaryDetailStringDto stringDto) {
if (stringDto == null) {
return null;
}
OaSalaryDetailBo detailBo = new OaSalaryDetailBo();
// 基本信息
detailBo.setDetailId(stringDto.getDetailId());
detailBo.setMainId(stringDto.getMainId());
detailBo.setSerialNumber(stringDto.getSerialNumberAsLong());
detailBo.setDept(stringDto.getDept());
detailBo.setName(stringDto.getName());
detailBo.setRemark(stringDto.getRemark());
// 工资项目 - 使用转换方法
detailBo.setBasicSalary(stringDto.getBasicSalaryAsDecimal());
detailBo.setPostSalary(stringDto.getPostSalaryAsDecimal());
detailBo.setMealAllowance(stringDto.getMealAllowanceAsDecimal());
detailBo.setHousingAllowance(stringDto.getHousingAllowanceAsDecimal());
detailBo.setBusAllowance(stringDto.getBusAllowanceAsDecimal());
detailBo.setBusinessDaysOther(stringDto.getBusinessDaysOtherAsLong());
detailBo.setBusinessAllowance(stringDto.getBusinessAllowanceAsDecimal());
detailBo.setSocialSecurityAllowance(stringDto.getSocialSecurityAllowanceAsDecimal());
// 加班相关
detailBo.setOvertimeHours(stringDto.getOvertimeHoursAsDecimal());
detailBo.setOvertimeRate(stringDto.getOvertimeRateAsDecimal());
detailBo.setOvertimeTotal(stringDto.getOvertimeTotalAsDecimal());
detailBo.setBusinessDays(stringDto.getBusinessDaysAsLong());
// 扣款项目
detailBo.setLeaveDeduction(stringDto.getLeaveDeductionAsDecimal());
detailBo.setOtherDeduction(stringDto.getOtherDeductionAsDecimal());
detailBo.setGrossSalary(stringDto.getGrossSalaryAsDecimal());
// 个人缴费
detailBo.setPersonalPension(stringDto.getPersonalPensionAsDecimal());
detailBo.setPersonalMedical(stringDto.getPersonalMedicalAsDecimal());
detailBo.setPersonalUnemployment(stringDto.getPersonalUnemploymentAsDecimal());
detailBo.setPersonalBigMedical(stringDto.getPersonalBigMedicalAsDecimal());
detailBo.setPersonalHousingFund(stringDto.getPersonalHousingFundAsDecimal());
detailBo.setPersonalTax(stringDto.getPersonalTaxAsDecimal());
detailBo.setNetSalary(stringDto.getNetSalaryAsDecimal());
// 企业缴费
detailBo.setEnterprisePension(stringDto.getEnterprisePensionAsDecimal());
detailBo.setEnterpriseMedical(stringDto.getEnterpriseMedicalAsDecimal());
detailBo.setEnterpriseInjury(stringDto.getEnterpriseInjuryAsDecimal());
detailBo.setEnterpriseUnemployment(stringDto.getEnterpriseUnemploymentAsDecimal());
detailBo.setEnterpriseMaternity(stringDto.getEnterpriseMaternityAsDecimal());
detailBo.setEnterpriseHousingFund(stringDto.getEnterpriseHousingFundAsDecimal());
detailBo.setEnterpriseBigMedical(stringDto.getEnterpriseBigMedicalAsDecimal());
detailBo.setUnitTotalExpense(stringDto.getUnitTotalExpenseAsDecimal());
return detailBo;
}
}