refactor(system): 优化日志实例导出功能

- 使用 EasyExcel 替代 ExcelUtils 写入 Excel 文件
- 添加单元格合并功能
- 优化表格样式,包括列宽自适应、居中对齐等
-增加部门编号筛选条件
- 调整日志数据查询逻辑,提高效率
This commit is contained in:
furongxin 2025-03-09 10:27:12 +08:00
parent d83dca6b87
commit cdb4d79822
4 changed files with 91 additions and 22 deletions

View File

@ -2,10 +2,8 @@ package cn.iocoder.yudao.module.system.controller.admin.worklog;
import cn.hutool.core.collection.CollectionUtil; import cn.hutool.core.collection.CollectionUtil;
import cn.iocoder.yudao.framework.common.pojo.CommonResult; import cn.iocoder.yudao.framework.common.pojo.CommonResult;
import cn.iocoder.yudao.framework.common.pojo.PageParam;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils; import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission; import cn.iocoder.yudao.framework.datapermission.core.annotation.DataPermission;
import cn.iocoder.yudao.framework.excel.core.util.ExcelUtils;
import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog; import cn.iocoder.yudao.framework.operatelog.core.annotations.OperateLog;
import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptRespVO; import cn.iocoder.yudao.module.system.controller.admin.dept.vo.dept.DeptRespVO;
import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserRespVO; import cn.iocoder.yudao.module.system.controller.admin.user.vo.user.UserRespVO;
@ -21,10 +19,20 @@ import cn.iocoder.yudao.module.system.service.user.AdminUserService;
import cn.iocoder.yudao.module.system.service.worklog.LogInstanceService; import cn.iocoder.yudao.module.system.service.worklog.LogInstanceService;
import cn.iocoder.yudao.module.system.service.worklog.LogReadService; import cn.iocoder.yudao.module.system.service.worklog.LogReadService;
import cn.iocoder.yudao.module.system.service.worklog.LogUseService; import cn.iocoder.yudao.module.system.service.worklog.LogUseService;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.converters.longconverter.LongStringConverter;
import com.alibaba.excel.support.ExcelTypeEnum;
import com.alibaba.excel.write.builder.ExcelWriterBuilder;
import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy;
import com.alibaba.excel.write.metadata.style.WriteCellStyle;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.baomidou.mybatisplus.core.metadata.IPage; import com.baomidou.mybatisplus.core.metadata.IPage;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import org.apache.poi.ss.usermodel.HorizontalAlignment;
import org.apache.poi.ss.usermodel.VerticalAlignment;
import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
@ -33,10 +41,9 @@ import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse; import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid; import javax.validation.Valid;
import java.io.IOException; import java.io.IOException;
import java.util.ArrayList; import java.net.URLEncoder;
import java.util.HashMap; import java.nio.charset.StandardCharsets;
import java.util.List; import java.util.*;
import java.util.Map;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success; import static cn.iocoder.yudao.framework.common.pojo.CommonResult.success;
@ -310,8 +317,65 @@ public class LogInstanceController {
HttpServletResponse response) throws IOException { HttpServletResponse response) throws IOException {
List<LogExportExcelVO> list = logInstanceService.getLogInstanceExport(reqVO); List<LogExportExcelVO> list = logInstanceService.getLogInstanceExport(reqVO);
// 导出 Excel
ExcelUtils.write(response, "日志实例的拓展.xls", "数据", LogExportExcelVO.class,
list); // 输出excel
ExcelWriterBuilder writer = EasyExcel.write(response.getOutputStream(), LogExportExcelVO.class);
List<OnceAbsoluteMergeStrategy> strategies = getMergeStrategy(list);
strategies.forEach(writer::registerWriteHandler);
writer.autoCloseStream(false); // 不要自动关闭交给 Servlet 自己处理
writer.registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()); // 基于 column 长度自动适配最大 255 宽度
writer.registerWriteHandler(new HorizontalCellStyleStrategy(getHeadStyle(), getContentStyle()));
writer.registerConverter(new LongStringConverter()); // 避免 Long 类型丢失精度
writer.excelType(ExcelTypeEnum.XLS);
writer.sheet("数据").doWrite(list);
// 设置 header contentType写在最后的原因是避免报错时响应 contentType 已经被修改了
response.addHeader("Content-Disposition", "attachment;filename=" + URLEncoder.encode("日志实例的拓展.xls", StandardCharsets.UTF_8.name()));
response.setContentType("application/vnd.ms-excel;charset=UTF-8");
// // 导出 Excel
// ExcelUtils.write(response, "日志实例的拓展.xls", "数据", LogExportExcelVO.class,
// list);
}
public static List<OnceAbsoluteMergeStrategy> getMergeStrategy(List<LogExportExcelVO> dataList) {
List<OnceAbsoluteMergeStrategy> strategies = new ArrayList<>();
List<Integer> mergeCols = Arrays.asList(0, 1); // 需要合并的列索引
// 遍历数据生成合并区间
for (int i = 0; i < dataList.size(); i++) {
if (i > 0 && dataList.get(i).getNickname().equals(dataList.get(i - 1).getNickname()) && dataList.get(i).getTime().equals(dataList.get(i - 1).getTime())) {
continue;
}
int startRow = i + 1;
while (i < dataList.size() - 1 && dataList.get(i).getNickname().equals(dataList.get(i + 1).getNickname()) && dataList.get(i).getTime().equals(dataList.get(i + 1).getTime())) {
i++;
}
for (int col : mergeCols) {
strategies.add(new OnceAbsoluteMergeStrategy(startRow, i + 1, col, col));
}
}
return strategies;
}
// 表头样式居中
public static WriteCellStyle getHeadStyle() {
WriteCellStyle headStyle = new WriteCellStyle();
headStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
headStyle.setVerticalAlignment(VerticalAlignment.CENTER);
return headStyle;
}
// 内容样式居中
public static WriteCellStyle getContentStyle() {
WriteCellStyle contentStyle = new WriteCellStyle();
contentStyle.setWrapped(true); // 启用自动换行
contentStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
contentStyle.setVerticalAlignment(VerticalAlignment.CENTER);
return contentStyle;
} }
} }

