扩展若依@Excel注解,使其对字段的控制是否导出更加便捷

基于若依框架实现按角色控制 Excel 字段导出功能

一、背景介绍

在我们的项目开发中,采用了若依(RuoYi)的 Java Spring 框架进行系统搭建。若依框架提供了 @Excel 注解,通过在实体类的字段上添加该注解,能够方便地实现 Excel 数据的导出功能。然而,在实际业务场景中,领导提出了根据用户角色来控制某些字段是否导出的需求。但遗憾的是,若依自带的 @Excel 注解并不支持这一功能。为了满足这一业务需求,我们决定自行开发一套解决方案。

二、解决方案思路

为了实现按角色控制 Excel 字段导出的功能,我们的核心思路是自定义一个注解和一个导出工具类。具体步骤如下:

  1. 自定义注解:创建一个新的注解 RoleExcel,该注解能够标识出需要导出的字段,并且可以指定允许导出该字段的角色列表。
  2. 编写导出工具类:开发一个新的导出工具类 RoleExcelUtil,该工具类会根据当前用户的角色信息,过滤掉没有权限导出的字段,然后将有权限导出的字段数据导出到 Excel 文件中。
  3. 实体类注解应用:在需要导出和进行角色控制的实体类 JiheHandledRate 的相应字段上添加 RoleExcel 注解。
  4. 控制器调用:在控制器 JiheStationCommonControllerexportHandledRate 方法中调用 RoleExcelUtil 工具类的导出方法,完成数据的导出操作。

三、具体实现步骤

3.1 自定义 RoleExcel 注解

首先,我们定义了一个 RoleExcel 注解,用于标识需要导出的字段,并支持按角色控制导出。以下是 RoleExcel 注解的代码实现:

package com.sdhs.common.annotation;
import java.lang.annotation.*;
/**
 * 扩展Excel注解,支持按角色控制导出字段
 */
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface RoleExcel {
 // 字段显示名称(同若依@Excel的name)
 String name();
 // 列宽(默认20)
 int width() default 20;
 // 日期格式(如"yyyy-MM-dd HH:mm:ss")
 String dateFormat() default "";
 // 允许导出该字段的角色标识列表(如"admin","audit_role")
 String[] roles() default {};
 // 是否允许所有角色导出(默认false)
 boolean allowAll() default false;
 // 权限标识(支持按权限控制,如"jihe:export:vehplate")
 String[] permissions() default {};
}

在这个注解中,我们定义了字段显示名称、列宽、日期格式、允许导出的角色列表、是否允许所有角色导出以及权限标识等属性。

3.2 编写 RoleExcelUtil 导出工具类

接下来,我们编写了 RoleExcelUtil 工具类,该工具类会根据用户角色过滤需要导出的字段,并将数据导出到 Excel 文件中。以下是 RoleExcelUtil 工具类的代码实现:

package com.sdhs.common.utils.poi;
import com.sdhs.common.annotation.RoleExcel;
import com.sdhs.common.core.domain.entity.SysRole;
import com.sdhs.common.core.domain.model.LoginUser;
import com.sdhs.common.utils.SecurityUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;
import java.lang.reflect.Field;
import java.net.URLEncoder;
import java.util.*;
import java.util.stream.Collectors;
/*
 * @description:支持角色权限的Excel工具类
 * @author: 龙谷情
 * @date: 2025-07-08 16:39:28
 **/
