package com.ruoyi.staff.service.impl; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; import com.ruoyi.common.utils.StringUtils; import com.ruoyi.framework.web.domain.AjaxResult; import com.ruoyi.production.bean.dto.UserAccountDto; import com.ruoyi.production.bean.dto.UserProductionAccountingDto; import com.ruoyi.production.mapper.ProductionProductMainMapper; import com.ruoyi.production.service.SalesLedgerProductionAccountingService; import com.ruoyi.project.system.domain.SysDept; import com.ruoyi.project.system.domain.SysUser; import com.ruoyi.project.system.mapper.SysDeptMapper; import com.ruoyi.project.system.mapper.SysUserMapper; import com.ruoyi.staff.controller.TaxCalculator; import com.ruoyi.staff.mapper.SchemeApplicableStaffMapper; import com.ruoyi.staff.mapper.SchemeInsuranceDetailMapper; import com.ruoyi.staff.mapper.StaffOnJobMapper; import com.ruoyi.staff.pojo.SchemeApplicableStaff; import com.ruoyi.staff.pojo.SchemeInsuranceDetail; import com.ruoyi.staff.pojo.StaffOnJob; import com.ruoyi.staff.service.SchemeApplicableStaffService; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.util.CollectionUtils; import java.math.BigDecimal; import java.math.RoundingMode; import java.time.DayOfWeek; import java.time.YearMonth; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.stream.Collectors; import java.time.LocalDate; /** *

* 社保方案适用人员表 服务实现类 *

* * @author 芯导软件(江苏)有限公司 * @since 2026-03-05 11:50:17 */ @Service @RequiredArgsConstructor public class SchemeApplicableStaffServiceImpl extends ServiceImpl implements SchemeApplicableStaffService { private final SchemeApplicableStaffMapper schemeApplicableStaffMapper; private final SchemeInsuranceDetailMapper schemeInsuranceDetailMapper; private final SysUserMapper sysUserMapper; private final SysDeptMapper sysDeptMapper; private final StaffOnJobMapper staffOnJobMapper; private final SalesLedgerProductionAccountingService salesLedgerProductionAccountingService; private final ProductionProductMainMapper productionProductMainMapper; @Override public AjaxResult listPage(Page page, SchemeApplicableStaff schemeApplicableStaff) { LambdaQueryWrapper schemeApplicableStaffLambdaQueryWrapper = new LambdaQueryWrapper<>(); if(schemeApplicableStaff != null){ if(StringUtils.isNotEmpty(schemeApplicableStaff.getTitle())){ schemeApplicableStaffLambdaQueryWrapper.like(SchemeApplicableStaff::getTitle, schemeApplicableStaff.getTitle()); } } schemeApplicableStaffLambdaQueryWrapper.orderByDesc(SchemeApplicableStaff::getId); Page page1 = schemeApplicableStaffMapper.selectPage(page, schemeApplicableStaffLambdaQueryWrapper); List collect = page1.getRecords().stream().map(SchemeApplicableStaff::getId).collect(Collectors.toList()); if(CollectionUtils.isEmpty(collect)){ return AjaxResult.success(page1); } List schemeInsuranceDetails = schemeInsuranceDetailMapper .selectList(new LambdaQueryWrapper() .in(SchemeInsuranceDetail::getSchemeId, collect)); page1.getRecords().forEach(item -> { item.setSchemeInsuranceDetailList(schemeInsuranceDetails .stream() .filter(detail -> detail.getSchemeId().equals(item.getId())) .collect(Collectors.toList())); SysUser sysUser = sysUserMapper.selectUserById(item.getCreateUser().longValue()); item.setCreateUserName(sysUser == null ? "未知" : sysUser.getNickName()); // 获取部门信息 String[] split = item.getDeptIds().split(","); List sysDepts = sysDeptMapper.selectList(new LambdaQueryWrapper() .in(SysDept::getDeptId, Arrays.stream(split).map(Long::valueOf).collect(Collectors.toList()))); if(!CollectionUtils.isEmpty(sysDepts)){ item.setDeptNames(sysDepts.stream().map(SysDept::getDeptName).collect(Collectors.joining(","))); } }); return AjaxResult.success(page1); } public void setSchemeApplicableStaffUserInfo(SchemeApplicableStaff schemeApplicableStaff) { // 通过部门获取人员id String[] split = schemeApplicableStaff.getDeptIds().split(","); List staffOnJobs = staffOnJobMapper.selectList(new LambdaQueryWrapper() .in(StaffOnJob::getSysDeptId, Arrays.stream(split).map(Long::valueOf).collect(Collectors.toList()))); if(CollectionUtils.isEmpty(staffOnJobs)){ throw new IllegalArgumentException("部门下无员工"); } schemeApplicableStaff.setStaffIds(staffOnJobs .stream() .map(StaffOnJob::getId) .filter(Objects::nonNull) // 过滤掉 null 值 .map(String::valueOf) .collect(Collectors.joining( ","))); schemeApplicableStaff.setStaffNames(staffOnJobs.stream().map(StaffOnJob::getStaffName).collect(Collectors.joining(","))); } @Override public AjaxResult add(SchemeApplicableStaff schemeApplicableStaff) { if(schemeApplicableStaff == null){ return AjaxResult.error("参数错误"); } if(CollectionUtils.isEmpty(schemeApplicableStaff.getSchemeInsuranceDetailList())){ return AjaxResult.error("请选择方案明细"); } setSchemeApplicableStaffUserInfo(schemeApplicableStaff); //根据部门设置用户信息 int insert = schemeApplicableStaffMapper.insert(schemeApplicableStaff); schemeApplicableStaff.getSchemeInsuranceDetailList().forEach(item -> { item.setSchemeId(schemeApplicableStaff.getId()); schemeInsuranceDetailMapper.insert(item); }); return AjaxResult.success(insert); } @Override public AjaxResult updateSchemeApplicableStaff(SchemeApplicableStaff schemeApplicableStaff) { if(schemeApplicableStaff == null){ return AjaxResult.error("参数错误"); } setSchemeApplicableStaffUserInfo(schemeApplicableStaff); //根据部门设置用户信息 int update = schemeApplicableStaffMapper.updateById(schemeApplicableStaff); // 先删,重新绑定 schemeInsuranceDetailMapper.delete(new LambdaQueryWrapper() .eq(SchemeInsuranceDetail::getSchemeId, schemeApplicableStaff.getId())); schemeApplicableStaff.getSchemeInsuranceDetailList().forEach(item -> { item.setSchemeId(schemeApplicableStaff.getId()); schemeInsuranceDetailMapper.insert(item); }); return AjaxResult.success(update); } @Override public AjaxResult delete(List ids) { if (CollectionUtils.isEmpty(ids)) { return AjaxResult.error("参数错误"); } int delete = schemeApplicableStaffMapper.deleteBatchIds(ids); schemeInsuranceDetailMapper.delete(new LambdaQueryWrapper() .in(SchemeInsuranceDetail::getSchemeId, ids)); return AjaxResult.success(delete); } /** * 通过员工id计算社保方案 * @param id */ public void calculateByEmployeeId(Integer id,Map map,String date) { if (id == null || map == null) { return; } Long staffId = id.longValue(); StaffOnJob staffOnJobDto = staffOnJobMapper.selectById(staffId); if(staffOnJobDto == null){ return; } BigDecimal basicSalary = resolveBasicSalary(map, staffOnJobDto); BigDecimal attendanceBonus = resolveAttendanceBonus(staffOnJobDto); BigDecimal otherIncome = normalizeMoney(getBigDecimal(map, "otherIncome")); BigDecimal otherDeduct = normalizeMoney(getBigDecimal(map, "otherDeduct")); BigDecimal socialSupplementAmount = resolveSocialSupplementAmount(map, staffOnJobDto); BigDecimal socialPersonal = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); BigDecimal fundPersonal = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); int monthWorkDays = calculateWorkDays(date); BigDecimal standardHours = calculateStandardHours(monthWorkDays); BigDecimal actualOutput = resolveActualOutput(map); BigDecimal standardQuantity = resolveStandardQuantity(map); BigDecimal conversionFactor = resolveConversionFactor(map); Long sysUserId = getUidByStaffId(staffId); UserProductionAccountingDto userProductionAccountingDto = new UserProductionAccountingDto(); userProductionAccountingDto.setUserId(sysUserId); userProductionAccountingDto.setDate(date); UserAccountDto byUserId = salesLedgerProductionAccountingService.getByUserId(userProductionAccountingDto); BigDecimal repairWorkHour = resolveRepairWorkHour(sysUserId, date); BigDecimal convertedWorkHours = resolveConvertedWorkHours( map, actualOutput, standardQuantity, conversionFactor, repairWorkHour, byUserId ); BigDecimal conversionRatio = calculateConversionRatio(standardHours, convertedWorkHours); // 查询该人员对应的社保方案 List schemeList = schemeApplicableStaffMapper.selectSchemeByStaffId(staffId); if (!CollectionUtils.isEmpty(schemeList)) { for (SchemeApplicableStaff scheme : schemeList) { List detailList = schemeApplicableStaffMapper.selectDetailBySchemeId(scheme.getId()); if(CollectionUtils.isEmpty(detailList)){ continue; } for (SchemeInsuranceDetail detail : detailList) { if("公积金".equals(detail.getInsuranceType())){ fundPersonal = calculateByEmployeeIdType(detail.getInsuranceType(), fundPersonal, basicSalary, staffOnJobDto, detail); }else{ socialPersonal = calculateByEmployeeIdType(detail.getInsuranceType(), socialPersonal, basicSalary, staffOnJobDto, detail); } } } } // 工资 = 全勤奖金 + 基本工资 * 折合倍数 BigDecimal grossSalary = attendanceBonus.add(basicSalary.multiply(conversionRatio)).add(otherIncome); BigDecimal salaryTax = TaxCalculator.calculateMonthlyTax( grossSalary, socialPersonal.add(socialSupplementAmount), fundPersonal ); BigDecimal deductSalary = salaryTax .add(fundPersonal) .add(socialPersonal) .add(socialSupplementAmount) .add(otherDeduct); BigDecimal netSalary = grossSalary.subtract(deductSalary); if (!hasText(map, "staffName")) { map.put("staffName", staffOnJobDto.getStaffName()); } if (!hasText(map, "nation")) { map.put("nation", staffOnJobDto.getNation()); } map.put("basicSalary", basicSalary); map.put("attendanceBonus", attendanceBonus); map.put("monthWorkDays", monthWorkDays); map.put("standardHours", standardHours); map.put("actualOutput", actualOutput); map.put("standardQuantity", standardQuantity); map.put("conversionFactor", conversionFactor); map.put("convertedWorkHours", convertedWorkHours); map.put("monthTimeTotal", convertedWorkHours); map.put("conversionRatio", conversionRatio); map.put("pieceSalary", convertedWorkHours); map.put("hourlySalary", BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP)); map.put("otherIncome", otherIncome); map.put("socialPersonal", socialPersonal); map.put("fundPersonal", fundPersonal); map.put("otherDeduct", otherDeduct); map.put("socialSupplementAmount", socialSupplementAmount); map.put("grossSalary", grossSalary); map.put("salaryTax", salaryTax); map.put("deductSalary", deductSalary); map.put("netSalary", netSalary); } /** * 解析员工基础工资。 * 优先使用工资表里本次传入的值,未传时回落到员工档案中的基础工资。 */ private BigDecimal resolveBasicSalary(Map map, StaffOnJob staffOnJobDto) { // 员工基础工资优先取工资表传入值,没有则回落到员工档案中的基础工资。 return hasValue(map, "basicSalary") ? normalizeMoney(getBigDecimal(map, "basicSalary")) : normalizeMoney(staffOnJobDto.getBasicSalary()); } /** * 获取员工最新全勤奖金。 * 全勤奖金统一以员工档案配置为准。 */ private BigDecimal resolveAttendanceBonus(StaffOnJob staffOnJobDto) { // 全勤奖金直接取员工档案里的最新配置。 return staffOnJobDto.getAttendanceBonus() == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : normalizeMoney(staffOnJobDto.getAttendanceBonus()); } /** * 解析社保补缴金额。 * 如果工资表传入了补缴金额则直接使用,否则按员工所属部门最新方案计算。 */ private BigDecimal resolveSocialSupplementAmount(Map map, StaffOnJob staffOnJobDto) { return hasValue(map, "socialSupplementAmount") ? normalizeMoney(getBigDecimal(map, "socialSupplementAmount")) : calculateLatestDeptSocialSupplementAmount(staffOnJobDto); } /** * 计算月标准工时。 * 规则:工作日天数 * 8 小时。 */ private BigDecimal calculateStandardHours(int monthWorkDays) { // 月标准工时 = 工作日天数 * 8 小时。 return BigDecimal.valueOf(monthWorkDays) .multiply(new BigDecimal("8")) .setScale(2, RoundingMode.HALF_UP); } /** * 解析当月实际产量。 */ private BigDecimal resolveActualOutput(Map map) { return hasValue(map, "actualOutput") ? normalizeMoney(getBigDecimal(map, "actualOutput")) : BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); } /** * 解析工序标准数量。 */ private BigDecimal resolveStandardQuantity(Map map) { return hasValue(map, "standardQuantity") ? normalizeMoney(getBigDecimal(map, "standardQuantity")) : BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); } /** * 解析工序折算系数,未配置时默认 1。 */ private BigDecimal resolveConversionFactor(Map map) { return hasValue(map, "conversionFactor") ? normalizeMoney(getBigDecimal(map, "conversionFactor")) : BigDecimal.ONE.setScale(2, RoundingMode.HALF_UP); } /** * 汇总轻微返工的补修理工时。 * 按系统用户 ID 和工资月份统计,并作为月时间合计的一部分。 */ private BigDecimal resolveRepairWorkHour(Long sysUserId, String date) { if (sysUserId == null || sysUserId <= 0 || StringUtils.isEmpty(date)) { return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); } LocalDate monthStart = YearMonth.parse(date).atDay(1); LocalDate monthEnd = YearMonth.parse(date).atEndOfMonth(); return productionProductMainMapper.selectList(new LambdaQueryWrapper() .eq(com.ruoyi.production.pojo.ProductionProductMain::getUserId, sysUserId) .eq(com.ruoyi.production.pojo.ProductionProductMain::getReportType, 1) .ge(com.ruoyi.production.pojo.ProductionProductMain::getCreateTime, monthStart.atStartOfDay()) .le(com.ruoyi.production.pojo.ProductionProductMain::getCreateTime, monthEnd.atTime(23, 59, 59))) .stream() .map(item -> normalizeMoney(item.getRepairWorkHour())) .reduce(BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP), BigDecimal::add) .setScale(2, RoundingMode.HALF_UP); } /** * 计算月时间合计。 * 计件场景使用实际产量、标准数量和折算系数换算; * 没有计件数据时,回落到生产核算汇总值; * 最后再叠加返工补修理工时。 */ private BigDecimal resolveConvertedWorkHours(Map map, BigDecimal actualOutput, BigDecimal standardQuantity, BigDecimal conversionFactor, BigDecimal repairWorkHour, UserAccountDto byUserId) { if (hasValue(map, "monthTimeTotal")) { BigDecimal monthTimeTotal = normalizeMoney(getBigDecimal(map, "monthTimeTotal")); if (monthTimeTotal.compareTo(BigDecimal.ZERO) > 0) { return monthTimeTotal; } } BigDecimal convertedWorkHours = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); // 计件折算工时 = 当月实际产量 / 工序标准数量 * 8 * 工序折算系数 if (actualOutput.compareTo(BigDecimal.ZERO) > 0 && standardQuantity.compareTo(BigDecimal.ZERO) > 0) { convertedWorkHours = actualOutput .divide(standardQuantity, 8, RoundingMode.HALF_UP) .multiply(new BigDecimal("8")) .multiply(conversionFactor) .setScale(2, RoundingMode.HALF_UP); } else if (byUserId != null) { convertedWorkHours = normalizeMoney(byUserId.getAccountBalance().add(byUserId.getAccount())); } return convertedWorkHours.add(repairWorkHour).setScale(2, RoundingMode.HALF_UP); } /** * 计算折合倍数。 * 规则:月时间合计 / 月标准工时。 */ private BigDecimal calculateConversionRatio(BigDecimal standardHours, BigDecimal convertedWorkHours) { return standardHours.compareTo(BigDecimal.ZERO) == 0 ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : convertedWorkHours.divide(standardHours, 8, RoundingMode.HALF_UP) .setScale(2, RoundingMode.HALF_UP); } /** * 通过员工Id获取用户id * @param staffId * @return */ public Long getUidByStaffId(Long staffId){ StaffOnJob staffOnJob = staffOnJobMapper.selectById(staffId); if(staffOnJob == null){ return -1L; // 返回不存在Id } SysUser sysUser = sysUserMapper.selectOne(new LambdaQueryWrapper() .eq(SysUser::getUserName, staffOnJob.getStaffNo()) .eq(SysUser::getDelFlag, "0") .last("limit 1")); if(sysUser == null){ return -1L; // 返回不存在Id } return sysUser.getUserId(); } /** * 计算 * @param type * @param bigDecimal * @param staffOnJobDto * @param detail * @return */ public BigDecimal calculateByEmployeeIdType(String type,BigDecimal bigDecimal, BigDecimal basicSalary, StaffOnJob staffOnJobDto,SchemeInsuranceDetail detail) { // 判断是否调用基本工资 if (detail.getUseBasicSalary() == 1) { BigDecimal divide = detail.getPaymentBase().multiply(detail.getPersonalRatio()).divide(new BigDecimal("100"), 2); bigDecimal = bigDecimal.add(divide); }else{ // 调用当前工资表中的基本工资 BigDecimal multiply = basicSalary.multiply(detail.getPersonalRatio().divide(new BigDecimal("100"), 2)); bigDecimal = bigDecimal.add(multiply); } bigDecimal = bigDecimal.add(detail.getPersonalFixed()); return bigDecimal; } private BigDecimal calculateLatestDeptSocialSupplementAmount(StaffOnJob staffOnJobDto) { if (staffOnJobDto == null || staffOnJobDto.getSysDeptId() == null) { return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); } SchemeApplicableStaff latestScheme = schemeApplicableStaffMapper.selectLatestSchemeByDeptId(staffOnJobDto.getSysDeptId()); if (latestScheme == null) { return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); } List detailList = schemeApplicableStaffMapper.selectDetailBySchemeId(latestScheme.getId()); if (CollectionUtils.isEmpty(detailList)) { return BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); } BigDecimal socialSupplementAmount = BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP); for (SchemeInsuranceDetail detail : detailList) { if (!"公积金".equals(detail.getInsuranceType())) { socialSupplementAmount = calculateByEmployeeIdType( detail.getInsuranceType(), socialSupplementAmount, normalizeMoney(staffOnJobDto.getBasicSalary()), staffOnJobDto, detail ); } } return socialSupplementAmount.setScale(2, RoundingMode.HALF_UP); } private boolean hasValue(Map map, String key) { return map != null && map.get(key) != null; } private boolean hasText(Map map, String key) { if (map == null) { return false; } Object value = map.get(key); return value != null && !String.valueOf(value).trim().isEmpty(); } private BigDecimal getBigDecimal(Map map, String key) { if (map == null) { return null; } Object value = map.get(key); if (value == null) { return null; } if (value instanceof BigDecimal) { return (BigDecimal) value; } if (value instanceof Number) { return new BigDecimal(String.valueOf(value)); } String text = String.valueOf(value).trim(); if (text.isEmpty()) { return null; } return new BigDecimal(text); } private BigDecimal normalizeMoney(BigDecimal value) { return value == null ? BigDecimal.ZERO.setScale(2, RoundingMode.HALF_UP) : value.setScale(2, RoundingMode.HALF_UP); } private int calculateWorkDays(String date) { if (StringUtils.isEmpty(date)) { return 0; } YearMonth yearMonth = YearMonth.parse(date); int workDays = 0; for (int day = 1; day <= yearMonth.lengthOfMonth(); day++) { DayOfWeek dayOfWeek = yearMonth.atDay(day).getDayOfWeek(); if (dayOfWeek != DayOfWeek.SATURDAY && dayOfWeek != DayOfWeek.SUNDAY) { workDays++; } } return workDays; } }