医疗补助算法整改

This commit is contained in:
2026-10-12 18:31:14 +08:00
parent 9ffdbd21ad
commit 4440420d90
13 changed files with 727 additions and 376 deletions
@@ -1,24 +1,18 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.controller;
import cn.dev33.satoken.annotation.SaCheckPermission;
import cn.dev33.satoken.annotation.SaMode;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.flow.engine.FlowEngine;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.param.AidFundPageForm;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service.AidFundMemberChangeRecordService;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service.AidFundMemberService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.sql.Sql;
import org.nutz.dao.util.cri.SqlExpressionGroup;
import org.nutz.ioc.aop.Aop;
import org.nutz.ioc.loader.annotation.Inject;
import org.nutz.ioc.loader.annotation.IocBean;
@@ -26,6 +20,8 @@ import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.servlet.http.HttpServletResponse;
/**
* @author zhf
* @date 2026/1/19 17:55
@@ -52,73 +48,36 @@ public class AidFundChangeRecordController {
}
/**
* 按页面条件分页返回基金会员变更记录。
*
* @param pageForm 年度、姓名/工号、会员类型、变更类型、工会、单位、排序及页码/每页条数
* @return Result.data 为 Pagination,list 为当前页记录,totalCount 为匹配总条数
*/
@At
@ApiOperation("分页查询")
@SaCheckPermission("medicalMutualAid.aidFund.changeRecord")
public Result pageData(AidFundPageForm pageForm) {
Sql sql = Sqls.create("""
SELECT
info.*,
us.sex,
us.username,
us.loginname,
us.arrivalAtSchoolDate,
us.mobile,
us.unitName,
us.unionName,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariable,
IF((SELECT taskName FROM wf_process_task tt WHERE tt.id = t.taskParentId) = 'startTask', 1, 0) canRevoke,
(select MAX(id) from wf_process_task where processInstanceId = ins.id and taskName = 'startTask' AND taskState in (10,20)) AS startTaskId
FROM
`aid_fund_member_change_record` info
LEFT JOIN vw_user us ON us.id = info.userId
LEFT JOIN `wf_process_instance` ins ON ins.businessNo = info.id
LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id AND t.taskState = 10
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
$condition
""");
Cnd cnd = Cnd.NEW();
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.or("us.loginname", "like", "%" + pageForm.getSearchKeyword() + "%");
seg.or("us.username", "like", "%" + pageForm.getSearchKeyword() + "%");
cnd.and(seg);
}
cnd.andEX("IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType)", "=", pageForm.getAidFundMemberUserType());
cnd.andEX("us.unitid", "=", pageForm.getUnitId());
cnd.andEX("us.unionid", "=", pageForm.getUnionId());
cnd.andEX("info.changeType", "=", pageForm.getChangeType());
cnd.andEX("YEAR(info.applyTime)", "=", pageForm.getYear());
SqlExpressionGroup group = new SqlExpressionGroup();
group.or("ins.state","=",20);
group.or("ins.state","is",null);
cnd.and(group);
if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.desc("us.unitCode");
} else {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
}
cnd.groupBy("info.id");
sql.setCondition(cnd);
Sql sql = changeRecordService.getChangeRecordSql(pageForm);
Pagination pagination = changeRecordService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
return Result.success(pagination);
}
/**
* 导出页面筛选条件下的全部基金会员变更记录。
*
* @param pageForm 年度、姓名/工号、会员类型、变更类型、工会、单位及排序参数;忽略分页参数
* @param response 输出包含页面业务列的 XLSX 文件流,无 JSON 返回值
*/
@At
@Ok("void")
@ApiOperation("导出变更记录")
@SaCheckPermission("medicalMutualAid.aidFund.changeRecord")
public void exportChangeRecord(AidFundPageForm pageForm, HttpServletResponse response) {
changeRecordService.exportChangeRecord(pageForm, response);
}
/**
* 删除基金会员变更记录。
*
@@ -7,10 +7,27 @@ import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMem
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberPay;
import org.nutz.lang.util.NutMap;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
public interface AidFundMemberChangeRecordService extends BaseService<AidFundMemberChangeRecord> {
/**
* 构建变更记录列表与导出共用的查询 SQL。
*
* @param pageForm 年度、姓名/工号、会员类型、变更类型、工会、单位及排序参数;空筛选项不限制
* @return 未设置分页的 SQL,包含申请、人员和 WF 流程字段
*/
Sql getChangeRecordSql(AidFundPageForm pageForm);
/**
* 导出当前筛选条件下的全部变更记录,列顺序与页面业务列一致。
*
* @param pageForm 与列表相同的筛选和排序参数;页码、每页条数不参与导出限制
* @param response XLSX 文件流的 HTTP 响应,无 JSON 返回值
*/
void exportChangeRecord(AidFundPageForm pageForm, HttpServletResponse response);
/**
* 构建互管会审核页面查询 SQL。
*
@@ -1,5 +1,9 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service.impl;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
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.collection.CollectionUtil;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.lang.Dict;
@@ -8,6 +12,7 @@ import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.constant.RoleConstant;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.flow.constant.FlowConst;
import com.budwk.app.flow.entity.ProcessInstance;
@@ -24,6 +29,7 @@ import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMem
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberPay;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.param.AidFundPageForm;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.service.AidFundMemberChangeRecordService;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
@@ -38,6 +44,7 @@ import org.nutz.json.Json;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
import javax.servlet.http.HttpServletResponse;
import java.util.*;
/**
@@ -64,6 +71,119 @@ public class AidFundMemberChangeRecordServiceImpl extends BaseServiceImpl<AidFun
@Inject
private SysDictService sysDictService;
/**
* 复用变更记录列表的筛选、分组及排序,分页和导出使用同一查询口径。
*
* @param pageForm 年度、姓名/工号、会员类型、变更类型、工会、单位及排序参数;空筛选项不限制
* @return 未设置分页的 SQL,返回申请记录、人员信息和 WF 流程信息
*/
@Override
public Sql getChangeRecordSql(AidFundPageForm pageForm) {
Sql sql = Sqls.create("""
SELECT
info.*,
us.sex,
us.username,
us.loginname,
us.arrivalAtSchoolDate,
us.mobile,
us.unitName,
us.unionName,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariable,
IF((SELECT taskName FROM wf_process_task tt WHERE tt.id = t.taskParentId) = 'startTask', 1, 0) canRevoke,
(select MAX(id) from wf_process_task where processInstanceId = ins.id and taskName = 'startTask' AND taskState in (10,20)) AS startTaskId
FROM
`aid_fund_member_change_record` info
LEFT JOIN vw_user us ON us.id = info.userId
LEFT JOIN `wf_process_instance` ins ON ins.businessNo = info.id
LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id AND t.taskState = 10
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
$condition
""");
Cnd cnd = Cnd.NEW();
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.or("us.loginname", "like", "%" + pageForm.getSearchKeyword() + "%");
seg.or("us.username", "like", "%" + pageForm.getSearchKeyword() + "%");
cnd.and(seg);
}
cnd.andEX("IFNULL(info.aidFundMemberUserType, us.aidFundMemberUserType)", "=", pageForm.getAidFundMemberUserType());
cnd.andEX("us.unitid", "=", pageForm.getUnitId());
cnd.andEX("us.unionid", "=", pageForm.getUnionId());
cnd.andEX("info.changeType", "=", pageForm.getChangeType());
cnd.andEX("YEAR(info.applyTime)", "=", pageForm.getYear());
SqlExpressionGroup group = new SqlExpressionGroup();
group.or("ins.state","=",20);
group.or("ins.state","is",null);
cnd.and(group);
if (StrUtil.isAllBlank(pageForm.getPageOrderName(), pageForm.getPageOrderBy())) {
cnd.desc("us.unitCode");
} else {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
}
cnd.groupBy("info.id");
sql.setCondition(cnd);
return sql;
}
/**
* 按列表条件导出全部匹配记录,并将字典编码转为页面展示的名称。
*
* @param pageForm 页面筛选及排序参数,分页参数不限制导出条数
* @param response 输出按页面列顺序生成的 XLSX 文件流,无 JSON 返回值
*/
@Override
public void exportChangeRecord(AidFundPageForm pageForm, HttpServletResponse response) {
List<NutMap> list = listMap(getChangeRecordSql(pageForm));
Map<String, String> memberTypeMap = getDictNameMap("AIDFUND_MEMBER_USER_TYPE");
Map<String, String> changeTypeMap = getDictNameMap("AIDFUND_MEMBER_CHANGE_TYPE");
for (int index = 0; index < list.size(); index++) {
NutMap row = list.get(index);
row.put("sequence", index + 1);
String memberType = row.getString("aidFundMemberUserType");
String changeType = row.getString("changeType");
// 空值保持空白,未配置的字典编码保留原值,避免导出时丢失业务信息。
row.put("aidFundMemberUserType", memberTypeMap.getOrDefault(memberType, memberType));
row.put("changeType", changeTypeMap.getOrDefault(changeType, changeType));
row.put("applyTime", row.getTime("applyTime") == null ? "" : DateUtil.formatDate(row.getTime("applyTime")));
}
List<ExcelExportEntity> columns = new ArrayList<>();
ExcelExportEntity sequence = new ExcelExportEntity("序号", "sequence", 10);
sequence.setType(10);
columns.add(sequence);
// 工号使用文本单元格,保留前导零;其余业务列与页面显示顺序一致。
columns.add(new ExcelExportEntity("工号", "loginname", 20));
columns.add(new ExcelExportEntity("姓名", "username", 20));
columns.add(new ExcelExportEntity("性别", "sex", 10));
columns.add(new ExcelExportEntity("入校时间", "arrivalAtSchoolDate", 20));
columns.add(new ExcelExportEntity("所属单位", "unitName", 25));
columns.add(new ExcelExportEntity("所属工会", "unionName", 25));
columns.add(new ExcelExportEntity("基金会员类型", "aidFundMemberUserType", 20));
columns.add(new ExcelExportEntity("变更类型", "changeType", 20));
columns.add(new ExcelExportEntity("变更时间", "applyTime", 20));
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, columns, list);
String fileName = (pageForm.getYear() == null ? "" : pageForm.getYear()) + "基金会员变更记录.xlsx";
CommonDownloadUtil.download(fileName, workbook, response);
}
@Override
public Sql getFoundationAuditSql(AidFundPageForm pageForm) {
Sql sql = Sqls.create("""
@@ -1,20 +1,14 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.controller;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
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.dev33.satoken.annotation.SaCheckPermission;
import com.budwk.app.base.annotation.SLog;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalApply;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalMoneyDetail;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.MedicalCalculateMoneyService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.aop.interceptor.ioc.TransAop;
import org.nutz.dao.Chain;
import org.nutz.dao.Cnd;
@@ -28,11 +22,11 @@ import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import org.nutz.mvc.annotation.Param;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -97,12 +91,28 @@ public class MedicalCalculateMoneyController {
}
/**
* 按页面测算结果分配补助,并保存该年度结余。
* 爱心金额显式按参数名绑定,避免 Nutz 将 BigDecimal 当作对象解析其内部字段。
*
* @param data 申请记录的 JSON 数组,包含申请 id 和最终测算金额 forecastSubsidyMoney
* @param ratio 页面当前补助比例,单位为百分比
* @param aidFund 本年度会员缴费总额,单位为元
* @param inComeSum 本年度收入累计总额,单位为元
* @param loveSubsidyMoney 实际爱心补助合计,单位为元,支持小数;未传时按零处理
* @return Result 操作结果,code 为 0 表示补助金额与年度结余保存成功
*/
@At
@ApiOperation("按比例修改补助金额")
@Aop(TransAop.READ_COMMITTED)
@SLog(tag = "医疗补助系统-医疗补助预测", msg = "按比例修改补助金额")
@SaCheckPermission("medicalMutualAid.medical.calculateMoney")
public Result doHandle(String data, float ratio, double aidFund, double inComeSum, long loveSubsidyMoney){
public Result doHandle(String data, float ratio, double aidFund, double inComeSum,
@Param("loveSubsidyMoney") BigDecimal loveSubsidyMoney){
// 延续原整数参数未传时按零处理的行为,同时保留实际爱心补助的小数精度。
if (loveSubsidyMoney == null) {
loveSubsidyMoney = BigDecimal.ZERO;
}
//获取申请最新年份
Sql yearSql = Sqls.create("SELECT MAX(YEAR(applyTime)) AS currentYear FROM medical_apply");
yearSql.setCallback(Sqls.callback.longs());
@@ -122,7 +132,7 @@ public class MedicalCalculateMoneyController {
BigDecimal incomeRounded = BigDecimal.valueOf(Math.round(inComeSum));
BigDecimal thisYearBalance = incomeRounded
.subtract(subsidyMoney)
.subtract(BigDecimal.valueOf(loveSubsidyMoney));
.subtract(loveSubsidyMoney);
MedicalMoneyDetail moneyDetail = medicalCalculateMoneyService.dao().fetch(MedicalMoneyDetail.class, Cnd.where("year", "=", year));
@@ -143,70 +153,17 @@ public class MedicalCalculateMoneyController {
}
/**
* 导出当前比例的测算结果;返回类型为 void,Excel 文件写入 response。
*
* @param ratio 页面当前补助比例,单位为百分比
* @param response 接收 Excel 下载内容的 HTTP 响应
*/
@At
@Ok("void")
@SaCheckPermission("medicalMutualAid.medical.calculateMoney")
public void doExport(double ratio, HttpServletResponse response){
List<NutMap> list = medicalCalculateMoneyService.getApplyData(ratio);
list.forEach(l -> {
l.put("majorDisease", l.getBoolean("isMajorDisease") ? "是" : "否");
});
List<ExcelExportEntity> exportEntities = new ArrayList<>();
exportEntities.add(new ExcelExportEntity("姓名", "username", 20));
exportEntities.add(new ExcelExportEntity("工号", "loginname", 20));
exportEntities.add(new ExcelExportEntity("所属单位", "unitName", 20));
exportEntities.add(new ExcelExportEntity("所属分工会", "unionName", 20));
exportEntities.add(new ExcelExportEntity("疾病种类", "diseaseName", 20));
exportEntities.add(new ExcelExportEntity("重大疾病", "majorDisease", 20));
exportEntities.add(new ExcelExportEntity("申请时间", "applyTime", 20));
exportEntities.add(new ExcelExportEntity("申请次数", "applyNum", 20));
exportEntities.add(new ExcelExportEntity("起扣时间", "aidFundDeductTime", 20));
exportEntities.add(new ExcelExportEntity("缴费年数", "yearsSinceDeduct", 20));
ExcelExportEntity hospitalSumMoneyExcelExportEntity = new ExcelExportEntity("住院医疗总额(不含门诊)", "hospitalSumMoney$", 20);
hospitalSumMoneyExcelExportEntity.setType(10);
exportEntities.add(hospitalSumMoneyExcelExportEntity);
ExcelExportEntity reimbursementMoneyExcelExportEntity = new ExcelExportEntity("实际报销总额", "reimbursementMoney$", 20);
reimbursementMoneyExcelExportEntity.setType(10);
exportEntities.add(reimbursementMoneyExcelExportEntity);
ExcelExportEntity singleBearMoneyExcelExportEntity = new ExcelExportEntity("(住院医疗)允许报销范围内个人承担部分总额", "singleBearMoney$", 20);
singleBearMoneyExcelExportEntity.setType(10);
exportEntities.add(singleBearMoneyExcelExportEntity);
ExcelExportEntity personExpenseMoneyExcelExportEntity = new ExcelExportEntity("(住院医疗)自费部分总额", "personExpenseMoney$", 20);
personExpenseMoneyExcelExportEntity.setType(10);
exportEntities.add(personExpenseMoneyExcelExportEntity);
ExcelExportEntity outpatientServiceMoneyExcelExportEntity = new ExcelExportEntity("重病门诊自费金额", "outpatientServiceMoney$", 20);
outpatientServiceMoneyExcelExportEntity.setType(10);
exportEntities.add(outpatientServiceMoneyExcelExportEntity);
ExcelExportEntity bxyMoneyExcelExportEntity = new ExcelExportEntity("非住院肾衰竭治疗、靶向药", "bxyMoney$", 20);
bxyMoneyExcelExportEntity.setType(10);
exportEntities.add(bxyMoneyExcelExportEntity);
ExcelExportEntity totalMoneyExcelExportEntity = new ExcelExportEntity("合计金额", "totalMoney", 20);
totalMoneyExcelExportEntity.setType(10);
exportEntities.add(totalMoneyExcelExportEntity);
ExcelExportEntity loveSubsidyMoneyExcelExportEntity = new ExcelExportEntity("爱心补助", "loveSubsidyMoney", 20);
loveSubsidyMoneyExcelExportEntity.setType(10);
exportEntities.add(loveSubsidyMoneyExcelExportEntity);
ExcelExportEntity subsidyMoneyExcelExportEntity = new ExcelExportEntity("当前补助金额", "subsidyMoney", 20);
subsidyMoneyExcelExportEntity.setType(10);
exportEntities.add(subsidyMoneyExcelExportEntity);
ExcelExportEntity ExcelExportEntityForecastSubsidyMoney = new ExcelExportEntity("按" + ratio + "%补助金额", "forecastSubsidyMoney", 20);
ExcelExportEntityForecastSubsidyMoney.setType(10);
exportEntities.add(ExcelExportEntityForecastSubsidyMoney);
ExcelExportEntity medicalSubsidyMoneyExcelExportEntity = new ExcelExportEntity("以往累计补助金额", "medicalSubsidyMoney", 20);
medicalSubsidyMoneyExcelExportEntity.setType(10);
exportEntities.add(medicalSubsidyMoneyExcelExportEntity);
try {
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, list);
CommonDownloadUtil.download("测算补助金额汇总表.xlsx", workbook, response);
}catch (Exception e){
e.printStackTrace();
}
medicalCalculateMoneyService.doExport(ratio, response);
}
@@ -1,14 +1,9 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.controller;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
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.dev33.satoken.annotation.SaCheckPermission;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.flow.engine.FlowEngine;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
@@ -17,7 +12,6 @@ import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.MedicalA
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.dao.Cnd;
import org.nutz.dao.Sqls;
import org.nutz.dao.entity.Record;
@@ -30,7 +24,6 @@ import org.nutz.mvc.annotation.At;
import org.nutz.mvc.annotation.Ok;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
import java.util.List;
/**
@@ -124,115 +117,19 @@ public class MedicalStatisticsController {
return Result.success(pagination);
}
/**
* 导出医疗补助统计表,返回类型为 void,Excel 文件直接写入 response。
*
* @param pageForm 页面筛选、分页及排序条件
* @param isMajorDiseases true 为重疾,false 为普通疾病,null 不限制类型
* @param response 接收下载内容的 HTTP 响应
*/
@At
@Ok("void")
@ApiOperation("分页查询")
@ApiOperation("导出医疗补助统计表")
@SaCheckPermission("medicalMutualAid.medical.statistics")
public void doExport(MedicalPageForm pageForm, Boolean isMajorDiseases, HttpServletResponse response) {
Sql sql = Sqls.create("""
SELECT
info.*,
type.diseaseName,
type.diseaseCode,
type.isMajorDiseases,
us.username,
us.loginname,
us.unitName,
us.unitCode,
us.unionName,
us.unionCode,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariable,
IF((SELECT taskName FROM wf_process_task tt WHERE tt.id = t.taskParentId) = 'startTask', 1, 0) canRevoke,
(SELECT MAX(id) FROM wf_process_task WHERE processInstanceId = ins.id AND taskName = 'startTask') AS startTaskId
FROM
medical_apply info
LEFT JOIN medical_disease_type type ON info.diseaseId = type.id
LEFT JOIN aid_fund_member_pay member ON member.userId = info.userId
LEFT JOIN vw_user us ON us.id = info.userId
LEFT JOIN `wf_process_instance` ins ON ins.businessNo = info.id
LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id
AND t.taskState = 10
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
$condition
""");
Cnd cnd = Cnd.NEW();
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("us.loginname", pageForm.getSearchKeyword());
seg.orLike("us.username", pageForm.getSearchKeyword());
cnd.and(seg);
}
cnd.andEX("year(info.applyTime)", "=", pageForm.getYear());
cnd.andEX("info.diseaseId", "=", pageForm.getDiseaseId());
cnd.andEX("us.unionId", "=", pageForm.getUnionId());
cnd.andEX("us.unitId", "=", pageForm.getUnitId());
cnd.andEX("member.aidFundMemberUserType", "=", pageForm.getAidFundMemberUserType());
cnd.andEX("type.isMajorDiseases", "=", isMajorDiseases);
if (StrUtil.isNotBlank(pageForm.getPageOrderName()) && StrUtil.isNotBlank(pageForm.getPageOrderBy())) {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
} else {
cnd.desc("info.applyTime");
}
cnd.and("ins.state", "=", 20);
cnd.groupBy("info.id");
sql.setCondition(cnd);
Pagination<NutMap> pagination = medicalApplyService.listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
List<NutMap> list = medicalApplyService.getCostList(pagination.getList());
for (NutMap map : list) {
boolean isMajor = map.getBoolean("isMajorDiseases");
map.put("isMajorDiseasesDisplay", isMajor ? "是" : "否");
}
List<ExcelExportEntity> excelExportEntities = new ArrayList<>();
excelExportEntities.add(new ExcelExportEntity("姓名","username",20));
excelExportEntities.add(new ExcelExportEntity("工号","loginname",20));
excelExportEntities.add(new ExcelExportEntity("单位","unitName",20));
excelExportEntities.add(new ExcelExportEntity("单位编码","unitCode",20));
excelExportEntities.add(new ExcelExportEntity("工会","unionName",20));
excelExportEntities.add(new ExcelExportEntity("工会编码","unionCode",20));
excelExportEntities.add(new ExcelExportEntity("疾病名称","diseaseName",20));
excelExportEntities.add(new ExcelExportEntity("疾病编码","diseaseCode",20));
excelExportEntities.add(new ExcelExportEntity("是否重疾","isMajorDiseasesDisplay",20));
excelExportEntities.add(new ExcelExportEntity("申请时间","applyTime",20));
excelExportEntities.add(new ExcelExportEntity("申请次数","applyNum",20));
excelExportEntities.add(new ExcelExportEntity("住院医疗费总额(不含门诊)","hospitalSumMoney$",20));
excelExportEntities.add(new ExcelExportEntity("实际报销总额","reimbursementMoney$",20));
excelExportEntities.add(new ExcelExportEntity("(住院医疗)允许报销范围内个人承担部分金额","singleBearMoney$",20));
excelExportEntities.add(new ExcelExportEntity("(住院医疗)自费部分金额","personExpenseMoney$",20));
excelExportEntities.add(new ExcelExportEntity("重病门诊自费金额","outpatientServiceMoney$",20));
excelExportEntities.add(new ExcelExportEntity("非住院肾衰竭治疗、靶向药金额","bxyMoney$",20));
excelExportEntities.add(new ExcelExportEntity("合计","totalMoney",20));
excelExportEntities.add(new ExcelExportEntity("预测补助金额","forecastSubsidyMoney",20));
excelExportEntities.add(new ExcelExportEntity("补助金额","subsidyMoney",20));
excelExportEntities.add(new ExcelExportEntity("爱心基金","loveSubsidyMoney",20));
String exportYear = String.valueOf(pageForm.getYear());
if (StrUtil.isBlank(exportYear)) {
exportYear = String.valueOf(java.time.Year.now().getValue());
}
String fileName = exportYear + "年补助人员列表.xlsx";
try {
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, excelExportEntities, list);
CommonDownloadUtil.download(fileName, workbook, response);
} catch (Exception e) {
log.error("导出补助人员列表失败", e);
}
medicalApplyService.exportStatistics(pageForm, isMajorDiseases, response);
}
}
@@ -2,8 +2,10 @@ package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service;
import com.budwk.app.base.service.BaseService;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalApply;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.param.MedicalPageForm;
import org.nutz.lang.util.NutMap;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
public interface MedicalApplyService extends BaseService<MedicalApply> {
@@ -27,6 +29,16 @@ public interface MedicalApplyService extends BaseService<MedicalApply> {
*/
List<NutMap> getCostList(List<NutMap> list);
/**
* 导出医疗补助统计记录,金额列为数值类型,最后一行为本次导出记录的逐列合计。
* 返回类型为 void,XSSF 文件内容直接写入 response。
*
* @param pageForm 页面筛选与分页条件,包含年度、姓名或工号、疾病、工会、单位、会员类型和排序
* @param isMajorDiseases true 导出重疾,false 导出普通疾病,null 不限制疾病类型
* @param response 接收 Excel 下载内容的 HTTP 响应
*/
void exportStatistics(MedicalPageForm pageForm, Boolean isMajorDiseases, HttpServletResponse response);
/**
* 查询是否有进行中的申请
@@ -3,6 +3,7 @@ package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service;
import com.budwk.app.base.service.BaseService;
import org.nutz.lang.util.NutMap;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
public interface MedicalCalculateMoneyService extends BaseService {
@@ -14,4 +15,12 @@ public interface MedicalCalculateMoneyService extends BaseService {
NutMap getData();
List<NutMap> getApplyData(double ratio);
/**
* 按列表测算口径导出申请数据;返回类型为 void,Excel 列对应申请信息、费用和测算金额。
*
* @param ratio 页面当前补助比例,单位为百分比
* @param response 用于写入 Excel 文件的 HTTP 响应
*/
void doExport(double ratio, HttpServletResponse response);
}
@@ -1,9 +1,16 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.impl;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
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.DateUtil;
import cn.hutool.core.util.StrUtil;
import com.budwk.app.base.page.Pagination;
import com.budwk.app.base.result.Result;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.base.utils.PageUtil;
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
import com.budwk.app.sys.models.Sys_user;
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
@@ -11,7 +18,10 @@ import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalApp
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalApplyCost;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalDiseaseType;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalSetting;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.param.MedicalPageForm;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.MedicalApplyService;
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.dao.Sqls;
@@ -21,9 +31,11 @@ import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.HashMap;
import java.util.List;
@@ -35,6 +47,7 @@ import java.util.Objects;
* @description
*/
@IocBean(args = {"refer:dao"})
@Slf4j
public class MedicalApplyServiceImpl extends BaseServiceImpl<MedicalApply> implements MedicalApplyService {
public MedicalApplyServiceImpl(Dao dao) {
super(dao);
@@ -109,6 +122,151 @@ public class MedicalApplyServiceImpl extends BaseServiceImpl<MedicalApply> imple
return list;
}
@Override
public void exportStatistics(MedicalPageForm pageForm, Boolean isMajorDiseases, HttpServletResponse response) {
// 沿用统计页的筛选、排序及分页范围,合计金额只覆盖本次实际导出的记录。
Sql sql = Sqls.create("""
SELECT
info.*,
type.diseaseName,
type.diseaseCode,
type.isMajorDiseases,
us.username,
us.loginname,
us.unitName,
us.unitCode,
us.unionName,
us.unionCode,
ins.id AS instanceId,
ins.businessNo,
ins.state instanceState,
ins.variable instanceVariable,
ins.processDefineId instanceProcessDefineId,
t.id taskId,
t.taskName AS taskKey,
t.displayName taskName,
t.taskType,
t.performType taskPerformType,
t.taskState,
t.finishTime,
t.taskParentId,
t.variable taskVariable,
IF((SELECT taskName FROM wf_process_task tt WHERE tt.id = t.taskParentId) = 'startTask', 1, 0) canRevoke,
(SELECT MAX(id) FROM wf_process_task WHERE processInstanceId = ins.id AND taskName = 'startTask') AS startTaskId
FROM
medical_apply info
LEFT JOIN medical_disease_type type ON info.diseaseId = type.id
LEFT JOIN aid_fund_member_pay member ON member.userId = info.userId
LEFT JOIN vw_user us ON us.id = info.userId
LEFT JOIN `wf_process_instance` ins ON ins.businessNo = info.id
LEFT JOIN wf_process_task t ON t.processInstanceId = ins.id
AND t.taskState = 10
LEFT JOIN wf_process_task_actor ta ON ta.processTaskId = t.id
$condition
""");
Cnd cnd = Cnd.NEW();
if (StrUtil.isNotBlank(pageForm.getSearchKeyword())) {
SqlExpressionGroup seg = new SqlExpressionGroup();
seg.orLike("us.loginname", pageForm.getSearchKeyword());
seg.orLike("us.username", pageForm.getSearchKeyword());
cnd.and(seg);
}
cnd.andEX("year(info.applyTime)", "=", pageForm.getYear());
cnd.andEX("info.diseaseId", "=", pageForm.getDiseaseId());
cnd.andEX("us.unionId", "=", pageForm.getUnionId());
cnd.andEX("us.unitId", "=", pageForm.getUnitId());
cnd.andEX("member.aidFundMemberUserType", "=", pageForm.getAidFundMemberUserType());
cnd.andEX("type.isMajorDiseases", "=", isMajorDiseases);
if (StrUtil.isNotBlank(pageForm.getPageOrderName()) && StrUtil.isNotBlank(pageForm.getPageOrderBy())) {
cnd.orderBy(pageForm.getPageOrderName(), PageUtil.getOrder(pageForm.getPageOrderBy()));
} else {
cnd.desc("info.applyTime");
}
cnd.and("ins.state", "=", 20);
cnd.groupBy("info.id");
sql.setCondition(cnd);
Pagination<NutMap> pagination = listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
List<NutMap> list = getCostList(pagination.getList());
for (NutMap map : list) {
boolean isMajor = map.getBoolean("isMajorDiseases");
map.put("isMajorDiseasesDisplay", isMajor ? "是" : "否");
}
String exportYear = String.valueOf(pageForm.getYear());
if (StrUtil.isBlank(exportYear)) {
exportYear = String.valueOf(java.time.Year.now().getValue());
}
String fileName = exportYear + "年补助人员列表.xlsx";
try {
Workbook workbook = createStatisticsExportWorkbook(list);
CommonDownloadUtil.download(fileName, workbook, response);
} catch (Exception e) {
log.error("导出补助人员列表失败", e);
}
}
/**
* 生成统计导出工作簿,统一金额列类型并追加逐列合计。
*
* @param list 已补齐费用汇总字段的申请记录,不包含合计行
* @return XSSF 工作簿;申请信息保持原列顺序,十个金额列及末尾合计金额均为数值类型
*/
private Workbook createStatisticsExportWorkbook(List<NutMap> list) {
List<ExcelExportEntity> excelExportEntities = new ArrayList<>();
excelExportEntities.add(new ExcelExportEntity("姓名","username",20));
excelExportEntities.add(new ExcelExportEntity("工号","loginname",20));
excelExportEntities.add(new ExcelExportEntity("单位","unitName",20));
excelExportEntities.add(new ExcelExportEntity("单位编码","unitCode",20));
excelExportEntities.add(new ExcelExportEntity("工会","unionName",20));
excelExportEntities.add(new ExcelExportEntity("工会编码","unionCode",20));
excelExportEntities.add(new ExcelExportEntity("疾病名称","diseaseName",20));
excelExportEntities.add(new ExcelExportEntity("疾病编码","diseaseCode",20));
excelExportEntities.add(new ExcelExportEntity("是否重疾","isMajorDiseasesDisplay",20));
excelExportEntities.add(new ExcelExportEntity("申请时间","applyTime",20));
excelExportEntities.add(new ExcelExportEntity("申请次数","applyNum",20));
excelExportEntities.add(new ExcelExportEntity("住院医疗费总额(不含门诊)","hospitalSumMoney$",20));
excelExportEntities.add(new ExcelExportEntity("实际报销总额","reimbursementMoney$",20));
excelExportEntities.add(new ExcelExportEntity("(住院医疗)允许报销范围内个人承担部分金额","singleBearMoney$",20));
excelExportEntities.add(new ExcelExportEntity("(住院医疗)自费部分金额","personExpenseMoney$",20));
excelExportEntities.add(new ExcelExportEntity("重病门诊自费金额","outpatientServiceMoney$",20));
excelExportEntities.add(new ExcelExportEntity("非住院肾衰竭治疗、靶向药金额","bxyMoney$",20));
excelExportEntities.add(new ExcelExportEntity("合计","totalMoney",20));
excelExportEntities.add(new ExcelExportEntity("预测补助金额","forecastSubsidyMoney",20));
excelExportEntities.add(new ExcelExportEntity("补助金额","subsidyMoney",20));
excelExportEntities.add(new ExcelExportEntity("爱心基金","loveSubsidyMoney",20));
List<String> moneyFields = List.of("hospitalSumMoney$", "reimbursementMoney$", "singleBearMoney$",
"personExpenseMoney$", "outpatientServiceMoney$", "bxyMoney$", "totalMoney",
"forecastSubsidyMoney", "subsidyMoney", "loveSubsidyMoney");
// 与测算页导出一致,金额写入 Excel 数值单元格;工号和单位编码仍按文本输出。
for (ExcelExportEntity entity : excelExportEntities) {
if (moneyFields.contains(entity.getKey())) {
entity.setType(10);
}
}
NutMap summary = new NutMap();
summary.put("username", "合计");
// 空金额按零参与汇总,使用十进制累加避免浮点误差;没有记录时合计也为零。
for (String field : moneyFields) {
BigDecimal sum = BigDecimal.ZERO;
for (NutMap row : list) {
String rawValue = row.getString(field);
BigDecimal money = StrUtil.isBlank(rawValue) ? BigDecimal.ZERO : new BigDecimal(rawValue);
row.put(field, money);
sum = sum.add(money);
}
summary.put(field, sum);
}
List<NutMap> exportList = new ArrayList<>(list);
exportList.add(summary);
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
return ExcelExportUtil.exportExcel(exportParams, excelExportEntities, exportList);
}
@Override
public Boolean getIsAfootMedicalApply(String userId, String id) {
Sql sql = Sqls.create("""
@@ -1,12 +1,19 @@
package com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.impl;
import cn.afterturn.easypoi.excel.ExcelExportUtil;
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.util.StrUtil;
import com.budwk.app.base.service.impl.BaseServiceImpl;
import com.budwk.app.base.utils.CommonDownloadUtil;
import com.budwk.app.flow.enums.ProcessInstanceStateEnum;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.aidFund.model.AidFundMemberPay;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalApply;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.model.MedicalMoneyDetail;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.MedicalApplyService;
import com.budwk.app.zhgh.staffbenefit.medicalMutualAid.medical.service.MedicalCalculateMoneyService;
import org.apache.poi.ss.usermodel.Workbook;
import org.nutz.dao.Cnd;
import org.nutz.dao.Dao;
import org.nutz.dao.Sqls;
@@ -16,9 +23,10 @@ import org.nutz.ioc.loader.annotation.IocBean;
import org.nutz.lang.Lang;
import org.nutz.lang.util.NutMap;
import javax.servlet.http.HttpServletResponse;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -70,10 +78,12 @@ public class MedicalCalculateMoneyServiceImpl extends BaseServiceImpl implements
double subsidyMoney = applyList.stream().mapToDouble(apply -> apply.getDouble("subsidyMoney")).sum();
nutMap.setv("subsidyMoney", subsidyMoney);
//获取申请成功的爱心补助金额
//找到有多少数据
int firstApplyCount = applyList.stream().filter(apply -> apply.getInt("applyNum") == 1).toList().size();
nutMap.setv("loveSubsidyMoney", firstApplyCount * 5000);
// 与列表使用相同的已办结申请记录,汇总实际爱心补助;零金额和空值不增加合计。
BigDecimal loveSubsidyMoney = applyList.stream()
.map(apply -> apply.getString("loveSubsidyMoney"))
.map(money -> StrUtil.isBlank(money) ? BigDecimal.ZERO : new BigDecimal(money))
.reduce(BigDecimal.ZERO, BigDecimal::add);
nutMap.setv("loveSubsidyMoney", loveSubsidyMoney);
//查询申请成功后的住院缴费金额
List<String> applyIds = applyList.stream().map(v -> v.getString("id")).toList();
@@ -85,6 +95,12 @@ public class MedicalCalculateMoneyServiceImpl extends BaseServiceImpl implements
return nutMap;
}
/**
* 返回最新申请年度的已办结测算列表,供页面和导出共用。
*
* @param ratio 补助比例的百分数,例如 60 表示按 60% 测算
* @return 每项为申请及费用字段的 NutMap;subsidyMoney、forecastSubsidyMoney 按笔四舍五入到整数元
*/
@Override
public List<NutMap> getApplyData(double ratio) {
//获取申请最新年份
@@ -98,12 +114,100 @@ public class MedicalCalculateMoneyServiceImpl extends BaseServiceImpl implements
applyList.forEach(apply -> {
apply.putAll(medicalApplyService.calcMedicalMoney(apply.getString("id"),null, ratio));
// 本页按每笔补助四舍五入到元,列表、导出和按比例分配共用取整后的金额;空金额按零处理。
for (String field : List.of("subsidyMoney", "forecastSubsidyMoney")) {
String money = apply.getString(field);
apply.put(field, StrUtil.isBlank(money) ? BigDecimal.ZERO
: new BigDecimal(money).setScale(0, RoundingMode.HALF_UP));
}
});
return applyList;
}
@Override
public void doExport(double ratio, HttpServletResponse response) {
// 导出复用列表查询结果,保证服务端测算金额与页面初次加载时的口径一致。
List<NutMap> list = getApplyData(ratio);
list.forEach(apply -> apply.put("majorDisease", apply.getBoolean("isMajorDiseases") ? "是" : "否"));
List<ExcelExportEntity> exportEntities = new ArrayList<>();
exportEntities.add(new ExcelExportEntity("姓名", "username", 20));
exportEntities.add(new ExcelExportEntity("工号", "loginname", 20));
exportEntities.add(new ExcelExportEntity("所属单位", "unitName", 20));
exportEntities.add(new ExcelExportEntity("所属分工会", "unionName", 20));
exportEntities.add(new ExcelExportEntity("疾病种类", "diseaseName", 20));
exportEntities.add(new ExcelExportEntity("重大疾病", "majorDisease", 20));
exportEntities.add(new ExcelExportEntity("申请时间", "applyTime", 20));
exportEntities.add(new ExcelExportEntity("申请次数", "applyNum", 20));
exportEntities.add(new ExcelExportEntity("起扣时间", "aidFundDeductTime", 20));
exportEntities.add(new ExcelExportEntity("缴费年数", "yearsSinceDeduct", 20));
ExcelExportEntity hospitalSumMoneyExcelExportEntity = new ExcelExportEntity("住院医疗总额(不含门诊)", "hospitalSumMoney$", 20);
hospitalSumMoneyExcelExportEntity.setType(10);
exportEntities.add(hospitalSumMoneyExcelExportEntity);
ExcelExportEntity reimbursementMoneyExcelExportEntity = new ExcelExportEntity("实际报销总额", "reimbursementMoney$", 20);
reimbursementMoneyExcelExportEntity.setType(10);
exportEntities.add(reimbursementMoneyExcelExportEntity);
ExcelExportEntity singleBearMoneyExcelExportEntity = new ExcelExportEntity("(住院医疗)允许报销范围内个人承担部分总额", "singleBearMoney$", 20);
singleBearMoneyExcelExportEntity.setType(10);
exportEntities.add(singleBearMoneyExcelExportEntity);
ExcelExportEntity personExpenseMoneyExcelExportEntity = new ExcelExportEntity("(住院医疗)自费部分总额", "personExpenseMoney$", 20);
personExpenseMoneyExcelExportEntity.setType(10);
exportEntities.add(personExpenseMoneyExcelExportEntity);
ExcelExportEntity outpatientServiceMoneyExcelExportEntity = new ExcelExportEntity("重病门诊自费金额", "outpatientServiceMoney$", 20);
outpatientServiceMoneyExcelExportEntity.setType(10);
exportEntities.add(outpatientServiceMoneyExcelExportEntity);
ExcelExportEntity bxyMoneyExcelExportEntity = new ExcelExportEntity("非住院肾衰竭治疗、靶向药", "bxyMoney$", 20);
bxyMoneyExcelExportEntity.setType(10);
exportEntities.add(bxyMoneyExcelExportEntity);
ExcelExportEntity totalMoneyExcelExportEntity = new ExcelExportEntity("合计金额", "totalMoney", 20);
totalMoneyExcelExportEntity.setType(10);
exportEntities.add(totalMoneyExcelExportEntity);
ExcelExportEntity loveSubsidyMoneyExcelExportEntity = new ExcelExportEntity("爱心补助", "loveSubsidyMoney", 20);
loveSubsidyMoneyExcelExportEntity.setType(10);
exportEntities.add(loveSubsidyMoneyExcelExportEntity);
// 页面“当前补助金额”展示的是测算字段,导出同名列保持相同取值。
ExcelExportEntity subsidyMoneyExcelExportEntity = new ExcelExportEntity("当前补助金额", "forecastSubsidyMoney", 20);
subsidyMoneyExcelExportEntity.setType(10);
exportEntities.add(subsidyMoneyExcelExportEntity);
ExcelExportEntity forecastSubsidyMoneyExcelExportEntity = new ExcelExportEntity("按" + ratio + "%补助金额", "forecastSubsidyMoney", 20);
forecastSubsidyMoneyExcelExportEntity.setType(10);
exportEntities.add(forecastSubsidyMoneyExcelExportEntity);
ExcelExportEntity medicalSubsidyMoneyExcelExportEntity = new ExcelExportEntity("以往累计补助金额", "medicalSubsidyMoney", 20);
medicalSubsidyMoneyExcelExportEntity.setType(10);
exportEntities.add(medicalSubsidyMoneyExcelExportEntity);
try {
List<String> moneyFields = List.of("hospitalSumMoney$", "reimbursementMoney$", "singleBearMoney$",
"personExpenseMoney$", "outpatientServiceMoney$", "bxyMoney$", "totalMoney",
"loveSubsidyMoney", "forecastSubsidyMoney", "medicalSubsidyMoney");
NutMap summary = new NutMap();
summary.put("username", "合计");
// 按金额字段精确累加;两个测算金额列共用的字段只汇总一次,空金额按零处理。
for (String field : moneyFields) {
BigDecimal sum = BigDecimal.ZERO;
for (NutMap row : list) {
String rawValue = row.getString(field);
BigDecimal money = StrUtil.isBlank(rawValue) ? BigDecimal.ZERO : new BigDecimal(rawValue);
row.put(field, money);
sum = sum.add(money);
}
summary.put(field, sum);
}
// 仅在导出数据末尾追加合计行,未查询到申请时各金额列显示数值零。
List<NutMap> exportList = new ArrayList<>(list);
exportList.add(summary);
ExportParams exportParams = new ExportParams();
exportParams.setType(ExcelType.XSSF);
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, exportEntities, exportList);
CommonDownloadUtil.download("测算补助金额汇总表.xlsx", workbook, response);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据申请id查询出住院缴费金额
-79
View File
@@ -54,86 +54,7 @@
<!--
#################################################################################
# #
# debug、info、error分文件输出 start #
# #
#################################################################################
-->
<!-- DEBUG级别日志文件配置 -->
<!--<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/debug.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/debug-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
&lt;!&ndash; INFO级别日志文件配置 &ndash;&gt;
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/info.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/info-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
&lt;!&ndash; ERROR级别日志文件配置 &ndash;&gt;
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>${LOG_HOME}/error.log</file>
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/error-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>300</maxHistory>
</rollingPolicy>
</appender>
&lt;!&ndash; 框架日志级别 &ndash;&gt;
&lt;!&ndash; <logger name="java" additivity="false" />&ndash;&gt;
<logger name="org.eclipse.jetty" level="INFO"/>
<logger name="org.quartz" level="INFO"/>
<logger name="org.nutz" level="DEBUG"/>
&lt;!&ndash; 日志级别和appender的关联 &ndash;&gt;
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="DEBUG_FILE"/>
<appender-ref ref="INFO_FILE"/>
<appender-ref ref="ERROR_FILE"/>
</root>-->
<!--
#################################################################################
# #
# debug、info、error分文件输出 end #
# #
#################################################################################
-->
</configuration>
@@ -54,6 +54,7 @@ layout("/layouts/platform.html"){
<el-card shadow="never" class="mt10">
<table-tool label="基金会员">
<el-button @click="exportChangeRecord" size="small" icon="el-icon-download" type="primary">导出</el-button>
</table-tool>
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder"
@@ -71,7 +72,11 @@ layout("/layouts/platform.html"){
show-overflow-tooltip
v-for="column in tableColumns"
>
<template v-slot="{row}" v-if="column.prop=='changeType'">
<template v-slot="{row}" v-if="column.prop=='aidFundMemberUserType'">
<dict-tag :options="dict.type.AIDFUND_MEMBER_USER_TYPE"
:value="row.aidFundMemberUserType"></dict-tag>
</template>
<template v-slot="{row}" v-else-if="column.prop=='changeType'">
<dict-tag :options="dict.type.AIDFUND_MEMBER_CHANGE_TYPE"
:value="row.changeType"></dict-tag>
</template>
@@ -131,6 +136,10 @@ layout("/layouts/platform.html"){
'info': APPLY_INFO,
},
methods: {
exportChangeRecord() {
// 传入页面筛选和排序参数,服务端按相同条件导出全部匹配记录。
this.$downLoad(loc() + "/exportChangeRecord", this.pageForm)
},
onView(row) {
this.$refs.guava.view(() => {
this.$refs.infoRef.onOpen(row)
@@ -11,9 +11,14 @@ layout("/layouts/platform.html"){
justify-content: center;
font-size: 30px;
}
#app .calculate-money-card > .card-body,
#calcSubsidyTable .el-table__body-wrapper {
overscroll-behavior-y: contain;
}
</style>
<div id="app">
<custom-card>
<custom-card class="calculate-money-card" ref="calculateCard" @hook:mounted="initTableScroll">
<div id="calcSubsidyTable" v-loading="formLoading">
<div class="process-title">1.输入金额(元)</div>
<el-row :gutter="20">
@@ -129,7 +134,7 @@ layout("/layouts/platform.html"){
</el-col>
</el-row>
<el-table :data="applyListData" ref="table" row-key="id" style="width: 100%"
:summary-method="getSummaries" show-summary>
:height="tableHeight" :summary-method="getSummaries" show-summary>
<el-table-column type="index" width="50px" label="序号"
fixed="left"></el-table-column>
<el-table-column
@@ -191,6 +196,7 @@ layout("/layouts/platform.html"){
ratio: 60,
medicalSetting: {},
applyListData: [],
tableHeight: null,
tableSummaries: [],
tableColumns: [
{prop: 'loginname', label: '工号', fixed: "left"},
@@ -215,6 +221,12 @@ layout("/layouts/platform.html"){
],
}
},
watch: {
applyListData() {
// 空列表切换为有数据时也重排固定列和合计,避免底部合计与数据行重叠。
this.$nextTick(() => this.updateTableHeight())
},
},
computed: {
//申请费用总额
@@ -245,6 +257,71 @@ layout("/layouts/platform.html"){
},
},
methods: {
initTableScroll() {
this.$nextTick(() => {
const card = this.$refs.calculateCard
const scrollBody = card && card.$el.querySelector('.card-body')
if (!scrollBody || !this.$refs.table || this._calculateScrollBody === scrollBody) {
return
}
// custom-card 异步挂载后再绑定,滚动仅限当前卡片;销毁时解除监听。
this._calculateScrollBody = scrollBody
scrollBody.addEventListener('wheel', this.handleTableWheel, {passive: false})
window.addEventListener('resize', this.updateTableHeight)
if (window.ResizeObserver) {
this._calculateResizeObserver = new ResizeObserver(() => this.updateTableHeight())
this._calculateResizeObserver.observe(scrollBody)
}
this.updateTableHeight()
})
},
updateTableHeight() {
const scrollBody = this._calculateScrollBody
const table = this.$refs.table
if (!scrollBody || !table) {
return
}
// 列表占满卡片可用高度,表头、合计和底部操作按钮保留在可见区域。
const style = window.getComputedStyle(scrollBody)
const height = Math.max(0, Math.floor(scrollBody.clientHeight
- parseFloat(style.paddingTop) - parseFloat(style.paddingBottom)))
if (this.tableHeight !== height) {
this.$set(this, 'tableHeight', height)
}
this.$nextTick(() => table.doLayout())
},
handleTableWheel(event) {
const scrollBody = this._calculateScrollBody
const table = this.$refs.table
if (!scrollBody || !table || !table.bodyWrapper || event.ctrlKey
|| Math.abs(event.deltaX) > Math.abs(event.deltaY) || !event.deltaY) {
return
}
const style = window.getComputedStyle(scrollBody)
const tableTop = scrollBody.scrollTop + table.$el.getBoundingClientRect().top
- scrollBody.getBoundingClientRect().top - scrollBody.clientTop - parseFloat(style.paddingTop)
const outerLimit = Math.max(0, Math.min(tableTop, scrollBody.scrollHeight - scrollBody.clientHeight))
const listBody = table.bodyWrapper
const innerLimit = Math.max(0, listBody.scrollHeight - listBody.clientHeight)
const unit = event.deltaMode === 1 ? 16 : (event.deltaMode === 2 ? scrollBody.clientHeight : 1)
let delta = event.deltaY * unit
event.preventDefault()
if (delta > 0) {
// 先将列表滚到卡片顶部,再把同次滚轮的剩余距离交给列表内容。
const outerMove = Math.min(delta, Math.max(0, outerLimit - scrollBody.scrollTop))
scrollBody.scrollTop += outerMove
delta -= outerMove
listBody.scrollTop = Math.min(innerLimit, listBody.scrollTop + delta)
} else {
// 反向滚动先回到列表第一行,再返回上方金额和比例区域;边界不带动整页。
if (scrollBody.scrollTop >= outerLimit - 1) {
const innerMove = Math.min(-delta, listBody.scrollTop)
listBody.scrollTop -= innerMove
delta += innerMove
}
scrollBody.scrollTop = Math.max(0, scrollBody.scrollTop + delta)
}
},
async grantOrIntoChange(key, val) {
if (val === '') {
if (key === 'grants') {
@@ -266,7 +343,9 @@ layout("/layouts/platform.html"){
const values = data.map(i => Number(i[column.property]))
if (values && values.length > 0) {
const everyColumnSum = values.reduce((a, b) => a + b)
sums[index] = isNaN(everyColumnSum) ? 0 : everyColumnSum.toFixed(2)
// 补助合计累加已按笔取整的金额,其他费用仍保留两位小数。
const digits = ['subsidyMoney', 'forecastSubsidyMoney'].includes(column.property) ? 0 : 2
sums[index] = isNaN(everyColumnSum) ? 0 : everyColumnSum.toFixed(digits)
}
}
})
@@ -340,6 +419,18 @@ layout("/layouts/platform.html"){
async created() {
await this.getMedicalSetting()
this.getData()
},
mounted() {
this.initTableScroll()
},
beforeDestroy() {
if (this._calculateScrollBody) {
this._calculateScrollBody.removeEventListener('wheel', this.handleTableWheel)
}
if (this._calculateResizeObserver) {
this._calculateResizeObserver.disconnect()
}
window.removeEventListener('resize', this.updateTableHeight)
}
})
@@ -2,9 +2,62 @@
layout("/layouts/platform.html"){
#-->
<style>
/* 查询区保持可见,申请列表使用页面剩余高度。 */
.medical-statistics-layout {
height: calc(100vh - 84px);
min-height: 0;
overflow: hidden;
}
.medical-statistics-layout > .transition-item {
height: 100%;
min-height: 0;
display: flex;
flex-direction: column;
gap: 15px;
overflow: hidden;
}
.medical-statistics-layout .medical-statistics-search-card {
flex: 0 0 auto;
}
.medical-statistics-layout .medical-statistics-table-card {
flex: 1 1 0;
min-height: 0;
margin-top: 0;
overflow: hidden;
}
.medical-statistics-layout .medical-statistics-table-card > .el-card__body {
height: 100%;
min-height: 0;
box-sizing: border-box;
display: flex;
flex-direction: column;
overflow: hidden;
}
.medical-statistics-layout .medical-statistics-table-card .ele-table-tool {
flex: 0 0 auto;
}
.medical-statistics-layout .medical-statistics-table-container {
flex: 1 1 0;
min-height: 0;
overflow: hidden;
}
.medical-statistics-layout .medical-statistics-main-table .el-table__body-wrapper {
overflow-y: auto;
}
</style>
<div id="app">
<guava ref="guava">
<el-card shadow="never">
<guava ref="guava" class="medical-statistics-layout"
@hook:mounted="initTableLayout" @vchange="initTableLayout">
<el-card shadow="never" class="medical-statistics-search-card">
<search @search="doSearch">
<search-item label="年度">
<el-date-picker
@@ -53,7 +106,7 @@ layout("/layouts/platform.html"){
</search>
</el-card>
<el-card shadow="never">
<el-card shadow="never" class="medical-statistics-table-card">
<table-tool label="申请列表">
<el-dropdown @command="doExport">
<el-button type="primary" size="small">导出 <i class="el-icon-arrow-down el-icon--right"></i></el-button>
@@ -63,47 +116,50 @@ layout("/layouts/platform.html"){
</el-dropdown-menu>
</el-dropdown>
</table-tool>
<el-table :data="tableData" :size="tableSize" @sort-change="pageOrder"
ref="table" row-key="id" style="width: 100%"
v-loading="tableLoading"
:summary-method="getSummaries" :show-summary="showSummary">
<el-table-column type="index" width="50px" label="序号" :index="indexMethod"
fixed="left"></el-table-column>
<el-table-column
:label="column.label"
:prop="column.prop"
:key="column.prop"
:width="column.width"
:fixed="column.fixed"
:sortable="column.sortable"
show-overflow-tooltip
sortable
v-for="column in tableColumns"
>
<template v-if="column.prop==='isMajorDiseases'" v-slot="{row}">
<span :class="[row.isMajorDiseases?'text-danger':'text-info']">
<i class="fa fa-circle ml5"></i>
{{row.isMajorDiseases?'是':'否'}}({{row.applyNum}})
</span>
</template>
<template v-slot="{ row }" v-else-if="column.prop === 'instanceState'">
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message"
size="small"></enum-tag>
</template>
<template v-slot="{ row }" v-else-if="column.prop === 'curTaskName'">
{{row.curTaskName?row.curTaskName:row.taskName}}
</template>
</el-table-column>
<el-table-column label="操作" fixed="right" width="100">
<template slot-scope="{row}">
<el-button @click="onView(row)" size="mini" type="primary">查看</el-button>
<el-button v-if="row.taskKey === 'startTask' || !row.instanceId" @click="onDelete(row.id)"
size="mini" type="danger">
删除
</el-button>
<div ref="tableContainer" class="medical-statistics-table-container">
<el-table class="medical-statistics-main-table" :height="tableHeight"
:data="tableData" :size="tableSize" @sort-change="pageOrder"
ref="table" row-key="id" style="width: 100%"
v-loading="tableLoading"
:summary-method="getSummaries" :show-summary="showSummary">
<el-table-column type="index" width="50px" label="序号" :index="indexMethod"
fixed="left"></el-table-column>
<el-table-column
:label="column.label"
:prop="column.prop"
:key="column.prop"
:width="column.width"
:fixed="column.fixed"
:sortable="column.sortable"
show-overflow-tooltip
sortable
v-for="column in tableColumns"
>
<template v-if="column.prop==='isMajorDiseases'" v-slot="{row}">
<span :class="[row.isMajorDiseases?'text-danger':'text-info']">
<i class="fa fa-circle ml5"></i>
{{row.isMajorDiseases?'是':'否'}}({{row.applyNum}})
</span>
</template>
<template v-slot="{ row }" v-else-if="column.prop === 'instanceState'">
<enum-tag :value="row.instanceState" name="ProcessInstanceStateEnum" label_key="message"
size="small"></enum-tag>
</template>
<template v-slot="{ row }" v-else-if="column.prop === 'curTaskName'">
{{row.curTaskName?row.curTaskName:row.taskName}}
</template>
</el-table-column>
</el-table>
<el-table-column label="操作" fixed="right" width="100">
<template slot-scope="{row}">
<el-button @click="onView(row)" size="mini" type="primary">查看</el-button>
<el-button v-if="row.taskKey === 'startTask' || !row.instanceId" @click="onDelete(row.id)"
size="mini" type="danger">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</div>
</el-card>
<template #edit>
@@ -276,13 +332,18 @@ layout("/layouts/platform.html"){
pageSize: 9999,
isMajorDiseases: null
},
medicalSetting: {}
medicalSetting: {},
tableHeight: 300
}
},
components: {
"medical-info": MEDICAL_INFO
},
watch: {
// 年度切换可能使合计行从隐藏变为显示,待数据渲染后重新测量表头、合计行和固定列。
tableData() {
this.$nextTick(() => this.updateTableHeight())
},
"localPageForm": {
handler(val) {
this.pageForm = {...this.pageForm, ...val}
@@ -291,6 +352,34 @@ layout("/layouts/platform.html"){
},
},
methods: {
// guava 异步挂载后才存在列表容器;重复调用时复用监听,避免首次进入漏掉初始化。
initTableLayout() {
this.$nextTick(() => {
const container = this.$refs.tableContainer
if (!container || !this.$refs.table) return
if (!this._tableResizeObserver) {
this._tableResizeObserver = new ResizeObserver(() => this.updateTableHeight())
this._tableResizeObserver.observe(container)
}
this.updateTableHeight()
})
},
// 独立容器占用工具栏下方的剩余空间,隐藏列表时保留高度,返回列表后再刷新布局。
updateTableHeight() {
const container = this.$refs.tableContainer
const table = this.$refs.table
if (!container || !table || container.clientHeight <= 0) return
const height = container.clientHeight
if (this.tableHeight !== height) {
this.$set(this, "tableHeight", height)
}
// 空数据变为有数据时高度可能不变,仍需重算合计行占用空间。
this.$nextTick(() => {
if (this.$refs.table) this.$refs.table.doLayout()
})
},
onDelete(id) {
this.$confirm("您确定要删除吗?", "提示", {
confirmButtonText: "确定",
@@ -403,6 +492,14 @@ layout("/layouts/platform.html"){
this.getDiseaseList()
this.unionList = await this.$businessTool.listUnion()
this.unitList = await this.$businessTool.listUnit()
},
mounted() {
this.initTableLayout()
},
beforeDestroy() {
if (this._tableResizeObserver) {
this._tableResizeObserver.disconnect()
}
}
})
</script>