init local code
This commit is contained in:
@@ -0,0 +1,55 @@
|
||||
package com.yunhai.notice.common;
|
||||
|
||||
import com.yunhai.platform.apistd.ApiResponse;
|
||||
import com.yunhai.platform.utils.exception.ErrorCode;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.http.HttpStatus;
|
||||
import org.springframework.web.bind.MethodArgumentNotValidException;
|
||||
import org.springframework.web.bind.annotation.ExceptionHandler;
|
||||
import org.springframework.web.bind.annotation.ResponseStatus;
|
||||
import org.springframework.web.bind.annotation.RestControllerAdvice;
|
||||
|
||||
import javax.validation.ConstraintViolationException;
|
||||
|
||||
/**
|
||||
* 全局异常处理器:将业务异常转为统一响应 {@link ApiResponse}(code/msg/data)。
|
||||
*/
|
||||
@RestControllerAdvice
|
||||
public class GlobalExceptionHandler {
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(GlobalExceptionHandler.class);
|
||||
|
||||
@ExceptionHandler(NoticeException.class)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public ApiResponse<Object> handleNoticeException(NoticeException ex) {
|
||||
log.warn("[NoticeException] {}", ex.getMessage());
|
||||
String msg = ex.getMessage() == null ? "业务异常" : ex.getMessage();
|
||||
return ApiResponse.fail(5, msg);
|
||||
}
|
||||
|
||||
@ExceptionHandler(MethodArgumentNotValidException.class)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public ApiResponse<Object> handleValidException(MethodArgumentNotValidException ex) {
|
||||
String msg = ex.getBindingResult().getFieldError() != null
|
||||
? ex.getBindingResult().getFieldError().getDefaultMessage()
|
||||
: "参数校验失败";
|
||||
log.warn("[Validation] {}", msg);
|
||||
return ApiResponse.fail(ErrorCode.PARAM_ERROR, msg);
|
||||
}
|
||||
|
||||
@ExceptionHandler(ConstraintViolationException.class)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public ApiResponse<Object> handleConstraintViolation(ConstraintViolationException ex) {
|
||||
log.warn("[ConstraintViolation] {}", ex.getMessage());
|
||||
return ApiResponse.fail(ErrorCode.PARAM_ERROR, ex.getMessage());
|
||||
}
|
||||
|
||||
@ExceptionHandler(Exception.class)
|
||||
@ResponseStatus(HttpStatus.OK)
|
||||
public ApiResponse<Object> handleException(Exception ex) {
|
||||
log.error("[UnhandledException] ", ex);
|
||||
String msg = ex.getMessage() == null ? ex.getClass().getSimpleName() : ex.getMessage();
|
||||
return ApiResponse.fail(ErrorCode.INTERNAL_SERVER_ERROR.getCode(), "服务器内部错误:" + msg);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.yunhai.notice.common;
|
||||
|
||||
/**
|
||||
* 业务异常。等价于 UserBase 的 com.bestsch.utils.DException,统一以 RuntimeException 抛出。
|
||||
*/
|
||||
public class NoticeException extends RuntimeException {
|
||||
|
||||
public NoticeException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public NoticeException(String message, Throwable cause) {
|
||||
super(message, cause);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.yunhai.notice.common.context;
|
||||
|
||||
/**
|
||||
* 当前登录用户上下文(ThreadLocal)。<br>
|
||||
* 由 {@link com.yunhai.notice.common.interceptor.CurrentUserInterceptor} 在请求入口写入,
|
||||
* 供 JPA 审计监听器与业务层取用发布人/读者身份。<br><br>
|
||||
* 身份取自 CAS 写入的 {@code request.getRemoteUser()},本地未接入 CAS 时回退到
|
||||
* application.yml 的 dev.default-user 或请求头 X-Login-Account,便于本地联调。
|
||||
*/
|
||||
public class NoticeContext {
|
||||
|
||||
private static final ThreadLocal<String> ACCOUNT = new ThreadLocal<>();
|
||||
private static final ThreadLocal<String> NAME = new ThreadLocal<>();
|
||||
|
||||
public static void set(String account, String name) {
|
||||
ACCOUNT.set(account);
|
||||
NAME.set(name);
|
||||
}
|
||||
|
||||
public static String getAccount() {
|
||||
return ACCOUNT.get();
|
||||
}
|
||||
|
||||
public static String getName() {
|
||||
return NAME.get();
|
||||
}
|
||||
|
||||
public static void clear() {
|
||||
ACCOUNT.remove();
|
||||
NAME.remove();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
package com.yunhai.notice.common.event;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* 通知发布事件。在通知状态从草稿变更为已发布时发布,
|
||||
* 由 {@code MessageService} 监听并在事务提交后异步推送消息(短信/App/角标等)。
|
||||
*/
|
||||
public class NoticePublishedEvent {
|
||||
|
||||
private final Integer noticeId;
|
||||
private final String title;
|
||||
private final List<String> receiverAccounts;
|
||||
|
||||
public NoticePublishedEvent(Integer noticeId, String title, List<String> receiverAccounts) {
|
||||
this.noticeId = noticeId;
|
||||
this.title = title;
|
||||
this.receiverAccounts = receiverAccounts;
|
||||
}
|
||||
|
||||
public Integer getNoticeId() {
|
||||
return noticeId;
|
||||
}
|
||||
|
||||
public String getTitle() {
|
||||
return title;
|
||||
}
|
||||
|
||||
public List<String> getReceiverAccounts() {
|
||||
return receiverAccounts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.yunhai.notice.common.interceptor;
|
||||
|
||||
import com.yunhai.notice.common.context.NoticeContext;
|
||||
import org.springframework.beans.factory.annotation.Value;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.web.servlet.HandlerInterceptor;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
/**
|
||||
* 当前登录用户拦截器:优先取 CAS 写入的 {@code getRemoteUser()},
|
||||
* 其次取请求头 X-Login-Account / X-Login-Name(本地联调用),最后回退到 dev.default-user。
|
||||
* 写入 {@link NoticeContext} 供业务层与 JPA 审计监听器使用。
|
||||
*/
|
||||
@Component
|
||||
public class CurrentUserInterceptor implements HandlerInterceptor {
|
||||
|
||||
@Value("${dev.default-user.account:admin}")
|
||||
private String devAccount;
|
||||
|
||||
@Value("${dev.default-user.name:管理员}")
|
||||
private String devName;
|
||||
|
||||
@Override
|
||||
public boolean preHandle(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler) {
|
||||
String account = request.getRemoteUser();
|
||||
String name = null;
|
||||
if (account == null) {
|
||||
account = request.getHeader("X-Login-Account");
|
||||
name = request.getHeader("X-Login-Name");
|
||||
}
|
||||
if (account == null) {
|
||||
account = devAccount;
|
||||
name = devName;
|
||||
}
|
||||
if (name == null) {
|
||||
name = account;
|
||||
}
|
||||
NoticeContext.set(account, name);
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterCompletion(HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
Object handler, Exception ex) {
|
||||
NoticeContext.clear();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,40 @@
|
||||
package com.yunhai.notice.common.listener;
|
||||
|
||||
import com.yunhai.notice.common.context.NoticeContext;
|
||||
import com.yunhai.notice.entity.BaseEntity;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import javax.persistence.PrePersist;
|
||||
import javax.persistence.PreUpdate;
|
||||
import java.time.LocalDateTime;
|
||||
|
||||
/**
|
||||
* JPA 审计监听器:在实体持久化/更新前自动填充创建人/更新人(登录账号)与时间戳。
|
||||
* 对应 {@link BaseEntity} 的 createBy / updateBy / createTime / updateTime 字段。
|
||||
*/
|
||||
@Component
|
||||
public class NoticeAuditListener {
|
||||
|
||||
@PrePersist
|
||||
public void prePersist(BaseEntity entity) {
|
||||
LocalDateTime now = LocalDateTime.now();
|
||||
entity.setCreateTime(now);
|
||||
entity.setUpdateTime(now);
|
||||
String account = NoticeContext.getAccount();
|
||||
if (account != null) {
|
||||
if (entity.getCreateBy() == null) {
|
||||
entity.setCreateBy(account);
|
||||
}
|
||||
entity.setUpdateBy(account);
|
||||
}
|
||||
}
|
||||
|
||||
@PreUpdate
|
||||
public void preUpdate(BaseEntity entity) {
|
||||
entity.setUpdateTime(LocalDateTime.now());
|
||||
String account = NoticeContext.getAccount();
|
||||
if (account != null) {
|
||||
entity.setUpdateBy(account);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user