init: 初始化项目

This commit is contained in:
zhansihu 2023-09-21 15:44:11 +08:00
commit bbb638c28a
55 changed files with 2009 additions and 0 deletions

39
.gitignore vendored Normal file
View File

@ -0,0 +1,39 @@
HELP.md
target/
!.mvn/wrapper/maven-wrapper.jar
!**/src/main/**/target/
!**/src/test/**/target/
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
.sts4-cache
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### NetBeans ###
/nbproject/private/
/nbbuild/
/dist/
/nbdist/
/.nb-gradle/
build/
!**/src/main/**/build/
!**/src/test/**/build/
### VS Code ###
.vscode/
application-local.yml
*.log
rebel.xml
.flattened-pom.xml

9
README.md Normal file
View File

@ -0,0 +1,9 @@
Riven-瑞雯
--------------------------
## 能力
* 钉钉/企微 组织人员同步
## 本地运行参数
* 系统环境变量
server.port=8080;spring.datasource.url=jdbc:mysql://116.63.13.181:3311/pudge-dev?useUnicode=true&characterEncoding=utf8&allowMultiQueries=true&serverTimezone=Asia/Shanghai&useSSL=true&verifyServerCertificate=false&rewriteBatchedStatements=true;CUSTOM_ENV=dev

2
RELEASE.md Normal file
View File

@ -0,0 +1,2 @@
# 发布记录

103
pom.xml Normal file
View File

@ -0,0 +1,103 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>cn.axzo.infra</groupId>
<artifactId>axzo-parent</artifactId>
<version>2.4.13.5</version>
</parent>
<groupId>cn.axzo</groupId>
<artifactId>riven</artifactId>
<packaging>pom</packaging>
<version>${revision}</version>
<name>riven</name>
<properties>
<axzo-bom.version>2.0.0-SNAPSHOT</axzo-bom.version>
<axzo-dependencies.version>2.0.0-SNAPSHOT</axzo-dependencies.version>
<lombok.version>1.18.22</lombok.version>
<mapstruct.version>1.4.2.Final</mapstruct.version>
<revision>2.0.0-SNAPSHOT</revision>
</properties>
<modules>
<module>riven-server</module>
<module>riven-api</module>
</modules>
<dependencyManagement>
<dependencies>
<!-- 导入axzo通用api依赖 -->
<dependency>
<groupId>cn.axzo.infra</groupId>
<artifactId>axzo-bom</artifactId>
<version>${axzo-bom.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<dependency>
<groupId>cn.axzo.infra</groupId>
<artifactId>axzo-dependencies</artifactId>
<version>${axzo-dependencies.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
<dependencies>
<!-- lombok -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- for test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<!-- maven-compiler-plugin 插件,解决 Lombok + MapStruct 组合 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
</plugins>
</build>
<repositories>
<repository>
<id>axzo</id>
<name>axzo repository</name>
<url>https://nexus.axzo.cn/repository/axzo/</url>
</repository>
</repositories>
</project>

22
riven-api/pom.xml Normal file
View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>riven</artifactId>
<groupId>cn.axzo</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<artifactId>riven-api</artifactId>
<packaging>jar</packaging>
<name>riven-api</name>
<dependencies>
<dependency>
<groupId>cn.axzo.framework</groupId>
<artifactId>axzo-consumer-spring-cloud-starter</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,33 @@
package cn.axzo.riven.client.feign;
import cn.axzo.framework.domain.page.PageQO;
import cn.axzo.framework.domain.web.result.ApiPageResult;
import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.riven.client.model.NewUserReq;
import cn.axzo.riven.client.model.UpdateUserReq;
import cn.axzo.riven.client.model.UserRes;
import cn.azxo.framework.common.model.CommonResponse;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.*;
import java.util.Map;
import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
/**
* @Author: liyong.tian
* @Date: 2022/9/17
* @Description:
*/
@FeignClient(name = "riven", url = "http://localhost:8899", fallbackFactory = MicroArchetypeFallbackFactory.class)
public interface MicroArchetypeApi {
@PostMapping(value = "/api/v1/users", consumes = APPLICATION_JSON_VALUE)
CommonResponse<UserRes> createUser(@RequestBody NewUserReq req);
@PutMapping(value = "/api/v2/users/{id}", consumes = APPLICATION_JSON_VALUE)
ApiResult<UserRes> updateUser(@PathVariable("id") Long id, @RequestBody UpdateUserReq req);
@GetMapping(value = "/api/v2/users")
ApiPageResult<UserRes> fetchUsers(@RequestParam Map<String, Object> query, PageQO page);
}

View File

@ -0,0 +1,52 @@
package cn.axzo.riven.client.feign;
import cn.axzo.framework.client.feign.FeignFallback;
import cn.axzo.framework.domain.page.PageQO;
import cn.axzo.framework.domain.web.result.ApiPageResult;
import cn.axzo.framework.domain.web.result.ApiResult;
import cn.axzo.riven.client.model.NewUserReq;
import cn.axzo.riven.client.model.UpdateUserReq;
import cn.axzo.riven.client.model.UserRes;
import cn.azxo.framework.common.model.CommonResponse;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import java.util.Map;
/**
* @Author: liyong.tian
* @Date: 2022/9/17
* @Description:
*/
@Slf4j
@RequiredArgsConstructor
public class MicroArchetypeApiFallback implements MicroArchetypeApi {
private final FeignFallback fallback;
/**
* 老项目迁移使用
* @param req
* @return
*/
@Override
public CommonResponse<UserRes> createUser(NewUserReq req) {
log.error("[riven-api] createUser fallback", fallback.getCause());
return CommonResponse.error("创建用户失败");
}
/**
* 新项目推荐使用
*/
@Override
public ApiResult<UserRes> updateUser(Long id, UpdateUserReq req) {
log.error("[riven-api] updateUser fallback", fallback.getCause());
return fallback.resp();
}
@Override
public ApiPageResult<UserRes> fetchUsers(Map<String, Object> query, PageQO page) {
log.error("[riven-api] fetchUsers fallback", fallback.getCause());
return fallback.pageResp();
}
}

View File

@ -0,0 +1,19 @@
package cn.axzo.riven.client.feign;
import cn.axzo.framework.client.feign.FeignFallback;
import cn.axzo.framework.domain.web.code.IRespCode;
import cn.axzo.framework.domain.web.code.RespCode;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
@Component
public class MicroArchetypeFallbackFactory implements FallbackFactory<MicroArchetypeApiFallback> {
// TODO: 2022/11/3 100-调整为具体的项目编号XXX-调整为项目名
private final IRespCode respCode = new RespCode("100" + "91001", "XXX服务不可用");
@Override
public MicroArchetypeApiFallback create(Throwable cause) {
return new MicroArchetypeApiFallback(new FeignFallback(cause, respCode));
}
}

View File

@ -0,0 +1,29 @@
package cn.axzo.riven.client.model;
import lombok.Data;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @Author: liyong.tian
* @Date: 2022/9/2
* @Description:
*/
@Data
public class NewUserReq {
@NotBlank(message = "名称不能为空")
private String name;
@NotNull
private Integer sex;
private Integer age;
private String phone;
private String email;
private String address;
}

View File

@ -0,0 +1,22 @@
package cn.axzo.riven.client.model;
import cn.axzo.framework.context.client.IQueryMap;
import cn.axzo.framework.context.client.QueryMap;
import lombok.Data;
@Data
public class QueryUserReq implements IQueryMap {
private Long id;
private String name;
private String phone;
private String email;
@Override
public void append(QueryMap.Builder builder) {
builder.put("id", id).put("name", name).put("phone", phone).put("email", email);
}
}

View File

@ -0,0 +1,19 @@
package cn.axzo.riven.client.model;
import lombok.Data;
@Data
public class UpdateUserReq {
private String name;
private Integer sex;
private Integer age;
private String phone;
private String email;
private String address;
}

View File

@ -0,0 +1,21 @@
package cn.axzo.riven.client.model;
import lombok.Data;
@Data
public class UserRes {
private Long id;
private String name;
private Integer sex;
private Integer age;
private String phone;
private String email;
private String address;
}

View File

@ -0,0 +1,14 @@
package cn.axzo.maven.archetype.client;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Unit test for simple App.
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class AppTest {
}

66
riven-server/pom.xml Normal file
View File

@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>riven</artifactId>
<groupId>cn.axzo</groupId>
<version>${revision}</version>
<relativePath>../pom.xml</relativePath>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>riven-server</artifactId>
<packaging>jar</packaging>
<name>riven-server</name>
<properties>
<!--不打包发布server模块-->
<maven.test.skip>true</maven.test.skip>
<maven.deploy.skip>true</maven.deploy.skip>
<maven.deploy.skip>true</maven.deploy.skip>
</properties>
<dependencies>
<dependency>
<groupId>cn.axzo</groupId>
<artifactId>riven-api</artifactId>
<version>${project.version}</version>
</dependency>
<dependency>
<groupId>cn.axzo.framework</groupId>
<artifactId>axzo-web-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.axzo.framework</groupId>
<artifactId>axzo-spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.axzo.framework</groupId>
<artifactId>axzo-consumer-spring-cloud-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.axzo.framework</groupId>
<artifactId>axzo-processor-spring-boot-starter</artifactId>
</dependency>
<!--mybatis-plus-->
<dependency>
<groupId>cn.axzo.framework</groupId>
<artifactId>axzo-mybatisplus-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.axzo.framework</groupId>
<artifactId>axzo-logger-spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
</dependencies>
</project>

View File

@ -0,0 +1,36 @@
package cn.axzo.riven;
import lombok.extern.slf4j.Slf4j;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.core.env.Environment;
@Slf4j
@MapperScan(value = {"cn.axzo.riven.repository.mapper"})
@SpringBootApplication(scanBasePackages = "cn.axzo")
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext run = SpringApplication.run(Application.class, args);
Environment env = run.getEnvironment();
log.info(
"--------------------------------------------------------------------------------------------------------------------\n" +
"Application 【{}】 is running on 【{}】 environment!\n" +
"Api Local: \thttp://127.0.0.1:{}\n" +
"Mysql: \t{}\t username:{}\n" +
"Redis: \t{}:{}\t database:{}\n" +
"RabbitMQ: \t{}\t username:{}",
env.getProperty("spring.application.name"),
env.getProperty("spring.profiles.active"),
env.getProperty("server.port"),
env.getProperty("spring.datasource.url"),
env.getProperty("spring.datasource.username"),
env.getProperty("spring.redis.host"),
env.getProperty("spring.redis.port"),
env.getProperty("spring.redis.database"),
env.getProperty("spring.rabbitmq.addresses"),
env.getProperty("spring.rabbitmq.username") +
"\n----------------------------------------------------------");
}
}