public class RoleExcelUtil<T> {
 private final Class<T> clazz;
 private List<ExcelColumn> excelColumns;
 public RoleExcelUtil(Class<T> clazz) {
 this.clazz = clazz;
 this.excelColumns = getFilteredColumns();
 }
 /**
 * 获取过滤后的列(根据角色权限)
 */
 private List<ExcelColumn> getFilteredColumns() {
 List<ExcelColumn> allColumns = new ArrayList<>();
 Class<?> currentClass = clazz;
 while (currentClass != Object.class) {
 Field[] fields = currentClass.getDeclaredFields();
 for (Field field : fields) {
 RoleExcel roleExcel = field.getAnnotation(RoleExcel.class);
 if (roleExcel == null) {
 continue;
 }
 if (hasExportPermission(roleExcel)) {
 allColumns.add(buildExcelColumn(field, roleExcel));
 }
 }
 currentClass = currentClass.getSuperclass();
 }
 return allColumns;
 }
 /**
 * 检查当前用户是否有权限导出该字段
 */
 private boolean hasExportPermission(RoleExcel roleExcel) {
 if (roleExcel.allowAll()) {
 return true;
 }
 LoginUser loginUser = SecurityUtils.getLoginUser();
 if (loginUser == null) {
 return false;
 }
 // 检查角色权限
 String[] allowRoles = roleExcel.roles();
 if (allowRoles.length > 0) {
 List<String> userRoleKeys = loginUser.getUser().getRoles().stream()
 .map(SysRole::getRoleKey)
 .collect(Collectors.toList());
 for (String role : allowRoles) {
 if (userRoleKeys.contains(role) || loginUser.getUser().isAdmin()) {
 return true;
 }
 }
 }
 return false;
 }
 /**
 * 构建Excel列信息
 */
 private ExcelColumn buildExcelColumn(Field field, RoleExcel roleExcel) {
 ExcelColumn column = new ExcelColumn();
 column.setField(field);
 column.setHeader(roleExcel.name());
 column.setWidth(roleExcel.width());
 column.setDateFormat(roleExcel.dateFormat());
 return column;
 }
 /**
 * 导出Excel文件
 */
 public void exportExcel(HttpServletResponse response, List<T> list, String title) {
 try (Workbook workbook = new SXSSFWorkbook(500)) {
 Sheet sheet = workbook.createSheet(title);
 setSheetStyle(sheet);
 // 创建表头
 Row headerRow = sheet.createRow(0);
 createHeader(headerRow);
 // 填充数据
 for (int i = 0; i < list.size(); i++) {
 Row dataRow = sheet.createRow(i + 1);
 fillDataRow(dataRow, list.get(i));
 }
 // 输出到客户端
 response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
 response.setCharacterEncoding("utf-8");
 String fileName = URLEncoder.encode(title + ".xlsx", "UTF-8").replaceAll("\\+", "%20");
 response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName);
 try (OutputStream os = response.getOutputStream()) {
 workbook.write(os);
 os.flush();
 }
 } catch (Exception e) {
 throw new RuntimeException("导出Excel失败", e);
 }
 }
 /**
 * 设置工作表样式
 */
 private void setSheetStyle(Sheet sheet) {
 for (int i = 0; i < excelColumns.size(); i++) {
 sheet.setColumnWidth(i, excelColumns.get(i).getWidth() * 256);
 }
 }
 /**
 * 创建表头
 */
 private void createHeader(Row headerRow) {
 CellStyle headerStyle = headerRow.getSheet().getWorkbook().createCellStyle();
 Font headerFont = headerRow.getSheet().getWorkbook().createFont();
 headerFont.setBold(true);
 headerStyle.setFont(headerFont);
 headerStyle.setAlignment(HorizontalAlignment.CENTER);
 for (int i = 0; i < excelColumns.size(); i++) {
 Cell cell = headerRow.createCell(i);
 cell.setCellValue(excelColumns.get(i).getHeader());
 cell.setCellStyle(headerStyle);
 }
 }
 /**
 * 填充数据行
 */
 private void fillDataRow(Row dataRow, T obj) throws Exception {
 for (int i = 0; i < excelColumns.size(); i++) {
 ExcelColumn column = excelColumns.get(i);
 Field field = column.getField();
 field.setAccessible(true);
 Cell cell = dataRow.createCell(i);
 Object value = field.get(obj);
 if (value == null) {
 cell.setCellValue("");
 continue;
 }
 // 处理日期类型
 if (value instanceof Date && !column.getDateFormat().isEmpty()) {
 CellStyle style = dataRow.getSheet().getWorkbook().createCellStyle();
 DataFormat format = dataRow.getSheet().getWorkbook().createDataFormat();
 style.setDataFormat(format.getFormat(column.getDateFormat()));
 cell.setCellStyle(style);
 cell.setCellValue((Date) value);
 continue;
 }
 // 处理基本类型
 cell.setCellValue(value.toString());
 }
 }
 // ExcelColumn类定义
 public static class ExcelColumn {
 private Field field;
 private String header;
 private int width;
 private String dateFormat;
 // getters/setters
 public Field getField() {
 return field;
 }
 public void setField(Field field) {
 this.field = field;
 }
 public String getHeader() {
 return header;
 }
 public void setHeader(String header) {
 this.header = header;
 }
 public int getWidth() {
 return width;
 }
 public void setWidth(int width) {
 this.width = width;
 }
 public String getDateFormat() {
 return dateFormat;
 }
 public void setDateFormat(String dateFormat) {
 this.dateFormat = dateFormat;
 }
 }
}