View File

@ -1,7 +1,5 @@
package cn.iocoder.yudao.module.system.controller.admin.worklog.vo.loginstance; package cn.iocoder.yudao.module.system.controller.admin.worklog.vo.loginstance;
import cn.iocoder.yudao.framework.common.validation.IdCard;
import cn.iocoder.yudao.framework.common.validation.Mobile;
import com.alibaba.excel.annotation.ExcelProperty; import com.alibaba.excel.annotation.ExcelProperty;
import lombok.AllArgsConstructor; import lombok.AllArgsConstructor;
import lombok.Builder; import lombok.Builder;

View File

@ -7,6 +7,9 @@ import lombok.Data;
@Data @Data
public class LogExportVO { public class LogExportVO {
@Schema(description = "部门编号")
private Long companyDeptId;
@Schema(description = "日志模版编号") @Schema(description = "日志模版编号")
private Long formId; private Long formId;

View File

@ -142,7 +142,8 @@ public class LogInstanceServiceImpl implements LogInstanceService {
List<LogReadUserRespDTO> respDTOS = logInstanceMapper.selectRaedUser(userId, adminUserDO.getDeptId()); List<LogReadUserRespDTO> respDTOS = logInstanceMapper.selectRaedUser(userId, adminUserDO.getDeptId());
//特殊情况 日志发起人为研发部时 手动添加查看者 //特殊情况 日志发起人为研发部时 手动添加查看者
if (adminUserDO.getDeptId() == 128L && adminUserDO.getId() != 126L) { List<Long> deptIds = convertList(deptService.getChildDept(128L), DeptDO::getId);
if (deptIds.contains(adminUserDO.getDeptId()) && adminUserDO.getId() != 126L) {
LogReadUserRespDTO dto = new LogReadUserRespDTO(); LogReadUserRespDTO dto = new LogReadUserRespDTO();
dto.setUserId(126L); dto.setUserId(126L);
@ -450,6 +451,7 @@ public class LogInstanceServiceImpl implements LogInstanceService {
@Override @Override
public List<LogExportExcelVO> getLogInstanceExport(LogExportVO reqVO) { public List<LogExportExcelVO> getLogInstanceExport(LogExportVO reqVO) {
List<LogInstanceDO> list = logInstanceMapper.selectList(new LambdaQueryWrapperX<LogInstanceDO>() List<LogInstanceDO> list = logInstanceMapper.selectList(new LambdaQueryWrapperX<LogInstanceDO>()
.eqIfPresent(LogInstanceDO::getDeptId, reqVO.getCompanyDeptId())
.eqIfPresent(LogInstanceDO::getFormId, reqVO.getFormId()) .eqIfPresent(LogInstanceDO::getFormId, reqVO.getFormId())
.betweenIfPresent(LogInstanceDO::getTime, reqVO.getTime())); .betweenIfPresent(LogInstanceDO::getTime, reqVO.getTime()));
@ -469,16 +471,6 @@ public class LogInstanceServiceImpl implements LogInstanceService {
//遍历 //遍历
for (LogInstanceDO item : list) { for (LogInstanceDO item : list) {
LogExportExcelVO logExportExcelVO = new LogExportExcelVO();
// 设置时间
logExportExcelVO.setTime(item.getTime());
//设置发起人用户名称和头像
AdminUserDO userDO = userMap.get(item.getStartUserId());
if (userDO != null) {
logExportExcelVO.setNickname(userDO.getNickname());
}
//设置日志内部分an //设置日志内部分an
LogFormDO logFormDO = formMap.get(item.getFormId()); LogFormDO logFormDO = formMap.get(item.getFormId());
@ -496,6 +488,18 @@ public class LogInstanceServiceImpl implements LogInstanceService {
continue; continue;
} }
String field = workLogContentJson.getStr(fieldStr); String field = workLogContentJson.getStr(fieldStr);
LogExportExcelVO logExportExcelVO = new LogExportExcelVO();
// 设置时间
logExportExcelVO.setTime(item.getTime());
//设置发起人用户名称和头像
AdminUserDO userDO = userMap.get(item.getStartUserId());
if (userDO != null) {
logExportExcelVO.setNickname(userDO.getNickname());
}
logExportExcelVO.setTitle(title); logExportExcelVO.setTitle(title);
logExportExcelVO.setContent(field); logExportExcelVO.setContent(field);