View File

@ -0,0 +1,27 @@
package cn.axzo.riven.common.enums;
import cn.axzo.framework.domain.web.code.IProjectRespCode;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
* @Author: liyong.tian
* @Date: 2022/9/5
* @Description: 响应码规范一共8位取值范围0~93位项目编号(首位不能为0)+2位模块编号+3位自定义编号
*/
@Getter
@AllArgsConstructor
public enum ErrorCode implements IProjectRespCode {
USER_NOT_EXISTS("01001", "用户不存在id=%s"),
USER_PHONE_EMAIL_IS_NULL("01002", "电话和邮箱不能都为空");
private String code;
private String message;
@Override
public String getProjectCode() {
// 根据不同项目进行项目编码调整可联系框架组获取项目编号(首位不能为0)
return "100";
}
}

View File

@ -0,0 +1,88 @@
package cn.axzo.riven.common.enums;
/**
* @Author: liyong.tian
* @Date: 2022/9/5
* @Description:
*/
public enum ResultCode {
/**
* 成功 [GET]
*/
SUCCESS(200),
/**
* [POST/PUT/PATCH] 用户新建或修改数据成功
*/
CREATED(201),
/**
* [*] 标识一个请求已经进入后台排队 (异步任务)
*/
ACCEPTED(202),
/**
* [DELETE]: 用户删除数据成功
*/
NO_CONTENT(204),
/**
* [POST/PUT/PATCH] 用户发出的请求有错误, 服务器没有进行新建或修改数据的操作, 该操作是幂等的.
*/
FAIL(400),
/**
* [*] 标识没有权限 (令牌用户名密码错误)
*/
UNAUTHORIZED(401),
/**
* [*] 标识用户得到授权(与401错误相对), 但是访问是被禁止的
*/
FORBIDDEN(403),
/**
* [*] 用户发出的请求针对的是不存在的记录, 服务器没有进行操作
*/
NOT_FOUND(404),
/**
* [GET] 用户请求的格式不可得 (比如用户请求JSON格式, 但是只有XML格式)
*/
NOT_ACCEPTABLE(406),
/**
* [GET] 用户请求的资源被永久删除, 且不会再得到
*/
GONE(410),
/**
* [POST/PUT/PATCH] 当创建一个对象时, 发生一个验证错误646
*/
UNPROCESSABLE_ENTITY(422),
/**
* 服务器内部错误
*/
INTERNAL_SERVER_ERROR(9999),
/**
* 通用业务异常
*/
SERVICE_EXCEPTION_ERROR(9998),
/**
* ####业务的响应码####
* 按业务依次划分 :
* 一共6位, 第6位是业务代码 第1-5位响应码, 按业务不同码不同
* #100000 全局级别
*/
/**
* 100001 当前用户被强制下线
*/
CUSTOM_100001(100001),
/**
* 确认弹窗响应码
*/
CUSTOM_100002(100002);
public int code;
ResultCode(int code) {
this.code = code;
}
public int getCode() {
return code;
}
}

