1、总的pom文件:
<?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>
<groupId>xyz.haijin</groupId>
<artifactId>nacosProject</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>cloud-api-commons-stu</module>
</modules>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<hutool.version>5.8.22</hutool.version>
<lombok.version>1.18.26</lombok.version>
<druid.version>1.1.20</druid.version>
<mybatis.springboot.version>3.0.2</mybatis.springboot.version>
<mysql.version>8.0.11</mysql.version>
<swagger3.version>2.2.0</swagger3.version>
<mapper.version>4.2.3</mapper.version>
<fastjson2.version>2.0.40</fastjson2.version>
<persistence-api.version>1.0.2</persistence-api.version>
<spring.boot.test.version>3.1.5</spring.boot.test.version>
<!-- <spring.boot.version>3.2.0</spring.boot.version>
<spring.cloud.version>2023.0.0</spring.cloud.version>-->
<!--仅为了整合openfeign + alibaba sentinel的案例,降低版本处理下-->
<!-- <spring.boot.version>3.0.9</spring.boot.version>
<spring.cloud.version>2022.0.2</spring.cloud.version>-->
<!--仅为了整合openfeign + alibaba seata的案例,降低版本处理下-->
<spring.boot.version>3.1.7</spring.boot.version>
<spring.cloud.version>2022.0.4</spring.cloud.version>
<spring.cloud.alibaba.version>2022.0.0.0-RC2</spring.cloud.alibaba.version>
<micrometer-tracing.version>1.2.0</micrometer-tracing.version>
<micrometer-observation.version>1.12.0</micrometer-observation.version>
<feign-micrometer.version>12.5</feign-micrometer.version>
<zipkin-reporter-brave.version>2.17.0</zipkin-reporter-brave.version>
</properties>
<dependencyManagement>
<dependencies>
<!--springboot 3.2.0-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>${spring.boot.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--springcloud 2023.0.0-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>${spring.cloud.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--springcloud alibaba 2022.0.0.0-RC2-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-alibaba-dependencies</artifactId>
<version>${spring.cloud.alibaba.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--SpringBoot集成mybatis-->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>${mybatis.springboot.version}</version>
</dependency>
<!--Mysql数据库驱动8 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${mysql.version}</version>
</dependency>
<!--SpringBoot集成druid连接池-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>${druid.version}</version>
</dependency>
<!--通用Mapper4之tk.mybatis-->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>${mapper.version}</version>
</dependency>
<!--persistence-->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>${persistence-api.version}</version>
</dependency>
<!-- fastjson2 -->
<dependency>
<groupId>com.alibaba.fastjson2</groupId>
<artifactId>fastjson2</artifactId>
<version>${fastjson2.version}</version>
</dependency>
<!-- swagger3 调用方式 http://你的主机IP地址:5555/swagger-ui/index.html -->
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${swagger3.version}</version>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
<version>${hutool.version}</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
<optional>true</optional>
</dependency>
<!-- spring-boot-starter-test -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<version>${spring.boot.test.version}</version>
<scope>test</scope>
</dependency>
<!--micrometer-tracing-bom导入链路追踪版本中心 1-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bom</artifactId>
<version>${micrometer-tracing.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
<!--micrometer-tracing指标追踪 2-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing</artifactId>
<version>${micrometer-tracing.version}</version>
</dependency>
<!--micrometer-tracing-bridge-brave适配zipkin的桥接包 3-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-tracing-bridge-brave</artifactId>
<version>${micrometer-tracing.version}</version>
</dependency>
<!--micrometer-observation 4-->
<dependency>
<groupId>io.micrometer</groupId>
<artifactId>micrometer-observation</artifactId>
<version>${micrometer-observation.version}</version>
</dependency>
<!--feign-micrometer 5-->
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-micrometer</artifactId>
<version>${feign-micrometer.version}</version>
</dependency>
<!--zipkin-reporter-brave 6-->
<dependency>
<groupId>io.zipkin.reporter2</groupId>
<artifactId>zipkin-reporter-brave</artifactId>
<version>${zipkin-reporter-brave.version}</version>
</dependency>
</dependencies>
</dependencyManagement>
</project>
2、common的pom文件:
<?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>nacosProject</artifactId>
<groupId>xyz.haijin</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>cloud-api-commons-stu</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--alibaba-sentinel-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!--openfeign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!--SpringBoot通用依赖模块-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--hutool-->
<dependency>
<groupId>cn.hutool</groupId>
<artifactId>hutool-all</artifactId>
</dependency>
</dependencies>
</project>
3、AccountFeignApi:
package xyz.haijin.cloud.apis;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import xyz.haijin.cloud.resp.ResultData;
@FeignClient(value = "seata-account-service")
public interface AccountFeignApi {
//扣减账户余额
@PostMapping("/account/decrease")
ResultData decrease(@RequestParam("userId") Long userId, @RequestParam("money") Long money);
}
4、PayFeignApi:
package xyz.haijin.cloud.apis;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import xyz.haijin.cloud.entities.PayDTO;
import xyz.haijin.cloud.resp.ResultData;
@FeignClient("cloud-gateway")
public interface PayFeignApi {
/**
* 新增一条支付相关流水记录
* @param
* @return
*/
@PostMapping(value = "/pay/add")
public ResultData addPay(@RequestBody PayDTO payDTO);
/**
* openfeign天然支持负载均衡演示
* @return
*/
@GetMapping("/pay/get/info")
public ResultData mylb();
/**
* Resillisence4J CircuitBreaker 的案例
* @param id
* @return
*/
@GetMapping(value = "/pay/circuit/{id}")
public String myCircuit(@PathVariable("id") Integer id);
/**
* Resillience4J CircuitBreaker的案例演示
* @param id
* @return
*/
@GetMapping(value = "/pay/bulkhead/{id}")
public String myBulkhead(@PathVariable("id") Integer id);
/**
* Resilience4J Ratelimiter的案例演示
* @param id
* @return
*/
@GetMapping(value = "/pay/ratelimit/{id}")
public String myRatelimit(@PathVariable("id") Integer id);
/**
* Micrometer监控案例
* @param id
* @return
*/
@GetMapping(value = "/pay/micrometer/{id}")
public String myMicrometer(@PathVariable("id") Integer id);
@GetMapping(value = "/pay/gateway/get/{id}")
public ResultData getById(@PathVariable("id") Integer id);
@GetMapping(value = "/pay/gateway/info")
public ResultData<String> getGatewayInfo();
}
5、PayFeignSentinelApi:
package xyz.haijin.cloud.apis;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import xyz.haijin.cloud.resp.ResultData;
@FeignClient(value = "nacos-payment-provider",fallback = PayFeignSentinelApiFallBack.class)
public interface PayFeignSentinelApi
{
@GetMapping("/pay/nacos/get/{orderNo}")
public ResultData getPayByOrderNo(@PathVariable("orderNo") String orderNo);
}
6、PayFeignSentinelApiFallBack:
package xyz.haijin.cloud.apis;
import org.springframework.stereotype.Component;
import xyz.haijin.cloud.resp.ResultData;
import xyz.haijin.cloud.resp.ReturnCodeEnum;
@Component
public class PayFeignSentinelApiFallBack implements PayFeignSentinelApi{
@Override
public ResultData getPayByOrderNo(String orderNo) {
return ResultData.fail(ReturnCodeEnum.RC500.getCode(),"对方服务宕机或不可用,FallBack服务降级o(╥﹏╥)o");
}
}
7、StorageFeignApi:
package xyz.haijin.cloud.apis;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import xyz.haijin.cloud.resp.ResultData;
@FeignClient(value = "seata-storage-service")
public interface StorageFeignApi {
/**
* 扣减库存
*/
@PostMapping(value = "/storage/decrease")
ResultData decrease(@RequestParam("productId") Long productId, @RequestParam("count") Integer count);
}
8、PayDTO:
package xyz.haijin.cloud.entities;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class PayDTO implements Serializable
{
private Integer id;
//支付流水号
private String payNo;
//订单流水号
private String orderNo;
//用户账号ID
private Integer userId;
//交易金额
private BigDecimal amount;
}
9、GlobalExceptionHandler:
package xyz.haijin.cloud.exp;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import xyz.haijin.cloud.resp.ResultData;
import xyz.haijin.cloud.resp.ReturnCodeEnum;
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(RuntimeException.class)
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ResultData<String> exception(Exception e){
log.info("全局异常: {}", e.getMessage(), e);
return ResultData.fail(ReturnCodeEnum.RC500.getCode(), e.getMessage());
}
}
10、ResultData:
package xyz.haijin.cloud.resp;
import lombok.Data;
import lombok.experimental.Accessors;
@Data
@Accessors(chain = true)
public class ResultData<T> {
private String code;
private String message;
private T data;
private long timestamp;
public ResultData(){
this.timestamp = System.currentTimeMillis();
}
public static <T> ResultData<T> success(T data){
return new ResultData<T>()
.setCode(ReturnCodeEnum.RC200.getCode())
.setMessage(ReturnCodeEnum.RC200.getMessage())
.setData(data);
}
public static <T> ResultData<T> fail(String code, String message){
return new ResultData<T>()
.setCode(code)
.setMessage(message);
}
}
11、ReturnCodeEnum:
package xyz.haijin.cloud.resp;
import lombok.Getter;
import java.util.Arrays;
@Getter
public enum ReturnCodeEnum {
/**
* 操作失败
**/
RC999("999", "操作XXX失败"),
/**
* 操作成功
**/
RC200("200", "success"),
/**
* 服务降级
**/
RC201("201", "服务开启降级保护,请稍后再试!"),
/**
* 热点参数限流
**/
RC202("202", "热点参数限流,请稍后再试!"),
/**
* 系统规则不满足
**/
RC203("203", "系统规则不满足要求,请稍后再试!"),
/**
* 授权规则不通过
**/
RC204("204", "授权规则不通过,请稍后再试!"),
/**
* access_denied
**/
RC403("403", "无访问权限,请联系管理员授予权限"),
/**
* access_denied
**/
RC401("401", "匿名用户访问无权限资源时的异常"),
RC404("404", "404页面找不到的异常"),
/**
* 服务异常
**/
RC500("500", "系统异常,请稍后重试"),
RC375("375", "数学运算异常,请稍后重试"),
INVALID_TOKEN("2001", "访问令牌不合法"),
ACCESS_DENIED("2003", "没有权限访问该资源"),
CLIENT_AUTHENTICATION_FAILED("1001", "客户端认证失败"),
USERNAME_OR_PASSWORD_ERROR("1002", "用户名或密码错误"),
BUSINESS_ERROR("1004", "业务逻辑异常"),
UNSUPPORTED_GRANT_TYPE("1003", "不支持的认证模式");
private final String code;
private final String message;
ReturnCodeEnum(String code, String message) {
this.code = code;
this.message = message;
}
public static ReturnCodeEnum getReturnCodeEnumV1(String code) {
for (ReturnCodeEnum elem : ReturnCodeEnum.values()) {
if (code.equals(elem.getCode())) {
return elem;
}
}
return null;
}
public static ReturnCodeEnum getReturnCodeEnumV2(String code) {
return Arrays.stream(ReturnCodeEnum.values())
.filter(elem -> code.equals(elem.getCode()))
.findFirst()
.orElse(null);
}
/* public static void main(String[] args) {
String testCode = "200";
ReturnCodeEnum returnCodeEnumV1 = getReturnCodeEnumV1(testCode);
System.out.println(returnCodeEnumV1.getCode());
System.out.println(returnCodeEnumV1.getMessage());
System.out.println();
ReturnCodeEnum returnCodeEnumV2 = getReturnCodeEnumV2(testCode);
System.out.println(returnCodeEnumV2.getCode());
System.out.println(returnCodeEnumV2.getMessage());
}*/
}