diff --git a/doc/doc-api/src/main/java/cn/axzo/nanopart/doc/api/autoconfig/DocAutoConfiguration.java b/doc/doc-api/src/main/java/cn/axzo/nanopart/doc/api/autoconfig/DocAutoConfiguration.java new file mode 100644 index 00000000..6b792090 --- /dev/null +++ b/doc/doc-api/src/main/java/cn/axzo/nanopart/doc/api/autoconfig/DocAutoConfiguration.java @@ -0,0 +1,7 @@ +package cn.axzo.nanopart.doc.api.autoconfig; + +import org.springframework.cloud.openfeign.EnableFeignClients; + +@EnableFeignClients("cn.axzo.nanopart.doc") +public class DocAutoConfiguration { +} diff --git a/doc/doc-api/src/main/resources/META-INF/spring.factories b/doc/doc-api/src/main/resources/META-INF/spring.factories new file mode 100644 index 00000000..7a458884 --- /dev/null +++ b/doc/doc-api/src/main/resources/META-INF/spring.factories @@ -0,0 +1,2 @@ +org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ +cn.axzo.nanopart.doc.api.autoconfig.DocAutoConfiguration \ No newline at end of file diff --git a/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/dao/EssLogDao.java b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/dao/EssLogDao.java new file mode 100644 index 00000000..7bf7fdb7 --- /dev/null +++ b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/dao/EssLogDao.java @@ -0,0 +1,41 @@ + +package cn.axzo.nanopart.doc.dao; + +import org.springframework.stereotype.Repository; + +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; + +import cn.axzo.nanopart.doc.entity.DocLog; +import cn.axzo.nanopart.doc.mapper.DocLogMapper; +import cn.axzo.nanopart.doc.utils.BizAssertions; + +/** + * @author yanglin + */ +@Repository +public class EssLogDao extends ServiceImpl { + + public void logRequest(String context, Object subject, Object request) { + log(context, subject, "request", request); + } + + public void log(String context, Object subject, Object... logContents) { + log(null, context, subject, logContents); + } + + public void log(Exception exception, String context, Object subject, Object... logContents) { + DocLog log = new DocLog(); + log.setContext(context); + log.setSubject(String.valueOf(subject)); + log.setError(exception); + if (logContents != null && logContents.length > 0) { + BizAssertions.assertTrue(logContents.length % 2 == 0, "logContents must be even"); + for (int i = 0; i < logContents.length; i += 2) { + BizAssertions.assertTrue(logContents[i] instanceof String, "logContents key must be String"); + log.addLogContent((String) logContents[i], logContents[i + 1]); + } + } + save(log); + } + +} diff --git a/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/entity/DocLog.java b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/entity/DocLog.java new file mode 100644 index 00000000..21047d0e --- /dev/null +++ b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/entity/DocLog.java @@ -0,0 +1,73 @@ + +package cn.axzo.nanopart.doc.entity; + +import com.alibaba.fastjson.JSON; +import com.alibaba.fastjson.JSONObject; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableName; +import com.baomidou.mybatisplus.extension.handlers.FastjsonTypeHandler; +import com.google.common.base.Throwables; + +import cn.axzo.nanopart.doc.http.RequestNoInterceptor; +import cn.axzo.nanopart.doc.utils.YesOrNo; +import cn.axzo.pokonyan.config.mybatisplus.BaseEntity; +import lombok.Getter; +import lombok.Setter; + +/** + * @author yanglin + */ +@Setter +@Getter +@TableName(value = "doc_log", autoResultMap = true) +public class DocLog extends BaseEntity { + + /** + * 上下文 + */ + private String context; + + /** + * 主体 + */ + private String subject; + + /** + * 腾讯返回的请求id + */ + private String requestNo; + + /** + * 是否异常 + */ + private YesOrNo isError; + + /** + * 日志内容 + */ + @TableField(typeHandler = FastjsonTypeHandler.class) + private JSONObject logContent; + + public void addLogContent(String key, Object value) { + if (logContent == null) + initializeLogContent(); + logContent.put(key, value); + } + + private void initializeLogContent() { + logContent = new JSONObject(); + this.requestNo = RequestNoInterceptor.getRequestNo(); + } + + public void setError(Exception exception) { + if (exception == null) + return; + setIsError(YesOrNo.YES); + addLogContent("exception", Throwables.getStackTraceAsString(exception)); + } + + @Override + public String toString() { + return JSON.toJSONString(this); + } +} diff --git a/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/http/EssWebMvcConfigurer.java b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/http/EssWebMvcConfigurer.java new file mode 100644 index 00000000..1f17afab --- /dev/null +++ b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/http/EssWebMvcConfigurer.java @@ -0,0 +1,24 @@ +package cn.axzo.nanopart.doc.http; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.InterceptorRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +import lombok.RequiredArgsConstructor; + +/** + * @author yanglin + */ +@Configuration +@RequiredArgsConstructor +public class EssWebMvcConfigurer implements WebMvcConfigurer { + + private final RequestNoInterceptor requestNoInterceptor; + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(requestNoInterceptor) + .addPathPatterns("/**"); + } + +} \ No newline at end of file diff --git a/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/http/RequestNoInterceptor.java b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/http/RequestNoInterceptor.java new file mode 100644 index 00000000..57410e5e --- /dev/null +++ b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/http/RequestNoInterceptor.java @@ -0,0 +1,45 @@ + +package cn.axzo.nanopart.doc.http; + +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import cn.axzo.nanopart.doc.utils.UUIDUtil; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.HandlerInterceptor; +import org.springframework.web.servlet.ModelAndView; + +import lombok.NonNull; + +/** + * @author yanglin + */ +@Component +public class RequestNoInterceptor implements HandlerInterceptor { + + private static final ThreadLocal REQUEST_NO = new ThreadLocal<>(); + + @Override + public boolean preHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, + @NonNull Object handler) { + REQUEST_NO.set(UUIDUtil.uuidString()); + return true; + } + + @Override + public void postHandle(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, + @NonNull Object handler, ModelAndView modelAndView) { + REQUEST_NO.remove(); + } + + @Override + public void afterCompletion(@NonNull HttpServletRequest request, @NonNull HttpServletResponse response, + @NonNull Object handler, Exception ex) { + REQUEST_NO.remove(); + } + + public static String getRequestNo() { + return REQUEST_NO.get(); + } + +} diff --git a/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/mapper/DocLogMapper.java b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/mapper/DocLogMapper.java new file mode 100644 index 00000000..456ac9c5 --- /dev/null +++ b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/mapper/DocLogMapper.java @@ -0,0 +1,12 @@ +package cn.axzo.nanopart.doc.mapper; + +import cn.axzo.nanopart.doc.entity.DocLog; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + * @author yanglin + */ +@Mapper +public interface DocLogMapper extends BaseMapper { +} diff --git a/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/utils/BizAssertions.java b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/utils/BizAssertions.java new file mode 100644 index 00000000..b4e6a50a --- /dev/null +++ b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/utils/BizAssertions.java @@ -0,0 +1,159 @@ + +package cn.axzo.nanopart.doc.utils; + +import cn.axzo.basics.common.exception.ServiceException; +import cn.axzo.basics.common.util.AssertUtil; +import cn.axzo.framework.domain.web.result.ApiListResult; +import cn.axzo.framework.domain.web.result.ApiResult; +import cn.azxo.framework.common.model.CommonResponse; +import cn.hutool.http.HttpStatus; +import com.alibaba.fastjson.JSON; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.slf4j.helpers.MessageFormatter; + +import java.util.Collection; +import java.util.List; +import java.util.Objects; + +/** + * @author yanglin + */ +@Slf4j +public class BizAssertions { + + public static ServiceException fail(String message, Object... args) { + return new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage()); + } + + public static ServiceException fail(Exception e, String message, Object... args) { + return new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage(), e); + } + + /** + * 断言集合为空 + */ + public static void assertEmpty(Collection actual, String message, Object... args) { + AssertUtil.isEmpty(actual, MessageFormatter.arrayFormat(message, args).getMessage()); + } + + /** + * 断言为NULL + */ + public static void assertNull(Object actual, String message, Object... args) { + AssertUtil.isNull(actual, MessageFormatter.arrayFormat(message, args).getMessage()); + } + + /** + * 断言不为NULL + */ + public static void assertNotNull(Object actual, String message, Object... args) { + AssertUtil.notNull(actual, MessageFormatter.arrayFormat(message, args).getMessage()); + } + + /** + * 断言集合不为空 + */ + public static void assertNotEmpty(Collection actual, String message, Object... args) { + AssertUtil.notEmpty(actual, MessageFormatter.arrayFormat(message, args).getMessage()); + } + + /** + * 断言数组不为空 + */ + public static void assertNotEmpty(T[] actual, String message, Object... args) { + AssertUtil.notEmpty(actual, MessageFormatter.arrayFormat(message, args).getMessage()); + } + + /** + * 断言值为真 + */ + public static void assertTrue(boolean actual, String message, Object... args) { + AssertUtil.isTrue(actual, MessageFormatter.arrayFormat(message, args).getMessage()); + } + + /** + * 断言值不为真 + */ + public static void assertFalse(boolean actual, String message, Object... args) { + AssertUtil.isFalse(actual, MessageFormatter.arrayFormat(message, args).getMessage()); + } + + /** + * 断言值2个值是否equals + */ + public static void assertEquals(Object expected, Object actual, String message, Object... args) { + if (!Objects.equals(expected, actual)) { + throw new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage()); + } + } + + /** + * 断言值2个值是否不equals + */ + public static void assertNotEquals(Object expected, Object actual, String message, Object... args) { + if (Objects.equals(expected, actual)) { + throw new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage()); + } + } + + public static void assertNotBlank(String content, String message, Object... args) { + if (StringUtils.isBlank(content)) { + throw new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage()); + } + } + + public static void assertBlank(String content, String message, Object... args) { + if (StringUtils.isNotBlank(content)) { + throw new ServiceException(MessageFormatter.arrayFormat(message, args).getMessage()); + } + } + + private static String messageOrTemplateMessage(String message, String template, Object... args) { + if (message != null) { + return message; + } + return MessageFormatter.arrayFormat(template, args).getMessage(); + } + + public static T assertResponse(CommonResponse response) { + return assertResponse(response, "error resp={}", JSON.toJSONString(response)); + } + + public static T assertResponse(CommonResponse response, String message, Object... args) { + if (response == null || response.getCode() != HttpStatus.HTTP_OK) { + ServiceException e = new ServiceException( + messageOrTemplateMessage(response == null ? null : response.getMsg(), message, args)); + log.warn("remote call response with error", e); + throw e; + } + return response.getData(); + } + + public static T assertResponse(ApiResult response) { + return assertResponse(response, "error resp={}", JSON.toJSONString(response)); + } + + public static T assertResponse(ApiResult response, String message, Object... args) { + if (!response.isSuccess()) { + ServiceException e = new ServiceException(messageOrTemplateMessage(response.getMsg(), message, args)); + log.warn("remote call response with error", e); + throw e; + } + return response.getData(); + } + + public static List assertResponse(ApiListResult response) { + return assertResponse(response, "error resp={}", JSON.toJSONString(response)); + } + + public static List assertResponse(ApiListResult response, String message, Object... args) { + if (!response.isSuccess()) { + ServiceException e = new ServiceException(messageOrTemplateMessage(response.getMsg(), message, args)); + log.warn("remote call response with error", e); + throw e; + } + return response.getData(); + } + +} diff --git a/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/utils/BizTransactional.java b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/utils/BizTransactional.java new file mode 100644 index 00000000..59e580e9 --- /dev/null +++ b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/utils/BizTransactional.java @@ -0,0 +1,20 @@ + +package cn.axzo.nanopart.doc.utils; + +import org.springframework.transaction.annotation.Transactional; + +import java.lang.annotation.ElementType; +import java.lang.annotation.Inherited; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +/** + * @author yanglin + */ +@Inherited +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Transactional(rollbackFor = Exception.class) +public @interface BizTransactional { +} diff --git a/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/utils/UUIDUtil.java b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/utils/UUIDUtil.java new file mode 100644 index 00000000..cdc2a288 --- /dev/null +++ b/doc/doc-server/src/main/java/cn/axzo/nanopart/doc/utils/UUIDUtil.java @@ -0,0 +1,26 @@ +package cn.axzo.nanopart.doc.utils; + +import java.util.UUID; + +import lombok.AccessLevel; +import lombok.NoArgsConstructor; + +/** + * UUID生成器 + * + * @author cold_blade + * @date 2023/10/5 + * @version 1.0 + */ +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public final class UUIDUtil { + + public static String uuidString() { + String str = UUID.randomUUID().toString(); + return str.replaceAll("-", ""); + } + + public static String uuidRawString() { + return UUID.randomUUID().toString(); + } +} diff --git a/nanopart-server/pom.xml b/nanopart-server/pom.xml index 178abe7f..5a4f2541 100644 --- a/nanopart-server/pom.xml +++ b/nanopart-server/pom.xml @@ -150,6 +150,18 @@ 2.0.0-SNAPSHOT + + cn.axzo.nanopart + doc-api + 2.0.0-SNAPSHOT + + + + cn.axzo.nanopart + doc-server + 2.0.0-SNAPSHOT + + cn.axzo.im.center im-center-api