View File

@ -0,0 +1 @@
package cn.axzo.riven.common.util;

View File

@ -0,0 +1,173 @@
package cn.axzo.riven.config;
import cn.axzo.framework.domain.ServiceException;
import cn.azxo.framework.common.constatns.Constants;
import cn.hutool.core.util.ReflectUtil;
import com.google.common.collect.Lists;
import feign.RequestInterceptor;
import feign.RequestTemplate;
import feign.Target.HardCodedTarget;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.MDC;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import java.lang.reflect.Field;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @Author: liyong.tian
* @Date: 2023/8/29 14:50
* @Description:
*/
@Slf4j
@Configuration
public class FeignConfiguration implements RequestInterceptor {
@Autowired
private Environment environment;
private static final String FEIGN_URL_REGEX = "http://([A-Za-z-|_]+)(:[1-9]\\d{1,4}[/]?$)?";
private static final String LOCAL_HOST = "localhost";
private static final String DEV_HOST = "http://dev-app.axzo.cn/";
private static final String TEST_HOST = "http://test-api.axzo.cn/";
private static final String TEST1_HOST = "http://test1-api.axzo.cn/";
private static final String PRE_HOST = "http://pre-api.axzo.cn/";
private static final String PRE_NEW_HOST = "http://pre-new-api.axzo.cn/";
private static final String UAT_HOST = "http://uat-api.axzo.cn/";
private static final List<String> HOSTS = Lists
.newArrayList(LOCAL_HOST, DEV_HOST, TEST_HOST, TEST1_HOST, PRE_HOST, PRE_NEW_HOST, UAT_HOST);
private static String POD_NAMESPACE;
static {
Map<String, String> env = System.getenv();
if (env != null) {
POD_NAMESPACE = env.get("MY_POD_NAMESPACE");
}
log.info("init FeignConfig, POD_NAMESPACE value is {}", POD_NAMESPACE);
}
@SneakyThrows
@Override
public void apply(RequestTemplate requestTemplate) {
// POD_NAMESPACE为空 说明服务没有运行在rancher中则进行url替换POD_NAMESPACE不为空时服务间通过Istio通信则不需要替换url
if (POD_NAMESPACE == null) {
HardCodedTarget<?> target = (HardCodedTarget<?>) requestTemplate.feignTarget();
Field field = ReflectUtil.getField(target.getClass(), "url");
field.setAccessible(true);
String url = (String) field.get(target);
// 如需要调用开发者本地启动的xxx服务传入到toLocalServiceNames
List<String> toLocalServiceNames = Lists.newArrayList("");
String convertUrl = convertUrl(url, toLocalServiceNames);
field.set(target, convertService(convertUrl));
target.apply(requestTemplate);
}
requestTemplate.header(Constants.CTX_LOG_ID_MDC, MDC.get(Constants.CTX_LOG_ID_MDC));
requestTemplate.header("X-SERVER-NAME", environment.getProperty("spring.application.name"));
}
/**
* 某些服务URL需要特殊处理
*/
private String convertService(String url) {
if (url.contains("data-collection")) {
return url.replaceAll("data-collection", "dataCollection");
} else if (url.contains("iot-hub")) {
return url.replaceAll("iot-hub", "iotHub");
}
return url;
}
/**
* 将feign api中的url替换为其他环境
*/
private String convertUrl(String url, List<String> toLocalServiceNames) {
// 已经替换过的 不再进行替换了
if (HOSTS.stream().anyMatch(url::contains)) {
return url;
}
String convertUrl = toLocalHost(url, toLocalServiceNames);
if (!convertUrl.equals(url)) {
return convertUrl;
}
// 环境为空或为生产环境不替换
String profile = environment.getProperty("spring.profiles.active");
if (profile == null) {
return url;
}
// 调用profile对应环境的服务
switch (profile.toLowerCase()) {
case "local":
return toLocalHost(url);
case "dev":
return toRemoteHost(url, DEV_HOST);
case "test":
return toRemoteHost(url, TEST_HOST);
case "test1":
return toRemoteHost(url, TEST1_HOST);
case "pre":
return toRemoteHost(url, PRE_HOST);
case "pre-new":
return toRemoteHost(url, PRE_NEW_HOST);
case "uat":
return toRemoteHost(url, UAT_HOST);
default:
throw new ServiceException("不支持该环境的feign api调用");
}
}
/**
* 调用本地启动的服务
*
* @param url 原url
* @param toLocalServiceNames 需要切换到本地的服务名
*/
private static String toLocalHost(String url, List<String> toLocalServiceNames) {
if (CollectionUtils.isEmpty(toLocalServiceNames)) {
return url;
}
if (toLocalServiceNames.stream().anyMatch(o -> StringUtils.isNotBlank(o) && url.contains(o))) {
return toLocalHost(url);
}
return url;
}
/**
* 调用本地启动的服务http://apollo:11000 -> http://localhost:11000
*/
private static String toLocalHost(String url) {
String serviceName = getServiceNameFromUrl(url);
// 替换服务名为localhost
return serviceName == null ? url : url.replaceAll(serviceName, LOCAL_HOST);
}
/**
* 调用远程指定环境的服务http://apollo:11000 -> http://dev-app.axzo.cn/apollo
*/
private static String toRemoteHost(String url, String host) {
String serviceName = getServiceNameFromUrl(url);
// 拼接远端host到url
return serviceName == null ? url : host + serviceName;
}
/**
* 提取url中的服务名
*/
private static String getServiceNameFromUrl(String url) {
Pattern pattern = Pattern.compile(FEIGN_URL_REGEX);
Matcher matcher = pattern.matcher(url);
String serviceName = null;
if (matcher.matches()) {
serviceName = matcher.group(1);
}
return serviceName;
}
}

View File

