# 微服务
**Repository Path**: irving2020/microservices
## Basic Information
- **Project Name**: 微服务
- **Description**: 第五阶段微服务学习用
- **Primary Language**: Java
- **License**: Not specified
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2023-12-29
- **Last Updated**: 2024-11-22
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# **_微服务商城项目_**
## **父项目**
每次新建一个子项目都要进行父子相认

修改pom文件
注意parent标签中 version版本要更改(idea2023并没有java8的选项 要更改服务器为阿里云的)

### 子项目
## stock模块
创建子项目模块,在模块的pom中修改parent标签为父项目的这一部分
```xml
cn.tedu
csmall
0.0.1-SNAPSHOT
```

到这里父子相认就完成了
## 2、父项目添加依赖
### 又称锁版本
定义依赖版本的变量,父pom中指定版本,子pom中就不需要指定版本了
除非有额外的版本需要,才在子项目中添加版本信息
```xml
1.8
2.2.2
```
添加完毕之后开始添加依赖,在version标签中添加之前定义的变量${*}的格式来使用变量
```xml
org.mybatis.spring.boot
mybatis-spring-boot-starter
${mybats.version}
```
子项目中不需要添加版本信息,直接添加所需要的依赖就可以自动匹配父项目的版本

添加依赖之后可能会出现找不到依赖包的报错,这种时候可以尝试使用下面的方法
就是把报错的dependency标签赋值到dependencyManagement外面的dependencies(就是跟我的lombok同级,并不是在原来的依赖管理中)
然后刷新maven就可以下载了

