Pensando en las funciones generales de importación y exportación

Utilice la configuración JSON para lograr generalidad y ajuste dinámico. Por supuesto, esta generalización todavía tiene ciertas limitaciones. El estilo de código de cada proyecto es diferente. No es fácil escribir un módulo generalizado adecuado para todos los proyectos. Aquí el uso común se limita a el proyecto donde se encuentra, por lo que si el código de la función no es aplicable a su propio proyecto, espero que pueda usarlo como referencia y hacer una pequeña modificación.

Así que ahora analicemos qué elementos de configuración JSON necesitaremos.

exportar

Elementos básicos de configuración

Comience con la exportación más simple. Los datos exportados deben admitir la detección a través de la capa empresarial, como: Service.search(param) . Esta es la premisa principal.

Luego, para admitir la visualización del progreso de la exportación, la capa empresarial también debe proporcionar un método de consulta de cantidad, como: Service.count(param) , de lo contrario, no se podrá realizar el progreso de la exportación.

Finalmente, el nombre del archivo de exportación también se puede personalizar, como: nombre de archivo

Los elementos de configuración se pueden obtener de lo anterior:

  • serviceClazz : ruta de clase empresarial, como: com.cc.service.UserService, requerido
  • methodName : nombre del método de consulta, como: listByCondition, requerido
  • countMethodName : nombre del método de consulta de cantidad, opcional, utilizado para respaldar el progreso de la exportación
  • nombre de archivo : nombre del archivo de exportación
  • searchParams : parámetros de consulta, tipo de matriz, elementos de diccionario. Las matrices se utilizan para admitir el caso en el que el método de consulta necesita pasar múltiples parámetros.

En cuanto a la clase de parámetro del método de consulta, no es necesario completarlo, ya que podemos obtener el tipo de parámetro que el método necesita pasar a través de la reflexión (tenga en cuenta que los códigos clave publicados a continuación son solo para referencia):

 
 

Java

copiar codigo

Class<?> serviceClass = Class.forName(param.getServiceClazz()); // param为请求参数类 Method searchMethod = ReflectUtil.findMethodByName(serviceClass, param.getMethodName()); // 方法所需要传入的参数列表 Class<?>[] parameterTypes = searchMethod.getParameterTypes();

 
 

Java

copiar codigo

/** * 通过反射从指定类中获取方法对象 */ public static Method findMethodByName(Class<?> clazz, String name) { Method[] methods = clazz.getMethods(); if (StringUtils.isEmpty(name)) { return null; } for (Method method : methods) { if (method.getName().equals(name)) { return method; } } return null; }

Ahora pensemos qué escenarios se exportarán:

  1. La consulta de paginación de la página de lista puede ser la exportación de datos de la página actual o la exportación de todos los datos, lo que implica una consulta de paginación
  2. La consulta en la página de resumen de datos suele ser una consulta de tabla de vínculos compleja personalizada por el desarrollador y no requiere paginación.

Luego, este artículo implementa la primera versión de la función de exportación general para las dos situaciones anteriores.

Consulta de paginación para páginas de lista

La exportación de datos de la página de lista se divide en exportación de página actual y exportación de todos los datos .

Supongamos que el proceso de consulta es así:

  1. La capa de interfaz recibe parámetros: Controller.search(Param param)
  2. La capa empresarial llama al método de consulta: Service.search(param)
  3. La capa de persistencia accede a la base de datos: Mapper.search(param)

Este caso es simple, pero si el flujo es así:

  1. La capa de interfaz recibe parámetros: Controller.search(Param param)
  2. La capa empresarial llama al método de consulta: Service.search(new Condition(param))
  3. La capa de persistencia accede a la base de datos: Mapper.search(condición)

En el código anterior, los parámetros de solicitud de la interfaz no son coherentes con los parámetros de la capa de persistencia y se han empaquetado en la capa empresarial, por lo que esta situación también debe manejarse de manera compatible.

Pero si los parámetros de la solicitud pasan por el paquete en el paquete de la capa empresarial, olvídalo.

