Merge branch 'feature_周何峰'
This commit is contained in:
+4
-2
@@ -157,12 +157,14 @@ public class RecuperationEvaluateStatisticsController {
|
||||
List<ExcelExportEntity> columns = new ArrayList<>();
|
||||
columns.add(new ExcelExportEntity("序号", "no", 10));
|
||||
columns.add(new ExcelExportEntity("报名方式", "typeName", 12));
|
||||
columns.add(new ExcelExportEntity("方案名称", "schemeName", 24));
|
||||
columns.add(new ExcelExportEntity("方案名称", "projectName", 24));
|
||||
columns.add(new ExcelExportEntity("旅行社", "travelAgencyName", 22));
|
||||
columns.add(new ExcelExportEntity("评价人", "userName", 14));
|
||||
columns.add(new ExcelExportEntity("所属单位", "unitName", 24));
|
||||
columns.add(new ExcelExportEntity("旅行社评分", "evaluationForTravelAgency", 14));
|
||||
columns.add(new ExcelExportEntity("住宿/酒店评分", "evaluationForAccommodation", 16));
|
||||
String accommodationLabel = "线路".equals(typeName) ? "住宿评分"
|
||||
: ("灵活组团".equals(typeName) || "定点".equals(typeName) ? "酒店评分" : "住宿/酒店评分");
|
||||
columns.add(new ExcelExportEntity(accommodationLabel, "evaluationForAccommodation", 16));
|
||||
columns.add(new ExcelExportEntity("行程评分", "evaluationForJourney", 14));
|
||||
columns.add(new ExcelExportEntity("餐饮评分", "evaluationForDining", 14));
|
||||
columns.add(new ExcelExportEntity("交通评分", "evaluationForTransportation", 14));
|
||||
|
||||
+11
-10
@@ -62,10 +62,11 @@ public class RecuperationFlexibleGroupQueryController {
|
||||
return Result.success(flexibleGroupService.querySignUsers(pageForm, groupId, groupLeaderUserId, groupLeaderLoginName, noGroupLeader));
|
||||
}
|
||||
|
||||
/** pageForm 传分页信息,groupId 传灵活组团 ID;data 返回全部报名人员的分页结构。 */
|
||||
@At
|
||||
@SaCheckLogin
|
||||
public Result getAllSignUser(PageForm pageForm, String groupId) {
|
||||
return Result.success(flexibleGroupService.querySignUsers(pageForm, groupId, null, null, false));
|
||||
return Result.success(flexibleGroupService.queryAllSignUsers(pageForm, groupId));
|
||||
}
|
||||
|
||||
@At
|
||||
@@ -98,16 +99,16 @@ public class RecuperationFlexibleGroupQueryController {
|
||||
String searchKeyword, HttpServletResponse response) {
|
||||
List<ExcelExportEntity> columns = new ArrayList<>();
|
||||
columns.add(new ExcelExportEntity("序号", "no", 10));
|
||||
columns.add(new ExcelExportEntity("工号", "loginName", 18));
|
||||
columns.add(new ExcelExportEntity("姓名", "userName", 14));
|
||||
columns.add(new ExcelExportEntity("联系电话", "mobile", 18));
|
||||
columns.add(new ExcelExportEntity("身份证号", "idCard", 24));
|
||||
columns.add(new ExcelExportEntity("单位", "unitName", 24));
|
||||
columns.add(new ExcelExportEntity("工会", "unionName", 20));
|
||||
columns.add(new ExcelExportEntity("旅行社", "travelAgencyName", 22));
|
||||
columns.add(new ExcelExportEntity("工号", "loginName", 20));
|
||||
columns.add(new ExcelExportEntity("姓名", "userName", 20));
|
||||
columns.add(new ExcelExportEntity("联系电话", "mobile", 20));
|
||||
columns.add(new ExcelExportEntity("身份证号", "idCard", 30));
|
||||
columns.add(new ExcelExportEntity("单位", "unitName", 30));
|
||||
columns.add(new ExcelExportEntity("工会", "unionName", 30));
|
||||
columns.add(new ExcelExportEntity("旅行社", "travelAgencyName", 30));
|
||||
columns.add(new ExcelExportEntity("报名时间", "signingUptime", 20));
|
||||
columns.add(new ExcelExportEntity("团长", "leaderName", 20));
|
||||
columns.add(new ExcelExportEntity("是否成团", "formedTeamState", 14));
|
||||
columns.add(new ExcelExportEntity("团长", "leaderName", 25));
|
||||
columns.add(new ExcelExportEntity("是否成团", "formedTeamState", 15));
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
Workbook workbook = ExcelExportUtil.exportExcel(exportParams, columns,
|
||||
|
||||
+21
@@ -13,6 +13,9 @@ import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.mvc.annotation.At;
|
||||
import org.nutz.mvc.annotation.Ok;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
|
||||
/** PC 端线路报名情况及成团通知。 */
|
||||
@IocBean
|
||||
@At("/platform/recuperation/lineStatistics")
|
||||
@@ -48,6 +51,24 @@ public class RecuperationLineStatisticsController {
|
||||
return Result.success(statisticsService.lineOptions(startYear, endYear, signUpMode, regionalNature));
|
||||
}
|
||||
|
||||
/** lineId 为线路 ID;data 中 selectId 表示具体出行批次,times 为时间及报名人数说明。 */
|
||||
@At
|
||||
@SaCheckPermission("recuperation.lineStatistics")
|
||||
public Result getLinePlayTimes(String lineId, Integer startYear, Integer endYear) {
|
||||
return Result.success(statisticsService.linePlayTimeOptions(lineId, startYear, endYear));
|
||||
}
|
||||
|
||||
/** 按当前筛选条件导出达到最低成团人数的线路人员,并按线路生成 Excel 压缩包。 */
|
||||
@At
|
||||
@Ok("void")
|
||||
@SaCheckPermission("recuperation.lineStatistics")
|
||||
public void doExport(Integer startYear, Integer endYear, String unionId, String takePartInLineId,
|
||||
String lotId, String selectId, String regionalNature,
|
||||
HttpServletResponse response) throws IOException {
|
||||
statisticsService.exportFormedLineUsers(startYear, endYear, unionId, takePartInLineId,
|
||||
lotId, selectId, regionalNature, response);
|
||||
}
|
||||
|
||||
@At
|
||||
@SaCheckPermission("recuperation.lineStatistics")
|
||||
public Result countSuccessNotice(String id) { return Result.success(enrollService.countLineGroupNoticeUsers(id, true)); }
|
||||
|
||||
+28
@@ -4,6 +4,8 @@ import com.budwk.app.base.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
|
||||
/** PC 端线路成团统计查询。 */
|
||||
@@ -14,4 +16,30 @@ public interface RecuperationLineStatisticsService {
|
||||
Pagination userPage(PageForm pageForm, String lineSelectId, String searchKeyword, String unionId, String unitId);
|
||||
|
||||
List<NutMap> lineOptions(Integer startYear, Integer endYear, String signUpMode, String regionalNature);
|
||||
|
||||
/**
|
||||
* 查询指定线路在年度范围内的出行批次选项。
|
||||
*
|
||||
* @param lineId 线路 ID
|
||||
* @param startYear 开始年度
|
||||
* @param endYear 结束年度
|
||||
* @return selectId 为工会选线记录 ID,times 为出行时间及报名人数说明
|
||||
*/
|
||||
List<NutMap> linePlayTimeOptions(String lineId, Integer startYear, Integer endYear);
|
||||
|
||||
/**
|
||||
* 导出达到最低成团人数的线路报名人员。
|
||||
*
|
||||
* @param startYear 开始年度
|
||||
* @param endYear 结束年度
|
||||
* @param unionId 报名人员所属工会 ID
|
||||
* @param lineId 线路 ID
|
||||
* @param lotId 标段 ID
|
||||
* @param selectId 工会选线记录 ID,用于限定具体出行批次
|
||||
* @param regionalNature 线路类型,可传省内、省外或空值
|
||||
* @param response 返回 ZIP 文件的 HTTP 响应
|
||||
*/
|
||||
void exportFormedLineUsers(Integer startYear, Integer endYear, String unionId, String lineId,
|
||||
String lotId, String selectId, String regionalNature,
|
||||
HttpServletResponse response) throws IOException;
|
||||
}
|
||||
|
||||
+9
@@ -105,6 +105,15 @@ public interface RecuperationProvinceFlexibleGroupService extends BaseService<Re
|
||||
Pagination querySignUsers(PageForm pageForm, String groupId, String groupLeaderUserId,
|
||||
String groupLeaderLoginName, Boolean noGroupLeader);
|
||||
|
||||
/**
|
||||
* 查询一个灵活组团的全部报名人员,并按团长、团内身份和报名时间排序。
|
||||
*
|
||||
* @param pageForm 分页信息
|
||||
* @param groupId 灵活组团 ID
|
||||
* @return 报名人员分页数据
|
||||
*/
|
||||
Pagination queryAllSignUsers(PageForm pageForm, String groupId);
|
||||
|
||||
/** 查询年度区间内可用旅行社。 */
|
||||
List<NutMap> queryTravelAgencyOptions(Integer startYear, Integer endYear);
|
||||
|
||||
|
||||
+16
-7
@@ -1594,7 +1594,7 @@ public class RecuperationEnrollServiceImpl extends BaseServiceImpl<RecuperationE
|
||||
AND rating.evaluationForAccommodation IS NOT NULL AND rating.evaluationForDining IS NOT NULL
|
||||
AND rating.evaluationForTransportation IS NOT NULL THEN rating.loginName END) AS ratingCount
|
||||
FROM the_rapy_recuperation_travel_agency agency
|
||||
LEFT JOIN ($ratingSql) rating ON rating.travelAgencyId=agency.id
|
||||
LEFT JOIN ($ratingSql) rating ON rating.travelAgencyId=agency.id AND rating.statisticYear=@year
|
||||
WHERE agency.year=@year
|
||||
GROUP BY agency.id, agency.travelAgencyName
|
||||
ORDER BY CASE WHEN avg IS NULL THEN 1 ELSE 0 END, avg DESC, ratingCount DESC, agency.travelAgencyName
|
||||
@@ -1623,7 +1623,7 @@ public class RecuperationEnrollServiceImpl extends BaseServiceImpl<RecuperationE
|
||||
AND rating.evaluationForAccommodation IS NOT NULL AND rating.evaluationForDining IS NOT NULL AND rating.evaluationForTransportation IS NOT NULL THEN rating.loginName
|
||||
WHEN rating.typeName IN ('灵活组团','定点') AND rating.evaluationForTravelAgency IS NOT NULL AND rating.evaluationForAccommodation IS NOT NULL THEN rating.loginName END) AS ratingCount
|
||||
FROM ($ratingSql) rating
|
||||
WHERE IFNULL(rating.schemeId,'')<>''
|
||||
WHERE rating.statisticYear=@year AND IFNULL(rating.schemeId,'')<>''
|
||||
GROUP BY rating.typeName, rating.schemeId, rating.schemeName, rating.travelAgencyName
|
||||
ORDER BY FIELD(rating.typeName,'线路','灵活组团','定点'), rating.schemeName
|
||||
""");
|
||||
@@ -1635,12 +1635,20 @@ public class RecuperationEnrollServiceImpl extends BaseServiceImpl<RecuperationE
|
||||
@Override
|
||||
public List<NutMap> satisfactionFeedbackDetails(Integer year, String typeName) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT rating.*,
|
||||
SELECT rating.*, rating.schemeName AS projectName,
|
||||
ROUND(CASE WHEN rating.typeName='线路' THEN
|
||||
(rating.evaluationForTravelAgency+rating.evaluationForJourney+rating.evaluationForAccommodation+rating.evaluationForDining+rating.evaluationForTransportation)/5
|
||||
ELSE (rating.evaluationForTravelAgency+rating.evaluationForAccommodation)/2 END,1) AS compositeScore
|
||||
FROM ($ratingSql) rating
|
||||
WHERE rating.feedbackContent IS NOT NULL AND rating.feedbackContent<>''
|
||||
WHERE rating.statisticYear=@year AND (
|
||||
rating.evaluationForTravelAgency IS NOT NULL
|
||||
OR rating.evaluationForJourney IS NOT NULL
|
||||
OR rating.evaluationForAccommodation IS NOT NULL
|
||||
OR rating.evaluationForDining IS NOT NULL
|
||||
OR rating.evaluationForTransportation IS NOT NULL
|
||||
OR rating.evaluationForLine IS NOT NULL
|
||||
OR IFNULL(rating.feedbackContent,'')<>''
|
||||
)
|
||||
ORDER BY rating.signingUptime DESC
|
||||
""");
|
||||
sql.setVar("ratingSql", satisfactionBaseSql()).setParam("year", year == null ? DateUtil.thisYear() : year);
|
||||
@@ -1652,9 +1660,10 @@ public class RecuperationEnrollServiceImpl extends BaseServiceImpl<RecuperationE
|
||||
/** 满意度统计与明细共用同一报名方式、方案和旅行社映射。 */
|
||||
private String satisfactionBaseSql() {
|
||||
return """
|
||||
SELECT enroll.id, enroll.loginName, enroll.userName, enroll.unitName, enroll.signingUptime, enroll.feedbackContent,
|
||||
SELECT enroll.id, enroll.loginName, enroll.userName, enroll.unitName, enroll.signingUptime,
|
||||
YEAR(enroll.signingUptime) AS statisticYear, enroll.feedbackContent,
|
||||
enroll.evaluationForTravelAgency, enroll.evaluationForJourney, enroll.evaluationForAccommodation,
|
||||
enroll.evaluationForDining, enroll.evaluationForTransportation,
|
||||
enroll.evaluationForDining, enroll.evaluationForTransportation, enroll.evaluationForLine,
|
||||
CASE WHEN enroll.takePartInLineId IS NOT NULL THEN '线路' WHEN enroll.takePartInBaseManagementId IS NOT NULL THEN '定点' ELSE '灵活组团' END AS typeName,
|
||||
CASE WHEN enroll.takePartInLineId IS NOT NULL THEN COALESCE(line.id,directLine.id) WHEN enroll.takePartInBaseManagementId IS NOT NULL THEN base.id ELSE COALESCE(flexible.id,enroll.takePartInTravelAgencyId) END AS schemeId,
|
||||
CASE WHEN enroll.takePartInLineId IS NOT NULL THEN COALESCE(line.lineName,directLine.lineName) WHEN enroll.takePartInBaseManagementId IS NOT NULL THEN base.baseName ELSE COALESCE(flexible.groupName, directAgency.travelAgencyName) END AS schemeName,
|
||||
@@ -1676,7 +1685,7 @@ public class RecuperationEnrollServiceImpl extends BaseServiceImpl<RecuperationE
|
||||
AND firstFlexible.year=YEAR(enroll.signingUptime) AND firstFlexible.isDisabled=false
|
||||
ORDER BY firstFlexible.sortNumber, firstFlexible.id LIMIT 1
|
||||
)
|
||||
WHERE enroll.isNormal=true AND enroll.isTakePartIn=true AND YEAR(enroll.signingUptime)=@year
|
||||
WHERE enroll.isNormal=true AND enroll.isTakePartIn=true
|
||||
""";
|
||||
}
|
||||
}
|
||||
|
||||
+212
-1
@@ -1,13 +1,20 @@
|
||||
package com.budwk.app.zhgh.staffbenefit.recuperation.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.page.Pagination;
|
||||
import com.budwk.app.base.param.PageForm;
|
||||
import com.budwk.app.base.service.impl.BaseServiceImpl;
|
||||
import com.budwk.app.web.commons.auth.utils.AuthUtil;
|
||||
import com.budwk.app.web.commons.auth.utils.SecurityUtil;
|
||||
import com.budwk.app.zhgh.staffbenefit.recuperation.common.RecuperationCommon;
|
||||
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationConfig;
|
||||
import com.budwk.app.zhgh.staffbenefit.recuperation.model.RecuperationEnroll;
|
||||
import com.budwk.app.zhgh.staffbenefit.recuperation.service.RecuperationLineStatisticsService;
|
||||
import org.apache.poi.ss.usermodel.Workbook;
|
||||
import org.nutz.dao.Cnd;
|
||||
import org.nutz.dao.Dao;
|
||||
import org.nutz.dao.Sqls;
|
||||
@@ -15,7 +22,21 @@ import org.nutz.dao.sql.Sql;
|
||||
import org.nutz.ioc.loader.annotation.IocBean;
|
||||
import org.nutz.lang.util.NutMap;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.net.URLEncoder;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
import java.util.zip.ZipEntry;
|
||||
import java.util.zip.ZipOutputStream;
|
||||
|
||||
/** 线路统计服务,统一限定审核通过且未调出的报名记录。 */
|
||||
@IocBean(args = {"refer:dao"})
|
||||
@@ -26,11 +47,12 @@ public class RecuperationLineStatisticsServiceImpl extends BaseServiceImpl<Recup
|
||||
public Pagination pageData(PageForm pageForm, Integer startYear, Integer endYear, String unionId,
|
||||
String lineId, String lotId, String signUpMode, String selectId, String regionalNature) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT line.id, line.lineName, line.regionalNature, lineu.id AS lineUId, lineu.signUpMode,
|
||||
SELECT line.id, line.id AS lineId, line.lineName, line.regionalNature, lineu.id AS lineUId, lineu.signUpMode,
|
||||
YEAR(lineu.selectTime) AS year,
|
||||
CONCAT(DATE_FORMAT(lineu.playStartTime,'%m月%d日'),'-',DATE_FORMAT(lineu.playEndTime,'%m月%d日')) AS linePlayTime,
|
||||
agency.travelAgencyName, agency.contact, agency.contactMobileNumber,
|
||||
IF(lineu.signUpMode=2,'校工会',un.name) AS unionName, lot.lotName,
|
||||
MAX(enroll.takePartInUnionId) AS usUnionId,
|
||||
COALESCE((SELECT outsideQuota FROM the_rapy_recuperation_config LIMIT 1),0) AS minimumGroupSize,
|
||||
COUNT(DISTINCT enroll.loginName) AS lineNum,
|
||||
IF(COALESCE((SELECT familyInfo FROM the_rapy_recuperation_config LIMIT 1),1)=2,
|
||||
@@ -94,4 +116,193 @@ public class RecuperationLineStatisticsServiceImpl extends BaseServiceImpl<Recup
|
||||
sql.setCondition(cnd);
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> linePlayTimeOptions(String lineId, Integer startYear, Integer endYear) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT lineu.id AS selectId,
|
||||
CONCAT(
|
||||
DATE_FORMAT(lineu.playStartTime,'%m月%d日'),'-',DATE_FORMAT(lineu.playEndTime,'%m月%d日'),
|
||||
'(报名人数:',
|
||||
COUNT(DISTINCT enroll.loginName) +
|
||||
IF(COALESCE((SELECT familyInfo FROM the_rapy_recuperation_config LIMIT 1),1)=2,
|
||||
(SELECT COUNT(1)
|
||||
FROM the_rapy_recuperation_enroll_companion companion
|
||||
WHERE companion.trreId IN (
|
||||
SELECT signed.id
|
||||
FROM the_rapy_recuperation_enroll signed
|
||||
WHERE signed.takePartInLineId=lineu.id
|
||||
AND signed.isNormal=true
|
||||
AND signed.stateId NOT IN (2715,2725,2735)
|
||||
)),
|
||||
COALESCE(SUM(enroll.familyNumber),0)),
|
||||
',其中家属:',
|
||||
IF(COALESCE((SELECT familyInfo FROM the_rapy_recuperation_config LIMIT 1),1)=2,
|
||||
(SELECT COUNT(1)
|
||||
FROM the_rapy_recuperation_enroll_companion companion
|
||||
WHERE companion.trreId IN (
|
||||
SELECT signed.id
|
||||
FROM the_rapy_recuperation_enroll signed
|
||||
WHERE signed.takePartInLineId=lineu.id
|
||||
AND signed.isNormal=true
|
||||
AND signed.stateId NOT IN (2715,2725,2735)
|
||||
)),
|
||||
COALESCE(SUM(enroll.familyNumber),0)),
|
||||
'人)'
|
||||
) AS times
|
||||
FROM the_rapy_recuperation_line_union_select lineu
|
||||
LEFT JOIN the_rapy_recuperation_enroll enroll
|
||||
ON enroll.takePartInLineId=lineu.id
|
||||
AND enroll.isNormal=true
|
||||
AND enroll.stateId NOT IN (2715,2725,2735)
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.where("lineu.lineId", "=", lineId)
|
||||
.andEX("YEAR(lineu.selectTime)", ">=", startYear)
|
||||
.andEX("YEAR(lineu.selectTime)", "<=", endYear);
|
||||
if (!AuthUtil.hasRoleOr("sysadmin", "H06", "A06")) {
|
||||
cnd.and("lineu.unionId", "=", SecurityUtil.getUnionId());
|
||||
}
|
||||
cnd.groupBy("lineu.id", "lineu.playStartTime", "lineu.playEndTime");
|
||||
cnd.asc("lineu.playStartTime");
|
||||
sql.setCondition(cnd);
|
||||
return listMap(sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void exportFormedLineUsers(Integer startYear, Integer endYear, String unionId, String lineId,
|
||||
String lotId, String selectId, String regionalNature,
|
||||
HttpServletResponse response) throws IOException {
|
||||
List<NutMap> exportRows = queryLineExportRows(startYear, endYear, unionId, lineId, lotId, selectId, regionalNature);
|
||||
RecuperationConfig config = dao().fetch(RecuperationConfig.class, Cnd.NEW());
|
||||
Integer minimumGroupSize = config == null ? null : config.getOutsideQuota();
|
||||
|
||||
Map<String, List<NutMap>> batchGroups = exportRows.stream().collect(Collectors.groupingBy(
|
||||
row -> StrUtil.blankToDefault(row.getString("takePartInLineId"), ""),
|
||||
LinkedHashMap::new,
|
||||
Collectors.toList()));
|
||||
List<NutMap> formedRows = batchGroups.values().stream()
|
||||
.filter(rows -> isFormedGroup(rows, minimumGroupSize))
|
||||
.flatMap(List::stream)
|
||||
.collect(Collectors.toList());
|
||||
Map<String, List<NutMap>> lineGroups = formedRows.stream().collect(Collectors.groupingBy(
|
||||
row -> StrUtil.blankToDefault(row.getString("lineId"), ""),
|
||||
LinkedHashMap::new,
|
||||
Collectors.toList()));
|
||||
|
||||
response.setContentType("application/zip");
|
||||
response.setHeader("Content-Disposition", "attachment;filename="
|
||||
+ URLEncoder.encode("疗休养集体线路报名人员名单.zip", StandardCharsets.UTF_8));
|
||||
Set<String> zipEntryNames = new HashSet<>();
|
||||
try (ZipOutputStream zipOutputStream = new ZipOutputStream(new BufferedOutputStream(response.getOutputStream()))) {
|
||||
for (List<NutMap> rows : lineGroups.values()) {
|
||||
String lineName = rows.isEmpty() ? "未命名" : StrUtil.blankToDefault(rows.get(0).getString("lineName"), "未命名");
|
||||
writeExcelToZip(zipOutputStream, zipEntryNames, lineName, rows);
|
||||
}
|
||||
zipOutputStream.flush();
|
||||
}
|
||||
response.flushBuffer();
|
||||
}
|
||||
|
||||
/** 查询导出所需的审核通过线路报名人员,并按当前登录角色限制可见工会。 */
|
||||
private List<NutMap> queryLineExportRows(Integer startYear, Integer endYear, String unionId, String lineId,
|
||||
String lotId, String selectId, String regionalNature) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT line.id AS lineId, line.lineName, line.regionalNature, agency.travelAgencyName,
|
||||
enroll.*, DATE_FORMAT(lineu.playStartTime,'%Y-%m-%d') AS playStartTime,
|
||||
IF(COALESCE((SELECT familyInfo FROM the_rapy_recuperation_config LIMIT 1),1)=2,
|
||||
(SELECT COUNT(1) FROM the_rapy_recuperation_enroll_companion companion WHERE companion.trreId=enroll.id),
|
||||
COALESCE(enroll.familyNumber,0)) AS familyCount,
|
||||
(SELECT GROUP_CONCAT(
|
||||
companion.userName,'-',companion.sex,'(',companion.idcard,'、',companion.mobile,')-',
|
||||
IFNULL(bed.bedType,''),',家属备注:',IFNULL(companion.remark,'')
|
||||
SEPARATOR ';')
|
||||
FROM the_rapy_recuperation_enroll_companion companion
|
||||
LEFT JOIN the_rapy_recuperation_enroll_bed bed ON bed.id=companion.bedInfoId
|
||||
WHERE companion.trreId=enroll.id) AS companionInfo,
|
||||
user_info.arrivalAtSchoolDate AS schoolTime
|
||||
FROM the_rapy_recuperation_enroll enroll
|
||||
LEFT JOIN sys_user user_info ON user_info.loginname=enroll.loginName
|
||||
LEFT JOIN the_rapy_recuperation_line_union_select lineu ON lineu.id=enroll.takePartInLineId
|
||||
LEFT JOIN the_rapy_recuperation_line line ON line.id=lineu.lineId
|
||||
LEFT JOIN the_rapy_recuperation_travel_agency agency ON agency.id=lineu.travelAgencyId
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.where("enroll.stateId", "=", 2750)
|
||||
.and("enroll.isNormal", "=", true)
|
||||
.and("enroll.takePartInLineId", "is not", null)
|
||||
.and("enroll.takePartInLineId", "!=", "")
|
||||
.andEX("YEAR(enroll.signingUptime)", ">=", startYear)
|
||||
.andEX("YEAR(enroll.signingUptime)", "<=", endYear)
|
||||
.andEX("line.id", "=", lineId)
|
||||
.andEX("line.lotId", "=", lotId)
|
||||
.andEX("lineu.id", "=", selectId)
|
||||
.andEX("line.regionalNature", "=", regionalNature);
|
||||
if (AuthUtil.hasRoleOr("sysadmin", "H06", "A06")) {
|
||||
cnd.andEX("enroll.selfUnionId", "=", unionId);
|
||||
} else {
|
||||
cnd.and(Cnd.exps("enroll.takePartInUnionId", "=", SecurityUtil.getUnionId())
|
||||
.or("enroll.selfUnionId", "=", SecurityUtil.getUnionId()));
|
||||
}
|
||||
cnd.asc("line.lineName").asc("lineu.playStartTime").asc("enroll.unionName");
|
||||
sql.setCondition(cnd);
|
||||
List<NutMap> rows = listMap(sql);
|
||||
rows.forEach(row -> row.setv("remark", RecuperationCommon.getRemarkBySchoolTime(row.getString("schoolTime"))));
|
||||
return rows;
|
||||
}
|
||||
|
||||
/** 最低成团人数同时计算去重后的教职工人数和家属人数。 */
|
||||
private boolean isFormedGroup(List<NutMap> rows, Integer minimumGroupSize) {
|
||||
if (minimumGroupSize == null || minimumGroupSize <= 0) {
|
||||
return false;
|
||||
}
|
||||
long teacherCount = rows.stream().map(row -> row.getString("loginName"))
|
||||
.filter(StrUtil::isNotBlank).distinct().count();
|
||||
int familyCount = rows.stream().mapToInt(row -> row.getInt("familyCount", 0)).sum();
|
||||
return teacherCount + familyCount >= minimumGroupSize;
|
||||
}
|
||||
|
||||
/** 每条线路生成一个 XSSF 工作簿后写入 ZIP,避免工作簿内部流与外层 ZIP 冲突。 */
|
||||
private void writeExcelToZip(ZipOutputStream zipOutputStream, Set<String> zipEntryNames,
|
||||
String rawLineName, List<NutMap> rows) throws IOException {
|
||||
String safeLineName = rawLineName.replaceAll("[\\\\/:*?\"<>|]", "_").replaceAll("[\\r\\n]", "").trim();
|
||||
String entryName = "集体线路报名人员/" + StrUtil.blankToDefault(safeLineName, "未命名") + ".xlsx";
|
||||
int index = 1;
|
||||
while (!zipEntryNames.add(entryName)) {
|
||||
entryName = "集体线路报名人员/" + StrUtil.blankToDefault(safeLineName, "未命名") + "-" + index++ + ".xlsx";
|
||||
}
|
||||
ExportParams exportParams = new ExportParams();
|
||||
exportParams.setType(ExcelType.XSSF);
|
||||
try (Workbook workbook = ExcelExportUtil.exportExcel(exportParams, lineExportColumns(), rows);
|
||||
ByteArrayOutputStream excelOutputStream = new ByteArrayOutputStream()) {
|
||||
workbook.write(excelOutputStream);
|
||||
zipOutputStream.putNextEntry(new ZipEntry(entryName));
|
||||
zipOutputStream.write(excelOutputStream.toByteArray());
|
||||
zipOutputStream.closeEntry();
|
||||
}
|
||||
}
|
||||
|
||||
/** 旧版成团线路人员导出的列结构。 */
|
||||
private List<ExcelExportEntity> lineExportColumns() {
|
||||
List<ExcelExportEntity> columns = new ArrayList<>();
|
||||
columns.add(new ExcelExportEntity("姓名", "userName", 20));
|
||||
columns.add(new ExcelExportEntity("工号", "loginName", 20));
|
||||
columns.add(new ExcelExportEntity("性别", "sex", 10));
|
||||
columns.add(new ExcelExportEntity("所属单位", "unitName", 20));
|
||||
columns.add(new ExcelExportEntity("所属工会", "unionName", 20));
|
||||
columns.add(new ExcelExportEntity("身份证号", "idCard", 30));
|
||||
columns.add(new ExcelExportEntity("手机号", "mobile", 15));
|
||||
columns.add(new ExcelExportEntity("备注", "remark", 30));
|
||||
ExcelExportEntity signingTime = new ExcelExportEntity("报名时间", "signingUptime", 20);
|
||||
signingTime.setFormat("yyyy-MM-dd HH:mm:ss");
|
||||
columns.add(signingTime);
|
||||
columns.add(new ExcelExportEntity("出行时间", "playStartTime", 20));
|
||||
columns.add(new ExcelExportEntity("线路名称", "lineName", 20));
|
||||
columns.add(new ExcelExportEntity("旅行社", "travelAgencyName", 20));
|
||||
columns.add(new ExcelExportEntity("家属人数", "familyCount", 12));
|
||||
ExcelExportEntity companionInfo = new ExcelExportEntity("家属信息", "companionInfo", 50);
|
||||
companionInfo.setWrap(true);
|
||||
columns.add(companionInfo);
|
||||
return columns;
|
||||
}
|
||||
}
|
||||
|
||||
+120
-9
@@ -380,6 +380,9 @@ public class RecuperationProvinceFlexibleGroupServiceImpl extends BaseServiceImp
|
||||
@Override
|
||||
public Pagination queryPage(PageForm pageForm, Integer startYear, Integer endYear, String groupName,
|
||||
String travelAgencyId, String searchKeyword) {
|
||||
if (StrUtil.isNotBlank(searchKeyword)) {
|
||||
return queryPersonMatchedGroups(pageForm, startYear, endYear, groupName, travelAgencyId, searchKeyword);
|
||||
}
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT fg.id, fg.groupName, fg.year, ta.travelAgencyName,
|
||||
COUNT(DISTINCT CASE WHEN enroll.groupLeaderUserId IS NOT NULL AND enroll.groupLeaderUserId <> ''
|
||||
@@ -412,6 +415,53 @@ public class RecuperationProvinceFlexibleGroupServiceImpl extends BaseServiceImp
|
||||
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
}
|
||||
|
||||
/** 姓名或工号查询时按匹配人员展开,并保留组团总团队数和总报名人数。 */
|
||||
private Pagination queryPersonMatchedGroups(PageForm pageForm, Integer startYear, Integer endYear,
|
||||
String groupName, String travelAgencyId, String searchKeyword) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT fg.id, CONCAT(fg.id,'_',enroll.id) AS rowKey, fg.groupName, fg.year,
|
||||
ta.travelAgencyName, enroll.userName AS signUserName, enroll.loginName AS signLoginName,
|
||||
(SELECT COUNT(DISTINCT CASE
|
||||
WHEN grouped.groupLeaderUserId IS NOT NULL AND grouped.groupLeaderUserId<>'' THEN grouped.groupLeaderUserId
|
||||
WHEN grouped.groupLeaderLoginName IS NOT NULL AND grouped.groupLeaderLoginName<>'' THEN grouped.groupLeaderLoginName
|
||||
END)
|
||||
FROM the_rapy_recuperation_enroll grouped
|
||||
WHERE grouped.takePartInTravelAgencyId=fg.travelAgencyId
|
||||
AND YEAR(grouped.signingUptime)=fg.year
|
||||
AND grouped.isNormal=true
|
||||
AND grouped.stateId NOT IN ($auditFailStates)) AS groupCount,
|
||||
(SELECT COUNT(DISTINCT grouped.loginName)
|
||||
FROM the_rapy_recuperation_enroll grouped
|
||||
WHERE grouped.takePartInTravelAgencyId=fg.travelAgencyId
|
||||
AND YEAR(grouped.signingUptime)=fg.year
|
||||
AND grouped.isNormal=true
|
||||
AND grouped.stateId NOT IN ($auditFailStates)) AS signCount
|
||||
FROM the_rapy_recuperation_province_flexible_group fg
|
||||
LEFT JOIN the_rapy_recuperation_travel_agency ta ON ta.id=fg.travelAgencyId
|
||||
LEFT JOIN the_rapy_recuperation_enroll enroll
|
||||
ON enroll.takePartInTravelAgencyId=fg.travelAgencyId
|
||||
AND YEAR(enroll.signingUptime)=fg.year
|
||||
AND enroll.isNormal=true
|
||||
AND enroll.stateId NOT IN ($auditFailStates)
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.where("ta.signUpTravelAgency", "=", true)
|
||||
.andEX("fg.year", ">=", startYear)
|
||||
.andEX("fg.year", "<=", endYear)
|
||||
.andEX("fg.travelAgencyId", "=", travelAgencyId)
|
||||
.and(Cnd.likeEX("fg.groupName", groupName))
|
||||
.and(Cnd.exps("enroll.userName", "like", "%" + searchKeyword + "%")
|
||||
.or("enroll.loginName", "like", "%" + searchKeyword + "%"));
|
||||
if (!AuthUtil.hasRoleOr("sysadmin", "A06") && AuthUtil.hasRoleOr("H04")) {
|
||||
cnd.and("fg.createUnionId", "=", SecurityUtil.getUnionId());
|
||||
}
|
||||
cnd.groupBy("fg.id", "enroll.id");
|
||||
cnd.desc("fg.year").asc("fg.groupName").asc("enroll.userName");
|
||||
sql.setVar("auditFailStates", auditFailStates());
|
||||
sql.setCondition(cnd);
|
||||
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> queryGroupInfo(String groupId) {
|
||||
Sql sql = Sqls.create("""
|
||||
@@ -462,11 +512,44 @@ public class RecuperationProvinceFlexibleGroupServiceImpl extends BaseServiceImp
|
||||
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pagination queryAllSignUsers(PageForm pageForm, String groupId) {
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT enroll.id, enroll.loginName, enroll.userName,
|
||||
CASE WHEN enroll.groupLeaderLoginName IS NOT NULL
|
||||
AND enroll.groupLeaderLoginName<>''
|
||||
AND enroll.loginName=enroll.groupLeaderLoginName
|
||||
THEN '团长' ELSE '队员' END AS identity,
|
||||
enroll.unionName, enroll.unitName, enroll.sex, enroll.mobile,
|
||||
(SELECT COUNT(1) FROM the_rapy_recuperation_enroll_companion WHERE trreId=enroll.id) AS isFamily
|
||||
FROM the_rapy_recuperation_enroll enroll
|
||||
WHERE enroll.takePartInTravelAgencyId=(
|
||||
SELECT travelAgencyId FROM the_rapy_recuperation_province_flexible_group WHERE id=@groupId)
|
||||
AND YEAR(enroll.signingUptime)=(
|
||||
SELECT year FROM the_rapy_recuperation_province_flexible_group WHERE id=@groupId)
|
||||
AND enroll.isNormal=true
|
||||
AND enroll.stateId NOT IN ($auditFailStates)
|
||||
ORDER BY enroll.groupLeaderUserName,
|
||||
CASE WHEN enroll.groupLeaderLoginName IS NOT NULL
|
||||
AND enroll.groupLeaderLoginName<>''
|
||||
AND enroll.loginName=enroll.groupLeaderLoginName
|
||||
THEN 0 ELSE 1 END,
|
||||
enroll.signingUptime
|
||||
""");
|
||||
sql.setParam("groupId", groupId).setVar("auditFailStates", auditFailStates());
|
||||
return listPageMap(pageForm.getPageNumber(), pageForm.getPageSize(), sql);
|
||||
}
|
||||
|
||||
/** 过滤浏览器可能传入的 undefined/null 文本,避免错误命中团长条件。 */
|
||||
private boolean isValidGroupLeaderParam(String value) {
|
||||
return StrUtil.isNotBlank(value) && !"undefined".equalsIgnoreCase(value) && !"null".equalsIgnoreCase(value);
|
||||
}
|
||||
|
||||
/** 灵活组团查询排除分工会、线路工会和校工会审核失败状态。 */
|
||||
private String auditFailStates() {
|
||||
return RecuperationState.UNITFAIL + "," + RecuperationState.LINEUNITFAIL + "," + RecuperationState.SCHOOLFAIL;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<NutMap> queryTravelAgencyOptions(Integer startYear, Integer endYear) {
|
||||
Sql sql = Sqls.create("""
|
||||
@@ -498,24 +581,52 @@ public class RecuperationProvinceFlexibleGroupServiceImpl extends BaseServiceImp
|
||||
Sql sql = Sqls.create("""
|
||||
SELECT enroll.loginName, enroll.userName, enroll.mobile, enroll.idCard, enroll.unitName, enroll.unionName,
|
||||
ta.travelAgencyName, DATE_FORMAT(enroll.signingUptime,'%Y-%m-%d %H:%i:%s') AS signingUptime,
|
||||
CONCAT(IFNULL(enroll.groupLeaderUserName,''), IF(enroll.groupLeaderLoginName IS NULL OR enroll.groupLeaderLoginName='', '', CONCAT('(',enroll.groupLeaderLoginName,')'))) AS leaderName,
|
||||
CASE WHEN (SELECT COUNT(DISTINCT sameLeader.loginName) FROM the_rapy_recuperation_enroll sameLeader
|
||||
WHERE sameLeader.takePartInTravelAgencyId=enroll.takePartInTravelAgencyId
|
||||
AND sameLeader.groupLeaderLoginName=enroll.groupLeaderLoginName AND sameLeader.isNormal=true
|
||||
AND YEAR(sameLeader.signingUptime)=YEAR(enroll.signingUptime))>=3 THEN '已成团' ELSE '未成团' END AS formedTeamState
|
||||
CASE
|
||||
WHEN (enroll.groupLeaderUserName IS NULL OR enroll.groupLeaderUserName='')
|
||||
AND (enroll.groupLeaderLoginName IS NULL OR enroll.groupLeaderLoginName='') THEN '未选择'
|
||||
WHEN enroll.groupLeaderLoginName IS NULL OR enroll.groupLeaderLoginName='' THEN enroll.groupLeaderUserName
|
||||
WHEN enroll.groupLeaderUserName IS NULL OR enroll.groupLeaderUserName='' THEN enroll.groupLeaderLoginName
|
||||
ELSE CONCAT(enroll.groupLeaderUserName,'(',enroll.groupLeaderLoginName,')')
|
||||
END AS leaderName,
|
||||
CASE WHEN (
|
||||
SELECT COUNT(DISTINCT sameLeader.loginName)
|
||||
FROM the_rapy_recuperation_enroll sameLeader
|
||||
WHERE sameLeader.takePartInTravelAgencyId=enroll.takePartInTravelAgencyId
|
||||
AND ((enroll.groupLeaderUserId IS NOT NULL AND enroll.groupLeaderUserId<>''
|
||||
AND sameLeader.groupLeaderUserId=enroll.groupLeaderUserId)
|
||||
OR (enroll.groupLeaderLoginName IS NOT NULL AND enroll.groupLeaderLoginName<>''
|
||||
AND sameLeader.groupLeaderLoginName=enroll.groupLeaderLoginName))
|
||||
AND YEAR(sameLeader.signingUptime)=YEAR(enroll.signingUptime)
|
||||
AND sameLeader.isNormal=true
|
||||
AND sameLeader.stateId NOT IN ($auditFailStates)
|
||||
)>=3 THEN '已成团' ELSE '未成团' END AS formedTeamState
|
||||
FROM the_rapy_recuperation_enroll enroll
|
||||
LEFT JOIN the_rapy_recuperation_travel_agency ta ON ta.id=enroll.takePartInTravelAgencyId
|
||||
LEFT JOIN the_rapy_recuperation_province_flexible_group fg ON fg.travelAgencyId=enroll.takePartInTravelAgencyId AND fg.year=YEAR(enroll.signingUptime)
|
||||
LEFT JOIN the_rapy_recuperation_province_flexible_group fg ON fg.id=(
|
||||
SELECT configured.id
|
||||
FROM the_rapy_recuperation_province_flexible_group configured
|
||||
WHERE configured.travelAgencyId=enroll.takePartInTravelAgencyId
|
||||
AND configured.year=YEAR(enroll.signingUptime)
|
||||
AND configured.isDisabled=false
|
||||
ORDER BY configured.id
|
||||
LIMIT 1)
|
||||
$condition
|
||||
""");
|
||||
Cnd cnd = Cnd.where("fg.year", ">=", startYear).and("fg.year", "<=", endYear)
|
||||
.and("enroll.isNormal", "=", true);
|
||||
Cnd cnd = Cnd.where("ta.signUpTravelAgency", "=", true)
|
||||
.and("fg.id", "is not", null)
|
||||
.andEX("fg.year", ">=", startYear)
|
||||
.andEX("fg.year", "<=", endYear)
|
||||
.and("enroll.isNormal", "=", true)
|
||||
.and("enroll.stateId", "not in", Lang.array(RecuperationState.UNITFAIL,
|
||||
RecuperationState.LINEUNITFAIL, RecuperationState.SCHOOLFAIL));
|
||||
cnd.andEX("fg.travelAgencyId", "=", travelAgencyId).and(Cnd.likeEX("fg.groupName", groupName));
|
||||
if (StrUtil.isNotBlank(searchKeyword)) {
|
||||
cnd.and(Cnd.exps("enroll.userName", "like", "%" + searchKeyword + "%").or("enroll.loginName", "like", "%" + searchKeyword + "%"));
|
||||
}
|
||||
if (!AuthUtil.hasRoleOr("sysadmin", "A06") && AuthUtil.hasRoleOr("H04")) cnd.and("fg.createUnionId", "=", SecurityUtil.getUnionId());
|
||||
cnd.asc("fg.year").asc("ta.serialNumber * 1").asc("enroll.groupLeaderUserName").asc("enroll.signingUptime");
|
||||
cnd.asc("ta.serialNumber * 1").asc("ta.travelAgencyName").asc("enroll.groupLeaderUserName")
|
||||
.asc("enroll.groupLeaderLoginName").asc("fg.year").asc("enroll.signingUptime");
|
||||
sql.setVar("auditFailStates", auditFailStates());
|
||||
sql.setCondition(cnd);
|
||||
List<NutMap> list = listMap(sql);
|
||||
for (int i = 0; i < list.size(); i++) list.get(i).setv("no", i + 1);
|
||||
|
||||
Reference in New Issue
Block a user