# cloud2024 **Repository Path**: defchigga/cloud2024 ## Basic Information - **Project Name**: cloud2024 - **Description**: 尚硅谷SpringCloud2024练习 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-09-12 - **Last Updated**: 2024-10-15 ## Categories & Tags **Categories**: Uncategorized **Tags**: SpringBoot, SpringCloud ## README # 尚硅谷SpringCloud2024练习项目 ## 3.微服务架构编码Base工程模块构建 ### 3.1 完善时间格式 > jackson - 局部配置 ```java /** * 创建时间 */ @Column(name = "create_time") @Schema(title = "创建时间") @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8") private LocalDateTime createTime; ``` - 全局配置 ```yaml spring: jackson: date-format: yyyy-MM-dd HH:mm:ss time-zone: GMT+8 ``` ### 3.2 完善统一返回枚举 > 解决:时间格式问题 编写步骤: 1. 举值 2. 构造 3. 遍历 > ResponseCodeEnum.java ```java package com.atguigu.cloud.response; import lombok.Getter; import java.util.Arrays; /** * 响应码枚举 * 定义一个通用的枚举类:举值-构造-遍历 */ @Getter public enum ResponseCodeEnum { // 1.举值 /** * 操作成功 **/ RC200(200, "success"), /** * 服务降级 **/ // RC201(201, "服务开启降级保护,请稍后再试!"), RC201(201, "创建成功"), /** * 热点参数限流 **/ RC202(202, "热点参数限流,请稍后再试!"), /** * 系统规则不满足 **/ RC203(203, "系统规则不满足要求,请稍后再试!"), /** * 授权规则不通过 **/ // RC204(204, "授权规则不通过,请稍后再试!"), RC204(204, "删除成功"), /** * 参数校验异常 **/ RC400(400, "参数校验异常"), /** * 未授权 **/ RC401(401, "匿名用户访问无权限资源时的异常"), /** * 用户未登录 **/ RC402(402, "用户未登录"), /** * 无访问权限 **/ RC403(403, "无访问权限,请联系管理员授予权限"), /** * 请求路径不存在 **/ RC404(404, "404页面找不到的异常"), /** * 请求方法不允许 **/ RC405(405, "请求方式错误"), /** * 请求超时 **/ RC408(408, "请求超时"), /** * 服务异常 **/ RC500(500, "系统异常,请稍后重试"), RC502(502, "网关异常,请稍后重试"), RC503(503, "服务不可用,请稍后重试"), RC504(504, "网关超时,请稍后重试"), RC375(375, "数学运算异常,请稍后重试"), /** * 操作失败 **/ RC999(999, "操作XXX失败"), /** * 自定义错误码 */ CLIENT_AUTHENTICATION_FAILED(1001, "客户端认证失败"), USERNAME_OR_PASSWORD_ERROR(1002, "用户名或密码错误"), BUSINESS_ERROR(1004, "业务逻辑异常"), UNSUPPORTED_GRANT_TYPE(1003, "不支持的认证模式"), INVALID_TOKEN(2001, "访问令牌不合法"), ACCESS_DENIED(2003, "没有权限访问该资源"); // 2.构造 /** * 响应码 */ private final Integer code; /** * 响应信息 */ private final String message; /** * 构造器 * * @param code 状态码 * @param message 响应信息 */ ResponseCodeEnum(Integer code, String message) { this.code = code; this.message = message; } // 3.遍历 /** * 根据code查找对应的枚举 * * @param code 状态码 * @return 枚举值 */ public static ResponseCodeEnum getResponseCodeEnum(Integer code) { return Arrays.stream(ResponseCodeEnum.values()).filter(item -> item.getCode().equals(code)).findFirst().orElse(null); } } ``` ### 3.3 完善统一返回对象 > 解决:返回值不统一问题 > ResultData.java ```java package com.atguigu.cloud.response; import lombok.Data; import lombok.NoArgsConstructor; import lombok.experimental.Accessors; /** * 统一响应结果 */ @Data @NoArgsConstructor @Accessors(chain = true) public class ResultData { /** * 响应数据 */ private T data; /** * 响应状态码 */ private Integer code; /** * 响应消息 */ private String message; /** * 时间戳 - 接口调用时间(方便调试是否为缓存数据) */ private Long timestamp; /** * 构造方法 * * @param data 数据 * @param code 状态码 * @param message 消息 */ public ResultData(T data, Integer code, String message) { this.data = data; this.code = code; this.message = message; this.timestamp = System.currentTimeMillis(); } /** * 通用成功响应 * * @param data 数据 */ public static ResultData success(T data) { /* // 设置 Http Status Code 状态码 RequestAttributes attributes = RequestContextHolder.getRequestAttributes(); HttpServletResponse response = ((ServletRequestAttributes) attributes).getResponse(); response.setStatus(200); */ return new ResultData<>(data, ResponseCodeEnum.RC200.getCode(), ResponseCodeEnum.RC200.getMessage()); } /** * 自定义成功响应数据与枚举 * * @param data 数据 * @param responseCodeEnum 响应码枚举 */ public static ResultData success(T data, ResponseCodeEnum responseCodeEnum) { return new ResultData<>(data, responseCodeEnum.getCode(), responseCodeEnum.getMessage()); } /** * 自定义成功响应 * * @param data 数据 * @param code 状态码 * @param message 消息 */ public static ResultData success(T data, Integer code, String message) { return new ResultData<>(data, code, message); } /** * 自定义成功响应数据与消息 * * @param data 数据 * @param message 消息 */ public static ResultData success(T data, String message) { return new ResultData<>(data, ResponseCodeEnum.RC200.getCode(), message); } /** * 通用失败响应 */ public static ResultData fail() { return new ResultData<>(null, ResponseCodeEnum.RC500.getCode(), ResponseCodeEnum.RC500.getMessage()); } /** * 自定义失败响应枚举 * * @param responseCodeEnum 响应码枚举 */ public static ResultData fail(ResponseCodeEnum responseCodeEnum) { return new ResultData<>(null, responseCodeEnum.getCode(), responseCodeEnum.getMessage()); } /** * 自定义失败响应状态码与消息 * * @param code 状态码 * @param message 消息 */ public static ResultData fail(Integer code, String message) { return new ResultData<>(null, code, message); } /** * 自定义失败响应消息 * * @param message 消息 */ public static ResultData fail(String message) { return new ResultData<>(null, ResponseCodeEnum.RC500.getCode(), message); } } ``` ### 3.4 完善测试返回时间和统一值 > 接口文档地址:https://apifox.com/apidoc/shared-326b149b-f40d-4e1b-812b-833c3665f02d ### 3.5 完善全局异常处理 > 解决: > 1.不用再手写 try catch > 2.统一返回自定义异常信息 > GlobalExceptionHandler.java ```java package com.atguigu.cloud.exception; import com.atguigu.cloud.response.ResultData; 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; /** * 全局异常处理器 */ @Slf4j @RestControllerAdvice public class GlobalExceptionHandler { /** * 全局异常处理器 */ @ExceptionHandler(Exception.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ResultData globalExceptionHandler(Exception e) { log.error("全局异常信息:{}", e.getMessage(), e); return ResultData.fail(e.getMessage()); } /** * 运行时异常处理器 */ @ExceptionHandler(RuntimeException.class) @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) public ResultData runtimeExceptionHandler(RuntimeException e) { log.error("运行时异常信息:{}", e.getMessage(), e); return ResultData.fail(e.getMessage()); } } ``` ### 3.6 新增Order订单微服务模块 新建微服务模块步骤: 1. 建Module,新建一个建 `cloud-consumer-order80` 模块,继承父模块 2. 改POM,添加对应依赖与插件等 3. 写YML,配置端口号,服务名,注册中心地址等 4. 主启动,修改 `Main` 类名为 `Main80`,并添加SpringBoot相关注解与启动代码 5. 业务类,Just do it! ### 3.7 工程重构,提取全局通用模块 > 解决:将微服务中重复的代码与配置提取到公共模块,方便管理维护 1. 建Module,新建一个 `cloud-api-commons` 模块,继承付模块 2. 改POM,添加对应依赖与插件等 3. 提取全局重复通用组件/api/接口/工具类等到`cloud-api-commons`模块 4. 执行清除并安装指令,mvn clear & mvn install 5. 删除其余模块原先使用存在的重复代码与依赖 6. 最后引入自己定义的api通用包,重载 maven > 使用`RedisTemplate`调用微服务 1. 全局配置注册 Bean RestTemplateConfig. java ```java package com.atguigu.cloud.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.web.client.RestTemplate; @Configuration public class RestTemplateConfig { @Bean public RestTemplate restTemplate() { return new RestTemplate(); } } ``` 2. 调用微服务 OrderController.java ```java package com.atguigu.cloud.controller; import com.atguigu.cloud.entities.Pay; import com.atguigu.cloud.entities.PayDTO; import com.atguigu.cloud.response.ResponseCodeEnum; import com.atguigu.cloud.response.ResultData; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.annotation.Resource; import lombok.extern.slf4j.Slf4j; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestTemplate; @Tag(name = "订单微服务模块", description = "订单交易CRUD") @RequestMapping("/order") @RestController @Slf4j public class OrderController { /** * 支付微服务地址 */ private static final String PAY_URL = "http://localhost:8001"; private static final String PAY_ADD_URL = "/pay/add"; private static final String PAY_DELETE_URL = "/pay/delete/{id}"; private static final String PAY_UPDATE_URL = "/pay/update"; private static final String PAY_GETBYID_URL = "/pay/getById/{id}"; private static final String PAY_GETALL_URL = "/pay/getAll"; @Resource private RestTemplate restTemplate; /** * 订单新增支付信息 * * @param payDTO 支付信息数据传输对象 * @return ResultData */ @PostMapping(PAY_ADD_URL) @Operation(summary = "订单新增支付信息", description = "订单新增支付操作") @Parameter(name = "payDTO", description = "支付信息数据传输对象") public ResultData addOrderPay(@RequestBody PayDTO payDTO) { log.info("订单新增支付信息:{}", payDTO); return restTemplate.postForObject(PAY_URL + PAY_ADD_URL, payDTO, ResultData.class); } /** * 订单删除支付信息 * * @param id 支付id * @return ResultData */ @DeleteMapping(PAY_DELETE_URL) @Operation(summary = "订单删除支付信息", description = "订单删除支付操作") @Parameter(name = "id", description = "支付id", required = true, example = "1") public ResultData deleteOrderPay(@PathVariable("id") Integer id) { log.info("订单删除支付信息:{}", id); try { restTemplate.delete(PAY_URL + PAY_DELETE_URL, id); return ResultData.success(1, ResponseCodeEnum.RC204); } catch (Exception e) { return ResultData.fail(e.getMessage()); } } /** * 订单更新支付信息 * * @param pay 支付信息 * @return ResultData */ @PutMapping(PAY_UPDATE_URL) @Operation(summary = "订单更新支付信息", description = "订单更新支付操作") @Parameter(name = "pay", description = "支付信息") public ResultData updateOrderPay(@RequestBody Pay pay) { log.info("订单更新支付信息:{}", pay); try { restTemplate.put(PAY_URL + PAY_UPDATE_URL, pay); return ResultData.success(1, "更新成功"); } catch (Exception e) { return ResultData.fail(e.getMessage()); } } /** * 订单根据id查询支付信息 * * @param id 支付id * @return ResultData */ @GetMapping(PAY_GETBYID_URL) @Operation(summary = "订单根据id查询支付信息", description = "订单根据id查询支付操作") @Parameter(name = "id", description = "支付id", required = true, example = "1") public ResultData getOrderPayById(@PathVariable("id") Integer id) { log.info("订单根据id查询支付信息:{}", id); return restTemplate.getForObject(PAY_URL + PAY_GETBYID_URL, ResultData.class, id); } /** * 订单查询所有支付信息 * @return ResultData */ @GetMapping(PAY_GETALL_URL) @Operation(summary = "订单查询所有支付信息", description = "订单查询所有支付操作") public ResultData getOrderPayAll() { log.info("订单查询所有支付信息"); return restTemplate.getForObject(PAY_URL + PAY_GETALL_URL, ResultData.class); } } ``` > 遗留问题:使用RedisTemplate调用微服务硬编码写死问题 ## 4. Consul 服务注册与发现 ### 4.1 Consul之学习方法论和eureka为什么凉凉 ### 4.2 Consul之是什么怎么玩