El siguiente es el parámetro de paginación, usamos pageNum y pageSize para representar el número de página y los campos de cantidad, similar a:

 
 

json

copiar codigo

{ "pageNum": 1, "pageSize": 10, "name": "老刘" // 此为查询字段,如查询名字为老刘的数据 }

Con respecto a la exportación de la página actual y la exportación de todos los datos, puede usar un bool para representar: onlyCurrentPage , el valor predeterminado es falso, es decir, los datos se paginarán y consultarán automáticamente al exportar, hasta que se consulten todos los datos. data, la consulta de la página es necesaria y puede mejorar el rendimiento Para evitar el desbordamiento de la memoria, cuando onlyCurrentPage es verdadero, solo se exportan los datos de la página actual.

Los elementos de configuración necesarios se obtienen de la siguiente manera:

  • searchParam : parámetro de solicitud de paginación de interfaz, tipo JSON, requerido
  • conditionClazz : clase de consulta condicional, también se puede considerar como una clase contenedora, como: com.cc.codition.UserCondition, se puede completar
  • onlyCurrentPage : solo se exporta la página actual, el valor predeterminado es falso y se puede completar

Consulta en la página de resumen de datos

Descripción general de los datos No hay un método de consulta de cantidad para los datos, es decir, Service.count(xxx), y no hay un parámetro de consulta de paginación, similar a la exportación de la página actual. En el caso de una sola capa de clase de empaquetado, no hay elementos de configuración adicionales, y lo anterior es suficiente Ahora, debe tenerse en cuenta que los parámetros de paginación deben eliminarse del código.

configuración de encabezado

Encabezado de nivel 1

Simule algunos datos para profundizar la comprensión. Una interfaz existente es consultar la lista de usuarios del sistema , como: /user/search, y el resultado devuelto es el siguiente:

 
 

json

copiar codigo

{ "code": 0, "msg": "请求成功", "data": [ { "id": 1, "username": "admin", "nickname": "超管", "phone": "18818881888", "createTime": "2023-06-23 17:16:00" }, { "id": 2, "username": "cc", "nickname": "管理员", "phone": "18818881888", "createTime": "2023-06-23 17:16:00" }, ... ] }

Ahora publique el código de EasyExcel:

 
 

Java

copiar codigo

// 创建excel文件 try (ExcelWriter excelWriter = EasyExcel.write(path).build()) { WriteSheet writeSheet = EasyExcel.writerSheet("sheet索引", "sheet名称").head(getHeader()).build(); excelWriter.write(getDataList(), writeSheet); }

 
 

json

copiar codigo

// 模拟表头 private static List<List<String>> getHeader() { List<List<String>> list = new ArrayList<>(); list.add(createHead("账号")); list.add(createHead("昵称")); list.add(createHead("联系方式")); list.add(createHead("注册时间")); return list; } public static List<String> createHead(String... head) { return new ArrayList<>(Arrays.asList(head)); } // 模拟数据 public static List<List<Object>> getDataList() { List<List<Object>> list = new ArrayList<>(); list.add(createData("admin", "超管", "18818881888", "2023-06-23 17:16:00")); list.add(createData("cc", "管理员", "18818881888", "2023-06-23 17:16:00")); return list; } public static List<Object> createData(String... data) { return new ArrayList<>(Arrays.asList(data)); }s

Entonces el efecto de exportación es así:

No se preocupe por el estilo de Excel de la imagen del efecto ahora, realizaremos la configuración dinámica más tarde, como el ancho de columna, el color de fondo del encabezado, el centrado de la fuente, etc.

Aunque hemos escrito el código hasta el cansancio anteriormente, los desarrolladores inteligentes deben saber cómo convertir los datos de la consulta de la base de datos al formato correspondiente, por lo que se omite esta sección.

Ahora podemos obtener la configuración básica del encabezado:

 
 

json

copiar codigo

"customHeads": [ { "fieldName": "username", "fieldNameZh": "账号" }, { "fieldName": "nickname", "fieldNameZh": "昵称" }, { "fieldName": "phone", "fieldNameZh": "联系方式" }, { "fieldName": "createTime", "fieldNameZh": "注册时间" } ]

Eso es:

  • fieldName : nombre del atributo, de modo que el atributo y el valor se puedan encontrar a través de la reflexión del objeto de datos del resultado devuelto
  • fieldNameZh : el nombre del atributo definitivamente no es adecuado como nombre del encabezado, agregue una descripción en chino para reemplazar el nombre del atributo como encabezado

Con la base anterior, podemos agregar más elementos para lograr la riqueza de funciones, como

 
 

json

copiar codigo

{ "fieldName": "username", "fieldNameZh": "账号", "width": 20, // 列宽 "backgroundColor": 1, // 表头背景色 "fontSize": 20, // 字体大小 "type": "date(yyyy-MM-dd)" // 字段类型 ... }

Nota: El tipo de campo se puede usar para el formato de datos. Por ejemplo, el atributo es un estado de estado, 1 significa normal y 2 significa anormal. Entonces no tiene sentido exportar este 1 o 2, por lo que el chino correspondiente a este valor de estado se puede identificar a través del tipo de campo Descripción, tal exportación es normal.

Los encabezados de tabla de un nivel ya pueden cumplir con muchos de nuestros escenarios, pero no es suficiente. Según mi experiencia, a menudo se necesitan encabezados de tabla de dos líneas o incluso encabezados de tabla complejos. Afortunadamente, EasyExcel admite encabezados de tabla de varios niveles.

encabezado de varios niveles

Primero publique el código de muestra para que EasyExcel genere el encabezado de segundo nivel :

 
 

Java

copiar codigo

// 模拟表头 private static List<List<String>> getHeader() { List<List<String>> list = new ArrayList<>(); list.add(createHead("用户信息", "账号")); list.add(createHead("用户信息", "昵称")); list.add(createHead("用户信息", "联系方式")); list.add(createHead("用户信息", "注册时间")); list.add(createHead("角色信息", "超管")); list.add(createHead("角色信息", "管理员")); return list; } public static List<String> createHead(String... head) { return new ArrayList<>(Arrays.asList(head)); } // 模拟数据 public static List<List<Object>> getDataList() { List<List<Object>> list = new ArrayList<>(); list.add(createData("admin", "超管", "18818881888", "2023-06-23 17:16:00", "是", "是")); list.add(createData("cc", "管理员", "18818881888", "2023-06-23 17:16:00", "否", "是")); return list; } public static List<Object> createData(String... data) { return new ArrayList<>(Arrays.asList(data)); }

El efecto es así:

Puede verse que las primeras cuatro columnas tienen un encabezado común [Información del usuario], y las dos últimas columnas tienen un encabezado común [Información del rol].

Del código de muestra anterior, sabemos que para fusionar los encabezados, la lista de datos debe estar en orden y tener el mismo nombre de encabezado, para que EasyExcel la reconozca y luego tenga el efecto de fusión, que necesita atención.

De manera similar, cuando necesitamos generar encabezados complejos, podemos hacer esto:

 
 

Java

copiar codigo

// 模拟表头 private static List<List<String>> getHeader() { List<List<String>> list = new ArrayList<>(); list.add(createHead("导出用户数据", "用户信息", "账号")); list.add(createHead("导出用户数据", "用户信息", "昵称")); list.add(createHead("导出用户数据", "用户信息", "联系方式")); list.add(createHead("导出用户数据", "用户信息", "注册时间")); list.add(createHead("导出用户数据", "角色信息", "超管")); list.add(createHead("导出用户数据", "角色信息", "管理员")); return list; }

Representaciones:

en conclusión

Lo anterior es mi pensamiento e implementación de la función de exportación. Debido a limitaciones de espacio, no he publicado el código completo, pero creo que el contenido anterior es suficiente para que todos lo usen como referencia. Para el contenido faltante, como el ancho de columna, fuente de color y otras configuraciones, consulte los documentos oficiales de EasyExcel para lograrlo, la forma principal es configurar dinámicamente el archivo de exportación de EasyExcel de acuerdo con la información de configuración JSON pasada desde el front-end.

importar

La importación es un proceso de dos pasos:

  1. Plantilla de importación de descarga de usuario
  2. El usuario completa el contenido en la plantilla de importación y luego carga el archivo de plantilla en el sistema para realizar la operación de importación de datos.

Descargar plantilla de importación

La importación de plantillas solo requiere el parámetro customHeads anterior :

 
 

json

copiar codigo

"customHeads": [ { "fieldName": "username", "fieldNameZh": "账号" }, { "fieldName": "nickname", "fieldNameZh": "昵称" }, { "fieldName": "phone", "fieldNameZh": "联系方式" }, { "fieldName": "createTime", "fieldNameZh": "注册时间" } ]

Incluso se puede omitir el nombre del campo y se genera un archivo de Excel con solo el encabezado.

Datos de importacion

Hay dos escenarios para importar datos:

  1. Importación de datos de una sola tabla , el escenario es muy simple
  2. La importación de datos complejos implica varias tablas, lo que es un poco más complicado
Importación de datos de una sola tabla

La tabla única solo necesita considerar los atributos de la clase de entidad correspondiente. Podemos obtener los atributos de la clase de entidad a través de la reflexión, por lo que los elementos de configuración necesarios son:

  • modelClazz : ruta de clase de entidad, como: com.cc.entity.User

Ejemplo de configuración:

 
 

json

copiar codigo

{ "modelClazz": "com.cc.entity.User", "customHeads": [ { "fieldName": "username", "fieldNameZh": "账号" }, { "fieldName": "nickname", "fieldNameZh": "昵称" }, { "fieldName": "phone", "fieldNameZh": "联系方式" }, { "fieldName": "createTime", "fieldNameZh": "注册时间" } ] }

De esta manera, cuando se importan los datos y EasyExcel lee cada fila de datos, se puede reconocer que el elemento de nombre de usuario corresponde al atributo de nombre de usuario de la clase com.cc.entity.User, y luego cosas como esta pueden ser hecho:

 
 

Java

copiar codigo

User user = new User(); user.setUsername(fieldName列的值)

A partir de esto, puede obtener una matriz List<User> userList y luego guardarla en la base de datos a través del UserService o UserMapper del sistema para realizar la operación de importación de datos.

importación de datos complejos

Datos complejos como este escenario: los datos de cada fila en el archivo de Excel son así:

cuenta Apodo Información del contacto tiempo de registro Nombre del personaje
administración súper tubo 18818881888 2023-06-23 17:16:00 superadministrador
CC administrador 18818881888 2023-06-23 17:16:00 administrador

Entre ellos, si se trata de una supergestión y si se trata de un administrador involucra la tabla de asociación:

  • Tabla de usuarios: tb_user
  • Tabla de roles: tb_role
  • Tabla de relación de roles de usuario: tb_user_role_relation

Para admitir esta importación de datos complejos, el sistema debe proporcionar un método de almacenamiento correspondiente:

  1. Crear una nueva clase DTO:

    La primera:

    Java

    copiar codigo

    public class UserDto { private String username; private String nickname; private String phone; private Date createTime; private Boolean superAdminFlag; private Boolean adminFlag; }

    El segundo tipo:

    Java

    copiar codigo

    public class UserDto { private User user; private Role role; }

    Deberíamos considerar estas dos situaciones de DTO. No hace falta decir que la primera puede ser tratada por la configuración anterior. Principalmente nos fijamos en la segunda. El segundo método necesita considerar el tema de la "ruta", por lo que la escritura El método de customHeads debe haber

    json

    copiar codigo

    { "modelClazz": "com.cc.model.UserDto", "customHeads": [ { "fieldName": "user.username", "fieldNameZh": "账号" }, ... ] }

    De esta forma, la ruta de la cuenta se configura como: usuario.nombredeusuario, y la consulta de reflejo de atributos debe tener un concepto recursivo, primero se busca el atributo de usuario de la clase UserDto, se obtiene la clase del atributo y luego se obtiene el atributo de nombre de usuario en él, y el método de asignación se convierte en:

    Java

    copiar codigo

    UserDto dto = new UserDto(); User user = new User(); user.setUsername(fieldName列的值); dto.setUser(user);

    Esto da como resultado una matriz List<UserDto> dtoList.

  2. Dado que existe un negocio de importación de datos complejo, en la capa comercial de servicios, también debe escribir una función de almacenamiento de datos compleja:

    Java

    copiar codigo

    public interface UserService { // 单条插入 void saveUserDto(UserDto dto); // 批量插入 void saveUserDtoBatch(List<UserDto> dtoList); }
    Java

    copiar codigo

    @Service public class UserServiceImpl implements UserService { @Autowired private UserMapper userMapper; @Autowired private RoleService roleService; @Autowired private UserRoleRelationService relationService; // 事务 @Transactional(rollbackFor = Exception.class) @Override public void saveUserDto(UserDto dto) { // 保存用户 User user = userMapper.save(dto.getUser()); // 保存角色 Role role = roleService.save(dto.getRole); // 保存关联 UserRoleRelation relation = new UserRoleRelation(); relation.setUserId(user.getId()); relation.setRoleId(role.getId()); relationService.save(relation); } // 批量插入代码省略,原理同上 void saveUserDtoBatch(List<UserDto> dtoList); }
  3. Cada fila de datos leídos a través de EasyExcel se puede convertir en un objeto UserDto, y luego los datos se pueden guardar de forma individual o en lotes.Durante este período, hay muchos puntos que se pueden optimizar y considerar, como:

    • Los lotes son más eficientes y eficaces que el almacenamiento único, pero no es fácil identificar algunas filas fallidas en los lotes.
    • El número de guardados por lotes no debe ser demasiado grande y se debe considerar el rendimiento del sistema y la base de datos.Por ejemplo, se realiza un guardado cada vez que se leen 500 filas.
    • La pantalla de progreso guardada, primero obtenga el número total de filas en Excel, luego calcule el progreso de acuerdo con el número actual de filas leídas y devuélvalo a la parte delantera
    • Si el tiempo de importación es demasiado largo, se puede convertir en una tarea en segundo plano. En cuanto al recordatorio de front-end, puede ser un sondeo o WebSocket.

Por lo tanto, debe especificar el método de consulta, que se proporciona arriba del elemento de configuración.

Resumen de elementos de configuración

Finalmente, se proporciona un elemento de configuración general como referencia:

Exportar configuración de datos

 
 

json

copiar codigo

{ "filename": "用户数据导出", "serviceClazz": "com.cc.service.UserService", "methodName": "listByCondition", "countMethodName": "countByCondition", "searchParams": [ { "nickname": "cc" // 搜索昵称为cc的用户 } ], "customHeads": [ { "fieldName": "username", "fieldNameZh": "账号", "width": 20, // 列宽 "fontSize": 20 // 字体大小 }, { "fieldName": "createTime", "fieldNameZh": "注册时间", "type": "date(yyyy-MM-dd)" // 属性类型声明为date,并且转换成指定格式导出 } ] }

Importar configuración de plantilla

 
 

json

copiar codigo

{ "filename": "用户数据导入", "modelClazz": "com.cc.entity.User", "customHeads": [ { "fieldName": "username", "fieldNameZh": "账号", "width": 20, // 列宽 "fontSize": 20 // 字体大小 }, { "fieldName": "createTime", "fieldNameZh": "注册时间", "type": "date(yyyy-MM-dd)" // 属性类型声明为date,并且转换成指定格式导出 } ] }

Importar configuración de datos

 
 

json

copiar codigo

{ "modelClazz": "com.cc.entity.User", "serviceClazz": "com.cc.service.UserService", "methodName": "save", "customHeads": [ { "fieldName": "username", "fieldNameZh": "账号", }, { "fieldName": "createTime", "fieldNameZh": "注册时间", "type": "date(yyyy-MM-dd)" // 属性类型声明为date,并且转换成指定格式导出 } ] }

Supongo que te gusta

Origin blog.csdn.net/BASK2312/article/details/131394721
Recomendado
Clasificación