冀北数据周报
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package com.njcn.device.pq.controller;
|
||||
|
||||
|
||||
import cn.hutool.core.date.DateUtil;
|
||||
import com.njcn.common.pojo.annotation.OperateInfo;
|
||||
import com.njcn.common.pojo.enums.common.LogEnum;
|
||||
import com.njcn.common.pojo.enums.response.CommonResponseEnum;
|
||||
@@ -155,6 +156,8 @@ public class DataVerifyController extends BaseController {
|
||||
public void dataVerifyExcel(HttpServletResponse response, MonitorBaseParam monitorBaseParam) throws IOException {
|
||||
response.setContentType("application/vnd.ms-excel");
|
||||
response.setCharacterEncoding("utf-8");
|
||||
monitorBaseParam.setSearchBeginTime(DateUtil.beginOfDay(DateUtil.parse(monitorBaseParam.getSearchBeginTime())).toString());
|
||||
monitorBaseParam.setSearchEndTime(DateUtil.endOfDay(DateUtil.parse(monitorBaseParam.getSearchEndTime())).toString());
|
||||
iPqDataVerifyBakService.dataVerifyExcel(response, monitorBaseParam);
|
||||
|
||||
}
|
||||
|
||||
@@ -4,6 +4,7 @@ package com.njcn.device.pq.service.impl;
|
||||
import cn.hutool.core.codec.Base64;
|
||||
import cn.hutool.core.collection.CollUtil;
|
||||
import cn.hutool.core.date.*;
|
||||
import cn.hutool.core.util.NumberUtil;
|
||||
import cn.hutool.core.util.ObjUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.hutool.json.JSONArray;
|
||||
@@ -11,29 +12,39 @@ import cn.hutool.json.JSONConfig;
|
||||
import cn.hutool.json.JSONObject;
|
||||
import cn.hutool.json.JSONTokener;
|
||||
import com.alibaba.excel.EasyExcel;
|
||||
import com.alibaba.excel.ExcelWriter;
|
||||
import com.alibaba.excel.write.metadata.WriteSheet;
|
||||
import com.alibaba.excel.write.metadata.WriteTable;
|
||||
import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
|
||||
import com.njcn.common.pojo.dto.SimpleDTO;
|
||||
import com.njcn.common.pojo.exception.BusinessException;
|
||||
import com.njcn.dataProcess.api.PqReasonableRangeFeignClient;
|
||||
import com.njcn.dataProcess.enums.DataCleanEnum;
|
||||
import com.njcn.dataProcess.param.DataCleanParam;
|
||||
import com.njcn.dataProcess.pojo.dto.PqReasonableRangeDto;
|
||||
import com.njcn.device.common.mapper.onlinerate.OnLineRateMapper;
|
||||
import com.njcn.device.common.service.GeneralDeviceService;
|
||||
import com.njcn.device.line.mapper.LineMapper;
|
||||
import com.njcn.device.line.service.DeptLineService;
|
||||
import com.njcn.device.pq.constant.Param;
|
||||
import com.njcn.device.pq.enums.LineBaseEnum;
|
||||
import com.njcn.device.pq.mapper.PqDataVerifyBakMapper;
|
||||
import com.njcn.device.pq.pojo.dto.GeneralDeviceDTO;
|
||||
import com.njcn.device.pq.pojo.param.DeviceInfoParam;
|
||||
import com.njcn.device.pq.pojo.param.OnlineRateParam;
|
||||
import com.njcn.device.pq.pojo.param.dataClean.MonitorBaseParam;
|
||||
import com.njcn.device.pq.pojo.po.DeptLine;
|
||||
import com.njcn.device.pq.pojo.po.PqDataVerifyBak;
|
||||
import com.njcn.device.pq.pojo.vo.AreaLineInfoVO;
|
||||
import com.njcn.device.pq.pojo.vo.LineDetailDataVO;
|
||||
import com.njcn.device.pq.pojo.vo.*;
|
||||
import com.njcn.device.pq.pojo.vo.dataClean.*;
|
||||
import com.njcn.device.pq.service.CommTerminalService;
|
||||
import com.njcn.device.pq.service.IPqDataVerifyBakService;
|
||||
import com.njcn.device.pq.utils.DataLineExcelUtil;
|
||||
import com.njcn.device.rstatintegrity.mapper.RStatIntegrityDMapper;
|
||||
import com.njcn.oss.utils.FileStorageUtil;
|
||||
import com.njcn.poi.excel.ExcelUtil;
|
||||
import com.njcn.supervision.api.UserLedgerFeignClient;
|
||||
import com.njcn.system.api.DictTreeFeignClient;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
@@ -43,6 +54,7 @@ import org.springframework.stereotype.Service;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
import java.time.format.DateTimeFormatter;
|
||||
import java.util.*;
|
||||
@@ -52,6 +64,7 @@ import java.util.regex.Pattern;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import static com.njcn.device.pq.utils.FixedDynamicExcelExport.exportExcelByDataSize;
|
||||
import static com.njcn.device.pq.utils.FixedDynamicExcelExport.writeCenterStyle;
|
||||
|
||||
/**
|
||||
* <p>
|
||||
@@ -75,6 +88,9 @@ public class PqDataVerifyBakServiceImpl extends ServiceImpl<PqDataVerifyBakMappe
|
||||
private final UserLedgerFeignClient userLedgerFeignClient;
|
||||
private final FileStorageUtil fileStorageUtil;
|
||||
private final DeptLineService deptLineService;
|
||||
private final GeneralDeviceService deviceService;
|
||||
private final RStatIntegrityDMapper integrityDMapper;
|
||||
private final OnLineRateMapper onLineRateMapper;
|
||||
|
||||
@Override
|
||||
public VerifyMonitorVO getMonitorVerifyData(MonitorBaseParam monitorBaseParam) {
|
||||
@@ -373,37 +389,225 @@ public class PqDataVerifyBakServiceImpl extends ServiceImpl<PqDataVerifyBakMappe
|
||||
@Override
|
||||
public void dataVerifyExcel(HttpServletResponse response, MonitorBaseParam monitorBaseParam) throws IOException {
|
||||
if (StrUtil.isNotBlank(monitorBaseParam.getDeptId())) {
|
||||
List<String> monitorIds = commTerminalService.getRunMonitorByDept(monitorBaseParam);
|
||||
monitorBaseParam.setMonitorIds(monitorIds);
|
||||
}
|
||||
List<DataVerifyExcel> dataVerifyExcels = this.baseMapper.selectDataVerifySum(monitorBaseParam);
|
||||
List<String> ids = dataVerifyExcels.stream().map(DataVerifyExcel::getLineId).collect(Collectors.toList());
|
||||
List<AreaLineInfoVO> areaLineInfoVOList = lineMapper.getBaseLineAreaInfo(ids, null, null);
|
||||
Map<String, AreaLineInfoVO> map = areaLineInfoVOList.stream().collect(Collectors.toMap(AreaLineInfoVO::getLineId, Function.identity()));
|
||||
for (DataVerifyExcel dataVerifyExcel : dataVerifyExcels) {
|
||||
if (map.containsKey(dataVerifyExcel.getLineId())) {
|
||||
AreaLineInfoVO areaLineInfoVO = map.get(dataVerifyExcel.getLineId());
|
||||
dataVerifyExcel.setCity(areaLineInfoVO.getGdName());
|
||||
dataVerifyExcel.setLineName(areaLineInfoVO.getLineName());
|
||||
dataVerifyExcel.setLoadType(areaLineInfoVO.getLoadType());
|
||||
dataVerifyExcel.setObjName(areaLineInfoVO.getObjName());
|
||||
dataVerifyExcel.setStationName(areaLineInfoVO.getSubName());
|
||||
dataVerifyExcel.setPowerSubstationName(areaLineInfoVO.getPowerSubstationName());
|
||||
dataVerifyExcel.setDevName(areaLineInfoVO.getDeviceName());
|
||||
dataVerifyExcel.setIp(areaLineInfoVO.getIp());
|
||||
dataVerifyExcel.setManufacturer(areaLineInfoVO.getManufacturer());
|
||||
DeviceInfoParam param = new DeviceInfoParam();
|
||||
param.setDeptIndex(monitorBaseParam.getDeptId());
|
||||
param.setStatisticalType(new SimpleDTO());
|
||||
//获取终端台账类信息
|
||||
List<GeneralDeviceDTO> deviceInfo = deviceService.getDeviceInfo(param, Arrays.asList(0), Arrays.asList(1));
|
||||
List<String> lineIds = deviceInfo.stream().flatMap(x -> x.getLineIndexes().stream()).distinct().collect(Collectors.toList());
|
||||
List<String> devIds = deviceInfo.stream().flatMap(x -> x.getDeviceIndexes().stream()).distinct().collect(Collectors.toList());
|
||||
List<LineInfoMonitorIdVO> areaLineInfoVOList = lineMapper.getBaseLineInfoMonitorIdInfo(lineIds);
|
||||
List<LineInfoMonitorIdVO> monitorLine = areaLineInfoVOList.stream().filter(x -> StrUtil.isNotBlank(x.getMonitorId())).collect(Collectors.toList());
|
||||
List<LineInfoMonitorIdVO> gridSide = areaLineInfoVOList.stream().filter(x -> x.getPowerFlag() == 0).collect(Collectors.toList());
|
||||
List<LineInfoMonitorIdVO> nonGridSide = areaLineInfoVOList.stream().filter(x -> x.getPowerFlag() == 1).collect(Collectors.toList());
|
||||
|
||||
OnlineRateParam onlineRateParam = new OnlineRateParam();
|
||||
onlineRateParam.setIds(devIds);
|
||||
onlineRateParam.setStartTime(monitorBaseParam.getSearchBeginTime());
|
||||
onlineRateParam.setEndTime(monitorBaseParam.getSearchEndTime());
|
||||
//完整率
|
||||
List<RStatIntegrityVO> integrityList = integrityDMapper.getLineIntegrityRateInfo(lineIds,
|
||||
monitorBaseParam.getSearchBeginTime(),
|
||||
monitorBaseParam.getSearchEndTime());
|
||||
//获取所有终端在线率
|
||||
List<RStatOnlineRateVO> onlineRateByDev = onLineRateMapper.getOnlineRateByDevIds(onlineRateParam);
|
||||
List<CityDataExcel> cityData1 = new ArrayList<>();
|
||||
List<CityDataExcel> cityData2 = new ArrayList<>();
|
||||
List<CityDataExcel> cityData3 = new ArrayList<>();
|
||||
for (GeneralDeviceDTO dto : deviceInfo) {
|
||||
cityData1.add(addCityDataExcel(dto, monitorLine, onlineRateByDev, integrityList));
|
||||
cityData2.add(addCityDataExcel(dto, gridSide, onlineRateByDev, integrityList));
|
||||
cityData3.add(addCityDataExcel(dto, nonGridSide, onlineRateByDev, integrityList));
|
||||
}
|
||||
monitorBaseParam.setMonitorIds(lineIds);
|
||||
List<DataVerifyExcel> dataVerifyExcels = this.baseMapper.selectDataVerifySum(monitorBaseParam);
|
||||
Map<String, LineInfoMonitorIdVO> lineInfomap = areaLineInfoVOList.stream().collect(Collectors.toMap(LineInfoMonitorIdVO::getLineId, Function.identity()));
|
||||
for (DataVerifyExcel dataVerifyExcel : dataVerifyExcels) {
|
||||
if (lineInfomap.containsKey(dataVerifyExcel.getLineId())) {
|
||||
LineInfoMonitorIdVO areaLineInfoVO = lineInfomap.get(dataVerifyExcel.getLineId());
|
||||
dataVerifyExcel.setCity(areaLineInfoVO.getGdName());
|
||||
dataVerifyExcel.setLineName(areaLineInfoVO.getLineName());
|
||||
dataVerifyExcel.setLoadType(areaLineInfoVO.getLoadType());
|
||||
dataVerifyExcel.setObjName(areaLineInfoVO.getObjName());
|
||||
dataVerifyExcel.setStationName(areaLineInfoVO.getSubName());
|
||||
dataVerifyExcel.setPowerSubstationName(areaLineInfoVO.getPowerSubstationName());
|
||||
dataVerifyExcel.setDevName(areaLineInfoVO.getDeviceName());
|
||||
dataVerifyExcel.setIp(areaLineInfoVO.getIp());
|
||||
dataVerifyExcel.setManufacturer(areaLineInfoVO.getManufacturer());
|
||||
}
|
||||
}
|
||||
dataVerifyExcels.sort(Comparator
|
||||
.comparing(DataVerifyExcel::getAllTime, Comparator.reverseOrder())
|
||||
.thenComparing((DataVerifyExcel item) -> item.getCity() + "_" + item.getStationName() + "_" + item.getDevName())
|
||||
);
|
||||
Map<String, List> map = exportExcelByDataSize(dataVerifyExcels);
|
||||
// 步骤5:导出Excel(含列宽设置)
|
||||
ExcelWriter excelWriter = EasyExcel.write(response.getOutputStream())
|
||||
.registerWriteHandler(new SimpleColumnWidthStyleStrategy(20))
|
||||
.registerWriteHandler(writeCenterStyle()).build();
|
||||
WriteSheet writeSheet0 = EasyExcel.writerSheet("监测点异常指标统计").build();
|
||||
WriteTable writeTable0 = EasyExcel.writerTable(0).needHead(Boolean.TRUE).head(map.get("head")).build();
|
||||
excelWriter.write(map.get("data"), writeSheet0, writeTable0);
|
||||
|
||||
Map<Integer, List<CityDataExcel>> cityData = new HashMap<>();
|
||||
cityData.put(1, cityData1);
|
||||
cityData.put(2, cityData2);
|
||||
cityData.put(3, cityData3);
|
||||
|
||||
Map<Integer, List<LineDataExcel>> lineData = new HashMap<>();
|
||||
addLineDataExcel(cityData1, areaLineInfoVOList, onlineRateByDev, integrityList, lineData, 1);
|
||||
addLineDataExcel(cityData2, areaLineInfoVOList, onlineRateByDev, integrityList, lineData, 2);
|
||||
addLineDataExcel(cityData3, areaLineInfoVOList, onlineRateByDev, integrityList, lineData, 3);
|
||||
|
||||
excelExtracted(excelWriter, "数据质量", new HashMap<>(), cityData, true);
|
||||
excelExtracted(excelWriter, "数据质量表格", lineData, new HashMap<>(), false);
|
||||
excelWriter.finish();
|
||||
}
|
||||
}
|
||||
|
||||
private void addLineDataExcel(List<CityDataExcel> cityData1, List<LineInfoMonitorIdVO> areaLineInfoVOList, List<RStatOnlineRateVO> onlineRateByDev, List<RStatIntegrityVO> integrityList, Map<Integer, List<LineDataExcel>> lineData, Integer num) {
|
||||
List<LineDataExcel> lineDataExcels1 = new ArrayList<>();
|
||||
List<String> lineData1 = cityData1.stream().flatMap(x -> x.getAbnormalList().stream()).collect(Collectors.toList());
|
||||
List<LineInfoMonitorIdVO> lineInfoData1 = areaLineInfoVOList.stream().filter(x -> lineData1.contains(x.getLineId())).collect(Collectors.toList());
|
||||
LineDataExcel lineDataExcel;
|
||||
for (int i = 0; i < lineInfoData1.size(); i++) {
|
||||
LineInfoMonitorIdVO dto = lineInfoData1.get(i);
|
||||
lineDataExcel = new LineDataExcel();
|
||||
lineDataExcel.setLineNum(BigDecimal.valueOf((i + 1)));
|
||||
lineDataExcel.setLineName(dto.getLineName());
|
||||
lineDataExcel.setDeptName(dto.getDeviceName());
|
||||
lineDataExcel.setPowerSubstationName(dto.getPowerSubstationName());
|
||||
lineDataExcel.setObjName(dto.getObjName());
|
||||
lineDataExcel.setDeviceName(dto.getDeviceName());
|
||||
lineDataExcel.setIp(dto.getIp());
|
||||
lineDataExcel.setManufacturer(dto.getManufacturer());
|
||||
lineDataExcel.setOnlineRate(onLineRate(onlineRateByDev, Arrays.asList(dto.getDeviceId())));
|
||||
lineDataExcel.setIntegrity(integrity(integrityList, Arrays.asList(dto.getLineId())));
|
||||
if (num == 1) {
|
||||
lineDataExcel.setMonitorId(new BigDecimal(dto.getMonitorId()));
|
||||
}
|
||||
lineDataExcels1.add(lineDataExcel);
|
||||
}
|
||||
lineData.put(num, lineDataExcels1);
|
||||
}
|
||||
|
||||
|
||||
private CityDataExcel addCityDataExcel(GeneralDeviceDTO dto, List<LineInfoMonitorIdVO> monitorLine, List<RStatOnlineRateVO> onlineRateByDev, List<RStatIntegrityVO> integrityList) {
|
||||
CityDataExcel data = new CityDataExcel();
|
||||
List<String> monitorLineList = monitorLine.stream()
|
||||
.filter(x -> dto.getLineIndexes().contains(x.getLineId()))
|
||||
.map(LineInfoMonitorIdVO::getLineId)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
List<String> devList = monitorLine.stream()
|
||||
.filter(x -> dto.getDeviceIndexes().contains(x.getDeviceId()))
|
||||
.map(LineInfoMonitorIdVO::getDeviceId)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
data.setDeptName(dto.getName());
|
||||
data.setDeviceNum(BigDecimal.valueOf(devList.size()));
|
||||
data.setLineNum(BigDecimal.valueOf(monitorLineList.size()));
|
||||
data.setOnlineRate(onLineRate(onlineRateByDev, devList));
|
||||
data.setIntegrity(integrity(integrityList, monitorLineList));
|
||||
List<String> abnormalList = integrityList.stream()
|
||||
.filter(x -> ObjUtil.isNotNull(x.getIntegrityRate()))
|
||||
.filter(x -> x.getIntegrityRate().compareTo(BigDecimal.ZERO) == 0)
|
||||
.filter(x -> monitorLineList.contains(x.getLineIndex()))
|
||||
.map(RStatIntegrityVO::getLineIndex)
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
data.setAbnormalNum(BigDecimal.valueOf(abnormalList.size()));
|
||||
data.setAbnormalList(abnormalList);
|
||||
return data;
|
||||
}
|
||||
|
||||
private BigDecimal integrity(List<RStatIntegrityVO> integrityList, List<String> lineIds) {
|
||||
//监测完整率
|
||||
List<RStatIntegrityVO> integrityDS = integrityList.stream().filter(x -> lineIds.contains(x.getLineIndex())).collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(integrityDS)) {
|
||||
double realTime = integrityDS.stream().mapToDouble(RStatIntegrityVO::getRealTime).sum();
|
||||
double dueTime = integrityDS.stream().mapToDouble(RStatIntegrityVO::getDueTime).sum();
|
||||
if (dueTime == 0) {
|
||||
return new BigDecimal(0);
|
||||
}
|
||||
return NumberUtil.round(Math.min(realTime * 100.0 / dueTime, 100), 2);
|
||||
} else {
|
||||
return new BigDecimal(0);
|
||||
}
|
||||
}
|
||||
|
||||
private BigDecimal onLineRate(List<RStatOnlineRateVO> onlineRateByDev, List<String> devIds) {
|
||||
//终端在线率
|
||||
List<RStatOnlineRateVO> onlineRateDS = onlineRateByDev.stream().filter(x -> devIds.contains(x.getDevIndex())).collect(Collectors.toList());
|
||||
if (CollUtil.isNotEmpty(onlineRateDS)) {
|
||||
double onlineTime = onlineRateDS.stream().mapToDouble(RStatOnlineRateVO::getOnlineMin).sum();
|
||||
double offlineTime = onlineRateDS.stream().mapToDouble(RStatOnlineRateVO::getOfflineMin).sum();
|
||||
if ((onlineTime + offlineTime) == 0) {
|
||||
return new BigDecimal(0);
|
||||
}
|
||||
return NumberUtil.round(Math.min(onlineTime * 100.0 / (onlineTime + offlineTime), 100), 2);
|
||||
} else {
|
||||
return new BigDecimal(0);
|
||||
}
|
||||
}
|
||||
|
||||
private void excelExtracted(ExcelWriter excelWriter, String sheetName, Map<Integer, List<LineDataExcel>> lineData, Map<Integer, List<CityDataExcel>> cityData, Boolean fly) {
|
||||
// 构建sheet页--表示不加表头
|
||||
WriteSheet writeSheet = EasyExcel.writerSheet(sheetName).needHead(Boolean.FALSE).build();
|
||||
// 表头的数量
|
||||
int num = 0;
|
||||
Boolean first = false;
|
||||
// 模拟写5张表
|
||||
for (int i = 1; i < 4; i++) {
|
||||
String name = "";
|
||||
if (i == 1) {
|
||||
if (fly) {
|
||||
name = "一类监测点";
|
||||
} else {
|
||||
name = "冀北公司一类监测点数据质量问题";
|
||||
}
|
||||
first = true;
|
||||
}
|
||||
if (i == 2) {
|
||||
if (fly) {
|
||||
name = "电网侧";
|
||||
} else {
|
||||
name = "冀北公司电网侧监测点数据质量问题(不包含一类监测点)";
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
if (i == 3) {
|
||||
if (fly) {
|
||||
name = "非电网侧";
|
||||
} else {
|
||||
name = "冀北公司非电网侧问题监测点(不包含一类监测点)";
|
||||
}
|
||||
first = false;
|
||||
}
|
||||
String finalName = name;
|
||||
List<List<String>> contentHeader;
|
||||
if (fly) {
|
||||
contentHeader = DataLineExcelUtil.cityHeader();
|
||||
} else {
|
||||
contentHeader = DataLineExcelUtil.lineHeader(first);
|
||||
}
|
||||
List<List<String>> nameHeader = contentHeader.stream().map(item -> Collections.singletonList(finalName)).collect(Collectors.toList());
|
||||
// 这里必须指定需要头,table 会继承sheet的配置,sheet配置了不需要,table 默认也是不需要
|
||||
// 创建一个表头
|
||||
WriteTable writeTable1 = EasyExcel.writerTable(num).needHead(Boolean.TRUE).head(nameHeader).build();
|
||||
excelWriter.write(new ArrayList<>(), writeSheet, writeTable1);
|
||||
//创建数据
|
||||
WriteTable writeTable2 = EasyExcel.writerTable(num + 1).needHead(Boolean.TRUE).head(contentHeader).build();
|
||||
List<List<Object>> body;
|
||||
if (fly) {
|
||||
body = DataLineExcelUtil.cityBody(cityData.get(i));
|
||||
} else {
|
||||
body = DataLineExcelUtil.lineBody(lineData.get(i));
|
||||
}
|
||||
excelWriter.write(body, writeSheet, writeTable2);
|
||||
// 插入两次表头加2
|
||||
num = num + 2;
|
||||
}
|
||||
dataVerifyExcels.sort(Comparator
|
||||
.comparing(DataVerifyExcel::getAllTime, Comparator.reverseOrder())
|
||||
.thenComparing((DataVerifyExcel item) -> item.getCity() + "_" + item.getStationName()+"_"+item.getDevName())
|
||||
);
|
||||
exportExcelByDataSize(dataVerifyExcels,response.getOutputStream());
|
||||
// Set<String> excludeColumnFiledNames = new HashSet<>(1);
|
||||
// excludeColumnFiledNames.add("lineId");
|
||||
// EasyExcel.write(response.getOutputStream(), DataVerifyExcel.class)
|
||||
// .excludeColumnFiledNames(excludeColumnFiledNames).sheet("sheet")
|
||||
// .doWrite(dataVerifyExcels);
|
||||
}
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user