@ -0,0 +1,259 @@
package cn.axzo.riven.config.filter;
import cn.azxo.framework.common.constatns.Constants;
import cn.azxo.framework.common.utils.LogUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONException;
import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.annotation.JSONField;
import com.google.common.base.Strings;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.apache.skywalking.apm.toolkit.trace.Trace;
import org.apache.skywalking.apm.toolkit.trace.TraceContext;
import org.slf4j.MDC;
import org.springframework.core.Ordered;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Component;
import org.springframework.web.filter.OncePerRequestFilter;
import org.springframework.web.util.ContentCachingRequestWrapper;
import org.springframework.web.util.ContentCachingResponseWrapper;
import org.springframework.web.util.WebUtils;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.util.*;
/**
* @Author: liyong.tian
* @Date: 2022/12/6 14:48
* @Description: Http接口日志记录
*/
@Slf4j
@Component
public class HttpTraceLogFilter extends OncePerRequestFilter implements Ordered {
private static final String X_REQUEST_ID = "x-request-id";
@Override
public int getOrder() {
return Ordered.LOWEST_PRECEDENCE - 10;
}
@Override
@Trace(operationName = "HttpTraceLogFilter")
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response,
FilterChain filterChain) throws ServletException, IOException {
String uri = request.getRequestURI();
String contextPath = request.getContextPath();
String url = uri.substring(contextPath.length());
//swagger 跳过
if (url.contains("api-docs") || url.contains("swagger") || url.contains("checkDeath")) {
filterChain.doFilter(request, response);
return;
}
//静态资源 跳过
if (url.contains(".")) {
filterChain.doFilter(request, response);
return;
}
if (!(request instanceof ContentCachingRequestWrapper)) {
request = new ContentCachingRequestWrapper(request);
}
if (!(response instanceof ContentCachingResponseWrapper)) {
response = new ContentCachingResponseWrapper(response);
}
String requestId = request.getHeader(X_REQUEST_ID);
if (Strings.isNullOrEmpty(requestId)) {
MDC.put(X_REQUEST_ID, getTraceId());
} else {
MDC.put(X_REQUEST_ID, requestId);
}
String ctxLogId = request.getHeader(Constants.CTX_LOG_ID_MDC);
if (Strings.isNullOrEmpty(ctxLogId)) {
MDC.put(Constants.CTX_LOG_ID_MDC, getTraceId());
} else {
MDC.put(Constants.CTX_LOG_ID_MDC, ctxLogId);
}
// 获取请求参数
String parameter = null;
String requestContentType = request.getHeader(HttpHeaders.CONTENT_TYPE);
if (requestContentType != null) {
if (requestContentType.startsWith(MediaType.APPLICATION_JSON_VALUE)) {
//Json
WrappedHttpServletRequest requestWrapper = new WrappedHttpServletRequest(request);
parameter = getRequestBody(requestWrapper);
request = requestWrapper;
} else if (requestContentType.startsWith(MediaType.APPLICATION_FORM_URLENCODED_VALUE)) {
//普通表单提交
parameter = JSON.toJSONString(request.getParameterMap());
} else if (requestContentType.startsWith(MediaType.MULTIPART_FORM_DATA_VALUE)) {
//文件表单提交
parameter = JSON.toJSONString("文件类型");
}
} else if (url.startsWith("/api")) {
if ("GET".equals(request.getMethod())) {
parameter = JSON.toJSONString(request.getParameterMap());
} else if ("POST".equals(request.getMethod())) {
WrappedHttpServletRequest requestWrapper = new WrappedHttpServletRequest(request);
parameter = getRequestBody(requestWrapper);
request = requestWrapper;
}
} else if ("GET".equals(request.getMethod())) {
parameter = JSON.toJSONString(request.getParameterMap());
}
long requestTime = System.currentTimeMillis();
try {
filterChain.doFilter(request, response);
} finally {
response.setHeader(Constants.CTX_LOG_ID_MDC, MDC.get(Constants.CTX_LOG_ID_MDC));
response.setHeader(X_REQUEST_ID, MDC.get(Constants.CTX_LOG_ID_MDC));
long latency = System.currentTimeMillis() - requestTime;
String responseBody = null;
int responseStatus = response.getStatus();
String responseContentType = response.getContentType();
//Json
if (responseContentType != null && responseContentType
.startsWith(MediaType.APPLICATION_JSON_VALUE)) {
responseBody = getResponseBody(response);
}
//记录日志
HttpTraceLog traceLog = new HttpTraceLog();
traceLog.setRequestContentType(requestContentType);
traceLog.setPath(url);
traceLog.setMethod(request.getMethod());
traceLog.setTimeTaken(latency);
traceLog.setParameter(parameter);
traceLog.setResponseContentType(responseContentType);
traceLog.setStatus(responseStatus);
traceLog.setResponseBody(responseBody);
traceLog.setRequestHeaders(getRequestHeader(request));
if (traceLog.getResponseCode() != null && traceLog.getResponseCode().equals(9999)) {
LogUtil.error(LogUtil.ErrorLevel.P0, LogUtil.ErrorType.ERROR_BUSINESS, JSON.toJSONString(traceLog));
} else if (traceLog.getResponseCode() != null && traceLog.getResponseCode().equals(9998)) {
log.warn(JSON.toJSONString(traceLog));
} else {
log.info(JSON.toJSONString(traceLog));
}
updateResponse(response);
// 清理链路id
MDC.clear();
}
}
private String getTraceId() {
String contextTraceId = TraceContext.traceId();
return Strings.isNullOrEmpty(contextTraceId)
? UUID.randomUUID().toString().replaceAll("-", "") : contextTraceId;
}
private String getRequestBody(WrappedHttpServletRequest request) throws IOException {
// 获取请求参数
return request.getRequestParams();
}
@Trace(operationName = "HttpTraceLogFilter#getResponseBody")
private String getResponseBody(HttpServletResponse response) {
String responseBody = null;
ContentCachingResponseWrapper wrapper = WebUtils
.getNativeResponse(response, ContentCachingResponseWrapper.class);
if (wrapper != null) {
responseBody = new String(wrapper.getContentAsByteArray(), StandardCharsets.UTF_8);
}
return responseBody;
}
public Map<String, String> getRequestHeader(HttpServletRequest request) {
Map<String, String> ret = new HashMap<>();
Enumeration<String> headerNames = request.getHeaderNames();
while (headerNames.hasMoreElements()) {
String headerName = headerNames.nextElement();
ret.put(headerName, request.getHeader(headerName));
}
return ret;
}
@Trace(operationName = "HttpTraceLogFilter#updateResponse")
private void updateResponse(HttpServletResponse response) throws IOException {
ContentCachingResponseWrapper responseWrapper = WebUtils
.getNativeResponse(response, ContentCachingResponseWrapper.class);
Objects.requireNonNull(responseWrapper).copyBodyToResponse();
}
@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
private static class HttpTraceLog {
/**
* 路径
*/
private String path;
/**
* 参数
*/
@JSONField(jsonDirect = true)
private String parameter;
private String requestContentType;
private String responseContentType;
private String method;
private Long timeTaken;
private Integer status;
/**
* 业务Code
*/
private Integer responseCode;//业务返回码
/**
* 响应参数
*/
@JSONField(jsonDirect = true)
private String responseBody;
@JSONField(jsonDirect = true)
private Map<String, String> requestHeaders;
public String getParameter() {
if (parameter == null) {
return parameter;
} else {
return parameter.replaceAll("\n", "").replaceAll("\t", "").replaceAll("\r", "");
}
}
public String getResponseBody() {
if (responseBody == null) {
return responseBody;
} else {
return responseBody.replaceAll("\n", "").replaceAll("\t", "").replaceAll("\r", "");
}
}
public void setResponseBody(String responseBody) {
if (StringUtils.isBlank(responseBody)) {
return;
}
this.responseBody = responseBody;
JSONObject responseJson = null;
try {
responseJson = JSONObject.parseObject(responseBody);
this.responseCode = responseJson.getInteger("code");
} catch (JSONException e) {
log.debug("ResponseBody非JSON返回", e);
}
}
}
}