# commons模块
maven搞定之后就新建一个模块为commons
```xml
com.github.xiaoymin
knife4j-spring-boot-starter
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-json
org.springframework.boot
spring-boot-starter-tomcat
```
改完pom之后删除启动类 resources包和test包
exclusions标签的作用是保留注解,排除启动的依赖
因为commons包不需要启动,是被其他模块调用的
```xml
org.springframework.boot
spring-boot-starter-web
org.springframework.boot
spring-boot-starter
org.springframework.boot
spring-boot-starter-json
org.springframework.boot
spring-boot-starter-tomcat
```
创建一个包 在commons包下,创建一个pojo包,在pojo包下创建一个cart,在cart下创建一个dto包
,dto包下创建一个CartAddDto实体类
**Dto**是前端传给后端的参数,**Vo**是前端返回给后端的参数,**entity/model/domain/bean/pojo/其他**的实体类为真正的实体类
持久层**mapper/dao/repository**
业务逻辑层较为统一**service/biz**(非常老)
控制层**controller/servlet**
与数据库字段一一对应
## 购物车实体类
```java
@Data
@ApiModel("购物车添加DTO")
public class CartAddDto implements Serializable {
@ApiModelProperty(value = "商品编号",name = "commodityCode",example = "PC100")
private String commodityCode;
@ApiModelProperty(value = "商品价格",name = "price",example = "20")
private Integer price;
@ApiModelProperty(value = "商品数量",name = "count",example = "2")
private Integer count;
@ApiModelProperty(value = "用户id",name = "userId",example = "UU100")
private String userId;
}
```
```java
@Data
@ApiModel("购物车实体类")
public class Cart implements Serializable {
@ApiModelProperty(value = "购物车id",name = "id",example = "1")
private Integer id;
@ApiModelProperty(value = "商品编号",name = "commodityCode",example = "PC100")
private String commodityCode;
@ApiModelProperty(value = "商品价格",name = "price",example = "20")
private Integer price;
@ApiModelProperty(value = "商品数量",name = "count",example = "2")
private Integer count;
@ApiModelProperty(value = "用户id",name = "userId",example = "UU100")
private String userId;
}
```
## 订单类
```java
@ApiModel(value = "新增订单的DTO")
@Data
public class OrderAddDTO implements Serializable {
@ApiModelProperty(value = "用户id",name="userId",example = "UU100")
private String userId;
@ApiModelProperty(value = "商品编号",name="commodityCode",example = "PC100")
private String commodityCode;
@ApiModelProperty(value = "购买数量",name="count",example = "5")
private Integer count;
@ApiModelProperty(value = "总金额",name="money",example = "500")
private Integer money;
}
```
```java
@Data
@ApiModel(value = "订单实体类")
public class Order implements Serializable {
private Integer id;
@ApiModelProperty(value = "用户id",name="userId",example = "UU100")
private String userId;
@ApiModelProperty(value = "商品编号",name="commodityCode",example = "PC100")
private String commodityCode;
@ApiModelProperty(value = "购买数量",name="count",example = "5")
private Integer count;
@ApiModelProperty(value = "总金额",name="money",example = "500")
private Integer money;
}
```
## 库存类
```java
@ApiModel(value = "商品减库存DTO")
@Data
public class StockReduceCountDTO implements Serializable {
@ApiModelProperty(value = "商品编号",name="commodityCode",example = "PC100")
private String commodityCode;
@ApiModelProperty(value = "减库存数",name="reduceCount",example = "5")
private Integer reduceCount;
}
```
```java
@Data
@ApiModel(value = "商品库存")
public class Stock implements Serializable {
@ApiModelProperty(value = "库存Id",name="commodityCode",example = "11")
private Integer id;
@ApiModelProperty(value = "商品编号",name="commodityCode",example = "PC100")
private String commodityCode;
@ApiModelProperty(value = "减库存数",name="reduceCount",example = "5")
private Integer reduceCount;
}
```
## 异常处理类
```java
/**
* 错误代码枚举类型
*/
public enum ResponseCode {
OK(200),
BAD_REQUEST(400),
UNAUTHORIZED(401),
FORBIDDEN(403),
NOT_FOUND(404),
NOT_ACCEPTABLE(406),
CONFLICT(409),
INTERNAL_SERVER_ERROR(500);
private Integer value;
ResponseCode(Integer value) {
this.value = value;
}
public Integer getValue() {
return value;
}
}
```
## 关于枚举
异常处理类使用的就是enum枚举,
这是一个相对特殊的类,
一般情况下用来表示一组常量,
对于一些固定值并且数量有限的情况,
可以使用枚举。
## 业务异常类
```java
/**
* 业务异常
*/
@Data
@EqualsAndHashCode(callSuper = false)
public class CoolSharkServiceException extends RuntimeException {
private ResponseCode responseCode;
public CoolSharkServiceException(ResponseCode responseCode, String message) {
super(message);
setResponseCode(responseCode);
}
}
```
super用法,子调父的私有属性不可直接调用,可以通过super来调用父级的私有属性
# business模块
还是删除test测试类
添加配置[application.yml](csmall-business%2Fsrc%2Fmain%2Fresources%2Fapplication.yml)
```yaml
server:
port: 20000
#公共配置
mybatis:
configuration:
cache-enabled: false # 不启用mybatis缓存
map-underscore-to-camel-case: true # 映射支持驼峰命名法
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 将运行的sql输出到控制台
knife4j:
# 开启增强配置
enable: true
# 生产环境屏蔽,开启将禁止访问在线API文档
production: false
# Basic认证功能,即是否需要通过用户名、密码验证后才可以访问在线API文档
basic:
# 是否开启Basic认证
enable: false
# 用户名,如果开启Basic认证却未配置用户名与密码,默认是:admin/123321
username: root
# 密码
password: root
spring:
profiles:
active: dev
```
server.port:端口号20000,mybatis关闭二级缓存,开启驼峰命名法,把运行的sql输出到控制台
knife4j: 在线api测试文档
spring.profiles.active:表示在这个yml文件加载之后还要在加载一个后缀为dev的yml文件
## 配置类
```java
//当前类是配置Spring扫描环境的配置类,必须添加此注解
@Configuration
//扫描全局异常处理类的包,使其生效
@ComponentScan("cn.tedu.csmall.commons.exception")
public class CommonsConfiguration {
}
```
```java
/**
* Knife4j(Swagger2)的配置
*/
@Configuration
@EnableSwagger2WebMvc
public class Knife4jConfiguration {
/**
* 【重要】指定Controller包路径
*/
private String basePackage = "cn.tedu.csmall.business.controller";
/**
* 分组名称
*/
private String groupName = "base-business";
/**
* 主机名
*/
private String host = "http://java.tedu.cn";
/**
* 标题
*/
private String title = "酷鲨商城项目案例在线API文档--基础businessr-web实例";
/**
* 简介
*/
private String description = "构建基础business-web项目,实现购买";
/**
* 服务条款URL
*/
private String termsOfServiceUrl = "http://www.apache.org/licenses/LICENSE-2.0";
/**
* 联系人
*/
private String contactName = "项目研发部";
/**
* 联系网址
*/
private String contactUrl = "http://java.tedu.cn";
/**
* 联系邮箱
*/
private String contactEmail = "java@tedu.cn";
/**
* 版本号
*/
private String version = "1.0-SNAPSHOT";
@Autowired
private OpenApiExtensionResolver openApiExtensionResolver;
@Bean
public Docket docket() {
String groupName = "1.0-SNAPSHOT";
Docket docket = new Docket(DocumentationType.SWAGGER_2)
.host(host)
.apiInfo(apiInfo())
.groupName(groupName)
.select()
.apis(RequestHandlerSelectors.basePackage(basePackage))
.paths(PathSelectors.any())
.build()
.extensions(openApiExtensionResolver.buildExtensions(groupName));
return docket;
}
private ApiInfo apiInfo() {
return new ApiInfoBuilder()
.title(title)
.description(description)
.termsOfServiceUrl(termsOfServiceUrl)
.contact(new Contact(contactName, contactUrl, contactEmail))
.version(version)
.build();
}
}
```
## service
在business模块下新建service层和impl层
```java
public interface IBusinessService {
void buy();
}
```
## impl
因为是测试模块功能,所以不连数据库没有mapper层,直接在impl层写业务逻辑
```java
@Service
@Slf4j //控制套输出使用到的注解
public class BusinessServiceImpl implements IBusinessService{
@Override
public void buy() {
//编写新增订单雏形
// 先实例化一个新增的订单对象
OrderAddDTO orderAddDTO = new OrderAddDTO();
orderAddDTO.setUserId("UU100");
orderAddDTO.setCount(10);
orderAddDTO.setMoney(199);
orderAddDTO.setCommodityCode("PC100");
//这个实例化的DTO对象,需要传递给订单order模块,让订单模块去新增订单
log.info("订单模块新增订单,传递的参数是:"+orderAddDTO);
}
}
```
## controller
```java
@RestController
@RequestMapping("/base/business")
@Api(tags = "业务触发模块")
public class BusinessController {
@Resource
private IBusinessService businessService;
@PostMapping("/buy")
@ApiOperation("执行业务触发的方法")
public JsonResult buy(){
businessService.buy();
return JsonResult.ok("完美成功");
}
}
```
通过Knife4j测试(正确返回如下)

