diff --git a/safety-eval-adapter/src/main/java/org/qinan/safetyeval/adapter/config/support/ControllerLogAspect.java b/safety-eval-adapter/src/main/java/org/qinan/safetyeval/adapter/config/support/ControllerLogAspect.java new file mode 100644 index 0000000..c18271d --- /dev/null +++ b/safety-eval-adapter/src/main/java/org/qinan/safetyeval/adapter/config/support/ControllerLogAspect.java @@ -0,0 +1,208 @@ +package org.qinan.safetyeval.adapter.config.support; + +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.aspectj.lang.ProceedingJoinPoint; +import org.aspectj.lang.annotation.Around; +import org.aspectj.lang.annotation.Aspect; +import org.aspectj.lang.annotation.Pointcut; +import org.aspectj.lang.reflect.MethodSignature; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.util.AntPathMatcher; +import org.springframework.web.context.request.RequestContextHolder; +import org.springframework.web.context.request.ServletRequestAttributes; + +import javax.servlet.ServletRequest; +import javax.servlet.ServletResponse; +import javax.servlet.http.HttpServletRequest; +import java.lang.reflect.Method; +import java.util.*; + +@Slf4j +@Aspect +@Component +public class ControllerLogAspect { + /** + * 定义切点:拦截所有Controller层的方法 + */ + @Pointcut("execution(* org.qinan.safetyeval.adapter..web..*.*(..))") + public void controllerPointcut() { + } + + private final AntPathMatcher pathMatcher = new AntPathMatcher(); + @Value("${anonymous.path:}") + private List anonymousUrl; + @Value("${def.logEnable:true}") + private boolean logEnable; + + /** + * 环绕通知 + */ + @Around("controllerPointcut()") + public Object around(ProceedingJoinPoint joinPoint) throws Throwable { + if (!logEnable) { + return joinPoint.proceed(); + } + long startTime = System.currentTimeMillis(); + + // 获取请求信息 + ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes(); + HttpServletRequest request = attributes != null ? attributes.getRequest() : null; + // 白名单放行 + if (request != null && isWhiteUrl(request.getRequestURI())) { + return joinPoint.proceed(); + } + // 获取方法信息 + MethodSignature signature = (MethodSignature) joinPoint.getSignature(); + Method method = signature.getMethod(); + String className = joinPoint.getTarget().getClass().getName(); + String methodName = method.getName(); + + // 记录请求开始日志 + StringBuilder sb = logRequest(request, className, methodName, joinPoint.getArgs()); + + Object result; + try { + // 执行目标方法 + result = joinPoint.proceed(); + long endTime = System.currentTimeMillis(); + + // 记录成功响应日志 + logResponse(sb, className, methodName, result, endTime - startTime, true); + + } catch (Throwable throwable) { + long endTime = System.currentTimeMillis(); + + // 记录异常日志 + logError(sb, className, methodName, throwable, endTime - startTime); + throw throwable; + } + + return result; + } + + /** + * 记录请求日志 + */ + private StringBuilder logRequest(HttpServletRequest request, String className, String methodName, Object[] args) { + StringBuilder sb = new StringBuilder(); + if (request != null) { + sb.append("\n========================================== Start ==========================================\n"); + sb.append("URL : ").append(request.getScheme()).append("://").append(request.getServerName()) + .append(":").append(request.getServerPort()).append(request.getRequestURI()).append("\n"); + sb.append("HTTP Method : ").append(request.getMethod()).append("\n"); + sb.append("Request IP : ").append(HttpUtil.getRemoteAddress(request)).append("\n"); + + // 获取请求头信息 + Map headers = getHeaders(request); + sb.append("Request Header : ").append(JSON.toJSONString(headers)).append("\n"); + + sb.append("Parameter Map : ").append(JSON.toJSONString(request.getParameterMap())).append("\n"); + sb.append("Class Method : ").append(className).append(".").append(methodName).append("\n"); + + // 格式化请求参数 + if (args != null && args.length > 0) { + sb.append("Request Args : ").append(formatRequestArgs(args)).append("\n"); + } else { + sb.append("Request Args : {}\n"); + } + } else { + sb.append("========================================== Start ==========================================\n"); + sb.append("Class Method : ").append(className).append(".").append(methodName).append("\n"); + if (args != null && args.length > 0) { + sb.append("Request Args : ").append(formatRequestArgs(args)).append("\n"); + } else { + sb.append("Request Args : {}\n"); + } + } + return sb; + } + + /** + * 记录响应日志 + */ + private void logResponse(StringBuilder sb, String className, String methodName, Object result, long executionTime, boolean success) { + sb.append("Response Args : ").append(result != null ? JSON.toJSONString(result) : "{}").append("\n"); + sb.append("Time Cost : ").append(executionTime).append(" ms\n"); + sb.append("=========================================== End ===========================================\n"); + log.info(sb.toString()); + } + + /** + * 记录异常日志 + */ + private void logError(StringBuilder sb, String className, String methodName, Throwable throwable, long executionTime) { + sb.append("Response Args : {\"error\": \"").append(throwable.getMessage()).append("\"}\n"); + sb.append("Time Cost : ").append(executionTime).append(" ms\n"); + sb.append("=========================================== End ===========================================\n"); + log.error(sb.toString()); + } + + /** + * 获取请求头信息 + */ + private Map getHeaders(HttpServletRequest request) { + Map headers = new HashMap<>(); + Enumeration headerNames = request.getHeaderNames(); + while (headerNames.hasMoreElements()) { + String headerName = headerNames.nextElement(); + headers.put(headerName, request.getHeader(headerName)); + } + return headers; + } + + /** + * 格式化请求参数 + */ + private String formatRequestArgs(Object[] args) { + if (args == null || args.length == 0) { + return "{}"; + } + List safeArgs = new ArrayList<>(args.length); + for (Object arg : args) { + safeArgs.add(formatArg(arg)); + } + if (safeArgs.size() == 1) { + return JSON.toJSONString(safeArgs.get(0)); + } + return JSON.toJSONString(safeArgs); + } + + private Object formatArg(Object arg) { + if (arg == null) { + return null; + } +// if (arg instanceof MultipartFile file) { +// Map meta = new LinkedHashMap<>(); +// meta.put("type", "MultipartFile"); +// meta.put("originalFilename", StringUtils.defaultString(file.getOriginalFilename())); +// meta.put("size", file.getSize()); +// meta.put("contentType", StringUtils.defaultString(file.getContentType())); +// return meta; +// } + if (arg instanceof ServletRequest || arg instanceof ServletResponse) { + return arg.getClass().getSimpleName(); + } + return arg; + } + + /** + * 放行白名单 + * @param url + * @return + */ + private boolean isWhiteUrl(String url) { + if (StringUtils.isBlank(url)) { + return false; + } + for (String s : anonymousUrl) { + if (pathMatcher.match(s, url)) { + return true; + } + } + return false; + } + +} \ No newline at end of file diff --git a/safety-eval-adapter/src/main/java/org/qinan/safetyeval/adapter/config/support/HttpUtil.java b/safety-eval-adapter/src/main/java/org/qinan/safetyeval/adapter/config/support/HttpUtil.java new file mode 100644 index 0000000..41fc8c5 --- /dev/null +++ b/safety-eval-adapter/src/main/java/org/qinan/safetyeval/adapter/config/support/HttpUtil.java @@ -0,0 +1,102 @@ +package org.qinan.safetyeval.adapter.config.support; + +import org.apache.commons.lang3.StringUtils; + +import javax.servlet.http.HttpServletRequest; + +public class HttpUtil { + + /** + * 获取客户端IP + * + * @param request 请求对象 + * @return IP地址 + */ + public static String getRemoteAddress(HttpServletRequest request) { + return getClientIp(request); + } + + /** + * 获取客户端IP地址 + */ + public static String getClientIp(HttpServletRequest request) { + String ip = request.getHeader("X-Forwarded-For"); + if (ip != null && ip.length() != 0 && !"unknown".equalsIgnoreCase(ip)) { + // 多次反向代理后会有多个ip值,第一个ip才是真实ip + if (ip.contains(",")) { + ip = ip.split(",")[0]; + } + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("WL-Proxy-Client-IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_CLIENT_IP"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getHeader("HTTP_X_FORWARDED_FOR"); + } + if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) { + ip = request.getRemoteAddr(); + } + return ip; + } + + /** + * 从多级反向代理中获得第一个非unknown IP地址 + * + * @param ip 获得的IP地址 + * @return 第一个非unknown IP地址 + */ + public static String getMultistageReverseProxyIp(String ip) { + // 多级反向代理检测 + if (ip != null && ip.indexOf(",") > 0) { + final String[] ips = ip.trim().split(","); + for (String subIp : ips) { + if (!isUnknown(subIp)) { + ip = subIp; + break; + } + } + } + return ip; + } + + /** + * 检测给定字符串是否为未知,多用于检测HTTP请求相关 + * + * @param checkString 被检测的字符串 + * @return 是否未知 + */ + public static boolean isUnknown(String checkString) { + return StringUtils.isBlank(checkString) || "unknown".equalsIgnoreCase(checkString); + } + + /** + * 通过请求对象,获取访问路径 + * + * @param request + * @param includeContextPath + * @return + */ + private static String getRequestURI(HttpServletRequest request, boolean includeContextPath) { + String requestURI = request.getRequestURI(); + if (includeContextPath) { + return requestURI; + } else { + String contextPath = request.getContextPath(); + return requestURI.replace(contextPath, ""); + } + } + + public static String getRequestURI(HttpServletRequest request) { + return getRequestURI(request, false); + } + + public static String getFullRequestURI(HttpServletRequest request) { + return getRequestURI(request, true); + } +} diff --git a/safety-eval-adapter/src/main/java/org/qinan/safetyeval/adapter/web/QualFilingController.java b/safety-eval-adapter/src/main/java/org/qinan/safetyeval/adapter/web/QualFilingController.java index 9ff3ede..86793b2 100644 --- a/safety-eval-adapter/src/main/java/org/qinan/safetyeval/adapter/web/QualFilingController.java +++ b/safety-eval-adapter/src/main/java/org/qinan/safetyeval/adapter/web/QualFilingController.java @@ -7,6 +7,7 @@ import org.qinan.safetyeval.client.api.QualFilingApi; import org.qinan.safetyeval.client.co.QualFilingCO; import org.qinan.safetyeval.client.co.QualFilingComplianceCheckResultCO; import org.qinan.safetyeval.client.co.QualFilingDetailCO; +import org.qinan.safetyeval.client.co.QualFilingStatisticsCO; import org.qinan.safetyeval.client.dto.*; import org.qinan.safetyeval.infrastructure.dataobject.base.Req; import org.springframework.validation.annotation.Validated; @@ -120,4 +121,10 @@ public class QualFilingController { return qualFilingApi.qualificationMonitoringPage(query); } + @ApiOperation("资质监控页面统计") + @GetMapping("/qualificationMonitoringPageStatistics") + public SingleResponse qualificationMonitoringPageStatistics() { + return qualFilingApi.qualificationMonitoringPageStatistics(); + } + } diff --git a/safety-eval-app/src/main/java/org/qinan/safetyeval/app/executor/QualFilingExecutor.java b/safety-eval-app/src/main/java/org/qinan/safetyeval/app/executor/QualFilingExecutor.java index fe82bf9..9faae67 100644 --- a/safety-eval-app/src/main/java/org/qinan/safetyeval/app/executor/QualFilingExecutor.java +++ b/safety-eval-app/src/main/java/org/qinan/safetyeval/app/executor/QualFilingExecutor.java @@ -11,6 +11,7 @@ import org.qinan.safetyeval.client.change.request.QualFilingPersonnelChangeAddCm import org.qinan.safetyeval.client.co.QualFilingCO; import org.qinan.safetyeval.client.co.QualFilingComplianceCheckResultCO; import org.qinan.safetyeval.client.co.QualFilingDetailCO; +import org.qinan.safetyeval.client.co.QualFilingStatisticsCO; import org.qinan.safetyeval.client.dto.*; import org.qinan.safetyeval.domain.constant.BooleanQAEnum; import org.qinan.safetyeval.domain.constant.QualFilingApplyTypeEnum; @@ -273,6 +274,12 @@ public class QualFilingExecutor implements QualFilingApi { return PageResponse.of(pageResult.getRecords(), pageResult.getTotal()); } + @Override + public SingleResponse qualificationMonitoringPageStatistics() { + QualFilingStatisticsCO co = qualFilingOrchestrator.qualificationMonitoringPageStatistics(); + return SingleResponse.success(co); + } + private QualFilingCO toLegacyCO(QualFilingEntity entity) { if (entity == null) { return null; diff --git a/safety-eval-app/src/main/java/org/qinan/safetyeval/app/orchestrator/QualFilingOrchestrator.java b/safety-eval-app/src/main/java/org/qinan/safetyeval/app/orchestrator/QualFilingOrchestrator.java index 42ddb99..9e93c4e 100644 --- a/safety-eval-app/src/main/java/org/qinan/safetyeval/app/orchestrator/QualFilingOrchestrator.java +++ b/safety-eval-app/src/main/java/org/qinan/safetyeval/app/orchestrator/QualFilingOrchestrator.java @@ -913,4 +913,8 @@ public class QualFilingOrchestrator { List co = qualFilingMapper.qualificationMonitoringPage(page, query); return PageResult.of(co, page.getTotal(), page.getCurrent(), page.getSize()); } + + public QualFilingStatisticsCO qualificationMonitoringPageStatistics() { + return qualFilingMapper.qualificationMonitoringPageStatistics(); + } } diff --git a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/api/QualFilingApi.java b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/api/QualFilingApi.java index 29f3994..43f719c 100644 --- a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/api/QualFilingApi.java +++ b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/api/QualFilingApi.java @@ -3,6 +3,7 @@ package org.qinan.safetyeval.client.api; import org.qinan.safetyeval.client.co.QualFilingCO; import org.qinan.safetyeval.client.co.QualFilingComplianceCheckResultCO; import org.qinan.safetyeval.client.co.QualFilingDetailCO; +import org.qinan.safetyeval.client.co.QualFilingStatisticsCO; import org.qinan.safetyeval.client.dto.*; import java.util.List; @@ -45,4 +46,6 @@ public interface QualFilingApi { void review(QualFilingReviewAddCmd cmd); PageResponse qualificationMonitoringPage(QualificationMonitoringQuery query); + + SingleResponse qualificationMonitoringPageStatistics(); } diff --git a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/co/QualFilingCO.java b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/co/QualFilingCO.java index 3c85c2e..0790ad2 100644 --- a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/co/QualFilingCO.java +++ b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/co/QualFilingCO.java @@ -3,7 +3,6 @@ package org.qinan.safetyeval.client.co; import com.fasterxml.jackson.annotation.JsonFormat; import io.swagger.annotations.ApiModelProperty; import lombok.Data; -import org.qinan.safetyeval.domain.constant.IndustryEnum; import org.springframework.format.annotation.DateTimeFormat; import java.math.BigDecimal; @@ -135,4 +134,7 @@ public class QualFilingCO { @ApiModelProperty(value = "单位名称") private String unitName; + + @ApiModelProperty(value = "统一社会信用代码: 机构信息") + private String orgInfoCreditCode; } diff --git a/safety-eval-client/src/main/java/org/qinan/safetyeval/client/co/QualFilingStatisticsCO.java b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/co/QualFilingStatisticsCO.java new file mode 100644 index 0000000..910a1c4 --- /dev/null +++ b/safety-eval-client/src/main/java/org/qinan/safetyeval/client/co/QualFilingStatisticsCO.java @@ -0,0 +1,22 @@ +package org.qinan.safetyeval.client.co; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +/** + * 资质备案申请CO(Client Object) + * + * @author safety-eval + */ +@Data +public class QualFilingStatisticsCO { + + @ApiModelProperty(value = "正常统计") + private Integer normalSum; + + @ApiModelProperty(value = "预警统计") + private Integer earlyWarningSum; + + @ApiModelProperty(value = "机构总数统计") + private Integer orgInfoSum; +} diff --git a/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/mapper/QualFilingMapper.java b/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/mapper/QualFilingMapper.java index 74103d0..5880a39 100644 --- a/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/mapper/QualFilingMapper.java +++ b/safety-eval-infrastructure/src/main/java/org/qinan/safetyeval/infrastructure/mapper/QualFilingMapper.java @@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Param; import org.qinan.safetyeval.client.co.QualFilingCO; +import org.qinan.safetyeval.client.co.QualFilingStatisticsCO; import org.qinan.safetyeval.client.dto.QualificationMonitoringQuery; import org.qinan.safetyeval.infrastructure.dataobject.QualFilingDO; @@ -13,4 +14,6 @@ import java.util.List; @Mapper public interface QualFilingMapper extends BaseMapper { List qualificationMonitoringPage(@Param("page") Page page, @Param("query") QualificationMonitoringQuery query); + + QualFilingStatisticsCO qualificationMonitoringPageStatistics(); } diff --git a/safety-eval-infrastructure/src/main/resources/mapper/QualFilingMapper.xml b/safety-eval-infrastructure/src/main/resources/mapper/QualFilingMapper.xml index 25813d0..a286b0a 100644 --- a/safety-eval-infrastructure/src/main/resources/mapper/QualFilingMapper.xml +++ b/safety-eval-infrastructure/src/main/resources/mapper/QualFilingMapper.xml @@ -60,9 +60,9 @@ +