feat: 功能开发
parent
fbd8561b79
commit
eb6a8f5cef
|
|
@ -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<String> 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<String, String> 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<String, String> getHeaders(HttpServletRequest request) {
|
||||
Map<String, String> headers = new HashMap<>();
|
||||
Enumeration<String> 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<Object> 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<String, Object> 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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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<QualFilingStatisticsCO> qualificationMonitoringPageStatistics() {
|
||||
return qualFilingApi.qualificationMonitoringPageStatistics();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<QualFilingStatisticsCO> qualificationMonitoringPageStatistics() {
|
||||
QualFilingStatisticsCO co = qualFilingOrchestrator.qualificationMonitoringPageStatistics();
|
||||
return SingleResponse.success(co);
|
||||
}
|
||||
|
||||
private QualFilingCO toLegacyCO(QualFilingEntity entity) {
|
||||
if (entity == null) {
|
||||
return null;
|
||||
|
|
|
|||
|
|
@ -913,4 +913,8 @@ public class QualFilingOrchestrator {
|
|||
List<QualFilingCO> co = qualFilingMapper.qualificationMonitoringPage(page, query);
|
||||
return PageResult.of(co, page.getTotal(), page.getCurrent(), page.getSize());
|
||||
}
|
||||
|
||||
public QualFilingStatisticsCO qualificationMonitoringPageStatistics() {
|
||||
return qualFilingMapper.qualificationMonitoringPageStatistics();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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<QualFilingCO> qualificationMonitoringPage(QualificationMonitoringQuery query);
|
||||
|
||||
SingleResponse<QualFilingStatisticsCO> qualificationMonitoringPageStatistics();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
|
@ -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<QualFilingDO> {
|
||||
List<QualFilingCO> qualificationMonitoringPage(@Param("page") Page<QualFilingCO> page, @Param("query") QualificationMonitoringQuery query);
|
||||
|
||||
QualFilingStatisticsCO qualificationMonitoringPageStatistics();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -60,9 +60,9 @@
|
|||
</resultMap>
|
||||
|
||||
<select id="qualificationMonitoringPage" resultMap="QualFilingCOResultMap">
|
||||
select info.credit_code as creditCode, info.unit_name as unitName, filing.* from org_info as info
|
||||
select info.credit_code as orgInfoCreditCode, info.unit_name as unitName, filing.* from org_info as info
|
||||
left join qual_filing as filing on info.id = filing.org_id
|
||||
where 1=1
|
||||
where info.delete_enum = 'false'
|
||||
<if test="query.keyword != null">
|
||||
and (info.credit_code like concat('%',#{query.keyword},'%') or info.unit_name like concat('%',#{query.keyword},'%'))
|
||||
</if>
|
||||
|
|
@ -74,5 +74,14 @@
|
|||
</if>
|
||||
order by filing.id desc
|
||||
</select>
|
||||
<select id="qualificationMonitoringPageStatistics"
|
||||
resultType="org.qinan.safetyeval.client.co.QualFilingStatisticsCO">
|
||||
select count(*) as orgInfoSum,
|
||||
SUM(CASE WHEN alert_status = 'normal' THEN 1 ELSE 0 END) AS normalSum,
|
||||
SUM(CASE WHEN alert_status = 'person_rate' THEN 1 ELSE 0 END) AS earlyWarningSum
|
||||
from org_info as info
|
||||
left join qual_filing as filing on info.id = filing.org_id
|
||||
where info.delete_enum = 'false';
|
||||
</select>
|
||||
|
||||
</mapper>
|
||||
|
|
|
|||
Loading…
Reference in New Issue