现在要开始将项目注册到nacos注册中心,现在business模块中增加依赖
```yaml
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
```
然后修改[application-dev.yml](csmall-business%2Fsrc%2Fmain%2Fresources%2Fapplication-dev.yml)文件
```yaml
spring:
application:
name: csmall-business #设置当前项目的名称,注册到nacos的名称与这个相同
cloud:
nacos:
discovery:
server-addr: localhost:8849 #nacos服务地址
```
然后启动nacos再启动项目(顺序很关键)
## 心跳机制
nacos有一个心跳机制, 服务每五秒会向nacos发送一次信息交互,当我们把服务注册到nacos会发送一个心跳包,
nacos会根据心跳包检查当前服务是否在nacos中存在,
如果不存在就按照新业务进行注册,否则代表当前服务是健康状态
如果一个服务连续三次心跳(默认是15秒)没有和Nacos进行信息的交互,就会把当前服务标记为不健康状态
如果连续六次心跳(默认30秒)没有和Nacos进行信息的交互,就会把当前服务从Nacos中删除
这些时间是可以通过配置修改的
## 实例类型分类
实际上nacos的服务类型是有分类的
* 临时实例
* 持久化实例(永久实例)
持久化实例启动时像nacos注册,心跳包的规则和临时实例是一样的,
只是不会将该服务从列表中剔除,一边情况下默认是临时实例,
只有项目的主干业务才会设置为永久实例
# cart模块
新建cart模块,导入pom依赖
```xml
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
com.alibaba
druid
mysql
mysql-connector-java
cn.tedu
csmall-commons
0.0.1-SNAPSHOT
com.github.xiaoymin
knife4j-spring-boot-starter
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
```
把busniess模块的配置文件复制过来
```yaml
server:
port: 20001
#公共配置
mybatis:
configuration:
cache-enabled: false # 不启用mybatis缓存
map-underscore-to-camel-case: true # 映射支持驼峰命名法
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 将运行的sql输出到控制台
knife4j:
# 开启增强配置
enable: true
# 生产环境屏蔽,开启将禁止访问在线API文档
production: false
# Basic认证功能,即是否需要通过用户名、密码验证后才可以访问在线API文档
basic:
# 是否开启Basic认证
enable: false
# 用户名,如果开启Basic认证却未配置用户名与密码,默认是:admin/123321
username: root
# 密码
password: root
spring:
profiles:
active: dev
```
```yaml
spring:
application:
name: csmall-cart #设置当前项目的名称,注册到nacos的名称与这个相同
cloud:
nacos:
discovery:
server-addr: localhost:8849 #nacos服务地址
#ephemeral: false
```
配置好了开始连接数据库,导入准备好的sql文件[csmall_db.sql](sql%2Fcsmall_db.sql),我使用的是MySQL8
,依然是导入两个配置

