Commit 949dee05 authored by hejincai's avatar hejincai

feat: 完善时间校验的规则

parent 5abdcc8c
...@@ -3,9 +3,12 @@ package com.afanticar.afantiopenapi.config; ...@@ -3,9 +3,12 @@ package com.afanticar.afantiopenapi.config;
import com.afanticar.afantiopenapi.constant.ExceptionEnum; import com.afanticar.afantiopenapi.constant.ExceptionEnum;
import com.afanticar.afantiopenapi.controller.BaseController; import com.afanticar.afantiopenapi.controller.BaseController;
import com.afanticar.afantiopenapi.model.BaseResponse; import com.afanticar.afantiopenapi.model.BaseResponse;
import com.afanticar.common.core.api.ResultCodeEnum;
import com.afanticar.common.core.exception.ApiException;
import org.apache.commons.lang3.exception.ExceptionUtils; import org.apache.commons.lang3.exception.ExceptionUtils;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
import org.springframework.http.converter.HttpMessageNotReadableException;
import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice; import org.springframework.web.bind.annotation.RestControllerAdvice;
...@@ -32,6 +35,18 @@ public class GlobalExceptionHandler { ...@@ -32,6 +35,18 @@ public class GlobalExceptionHandler {
return handleError(ExceptionEnum.ERROR, e); return handleError(ExceptionEnum.ERROR, e);
} }
@ExceptionHandler(HttpMessageNotReadableException.class)
public BaseResponse processHttpMessageNotReadableException(HttpMessageNotReadableException e) {
LOGGER.error("HttpMessageNotReadableException 调用异常 => :{}", ExceptionUtils.getStackTrace(e));
return BaseController.error(ResultCodeEnum.VALIDATE_FAILED.getCode().toString(), e.getMessage());
}
@ExceptionHandler(ApiException.class)
public BaseResponse processApiException(ApiException e) {
LOGGER.error("api 调用异常 => :{}", ExceptionUtils.getStackTrace(e));
return BaseController.error(e.getCode().toString(), e.getMessage());
}
private BaseResponse<Object> handleError(int code, String message, Exception ex) { private BaseResponse<Object> handleError(int code, String message, Exception ex) {
return BaseController.error(code + "", message); return BaseController.error(code + "", message);
} }
......
...@@ -6,8 +6,8 @@ import io.swagger.annotations.ApiModel; ...@@ -6,8 +6,8 @@ import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotEmpty;
import java.time.LocalDate;
import java.util.List; import java.util.List;
/** /**
...@@ -20,12 +20,10 @@ import java.util.List; ...@@ -20,12 +20,10 @@ import java.util.List;
public class DataReportRequestDTO { public class DataReportRequestDTO {
@ApiModelProperty("开始时间。格式为:yyyy-MM-dd。例如2024-01-01") @ApiModelProperty("开始时间。格式为:yyyy-MM-dd。例如2024-01-01")
@NotBlank(message = "开始时间不可为空") private LocalDate startTime;
private String startTime;
@ApiModelProperty("结束时间。格式为:yyyy-MM-dd。例如2024-01-01") @ApiModelProperty("结束时间。格式为:yyyy-MM-dd。例如2024-01-01")
@NotBlank(message = "结束时间不可为空") private LocalDate endTime;
private String endTime;
@ApiModelProperty("平台类型 public-公共数据 douyin-抖音 kuaishou-快手xiaohongshu-小红书 shipinhao-视频号") @ApiModelProperty("平台类型 public-公共数据 douyin-抖音 kuaishou-快手xiaohongshu-小红书 shipinhao-视频号")
@NotEmpty(message = "平台类型不可为空,可选类型 public-公共数据 douyin-抖音 kuaishou-快手xiaohongshu-小红书 shipinhao-视频号 ") @NotEmpty(message = "平台类型不可为空,可选类型 public-公共数据 douyin-抖音 kuaishou-快手xiaohongshu-小红书 shipinhao-视频号 ")
......
...@@ -11,12 +11,15 @@ import com.afanticar.afantiopenapi.model.dto.PageInfoDTO; ...@@ -11,12 +11,15 @@ import com.afanticar.afantiopenapi.model.dto.PageInfoDTO;
import com.afanticar.afantiopenapi.model.dto.ReportDataInfoDTO; import com.afanticar.afantiopenapi.model.dto.ReportDataInfoDTO;
import com.afanticar.afantiopenapi.model.entity.DwsAfantiAdbDataOssRecordEntity; import com.afanticar.afantiopenapi.model.entity.DwsAfantiAdbDataOssRecordEntity;
import com.afanticar.afantiopenapi.model.entity.OpenApiClientPrincipalRelationEntity; import com.afanticar.afantiopenapi.model.entity.OpenApiClientPrincipalRelationEntity;
import com.afanticar.common.core.api.ResultCodeEnum;
import com.afanticar.common.core.exception.ApiException; import com.afanticar.common.core.exception.ApiException;
import com.aliyun.oss.OSS; import com.aliyun.oss.OSS;
import com.aliyun.oss.OSSClientBuilder; import com.aliyun.oss.OSSClientBuilder;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import lombok.SneakyThrows; import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value; import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
...@@ -26,8 +29,10 @@ import java.net.URI; ...@@ -26,8 +29,10 @@ import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL; import java.net.URL;
import java.time.Instant; import java.time.Instant;
import java.time.LocalDate;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; import java.util.regex.Pattern;
...@@ -36,104 +41,134 @@ import java.util.regex.Pattern; ...@@ -36,104 +41,134 @@ import java.util.regex.Pattern;
* @since 2025/4/24 9:41 * @since 2025/4/24 9:41
**/ **/
@Service @Service
@Slf4j
public class DataReportService { public class DataReportService {
@Resource @Resource
private OpenApiClientPrincipalRelationMapper openApiClientPrincipalRelationMapper; private OpenApiClientPrincipalRelationMapper openApiClientPrincipalRelationMapper;
@Resource @Resource
private DwsAfantiAdbDataOssRecordMapper dwsAfantiAdbDataOssRecordMapper; private DwsAfantiAdbDataOssRecordMapper dwsAfantiAdbDataOssRecordMapper;
@Value("${spring.rocket-mq.accessKey}") @Resource
private String ossAccessKey; private ObjectMapper objectMapper;
@Value("${spring.rocket-mq.secretKey}") @Value("${spring.rocket-mq.accessKey}")
private String ossAccessSecret; private String ossAccessKey;
private OSS oss; @Value("${spring.rocket-mq.secretKey}")
private String ossAccessSecret;
@PostConstruct
public void init() { private OSS oss;
String ossEndpoint = "oss-cn-hangzhou.aliyuncs.com";
oss = new OSSClientBuilder().build(ossEndpoint, ossAccessKey, ossAccessSecret); @PostConstruct
} public void init() {
String ossEndpoint = "oss-cn-hangzhou.aliyuncs.com";
public CommonPageInfoDTO<ReportDataInfoDTO> report(String clientId, DataReportRequestDTO requestBody) { oss = new OSSClientBuilder().build(ossEndpoint, ossAccessKey, ossAccessSecret);
List<OpenApiClientPrincipalRelationEntity> openApiClientPrincipalEntityList = openApiClientPrincipalRelationMapper.selectByClientId(clientId); }
if (CollUtil.isEmpty(openApiClientPrincipalEntityList)) {
throw new ApiException("客户端未配置品牌,请联系管理员"); @SneakyThrows
} public CommonPageInfoDTO<ReportDataInfoDTO> report(String clientId, DataReportRequestDTO requestBody) {
OpenApiClientPrincipalRelationEntity openApiClientPrincipalRelationEntity = openApiClientPrincipalEntityList.get(0); log.info("请求数据报表入参,client_id:{}, 请求参数:{}", clientId, objectMapper.writeValueAsString(requestBody));
String principalId = openApiClientPrincipalRelationEntity.getPrincipalId(); handleTimeRange(requestBody);
requestBody.setPrincipalId(principalId); List<OpenApiClientPrincipalRelationEntity> openApiClientPrincipalEntityList = openApiClientPrincipalRelationMapper.selectByClientId(clientId);
Page<DwsAfantiAdbDataOssRecordEntity> page = dwsAfantiAdbDataOssRecordMapper.page(new Page<>(requestBody.getPage(), requestBody.getSize()), requestBody); if (CollUtil.isEmpty(openApiClientPrincipalEntityList)) {
return convertToCommonPageInfo(page); throw new ApiException("客户端未配置品牌,请联系管理员");
} }
OpenApiClientPrincipalRelationEntity openApiClientPrincipalRelationEntity = openApiClientPrincipalEntityList.get(0);
String principalId = openApiClientPrincipalRelationEntity.getPrincipalId();
@SneakyThrows requestBody.setPrincipalId(principalId);
private String generatePresignedUrl(String ossUrl) { Page<DwsAfantiAdbDataOssRecordEntity> page = dwsAfantiAdbDataOssRecordMapper.page(new Page<>(requestBody.getPage(), requestBody.getSize()), requestBody);
// 生成一个带过期时间的url return convertToCommonPageInfo(page);
String[] ossObjInfo = parseOSSUrl(ossUrl); }
Instant expireInstant = Instant.now().plusSeconds(86400);
URL url = oss.generatePresignedUrl(ossObjInfo[0], ossObjInfo[1], Date.from(expireInstant)); private void handleTimeRange(DataReportRequestDTO requestBody) {
return url.toString(); LocalDate yesterday = LocalDate.now().minusDays(1);
} if (Objects.nonNull(requestBody.getStartTime())) {
LocalDate sevenDaysAgo = LocalDate.now().minusDays(7);
public static String[] parseOSSUrl(String ossUrl) throws URISyntaxException { if (sevenDaysAgo.isAfter(requestBody.getStartTime())) {
// 处理URL编码的特殊字符(如%2F) throw new ApiException(ResultCodeEnum.VALIDATE_FAILED.getCode(), "开始时间不能早于前7天");
// 使用正则表达式匹配bucket和object }
Pattern pattern = Pattern.compile("https?://([^.]+)\\.oss(-[^.]+)?\\.([^/]+)/(.+)"); }
Matcher matcher = pattern.matcher(ossUrl); if (Objects.isNull(requestBody.getStartTime()) && Objects.isNull(requestBody.getEndTime())) {
requestBody.setStartTime(yesterday);
if (matcher.find()) { requestBody.setEndTime(yesterday);
String bucket = matcher.group(1); } else if (Objects.isNull(requestBody.getStartTime()) && Objects.nonNull(requestBody.getEndTime())) {
// 从匹配结果中提取object,并去掉查询参数 requestBody.setStartTime(requestBody.getEndTime());
String fullObjectPath = matcher.group(4); } else if (Objects.isNull(requestBody.getEndTime()) && Objects.nonNull(requestBody.getStartTime())) {
String object = fullObjectPath.split("\\?")[0]; requestBody.setEndTime(requestBody.getStartTime());
return new String[]{bucket, object}; } else {
}
if (requestBody.getEndTime().isBefore(requestBody.getStartTime())) {
// 如果正则匹配失败,尝试URI解析 throw new ApiException(ResultCodeEnum.VALIDATE_FAILED.getCode(), "开始时间不能晚于结束时间");
URI uri = new URI(ossUrl); }
String host = uri.getHost(); }
}
if (host != null && host.endsWith(".aliyuncs.com")) {
String bucket = host.split("\\.")[0];
String object = uri.getPath().substring(1); // 去掉开头的/ @SneakyThrows
return new String[]{bucket, object}; private String generatePresignedUrl(String ossUrl) {
} // 生成一个带过期时间的url
String[] ossObjInfo = parseOSSUrl(ossUrl);
throw new IllegalArgumentException("无法从URL中解析出OSS bucket和object: " + ossUrl); Instant expireInstant = Instant.now().plusSeconds(86400);
} URL url = oss.generatePresignedUrl(ossObjInfo[0], ossObjInfo[1], Date.from(expireInstant));
return url.toString();
}
private CommonPageInfoDTO<ReportDataInfoDTO> convertToCommonPageInfo(Page<DwsAfantiAdbDataOssRecordEntity> page) {
public static String[] parseOSSUrl(String ossUrl) throws URISyntaxException {
CommonPageInfoDTO<ReportDataInfoDTO> commonPageInfo = new CommonPageInfoDTO<>(); // 处理URL编码的特殊字符(如%2F)
PageInfoDTO pageInfoDTO = new PageInfoDTO(); // 使用正则表达式匹配bucket和object
pageInfoDTO.setPage((int) page.getCurrent()); Pattern pattern = Pattern.compile("https?://([^.]+)\\.oss(-[^.]+)?\\.([^/]+)/(.+)");
pageInfoDTO.setSize((int) page.getSize()); Matcher matcher = pattern.matcher(ossUrl);
pageInfoDTO.setTotal((int) page.getTotal());
pageInfoDTO.setTotalPage((int) page.getPages()); if (matcher.find()) {
commonPageInfo.setPageInfo(pageInfoDTO); String bucket = matcher.group(1);
List<DwsAfantiAdbDataOssRecordEntity> entityList = page.getRecords(); // 从匹配结果中提取object,并去掉查询参数
List<ReportDataInfoDTO> list = Lists.newArrayList(); String fullObjectPath = matcher.group(4);
for (DwsAfantiAdbDataOssRecordEntity entity : entityList) { String object = fullObjectPath.split("\\?")[0];
ReportDataInfoDTO reportDataInfo = new ReportDataInfoDTO(); return new String[]{bucket, object};
reportDataInfo.setStatisticsDay(entity.getStatisticsDay().toLocalDate()); }
reportDataInfo.setPlatform(entity.getPlatform());
reportDataInfo.setDataType(entity.getDataType()); // 如果正则匹配失败,尝试URI解析
reportDataInfo.setFileUrl(generatePresignedUrl(entity.getOssUrl())); URI uri = new URI(ossUrl);
reportDataInfo.setCtime(entity.getCtime()); String host = uri.getHost();
reportDataInfo.setMtime(entity.getMtime());
reportDataInfo.setId(StrUtil.format("{}:{}:{}:{}", entity.getPrincipalId(), if (host != null && host.endsWith(".aliyuncs.com")) {
DateUtil.format(entity.getStatisticsDay(), "yyyy-MM-dd"), String bucket = host.split("\\.")[0];
entity.getPlatform(), entity.getDataType())); String object = uri.getPath().substring(1); // 去掉开头的/
list.add(reportDataInfo); return new String[]{bucket, object};
} }
commonPageInfo.setRows(list);
return commonPageInfo; throw new IllegalArgumentException("无法从URL中解析出OSS bucket和object: " + ossUrl);
}
}
private CommonPageInfoDTO<ReportDataInfoDTO> convertToCommonPageInfo(Page<DwsAfantiAdbDataOssRecordEntity> page) {
CommonPageInfoDTO<ReportDataInfoDTO> commonPageInfo = new CommonPageInfoDTO<>();
PageInfoDTO pageInfoDTO = new PageInfoDTO();
pageInfoDTO.setPage((int) page.getCurrent());
pageInfoDTO.setSize((int) page.getSize());
pageInfoDTO.setTotal((int) page.getTotal());
pageInfoDTO.setTotalPage((int) page.getPages());
commonPageInfo.setPageInfo(pageInfoDTO);
List<DwsAfantiAdbDataOssRecordEntity> entityList = page.getRecords();
List<ReportDataInfoDTO> list = Lists.newArrayList();
for (DwsAfantiAdbDataOssRecordEntity entity : entityList) {
ReportDataInfoDTO reportDataInfo = new ReportDataInfoDTO();
reportDataInfo.setStatisticsDay(entity.getStatisticsDay().toLocalDate());
reportDataInfo.setPlatform(entity.getPlatform());
reportDataInfo.setDataType(entity.getDataType());
reportDataInfo.setFileUrl(generatePresignedUrl(entity.getOssUrl()));
reportDataInfo.setCtime(entity.getCtime());
reportDataInfo.setMtime(entity.getMtime());
reportDataInfo.setId(StrUtil.format("{}:{}:{}:{}", entity.getPrincipalId(),
DateUtil.format(entity.getStatisticsDay(), "yyyy-MM-dd"),
entity.getPlatform(), entity.getDataType()));
list.add(reportDataInfo);
}
commonPageInfo.setRows(list);
return commonPageInfo;
}
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment