新增缴费导入名单
This commit is contained in:
+36
-6
@@ -19,6 +19,7 @@ import io.v.nutz.web.commons.utils.ShiroUtil;
|
||||
import io.v.nutz.zhgh.memberCost.model.MemberPaymentInfo;
|
||||
import io.v.nutz.zhgh.memberCost.service.MemberPaymentService;
|
||||
import io.v.nutz.zhgh.memberCost.template.MemberPaymentTemp;
|
||||
import io.v.nutz.zhgh.memberCost.template.MemberPaymentUserImportTemp;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||
@@ -228,7 +229,7 @@ public class MemberPaymentSummaryController {
|
||||
List<MemberPaymentTemp> errorInfos = new ArrayList<>();
|
||||
List<MemberPaymentInfo> needInsertList = new ArrayList<>();
|
||||
|
||||
//缴费比例
|
||||
// 原有导入逻辑:按导入的缴费基数更新已存在名单中的缴费信息。
|
||||
BigDecimal paymentProportion = new BigDecimal("0.005");
|
||||
memberPaymentTempList.forEach(v -> {
|
||||
if (infoMap.containsKey(v.getLoginname().trim())) {
|
||||
@@ -238,14 +239,12 @@ public class MemberPaymentSummaryController {
|
||||
memberPaymentInfo.setPaymentBaseOne(v.getPaymentBaseOne());
|
||||
memberPaymentInfo.setPaymentBaseTwo(v.getPaymentBaseTwo());
|
||||
|
||||
//缴费金额,精度计算
|
||||
DecimalFormat df = new DecimalFormat("#0.000");
|
||||
BigDecimal baseOne = new BigDecimal(v.getPaymentBaseOne());
|
||||
BigDecimal baseTwo = new BigDecimal(v.getPaymentBaseTwo());
|
||||
|
||||
String paymentMoney = df.format((baseOne.add(baseTwo)).multiply(paymentProportion));
|
||||
memberPaymentInfo.setPaymentMoney(paymentMoney);
|
||||
// memberPaymentInfo.setPaymentTime(v.getPaymentTime() != null ? v.getPaymentTime() : new Date());
|
||||
memberPaymentInfo.setRemark(v.getRemark());
|
||||
memberPaymentInfo.setPaymentMethod("导入");
|
||||
needInsertList.add(memberPaymentInfo);
|
||||
@@ -268,9 +267,10 @@ public class MemberPaymentSummaryController {
|
||||
nutMap.setv("totalCount", memberPaymentTempList.size());
|
||||
nutMap.setv("successCount", i);
|
||||
nutMap.setv("errorCount", errorInfos.size());
|
||||
nutMap.setv("errorList", errorInfos.stream().map(v -> {
|
||||
return NutMap.NEW().addv("工号", v.getLoginname()).addv("姓名", v.getUsername()).addv("错误原因", v.getResult());
|
||||
}).collect(Collectors.toList()));
|
||||
nutMap.setv("errorList", errorInfos.stream().map(v -> NutMap.NEW()
|
||||
.addv("工号", v.getLoginname())
|
||||
.addv("姓名", v.getUsername())
|
||||
.addv("错误原因", v.getResult())).collect(Collectors.toList()));
|
||||
return Result.success(nutMap);
|
||||
}
|
||||
return Result.success();
|
||||
@@ -280,6 +280,36 @@ public class MemberPaymentSummaryController {
|
||||
}
|
||||
}
|
||||
|
||||
@At
|
||||
@Ok("void")
|
||||
@RequiresPermissions("member.payment.summary")
|
||||
public void downloadUserImportTemp(HttpServletResponse response) {
|
||||
try {
|
||||
ViTool.excelResponse(response, "缴费人员导入模版.xlsx");
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
Workbook sheets = ExcelExportUtil.exportExcel(exportParams, MemberPaymentUserImportTemp.class, new ArrayList<>());
|
||||
ServletOutputStream outputStream = response.getOutputStream();
|
||||
sheets.write(outputStream);
|
||||
outputStream.close();
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@At
|
||||
@RequiresPermissions("member.payment.summary")
|
||||
@AdaptBy(type = UploadAdaptor.class, args = {"ioc:fileUpload"})
|
||||
public Object doUserImport(TempFile file, String projectId, @Param(value = "isFlag", required = false) Boolean isFlag) {
|
||||
try {
|
||||
List<MemberPaymentUserImportTemp> importList = ExcelImportUtil.importExcel(file.getFile(), MemberPaymentUserImportTemp.class, new ImportParams());
|
||||
return memberPaymentService.importPaymentUsers(projectId, importList, Boolean.TRUE.equals(isFlag));
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return Result.error();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@At
|
||||
@Ok("void")
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package io.v.nutz.zhgh.memberCost.service;
|
||||
|
||||
import io.v.nutz.base.service.ViService;
|
||||
import io.v.nutz.base.result.Result;
|
||||
import io.v.nutz.zhgh.memberCost.model.MemberPaymentConfig;
|
||||
import io.v.nutz.zhgh.memberCost.model.MemberPaymentInfo;
|
||||
import io.v.nutz.zhgh.memberCost.model.MemberPaymentProject;
|
||||
import io.v.nutz.zhgh.memberCost.template.MemberPaymentUserImportTemp;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
@@ -21,4 +23,14 @@ public interface MemberPaymentService extends ViService<MemberPaymentInfo> {
|
||||
List<MemberPaymentConfig> getPaymentConfigList();
|
||||
|
||||
MemberPaymentConfig getConfigById(String configId);
|
||||
|
||||
/**
|
||||
* 导入缴费人员到缴费名单,并根据缴费配置生成缴费金额。
|
||||
*
|
||||
* @param projectId 缴费项目id
|
||||
* @param importList 导入名单
|
||||
* @param isClearImport 是否清空当前项目原有名单后再导入
|
||||
* @return 导入结果
|
||||
*/
|
||||
Result importPaymentUsers(String projectId, List<MemberPaymentUserImportTemp> importList, boolean isClearImport);
|
||||
}
|
||||
|
||||
@@ -1,28 +1,51 @@
|
||||
package io.v.nutz.zhgh.memberCost.service.impl;
|
||||
|
||||
import cn.afterturn.easypoi.excel.ExcelExportUtil;
|
||||
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import cn.afterturn.easypoi.excel.entity.ExportParams;
|
||||
import cn.afterturn.easypoi.excel.entity.enmus.ExcelType;
|
||||
import cn.afterturn.easypoi.excel.entity.params.ExcelExportEntity;
|
||||
import cn.hutool.core.date.DateTime;
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import io.v.nutz.base.result.Result;
|
||||
import io.v.nutz.base.service.impl.ViServiceImpl;
|
||||
import io.v.nutz.sys.models.Sys_user;
|
||||
import io.v.nutz.zhgh.memberCost.model.MemberPaymentConfig;
|
||||
import io.v.nutz.zhgh.memberCost.model.MemberPaymentInfo;
|
||||
import io.v.nutz.zhgh.memberCost.model.MemberPaymentProject;
|
||||
import io.v.nutz.zhgh.memberCost.service.MemberPaymentService;
|
||||
import io.v.nutz.zhgh.memberCost.template.MemberPaymentUserImportTemp;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.Lang;
|
||||
import org.nutz.lang.Strings;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
import org.nutz.trans.Atom;
|
||||
import org.nutz.trans.Trans;
|
||||
|
||||
import java.io.OutputStream;
|
||||
import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.math.RoundingMode;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@IocBean(args = {"refer:dao"})
|
||||
public class MemberPaymentServiceImpl extends ViServiceImpl<MemberPaymentInfo> implements MemberPaymentService {
|
||||
private static final BigDecimal MEMBER_PAYMENT_RATE = new BigDecimal("0.005");
|
||||
|
||||
public MemberPaymentServiceImpl(Dao dao) {
|
||||
super(dao);
|
||||
}
|
||||
@@ -72,4 +95,321 @@ public class MemberPaymentServiceImpl extends ViServiceImpl<MemberPaymentInfo> i
|
||||
public MemberPaymentConfig getConfigById(String configId) {
|
||||
return dao().fetch(MemberPaymentConfig.class,configId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Result importPaymentUsers(String projectId, List<MemberPaymentUserImportTemp> importList, boolean isClearImport) {
|
||||
if (Strings.isBlank(projectId)) {
|
||||
return Result.error("请先选择缴费项目后再导入!");
|
||||
}
|
||||
if (Lang.isEmpty(importList)) {
|
||||
return Result.error("导入文件中未读取到有效数据!");
|
||||
}
|
||||
MemberPaymentProject project = dao().fetch(MemberPaymentProject.class, projectId);
|
||||
if (project == null) {
|
||||
return Result.error("未找到对应的缴费项目!");
|
||||
}
|
||||
MemberPaymentConfig config = dao().fetch(MemberPaymentConfig.class, project.getConfigId());
|
||||
if (config == null || Lang.isEmpty(config.getPayStandards())) {
|
||||
return Result.error("当前缴费项目未配置缴费规则,无法导入!");
|
||||
}
|
||||
|
||||
// 追加模式下需要校验数据库中是否已存在该工号;清空模式会先删除原记录,因此不参与重复校验。
|
||||
List<MemberPaymentInfo> existsInfoList = isClearImport ? new ArrayList<>() : dao().query(MemberPaymentInfo.class, Cnd.where("paymentProjectId", "=", projectId));
|
||||
Set<String> existsLoginnameSet = existsInfoList.stream()
|
||||
.map(MemberPaymentInfo::getLoginname)
|
||||
.filter(Strings::isNotBlank)
|
||||
.map(this::normalizeExcelText)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
List<String> loginnameList = importList.stream()
|
||||
.map(MemberPaymentUserImportTemp::getLoginname)
|
||||
.filter(Strings::isNotBlank)
|
||||
.map(this::normalizeExcelText)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
Map<String, Sys_user> userMap = new HashMap<>();
|
||||
if (Lang.isNotEmpty(loginnameList)) {
|
||||
userMap = dao().query(Sys_user.class, Cnd.where("loginname", "in", loginnameList)).stream()
|
||||
.collect(Collectors.toMap(v -> normalizeExcelText(v.getLoginname()), v -> v, (left, right) -> left));
|
||||
}
|
||||
|
||||
List<MemberPaymentUserImportTemp> errorInfos = new ArrayList<>();
|
||||
List<MemberPaymentInfo> needInsertList = new ArrayList<>();
|
||||
Set<String> currentImportLoginnameSet = new HashSet<>();
|
||||
|
||||
for (MemberPaymentUserImportTemp importTemp : importList) {
|
||||
String errorMsg = validateImportRow(importTemp, currentImportLoginnameSet, existsLoginnameSet, userMap);
|
||||
if (Strings.isNotBlank(errorMsg)) {
|
||||
importTemp.setResult(errorMsg);
|
||||
errorInfos.add(importTemp);
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
Sys_user sysUser = userMap.get(normalizeExcelText(importTemp.getLoginname()));
|
||||
MemberPaymentInfo paymentInfo = buildImportPaymentInfo(sysUser, projectId, config, importTemp);
|
||||
needInsertList.add(paymentInfo);
|
||||
currentImportLoginnameSet.add(normalizeExcelText(importTemp.getLoginname()));
|
||||
} catch (IllegalArgumentException e) {
|
||||
importTemp.setResult(e.getMessage());
|
||||
errorInfos.add(importTemp);
|
||||
}
|
||||
}
|
||||
|
||||
if (isClearImport || Lang.isNotEmpty(needInsertList)) {
|
||||
Trans.exec(new Atom() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (isClearImport) {
|
||||
// 清空导入时仅清理当前缴费项目的名单,避免影响其他项目数据。
|
||||
dao().clear(MemberPaymentInfo.class, Cnd.where("paymentProjectId", "=", projectId));
|
||||
}
|
||||
if (Lang.isNotEmpty(needInsertList)) {
|
||||
dao().insert(needInsertList);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (Lang.isNotEmpty(errorInfos)) {
|
||||
NutMap resultData = NutMap.NEW();
|
||||
resultData.setv("totalCount", importList.size());
|
||||
resultData.setv("successCount", needInsertList.size());
|
||||
resultData.setv("errorCount", errorInfos.size());
|
||||
resultData.setv("errorList", errorInfos.stream().map(v ->
|
||||
NutMap.NEW()
|
||||
.addv("工号", v.getLoginname())
|
||||
.addv("姓名", v.getUsername())
|
||||
.addv("错误原因", v.getResult())
|
||||
).collect(Collectors.toList()));
|
||||
return Result.success(resultData);
|
||||
}
|
||||
return Result.success();
|
||||
}
|
||||
|
||||
/**
|
||||
* 按导入行生成待缴费记录。
|
||||
* 这里统一处理会员类型判断、缴费月数计算、规则匹配和金额计算,避免控制层重复拼装业务逻辑。
|
||||
*/
|
||||
private MemberPaymentInfo buildImportPaymentInfo(Sys_user sysUser, String projectId, MemberPaymentConfig config, MemberPaymentUserImportTemp importTemp) {
|
||||
PaymentPeriodInfo periodInfo = getPaymentPeriodInfo(sysUser, config);
|
||||
MemberPaymentConfig.PayStandards standards = findPayStandard(config.getPayStandards(), periodInfo.getPayPersonnel(), normalizeText(importTemp.getUserState()));
|
||||
if (standards == null) {
|
||||
throw new IllegalArgumentException("未找到【" + normalizeText(importTemp.getUserState()) + " / " + periodInfo.getPayPersonnel() + "】对应的缴费配置!");
|
||||
}
|
||||
|
||||
MemberPaymentInfo paymentInfo = new MemberPaymentInfo();
|
||||
paymentInfo.setUserId(sysUser.getId());
|
||||
paymentInfo.setLoginname(normalizeExcelText(importTemp.getLoginname()));
|
||||
paymentInfo.setPaymentProjectId(projectId);
|
||||
paymentInfo.setPayment(false);
|
||||
paymentInfo.setPaymentMonth(String.valueOf(periodInfo.getPaymentMonth()));
|
||||
// member_payment_info.personType 在当前模块中实际用于匹配配置里的“在职状态”。
|
||||
paymentInfo.setPersonType(normalizeText(importTemp.getUserState()));
|
||||
paymentInfo.setPayPersonnel(periodInfo.getPayPersonnel());
|
||||
paymentInfo.setPaymentMoney(calculatePaymentMoney(standards, periodInfo.getPaymentMonth(), importTemp));
|
||||
return paymentInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* 校验导入行的基础数据,优先拦住工号重复、名单已存在、人员不存在等明显问题。
|
||||
*/
|
||||
private String validateImportRow(MemberPaymentUserImportTemp importTemp,
|
||||
Set<String> currentImportLoginnameSet,
|
||||
Set<String> existsLoginnameSet,
|
||||
Map<String, Sys_user> userMap) {
|
||||
String loginname = normalizeExcelText(importTemp.getLoginname());
|
||||
String username = normalizeText(importTemp.getUsername());
|
||||
String userState = normalizeText(importTemp.getUserState());
|
||||
if (Strings.isBlank(loginname)) {
|
||||
return "工号不能为空!";
|
||||
}
|
||||
if (Strings.isBlank(username)) {
|
||||
return "姓名不能为空!";
|
||||
}
|
||||
if (Strings.isBlank(userState)) {
|
||||
return "在职状态不能为空!";
|
||||
}
|
||||
if (currentImportLoginnameSet.contains(loginname)) {
|
||||
return "导入文件中工号重复,请检查后重试!";
|
||||
}
|
||||
if (existsLoginnameSet.contains(loginname)) {
|
||||
return "该工号已存在于当前缴费名单中,无需重复导入!";
|
||||
}
|
||||
Sys_user sysUser = userMap.get(loginname);
|
||||
if (sysUser == null) {
|
||||
return "系统中未找到该工号对应的会员信息!";
|
||||
}
|
||||
if (!Objects.equals(normalizeText(sysUser.getUsername()), username)) {
|
||||
return "导入姓名与系统姓名不一致,请检查工号或姓名!";
|
||||
}
|
||||
if (!Objects.equals(sysUser.getMember(), 1)) {
|
||||
return "该人员不是工会会员,无法加入缴费名单!";
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据缴费周期和会员状态判断本次导入属于哪类缴费人员,并计算应缴月份。
|
||||
*/
|
||||
private PaymentPeriodInfo getPaymentPeriodInfo(Sys_user sysUser, MemberPaymentConfig config) {
|
||||
DateTime beginDate = DateUtil.parse(config.getPaymentStartMonth() + "-01");
|
||||
DateTime endDate = DateUtil.endOfMonth(DateUtil.parse(config.getPaymentEndMonth() + "-01"));
|
||||
Date memberJoinTime = sysUser.getMemberJoinTime();
|
||||
Date retireDate = sysUser.getRetireDate();
|
||||
|
||||
if (isDateBetween(memberJoinTime, beginDate, endDate)) {
|
||||
long differMonth = DateUtil.betweenMonth(memberJoinTime, endDate, false) + 1;
|
||||
return new PaymentPeriodInfo("新进会员", differMonth);
|
||||
}
|
||||
if (isDateBetween(retireDate, beginDate, endDate)) {
|
||||
long differMonth = DateUtil.betweenMonth(beginDate, retireDate, false) + 1;
|
||||
return new PaymentPeriodInfo("退休会员", differMonth);
|
||||
}
|
||||
long differMonth = DateUtil.betweenMonth(beginDate, endDate, false) + 1;
|
||||
return new PaymentPeriodInfo("普通会员", differMonth);
|
||||
}
|
||||
|
||||
private boolean isDateBetween(Date currentDate, Date beginDate, Date endDate) {
|
||||
if (currentDate == null) {
|
||||
return false;
|
||||
}
|
||||
return !currentDate.before(beginDate) && !currentDate.after(endDate);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据缴费配置匹配当前导入人员对应的规则。
|
||||
*/
|
||||
private MemberPaymentConfig.PayStandards findPayStandard(List<MemberPaymentConfig.PayStandards> payStandards,
|
||||
String payPersonnel,
|
||||
String userState) {
|
||||
return payStandards.stream()
|
||||
.filter(item -> Objects.equals(normalizeText(item.getPayPersonnel()), payPersonnel)
|
||||
&& Objects.equals(normalizeText(item.getPersonType()), userState))
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* 统一按配置计算缴费金额。
|
||||
* 当前业务以 payModeValueList 为主,只要配置了工资项,就按“工资项求和 * 0.5% * 缴费月数”计算;
|
||||
* 若未配置工资项,再兜底按固定金额 * 缴费月数计算,兼容旧配置。
|
||||
*/
|
||||
private String calculatePaymentMoney(MemberPaymentConfig.PayStandards standards,
|
||||
long paymentMonth,
|
||||
MemberPaymentUserImportTemp importTemp) {
|
||||
if (Lang.isNotEmpty(standards.getPayModeValueList())) {
|
||||
BigDecimal totalBase = BigDecimal.ZERO;
|
||||
for (NutMap item : standards.getPayModeValueList()) {
|
||||
String salaryFieldName = normalizeText(item.getString("name"));
|
||||
if (Strings.isBlank(salaryFieldName)) {
|
||||
throw new IllegalArgumentException("缴费配置中存在空的工资项,请先完善缴费配置后再导入!");
|
||||
}
|
||||
totalBase = totalBase.add(resolveSalaryValue(importTemp, salaryFieldName));
|
||||
}
|
||||
BigDecimal paymentMoney = totalBase.multiply(MEMBER_PAYMENT_RATE).multiply(BigDecimal.valueOf(paymentMonth));
|
||||
return formatMoney(paymentMoney.setScale(2, RoundingMode.HALF_UP));
|
||||
}
|
||||
if (Strings.isNotBlank(standards.getPayModeValue())) {
|
||||
BigDecimal fixedMoney = parseNumber(standards.getPayModeValue(), "固定缴费金额");
|
||||
return formatMoney(fixedMoney.multiply(BigDecimal.valueOf(paymentMonth)));
|
||||
}
|
||||
throw new IllegalArgumentException("当前缴费规则未配置可计算的金额信息,请先完善缴费配置!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 按导入模板上的 Excel 列名动态匹配工资项。
|
||||
* 后续如果模板新增了新的工资字段,只要字段上配置了同名 @Excel(name="..."),
|
||||
* 并且不是工号、姓名、在职状态这类基础字段,就可以直接参与金额计算。
|
||||
*/
|
||||
private BigDecimal resolveSalaryValue(MemberPaymentUserImportTemp importTemp, String salaryFieldName) {
|
||||
Map<String, String> salaryFieldMap = getImportSalaryFieldMap(importTemp);
|
||||
if (salaryFieldMap.containsKey(salaryFieldName)) {
|
||||
return parseNumber(salaryFieldMap.get(salaryFieldName), salaryFieldName);
|
||||
}
|
||||
throw new IllegalArgumentException("缴费配置中的工资项【" + salaryFieldName + "】暂不支持导入,请检查缴费配置!");
|
||||
}
|
||||
|
||||
/**
|
||||
* 动态提取导入模板中的工资类字段,key 为 Excel 列名,value 为导入值。
|
||||
*/
|
||||
private Map<String, String> getImportSalaryFieldMap(MemberPaymentUserImportTemp importTemp) {
|
||||
Map<String, String> salaryFieldMap = new HashMap<>();
|
||||
for (Field field : MemberPaymentUserImportTemp.class.getDeclaredFields()) {
|
||||
Excel excel = field.getAnnotation(Excel.class);
|
||||
if (excel == null) {
|
||||
continue;
|
||||
}
|
||||
String excelName = normalizeText(excel.name());
|
||||
if (isBasicImportField(excelName)) {
|
||||
continue;
|
||||
}
|
||||
field.setAccessible(true);
|
||||
try {
|
||||
Object fieldValue = field.get(importTemp);
|
||||
salaryFieldMap.put(excelName, fieldValue == null ? "" : String.valueOf(fieldValue));
|
||||
} catch (IllegalAccessException e) {
|
||||
throw new IllegalArgumentException("读取导入字段【" + excelName + "】失败,请重试!");
|
||||
}
|
||||
}
|
||||
return salaryFieldMap;
|
||||
}
|
||||
|
||||
/**
|
||||
* 这些字段用于人员匹配和规则匹配,不参与工资基数求和。
|
||||
*/
|
||||
private boolean isBasicImportField(String excelName) {
|
||||
return Objects.equals("工号", excelName)
|
||||
|| Objects.equals("姓名", excelName)
|
||||
|| Objects.equals("在职状态", excelName);
|
||||
}
|
||||
|
||||
private BigDecimal parseNumber(String value, String fieldName) {
|
||||
String normalizedValue = normalizeExcelText(value);
|
||||
if (Strings.isBlank(normalizedValue)) {
|
||||
throw new IllegalArgumentException(fieldName + "不能为空!");
|
||||
}
|
||||
try {
|
||||
return new BigDecimal(normalizedValue);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(fieldName + "格式不正确,请填写数字!");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Excel 中的工号、工资值可能被转成 123.0,这里统一做兼容处理,避免因格式问题匹配失败。
|
||||
*/
|
||||
private String normalizeExcelText(String value) {
|
||||
String normalizedValue = normalizeText(value);
|
||||
if (normalizedValue.matches("^-?\\d+\\.0+$")) {
|
||||
return normalizedValue.substring(0, normalizedValue.indexOf("."));
|
||||
}
|
||||
return normalizedValue;
|
||||
}
|
||||
|
||||
private String normalizeText(String value) {
|
||||
return Strings.isBlank(value) ? "" : value.trim();
|
||||
}
|
||||
|
||||
private String formatMoney(BigDecimal value) {
|
||||
return value.stripTrailingZeros().toPlainString();
|
||||
}
|
||||
|
||||
private static class PaymentPeriodInfo {
|
||||
private final String payPersonnel;
|
||||
private final long paymentMonth;
|
||||
|
||||
private PaymentPeriodInfo(String payPersonnel, long paymentMonth) {
|
||||
this.payPersonnel = payPersonnel;
|
||||
this.paymentMonth = paymentMonth;
|
||||
}
|
||||
|
||||
public String getPayPersonnel() {
|
||||
return payPersonnel;
|
||||
}
|
||||
|
||||
public long getPaymentMonth() {
|
||||
return paymentMonth;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
package io.v.nutz.zhgh.memberCost.template;
|
||||
|
||||
import cn.afterturn.easypoi.excel.annotation.Excel;
|
||||
import lombok.Data;
|
||||
|
||||
/**
|
||||
* 缴费名单导入模板
|
||||
* 按工号导入待缴费人员,并根据缴费配置计算应缴金额。
|
||||
*/
|
||||
@Data
|
||||
public class MemberPaymentUserImportTemp {
|
||||
|
||||
@Excel(name = "工号")
|
||||
private String loginname;
|
||||
|
||||
@Excel(name = "姓名")
|
||||
private String username;
|
||||
|
||||
@Excel(name = "岗位工资")
|
||||
private String postSalary;
|
||||
|
||||
@Excel(name = "薪级工资")
|
||||
private String salaryGrade;
|
||||
|
||||
@Excel(name = "在职状态")
|
||||
private String userState;
|
||||
|
||||
/**
|
||||
* 导入失败时回填错误原因,供前端导出错误记录使用。
|
||||
*/
|
||||
private String result;
|
||||
}
|
||||
@@ -150,11 +150,11 @@ layout("/layouts/platform.html"){
|
||||
</el-form-item>
|
||||
</template>
|
||||
</el-table-column>
|
||||
<el-table-column label="人员类型" prop="personType" >
|
||||
<el-table-column label="在职类型" prop="personType" >
|
||||
<template slot-scope="{row,$index}">
|
||||
<el-select @change="row.payMode=null" placeholder="请选择人员类型" v-model="row.personType"
|
||||
<el-select @change="row.payMode=null" placeholder="请选择在职类型" v-model="row.personType"
|
||||
style="width: 100%" clearable>
|
||||
<el-option v-for="item in personTypeList" :label="item.name" :value="item.code"></el-option>
|
||||
<el-option v-for="item in userStateList" :label="item.name" :value="item.code"></el-option>
|
||||
</el-select>
|
||||
</template>
|
||||
</el-table-column>
|
||||
|
||||
@@ -105,13 +105,13 @@ layout("/layouts/platform.html"){
|
||||
<el-dropdown-item :command="{type:'edit',data:scope.row}">
|
||||
编辑
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'doRenew',data:scope.row}"
|
||||
<!-- <el-dropdown-item :command="{type:'doRenew',data:scope.row}"
|
||||
v-if="scope.row.created">
|
||||
更新缴费人员
|
||||
</el-dropdown-item>
|
||||
<el-dropdown-item :command="{type:'doSet',data:scope.row}">
|
||||
{{scope.row.created?'重置缴费人员':'生成缴费人员'}}
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-item>-->
|
||||
<el-dropdown-item :command="{type:'remove',data:scope.row}">
|
||||
删除
|
||||
</el-dropdown-item>
|
||||
|
||||
@@ -192,9 +192,13 @@ layout("/layouts/platform.html"){
|
||||
|
||||
<el-button @click="doPaidToUnPaid(false)" size="small" type="primary">批量设为未缴</el-button>
|
||||
|
||||
<el-button icon="el-icon-s-promotion" type="primary" size="small" :loading="submitLoading" @click="openImport">
|
||||
<el-button icon="el-icon-s-promotion" type="primary" size="small" :loading="submitLoading" @click="openImport('payment')">
|
||||
导入
|
||||
</el-button>
|
||||
|
||||
<el-button icon="el-icon-s-promotion" type="primary" size="small" :loading="submitLoading" @click="openImport('user')">
|
||||
导入缴费人员
|
||||
</el-button>
|
||||
<el-button icon="el-icon-document-add" type="primary" size="small" :loading="submitLoading" @click="doExport(false)">
|
||||
导出
|
||||
</el-button>
|
||||
@@ -254,9 +258,11 @@ layout("/layouts/platform.html"){
|
||||
<template #edit>
|
||||
<file-import
|
||||
ref="viewImport"
|
||||
temp_url="/platform/member/payment/summary/downloadImportTemp"
|
||||
post_url="/platform/member/payment/summary/doImport"
|
||||
:temp_url="importConfig.tempUrl"
|
||||
:post_url="importConfig.postUrl"
|
||||
:project_id="pageForm.paymentProjectId"
|
||||
:text="importConfig.text"
|
||||
:is_show_radio="importConfig.isShowRadio"
|
||||
@flush="successImport"
|
||||
></file-import>
|
||||
</template>
|
||||
@@ -291,14 +297,15 @@ layout("/layouts/platform.html"){
|
||||
units: [],
|
||||
unions: [],
|
||||
tableColumns: [
|
||||
{ prop: "projectName", label: "缴费项目" },
|
||||
{ prop: "loginname", label: "工号", sortable: true },
|
||||
{ prop: "username", label: "姓名" },
|
||||
{ prop: "mobile", label: "联系电话" },
|
||||
{ prop: "schoolTime", label: "入职时间" },
|
||||
{ prop: "schoolTime", label: "入职时间" , checked: 0},
|
||||
// { prop: "retireDate", label: "退休时间" },
|
||||
{ prop: "personType", label: "人员类型", sortable: true },
|
||||
{ prop: "personType", label: "人员类型", sortable: true , checked: 0},
|
||||
{ prop: "userState", label: "在职状态", sortable: true },
|
||||
{ prop: "unionname", label: "所属工会", sortable: true },
|
||||
{ prop: "unionname", label: "所属工会", sortable: true , checked: 0},
|
||||
{ prop: "unitname", label: "所属单位", sortable: true },
|
||||
{ prop: "isPayment", label: "是否缴费", sortable: true },
|
||||
{ prop: "paymentMoney", label: "缴费金额", sortable: true }
|
||||
@@ -319,6 +326,13 @@ layout("/layouts/platform.html"){
|
||||
checkUsers: [],
|
||||
|
||||
modifyVisible: false,
|
||||
importConfig: {
|
||||
tempUrl: "/platform/member/payment/summary/downloadImportTemp",
|
||||
postUrl: "/platform/member/payment/summary/doImport",
|
||||
text: "",
|
||||
isShowRadio: false
|
||||
},
|
||||
importAjaxSuccessHandler: null,
|
||||
formData: {
|
||||
ids: []
|
||||
},
|
||||
@@ -452,7 +466,26 @@ layout("/layouts/platform.html"){
|
||||
}
|
||||
}
|
||||
},
|
||||
openImport() {
|
||||
openImport(type) {
|
||||
if (!this.pageForm.paymentProjectId) {
|
||||
this.notifyWarning("请先选择缴费项目后再导入!")
|
||||
return
|
||||
}
|
||||
if (type === "user") {
|
||||
this.importConfig = {
|
||||
tempUrl: "/platform/member/payment/summary/downloadUserImportTemp",
|
||||
postUrl: "/platform/member/payment/summary/doUserImport",
|
||||
text: "模板字段:工号、姓名、岗位工资、薪级工资、在职状态",
|
||||
isShowRadio: true
|
||||
}
|
||||
} else {
|
||||
this.importConfig = {
|
||||
tempUrl: "/platform/member/payment/summary/downloadImportTemp",
|
||||
postUrl: "/platform/member/payment/summary/doImport",
|
||||
text: "",
|
||||
isShowRadio: false
|
||||
}
|
||||
}
|
||||
this.$refs.guava.edit()
|
||||
this.$refs.viewImport.resetImportData()
|
||||
},
|
||||
@@ -528,6 +561,29 @@ layout("/layouts/platform.html"){
|
||||
this.pageData()
|
||||
}
|
||||
},
|
||||
mounted() {
|
||||
/**
|
||||
* 仅在当前页面监听导入接口返回。
|
||||
* 导入存在错误明细时组件不会触发 flush,这里统一补一次列表刷新。
|
||||
*/
|
||||
this.importAjaxSuccessHandler = (event, xhr, settings) => {
|
||||
if (!settings || !settings.url) {
|
||||
return
|
||||
}
|
||||
if (
|
||||
settings.url.indexOf("/platform/member/payment/summary/doImport") > -1 ||
|
||||
settings.url.indexOf("/platform/member/payment/summary/doUserImport") > -1
|
||||
) {
|
||||
this.pageData()
|
||||
}
|
||||
}
|
||||
$(document).ajaxSuccess(this.importAjaxSuccessHandler)
|
||||
},
|
||||
beforeDestroy() {
|
||||
if (this.importAjaxSuccessHandler) {
|
||||
$(document).off("ajaxSuccess", this.importAjaxSuccessHandler)
|
||||
}
|
||||
},
|
||||
async created() {
|
||||
this.isThreeUnit = (await getConfigKey("isThreeUnit")) === "true"
|
||||
if (this.isThreeUnit) {
|
||||
|
||||
Reference in New Issue
Block a user