需要注意一点 knife4j中的扫描包名等要改为cart(如果是从busniess CV来的话)
写好配置之后就可以开始写业务逻辑了
## Mapper
```java
package cn.tedu.csmall.cart.mapper;
import cn.tedu.csmall.commons.pojo.cart.entity.Cart;
import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.springframework.stereotype.Repository;
@Repository
public interface CartMapper {
//向购物车表中新增商品数据
@Insert("insert into cart_tbl(commodity_code,price,count,user_id) " +
"values (#{commodityCode},#{price},#{count},#{userId})")
int insertCart(Cart cart);
//删除购物车中的商品信息
@Delete("delete from cart_tbl where user_id=#{userId} and commodity_code=#{commodityCode}")
int deleteCartByUserIdAndCommodityCode(@Param("userId") String userId,
@Param("commodityCode") String commodityCode);
}
```
## service
```java
package cn.tedu.csmall.cart.service;
import cn.tedu.csmall.commons.pojo.cart.dto.CartAddDto;
import cn.tedu.csmall.commons.pojo.cart.entity.Cart;
/**
* @author 刘浩男
*/
public interface ICartService {
void cartAdd(CartAddDto cartAddDto);
void deleteUserCart(String userId, String commodityCode);
}
```
## impl
```java
package cn.tedu.csmall.cart.service.impl;
import cn.tedu.csmall.cart.mapper.CartMapper;
import cn.tedu.csmall.cart.service.ICartService;
import cn.tedu.csmall.commons.pojo.cart.dto.CartAddDto;
import cn.tedu.csmall.commons.pojo.cart.entity.Cart;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
/**
* @author 刘浩男
*/
@Service
@Slf4j
public class CartServiceImpl implements ICartService {
@Autowired
private CartMapper cartMapper;
//新增购物车方法
@Override
public void cartAdd(CartAddDto cartAddDto) {
//当前方法是CartAddDto对象
//mapper接受的是cart对象,所以要进行转换
Cart cart = new Cart();
//利用BeanUtils。copyProperties方法进行转换
BeanUtils.copyProperties(cartAddDto, cart);
// 调用mapper的方法,将cart对象插入到数据库中
int rows = cartMapper.insertCart(cart);
//打印日志,可以通过rows的值来判断是否成功
log.info("新增购物车商品成功,受影响的行数为:{}", rows);
}
//删除购物车的方法
@Override
public void deleteUserCart(String userId, String commodityCode) {
// 根据用户id和商品编号删除购物车中的商品信息
int i = cartMapper.deleteCartByUserIdAndCommodityCode(userId, commodityCode);
log.info("删除购物车商品成功,受影响的行数为:{}", i);
}
}
```
## Controller
```java
package cn.tedu.csmall.cart.controller;
import cn.tedu.csmall.cart.service.ICartService;
import cn.tedu.csmall.commons.pojo.cart.dto.CartAddDto;
import cn.tedu.csmall.commons.restful.JsonResult;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* @author 刘浩男
*/
@RestController
@RequestMapping("/cart")
public class CartController {
@Autowired
private ICartService cartService;
@PostMapping("/add")
@ApiOperation("新增商品到购物车")
public JsonResult add(CartAddDto cartAddDto) {
cartService.cartAdd(cartAddDto);
return JsonResult.ok("新增商品到购物车成功!");
}
@PostMapping("delete")
@ApiOperation("删除购物车中的商品")
public JsonResult delete(String userId, String commodityCode) {
cartService.deleteUserCart(userId, commodityCode);
return JsonResult.ok("删除购物车中的商品成功!");
}
}
```
# Order模块
新建Order模块,导入依赖,父子相认,与Cart模块一样
```xml
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
com.alibaba
druid
mysql
mysql-connector-java
cn.tedu
csmall-commons
0.0.1-SNAPSHOT
com.github.xiaoymin
knife4j-spring-boot-starter
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
```
编写代码,实现新增订单功能,因为订单不需要删除所以只需要写新增方法就可以
## Mapper
```java
package cn.tedu.csmall.order.mapper;
import cn.tedu.csmall.commons.pojo.order.entity.Order;
import org.apache.ibatis.annotations.Insert;
import org.springframework.stereotype.Repository;
@Repository
public interface OrderMapper {
@Insert("insert into order_tbl(user_id,commodity_code,count,money) " +
"values(#{userId},#{commodityCode},#{count},#{money})")
int insertOrder(Order order);
}
```
## Service
```java
package cn.tedu.csmall.order.service;
import cn.tedu.csmall.commons.pojo.order.dto.OrderAddDTO;
public interface IOrderService {
void OrderAdd(OrderAddDTO orderAddDTO);
}
```
## Impl
```java
package cn.tedu.csmall.order.service.impl;
import cn.tedu.csmall.commons.pojo.order.dto.OrderAddDTO;
import cn.tedu.csmall.commons.pojo.order.entity.Order;
import cn.tedu.csmall.order.mapper.OrderMapper;
import cn.tedu.csmall.order.service.IOrderService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class OrderServiceImpl implements IOrderService {
@Autowired
private OrderMapper orderMapper;
@Override
public void OrderAdd(OrderAddDTO orderAddDTO) {
//1.这里要先进行减少数据库中库存的操作(stock模块)
//2.还要从购物车中删除用户选中的商品(cart模块)
Order order = new Order();
BeanUtils.copyProperties(orderAddDTO,order);
int i = orderMapper.insertOrder(order);
log.info("新增订单成功,插入了:{}条",i);
}
}
```
## Controller
```java
package cn.tedu.csmall.order.controller;
import cn.tedu.csmall.commons.pojo.order.dto.OrderAddDTO;
import cn.tedu.csmall.commons.restful.JsonResult;
import cn.tedu.csmall.order.service.IOrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/order")
@Api(tags = "订单业务触发模块")
public class OrderController {
@Autowired
private IOrderService orderService;
@PostMapping("/add")
@ApiOperation("新增订单")
public JsonResult OrderAdd(OrderAddDTO orderAddDTO){
orderService.OrderAdd(orderAddDTO);
return JsonResult.ok("新增订单成功!");
}
}
```
# Stock模块
编写Stock模块,第一步添加依赖
```xml
4.0.0
cn.tedu
csmall
0.0.1-SNAPSHOT
cn.tedu
csmall-stock
0.0.1-SNAPSHOT
csmall-stock
csmall-stock
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
com.alibaba
druid
mysql
mysql-connector-java
cn.tedu
csmall-commons
0.0.1-SNAPSHOT
com.github.xiaoymin
knife4j-spring-boot-starter
com.alibaba.cloud
spring-cloud-starter-alibaba-nacos-discovery
```
第二步,修改配置文件
```yaml
server:
port: 20003
#公共配置
mybatis:
configuration:
cache-enabled: false # 不启用mybatis缓存
map-underscore-to-camel-case: true # 映射支持驼峰命名法
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 将运行的sql输出到控制台
knife4j:
# 开启增强配置
enable: true
# 生产环境屏蔽,开启将禁止访问在线API文档
production: false
# Basic认证功能,即是否需要通过用户名、密码验证后才可以访问在线API文档
basic:
# 是否开启Basic认证
enable: false
# 用户名,如果开启Basic认证却未配置用户名与密码,默认是:admin/123321
username: root
# 密码
password: root
spring:
profiles:
active: dev
```
```yaml
spring:
application:
name: nacos-stock #设置当前项目的名称,注册到nacos的名称与这个相同
cloud:
nacos:
discovery:
server-addr: localhost:8849 #nacos服务地址
#ephemeral: false
datasource:
url: jdbc:mysql://localhost:3306/csmall_db?useSSL=false&useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai&allowMultiQueries=true
username: root
password: root
```
## 配置类
和Order模块与Cart模块一样 CV就可以
## Mapper
需要注意一些问题,首先@Param在多参数时必须添加,否则会报错,减库存的逻辑与订单和购物车有所区别,首先是需要一些逻辑运算
要判断库存是否足够
```java
package cn.tedu.csmall.stock.mapper;
import cn.tedu.csmall.commons.pojo.stock.entity.Stock;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Repository;
@Repository
public interface StockMapper {
@Update("update stock_tbl set count = count - #{countNum} " +
" where commodity_code = #{commodityCode} AND " +
" count>=#{countNum}")
int stockUpdate(@Param("commodityCode") String commodityCode, @Param("countNum") int countNum);
}
```
## Service
service依旧接参为DTO
```java
package cn.tedu.csmall.stock.service;
import cn.tedu.csmall.commons.pojo.stock.dto.StockReduceCountDTO;
public interface IStockService {
void reduceStock(StockReduceCountDTO stockReduceCountDTO);
}
```
## Impl
这里要判断他是否成功,如果失败要抛出异常,如果不抛异常可能会造成程序服务停机
```java
package cn.tedu.csmall.stock.service.impl;
import cn.tedu.csmall.commons.exception.CoolSharkServiceException;
import cn.tedu.csmall.commons.pojo.stock.dto.StockReduceCountDTO;
import cn.tedu.csmall.commons.pojo.stock.entity.Stock;
import cn.tedu.csmall.commons.restful.ResponseCode;
import cn.tedu.csmall.stock.mapper.StockMapper;
import cn.tedu.csmall.stock.service.IStockService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class StockServiceImpl implements IStockService {
@Autowired
private StockMapper stockMapper;
@Override
public void reduceStock(StockReduceCountDTO stockReduceCountDTO) {
int rows = stockMapper.stockUpdate(stockReduceCountDTO.getCommodityCode(),
stockReduceCountDTO.getReduceCount());
if (rows==0){
throw new CoolSharkServiceException(ResponseCode.BAD_REQUEST,"库存不足或商品不存在");
}
log.info("库存扣减成功");
}
}
```
## Controller
```java
package cn.tedu.csmall.stock.controller;
import cn.tedu.csmall.commons.pojo.stock.dto.StockReduceCountDTO;
import cn.tedu.csmall.stock.service.IStockService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/stock")
@Api(tags = "库存业务模块")
public class StockController {
@Autowired
private IStockService stockService;
@PostMapping("/reduce")
@ApiOperation(value = "扣减库存")
public void reduceStock(StockReduceCountDTO stockReduceCountDTO) {
stockService.reduceStock(stockReduceCountDTO);
}
}
```
# Dubbo

默认使用Dubbo协议,支持很多序列化协议,默认使用Hessian2,默认情况下支持的协议有如下特征:
* 采用NIO单一长连接
* 优秀的并发性能,但是处理大型文件的能力差
Dubbo方便支持高并发和高性能

consumer服务的消费者,指的是服务的调用者(使用者),也是需要注册到注册中心,provider启动之后把服务都给到注册中心
consumer启动之后能够看到注册中心所有的服务,发现之后就可以根据RPC远程过程调用的方式调用服务,Dubbo中,远程调用
依据是服务的提供者在Nacos中的注册服务名称