在这个工具类中,getFilteredColumns 方法用于获取过滤后的列,hasExportPermission 方法用于检查当前用户是否有权限导出该字段,exportExcel 方法用于将数据导出到 Excel 文件中。

3.3 在 JiheHandledRate 实体类中添加注解

JiheHandledRate 实体类中,我们在需要导出和进行角色控制的字段上添加了 RoleExcel 注解。以下是 JiheHandledRate 实体类的部分代码示例:

package com.sdhs.jihe.domain;
import com.sdhs.common.annotation.RoleExcel;
import com.sdhs.common.utils.CalculationUtils;
import lombok.Data;
import static com.sdhs.common.utils.CalculationUtils.divide;
import static com.sdhs.common.utils.CalculationUtils.multiplyAndRound;
@Data
public class JiheHandledRate {
 private static final long serialVersionUID = 1L;
 /**
 * 运管中心名称
 */
 @RoleExcel(name = "运管中心名称", allowAll = true)
 private String ygcentername;
 /**
 * 总数量
 */
 @RoleExcel(name = "线索总数量", allowAll = true)
 private Long totalCount;
package com.sdhs.jihe.domain;
import com.sdhs.common.annotation.RoleExcel;
import com.sdhs.common.utils.CalculationUtils;
import lombok.Data;
import static com.sdhs.common.utils.CalculationUtils.divide;
import static com.sdhs.common.utils.CalculationUtils.multiplyAndRound;
@Data
public class JiheHandledRate {
 private static final long serialVersionUID = 1L;
 /**
 * 运管中心名称
 */
 @RoleExcel(name = "运管中心名称", allowAll = true)
 private String ygcentername;
 /**
 * 总数量
 */
 @RoleExcel(name = "线索总数量", allowAll = true)
 private Long totalCount;
 /**
 * 已处理数量
 */
 @RoleExcel(name = "已确认线索数量", allowAll = true)
 private Long handledCount;
 /**
 * 待处理数量
 */
 @RoleExcel(name = "待确认线索数量", allowAll = true)
 private Long unhandledCount;
 /**
 * 处理数量占比 描述
 */
 @RoleExcel(name = "线索确认占比", allowAll = true)
 private String handledPercentDesc;
 /**
 * 工单发起数量
 */
 @RoleExcel(name = "工单发起数量", roles = {"internalAuditRole", "admin"})
 private Long ticketCount;
 /**
 * 工单发起数量占比 描述
 */
 @RoleExcel(name = "工单发起数量占比", roles = {"internalAuditRole", "admin"})
 private String ticketPercentDesc;
 /**
 * 应追缴金额
 */
 @RoleExcel(name = "工单合计漏征金额(元)", roles = {"internalAuditRole", "admin"})
 private double owefeeYuan;
 // 其他字段...
}

3.4 在 JiheStationCommonController 中调用导出方法

最后,在 JiheStationCommonControllerexportHandledRate 方法中,我们调用了 RoleExcelUtil 工具类的 exportExcel 方法,完成数据的导出操作。以下是 exportHandledRate 方法的代码实现:

/**
 * 导出处理率
 */
@Log(title = "导出处理率", businessType = BusinessType.EXPORT)
@PostMapping("/exportHandledRate")
public void exportHandledRate(HttpServletResponse response, JiheStationCommon jiheStationCommon) {
 //jiheStationCommon.setIsabnormal(2);
 jiheStationCommon.setCurrentstatus("abnormal");
 jiheStationCommon.setTicketcodeFlag(2);
 List<JiheHandledRate> list = jiheStationCommonService.statisticHandledRate(jiheStationCommon);
 RoleExcelUtil<JiheHandledRate> util = new RoleExcelUtil<>(JiheHandledRate.class);
 util.exportExcel(response, list, "稽核车辆明细数据");
}

四、总结

通过自定义 RoleExcel 注解和 RoleExcelUtil 导出工具类,我们成功实现了在若依框架中按角色控制 Excel 字段导出的功能。这种实现方式不仅满足了业务需求,还具有良好的扩展性和可维护性。在实际项目中,我们可以根据具体需求对注解和工具类进行进一步的扩展和优化,以适应更多复杂的业务场景。

希望本文对大家在实现类似功能时有所帮助,如果你在实现过程中遇到任何问题,欢迎在评论区留言交流。

作者:龙谷情Sinoam原文地址:https://www.cnblogs.com/lgqrlchinese/p/18973540

%s 个评论

要回复文章请先登录注册