diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..a66b8e2f
--- /dev/null
+++ b/.gitignore
@@ -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
\ No newline at end of file
diff --git a/README.md b/README.md
index 5c7a0425..67fd2d26 100644
--- a/README.md
+++ b/README.md
@@ -1,3 +1,2 @@
-# tyr
-
+# 项目介绍
权限服务
\ No newline at end of file
diff --git a/RELEASE.md b/RELEASE.md
new file mode 100644
index 00000000..a033ae0c
--- /dev/null
+++ b/RELEASE.md
@@ -0,0 +1,2 @@
+# 发布记录
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 00000000..4bead31e
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,102 @@
+
+
+ 4.0.0
+
+
+ cn.axzo.infra
+ axzo-parent
+ 2.4.13.5
+
+
+ cn.axzo.tyr
+ tyr
+ pom
+ 1.0.0-SNAPSHOT
+ tyr
+
+
+ tyr-server
+ tyr-api
+
+
+
+ 2.0.0-SNAPSHOT
+ 2.0.0-SNAPSHOT
+ 1.18.22
+ 1.4.2.Final
+
+
+
+
+
+
+ cn.axzo.infra
+ axzo-bom
+ ${axzo-bom.version}
+ pom
+ import
+
+
+ cn.axzo.infra
+ axzo-dependencies
+ ${axzo-dependencies.version}
+ pom
+ import
+
+
+
+
+
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+ junit
+ junit
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ org.projectlombok
+ lombok
+ ${lombok.version}
+
+
+ org.mapstruct
+ mapstruct-processor
+ ${mapstruct.version}
+
+
+
+
+
+
+
+
+
+ axzo
+ axzo repository
+ https://nexus.axzo.cn/repository/axzo/
+
+
+
diff --git a/tyr-api/pom.xml b/tyr-api/pom.xml
new file mode 100644
index 00000000..298ff057
--- /dev/null
+++ b/tyr-api/pom.xml
@@ -0,0 +1,22 @@
+
+
+ 4.0.0
+
+
+ tyr
+ cn.axzo.tyr
+ 1.0.0-SNAPSHOT
+ ../pom.xml
+
+
+ tyr-api
+ jar
+ tyr-api
+
+
+
+ cn.axzo.framework
+ axzo-consumer-spring-cloud-starter
+
+
+
diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/TyrClient.java b/tyr-api/src/main/java/cn/axzo/tyr/client/TyrClient.java
new file mode 100644
index 00000000..deaf693d
--- /dev/null
+++ b/tyr-api/src/main/java/cn/axzo/tyr/client/TyrClient.java
@@ -0,0 +1,57 @@
+package cn.axzo.tyr.client;
+
+import cn.axzo.framework.domain.page.PageQO;
+import cn.axzo.framework.domain.page.PageResp;
+import cn.axzo.framework.domain.web.ApiException;
+import cn.axzo.framework.domain.web.result.ApiPageResult;
+import cn.axzo.framework.domain.web.result.ApiResult;
+import cn.axzo.tyr.client.feign.TyrApi;
+import cn.axzo.tyr.client.model.NewUserReq;
+import cn.axzo.tyr.client.model.QueryUserReq;
+import cn.axzo.tyr.client.model.UpdateUserReq;
+import cn.axzo.tyr.client.model.UserRes;
+import cn.azxo.framework.common.model.CommonResponse;
+import lombok.RequiredArgsConstructor;
+
+import javax.validation.Valid;
+
+/**
+ * @Author: liyong.tian
+ * @Date: 2022/9/17
+ * @Description:
+ */
+@RequiredArgsConstructor
+public class TyrClient {
+
+ private final TyrApi api;
+
+ /**
+ * 老项目迁移使用
+ */
+ public UserRes createUser(@Valid NewUserReq req) {
+ CommonResponse apiResult = api.createUser(req);
+ if (apiResult.getCode() == 200) {
+ return apiResult.getData();
+ }
+ throw new RuntimeException(apiResult.getMsg());
+ }
+
+ /**
+ * 新项目使用
+ */
+ public UserRes updateUser(Long id, @Valid UpdateUserReq req) {
+ ApiResult apiResult = api.updateUser(id, req);
+ if (apiResult.isSuccess()) {
+ return apiResult.getData();
+ }
+ throw new ApiException(apiResult.getRespCode());
+ }
+
+ public PageResp fetchUsers(QueryUserReq req, PageQO page) {
+ ApiPageResult apiPageResult = api.fetchUsers(req.toQueryMap(), page);
+ if (apiPageResult.isSuccess()) {
+ return apiPageResult.toPage();
+ }
+ throw new ApiException(apiPageResult.getRespCode());
+ }
+}
diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/config/TyrClientAutoConfiguration.java b/tyr-api/src/main/java/cn/axzo/tyr/client/config/TyrClientAutoConfiguration.java
new file mode 100644
index 00000000..9790a2e2
--- /dev/null
+++ b/tyr-api/src/main/java/cn/axzo/tyr/client/config/TyrClientAutoConfiguration.java
@@ -0,0 +1,28 @@
+package cn.axzo.tyr.client.config;
+
+import cn.axzo.tyr.client.TyrClient;
+import cn.axzo.tyr.client.feign.TyrApi;
+import cn.axzo.tyr.client.feign.TyrFallbackFactory;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+/**
+ * @Author: liyong.tian
+ * @Date: 2022/9/17
+ * @Description:
+ */
+@EnableFeignClients(basePackages = {"cn.axzo.tyr.client"})
+@Configuration
+public class TyrClientAutoConfiguration {
+
+ @Bean
+ public TyrFallbackFactory microArchetypeFallbackFactory() {
+ return new TyrFallbackFactory();
+ }
+
+ @Bean
+ public TyrClient tyrClient(TyrApi tyrApi) {
+ return new TyrClient(tyrApi);
+ }
+}
diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrApi.java b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrApi.java
new file mode 100644
index 00000000..11e90458
--- /dev/null
+++ b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrApi.java
@@ -0,0 +1,33 @@
+package cn.axzo.tyr.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.tyr.client.model.NewUserReq;
+import cn.axzo.tyr.client.model.UpdateUserReq;
+import cn.axzo.tyr.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 = "tyr", url = "http://localhost:8899", fallbackFactory = TyrFallbackFactory.class)
+public interface TyrApi {
+
+ @PostMapping(value = "/api/v1/users", consumes = APPLICATION_JSON_VALUE)
+ CommonResponse createUser(@RequestBody NewUserReq req);
+
+ @PutMapping(value = "/api/v2/users/{id}", consumes = APPLICATION_JSON_VALUE)
+ ApiResult updateUser(@PathVariable("id") Long id, @RequestBody UpdateUserReq req);
+
+ @GetMapping(value = "/api/v2/users")
+ ApiPageResult fetchUsers(@RequestParam Map query, PageQO page);
+}
diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrApiFallback.java b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrApiFallback.java
new file mode 100644
index 00000000..eab606a0
--- /dev/null
+++ b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrApiFallback.java
@@ -0,0 +1,52 @@
+package cn.axzo.tyr.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.tyr.client.model.NewUserReq;
+import cn.axzo.tyr.client.model.UpdateUserReq;
+import cn.axzo.tyr.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 TyrApiFallback implements TyrApi {
+
+ private final FeignFallback fallback;
+
+ /**
+ * 老项目迁移使用
+ * @param req
+ * @return
+ */
+ @Override
+ public CommonResponse createUser(NewUserReq req) {
+ log.error("[tyr-api] createUser fallback", fallback.getCause());
+ return CommonResponse.error("创建用户失败");
+ }
+
+ /**
+ * 新项目推荐使用
+ */
+ @Override
+ public ApiResult updateUser(Long id, UpdateUserReq req) {
+ log.error("[tyr-api] updateUser fallback", fallback.getCause());
+ return fallback.resp();
+ }
+
+ @Override
+ public ApiPageResult fetchUsers(Map query, PageQO page) {
+ log.error("[tyr-api] fetchUsers fallback", fallback.getCause());
+ return fallback.pageResp();
+ }
+}
diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrFallbackFactory.java b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrFallbackFactory.java
new file mode 100644
index 00000000..d15c7c57
--- /dev/null
+++ b/tyr-api/src/main/java/cn/axzo/tyr/client/feign/TyrFallbackFactory.java
@@ -0,0 +1,19 @@
+package cn.axzo.tyr.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 TyrFallbackFactory implements FallbackFactory {
+
+ // TODO: 2022/11/3 100-调整为具体的项目编号,XXX-调整为项目名
+ private final IRespCode respCode = new RespCode("100" + "91001", "XXX服务不可用");
+
+ @Override
+ public TyrApiFallback create(Throwable cause) {
+ return new TyrApiFallback(new FeignFallback(cause, respCode));
+ }
+}
diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/NewUserReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/NewUserReq.java
new file mode 100644
index 00000000..21f5c7ad
--- /dev/null
+++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/NewUserReq.java
@@ -0,0 +1,29 @@
+package cn.axzo.tyr.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;
+}
diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/QueryUserReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/QueryUserReq.java
new file mode 100644
index 00000000..8d88889b
--- /dev/null
+++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/QueryUserReq.java
@@ -0,0 +1,22 @@
+package cn.axzo.tyr.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);
+ }
+}
diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/UpdateUserReq.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/UpdateUserReq.java
new file mode 100644
index 00000000..acb268a6
--- /dev/null
+++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/UpdateUserReq.java
@@ -0,0 +1,19 @@
+package cn.axzo.tyr.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;
+}
diff --git a/tyr-api/src/main/java/cn/axzo/tyr/client/model/UserRes.java b/tyr-api/src/main/java/cn/axzo/tyr/client/model/UserRes.java
new file mode 100644
index 00000000..7508a5f6
--- /dev/null
+++ b/tyr-api/src/main/java/cn/axzo/tyr/client/model/UserRes.java
@@ -0,0 +1,21 @@
+package cn.axzo.tyr.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;
+}
diff --git a/tyr-api/src/main/resources/META-INF/spring.factories b/tyr-api/src/main/resources/META-INF/spring.factories
new file mode 100644
index 00000000..070ecd68
--- /dev/null
+++ b/tyr-api/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,2 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+cn.axzo.micro.archetype.client.config.TyrClientAutoConfiguration
\ No newline at end of file
diff --git a/tyr-api/src/test/java/cn/axzo/maven/archetype/client/AppTest.java b/tyr-api/src/test/java/cn/axzo/maven/archetype/client/AppTest.java
new file mode 100644
index 00000000..7d836fa0
--- /dev/null
+++ b/tyr-api/src/test/java/cn/axzo/maven/archetype/client/AppTest.java
@@ -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 {
+
+}
diff --git a/tyr-server/pom.xml b/tyr-server/pom.xml
new file mode 100644
index 00000000..27a641c1
--- /dev/null
+++ b/tyr-server/pom.xml
@@ -0,0 +1,54 @@
+
+
+
+ tyr
+ cn.axzo.tyr
+ 1.0.0-SNAPSHOT
+ ../pom.xml
+
+ 4.0.0
+
+ tyr-server
+ jar
+
+ tyr-server
+
+
+
+ cn.axzo.framework
+ axzo-web-spring-boot-starter
+
+
+ cn.axzo.framework
+ axzo-spring-cloud-starter
+
+
+ cn.axzo.framework
+ axzo-consumer-spring-cloud-starter
+
+
+ cn.axzo.framework
+ axzo-processor-spring-boot-starter
+
+
+
+ cn.axzo.framework
+ axzo-mybatisplus-spring-boot-starter
+
+
+
+ cn.axzo.framework
+ axzo-swagger-yapi-spring-boot-starter
+
+
+
+ com.alibaba
+ druid-spring-boot-starter
+
+
+
+ cn.axzo.framework
+ axzo-logger-spring-boot-starter
+
+
+
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/TyrApplication.java b/tyr-server/src/main/java/cn/axzo/tyr/server/TyrApplication.java
new file mode 100644
index 00000000..dd5073f9
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/TyrApplication.java
@@ -0,0 +1,13 @@
+package cn.axzo.tyr.server;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@MapperScan(value = {"cn.axzo.**.mapper"})
+@SpringBootApplication
+public class TyrApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(TyrApplication.class, args);
+ }
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/common/enums/ErrorCode.java b/tyr-server/src/main/java/cn/axzo/tyr/server/common/enums/ErrorCode.java
new file mode 100644
index 00000000..7fa875f9
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/common/enums/ErrorCode.java
@@ -0,0 +1,27 @@
+package cn.axzo.tyr.server.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~9,3位项目编号(首位不能为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";
+ }
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/common/enums/ResultCode.java b/tyr-server/src/main/java/cn/axzo/tyr/server/common/enums/ResultCode.java
new file mode 100644
index 00000000..e96ef1be
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/common/enums/ResultCode.java
@@ -0,0 +1,88 @@
+package cn.axzo.tyr.server.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;
+ }
+
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/common/util/package-info.java b/tyr-server/src/main/java/cn/axzo/tyr/server/common/util/package-info.java
new file mode 100644
index 00000000..1cd2c6fa
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/common/util/package-info.java
@@ -0,0 +1 @@
+package cn.axzo.tyr.server.common.util;
\ No newline at end of file
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/config/filter/HttpTraceLogFilter.java b/tyr-server/src/main/java/cn/axzo/tyr/server/config/filter/HttpTraceLogFilter.java
new file mode 100644
index 00000000..d85f107a
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/config/filter/HttpTraceLogFilter.java
@@ -0,0 +1,259 @@
+package cn.axzo.tyr.server.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 getRequestHeader(HttpServletRequest request) {
+ Map ret = new HashMap<>();
+ Enumeration 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 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);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/config/filter/WrappedHttpServletRequest.java b/tyr-server/src/main/java/cn/axzo/tyr/server/config/filter/WrappedHttpServletRequest.java
new file mode 100644
index 00000000..367eaf12
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/config/filter/WrappedHttpServletRequest.java
@@ -0,0 +1,91 @@
+package cn.axzo.tyr.server.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) {
+ }
+ }
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/config/package-info.java b/tyr-server/src/main/java/cn/axzo/tyr/server/config/package-info.java
new file mode 100644
index 00000000..8d7742d5
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/config/package-info.java
@@ -0,0 +1 @@
+package cn.axzo.tyr.server.config;
\ No newline at end of file
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/consumer/package-info.java b/tyr-server/src/main/java/cn/axzo/tyr/server/consumer/package-info.java
new file mode 100644
index 00000000..eb001a5e
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/consumer/package-info.java
@@ -0,0 +1 @@
+package cn.axzo.tyr.server.consumer;
\ No newline at end of file
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/HealthCheckController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/HealthCheckController.java
new file mode 100644
index 00000000..816ebf86
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/HealthCheckController.java
@@ -0,0 +1,22 @@
+package cn.axzo.tyr.server.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 checkDeath() {
+ return ApiResponse.ok("ok");
+ }
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/app/package-info.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/app/package-info.java
new file mode 100644
index 00000000..49e38f03
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/app/package-info.java
@@ -0,0 +1 @@
+package cn.axzo.tyr.server.controller.app;
\ No newline at end of file
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/web/UserController.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/web/UserController.java
new file mode 100644
index 00000000..7ce449bd
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/web/UserController.java
@@ -0,0 +1,76 @@
+package cn.axzo.tyr.server.controller.web;
+
+import cn.axzo.tyr.server.service.user.UserService;
+import cn.axzo.tyr.server.service.dto.request.user.NewUserDTO;
+import cn.axzo.tyr.server.service.dto.request.user.UpdateUserDTO;
+import cn.axzo.tyr.server.service.dto.request.user.UserQO;
+import cn.axzo.tyr.server.service.dto.response.user.UserVO;
+import cn.azxo.framework.common.model.CommonPageResponse;
+import cn.azxo.framework.common.model.CommonResponse;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+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-用户信息接口")
+@ApiSupport(author = "田立勇")
+@RequestMapping("/api/v1")
+@RestController
+@RequiredArgsConstructor
+public class UserController {
+
+ private final UserService userService;
+
+ @ApiOperation(value = "创建用户")
+ @PostMapping("/users")
+ public CommonResponse 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 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> getUsers(@Valid UserQO userQO) {
+ CommonPageResponse 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();
+ }
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/controller/web/UserResource.java b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/web/UserResource.java
new file mode 100644
index 00000000..73607e7f
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/controller/web/UserResource.java
@@ -0,0 +1,78 @@
+package cn.axzo.tyr.server.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.tyr.server.service.dto.request.user.NewUserDTO;
+import cn.axzo.tyr.server.service.dto.request.user.UpdateUserDTO;
+import cn.axzo.tyr.server.service.dto.request.user.UserQO1;
+import cn.axzo.tyr.server.service.dto.response.user.UserVO;
+import cn.axzo.tyr.server.service.user.UserService;
+import com.github.xiaoymin.knife4j.annotations.ApiSupport;
+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-用户信息接口")
+@ApiSupport(author = "田立勇")
+@RequestMapping("/api/v2")
+@RestController
+@RequiredArgsConstructor
+public class UserResource {
+
+ private final UserService userService;
+
+ @ApiOperation(value = "创建用户")
+ @PostMapping("/users")
+ public ApiResponse 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 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 getUsers(@ModelAttribute UserQO1 userQo, PageQO page) {
+ PageResp results = userService.find(userQo, page);
+ return ApiPageResponse.ok(results);
+ }
+
+ @ApiOperation("获取用户详情")
+ @GetMapping("/users/{id}")
+ public ApiResponse 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();
+ }
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/job/package-info.java b/tyr-server/src/main/java/cn/axzo/tyr/server/job/package-info.java
new file mode 100644
index 00000000..d3a347ea
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/job/package-info.java
@@ -0,0 +1 @@
+package cn.axzo.tyr.server.job;
\ No newline at end of file
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/UserDao.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/UserDao.java
new file mode 100644
index 00000000..b0909277
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/UserDao.java
@@ -0,0 +1,55 @@
+package cn.axzo.tyr.server.repository;
+
+import cn.axzo.framework.domain.page.PageQO;
+import cn.axzo.tyr.server.repository.mapper.UserMapper;
+import cn.axzo.tyr.server.service.dto.request.user.UserQO;
+import cn.axzo.tyr.server.service.dto.request.user.UserQO1;
+import cn.axzo.tyr.server.repository.entity.user.User;
+import com.baomidou.mybatisplus.core.metadata.IPage;
+import com.baomidou.mybatisplus.core.toolkit.Wrappers;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+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 {
+
+ private final UserMapper userMapper;
+
+ public User findById(Long id) {
+ return userMapper.selectById(id);
+ }
+
+ public void delete(Long id) {
+ userMapper.deleteById(id);
+ }
+
+ public IPage queryByPage(UserQO userQO) {
+ return userMapper.selectPage(userQO.toPage(),
+ Wrappers.lambdaQuery(User.class)
+ .eq(userQO.getId() != null, User::getId, userQO.getId())
+ .like(StringUtils.isNotBlank(userQO.getName()), User::getName, userQO.getName())
+ .like(StringUtils.isNotBlank(userQO.getPhone()), User::getPhone, userQO.getPhone())
+ .like(StringUtils.isNotBlank(userQO.getEmail()), User::getEmail, userQO.getEmail())
+ .orderByDesc(User::getCreateAt)
+ );
+ }
+
+ public IPage find(UserQO1 userQO, PageQO page) {
+ return userMapper.selectPage(page.toPage(),
+ Wrappers.lambdaQuery(User.class)
+ .eq(userQO.getId() != null, User::getId, userQO.getId())
+ .like(StringUtils.isNotBlank(userQO.getName()), User::getName, userQO.getName())
+ .like(StringUtils.isNotBlank(userQO.getPhone()), User::getPhone, userQO.getPhone())
+ .like(StringUtils.isNotBlank(userQO.getEmail()), User::getEmail, userQO.getEmail())
+ .orderByDesc(User::getCreateAt)
+ );
+ }
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/package-info.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/package-info.java
new file mode 100644
index 00000000..f2633475
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/package-info.java
@@ -0,0 +1 @@
+package cn.axzo.tyr.server.repository.entity;
\ No newline at end of file
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/user/User.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/user/User.java
new file mode 100644
index 00000000..3ceee26d
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/entity/user/User.java
@@ -0,0 +1,27 @@
+package cn.axzo.tyr.server.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 {
+
+ private String name;
+
+ private Integer sex;
+
+ private Integer age;
+
+ private String phone;
+
+ private String email;
+
+ private String address;
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/UserMapper.java b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/UserMapper.java
new file mode 100644
index 00000000..0f0704f9
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/repository/mapper/UserMapper.java
@@ -0,0 +1,14 @@
+package cn.axzo.tyr.server.repository.mapper;
+
+import cn.axzo.tyr.server.repository.entity.user.User;
+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 {
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/converter/EntityConverter.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/converter/EntityConverter.java
new file mode 100644
index 00000000..1dd728f1
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/converter/EntityConverter.java
@@ -0,0 +1,15 @@
+package cn.axzo.tyr.server.service.converter;
+
+import java.util.List;
+
+/**
+ * @Author: liyong.tian
+ * @Date: 2022/9/5
+ * @Description:
+ */
+public interface EntityConverter{
+
+ V toVo(E var);
+
+ List toVo(List var);
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/converter/UserConverter.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/converter/UserConverter.java
new file mode 100644
index 00000000..af7010d8
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/converter/UserConverter.java
@@ -0,0 +1,26 @@
+package cn.axzo.tyr.server.service.converter;
+
+import cn.axzo.tyr.server.service.dto.request.user.NewUserDTO;
+import cn.axzo.tyr.server.service.dto.request.user.UpdateUserDTO;
+import cn.axzo.tyr.server.service.dto.response.user.UserVO;
+import cn.axzo.tyr.server.repository.entity.user.User;
+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 {
+
+ User toEntity(NewUserDTO dto);
+
+ void updateEntity(UpdateUserDTO dto, @MappingTarget User user);
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/package-info.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/package-info.java
new file mode 100644
index 00000000..8f23be65
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/package-info.java
@@ -0,0 +1 @@
+package cn.axzo.tyr.server.service.dto.request;
\ No newline at end of file
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/user/NewUserDTO.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/user/NewUserDTO.java
new file mode 100644
index 00000000..732aefe6
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/user/NewUserDTO.java
@@ -0,0 +1,44 @@
+package cn.axzo.tyr.server.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;
+
+ @ApiModelProperty(value = "地址", position = 6)
+ private String address;
+
+ public void valid() {
+ // 电话和邮箱不能都为空
+ if (StringUtils.isEmpty(phone) && StringUtils.isEmpty(email)) {
+ throw new RuntimeException("电话和邮箱不能都为空");
+ }
+ }
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/user/UpdateUserDTO.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/user/UpdateUserDTO.java
new file mode 100644
index 00000000..bec543a7
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/user/UpdateUserDTO.java
@@ -0,0 +1,47 @@
+package cn.axzo.tyr.server.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.tyr.server.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;
+
+ @ApiModelProperty(value = "地址", position = 6)
+ private String address;
+
+ public void valid() {
+ // 电话和邮箱不能都为空
+ if (StringUtils.isEmpty(phone) && StringUtils.isEmpty(email)) {
+ throw new ApiException(USER_PHONE_EMAIL_IS_NULL);
+ }
+ }
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/user/UserQO.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/user/UserQO.java
new file mode 100644
index 00000000..090a549b
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/user/UserQO.java
@@ -0,0 +1,26 @@
+package cn.axzo.tyr.server.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;
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/user/UserQO1.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/user/UserQO1.java
new file mode 100644
index 00000000..09c4dac5
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/request/user/UserQO1.java
@@ -0,0 +1,26 @@
+package cn.axzo.tyr.server.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 UserQO1 {
+
+ @ApiParam("主键")
+ private Long id;
+
+ @ApiParam("姓名")
+ private String name;
+
+ @ApiParam("手机")
+ private String phone;
+
+ @ApiParam("邮箱")
+ private String email;
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/response/package-info.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/response/package-info.java
new file mode 100644
index 00000000..28c9eaaf
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/response/package-info.java
@@ -0,0 +1 @@
+package cn.axzo.tyr.server.service.dto.response;
\ No newline at end of file
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/response/user/UserVO.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/response/user/UserVO.java
new file mode 100644
index 00000000..8317e4cd
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/dto/response/user/UserVO.java
@@ -0,0 +1,34 @@
+package cn.axzo.tyr.server.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;
+
+ @ApiModelProperty(value = "地址", position = 7)
+ private String address;
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/event/package-info.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/event/package-info.java
new file mode 100644
index 00000000..0d058404
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/event/package-info.java
@@ -0,0 +1 @@
+package cn.axzo.tyr.server.service.event;
\ No newline at end of file
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/manager/package-info.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/manager/package-info.java
new file mode 100644
index 00000000..9270b83b
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/manager/package-info.java
@@ -0,0 +1 @@
+package cn.axzo.tyr.server.service.manager;
\ No newline at end of file
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/user/UserService.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/user/UserService.java
new file mode 100644
index 00000000..1cffffb4
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/user/UserService.java
@@ -0,0 +1,29 @@
+package cn.axzo.tyr.server.service.user;
+
+import cn.axzo.framework.domain.page.PageQO;
+import cn.axzo.framework.domain.page.PageResp;
+import cn.axzo.tyr.server.service.dto.request.user.NewUserDTO;
+import cn.axzo.tyr.server.service.dto.request.user.UpdateUserDTO;
+import cn.axzo.tyr.server.service.dto.request.user.UserQO;
+import cn.axzo.tyr.server.service.dto.request.user.UserQO1;
+import cn.axzo.tyr.server.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 queryByPage(UserQO userQO);
+
+ PageResp find(UserQO1 userQo, PageQO page);
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/user/impl/UserServiceImpl.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/user/impl/UserServiceImpl.java
new file mode 100644
index 00000000..56fae11d
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/user/impl/UserServiceImpl.java
@@ -0,0 +1,82 @@
+package cn.axzo.tyr.server.service.user.impl;
+
+import cn.axzo.framework.domain.page.PageQO;
+import cn.axzo.framework.domain.page.PageResp;
+import cn.axzo.framework.domain.web.ApiException;
+import cn.axzo.tyr.server.common.enums.ErrorCode;
+import cn.axzo.tyr.server.service.dto.request.user.NewUserDTO;
+import cn.axzo.tyr.server.service.dto.request.user.UpdateUserDTO;
+import cn.axzo.tyr.server.service.dto.request.user.UserQO;
+import cn.axzo.tyr.server.service.dto.request.user.UserQO1;
+import cn.axzo.tyr.server.service.dto.response.user.UserVO;
+import cn.axzo.tyr.server.repository.entity.user.User;
+import cn.axzo.tyr.server.repository.UserDao;
+import cn.axzo.tyr.server.service.user.UserService;
+import cn.axzo.tyr.server.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 UserDao userDao;
+
+ @Override
+ public UserVO create(NewUserDTO dto) {
+ User user = userConverter.toEntity(dto);
+ userDao.save(user);
+ return userConverter.toVo(user);
+ }
+
+ @Override
+ public UserVO update(Long id, UpdateUserDTO dto) {
+ User user = userDao.findById(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) {
+ User user = userDao.findById(id);
+ return userConverter.toVo(user);
+ }
+
+ @Override
+ public void delete(Long id) {
+ userDao.delete(id);
+ }
+
+ @Override
+ public CommonPageResponse queryByPage(UserQO userQo) {
+ IPage page = userDao.queryByPage(userQo);
+ List 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 find(UserQO1 userQo, PageQO page) {
+ IPage userPage = userDao.find(userQo, page);
+ return PageResp.list(userPage, userConverter.toVo(userPage.getRecords()));
+ }
+}
diff --git a/tyr-server/src/main/java/cn/axzo/tyr/server/service/validator/package-info.java b/tyr-server/src/main/java/cn/axzo/tyr/server/service/validator/package-info.java
new file mode 100644
index 00000000..b4ae43f2
--- /dev/null
+++ b/tyr-server/src/main/java/cn/axzo/tyr/server/service/validator/package-info.java
@@ -0,0 +1 @@
+package cn.axzo.tyr.server.service.validator;
\ No newline at end of file
diff --git a/tyr-server/src/main/resources/bootstrap.yml b/tyr-server/src/main/resources/bootstrap.yml
new file mode 100644
index 00000000..91560edb
--- /dev/null
+++ b/tyr-server/src/main/resources/bootstrap.yml
@@ -0,0 +1,37 @@
+spring:
+ application:
+ name: tyr
+ cloud:
+ nacos:
+ config:
+ server-addr: ${NACOS_HOST:dev-nacos.axzo.cn}:${NACOS_PORT:80}
+ file-extension: yaml
+ namespace: ${NACOS_NAMESPACE_ID:35eada10-9574-4db8-9fea-bc6a4960b6c7}
+ prefix: ${spring.application.name}
+ profiles:
+ active: ${NACOS_PROFILES_ACTIVE:local}
+ include: swagger
+ 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: "*"
+ metrics:
+ export:
+ prometheus:
+ enabled: true
+
+knife4j:
+ enable: true
diff --git a/tyr-server/src/main/resources/logback-spring.xml b/tyr-server/src/main/resources/logback-spring.xml
new file mode 100644
index 00000000..922ff245
--- /dev/null
+++ b/tyr-server/src/main/resources/logback-spring.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/tyr-server/src/test/java/cn/axzo/maven/archetype/server/AppTest.java b/tyr-server/src/test/java/cn/axzo/maven/archetype/server/AppTest.java
new file mode 100644
index 00000000..f5b53190
--- /dev/null
+++ b/tyr-server/src/test/java/cn/axzo/maven/archetype/server/AppTest.java
@@ -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 {
+
+}