View File

@ -0,0 +1,91 @@
package cn.axzo.riven.config.filter;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import javax.servlet.ReadListener;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import org.apache.commons.io.IOUtils;
/**
* @Author: liyong.tian
* @Date: 2022/12/6 14:56
* @Description:
*/
public class WrappedHttpServletRequest extends HttpServletRequestWrapper {
private byte[] bytes;
private WrappedServletInputStream wrappedServletInputStream;
public WrappedHttpServletRequest(HttpServletRequest request) throws IOException {
super(request);
// 读取输入流里的请求参数并保存到bytes里
bytes = IOUtils.toByteArray(request.getInputStream());
ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(bytes);
this.wrappedServletInputStream = new WrappedServletInputStream(byteArrayInputStream);
// 很重要把post参数重新写入请求流
reWriteInputStream();
}
/**
* 把参数重新写进请求里
*/
public void reWriteInputStream() {
wrappedServletInputStream
.setStream(new ByteArrayInputStream(bytes != null ? bytes : new byte[0]));
}
@Override
public ServletInputStream getInputStream() throws IOException {
return wrappedServletInputStream;
}
@Override
public BufferedReader getReader() throws IOException {
return new BufferedReader(new InputStreamReader(wrappedServletInputStream));
}
/**
* 获取post参数可以自己再转为相应格式
*/
public String getRequestParams() throws IOException {
return new String(bytes, this.getCharacterEncoding());
}
private class WrappedServletInputStream extends ServletInputStream {
private InputStream stream;
public WrappedServletInputStream(InputStream stream) {
this.stream = stream;
}
public void setStream(InputStream stream) {
this.stream = stream;
}
@Override
public int read() throws IOException {
return stream.read();
}
@Override
public boolean isFinished() {
return true;
}
@Override
public boolean isReady() {
return true;
}
@Override
public void setReadListener(ReadListener readListener) {
}
}
}

View File

@ -0,0 +1 @@
package cn.axzo.riven.consumer;

View File

@ -0,0 +1,22 @@
package cn.axzo.riven.controller;
import cn.axzo.framework.web.http.ApiResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author: liyong.tian
* @Date: 2022/11/25 18:00
* @Description: 健康检查接口
*/
@RestController
public class HealthCheckController {
/**
* 探活
*/
@GetMapping("/checkDeath")
public ApiResponse<String> checkDeath() {
return ApiResponse.ok("ok");
}
}

View File

@ -0,0 +1 @@
package cn.axzo.riven.controller.app;

View File

@ -0,0 +1,74 @@
package cn.axzo.riven.controller.web;
import cn.axzo.riven.service.user.UserService;
import cn.axzo.riven.service.dto.request.user.NewUserDTO;
import cn.axzo.riven.service.dto.request.user.UpdateUserDTO;
import cn.axzo.riven.service.dto.request.user.UserQO;
import cn.axzo.riven.service.dto.response.user.UserVO;
import cn.azxo.framework.common.model.CommonPageResponse;
import cn.azxo.framework.common.model.CommonResponse;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/**
* @Author: liyong.tian
* @Date: 2022/9/2
* @Description:
*/
@Slf4j
@Api(tags = "web-用户信息接口")
@RequestMapping("/api/v1")
@RestController
@RequiredArgsConstructor
public class UserController {
private final UserService userService;
@ApiOperation(value = "创建用户")
@PostMapping("/users")
public CommonResponse<UserVO> createUser(@Valid @RequestBody NewUserDTO dto) {
log.info("REST request to save user : {}", dto);
// 校验入参
dto.valid();
UserVO result = userService.create(dto);
return CommonResponse.success(result);
}
@ApiOperation(value = "修改用户")
@PutMapping("/users/{id}")
public CommonResponse<UserVO> updateUser(@ApiParam("用户ID") @PathVariable Long id,
@Valid @RequestBody UpdateUserDTO dto) {
log.info("REST request to update user : {}", dto);
// 校验入参
dto.valid();
UserVO result = userService.update(id, dto);
return CommonResponse.success(result);
}
@ApiOperation("获取用户列表")
@GetMapping("/users")
public CommonResponse<CommonPageResponse<UserVO>> getUsers(@Valid UserQO userQO) {
CommonPageResponse<UserVO> results = userService.queryByPage(userQO);
return CommonResponse.success(results);
}
@ApiOperation("获取用户详情")
@GetMapping("/users/{id}")
public CommonResponse getUser(@ApiParam("用户ID") @PathVariable Long id) {
UserVO result = userService.getOne(id);
return CommonResponse.success(result);
}
@ApiOperation("删除用户")
@DeleteMapping("/users/{id}")
public CommonResponse deleteUser(@ApiParam("用户ID") @PathVariable Long id) {
userService.delete(id);
return CommonResponse.success();
}
}

View File

@ -0,0 +1,84 @@
package cn.axzo.riven.controller.web;
import cn.axzo.framework.domain.page.PageQO;
import cn.axzo.framework.domain.page.PageResp;
import cn.axzo.framework.web.http.ApiResponse;
import cn.axzo.framework.web.http.ApiPageResponse;
import cn.axzo.riven.service.dto.request.user.NewUserDTO;
import cn.axzo.riven.service.dto.request.user.UpdateUserDTO;
import cn.axzo.riven.service.dto.request.user.UserQO1;
import cn.axzo.riven.service.dto.response.user.UserRefVO;
import cn.axzo.riven.service.dto.response.user.UserVO;
import cn.axzo.riven.service.user.UserService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
/**
* @Author: liyong.tian
* @Date: 2022/10/28
* @Description: 新项目搭建推荐方式
*/
@Slf4j
@Api(tags = "web-用户信息接口")
@RequestMapping("/api/v2")
@RestController
@RequiredArgsConstructor
public class UserResource {
private final UserService userService;
@ApiOperation(value = "创建用户")
@PostMapping("/users")
public ApiResponse<UserVO> createUser(@Valid @RequestBody NewUserDTO dto) {
log.info("REST request to save user : {}", dto);
// 校验入参
dto.valid();
UserVO result = userService.create(dto);
return ApiResponse.ok(result);
}
@ApiOperation(value = "修改用户")
@PutMapping("/users/{id}")
public ApiResponse<UserVO> updateUser(@ApiParam("用户ID") @PathVariable Long id,
@Valid @RequestBody UpdateUserDTO dto) {
log.info("REST request to update user : {}", dto);
// 校验入参
dto.valid();
UserVO result = userService.update(id, dto);
return ApiResponse.ok(result);
}
@ApiOperation("获取用户列表")
@GetMapping("/users")
public ApiPageResponse<UserVO> getUsers(@ModelAttribute UserQO1 userQo, PageQO page) {
PageResp<UserVO> results = userService.find(userQo, page);
return ApiPageResponse.ok(results);
}
@ApiOperation("获取用户信息及地址列表")
@GetMapping("/users/adress")
public ApiPageResponse<UserRefVO> getUserAddress(@ModelAttribute UserQO1 userQo, PageQO page) {
PageResp<UserRefVO> results = userService.queryUserAddress(userQo, page);
return ApiPageResponse.ok(results);
}
@ApiOperation("获取用户详情")
@GetMapping("/users/{id}")
public ApiResponse<UserVO> getUser(@ApiParam("用户ID") @PathVariable Long id) {
UserVO result = userService.getOne(id);
return ApiResponse.ok(result);
}
@ApiOperation("删除用户")
@DeleteMapping("/users/{id}")
public ApiResponse deleteUser(@ApiParam("用户ID") @PathVariable Long id) {
userService.delete(id);
return ApiResponse.ok();
}
}

