|
@@ -3,6 +3,7 @@ package cn.iocoder.yudao.module.system.service.biz;
|
|
|
import cn.hutool.core.util.StrUtil;
|
|
import cn.hutool.core.util.StrUtil;
|
|
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
import cn.iocoder.yudao.framework.common.pojo.PageResult;
|
|
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
|
|
|
|
|
+import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
|
|
|
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
|
import cn.iocoder.yudao.framework.security.core.util.SecurityFrameworkUtils;
|
|
|
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
|
import cn.iocoder.yudao.framework.tenant.core.context.TenantContextHolder;
|
|
|
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.elderlyitemsround.*;
|
|
import cn.iocoder.yudao.module.system.controller.admin.biz.vo.elderlyitemsround.*;
|
|
@@ -11,11 +12,33 @@ import cn.iocoder.yudao.module.system.dal.dataobject.biz.ElderlyItemsRoundDO;
|
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.biz.ElderlyInfoDO;
|
|
import cn.iocoder.yudao.module.system.dal.dataobject.biz.ElderlyInfoDO;
|
|
|
import cn.iocoder.yudao.module.system.dal.mysql.biz.ElderlyItemsRoundMapper;
|
|
import cn.iocoder.yudao.module.system.dal.mysql.biz.ElderlyItemsRoundMapper;
|
|
|
import cn.iocoder.yudao.module.system.dal.mysql.biz.ElderlyInfoMapper;
|
|
import cn.iocoder.yudao.module.system.dal.mysql.biz.ElderlyInfoMapper;
|
|
|
|
|
+import com.alibaba.excel.EasyExcel;
|
|
|
|
|
+import com.alibaba.excel.ExcelWriter;
|
|
|
|
|
+import com.alibaba.excel.converters.longconverter.LongStringConverter;
|
|
|
|
|
+import com.alibaba.excel.write.handler.SheetWriteHandler;
|
|
|
|
|
+import com.alibaba.excel.write.metadata.WriteSheet;
|
|
|
|
|
+import com.alibaba.excel.write.metadata.holder.WriteSheetHolder;
|
|
|
|
|
+import com.alibaba.excel.write.metadata.holder.WriteWorkbookHolder;
|
|
|
|
|
+import org.apache.poi.ss.usermodel.Sheet;
|
|
|
|
|
+import org.apache.poi.ss.util.CellRangeAddress;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.validation.annotation.Validated;
|
|
import org.springframework.validation.annotation.Validated;
|
|
|
|
|
|
|
|
import javax.annotation.Resource;
|
|
import javax.annotation.Resource;
|
|
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
|
|
+import java.io.IOException;
|
|
|
|
|
+import java.time.LocalDateTime;
|
|
|
|
|
+import java.time.YearMonth;
|
|
|
import java.util.ArrayList;
|
|
import java.util.ArrayList;
|
|
|
|
|
+import java.util.Collections;
|
|
|
|
|
+import java.util.Comparator;
|
|
|
|
|
+import java.util.HashMap;
|
|
|
|
|
+import java.util.LinkedHashMap;
|
|
|
|
|
+import java.util.ArrayList;
|
|
|
|
|
+import java.util.Map;
|
|
|
|
|
+import java.util.Objects;
|
|
|
|
|
+import java.util.regex.Matcher;
|
|
|
|
|
+import java.util.regex.Pattern;
|
|
|
import java.util.List;
|
|
import java.util.List;
|
|
|
|
|
|
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
|
|
@@ -114,6 +137,125 @@ public class ElderlyItemsRoundServiceImpl implements ElderlyItemsRoundService {
|
|
|
return elderlyItemsRoundMapper.insertBatch(insertList);
|
|
return elderlyItemsRoundMapper.insertBatch(insertList);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void exportExcel(String yearMonth, HttpServletResponse response) throws IOException {
|
|
|
|
|
+ YearMonth ym = parseYearMonth(yearMonth);
|
|
|
|
|
+ Long tenantId = TenantContextHolder.getTenantId();
|
|
|
|
|
+
|
|
|
|
|
+ List<ElderlyInfoDO> elderlyList = elderlyInfoMapper.selectExportListWithBedInfo(tenantId);
|
|
|
|
|
+
|
|
|
|
|
+ int daysInMonth = ym.lengthOfMonth();
|
|
|
|
|
+ int colCount = 2 + daysInMonth;
|
|
|
|
|
+
|
|
|
|
|
+ Map<Long, Map<Integer, String>> elderDayStatusMap = new HashMap<>();
|
|
|
|
|
+ LocalDateTime start = ym.atDay(1).atStartOfDay();
|
|
|
|
|
+ LocalDateTime endExclusive = ym.plusMonths(1).atDay(1).atStartOfDay();
|
|
|
|
|
+ List<Map<String, Object>> dayStatusList = elderlyItemsRoundMapper.selectMonthDayMinStatus(tenantId, start, endExclusive);
|
|
|
|
|
+ for (Map<String, Object> row : dayStatusList) {
|
|
|
|
|
+ if (row == null) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ Object elderIdValue = row.get("elderId");
|
|
|
|
|
+ Object dayValue = row.get("day");
|
|
|
|
|
+ Object minStatusValue = row.get("minStatus");
|
|
|
|
|
+ if (!(elderIdValue instanceof Number) || !(dayValue instanceof Number) || !(minStatusValue instanceof Boolean)) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ Long elderId = ((Number) elderIdValue).longValue();
|
|
|
|
|
+ Integer day = ((Number) dayValue).intValue();
|
|
|
|
|
+ Boolean minStatus = ((Boolean) minStatusValue);
|
|
|
|
|
+
|
|
|
|
|
+ Map<Integer, String> dayMap = elderDayStatusMap.computeIfAbsent(elderId, k -> new HashMap<>());
|
|
|
|
|
+ dayMap.put(day, !minStatus ? "△" : "√");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, List<ElderlyInfoDO>> buildGroups = new LinkedHashMap<>();
|
|
|
|
|
+ for (ElderlyInfoDO elderly : elderlyList) {
|
|
|
|
|
+ String buildName = StrUtil.blankToDefault(elderly.getBuildName(), "未分配楼栋");
|
|
|
|
|
+ buildGroups.computeIfAbsent(buildName, k -> new ArrayList<>()).add(elderly);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ String filename = "长者巡房项目-" + ym + ".xlsx";
|
|
|
|
|
+ ExcelUtils.setResponseProperties(response, filename);
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, List<List<String>>> sheetRows = new LinkedHashMap<>();
|
|
|
|
|
+ Map<String, List<Integer>> sheetMergeRows = new LinkedHashMap<>();
|
|
|
|
|
+
|
|
|
|
|
+ if (buildGroups.isEmpty()) {
|
|
|
|
|
+ String sheetName = uniqueSheetName("无数据", sheetRows);
|
|
|
|
|
+ List<List<String>> rows = new ArrayList<>();
|
|
|
|
|
+ rows.add(padRow("长者巡房项目(" + ym + ")", colCount));
|
|
|
|
|
+ rows.add(padRow("无符合条件的长者数据", colCount));
|
|
|
|
|
+ sheetRows.put(sheetName, rows);
|
|
|
|
|
+ sheetMergeRows.put(sheetName, new ArrayList<>(Collections.singletonList(0)));
|
|
|
|
|
+ } else {
|
|
|
|
|
+ List<Map.Entry<String, List<ElderlyInfoDO>>> buildEntries = new ArrayList<>(buildGroups.entrySet());
|
|
|
|
|
+ buildEntries.sort(Map.Entry.comparingByKey(Comparator.nullsLast(String::compareTo)));
|
|
|
|
|
+ for (Map.Entry<String, List<ElderlyInfoDO>> buildEntry : buildEntries) {
|
|
|
|
|
+ String rawSheetName = sanitizeSheetName(buildEntry.getKey());
|
|
|
|
|
+ String sheetName = uniqueSheetName(rawSheetName, sheetRows);
|
|
|
|
|
+
|
|
|
|
|
+ List<ElderlyInfoDO> buildElders = buildEntry.getValue();
|
|
|
|
|
+ buildElders.sort(Comparator
|
|
|
|
|
+ .comparing((ElderlyInfoDO e) -> StrUtil.blankToDefault(e.getFloorName(), ""))
|
|
|
|
|
+ .thenComparing(e -> StrUtil.blankToDefault(e.getRoomName(), ""))
|
|
|
|
|
+ .thenComparing(e -> StrUtil.blankToDefault(e.getBedName(), ""))
|
|
|
|
|
+ .thenComparing(e -> StrUtil.blankToDefault(e.getElderName(), "")));
|
|
|
|
|
+
|
|
|
|
|
+ List<List<String>> rows = new ArrayList<>();
|
|
|
|
|
+ List<Integer> mergeRows = new ArrayList<>();
|
|
|
|
|
+
|
|
|
|
|
+ rows.add(padRow(buildEntry.getKey() + " 长者巡房项目(" + ym + ")", colCount));
|
|
|
|
|
+ mergeRows.add(0);
|
|
|
|
|
+ rows.add(blankRow(colCount));
|
|
|
|
|
+
|
|
|
|
|
+ int rowIndex = 2;
|
|
|
|
|
+ Map<String, List<ElderlyInfoDO>> floorGroups = new LinkedHashMap<>();
|
|
|
|
|
+ for (ElderlyInfoDO elderly : buildElders) {
|
|
|
|
|
+ String floorName = StrUtil.blankToDefault(elderly.getFloorName(), "未分配楼层");
|
|
|
|
|
+ floorGroups.computeIfAbsent(floorName, k -> new ArrayList<>()).add(elderly);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ List<Map.Entry<String, List<ElderlyInfoDO>>> floorEntries = new ArrayList<>(floorGroups.entrySet());
|
|
|
|
|
+ floorEntries.sort(Map.Entry.comparingByKey(Comparator.nullsLast(String::compareTo)));
|
|
|
|
|
+ for (Map.Entry<String, List<ElderlyInfoDO>> floorEntry : floorEntries) {
|
|
|
|
|
+ rows.add(padRow(floorEntry.getKey(), colCount));
|
|
|
|
|
+ mergeRows.add(rowIndex);
|
|
|
|
|
+ rowIndex++;
|
|
|
|
|
+
|
|
|
|
|
+ rows.add(headerRow(daysInMonth));
|
|
|
|
|
+ rowIndex++;
|
|
|
|
|
+
|
|
|
|
|
+ for (ElderlyInfoDO elderly : floorEntry.getValue()) {
|
|
|
|
|
+ rows.add(dataRow(elderly, elderDayStatusMap.get(elderly.getId()), daysInMonth));
|
|
|
|
|
+ rowIndex++;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ rows.add(blankRow(colCount));
|
|
|
|
|
+ rowIndex++;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ sheetRows.put(sheetName, rows);
|
|
|
|
|
+ sheetMergeRows.put(sheetName, mergeRows);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
|
|
|
|
|
+ .autoCloseStream(false)
|
|
|
|
|
+ .registerConverter(new LongStringConverter())
|
|
|
|
|
+ .registerWriteHandler(new SheetLayoutWriteHandler(sheetMergeRows, colCount - 1, 10, 32, 4))
|
|
|
|
|
+ .build();
|
|
|
|
|
+ try {
|
|
|
|
|
+ int sheetNo = 0;
|
|
|
|
|
+ for (Map.Entry<String, List<List<String>>> entry : sheetRows.entrySet()) {
|
|
|
|
|
+ WriteSheet sheet = EasyExcel.writerSheet(sheetNo++, entry.getKey()).build();
|
|
|
|
|
+ excelWriter.write(entry.getValue(), sheet);
|
|
|
|
|
+ }
|
|
|
|
|
+ } finally {
|
|
|
|
|
+ excelWriter.finish();
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
private void fillDefault(ElderlyItemsRoundDO record) {
|
|
private void fillDefault(ElderlyItemsRoundDO record) {
|
|
|
if (record.getTenantId() == null) {
|
|
if (record.getTenantId() == null) {
|
|
|
record.setTenantId(TenantContextHolder.getTenantId());
|
|
record.setTenantId(TenantContextHolder.getTenantId());
|
|
@@ -141,4 +283,142 @@ public class ElderlyItemsRoundServiceImpl implements ElderlyItemsRoundService {
|
|
|
throw exception(COMMON_NOT_FOUND);
|
|
throw exception(COMMON_NOT_FOUND);
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ private static YearMonth parseYearMonth(String value) {
|
|
|
|
|
+ if (StrUtil.isBlank(value)) {
|
|
|
|
|
+ throw invalidParamException("yearMonth不能为空");
|
|
|
|
|
+ }
|
|
|
|
|
+ String v = value.trim();
|
|
|
|
|
+ Pattern p1 = Pattern.compile("^(\\d{4})-(0[1-9]|1[0-2])$");
|
|
|
|
|
+ Matcher m1 = p1.matcher(v);
|
|
|
|
|
+ if (m1.matches()) {
|
|
|
|
|
+ return YearMonth.of(Integer.parseInt(m1.group(1)), Integer.parseInt(m1.group(2)));
|
|
|
|
|
+ }
|
|
|
|
|
+ Pattern p2 = Pattern.compile("^(\\d{4})(0[1-9]|1[0-2])$");
|
|
|
|
|
+ Matcher m2 = p2.matcher(v);
|
|
|
|
|
+ if (m2.matches()) {
|
|
|
|
|
+ return YearMonth.of(Integer.parseInt(m2.group(1)), Integer.parseInt(m2.group(2)));
|
|
|
|
|
+ }
|
|
|
|
|
+ throw invalidParamException("yearMonth格式错误,示例:2026-05 或 202605");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static String sanitizeSheetName(String name) {
|
|
|
|
|
+ String v = StrUtil.blankToDefault(name, "Sheet");
|
|
|
|
|
+ v = v.replaceAll("[\\\\/?*\\[\\]:]", " ");
|
|
|
|
|
+ v = v.trim();
|
|
|
|
|
+ if (v.length() > 31) {
|
|
|
|
|
+ v = v.substring(0, 31);
|
|
|
|
|
+ }
|
|
|
|
|
+ return StrUtil.blankToDefault(v, "Sheet");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static String uniqueSheetName(String baseName, Map<String, ?> existing) {
|
|
|
|
|
+ String base = sanitizeSheetName(baseName);
|
|
|
|
|
+ if (!existing.containsKey(base)) {
|
|
|
|
|
+ return base;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (int i = 2; i < 1000; i++) {
|
|
|
|
|
+ String suffix = "(" + i + ")";
|
|
|
|
|
+ int maxLen = 31 - suffix.length();
|
|
|
|
|
+ String candidateBase = base.length() > maxLen ? base.substring(0, maxLen) : base;
|
|
|
|
|
+ String candidate = candidateBase + suffix;
|
|
|
|
|
+ if (!existing.containsKey(candidate)) {
|
|
|
|
|
+ return candidate;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return String.valueOf(System.currentTimeMillis());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static List<String> padRow(String firstCell, int colCount) {
|
|
|
|
|
+ List<String> row = new ArrayList<>(colCount);
|
|
|
|
|
+ row.add(firstCell);
|
|
|
|
|
+ for (int i = 1; i < colCount; i++) {
|
|
|
|
|
+ row.add("");
|
|
|
|
|
+ }
|
|
|
|
|
+ return row;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static List<String> blankRow(int colCount) {
|
|
|
|
|
+ List<String> row = new ArrayList<>(colCount);
|
|
|
|
|
+ for (int i = 0; i < colCount; i++) {
|
|
|
|
|
+ row.add("");
|
|
|
|
|
+ }
|
|
|
|
|
+ return row;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static List<String> headerRow(int daysInMonth) {
|
|
|
|
|
+ int colCount = 2 + daysInMonth;
|
|
|
|
|
+ List<String> row = new ArrayList<>(colCount);
|
|
|
|
|
+ row.add("房号");
|
|
|
|
|
+ row.add("日期\\姓名");
|
|
|
|
|
+ for (int d = 1; d <= daysInMonth; d++) {
|
|
|
|
|
+ row.add(String.valueOf(d));
|
|
|
|
|
+ }
|
|
|
|
|
+ return row;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static List<String> dataRow(ElderlyInfoDO elderly, Map<Integer, String> dayStatusMap, int daysInMonth) {
|
|
|
|
|
+ int colCount = 2 + daysInMonth;
|
|
|
|
|
+ List<String> row = new ArrayList<>(colCount);
|
|
|
|
|
+ row.add(StrUtil.blankToDefault(elderly.getRoomName(), ""));
|
|
|
|
|
+ String bed = StrUtil.blankToDefault(elderly.getBedName(), "");
|
|
|
|
|
+ String elderName = StrUtil.blankToDefault(elderly.getElderName(), "");
|
|
|
|
|
+ if (StrUtil.isBlank(bed)) {
|
|
|
|
|
+ row.add(elderName);
|
|
|
|
|
+ } else if (StrUtil.isBlank(elderName)) {
|
|
|
|
|
+ row.add(bed);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ row.add(bed + "/" + elderName);
|
|
|
|
|
+ }
|
|
|
|
|
+ for (int d = 1; d <= daysInMonth; d++) {
|
|
|
|
|
+ String v = dayStatusMap != null ? dayStatusMap.get(d) : null;
|
|
|
|
|
+ row.add(StrUtil.blankToDefault(v, ""));
|
|
|
|
|
+ }
|
|
|
|
|
+ return row;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static class SheetLayoutWriteHandler implements SheetWriteHandler {
|
|
|
|
|
+ private final Map<String, List<Integer>> sheetMergeRows;
|
|
|
|
|
+ private final int lastColIndex;
|
|
|
|
|
+ private final int roomColWidthChars;
|
|
|
|
|
+ private final int nameColWidthChars;
|
|
|
|
|
+ private final int dayColWidthChars;
|
|
|
|
|
+
|
|
|
|
|
+ private SheetLayoutWriteHandler(Map<String, List<Integer>> sheetMergeRows, int lastColIndex,
|
|
|
|
|
+ int roomColWidthChars, int nameColWidthChars, int dayColWidthChars) {
|
|
|
|
|
+ this.sheetMergeRows = sheetMergeRows;
|
|
|
|
|
+ this.lastColIndex = lastColIndex;
|
|
|
|
|
+ this.roomColWidthChars = roomColWidthChars;
|
|
|
|
|
+ this.nameColWidthChars = nameColWidthChars;
|
|
|
|
|
+ this.dayColWidthChars = dayColWidthChars;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void beforeSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Override
|
|
|
|
|
+ public void afterSheetCreate(WriteWorkbookHolder writeWorkbookHolder, WriteSheetHolder writeSheetHolder) {
|
|
|
|
|
+ Sheet sheet = writeSheetHolder.getSheet();
|
|
|
|
|
+ for (int col = 0; col <= lastColIndex; col++) {
|
|
|
|
|
+ if (col == 0) {
|
|
|
|
|
+ sheet.setColumnWidth(col, roomColWidthChars * 256);
|
|
|
|
|
+ } else if (col == 1) {
|
|
|
|
|
+ sheet.setColumnWidth(col, nameColWidthChars * 256);
|
|
|
|
|
+ } else {
|
|
|
|
|
+ sheet.setColumnWidth(col, dayColWidthChars * 256);
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ List<Integer> rows = sheetMergeRows.get(writeSheetHolder.getSheetName());
|
|
|
|
|
+ if (rows == null) {
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (Integer row : rows) {
|
|
|
|
|
+ if (row == null) {
|
|
|
|
|
+ continue;
|
|
|
|
|
+ }
|
|
|
|
|
+ sheet.addMergedRegion(new CellRangeAddress(row, row, 0, lastColIndex));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|