海南版本提交
This commit is contained in:
@@ -0,0 +1,88 @@
|
||||
package com.njcn.advance.utils;
|
||||
|
||||
import com.alibaba.excel.context.AnalysisContext;
|
||||
import com.alibaba.excel.event.AnalysisEventListener;
|
||||
import com.alibaba.excel.exception.ExcelDataConvertException;
|
||||
import lombok.Getter;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Description:
|
||||
* Date: 2024/3/15 16:02【需求编号】
|
||||
*
|
||||
* @author clam
|
||||
* @version V1.0.0
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class EasyExcelDefaultListener<T> extends AnalysisEventListener<T> {
|
||||
|
||||
/**
|
||||
* 批处理阈值
|
||||
*/
|
||||
private static final int BATCH_COUNT = 20;
|
||||
|
||||
/**
|
||||
* 用来存放待处理的数据
|
||||
*/
|
||||
@Getter
|
||||
private List<T> list = new ArrayList<>(BATCH_COUNT);
|
||||
|
||||
/**
|
||||
* 读取excel数据前操作 <br>
|
||||
*
|
||||
* 只有不读取表头数据时才会触发此方法)
|
||||
*/
|
||||
@Override
|
||||
public void invokeHeadMap(Map<Integer, String> headMap, AnalysisContext context) {
|
||||
log.info("======================================================");
|
||||
log.info("======================================================");
|
||||
}
|
||||
|
||||
/**
|
||||
* 读取excel数据操作
|
||||
* @param obj
|
||||
* @param context
|
||||
*/
|
||||
@Override
|
||||
public void invoke(T obj, AnalysisContext context) {
|
||||
list.add(obj);
|
||||
|
||||
if (list.size() >= BATCH_COUNT) {
|
||||
//将数据保存到数据库中
|
||||
fun(list);
|
||||
list.clear();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 具体业务
|
||||
*/
|
||||
protected abstract void fun(List<T> list);
|
||||
|
||||
/**
|
||||
* 读取完excel数据后的操作
|
||||
*/
|
||||
@Override
|
||||
public void doAfterAllAnalysed(AnalysisContext context) {
|
||||
if (list.size() > 0) {
|
||||
fun(list);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 在读取excel异常 获取其他异常下会调用本接口。抛出异常则停止读取。如果这里不抛出异常则 继续读取下一行。
|
||||
*/
|
||||
@Override
|
||||
public void onException(Exception exception, AnalysisContext context) {
|
||||
log.error("解析失败,但是继续解析下一行:{}", exception.getMessage());
|
||||
if (exception instanceof ExcelDataConvertException) {
|
||||
ExcelDataConvertException ex = (ExcelDataConvertException) exception;
|
||||
log.error("第{}行,第{}列解析异常,数据为:{}", ex.getRowIndex(), ex.getColumnIndex(), ex.getCellData());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,379 @@
|
||||
package com.njcn.advance.utils;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.EasyExcelFactory;
|
||||
import com.alibaba.excel.event.AnalysisEventListener;
|
||||
import com.alibaba.excel.write.handler.WriteHandler;
|
||||
import lombok.SneakyThrows;
|
||||
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.net.URLEncoder;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Description:
|
||||
* Date: 2024/3/15 15:58【需求编号】
|
||||
*
|
||||
* @author clam
|
||||
* @version V1.0.0
|
||||
*/
|
||||
public class EasyExcelUtil {
|
||||
|
||||
//====================================================无JAVA模型读取excel数据===============================================================
|
||||
|
||||
/**
|
||||
* 同步无模型读(默认读取sheet0,从第2行开始读)
|
||||
* @param file excel文件的绝对路径
|
||||
*/
|
||||
public static List<Map<Integer, String>> syncRead(String file) {
|
||||
return EasyExcelFactory.read(file).sheet().doReadSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步无模型读(自定义读取sheetX,从第2行开始读)
|
||||
* @param file excel文件的绝对路径
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
*/
|
||||
public static List<Map<Integer, String>> syncRead(String file, Integer sheetNum) {
|
||||
return EasyExcelFactory.read(file).sheet(sheetNum).doReadSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步无模型读(指定sheet和表头占的行数)
|
||||
* @param file
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
* @param headNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
|
||||
*/
|
||||
public static List<Map<Integer, String>> syncRead(String file, Integer sheetNum, Integer headNum) {
|
||||
return EasyExcelFactory.read(file).sheet(sheetNum).headRowNumber(headNum).doReadSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步无模型读(指定sheet和表头占的行数)
|
||||
* @param inputStream
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
* @param headNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
|
||||
*/
|
||||
public static List<Map<Integer, String>> syncRead(InputStream inputStream, Integer sheetNum, Integer headNum) {
|
||||
return EasyExcelFactory.read(inputStream).sheet(sheetNum).headRowNumber(headNum).doReadSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步无模型读(指定sheet和表头占的行数)
|
||||
* @param file
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
* @param headNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
|
||||
*/
|
||||
public static List<Map<Integer, String>> syncRead(File file, Integer sheetNum, Integer headNum) {
|
||||
return EasyExcelFactory.read(file).sheet(sheetNum).headRowNumber(headNum).doReadSync();
|
||||
}
|
||||
//====================================================无JAVA模型读取excel数据===============================================================
|
||||
|
||||
//====================================================将excel数据同步到JAVA模型属性里===============================================================
|
||||
|
||||
/**
|
||||
* 同步按模型读(默认读取sheet0,不读取表头,从第2行开始读)
|
||||
* @param file
|
||||
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
|
||||
*/
|
||||
public static <T> List<T> syncReadModel(String file, Class clazz) {
|
||||
return EasyExcelFactory.read(file).sheet().head(clazz).doReadSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步按模型读(默认表头占一行,不读取表头,从第2行开始读)
|
||||
* @param file
|
||||
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
*/
|
||||
public static <T> List<T> syncReadModel(String file, Class clazz, Integer sheetNum, Integer headNum) {
|
||||
return EasyExcelFactory.read(file).sheet(sheetNum).headRowNumber(headNum).head(clazz).doReadSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步按模型读(指定sheet,不读取表头)
|
||||
* @param inputStream
|
||||
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
*/
|
||||
public static <T> List<T> syncReadModel(InputStream inputStream, Class clazz, Integer sheetNum, Integer headNum) {
|
||||
return EasyExcelFactory.read(inputStream).sheet(sheetNum).headRowNumber(headNum).head(clazz).doReadSync();
|
||||
}
|
||||
|
||||
/**
|
||||
* 同步按模型读(指定sheet,不读取表头)
|
||||
* @param file
|
||||
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
*/
|
||||
public static <T> List<T> syncReadModel(File file, Class clazz, Integer sheetNum, Integer headNum) {
|
||||
return EasyExcelFactory.read(file).sheet(sheetNum).headRowNumber(headNum).head(clazz).doReadSync();
|
||||
}
|
||||
//====================================================将excel数据同步到JAVA模型属性里===============================================================
|
||||
|
||||
//====================================================异步读取excel数据===============================================================
|
||||
|
||||
/**
|
||||
* 异步无模型读(默认读取sheet0,不读取表头,从第2行开始读)
|
||||
* @param listener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
|
||||
* @param file 表头占的行数,从0开始(如果要连表头一起读出来则传0)
|
||||
*/
|
||||
public static <T> void asyncRead(String file, AnalysisEventListener<T> listener) {
|
||||
EasyExcelFactory.read(file, listener).sheet().doRead();
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步无模型读(默认表头占一行,不读取表头,从第2行开始读)
|
||||
* @param file 表头占的行数,从0开始(如果要连表头一起读出来则传0)
|
||||
* @param listener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
*/
|
||||
public static <T> void asyncRead(String file, AnalysisEventListener<T> listener, Integer sheetNum) {
|
||||
EasyExcelFactory.read(file, listener).sheet(sheetNum).doRead();
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步无模型读(指定sheet和表头占的行数)
|
||||
* @param inputStream
|
||||
* @param listener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
* @param headNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
|
||||
*/
|
||||
public static <T> void asyncRead(InputStream inputStream, AnalysisEventListener<T> listener, Integer sheetNum, Integer headNum) {
|
||||
EasyExcelFactory.read(inputStream, listener).sheet(sheetNum).headRowNumber(headNum).doRead();
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步无模型读(指定sheet和表头占的行数)
|
||||
* @param file
|
||||
* @param listener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
* @param headNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
|
||||
*/
|
||||
public static <T> void asyncRead(File file, AnalysisEventListener<T> listener, Integer sheetNum, Integer headNum) {
|
||||
EasyExcelFactory.read(file, listener).sheet(sheetNum).headRowNumber(headNum).doRead();
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步无模型读(指定sheet和表头占的行数)
|
||||
* @param file
|
||||
* @param listener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
* @param headNum 表头占的行数,从0开始(如果要连表头一起读出来则传0)
|
||||
* @return
|
||||
*/
|
||||
public static <T> void asyncRead(String file, AnalysisEventListener<T> listener, Integer sheetNum, Integer headNum) {
|
||||
EasyExcelFactory.read(file, listener).sheet(sheetNum).headRowNumber(headNum).doRead();
|
||||
}
|
||||
//====================================================异步读取excel数据===============================================================
|
||||
|
||||
//====================================================将excel数据异步到JAVA模型属性里===============================================================
|
||||
/**
|
||||
* 异步按模型读取(默认读取sheet0,不读取表头,从第2行开始读)
|
||||
* @param file
|
||||
* @param listener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
|
||||
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
|
||||
*/
|
||||
public static <T> void asyncReadModel(String file, AnalysisEventListener<T> listener, Class clazz) {
|
||||
EasyExcelFactory.read(file, clazz, listener).sheet().doRead();
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步按模型读取(默认表头占一行,不读取表头,从第2行开始读)
|
||||
* @param file
|
||||
* @param listener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
|
||||
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
*/
|
||||
public static <T> void asyncReadModel(String file, AnalysisEventListener<T> listener, Class clazz, Integer sheetNum) {
|
||||
EasyExcelFactory.read(file, clazz, listener).sheet(sheetNum).doRead();
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步按模型读取
|
||||
* @param file
|
||||
* @param listener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
|
||||
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
*/
|
||||
public static <T> void asyncReadModel(File file, AnalysisEventListener<T> listener, Class clazz, Integer sheetNum) {
|
||||
EasyExcelFactory.read(file, clazz, listener).sheet(sheetNum).doRead();
|
||||
}
|
||||
|
||||
/**
|
||||
* 异步按模型读取
|
||||
* @param inputStream
|
||||
* @param listener 监听器,在监听器中可以处理行数据LinkedHashMap,表头数据,异常处理等
|
||||
* @param clazz 模型的类类型(excel数据会按该类型转换成对象)
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
*/
|
||||
public static <T> void asyncReadModel(InputStream inputStream, AnalysisEventListener<T> listener, Class clazz, Integer sheetNum) {
|
||||
EasyExcelFactory.read(inputStream, clazz, listener).sheet(sheetNum).doRead();
|
||||
}
|
||||
//====================================================将excel数据异步到JAVA模型属性里===============================================================
|
||||
|
||||
|
||||
//====================================================无JAVA模型写文件===============================================================
|
||||
/**
|
||||
* 无模板写文件
|
||||
* @param file
|
||||
* @param head 表头数据
|
||||
* @param data 表内容数据
|
||||
*/
|
||||
public static void write(String file, List<List<String>> head, List<List<Object>> data) {
|
||||
EasyExcel.write(file).head(head).sheet().doWrite(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 无模板写文件
|
||||
* @param file
|
||||
* @param head 表头数据
|
||||
* @param data 表内容数据
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
* @param sheetName sheet名称
|
||||
*/
|
||||
public static void write(String file, List<List<String>> head, List<List<Object>> data, Integer sheetNum, String sheetName) {
|
||||
EasyExcel.write(file).head(head).sheet(sheetNum, sheetName).doWrite(data);
|
||||
}
|
||||
//====================================================无JAVA模型写文件===============================================================
|
||||
|
||||
//====================================================有Excel模板写文件===============================================================
|
||||
/**
|
||||
* 根据excel模板文件写入文件,可以实现向已有文件中添加数据的功能
|
||||
* @param file
|
||||
* @param template
|
||||
* @param data
|
||||
*/
|
||||
public static <T> void writeTemplate(String file, String template, List<T> data) {
|
||||
EasyExcel.write(file).withTemplate(template).sheet().doWrite(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据excel模板文件写入文件
|
||||
* @param file
|
||||
* @param template
|
||||
* @param clazz
|
||||
* @param data
|
||||
*/
|
||||
public static <T> void writeTemplate(String file, String template, Class clazz, List<T> data) {
|
||||
EasyExcel.write(file, clazz).withTemplate(template).sheet().doWrite(data);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//====================================================无模板写文件===============================================================
|
||||
|
||||
//====================================================有模板写文件===============================================================
|
||||
/**
|
||||
* 按模板写文件
|
||||
* @param file
|
||||
* @param clazz 表头模板
|
||||
* @param data 数据
|
||||
*/
|
||||
public static <T> void write(String file, Class clazz, List<T> data) {
|
||||
EasyExcel.write(file, clazz).sheet().doWrite(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按模板写文件
|
||||
* @param file
|
||||
* @param clazz 表头模板
|
||||
* @param data 数据
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
* @param sheetName sheet名称
|
||||
*/
|
||||
public static <T> void write(String file, Class clazz, List<T> data, Integer sheetNum, String sheetName) {
|
||||
EasyExcel.write(file, clazz).sheet(sheetNum, sheetName).doWrite(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按模板写文件
|
||||
* @param file
|
||||
* @param clazz 表头模板
|
||||
* @param data 数据
|
||||
* @param writeHandler 自定义的处理器,比如设置table样式,设置超链接、单元格下拉框等等功能都可以通过这个实现(需要注册多个则自己通过链式去调用)
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
* @param sheetName sheet名称
|
||||
*/
|
||||
public static <T> void write(String file, Class clazz, List<T> data, WriteHandler writeHandler, Integer sheetNum, String sheetName) {
|
||||
EasyExcel.write(file, clazz).registerWriteHandler(writeHandler).sheet(sheetNum, sheetName).doWrite(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按模板写文件(包含某些字段)
|
||||
* @param file
|
||||
* @param clazz 表头模板
|
||||
* @param data 数据
|
||||
* @param includeCols 包含字段集合,根据字段名称显示
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
* @param sheetName sheet名称
|
||||
*/
|
||||
public static <T> void writeInclude(String file, Class clazz, List<T> data, Set<String> includeCols, Integer sheetNum, String sheetName) {
|
||||
EasyExcel.write(file, clazz).includeColumnFiledNames(includeCols).sheet(sheetNum, sheetName).doWrite(data);
|
||||
}
|
||||
|
||||
/**
|
||||
* 按模板写文件(排除某些字段)
|
||||
* @param file
|
||||
* @param clazz 表头模板
|
||||
* @param data 数据
|
||||
* @param excludeCols 过滤排除的字段,根据字段名称过滤
|
||||
* @param sheetNum sheet页号,从0开始
|
||||
* @param sheetName sheet名称
|
||||
*/
|
||||
public static <T> void writeExclude(String file, Class clazz, List<T> data, Set<String> excludeCols, Integer sheetNum, String sheetName) {
|
||||
EasyExcel.write(file, clazz).excludeColumnFiledNames(excludeCols).sheet(sheetNum, sheetName).doWrite(data);
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------------------------------
|
||||
/**
|
||||
* 多个sheet页的数据链式写入
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
public static EasyExcelWriteTool writeWithSheets(String file) {
|
||||
return new EasyExcelWriteTool(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多个sheet页的数据链式写入
|
||||
*
|
||||
* @param file
|
||||
*/
|
||||
public static EasyExcelWriteTool writeWithSheets(File file) {
|
||||
return new EasyExcelWriteTool(file);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多个sheet页的数据链式写入
|
||||
*
|
||||
* @param outputStream
|
||||
*/
|
||||
public static EasyExcelWriteTool writeWithSheets(OutputStream outputStream) {
|
||||
return new EasyExcelWriteTool(outputStream);
|
||||
}
|
||||
|
||||
/**
|
||||
* 多个sheet页的数据链式写入(失败了会返回一个有部分数据的Excel)
|
||||
*
|
||||
* @param response
|
||||
* @param exportFileName 导出的文件名称
|
||||
*/
|
||||
@SneakyThrows
|
||||
public static EasyExcelWriteTool writeWithSheetsWeb(HttpServletResponse response, String exportFileName) throws IOException {
|
||||
response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
|
||||
// 这里URLEncoder.encode可以防止中文乱码
|
||||
String fileName = URLEncoder.encode(exportFileName, "UTF-8");
|
||||
response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");
|
||||
|
||||
return new EasyExcelWriteTool(response.getOutputStream());
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,68 @@
|
||||
package com.njcn.advance.utils;
|
||||
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.ExcelWriter;
|
||||
import com.alibaba.excel.write.metadata.WriteSheet;
|
||||
import org.apache.poi.ss.formula.functions.T;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Description:
|
||||
* Date: 2024/3/15 16:00【需求编号】
|
||||
*
|
||||
* @author clam
|
||||
* @version V1.0.0
|
||||
*/
|
||||
public class EasyExcelWriteTool {
|
||||
|
||||
private int sheetNum;
|
||||
private ExcelWriter excelWriter;
|
||||
|
||||
public EasyExcelWriteTool(OutputStream outputStream) {
|
||||
excelWriter = EasyExcel.write(outputStream).build();
|
||||
}
|
||||
|
||||
public EasyExcelWriteTool(File file) {
|
||||
excelWriter = EasyExcel.write(file).build();
|
||||
}
|
||||
|
||||
public EasyExcelWriteTool(String filePath) {
|
||||
excelWriter = EasyExcel.write(filePath).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* 链式模板表头写入
|
||||
* @param clazz 表头格式
|
||||
* @param data 数据 List<ExcelModel> 或者List<List<Object>>
|
||||
* @return
|
||||
*/
|
||||
public <T> EasyExcelWriteTool writeModel(Class clazz, List<T> data, String sheetName) {
|
||||
final WriteSheet writeSheet = EasyExcel.writerSheet(this.sheetNum++, sheetName).head(clazz).build();
|
||||
excelWriter.write(data, writeSheet);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 链式自定义表头写入
|
||||
* @param head
|
||||
* @param data 数据 List<ExcelModel> 或者List<List<Object>>
|
||||
* @param sheetName
|
||||
* @return
|
||||
*/
|
||||
public EasyExcelWriteTool write(List<List<String>> head, List<T> data, String sheetName) {
|
||||
final WriteSheet writeSheet = EasyExcel.writerSheet(this.sheetNum++, sheetName).head(head).build();
|
||||
excelWriter.write(data, writeSheet);
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* 使用此类结束后,一定要关闭流
|
||||
*/
|
||||
public void finish() {
|
||||
excelWriter.finish();
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user