View File

@ -0,0 +1 @@
package cn.axzo.riven.job;

View File

@ -0,0 +1,78 @@
package cn.axzo.riven.repository;
import cn.axzo.framework.domain.page.PageQO;
import cn.axzo.riven.repository.entity.address.AddressDO;
import cn.axzo.riven.repository.entity.user.UserDO;
import cn.axzo.riven.repository.entity.user.UserRefDO;
import cn.axzo.riven.repository.mapper.UserMapper;
import cn.axzo.riven.repository.mapper.UserReferenceMapper;
import cn.axzo.riven.service.dto.request.user.UserQO;
import cn.axzo.riven.service.dto.request.user.UserQO1;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.yulichang.toolkit.MPJWrappers;
import com.github.yulichang.wrapper.MPJLambdaWrapper;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Repository;
/**
* @Author: liyong.tian
* @Date: 2022/9/5
* @Description:
*/
@Repository
@RequiredArgsConstructor
public class UserDao extends ServiceImpl<UserMapper, UserDO> {
private final UserMapper userMapper;
private final UserReferenceMapper userReferenceMapper;
public UserDO findById(Long id) {
return userMapper.selectById(id);
}
public void delete(Long id) {
userMapper.deleteById(id);
}
public IPage<UserDO> queryByPage(UserQO userQO) {
return userMapper.selectPage(userQO.toPage(),
Wrappers.lambdaQuery(UserDO.class)
.eq(userQO.getId() != null, UserDO::getId, userQO.getId())
.like(StringUtils.isNotBlank(userQO.getName()), UserDO::getName, userQO.getName())
.like(StringUtils.isNotBlank(userQO.getPhone()), UserDO::getPhone, userQO.getPhone())
.like(StringUtils.isNotBlank(userQO.getEmail()), UserDO::getEmail, userQO.getEmail())
.orderByDesc(UserDO::getCreateAt)
);
}
public IPage<UserDO> find(UserQO1 userQO, PageQO page) {
return userMapper.selectPage(page.toPage(),
Wrappers.lambdaQuery(UserDO.class)
.eq(userQO.getId() != null, UserDO::getId, userQO.getId())
.like(StringUtils.isNotBlank(userQO.getName()), UserDO::getName, userQO.getName())
.like(StringUtils.isNotBlank(userQO.getPhone()), UserDO::getPhone, userQO.getPhone())
.like(StringUtils.isNotBlank(userQO.getEmail()), UserDO::getEmail, userQO.getEmail())
.orderByDesc(UserDO::getCreateAt)
);
}
public IPage<UserRefDO> findUserAddress(UserQO1 userQO, PageQO page) {
MPJLambdaWrapper<UserDO> wrapper = MPJWrappers.<UserDO>lambdaJoin()
.selectAll(UserDO.class)
.select(AddressDO::getProvince)
.select(AddressDO::getCity)
.select(AddressDO::getDistrict)
.select(AddressDO::getStreet)
.leftJoin(AddressDO.class, AddressDO::getUserId, UserDO::getId)
.eq(userQO.getId() != null, UserDO::getId, userQO.getId())
.like(StringUtils.isNotBlank(userQO.getName()), UserDO::getName, userQO.getName())
.like(StringUtils.isNotBlank(userQO.getPhone()), UserDO::getPhone, userQO.getPhone())
.like(StringUtils.isNotBlank(userQO.getEmail()), UserDO::getEmail, userQO.getEmail())
.orderByDesc(UserDO::getCreateAt);
return userReferenceMapper.selectJoinPage(page.toPage(), UserRefDO.class, wrapper);
}
}

View File

@ -0,0 +1,25 @@
package cn.axzo.riven.repository.entity.address;
import cn.axzo.framework.data.mybatisplus.model.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* @Author: liyong.tian
* @Date: 2023/8/4 16:35
* @Description:
*/
@Data
@TableName("b_address")
public class AddressDO extends BaseEntity<AddressDO> {
private String province;
private String city;
private String district;
private String street;
private Long userId;
}

View File

@ -0,0 +1 @@
package cn.axzo.riven.repository.entity;

View File

@ -0,0 +1,27 @@
package cn.axzo.riven.repository.entity.user;
import cn.axzo.framework.data.mybatisplus.model.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* @Author: liyong.tian
* @Date: 2022/9/5
* @Description:
*/
@Data
@TableName("b_user")
public class User extends BaseEntity<User> {
private String name;
private Integer sex;
private Integer age;
private String phone;
private String email;
private String address;
}

View File

@ -0,0 +1,27 @@
package cn.axzo.riven.repository.entity.user;
import cn.axzo.framework.data.mybatisplus.model.BaseEntity;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
/**
* @Author: liyong.tian
* @Date: 2022/9/5
* @Description:
*/
@Data
@TableName("b_user")
public class UserDO extends BaseEntity<UserDO> {
private String name;
private Integer sex;
private Integer age;
private String phone;
private String email;
private String address;
}

View File

@ -0,0 +1,32 @@
package cn.axzo.riven.repository.entity.user;
import lombok.Data;
/**
* @Author: liyong.tian
* @Date: 2023/8/4 16:40
* @Description:
*/
@Data
public class UserRefDO {
private Long id;
private String name;
private Integer sex;
private Integer age;
private String phone;
private String email;
private String province;
private String city;
private String district;
private String street;
}

View File

