运维页面
This commit is contained in:
@@ -7,6 +7,7 @@ import com.yunhai.bschclassmanage.enums.SyncDataType;
|
||||
import com.yunhai.bschclassmanage.service.ISyncLogService;
|
||||
import com.yunhai.bschclassmanage.utils.OtherUtil;
|
||||
import com.yunhai.bschclassmanage.utils.SensitiveDataUtil;
|
||||
import com.yunhai.bschclassmanage.utils.SyncLogRetryContext;
|
||||
import com.yunhai.bschclassmanage.utils.UserContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.aspectj.lang.ProceedingJoinPoint;
|
||||
@@ -47,7 +48,12 @@ public class SyncLogAspect {
|
||||
SyncLog entity = new SyncLog();
|
||||
// 枚举类型 key/name
|
||||
entity.setSyncDataTypeKey(dataType.getKey());
|
||||
entity.setSyncDataTypeName(dataType.getName());
|
||||
String typeName = dataType.getName();
|
||||
if (SyncLogRetryContext.isRetry()) {
|
||||
// 重试重放时,在同步类型名上追加"(重试)"标记,便于与首次调用区分
|
||||
typeName = (typeName == null ? "" : typeName) + "(重试)";
|
||||
}
|
||||
entity.setSyncDataTypeName(typeName);
|
||||
// 大类:Send / Sync(由 key 前缀推导)
|
||||
boolean isSend = dataType.getKey() != null && dataType.getKey().startsWith("Send");
|
||||
entity.setSyncType(isSend ? "Send" : "Sync");
|
||||
|
||||
@@ -7,6 +7,7 @@ import com.yunhai.bschclassmanage.utils.GzipUtil;
|
||||
import com.yunhai.bschclassmanage.utils.OperationLogResolver;
|
||||
import com.yunhai.bschclassmanage.utils.OtherUtil;
|
||||
import com.yunhai.bschclassmanage.utils.SensitiveDataUtil;
|
||||
import com.yunhai.bschclassmanage.utils.SyncLogRetryContext;
|
||||
import com.yunhai.bschclassmanage.utils.UserContext;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.exception.ExceptionUtils;
|
||||
@@ -98,7 +99,12 @@ public class SysOperationLogAspect {
|
||||
e.setRequestMethod(httpMethod);
|
||||
e.setClassMethod(point.getTarget().getClass().getName() + "." + methodName);
|
||||
e.setLogLevel(OperationLogResolver.resolveLevel(httpMethod, uri, methodName).getCode());
|
||||
e.setOperationType(OperationLogResolver.resolveOperationType(httpMethod, uri, methodName));
|
||||
String opType = OperationLogResolver.resolveOperationType(httpMethod, uri, methodName);
|
||||
if (SyncLogRetryContext.isRetry()) {
|
||||
// 重试重放时,在操作类型上追加"(重试)"标记,便于与首次调用区分
|
||||
opType = (opType == null ? "" : opType) + "(重试)";
|
||||
}
|
||||
e.setOperationType(opType);
|
||||
if (request != null) {
|
||||
e.setUserAgent(request.getHeader("User-Agent"));
|
||||
e.setRequestParams(SensitiveDataUtil.mask(buildRequestParams(point, request)));
|
||||
|
||||
@@ -13,7 +13,9 @@ import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.servlet.http.HttpSession;
|
||||
import java.io.IOException;
|
||||
import java.util.Enumeration;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
@@ -37,6 +39,21 @@ public class LoginUserController {
|
||||
return authUserWithDept;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断是否登录:已登录则重定向到运维工作台 /work-view.html(兼容 context-path);
|
||||
* 未登录返回 401(通常 CAS 拦截器会先行 302 到 CAS 登录页)。
|
||||
*/
|
||||
@GetMapping("checkLogin")
|
||||
public void checkLogin(BoaUser authUserWithDept, HttpServletRequest request, HttpServletResponse response) throws IOException {
|
||||
if (authUserWithDept != null) {
|
||||
response.sendRedirect(request.getContextPath() + "/work-view.html");
|
||||
} else {
|
||||
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
|
||||
response.setContentType("application/json;charset=UTF-8");
|
||||
response.getWriter().write("{\"code\":401,\"msg\":\"未登录\"}");
|
||||
}
|
||||
}
|
||||
|
||||
@GetMapping("Logout")
|
||||
protected void logout(HttpServletRequest request) {
|
||||
HttpSession session = request.getSession();
|
||||
|
||||
@@ -79,7 +79,7 @@ public class SyncLogController extends BaseController {
|
||||
Object result = syncLogService.retry(id);
|
||||
Map<String, Object> m = new LinkedHashMap<>();
|
||||
m.put("success", true);
|
||||
m.put("message", "重试已提交,执行结果见新增的同步日志");
|
||||
m.put("message", "重试成功");
|
||||
if (result != null) m.put("data", result);
|
||||
return m;
|
||||
}
|
||||
|
||||
+1
-1
@@ -80,7 +80,7 @@ public class SysOperationLogController extends BaseController {
|
||||
Object result = sysOperationLogService.retry(id);
|
||||
Map<String, Object> m = new LinkedHashMap<>();
|
||||
m.put("success", true);
|
||||
m.put("message", "重试已提交,执行结果见新增的审计日志");
|
||||
m.put("message", "重试成功");
|
||||
if (result != null) m.put("data", result);
|
||||
return m;
|
||||
}
|
||||
|
||||
@@ -74,6 +74,18 @@ public class SyncLog implements Serializable {
|
||||
@Column(name = "status")
|
||||
private Integer status;
|
||||
|
||||
/** 重试次数 */
|
||||
@Column(name = "retry_count", columnDefinition = "INT DEFAULT 0")
|
||||
private Integer retryCount;
|
||||
|
||||
/** 上次重试状态:1 成功 / 0 失败 */
|
||||
@Column(name = "last_retry_status")
|
||||
private Integer lastRetryStatus;
|
||||
|
||||
/** 上次重试时间 */
|
||||
@Column(name = "last_retry_time", columnDefinition = "DATETIME(3)")
|
||||
private LocalDateTime lastRetryTime;
|
||||
|
||||
@CreationTimestamp
|
||||
@Column(name = "create_time", nullable = false, updatable = false, columnDefinition = "DATETIME(3)")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@@ -71,6 +71,18 @@ public class SysOperationLog implements Serializable {
|
||||
@Column(name = "error_msg", columnDefinition = "TEXT")
|
||||
private String errorMsg; // 异常信息,截断 2000
|
||||
|
||||
/** 重试次数 */
|
||||
@Column(name = "retry_count", columnDefinition = "INT DEFAULT 0")
|
||||
private Integer retryCount;
|
||||
|
||||
/** 上次重试状态:1 成功 / 0 失败 */
|
||||
@Column(name = "last_retry_status")
|
||||
private Integer lastRetryStatus;
|
||||
|
||||
/** 上次重试时间 */
|
||||
@Column(name = "last_retry_time", columnDefinition = "DATETIME(3)")
|
||||
private LocalDateTime lastRetryTime;
|
||||
|
||||
@CreationTimestamp
|
||||
@Column(name = "create_time", nullable = false, updatable = false, columnDefinition = "DATETIME(3)")
|
||||
private LocalDateTime createTime;
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.yunhai.bschclassmanage.entity.SyncLog;
|
||||
import com.yunhai.bschclassmanage.model.vo.PageVO;
|
||||
import com.yunhai.bschclassmanage.service.ISyncLogService;
|
||||
import com.yunhai.bschclassmanage.utils.ReflectRetryUtil;
|
||||
import com.yunhai.bschclassmanage.utils.SyncLogRetryContext;
|
||||
import com.yunhai.platform.utils.exception.DException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -23,6 +24,7 @@ import javax.persistence.criteria.Predicate;
|
||||
import java.lang.reflect.InvocationTargetException;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Parameter;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
@@ -104,6 +106,10 @@ public class SyncLogServiceImpl implements ISyncLogService {
|
||||
if (StringUtils.isBlank(e.getClassMethod())) throw new DException("该日志缺少调用信息(classMethod),无法重试");
|
||||
if (StringUtils.isBlank(e.getJsonData())) throw new DException("该日志缺少入参(jsonData),无法重试");
|
||||
|
||||
// 记录本次重试元数据(重试次数 +1、重试时间)
|
||||
e.setRetryCount((e.getRetryCount() == null ? 0 : e.getRetryCount()) + 1);
|
||||
e.setLastRetryTime(LocalDateTime.now());
|
||||
|
||||
try {
|
||||
@SuppressWarnings("unchecked")
|
||||
Map<String, Object> argMap = JSON.parseObject(e.getJsonData(), LinkedHashMap.class);
|
||||
@@ -133,11 +139,26 @@ public class SyncLogServiceImpl implements ISyncLogService {
|
||||
}
|
||||
|
||||
Method invokeMethod = ReflectRetryUtil.getInvokeMethod(resolved.bean, target);
|
||||
return invokeMethod.invoke(resolved.bean, args);
|
||||
SyncLogRetryContext.mark(); // 标记为重试,切面生成新日志时在同步类型名追加"(重试)"
|
||||
Object result = invokeMethod.invoke(resolved.bean, args);
|
||||
e.setLastRetryStatus(1); // 本次重试成功
|
||||
return result;
|
||||
} catch (DException de) {
|
||||
e.setLastRetryStatus(0); // 本次重试失败
|
||||
throw de;
|
||||
} catch (InvocationTargetException | IllegalAccessException ex) {
|
||||
e.setLastRetryStatus(0); // 本次重试失败
|
||||
throw ReflectRetryUtil.rethrow(ex);
|
||||
} catch (RuntimeException re) {
|
||||
e.setLastRetryStatus(0); // 本次重试失败(入参转换 / 定位等)
|
||||
throw re;
|
||||
} finally {
|
||||
SyncLogRetryContext.clear(); // 清除重试标记,避免线程池复用泄漏
|
||||
try {
|
||||
repository.save(e); // 同步落库重试元数据(次数 / 上次状态 / 时间)
|
||||
} catch (Exception saveEx) {
|
||||
log.error("重试元数据落库失败, id={}", e.getId(), saveEx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+45
-24
@@ -7,6 +7,7 @@ import com.yunhai.bschclassmanage.model.vo.PageVO;
|
||||
import com.yunhai.bschclassmanage.service.ISysOperationLogService;
|
||||
import com.yunhai.bschclassmanage.utils.GzipUtil;
|
||||
import com.yunhai.bschclassmanage.utils.ReflectRetryUtil;
|
||||
import com.yunhai.bschclassmanage.utils.SyncLogRetryContext;
|
||||
import com.yunhai.bschclassmanage.utils.UserContext;
|
||||
import com.yunhai.platform.utils.exception.DException;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
@@ -139,43 +140,63 @@ public class SysOperationLogServiceImpl implements ISysOperationLogService {
|
||||
throw new DException("非 JSON 数组入参(如 GET query)无法可靠重放,请手动重试");
|
||||
}
|
||||
|
||||
JSONArray arr;
|
||||
try {
|
||||
arr = JSONArray.parseArray(e.getRequestParams());
|
||||
} catch (Exception ex) {
|
||||
throw new DException("入参解析失败(requestParams),无法重试");
|
||||
}
|
||||
if (arr == null) throw new DException("入参解析失败(requestParams),无法重试");
|
||||
|
||||
ReflectRetryUtil.Resolved resolved = ReflectRetryUtil.resolveBeanAndMethod(
|
||||
applicationContext, e.getClassMethod(), arr.size());
|
||||
|
||||
Method target = resolved.method;
|
||||
Parameter[] params = target.getParameters();
|
||||
Object[] args = new Object[params.length];
|
||||
int jsonIdx = 0;
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
if (ReflectRetryUtil.isSkippable(params[i].getType())) {
|
||||
args[i] = null; // 容器注入参数,无法重建
|
||||
} else {
|
||||
Object raw = (jsonIdx < arr.size()) ? arr.get(jsonIdx++) : null;
|
||||
args[i] = ReflectRetryUtil.convert(raw, params[i]);
|
||||
}
|
||||
}
|
||||
// 记录本次重试元数据(重试次数 +1、重试时间)
|
||||
e.setRetryCount((e.getRetryCount() == null ? 0 : e.getRetryCount()) + 1);
|
||||
e.setLastRetryTime(LocalDateTime.now());
|
||||
|
||||
// 还原原操作人上下文,使重放触发的审计切面记录正确操作人;结束后恢复本次请求上下文
|
||||
UserContext.UserInfo snapshot = UserContext.getCurrentUser();
|
||||
try {
|
||||
JSONArray arr;
|
||||
try {
|
||||
arr = JSONArray.parseArray(e.getRequestParams());
|
||||
} catch (Exception ex) {
|
||||
throw new DException("入参解析失败(requestParams),无法重试");
|
||||
}
|
||||
if (arr == null) throw new DException("入参解析失败(requestParams),无法重试");
|
||||
|
||||
ReflectRetryUtil.Resolved resolved = ReflectRetryUtil.resolveBeanAndMethod(
|
||||
applicationContext, e.getClassMethod(), arr.size());
|
||||
|
||||
Method target = resolved.method;
|
||||
Parameter[] params = target.getParameters();
|
||||
Object[] args = new Object[params.length];
|
||||
int jsonIdx = 0;
|
||||
for (int i = 0; i < params.length; i++) {
|
||||
if (ReflectRetryUtil.isSkippable(params[i].getType())) {
|
||||
args[i] = null; // 容器注入参数,无法重建
|
||||
} else {
|
||||
Object raw = (jsonIdx < arr.size()) ? arr.get(jsonIdx++) : null;
|
||||
args[i] = ReflectRetryUtil.convert(raw, params[i]);
|
||||
}
|
||||
}
|
||||
|
||||
UserContext.setUserId(e.getUserId());
|
||||
UserContext.setUserName(e.getUserName());
|
||||
UserContext.setSchId(e.getSchId());
|
||||
UserContext.setIp(e.getRequestIp());
|
||||
Method invokeMethod = ReflectRetryUtil.getInvokeMethod(resolved.bean, target);
|
||||
return invokeMethod.invoke(resolved.bean, args);
|
||||
SyncLogRetryContext.mark(); // 标记为重试,审计切面生成新日志时在操作类型追加"(重试)"
|
||||
Object result = invokeMethod.invoke(resolved.bean, args);
|
||||
e.setLastRetryStatus(1); // 本次重试成功
|
||||
return result;
|
||||
} catch (DException de) {
|
||||
e.setLastRetryStatus(0); // 本次重试失败
|
||||
throw de;
|
||||
} catch (InvocationTargetException | IllegalAccessException ex) {
|
||||
e.setLastRetryStatus(0); // 本次重试失败
|
||||
throw ReflectRetryUtil.rethrow(ex);
|
||||
} catch (RuntimeException re) {
|
||||
e.setLastRetryStatus(0); // 本次重试失败(入参转换 / 定位等)
|
||||
throw re;
|
||||
} finally {
|
||||
UserContext.setUserInfo(snapshot);
|
||||
SyncLogRetryContext.clear(); // 清除重试标记,避免线程池复用泄漏
|
||||
try {
|
||||
repository.save(e); // 同步落库重试元数据(次数 / 上次状态 / 时间)
|
||||
} catch (Exception saveEx) {
|
||||
log.error("审计日志重试元数据落库失败, id={}", e.getId(), saveEx);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user