diff --git a/broker.conf b/broker.conf new file mode 100644 index 0000000..d1e694e --- /dev/null +++ b/broker.conf @@ -0,0 +1 @@ +"brokerIP1=127.0.0.1" diff --git a/docker-compose.yml b/docker-compose.yml index 75e59d4..af0825e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -1,61 +1,62 @@ -version: '3.8' - services: - # NameServer 服务 - rocketmq-namesrv: - image: apache/rocketmq:5.2.0 - container_name: rocketmq-namesrv + rmqnamesrv: + image: apache/rocketmq:5.3.2 # 或换成 5.2.0,如果你必须用这个版本 + container_name: rmqnamesrv + restart: unless-stopped ports: - "9876:9876" - # 移除挂载配置 + environment: + - TZ=Asia/Shanghai + - JAVA_OPT_EXT=-Xms512m -Xmx512m -Xmn256m command: sh mqnamesrv - restart: always networks: - - rocketmq-network + - rmq + + rmqbroker: + image: apache/rocketmq:5.3.2 + container_name: rmqbroker + restart: unless-stopped + ports: + - "10911:10911" # FastRemoting 端口(客户端主要连接这里) + - "10909:10909" # HA 端口(可选) environment: - TZ=Asia/Shanghai - # 调整JVM内存,避免内存不足 - - ROCKETMQ_OPT="-Xms256m -Xmx256m -Xmn128m" - - # Broker 服务 - rocketmq-broker: - image: apache/rocketmq:5.2.0 - container_name: rocketmq-broker - ports: - - "10911:10911" - - "10909:10909" - # 移除挂载配置,直接通过环境变量传递核心配置 + - NAMESRV_ADDR=rmqnamesrv:9876 + - JAVA_OPT_EXT=-Xms1g -Xmx1g -Xmn512m + depends_on: + - rmqnamesrv command: > - sh mqbroker -n rocketmq-namesrv:9876 - -c /home/rocketmq/conf/broker.conf - -Drocketmq.broker.ip1=你的服务器IP - -Drocketmq.broker.autoCreateTopicEnable=true - -Drocketmq.broker.autoCreateSubscriptionGroup=true - depends_on: - - rocketmq-namesrv - environment: - - TZ=Asia/Shanghai - - NAMESRV_ADDR=rocketmq-namesrv:9876 - - ROCKETMQ_OPT="-Xms512m -Xmx512m -Xmn256m" - restart: always + sh -c " + echo ' + brokerClusterName = DefaultCluster + brokerName = broker-a + brokerId = 0 + deleteWhen = 04 + fileReservedTime = 48 + brokerRole = ASYNC_MASTER + flushDiskType = ASYNC_FLUSH + autoCreateTopicEnable = true + autoCreateSubscriptionGroup = true + ' > /home/rocketmq/rocketmq-5.3.2/conf/broker.conf && + mqbroker -n rmqnamesrv:9876 -c /home/rocketmq/rocketmq-5.3.2/conf/broker.conf + " networks: - - rocketmq-network + - rmq - # RocketMQ 控制台(端口改为8083) - rocketmq-dashboard: - image: apacherocketmq/rocketmq-dashboard:2.0.0 - container_name: rocketmq-dashboard + rmqdashboard: + image: apacherocketmq/rocketmq-dashboard:latest + container_name: rmqdashboard + restart: unless-stopped ports: - - "8083:8080" # 宿主机8083端口映射到容器8080 + - "18080:8080" # 主机端口改成 18080,避免与你其他服务冲突 environment: - - NAMESRV_ADDR=rocketmq-namesrv:9876 - TZ=Asia/Shanghai + - JAVA_OPTS=-Drocketmq.namesrv.addr=rmqnamesrv:9876 -Xms512m -Xmx512m depends_on: - - rocketmq-namesrv - restart: always + - rmqnamesrv networks: - - rocketmq-network + - rmq networks: - rocketmq-network: + rmq: driver: bridge \ No newline at end of file diff --git a/javamemories-common/pom.xml b/javamemories-common/pom.xml index b5f6199..1352696 100644 --- a/javamemories-common/pom.xml +++ b/javamemories-common/pom.xml @@ -24,5 +24,16 @@ 3.8.1 test + + org.projectlombok + lombok + provided + + + org.jetbrains + annotations + 13.0 + compile + diff --git a/javamemories-common/src/main/java/cn/mayiming/Common/Entity/StockDeductDTO.java b/javamemories-common/src/main/java/cn/mayiming/Common/Entity/StockDeductDTO.java new file mode 100644 index 0000000..20537e8 --- /dev/null +++ b/javamemories-common/src/main/java/cn/mayiming/Common/Entity/StockDeductDTO.java @@ -0,0 +1,14 @@ +package cn.mayiming.Common.Entity; + + +import lombok.Data; +import org.jetbrains.annotations.NotNull; + +@Data +public class StockDeductDTO { + @NotNull + private Long id; + + @NotNull + private Integer deductNum; +} diff --git a/javamemories-gateway/src/main/resources/application.yml b/javamemories-gateway/src/main/resources/application.yml index 19ffbfa..28a9165 100644 --- a/javamemories-gateway/src/main/resources/application.yml +++ b/javamemories-gateway/src/main/resources/application.yml @@ -44,6 +44,22 @@ spring: # 按 IP 限流(默认) key-resolver: "#{@ipKeyResolver}" + - id: stock-service + uri: lb://stock-service + predicates: + - Path=/stock/** + filters: + - RewritePath=/stock/(?.*), /${segment} + # 全局限流配置 + - name: RequestRateLimiter + args: + # 令牌桶填充速率:每秒生成 1 个令牌(即 10 秒 10 个) + redis-rate-limiter.replenishRate: 1 + # 令牌桶最大容量:最多存 10 个令牌(允许突发 10 次请求) + redis-rate-limiter.burstCapacity: 10 + # 按 IP 限流(默认) + key-resolver: "#{@ipKeyResolver}" + sentinel: # Sentinel 控制台地址(如果启动了控制台,用于可视化配置) transport: diff --git a/order-service/pom.xml b/order-service/pom.xml index b2fecf4..a570083 100644 --- a/order-service/pom.xml +++ b/order-service/pom.xml @@ -78,5 +78,9 @@ mysql-connector-j runtime + + org.apache.rocketmq + rocketmq-spring-boot-starter + diff --git a/order-service/src/main/java/cn/mayiming/Controller/OrderController.java b/order-service/src/main/java/cn/mayiming/Controller/OrderController.java index efbab80..97d5b1f 100644 --- a/order-service/src/main/java/cn/mayiming/Controller/OrderController.java +++ b/order-service/src/main/java/cn/mayiming/Controller/OrderController.java @@ -3,27 +3,77 @@ package cn.mayiming.Controller; import cn.mayiming.Service.OrderService; import cn.mayiming.entity.Order; +import cn.mayiming.entity.Result; +import cn.mayiming.entity.StockDeductDTO; import cn.mayiming.entity.User; import com.alibaba.csp.sentinel.annotation.SentinelResource; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; import java.util.List; +import java.util.Map; @RestController +@RequestMapping("/order") public class OrderController { @Autowired private OrderService orderService; - @PostMapping("/order") + @PostMapping("/search") public User getUserOrder(@RequestBody User user){ //throw new RuntimeException("Cuowu "); return orderService.SearchUserbyname(user); } - @GetMapping("/order") + @GetMapping("/list") public List getUserOrder(@RequestParam Integer id){ return orderService.OrderListById(id); } + + @PostMapping("/create") + public Result createOrder(@RequestBody Map jsonMap){ + try { + // 1. 安全获取userId:先转Number,再取int值(兼容Integer/Long) + Number userIdNum = (Number) jsonMap.get("userId"); + if (userIdNum == null) { + return Result.fail(400, "userId不能为空"); + } + int userId = userIdNum.intValue(); + + // 2. 安全获取stockid:先转Number,再取long值(兼容Integer/Long) + Number stockIdNum = (Number) jsonMap.get("stockid"); + if (stockIdNum == null) { + return Result.fail(400, "stockid不能为空"); + } + Long stockId = stockIdNum.longValue(); + + // 3. 安全获取deductnum:先转Number,再取int值(兼容Integer/Long) + Number deductNumNum = (Number) jsonMap.get("deductnum"); + if (deductNumNum == null) { + return Result.fail(400, "deductnum不能为空"); + } + int deductNum = deductNumNum.intValue(); + + // 4. 组装DTO + StockDeductDTO stockDeductDTO = new StockDeductDTO(); + stockDeductDTO.setId(stockId); + stockDeductDTO.setDeductNum(deductNum); + + // 5. 调用服务 + int serviceResult = orderService.createOrder(stockDeductDTO, userId); + if (serviceResult != 0) { + // 库存扣减/订单创建失败 + return Result.fail("库存扣减失败,订单创建失败"); + } + // 成功:可以返回额外数据(比如订单ID) + return Result.success("库存扣减成功,订单创建成功"); + + } catch (Exception e) { + // 捕获所有异常,返回友好提示 + e.printStackTrace(); + return Result.fail("订单创建异常:" + e.getMessage()); + } + + } } diff --git a/order-service/src/main/java/cn/mayiming/Service/OrderService.java b/order-service/src/main/java/cn/mayiming/Service/OrderService.java index daddd6b..99bfcdc 100644 --- a/order-service/src/main/java/cn/mayiming/Service/OrderService.java +++ b/order-service/src/main/java/cn/mayiming/Service/OrderService.java @@ -2,8 +2,12 @@ package cn.mayiming.Service; import cn.mayiming.Mapper.OrderMapper; import cn.mayiming.entity.Order; +import cn.mayiming.entity.StockDeductDTO; import cn.mayiming.entity.User; +import cn.mayiming.feign.StockFeignClient; import cn.mayiming.feign.UserFeignClient; +import jakarta.annotation.Resource; +import org.apache.rocketmq.spring.core.RocketMQTemplate; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -17,6 +21,12 @@ public class OrderService { @Autowired private OrderMapper orderMapper; + @Autowired + private StockFeignClient stockFeignClient; + + @Resource + private RocketMQTemplate rocketMQTemplate; + public User SearchUserbyname (User user) { return userFeignClient.selectByUsername(user); } @@ -31,4 +41,30 @@ public class OrderService { } + public void sendPaySuccessMessage(String msg) { + + // 2. 发送消息:格式为 "主题:标签" + // 同步发送:等待 Broker 确认,返回发送结果(可靠) + try { + // 参数1:topic:tag(对应消费者的 topic + selectorExpression) + // 参数2:消息体(自动序列化) + rocketMQTemplate.convertAndSend("pay_topic:pay_success", msg); + } catch (Exception e) { + // 发送失败处理(如记录日志、重试、告警) + System.err.println("消息发送失败:" + ",原因:" + e.getMessage()); + throw new RuntimeException("消息发送失败", e); + } + } + + public int createOrder(StockDeductDTO stockDeductDTO,int i) { + int res = 0; + if(stockFeignClient.deductstock(stockDeductDTO)==0){ + res ++; + } + if(userFeignClient.GetUserByid(i)==null){ + res ++; + } + return res; + } + } diff --git a/order-service/src/main/java/cn/mayiming/entity/Result.java b/order-service/src/main/java/cn/mayiming/entity/Result.java new file mode 100644 index 0000000..beb5a10 --- /dev/null +++ b/order-service/src/main/java/cn/mayiming/entity/Result.java @@ -0,0 +1,69 @@ +package cn.mayiming.entity; + +/** + * 通用接口响应结果类 + * @param 响应数据类型 + */ +public class Result { + // 响应码:200成功,500失败,400参数错误等 + private Integer code; + // 响应消息 + private String msg; + // 响应数据 + private T data; + + // 静态构造方法(推荐) + // 成功(无数据) + public static Result success() { + return new Result<>(200, "操作成功", null); + } + + // 成功(带数据) + public static Result success(T data) { + return new Result<>(200, "操作成功", data); + } + + // 失败 + public static Result fail(String msg) { + return new Result<>(500, msg, null); + } + + // 失败(自定义码+消息) + public static Result fail(Integer code, String msg) { + return new Result<>(code, msg, null); + } + + // 构造器、getter/setter + public Result() {} + + public Result(Integer code, String msg, T data) { + this.code = code; + this.msg = msg; + this.data = data; + } + + // getter和setter + public Integer getCode() { + return code; + } + + public void setCode(Integer code) { + this.code = code; + } + + public String getMsg() { + return msg; + } + + public void setMsg(String msg) { + this.msg = msg; + } + + public T getData() { + return data; + } + + public void setData(T data) { + this.data = data; + } +} \ No newline at end of file diff --git a/order-service/src/main/java/cn/mayiming/entity/StockDeductDTO.java b/order-service/src/main/java/cn/mayiming/entity/StockDeductDTO.java new file mode 100644 index 0000000..0fbac97 --- /dev/null +++ b/order-service/src/main/java/cn/mayiming/entity/StockDeductDTO.java @@ -0,0 +1,17 @@ +package cn.mayiming.entity; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class StockDeductDTO { + @NotNull + @JsonProperty("id") + private Long id; + + @NotNull + @JsonProperty("deductnum") + private Integer deductNum; +} diff --git a/order-service/src/main/java/cn/mayiming/feign/StockFeignClient.java b/order-service/src/main/java/cn/mayiming/feign/StockFeignClient.java new file mode 100644 index 0000000..592110d --- /dev/null +++ b/order-service/src/main/java/cn/mayiming/feign/StockFeignClient.java @@ -0,0 +1,14 @@ +package cn.mayiming.feign; + + +import cn.mayiming.entity.StockDeductDTO; +import jakarta.validation.Valid; +import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; + +@FeignClient(name = "stock-service") +public interface StockFeignClient { + @PostMapping("/stock/deduct") + int deductstock(@RequestBody StockDeductDTO stockDeductDTO); +} diff --git a/order-service/src/main/resources/application.yml b/order-service/src/main/resources/application.yml index 34fe535..99082fe 100644 --- a/order-service/src/main/resources/application.yml +++ b/order-service/src/main/resources/application.yml @@ -24,7 +24,7 @@ spring: # 数据库驱动类(MySQL 8.x 用 com.mysql.cj.jdbc.Driver,5.x 用 com.mysql.jdbc.Driver) driver-class-name: com.mysql.cj.jdbc.Driver # 数据库连接 URL(替换为你的数据库地址、端口、库名,如 user_db) - url: jdbc:mysql://rm-f8z6oc5a03331500p8o.mysql.rds.aliyuncs.com:3306/test?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + url: jdbc:mysql://rm-f8z6oc5a03331500p8o.mysql.rds.aliyuncs.com:3306/order_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true # 数据库用户名(默认 root,根据实际情况修改) username: root # 数据库密码(替换为你的 MySQL 密码) @@ -38,4 +38,18 @@ spring: # 连接超时时间(毫秒) connection-timeout: 30000 # 连接最大存活时间(毫秒) - max-lifetime: 1800000 \ No newline at end of file + max-lifetime: 1800000 +rocketmq: + # NameServer 地址(替换为你的 RocketMQ 服务器IP) + name-server: 127.0.0.1:9876 + producer: + # 生产者组名(必须唯一,建议:服务名+producer) + group: pay-service-producer + # 发送失败重试次数(默认2次) + retry-times-when-send-failed: 3 + # 发送超时时间(默认3000ms) + send-message-timeout: 5000 + # 消息最大长度(默认4M) + max-message-size: 4194304 + # 压缩消息阈值(超过4K自动压缩) + compress-message-body-threshold: 4096 \ No newline at end of file diff --git a/pay-service/pom.xml b/pay-service/pom.xml index 08b11f4..f6c3c42 100644 --- a/pay-service/pom.xml +++ b/pay-service/pom.xml @@ -19,10 +19,69 @@ - junit - junit - 3.8.1 - test + org.springframework.boot + spring-boot-starter-web + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.alibaba.csp + sentinel-core + + + org.projectlombok + lombok + provided + + + org.mybatis + mybatis + 3.5.19 + compile + + + org.mybatis + mybatis-spring + 4.0.0 + compile + + + org.springframework.boot + spring-boot-starter-jdbc + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + + + + + com.mysql + mysql-connector-j + runtime + + + org.apache.rocketmq + rocketmq-spring-boot-starter + + diff --git a/pay-service/src/main/java/cn/mayiming/App.java b/pay-service/src/main/java/cn/mayiming/App.java index 93abe3a..bd10567 100644 --- a/pay-service/src/main/java/cn/mayiming/App.java +++ b/pay-service/src/main/java/cn/mayiming/App.java @@ -1,13 +1,17 @@ package cn.mayiming; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + /** * Hello world! * */ +@SpringBootApplication public class App { public static void main( String[] args ) { - System.out.println( "Hello World!" ); + SpringApplication.run(App.class, args); } } diff --git a/pay-service/src/main/java/cn/mayiming/Consumer/payConsumer.java b/pay-service/src/main/java/cn/mayiming/Consumer/payConsumer.java new file mode 100644 index 0000000..02645ac --- /dev/null +++ b/pay-service/src/main/java/cn/mayiming/Consumer/payConsumer.java @@ -0,0 +1,18 @@ +package cn.mayiming.Consumer; + +import org.apache.rocketmq.spring.annotation.RocketMQMessageListener; +import org.apache.rocketmq.spring.core.RocketMQListener; +import org.springframework.stereotype.Component; + +@Component +@RocketMQMessageListener( + topic = "pay_topic", + consumerGroup = "pay-service-consumer", + selectorExpression = "pay_success" +) +public class payConsumer implements RocketMQListener { + @Override + public void onMessage(String s) { + System.out.println("接收到消息"+s); + } +} diff --git a/pay-service/src/main/resources/application.yml b/pay-service/src/main/resources/application.yml new file mode 100644 index 0000000..18009b2 --- /dev/null +++ b/pay-service/src/main/resources/application.yml @@ -0,0 +1,52 @@ +server: + port: 9093 +spring: + application: + name: pay-service + cloud: + nacos: + # 服务注册发现配置 + discovery: + server-addr: localhost:8848 # Nacos 服务地址(默认端口 8848) + namespace: public # 命名空间(默认 public,自定义需先在 Nacos 控制台创建) + group: DEFAULT_GROUP # 分组(默认 DEFAULT_GROUP) + service: pay-service # 注册到 Nacos 的服务名(建议和子项目 artifactId 一致) + # 配置管理配置(如果引入了 config 依赖才需要) + config: + server-addr: localhost:8848 # 和 discovery 一致 + file-extension: yaml + namespace: public + group: DEFAULT_GROUP + config: + import: nacos:${spring.application.name}.${spring.cloud.nacos.config.file-extension}?server-addr=${spring.cloud.nacos.config.server-addr} + datasource: + # 数据库驱动类(MySQL 8.x 用 com.mysql.cj.jdbc.Driver,5.x 用 com.mysql.jdbc.Driver) + driver-class-name: com.mysql.cj.jdbc.Driver + # 数据库连接 URL(替换为你的数据库地址、端口、库名,如 user_db) + url: jdbc:mysql://rm-f8z6oc5a03331500p8o.mysql.rds.aliyuncs.com:3306/pay_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + # 数据库用户名(默认 root,根据实际情况修改) + username: root + # 数据库密码(替换为你的 MySQL 密码) + password: Root123456 + # 可选:连接池配置(推荐使用 HikariCP,Spring Boot 2.x 默认) + hikari: + # 连接池最大连接数 + maximum-pool-size: 10 + # 连接池最小空闲连接数 + minimum-idle: 2 + # 连接超时时间(毫秒) + connection-timeout: 30000 + # 连接最大存活时间(毫秒) + max-lifetime: 1800000 +rocketmq: + # NameServer 地址(替换为你的 RocketMQ 服务器IP) + name-server: 127.0.0.1:9876 + consumer: + # 消费者组名(必须唯一,建议按服务+用途命名) + group: order-service-consumer + # 消费模式:CONCURRENTLY(并发消费,默认)/ORDERLY(顺序消费) + consume-mode: CONCURRENTLY + # 批量消费最大条数(默认1,单条消费) + consume-message-batch-max-size: 1 + # 最大重试次数(消费失败后自动重试,超过次数进入死信队列) + max-reconsume-times: 3 \ No newline at end of file diff --git a/pom.xml b/pom.xml index 8e7df40..562261a 100644 --- a/pom.xml +++ b/pom.xml @@ -32,6 +32,8 @@ 8.0.33 + + @@ -88,6 +90,16 @@ rocketmq-client-java 5.3.2 + + org.apache.rocketmq + rocketmq-spring-boot-starter + 2.3.2 + + + org.apache.rocketmq + rocketmq-client + 5.3.2 + diff --git a/stock-service/pom.xml b/stock-service/pom.xml index 952e822..3577adb 100644 --- a/stock-service/pom.xml +++ b/stock-service/pom.xml @@ -19,10 +19,70 @@ - junit - junit - 3.8.1 - test + org.springframework.boot + spring-boot-starter-web + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-discovery + + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + org.springframework.cloud + spring-cloud-starter-openfeign + + + + org.springframework.cloud + spring-cloud-starter-loadbalancer + + + com.alibaba.csp + sentinel-core + + + org.projectlombok + lombok + provided + + + org.mybatis + mybatis + 3.5.19 + compile + + + org.mybatis + mybatis-spring + 4.0.0 + compile + + + org.springframework.boot + spring-boot-starter-jdbc + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + + + + + com.mysql + mysql-connector-j + runtime + + + org.apache.rocketmq + rocketmq-spring-boot-starter + + + diff --git a/stock-service/src/main/java/cn/mayiming/App.java b/stock-service/src/main/java/cn/mayiming/App.java index 93abe3a..4041a93 100644 --- a/stock-service/src/main/java/cn/mayiming/App.java +++ b/stock-service/src/main/java/cn/mayiming/App.java @@ -1,13 +1,21 @@ package cn.mayiming; +import org.mybatis.spring.annotation.MapperScan; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.transaction.annotation.EnableTransactionManagement; + /** * Hello world! * */ +@SpringBootApplication +@EnableTransactionManagement +@MapperScan("cn.mayiming.Mapper") public class App { public static void main( String[] args ) { - System.out.println( "Hello World!" ); + SpringApplication.run(App.class, args); } } diff --git a/stock-service/src/main/java/cn/mayiming/Controller/stockController.java b/stock-service/src/main/java/cn/mayiming/Controller/stockController.java new file mode 100644 index 0000000..a9d6350 --- /dev/null +++ b/stock-service/src/main/java/cn/mayiming/Controller/stockController.java @@ -0,0 +1,23 @@ +package cn.mayiming.Controller; + + +import cn.mayiming.Entity.StockDeductDTO; +import cn.mayiming.Service.stockService; +import jakarta.validation.Valid; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequestMapping("/stock") +public class stockController { + @Autowired + stockService stockService; + + @PostMapping("/deduct") + public int deductstock(@Valid @RequestBody StockDeductDTO stockDeductDTO) { + return stockService.deductStock(stockDeductDTO.getId(),stockDeductDTO.getDeductNum()); + } +} diff --git a/stock-service/src/main/java/cn/mayiming/Entity/StockDeductDTO.java b/stock-service/src/main/java/cn/mayiming/Entity/StockDeductDTO.java new file mode 100644 index 0000000..74c1dd9 --- /dev/null +++ b/stock-service/src/main/java/cn/mayiming/Entity/StockDeductDTO.java @@ -0,0 +1,17 @@ +package cn.mayiming.Entity; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import jakarta.validation.constraints.NotNull; +import lombok.Data; + +@Data +public class StockDeductDTO { + @NotNull + @JsonProperty("id") + private Long id; + + @NotNull + @JsonProperty("deductnum") + private Integer deductNum; +} diff --git a/stock-service/src/main/java/cn/mayiming/Mapper/StockMapper.java b/stock-service/src/main/java/cn/mayiming/Mapper/StockMapper.java new file mode 100644 index 0000000..3c77ae2 --- /dev/null +++ b/stock-service/src/main/java/cn/mayiming/Mapper/StockMapper.java @@ -0,0 +1,16 @@ +package cn.mayiming.Mapper; + +import feign.Param; +import org.apache.ibatis.annotations.Select; +import org.apache.ibatis.annotations.Update; +import org.springframework.stereotype.Repository; + +@Repository +public interface StockMapper { + + @Select("SELECT stock_num FROM t_stock WHERE goods_id = #{goodsId}") + Integer selectStockByGoodsId(@Param("goodsId") Long goodsId); + + @Update("UPDATE t_stock SET stock_num = stock_num - #{deductNum} WHERE goods_id = #{goodsId} AND stock_num >= #{deductNum}") + int deductStock(@Param("goodsId") Long goodsId, @Param("deductNum") Integer deductNum); +} diff --git a/stock-service/src/main/java/cn/mayiming/Service/stockService.java b/stock-service/src/main/java/cn/mayiming/Service/stockService.java new file mode 100644 index 0000000..1915f97 --- /dev/null +++ b/stock-service/src/main/java/cn/mayiming/Service/stockService.java @@ -0,0 +1,18 @@ +package cn.mayiming.Service; + + +import cn.mayiming.Mapper.StockMapper; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class stockService { + @Autowired + StockMapper stockMapper; + + @Transactional(rollbackFor = Exception.class) + public int deductStock(Long goodsId, int i) { + return stockMapper.deductStock(goodsId, i); + } +} diff --git a/stock-service/src/main/resources/application.yml b/stock-service/src/main/resources/application.yml new file mode 100644 index 0000000..99f396c --- /dev/null +++ b/stock-service/src/main/resources/application.yml @@ -0,0 +1,52 @@ +server: + port: 9094 +spring: + application: + name: stock-service + cloud: + nacos: + # 服务注册发现配置 + discovery: + server-addr: localhost:8848 # Nacos 服务地址(默认端口 8848) + namespace: public # 命名空间(默认 public,自定义需先在 Nacos 控制台创建) + group: DEFAULT_GROUP # 分组(默认 DEFAULT_GROUP) + service: stock-service # 注册到 Nacos 的服务名(建议和子项目 artifactId 一致) + # 配置管理配置(如果引入了 config 依赖才需要) + config: + server-addr: localhost:8848 # 和 discovery 一致 + file-extension: yaml + namespace: public + group: DEFAULT_GROUP + config: + import: nacos:${spring.application.name}.${spring.cloud.nacos.config.file-extension}?server-addr=${spring.cloud.nacos.config.server-addr} + datasource: + # 数据库驱动类(MySQL 8.x 用 com.mysql.cj.jdbc.Driver,5.x 用 com.mysql.jdbc.Driver) + driver-class-name: com.mysql.cj.jdbc.Driver + # 数据库连接 URL(替换为你的数据库地址、端口、库名,如 user_db) + url: jdbc:mysql://rm-f8z6oc5a03331500p8o.mysql.rds.aliyuncs.com:3306/stock_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true + # 数据库用户名(默认 root,根据实际情况修改) + username: root + # 数据库密码(替换为你的 MySQL 密码) + password: Root123456 + # 可选:连接池配置(推荐使用 HikariCP,Spring Boot 2.x 默认) + hikari: + # 连接池最大连接数 + maximum-pool-size: 10 + # 连接池最小空闲连接数 + minimum-idle: 2 + # 连接超时时间(毫秒) + connection-timeout: 30000 + # 连接最大存活时间(毫秒) + max-lifetime: 1800000 +#rocketmq: +# # NameServer 地址(替换为你的 RocketMQ 服务器IP) +# name-server: 127.0.0.1:9876 +# consumer: +# # 消费者组名(必须唯一,建议按服务+用途命名) +# group: order-service-consumer +# # 消费模式:CONCURRENTLY(并发消费,默认)/ORDERLY(顺序消费) +# consume-mode: CONCURRENTLY +# # 批量消费最大条数(默认1,单条消费) +# consume-message-batch-max-size: 1 +# # 最大重试次数(消费失败后自动重试,超过次数进入死信队列) +# max-reconsume-times: 3 \ No newline at end of file