@ -0,0 +1,14 @@
package cn.axzo.riven.repository.mapper;
import cn.axzo.riven.repository.entity.address.AddressDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* @Author: liyong.tian
* @Date: 2023/8/8 10:33
* @Description:
*/
@Mapper
public interface AddressMapper extends BaseMapper<AddressDO> {
}

View File

@ -0,0 +1,14 @@
package cn.axzo.riven.repository.mapper;
import cn.axzo.riven.repository.entity.user.UserDO;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* @Author: liyong.tian
* @Date: 2022/9/5
* @Description:
*/
@Mapper
public interface UserMapper extends BaseMapper<UserDO> {
}

View File

@ -0,0 +1,14 @@
package cn.axzo.riven.repository.mapper;
import cn.axzo.riven.repository.entity.user.UserDO;
import com.github.yulichang.base.MPJBaseMapper;
import org.apache.ibatis.annotations.Mapper;
/**
* @Author: liyong.tian
* @Date: 2023/8/4 16:49
* @Description:
*/
@Mapper
public interface UserReferenceMapper extends MPJBaseMapper<UserDO> {
}

View File

@ -0,0 +1 @@
package cn.axzo.riven.service.client;

View File

@ -0,0 +1,15 @@
package cn.axzo.riven.service.converter;
import java.util.List;
/**
* @Author: liyong.tian
* @Date: 2022/9/5
* @Description:
*/
public interface EntityConverter<V, E>{
V toVo(E var);
List<V> toVo(List<E> var);
}

View File

@ -0,0 +1,26 @@
package cn.axzo.riven.service.converter;
import cn.axzo.riven.service.dto.request.user.NewUserDTO;
import cn.axzo.riven.service.dto.request.user.UpdateUserDTO;
import cn.axzo.riven.service.dto.response.user.UserVO;
import cn.axzo.riven.repository.entity.user.UserDO;
import org.mapstruct.Mapper;
import org.mapstruct.MappingTarget;
import static org.mapstruct.NullValueCheckStrategy.ALWAYS;
/**
* @Author: liyong.tian
* @Date: 2022/9/2
* @Description:
*/
@Mapper(
componentModel = "spring",
nullValueCheckStrategy = ALWAYS
)
public interface UserConverter extends EntityConverter<UserVO, UserDO> {
UserDO toEntity(NewUserDTO dto);
void updateEntity(UpdateUserDTO dto, @MappingTarget UserDO user);
}

View File

@ -0,0 +1,19 @@
package cn.axzo.riven.service.converter;
import cn.axzo.riven.repository.entity.user.UserRefDO;
import cn.axzo.riven.service.dto.response.user.UserRefVO;
import org.mapstruct.Mapper;
import static org.mapstruct.NullValueCheckStrategy.ALWAYS;
/**
* @Author: liyong.tian
* @Date: 2023/8/4 17:02
* @Description:
*/
@Mapper(
componentModel = "spring",
nullValueCheckStrategy = ALWAYS
)
public interface UserRefConverter extends EntityConverter<UserRefVO, UserRefDO> {
}

View File

@ -0,0 +1 @@
package cn.axzo.riven.service.dto.request;

View File

@ -0,0 +1,41 @@
package cn.axzo.riven.service.dto.request.user;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
/**
* @Author: liyong.tian
* @Date: 2022/9/2
* @Description:
*/
@Data
public class NewUserDTO {
@ApiModelProperty(value = "名称", position = 1)
@NotBlank(message = "名称不能为空")
private String name;
@ApiModelProperty(value = "性别", position = 2)
@NotNull
private Integer sex;
@ApiModelProperty(value = "年龄", position = 3)
private Integer age;
@ApiModelProperty(value = "电话", position = 4)
private String phone;
@ApiModelProperty(value = "邮箱", position = 5)
private String email;
public void valid() {
// 电话和邮箱不能都为空
if (StringUtils.isEmpty(phone) && StringUtils.isEmpty(email)) {
throw new RuntimeException("电话和邮箱不能都为空");
}
}
}

View File

@ -0,0 +1,44 @@
package cn.axzo.riven.service.dto.request.user;
import cn.axzo.framework.domain.web.ApiException;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import org.apache.commons.lang3.StringUtils;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import static cn.axzo.riven.common.enums.ErrorCode.USER_PHONE_EMAIL_IS_NULL;
/**
* @Author: liyong.tian
* @Date: 2022/9/5
* @Description:
*/
@Data
public class UpdateUserDTO {
@ApiModelProperty(value = "名称", position = 1)
@NotBlank(message = "名称不能为空")
private String name;
@ApiModelProperty(value = "性别", position = 2)
@NotNull
private Integer sex;
@ApiModelProperty(value = "年龄", position = 3)
private Integer age;
@ApiModelProperty(value = "电话", position = 4)
private String phone;
@ApiModelProperty(value = "邮箱", position = 5)
private String email;
public void valid() {
// 电话和邮箱不能都为空
if (StringUtils.isEmpty(phone) && StringUtils.isEmpty(email)) {
throw new ApiException(USER_PHONE_EMAIL_IS_NULL);
}
}
}

View File

@ -0,0 +1,26 @@
package cn.axzo.riven.service.dto.request.user;
import cn.axzo.framework.domain.page.PageQO;
import io.swagger.annotations.ApiParam;
import lombok.Data;
/**
* @Author: liyong.tian
* @Date: 2022/9/5
* @Description:
*/
@Data
public class UserQO extends PageQO {
@ApiParam("主键")
private Long id;
@ApiParam("姓名")
private String name;
@ApiParam("手机")
private String phone;
@ApiParam("邮箱")
private String email;
}

View File

@ -0,0 +1,25 @@
package cn.axzo.riven.service.dto.request.user;
import io.swagger.annotations.ApiParam;
import lombok.Data;
/**
* @Author: liyong.tian
* @Date: 2022/9/5
* @Description:
*/
@Data
public class UserQO1 {
@ApiParam("主键")
private Long id;
@ApiParam("姓名")
private String name;
@ApiParam("手机")
private String phone;
@ApiParam("邮箱")
private String email;
}

View File

@ -0,0 +1 @@
package cn.axzo.riven.service.dto.response;

View File

@ -0,0 +1,43 @@
package cn.axzo.riven.service.dto.response.user;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @Author: liyong.tian
* @Date: 2023/8/4 16:54
* @Description:
*/
@Data
public class UserRefVO {
@ApiModelProperty(value = "id", position = 1)
private Long id;
@ApiModelProperty(value = "名称", position = 2)
private String name;
@ApiModelProperty(value = "性别", position = 3)
private Integer sex;
@ApiModelProperty(value = "年龄", position = 4)
private Integer age;
@ApiModelProperty(value = "电话", position = 5)
private String phone;
@ApiModelProperty(value = "邮箱", position = 6)
private String email;
@ApiModelProperty(value = "省份", position = 7)
private String province;
@ApiModelProperty(value = "城市", position = 8)
private String city;
@ApiModelProperty(value = "区域", position = 9)
private String district;
@ApiModelProperty(value = "街道", position = 10)
private String street;
}

