feat: 功能开发

dev_1.0.1
zhanglei 2026-07-16 13:10:31 +08:00
parent fbd8561b79
commit eb6a8f5cef
10 changed files with 370 additions and 3 deletions

View File

@ -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;
}
}

View File

@ -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);
}
}

View File

@ -7,6 +7,7 @@ import org.qinan.safetyeval.client.api.QualFilingApi;
import org.qinan.safetyeval.client.co.QualFilingCO; import org.qinan.safetyeval.client.co.QualFilingCO;
import org.qinan.safetyeval.client.co.QualFilingComplianceCheckResultCO; import org.qinan.safetyeval.client.co.QualFilingComplianceCheckResultCO;
import org.qinan.safetyeval.client.co.QualFilingDetailCO; import org.qinan.safetyeval.client.co.QualFilingDetailCO;
import org.qinan.safetyeval.client.co.QualFilingStatisticsCO;
import org.qinan.safetyeval.client.dto.*; import org.qinan.safetyeval.client.dto.*;
import org.qinan.safetyeval.infrastructure.dataobject.base.Req; import org.qinan.safetyeval.infrastructure.dataobject.base.Req;
import org.springframework.validation.annotation.Validated; import org.springframework.validation.annotation.Validated;
@ -120,4 +121,10 @@ public class QualFilingController {
return qualFilingApi.qualificationMonitoringPage(query); return qualFilingApi.qualificationMonitoringPage(query);
} }
@ApiOperation("资质监控页面统计")
@GetMapping("/qualificationMonitoringPageStatistics")
public SingleResponse<QualFilingStatisticsCO> qualificationMonitoringPageStatistics() {
return qualFilingApi.qualificationMonitoringPageStatistics();
}
} }

View File

@ -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.QualFilingCO;
import org.qinan.safetyeval.client.co.QualFilingComplianceCheckResultCO; import org.qinan.safetyeval.client.co.QualFilingComplianceCheckResultCO;
import org.qinan.safetyeval.client.co.QualFilingDetailCO; import org.qinan.safetyeval.client.co.QualFilingDetailCO;
import org.qinan.safetyeval.client.co.QualFilingStatisticsCO;
import org.qinan.safetyeval.client.dto.*; import org.qinan.safetyeval.client.dto.*;
import org.qinan.safetyeval.domain.constant.BooleanQAEnum; import org.qinan.safetyeval.domain.constant.BooleanQAEnum;
import org.qinan.safetyeval.domain.constant.QualFilingApplyTypeEnum; import org.qinan.safetyeval.domain.constant.QualFilingApplyTypeEnum;
@ -273,6 +274,12 @@ public class QualFilingExecutor implements QualFilingApi {
return PageResponse.of(pageResult.getRecords(), pageResult.getTotal()); 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) { private QualFilingCO toLegacyCO(QualFilingEntity entity) {
if (entity == null) { if (entity == null) {
return null; return null;

View File

@ -913,4 +913,8 @@ public class QualFilingOrchestrator {
List<QualFilingCO> co = qualFilingMapper.qualificationMonitoringPage(page, query); List<QualFilingCO> co = qualFilingMapper.qualificationMonitoringPage(page, query);
return PageResult.of(co, page.getTotal(), page.getCurrent(), page.getSize()); return PageResult.of(co, page.getTotal(), page.getCurrent(), page.getSize());
} }
public QualFilingStatisticsCO qualificationMonitoringPageStatistics() {
return qualFilingMapper.qualificationMonitoringPageStatistics();
}
} }

View File

@ -3,6 +3,7 @@ package org.qinan.safetyeval.client.api;
import org.qinan.safetyeval.client.co.QualFilingCO; import org.qinan.safetyeval.client.co.QualFilingCO;
import org.qinan.safetyeval.client.co.QualFilingComplianceCheckResultCO; import org.qinan.safetyeval.client.co.QualFilingComplianceCheckResultCO;
import org.qinan.safetyeval.client.co.QualFilingDetailCO; import org.qinan.safetyeval.client.co.QualFilingDetailCO;
import org.qinan.safetyeval.client.co.QualFilingStatisticsCO;
import org.qinan.safetyeval.client.dto.*; import org.qinan.safetyeval.client.dto.*;
import java.util.List; import java.util.List;
@ -45,4 +46,6 @@ public interface QualFilingApi {
void review(QualFilingReviewAddCmd cmd); void review(QualFilingReviewAddCmd cmd);
PageResponse<QualFilingCO> qualificationMonitoringPage(QualificationMonitoringQuery query); PageResponse<QualFilingCO> qualificationMonitoringPage(QualificationMonitoringQuery query);
SingleResponse<QualFilingStatisticsCO> qualificationMonitoringPageStatistics();
} }

View File

@ -3,7 +3,6 @@ package org.qinan.safetyeval.client.co;
import com.fasterxml.jackson.annotation.JsonFormat; import com.fasterxml.jackson.annotation.JsonFormat;
import io.swagger.annotations.ApiModelProperty; import io.swagger.annotations.ApiModelProperty;
import lombok.Data; import lombok.Data;
import org.qinan.safetyeval.domain.constant.IndustryEnum;
import org.springframework.format.annotation.DateTimeFormat; import org.springframework.format.annotation.DateTimeFormat;
import java.math.BigDecimal; import java.math.BigDecimal;
@ -135,4 +134,7 @@ public class QualFilingCO {
@ApiModelProperty(value = "单位名称") @ApiModelProperty(value = "单位名称")
private String unitName; private String unitName;
@ApiModelProperty(value = "统一社会信用代码: 机构信息")
private String orgInfoCreditCode;
} }

View File

@ -0,0 +1,22 @@
package org.qinan.safetyeval.client.co;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* COClient Object
*
* @author safety-eval
*/
@Data
public class QualFilingStatisticsCO {
@ApiModelProperty(value = "正常统计")
private Integer normalSum;
@ApiModelProperty(value = "预警统计")
private Integer earlyWarningSum;
@ApiModelProperty(value = "机构总数统计")
private Integer orgInfoSum;
}

View File

@ -5,6 +5,7 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Mapper; import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param; import org.apache.ibatis.annotations.Param;
import org.qinan.safetyeval.client.co.QualFilingCO; 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.client.dto.QualificationMonitoringQuery;
import org.qinan.safetyeval.infrastructure.dataobject.QualFilingDO; import org.qinan.safetyeval.infrastructure.dataobject.QualFilingDO;
@ -13,4 +14,6 @@ import java.util.List;
@Mapper @Mapper
public interface QualFilingMapper extends BaseMapper<QualFilingDO> { public interface QualFilingMapper extends BaseMapper<QualFilingDO> {
List<QualFilingCO> qualificationMonitoringPage(@Param("page") Page<QualFilingCO> page, @Param("query") QualificationMonitoringQuery query); List<QualFilingCO> qualificationMonitoringPage(@Param("page") Page<QualFilingCO> page, @Param("query") QualificationMonitoringQuery query);
QualFilingStatisticsCO qualificationMonitoringPageStatistics();
} }

View File

@ -60,9 +60,9 @@
</resultMap> </resultMap>
<select id="qualificationMonitoringPage" resultMap="QualFilingCOResultMap"> <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 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"> <if test="query.keyword != null">
and (info.credit_code like concat('%',#{query.keyword},'%') or info.unit_name like concat('%',#{query.keyword},'%')) and (info.credit_code like concat('%',#{query.keyword},'%') or info.unit_name like concat('%',#{query.keyword},'%'))
</if> </if>
@ -74,5 +74,14 @@
</if> </if>
order by filing.id desc order by filing.id desc
</select> </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> </mapper>