View File

@ -0,0 +1,31 @@
package cn.axzo.riven.service.dto.response.user;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
/**
* @Author: liyong.tian
* @Date: 2022/9/2
* @Description:
*/
@Data
public class UserVO {
@ApiModelProperty(value = "id", position = 1)
private Long id;
@ApiModelProperty(value = "名称", position = 2)
private String name;
@ApiModelProperty(value = "性别", position = 3)
private Integer sex;
@ApiModelProperty(value = "年龄", position = 4)
private Integer age;
@ApiModelProperty(value = "电话", position = 5)
private String phone;
@ApiModelProperty(value = "邮箱", position = 6)
private String email;
}

View File

@ -0,0 +1 @@
package cn.axzo.riven.service.event;

View File

@ -0,0 +1,32 @@
package cn.axzo.riven.service.user;
import cn.axzo.framework.domain.page.PageQO;
import cn.axzo.framework.domain.page.PageResp;
import cn.axzo.riven.service.dto.request.user.NewUserDTO;
import cn.axzo.riven.service.dto.request.user.UpdateUserDTO;
import cn.axzo.riven.service.dto.request.user.UserQO;
import cn.axzo.riven.service.dto.request.user.UserQO1;
import cn.axzo.riven.service.dto.response.user.UserRefVO;
import cn.axzo.riven.service.dto.response.user.UserVO;
import cn.azxo.framework.common.model.CommonPageResponse;
/**
* @Author: liyong.tian
* @Date: 2022/9/2
* @Description:
*/
public interface UserService {
UserVO create(NewUserDTO dto);
UserVO update(Long id, UpdateUserDTO dto);
UserVO getOne(Long id);
void delete(Long id);
CommonPageResponse<UserVO> queryByPage(UserQO userQO);
PageResp<UserVO> find(UserQO1 userQo, PageQO page);
PageResp<UserRefVO> queryUserAddress(UserQO1 userQo, PageQO page);
}

View File

@ -0,0 +1,94 @@
package cn.axzo.riven.service.user.impl;
import cn.axzo.framework.domain.data.AssertUtil;
import cn.axzo.framework.domain.page.PageQO;
import cn.axzo.framework.domain.page.PageResp;
import cn.axzo.riven.common.enums.ErrorCode;
import cn.axzo.riven.repository.entity.user.UserDO;
import cn.axzo.riven.repository.entity.user.UserRefDO;
import cn.axzo.riven.service.converter.UserRefConverter;
import cn.axzo.riven.service.dto.request.user.NewUserDTO;
import cn.axzo.riven.service.dto.request.user.UpdateUserDTO;
import cn.axzo.riven.service.dto.request.user.UserQO;
import cn.axzo.riven.service.dto.request.user.UserQO1;
import cn.axzo.riven.service.dto.response.user.UserRefVO;
import cn.axzo.riven.service.dto.response.user.UserVO;
import cn.axzo.riven.repository.UserDao;
import cn.axzo.riven.service.user.UserService;
import cn.axzo.riven.service.converter.UserConverter;
import cn.azxo.framework.common.model.CommonPageResponse;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.List;
/**
* @Author: liyong.tian
* @Date: 2022/9/2
* @Description:
*/
@Slf4j
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
private final UserConverter userConverter;
private final UserRefConverter userRefConverter;
private final UserDao userDao;
@Override
public UserVO create(NewUserDTO dto) {
UserDO user = userConverter.toEntity(dto);
userDao.save(user);
return userConverter.toVo(user);
}
@Override
public UserVO update(Long id, UpdateUserDTO dto) {
UserDO user = userDao.findById(id);
AssertUtil.notNull(user, ErrorCode.USER_NOT_EXISTS, id);
/*if (user == null) {
throw new ApiException(ErrorCode.USER_NOT_EXISTS, id);
}*/
userConverter.updateEntity(dto, user);
return userConverter.toVo(user);
}
@Override
public UserVO getOne(Long id) {
UserDO user = userDao.findById(id);
return userConverter.toVo(user);
}
@Override
public void delete(Long id) {
userDao.delete(id);
}
@Override
public CommonPageResponse<UserVO> queryByPage(UserQO userQo) {
IPage<UserDO> page = userDao.queryByPage(userQo);
List<UserDO> userList = page.getRecords();
if (CollectionUtils.isEmpty(userList)) {
return CommonPageResponse.zero(userQo.getPage(), userQo.getPageSize());
}
return new CommonPageResponse<>(page.getCurrent(), page.getSize(), page.getTotal(), userConverter.toVo(page.getRecords()));
}
@Override
public PageResp<UserVO> find(UserQO1 userQo, PageQO page) {
IPage<UserDO> userPage = userDao.find(userQo, page);
return PageResp.list(userPage, userConverter.toVo(userPage.getRecords()));
}
@Override
public PageResp<UserRefVO> queryUserAddress(UserQO1 userQo, PageQO page) {
IPage<UserRefDO> userRefPage = userDao.findUserAddress(userQo, page);
return PageResp.list(userRefPage, userRefConverter.toVo(userRefPage.getRecords()));
}
}

View File

@ -0,0 +1 @@
package cn.axzo.riven.service.validator;

View File

@ -0,0 +1,44 @@
spring:
application:
name: riven
cloud:
nacos:
config:
server-addr: ${NACOS_HOST:test-nacos.axzo.cn}:${NACOS_PORT:80}
file-extension: yaml
namespace: ${NACOS_NAMESPACE_ID:f82179f1-81a9-41a1-a489-4f9ab5660a6e}
prefix: ${spring.application.name}
profiles:
active: ${NACOS_PROFILES_ACTIVE:test}
main:
allow-bean-definition-overriding: true
logging:
level:
com.alibaba.nacos.client.config.impl: WARN
management:
endpoint:
metrics:
enabled: true
prometheus:
enabled: true
endpoints:
web:
exposure:
include: prometheus
path-mapping:
prometheus: metrics
base-path: /
metrics:
tags:
application: ${spring.application.name}
export:
prometheus:
enabled: true
server:
port: 8081
resp:
error-code:
non-base-mapping: {"[*]": OK}

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE xml>
<configuration>
<!-- 导入安心筑全局日志配置 -->
<include resource="logback/logback-axzo.xml" />
<!-- 覆盖开发环境日志配置 -->
<springProfile name="local,dev">
<logger name="cn.axzo" level="DEBUG" />
</springProfile>
</configuration>

View File

@ -0,0 +1,14 @@
package cn.axzo.maven.archetype.server;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
/**
* Unit test for simple App.
*/
@RunWith(SpringRunner.class)
@SpringBootTest
public class AppTest {
}