From 3c4c7e59e313183d01cbd386ecd74f0d2a142946 Mon Sep 17 00:00:00 2001
From: syd <1579670286@qq.com>
Date: Tue, 17 Jun 2025 10:57:13 +0800
Subject: [PATCH 1/8] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E2=80=9C?=
=?UTF-8?q?=E5=95=86=E5=93=81=E6=9C=8D=E5=8A=A1=E2=80=9D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
item-service/pom.xml | 55 +++++++++
.../hmall/item/ItemServiceApplication.java | 15 +++
.../hmall/item/controller/ItemController.java | 86 +++++++++++++
.../com/hmall/item/domain/dto/ItemDTO.java | 34 ++++++
.../hmall/item/domain/dto/OrderDetailDTO.java | 16 +++
.../java/com/hmall/item/domain/po/Item.java | 113 ++++++++++++++++++
.../item/domain/query/ItemPageQuery.java | 23 ++++
.../com/hmall/item/mapper/ItemMapper.java | 21 ++++
.../com/hmall/item/service/IItemService.java | 25 ++++
.../item/service/impl/ItemServiceImpl.java | 43 +++++++
.../src/main/resources/application-dev.yaml | 4 +
.../src/main/resources/application-local.yaml | 4 +
.../src/main/resources/application.yaml | 53 ++++++++
.../src/main/resources/mapper/ItemMapper.xml | 5 +
pom.xml | 105 ++++++++++++++++
15 files changed, 602 insertions(+)
create mode 100644 item-service/pom.xml
create mode 100644 item-service/src/main/java/com/hmall/item/ItemServiceApplication.java
create mode 100644 item-service/src/main/java/com/hmall/item/controller/ItemController.java
create mode 100644 item-service/src/main/java/com/hmall/item/domain/dto/ItemDTO.java
create mode 100644 item-service/src/main/java/com/hmall/item/domain/dto/OrderDetailDTO.java
create mode 100644 item-service/src/main/java/com/hmall/item/domain/po/Item.java
create mode 100644 item-service/src/main/java/com/hmall/item/domain/query/ItemPageQuery.java
create mode 100644 item-service/src/main/java/com/hmall/item/mapper/ItemMapper.java
create mode 100644 item-service/src/main/java/com/hmall/item/service/IItemService.java
create mode 100644 item-service/src/main/java/com/hmall/item/service/impl/ItemServiceImpl.java
create mode 100644 item-service/src/main/resources/application-dev.yaml
create mode 100644 item-service/src/main/resources/application-local.yaml
create mode 100644 item-service/src/main/resources/application.yaml
create mode 100644 item-service/src/main/resources/mapper/ItemMapper.xml
create mode 100644 pom.xml
diff --git a/item-service/pom.xml b/item-service/pom.xml
new file mode 100644
index 0000000..6ffb080
--- /dev/null
+++ b/item-service/pom.xml
@@ -0,0 +1,55 @@
+
+
+
+ hmall-parent
+ com.hmall
+ 1.0.0
+
+ 4.0.0
+
+ item-service
+
+
+ 11
+ 11
+
+
+
+
+ com.hmall
+ hm-common
+ 1.0.0
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
\ No newline at end of file
diff --git a/item-service/src/main/java/com/hmall/item/ItemServiceApplication.java b/item-service/src/main/java/com/hmall/item/ItemServiceApplication.java
new file mode 100644
index 0000000..b8f036f
--- /dev/null
+++ b/item-service/src/main/java/com/hmall/item/ItemServiceApplication.java
@@ -0,0 +1,15 @@
+package com.hmall.item;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@MapperScan("com.hmall.item.mapper")
+@SpringBootApplication
+public class ItemServiceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ItemServiceApplication.class, args);
+ }
+
+}
\ No newline at end of file
diff --git a/item-service/src/main/java/com/hmall/item/controller/ItemController.java b/item-service/src/main/java/com/hmall/item/controller/ItemController.java
new file mode 100644
index 0000000..dc64e32
--- /dev/null
+++ b/item-service/src/main/java/com/hmall/item/controller/ItemController.java
@@ -0,0 +1,86 @@
+package com.hmall.item.controller;
+
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.hmall.common.domain.PageDTO;
+import com.hmall.common.domain.PageQuery;
+import com.hmall.common.utils.BeanUtils;
+
+import com.hmall.item.domain.dto.ItemDTO;
+import com.hmall.item.domain.dto.OrderDetailDTO;
+import com.hmall.item.domain.po.Item;
+import com.hmall.item.service.IItemService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Collection;
+import java.util.List;
+
+@Api(tags = "商品管理相关接口")
+@RestController
+@RequestMapping("/items")
+@RequiredArgsConstructor
+public class ItemController {
+
+ private final IItemService itemService;
+
+ @ApiOperation("分页查询商品")
+ @GetMapping("/page")
+ public PageDTO queryItemByPage(PageQuery query) {
+ // 1.分页查询
+ Page- result = itemService.page(query.toMpPage("update_time", false));
+ // 2.封装并返回
+ return PageDTO.of(result, ItemDTO.class);
+ }
+
+ @ApiOperation("根据id批量查询商品")
+ @GetMapping
+ public List queryItemByIds(@RequestParam("ids") Collection ids){
+ return itemService.queryItemByIds(ids);
+ }
+
+ @ApiOperation("根据id查询商品")
+ @GetMapping("{id}")
+ public ItemDTO queryItemById(@PathVariable("id") Long id) {
+ return BeanUtils.copyBean(itemService.getById(id), ItemDTO.class);
+ }
+
+ @ApiOperation("新增商品")
+ @PostMapping
+ public void saveItem(@RequestBody ItemDTO item) {
+ // 新增
+ itemService.save(BeanUtils.copyBean(item, Item.class));
+ }
+
+ @ApiOperation("更新商品状态")
+ @PutMapping("/status/{id}/{status}")
+ public void updateItemStatus(@PathVariable("id") Long id, @PathVariable("status") Integer status){
+ Item item = new Item();
+ item.setId(id);
+ item.setStatus(status);
+ itemService.updateById(item);
+ }
+
+ @ApiOperation("更新商品")
+ @PutMapping
+ public void updateItem(@RequestBody ItemDTO item) {
+ // 不允许修改商品状态,所以强制设置为null,更新时,就会忽略该字段
+ item.setStatus(null);
+ // 更新
+ itemService.updateById(BeanUtils.copyBean(item, Item.class));
+ }
+
+ @ApiOperation("根据id删除商品")
+ @DeleteMapping("{id}")
+ public void deleteItemById(@PathVariable("id") Long id) {
+ itemService.removeById(id);
+ }
+
+ @ApiOperation("批量扣减库存")
+ @PutMapping("/stock/deduct")
+ public void deductStock(@RequestBody List items){
+ itemService.deductStock(items);
+ }
+}
diff --git a/item-service/src/main/java/com/hmall/item/domain/dto/ItemDTO.java b/item-service/src/main/java/com/hmall/item/domain/dto/ItemDTO.java
new file mode 100644
index 0000000..457f16d
--- /dev/null
+++ b/item-service/src/main/java/com/hmall/item/domain/dto/ItemDTO.java
@@ -0,0 +1,34 @@
+package com.hmall.item.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel(description = "商品实体")
+public class ItemDTO {
+ @ApiModelProperty("商品id")
+ private Long id;
+ @ApiModelProperty("SKU名称")
+ private String name;
+ @ApiModelProperty("价格(分)")
+ private Integer price;
+ @ApiModelProperty("库存数量")
+ private Integer stock;
+ @ApiModelProperty("商品图片")
+ private String image;
+ @ApiModelProperty("类目名称")
+ private String category;
+ @ApiModelProperty("品牌名称")
+ private String brand;
+ @ApiModelProperty("规格")
+ private String spec;
+ @ApiModelProperty("销量")
+ private Integer sold;
+ @ApiModelProperty("评论数")
+ private Integer commentCount;
+ @ApiModelProperty("是否是推广广告,true/false")
+ private Boolean isAD;
+ @ApiModelProperty("商品状态 1-正常,2-下架,3-删除")
+ private Integer status;
+}
diff --git a/item-service/src/main/java/com/hmall/item/domain/dto/OrderDetailDTO.java b/item-service/src/main/java/com/hmall/item/domain/dto/OrderDetailDTO.java
new file mode 100644
index 0000000..6d2088b
--- /dev/null
+++ b/item-service/src/main/java/com/hmall/item/domain/dto/OrderDetailDTO.java
@@ -0,0 +1,16 @@
+package com.hmall.item.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+@ApiModel(description = "订单明细条目")
+@Data
+@Accessors(chain = true)
+public class OrderDetailDTO {
+ @ApiModelProperty("商品id")
+ private Long itemId;
+ @ApiModelProperty("商品购买数量")
+ private Integer num;
+}
diff --git a/item-service/src/main/java/com/hmall/item/domain/po/Item.java b/item-service/src/main/java/com/hmall/item/domain/po/Item.java
new file mode 100644
index 0000000..dda3a95
--- /dev/null
+++ b/item-service/src/main/java/com/hmall/item/domain/po/Item.java
@@ -0,0 +1,113 @@
+package com.hmall.item.domain.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ *
+ * 商品表
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("item")
+public class Item implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 商品id
+ */
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * SKU名称
+ */
+ private String name;
+
+ /**
+ * 价格(分)
+ */
+ private Integer price;
+
+ /**
+ * 库存数量
+ */
+ private Integer stock;
+
+ /**
+ * 商品图片
+ */
+ private String image;
+
+ /**
+ * 类目名称
+ */
+ private String category;
+
+ /**
+ * 品牌名称
+ */
+ private String brand;
+
+ /**
+ * 规格
+ */
+ private String spec;
+
+ /**
+ * 销量
+ */
+ private Integer sold;
+
+ /**
+ * 评论数
+ */
+ private Integer commentCount;
+
+ /**
+ * 是否是推广广告,true/false
+ */
+ @TableField("isAD")
+ private Boolean isAD;
+
+ /**
+ * 商品状态 1-正常,2-下架,3-删除
+ */
+ private Integer status;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+
+ /**
+ * 创建人
+ */
+ private Long creater;
+
+ /**
+ * 修改人
+ */
+ private Long updater;
+
+
+}
diff --git a/item-service/src/main/java/com/hmall/item/domain/query/ItemPageQuery.java b/item-service/src/main/java/com/hmall/item/domain/query/ItemPageQuery.java
new file mode 100644
index 0000000..bff2ca4
--- /dev/null
+++ b/item-service/src/main/java/com/hmall/item/domain/query/ItemPageQuery.java
@@ -0,0 +1,23 @@
+package com.hmall.item.domain.query;
+
+import com.hmall.common.domain.PageQuery;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+
+@EqualsAndHashCode(callSuper = true)
+@Data
+@ApiModel(description = "商品分页查询条件")
+public class ItemPageQuery extends PageQuery {
+ @ApiModelProperty("搜索关键字")
+ private String key;
+ @ApiModelProperty("商品分类")
+ private String category;
+ @ApiModelProperty("商品品牌")
+ private String brand;
+ @ApiModelProperty("价格最小值")
+ private Integer minPrice;
+ @ApiModelProperty("价格最大值")
+ private Integer maxPrice;
+}
diff --git a/item-service/src/main/java/com/hmall/item/mapper/ItemMapper.java b/item-service/src/main/java/com/hmall/item/mapper/ItemMapper.java
new file mode 100644
index 0000000..73fea1b
--- /dev/null
+++ b/item-service/src/main/java/com/hmall/item/mapper/ItemMapper.java
@@ -0,0 +1,21 @@
+package com.hmall.item.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hmall.item.domain.dto.OrderDetailDTO;
+import com.hmall.item.domain.po.Item;
+import org.apache.ibatis.annotations.Update;
+
+/**
+ *
+ * 商品表 Mapper 接口
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface ItemMapper extends BaseMapper- {
+
+ @Update("UPDATE item SET stock = stock - #{num} WHERE id = #{itemId} and stock >= #{num}")
+ int updateStock(OrderDetailDTO orderDetail);
+}
diff --git a/item-service/src/main/java/com/hmall/item/service/IItemService.java b/item-service/src/main/java/com/hmall/item/service/IItemService.java
new file mode 100644
index 0000000..d2b8b6b
--- /dev/null
+++ b/item-service/src/main/java/com/hmall/item/service/IItemService.java
@@ -0,0 +1,25 @@
+package com.hmall.item.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hmall.item.domain.dto.ItemDTO;
+import com.hmall.item.domain.dto.OrderDetailDTO;
+import com.hmall.item.domain.po.Item;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ *
+ * 商品表 服务类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface IItemService extends IService- {
+
+ void deductStock(List items);
+
+ List queryItemByIds(Collection ids);
+}
diff --git a/item-service/src/main/java/com/hmall/item/service/impl/ItemServiceImpl.java b/item-service/src/main/java/com/hmall/item/service/impl/ItemServiceImpl.java
new file mode 100644
index 0000000..3b302c6
--- /dev/null
+++ b/item-service/src/main/java/com/hmall/item/service/impl/ItemServiceImpl.java
@@ -0,0 +1,43 @@
+package com.hmall.item.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hmall.common.exception.BizIllegalException;
+import com.hmall.common.utils.BeanUtils;
+
+import com.hmall.item.domain.dto.ItemDTO;
+import com.hmall.item.domain.dto.OrderDetailDTO;
+import com.hmall.item.domain.po.Item;
+import com.hmall.item.mapper.ItemMapper;
+import com.hmall.item.service.IItemService;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ *
+ * 商品表 服务实现类
+ *
+ *
+ * @author 虎哥
+ */
+@Service
+public class ItemServiceImpl extends ServiceImpl implements IItemService {
+
+ @Transactional
+ @Override
+ public void deductStock(List items) {
+ for (OrderDetailDTO item : items) {
+ int i = baseMapper.updateStock(item);
+ if(i<=0){
+ throw new BizIllegalException("库存不足!");
+ }
+ }
+ }
+
+ @Override
+ public List queryItemByIds(Collection ids) {
+ return BeanUtils.copyList(listByIds(ids), ItemDTO.class);
+ }
+}
diff --git a/item-service/src/main/resources/application-dev.yaml b/item-service/src/main/resources/application-dev.yaml
new file mode 100644
index 0000000..1fb8f00
--- /dev/null
+++ b/item-service/src/main/resources/application-dev.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: 192.168.101.68
+ pw: mysql
\ No newline at end of file
diff --git a/item-service/src/main/resources/application-local.yaml b/item-service/src/main/resources/application-local.yaml
new file mode 100644
index 0000000..87ff8f4
--- /dev/null
+++ b/item-service/src/main/resources/application-local.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: localhost # 修改为你自己的虚拟机IP地址
+ pw: mysql # 修改为docker中的MySQL密码
\ No newline at end of file
diff --git a/item-service/src/main/resources/application.yaml b/item-service/src/main/resources/application.yaml
new file mode 100644
index 0000000..8615e3e
--- /dev/null
+++ b/item-service/src/main/resources/application.yaml
@@ -0,0 +1,53 @@
+server:
+ port: 8081
+spring:
+ application:
+ name: item-service
+ profiles:
+ active: dev
+ datasource:
+ url: jdbc:mysql://${hm.db.host}:3306/hm-item?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ username: root
+ password: ${hm.db.pw}
+mybatis-plus:
+ configuration:
+ default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
+ global-config:
+ db-config:
+ update-strategy: not_null
+ id-type: auto
+logging:
+ level:
+ com.hmall: debug
+ pattern:
+ dateformat: HH:mm:ss:SSS
+ file:
+ path: "logs/${spring.application.name}"
+knife4j:
+ enable: true
+ openapi:
+ title: 黑马商城接口文档
+ description: "黑马商城接口文档"
+ email: 1579670286@qq.com
+ concat: 栋哥
+ version: v1.0.0
+ group:
+ default:
+ group-name: default
+ api-rule: package
+ api-rule-resources:
+ - com.hmall.item.controller
+hm:
+ jwt:
+ location: classpath:hmall.jks
+ alias: hmall
+ password: hmall123
+ tokenTTL: 30m
+ auth:
+ excludePaths:
+ - /search/**
+ - /users/login
+ - /items/**
+ - /hi
+# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123
\ No newline at end of file
diff --git a/item-service/src/main/resources/mapper/ItemMapper.xml b/item-service/src/main/resources/mapper/ItemMapper.xml
new file mode 100644
index 0000000..5a01cb5
--- /dev/null
+++ b/item-service/src/main/resources/mapper/ItemMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..09d9dbb
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,105 @@
+
+
+ 4.0.0
+
+ com.hmall
+ hmall-parent
+ pom
+
+ item-service
+
+ 1.0.0
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 2.7.12
+
+
+
+
+ 11
+ 11
+ UTF-8
+ UTF-8
+ 1.18.20
+ 2021.0.3
+ 2021.0.4.0
+ 3.4.3
+ 5.8.11
+ 8.0.23
+
+
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-dependencies
+ ${spring-cloud.version}
+
+ pom
+ import
+
+
+
+ com.alibaba.cloud
+ spring-cloud-alibaba-dependencies
+ ${spring-cloud-alibaba.version}
+ pom
+ import
+
+
+
+ mysql
+ mysql-connector-java
+ ${mysql.version}
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+ ${mybatis-plus.version}
+
+
+
+ cn.hutool
+ hutool-all
+ ${hutool.version}
+
+
+
+
+
+
+ org.projectlombok
+ lombok
+ ${org.projectlombok.version}
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+ test
+
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ 3.8.1
+
+ 11
+ 11
+
+
+
+
+
+
\ No newline at end of file
--
Gitee
From 611efbf42f7b42a63f599f220ce6e90b58a67174 Mon Sep 17 00:00:00 2001
From: syd <1579670286@qq.com>
Date: Tue, 17 Jun 2025 11:31:12 +0800
Subject: [PATCH 2/8] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E2=80=9C?=
=?UTF-8?q?=E8=B4=AD=E7=89=A9=E8=BD=A6=E6=9C=8D=E5=8A=A1=E2=80=9D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
cart-service/pom.xml | 55 ++++++++
.../hmall/cart/CartServiceApplication.java | 15 +++
.../hmall/cart/controller/CartController.java | 55 ++++++++
.../hmall/cart/domain/dto/CartFormDTO.java | 20 +++
.../com/hmall/cart/domain/dto/ItemDTO.java | 34 +++++
.../java/com/hmall/cart/domain/po/Cart.java | 81 +++++++++++
.../java/com/hmall/cart/domain/vo/CartVO.java | 43 ++++++
.../com/hmall/cart/mapper/CartMapper.java | 21 +++
.../com/hmall/cart/service/ICartService.java | 27 ++++
.../cart/service/impl/CartServiceImpl.java | 126 ++++++++++++++++++
.../src/main/resources/application-dev.yaml | 4 +
.../src/main/resources/application-local.yaml | 4 +
.../src/main/resources/application.yaml | 53 ++++++++
.../src/main/resources/mapper/CartMapper.xml | 5 +
pom.xml | 1 +
15 files changed, 544 insertions(+)
create mode 100644 cart-service/pom.xml
create mode 100644 cart-service/src/main/java/com/hmall/cart/CartServiceApplication.java
create mode 100644 cart-service/src/main/java/com/hmall/cart/controller/CartController.java
create mode 100644 cart-service/src/main/java/com/hmall/cart/domain/dto/CartFormDTO.java
create mode 100644 cart-service/src/main/java/com/hmall/cart/domain/dto/ItemDTO.java
create mode 100644 cart-service/src/main/java/com/hmall/cart/domain/po/Cart.java
create mode 100644 cart-service/src/main/java/com/hmall/cart/domain/vo/CartVO.java
create mode 100644 cart-service/src/main/java/com/hmall/cart/mapper/CartMapper.java
create mode 100644 cart-service/src/main/java/com/hmall/cart/service/ICartService.java
create mode 100644 cart-service/src/main/java/com/hmall/cart/service/impl/CartServiceImpl.java
create mode 100644 cart-service/src/main/resources/application-dev.yaml
create mode 100644 cart-service/src/main/resources/application-local.yaml
create mode 100644 cart-service/src/main/resources/application.yaml
create mode 100644 cart-service/src/main/resources/mapper/CartMapper.xml
diff --git a/cart-service/pom.xml b/cart-service/pom.xml
new file mode 100644
index 0000000..24ce2d2
--- /dev/null
+++ b/cart-service/pom.xml
@@ -0,0 +1,55 @@
+
+
+
+ hmall-parent
+ com.hmall
+ 1.0.0
+
+ 4.0.0
+
+ cart-service
+
+
+ 11
+ 11
+
+
+
+
+ com.hmall
+ hm-common
+ 1.0.0
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
\ No newline at end of file
diff --git a/cart-service/src/main/java/com/hmall/cart/CartServiceApplication.java b/cart-service/src/main/java/com/hmall/cart/CartServiceApplication.java
new file mode 100644
index 0000000..6a3ac40
--- /dev/null
+++ b/cart-service/src/main/java/com/hmall/cart/CartServiceApplication.java
@@ -0,0 +1,15 @@
+package com.hmall.cart;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@MapperScan("com.hmall.cart.mapper")
+@SpringBootApplication
+public class CartServiceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(CartServiceApplication.class, args);
+ }
+
+}
\ No newline at end of file
diff --git a/cart-service/src/main/java/com/hmall/cart/controller/CartController.java b/cart-service/src/main/java/com/hmall/cart/controller/CartController.java
new file mode 100644
index 0000000..87a350b
--- /dev/null
+++ b/cart-service/src/main/java/com/hmall/cart/controller/CartController.java
@@ -0,0 +1,55 @@
+package com.hmall.cart.controller;
+
+
+
+import com.hmall.cart.domain.dto.CartFormDTO;
+import com.hmall.cart.domain.po.Cart;
+import com.hmall.cart.domain.vo.CartVO;
+import com.hmall.cart.service.ICartService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.web.bind.annotation.*;
+
+import javax.validation.Valid;
+import java.util.List;
+
+@Api(tags = "购物车相关接口")
+@RestController
+@RequestMapping("/carts")
+@RequiredArgsConstructor
+public class CartController {
+ private final ICartService cartService;
+
+ @ApiOperation("添加商品到购物车")
+ @PostMapping
+ public void addItem2Cart(@Valid @RequestBody CartFormDTO cartFormDTO){
+ cartService.addItem2Cart(cartFormDTO);
+ }
+
+ @ApiOperation("更新购物车数据")
+ @PutMapping
+ public void updateCart(@RequestBody Cart cart){
+ cartService.updateById(cart);
+ }
+
+ @ApiOperation("删除购物车中商品")
+ @DeleteMapping("{id}")
+ public void deleteCartItem(@Param ("购物车条目id")@PathVariable("id") Long id){
+ cartService.removeById(id);
+ }
+
+ @ApiOperation("查询购物车列表")
+ @GetMapping
+ public List queryMyCarts(){
+ return cartService.queryMyCarts();
+ }
+ @ApiOperation("批量删除购物车中商品")
+ @ApiImplicitParam(name = "ids", value = "购物车条目id集合")
+ @DeleteMapping
+ public void deleteCartItemByIds(@RequestParam("ids") List ids){
+ cartService.removeByItemIds(ids);
+ }
+}
diff --git a/cart-service/src/main/java/com/hmall/cart/domain/dto/CartFormDTO.java b/cart-service/src/main/java/com/hmall/cart/domain/dto/CartFormDTO.java
new file mode 100644
index 0000000..eb9cb9f
--- /dev/null
+++ b/cart-service/src/main/java/com/hmall/cart/domain/dto/CartFormDTO.java
@@ -0,0 +1,20 @@
+package com.hmall.cart.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel(description = "新增购物车商品表单实体")
+public class CartFormDTO {
+ @ApiModelProperty("商品id")
+ private Long itemId;
+ @ApiModelProperty("商品标题")
+ private String name;
+ @ApiModelProperty("商品动态属性键值集")
+ private String spec;
+ @ApiModelProperty("价格,单位:分")
+ private Integer price;
+ @ApiModelProperty("商品图片")
+ private String image;
+}
diff --git a/cart-service/src/main/java/com/hmall/cart/domain/dto/ItemDTO.java b/cart-service/src/main/java/com/hmall/cart/domain/dto/ItemDTO.java
new file mode 100644
index 0000000..5498dd3
--- /dev/null
+++ b/cart-service/src/main/java/com/hmall/cart/domain/dto/ItemDTO.java
@@ -0,0 +1,34 @@
+package com.hmall.cart.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel(description = "商品实体")
+public class ItemDTO {
+ @ApiModelProperty("商品id")
+ private Long id;
+ @ApiModelProperty("SKU名称")
+ private String name;
+ @ApiModelProperty("价格(分)")
+ private Integer price;
+ @ApiModelProperty("库存数量")
+ private Integer stock;
+ @ApiModelProperty("商品图片")
+ private String image;
+ @ApiModelProperty("类目名称")
+ private String category;
+ @ApiModelProperty("品牌名称")
+ private String brand;
+ @ApiModelProperty("规格")
+ private String spec;
+ @ApiModelProperty("销量")
+ private Integer sold;
+ @ApiModelProperty("评论数")
+ private Integer commentCount;
+ @ApiModelProperty("是否是推广广告,true/false")
+ private Boolean isAD;
+ @ApiModelProperty("商品状态 1-正常,2-下架,3-删除")
+ private Integer status;
+}
diff --git a/cart-service/src/main/java/com/hmall/cart/domain/po/Cart.java b/cart-service/src/main/java/com/hmall/cart/domain/po/Cart.java
new file mode 100644
index 0000000..5ab31e6
--- /dev/null
+++ b/cart-service/src/main/java/com/hmall/cart/domain/po/Cart.java
@@ -0,0 +1,81 @@
+package com.hmall.cart.domain.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ *
+ * 订单详情表
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("cart")
+public class Cart implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 购物车条目id
+ */
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 用户id
+ */
+ private Long userId;
+
+ /**
+ * sku商品id
+ */
+ private Long itemId;
+
+ /**
+ * 购买数量
+ */
+ private Integer num;
+
+ /**
+ * 商品标题
+ */
+ private String name;
+
+ /**
+ * 商品动态属性键值集
+ */
+ private String spec;
+
+ /**
+ * 价格,单位:分
+ */
+ private Integer price;
+
+ /**
+ * 商品图片
+ */
+ private String image;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+
+
+}
diff --git a/cart-service/src/main/java/com/hmall/cart/domain/vo/CartVO.java b/cart-service/src/main/java/com/hmall/cart/domain/vo/CartVO.java
new file mode 100644
index 0000000..27ce1e3
--- /dev/null
+++ b/cart-service/src/main/java/com/hmall/cart/domain/vo/CartVO.java
@@ -0,0 +1,43 @@
+package com.hmall.cart.domain.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ *
+ * 订单详情表
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Data
+@ApiModel(description = "购物车VO实体")
+public class CartVO {
+ @ApiModelProperty("购物车条目id ")
+ private Long id;
+ @ApiModelProperty("sku商品id")
+ private Long itemId;
+ @ApiModelProperty("购买数量")
+ private Integer num;
+ @ApiModelProperty("商品标题")
+ private String name;
+ @ApiModelProperty("商品动态属性键值集")
+ private String spec;
+ @ApiModelProperty("价格,单位:分")
+ private Integer price;
+ @ApiModelProperty("商品最新价格")
+ private Integer newPrice;
+ @ApiModelProperty("商品最新状态")
+ private Integer status = 1;
+ @ApiModelProperty("商品最新库存")
+ private Integer stock = 10;
+ @ApiModelProperty("商品图片")
+ private String image;
+ @ApiModelProperty("创建时间")
+ private LocalDateTime createTime;
+
+}
diff --git a/cart-service/src/main/java/com/hmall/cart/mapper/CartMapper.java b/cart-service/src/main/java/com/hmall/cart/mapper/CartMapper.java
new file mode 100644
index 0000000..62019ae
--- /dev/null
+++ b/cart-service/src/main/java/com/hmall/cart/mapper/CartMapper.java
@@ -0,0 +1,21 @@
+package com.hmall.cart.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hmall.cart.domain.po.Cart;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Update;
+
+/**
+ *
+ * 订单详情表 Mapper 接口
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface CartMapper extends BaseMapper {
+
+ @Update("UPDATE cart SET num = num + 1 WHERE user_id = #{userId} AND item_id = #{itemId}")
+ void updateNum(@Param("itemId") Long itemId, @Param("userId") Long userId);
+}
diff --git a/cart-service/src/main/java/com/hmall/cart/service/ICartService.java b/cart-service/src/main/java/com/hmall/cart/service/ICartService.java
new file mode 100644
index 0000000..381d220
--- /dev/null
+++ b/cart-service/src/main/java/com/hmall/cart/service/ICartService.java
@@ -0,0 +1,27 @@
+package com.hmall.cart.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hmall.cart.domain.dto.CartFormDTO;
+import com.hmall.cart.domain.po.Cart;
+import com.hmall.cart.domain.vo.CartVO;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ *
+ * 订单详情表 服务类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface ICartService extends IService {
+
+ void addItem2Cart(CartFormDTO cartFormDTO);
+
+ List queryMyCarts();
+
+ void removeByItemIds(Collection itemIds);
+}
diff --git a/cart-service/src/main/java/com/hmall/cart/service/impl/CartServiceImpl.java b/cart-service/src/main/java/com/hmall/cart/service/impl/CartServiceImpl.java
new file mode 100644
index 0000000..fa2aaef
--- /dev/null
+++ b/cart-service/src/main/java/com/hmall/cart/service/impl/CartServiceImpl.java
@@ -0,0 +1,126 @@
+package com.hmall.cart.service.impl;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hmall.cart.domain.dto.CartFormDTO;
+import com.hmall.cart.domain.dto.ItemDTO;
+import com.hmall.cart.domain.po.Cart;
+import com.hmall.cart.domain.vo.CartVO;
+import com.hmall.cart.mapper.CartMapper;
+import com.hmall.cart.service.ICartService;
+import com.hmall.common.exception.BizIllegalException;
+import com.hmall.common.utils.BeanUtils;
+import com.hmall.common.utils.CollUtils;
+import com.hmall.common.utils.UserContext;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.*;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * 订单详情表 服务实现类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Service
+@RequiredArgsConstructor
+public class CartServiceImpl extends ServiceImpl implements ICartService {
+
+// private final IItemService itemService;
+
+ @Override
+ public void addItem2Cart(CartFormDTO cartFormDTO) {
+ // 1.获取登录用户
+ Long userId = UserContext.getUser();
+
+ // 2.判断是否已经存在
+ if(checkItemExists(cartFormDTO.getItemId(), userId)){
+ // 2.1.存在,则更新数量
+ baseMapper.updateNum(cartFormDTO.getItemId(), userId);
+ return;
+ }
+ // 2.2.不存在,判断是否超过购物车数量
+ checkCartsFull(userId);
+
+ // 3.新增购物车条目
+ // 3.1.转换PO
+ Cart cart = BeanUtils.copyBean(cartFormDTO, Cart.class);
+ // 3.2.保存当前用户
+ cart.setUserId(userId);
+ // 3.3.保存到数据库
+ save(cart);
+ }
+
+ @Override
+ public List queryMyCarts() {
+ // 1.查询我的购物车列表
+ List carts = lambdaQuery().eq(Cart::getUserId, UserContext.getUser()).list();
+ if (CollUtils.isEmpty(carts)) {
+ return CollUtils.emptyList();
+ }
+
+ // 2.转换VO
+ List vos = BeanUtils.copyList(carts, CartVO.class);
+
+ // 3.处理VO中的商品信息
+ handleCartItems(vos);
+
+ // 4.返回
+ return vos;
+ }
+
+ private void handleCartItems(List vos) {
+ // 1.获取商品id
+ Set itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
+ // 2.查询商品
+// List items = itemService.queryItemByIds(itemIds);
+ List items = new ArrayList<>();
+ if (CollUtils.isEmpty(items)) {
+ return;
+ }
+ // 3.转为 id 到 item的map
+ Map itemMap = items.stream().collect(Collectors.toMap(ItemDTO::getId, Function.identity()));
+ // 4.写入vo
+ for (CartVO v : vos) {
+ ItemDTO item = itemMap.get(v.getItemId());
+ if (item == null) {
+ continue;
+ }
+ v.setNewPrice(item.getPrice());
+ v.setStatus(item.getStatus());
+ v.setStock(item.getStock());
+ }
+ }
+
+ @Override
+ public void removeByItemIds(Collection itemIds) {
+ // 1.构建删除条件,userId和itemId
+ QueryWrapper queryWrapper = new QueryWrapper();
+ queryWrapper.lambda()
+ .eq(Cart::getUserId, UserContext.getUser())
+ .in(Cart::getItemId, itemIds);
+ // 2.删除
+ remove(queryWrapper);
+ }
+
+ private void checkCartsFull(Long userId) {
+ int count = lambdaQuery().eq(Cart::getUserId, userId).count();
+ if (count >= 10) {
+ throw new BizIllegalException(StrUtil.format("用户购物车课程不能超过{}", 10));
+ }
+ }
+
+ private boolean checkItemExists(Long itemId, Long userId) {
+ int count = lambdaQuery()
+ .eq(Cart::getUserId, userId)
+ .eq(Cart::getItemId, itemId)
+ .count();
+ return count > 0;
+ }
+}
diff --git a/cart-service/src/main/resources/application-dev.yaml b/cart-service/src/main/resources/application-dev.yaml
new file mode 100644
index 0000000..1fb8f00
--- /dev/null
+++ b/cart-service/src/main/resources/application-dev.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: 192.168.101.68
+ pw: mysql
\ No newline at end of file
diff --git a/cart-service/src/main/resources/application-local.yaml b/cart-service/src/main/resources/application-local.yaml
new file mode 100644
index 0000000..87ff8f4
--- /dev/null
+++ b/cart-service/src/main/resources/application-local.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: localhost # 修改为你自己的虚拟机IP地址
+ pw: mysql # 修改为docker中的MySQL密码
\ No newline at end of file
diff --git a/cart-service/src/main/resources/application.yaml b/cart-service/src/main/resources/application.yaml
new file mode 100644
index 0000000..e0a795a
--- /dev/null
+++ b/cart-service/src/main/resources/application.yaml
@@ -0,0 +1,53 @@
+server:
+ port: 8082
+spring:
+ application:
+ name: cart-service
+ profiles:
+ active: dev
+ datasource:
+ url: jdbc:mysql://${hm.db.host}:3306/hm-cart?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ username: root
+ password: ${hm.db.pw}
+mybatis-plus:
+ configuration:
+ default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
+ global-config:
+ db-config:
+ update-strategy: not_null
+ id-type: auto
+logging:
+ level:
+ com.hmall: debug
+ pattern:
+ dateformat: HH:mm:ss:SSS
+ file:
+ path: "logs/${spring.application.name}"
+knife4j:
+ enable: true
+ openapi:
+ title: 黑马商城接口文档
+ description: "黑马商城接口文档"
+ email: 1579670286@qq.com
+ concat: 栋哥
+ version: v1.0.0
+ group:
+ default:
+ group-name: default
+ api-rule: package
+ api-rule-resources:
+ - com.hmall.cart.controller
+hm:
+ jwt:
+ location: classpath:hmall.jks
+ alias: hmall
+ password: hmall123
+ tokenTTL: 30m
+ auth:
+ excludePaths:
+ - /search/**
+ - /users/login
+ - /items/**
+ - /hi
+# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123
\ No newline at end of file
diff --git a/cart-service/src/main/resources/mapper/CartMapper.xml b/cart-service/src/main/resources/mapper/CartMapper.xml
new file mode 100644
index 0000000..d9c56a5
--- /dev/null
+++ b/cart-service/src/main/resources/mapper/CartMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index 09d9dbb..be4576e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -9,6 +9,7 @@
pom
item-service
+ cart-service
1.0.0
--
Gitee
From d915bcad5ca42fc59b2878c0a80973d754e70d08 Mon Sep 17 00:00:00 2001
From: syd <1579670286@qq.com>
Date: Tue, 17 Jun 2025 11:31:31 +0800
Subject: [PATCH 3/8] =?UTF-8?q?=E5=88=9D=E5=A7=8B=E5=8C=96=E2=80=9C?=
=?UTF-8?q?=E9=80=9A=E7=94=A8=E6=A8=A1=E5=9D=97=E2=80=9D?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
hm-common/pom.xml | 95 +++++++
.../common/advice/CommonExceptionAdvice.java | 68 +++++
.../com/hmall/common/config/JsonConfig.java | 23 ++
.../hmall/common/config/MyBatisConfig.java | 25 ++
.../java/com/hmall/common/domain/PageDTO.java | 61 +++++
.../com/hmall/common/domain/PageQuery.java | 69 +++++
.../main/java/com/hmall/common/domain/R.java | 45 ++++
.../common/exception/BadRequestException.java | 16 ++
.../common/exception/BizIllegalException.java | 16 ++
.../common/exception/CommonException.java | 23 ++
.../hmall/common/exception/DbException.java | 16 ++
.../common/exception/ForbiddenException.java | 16 ++
.../exception/UnauthorizedException.java | 16 ++
.../com/hmall/common/utils/BeanUtils.java | 59 ++++
.../com/hmall/common/utils/CollUtils.java | 72 +++++
.../java/com/hmall/common/utils/Convert.java | 8 +
.../com/hmall/common/utils/CookieBuilder.java | 67 +++++
.../com/hmall/common/utils/DateUtils.java | 251 ++++++++++++++++++
.../com/hmall/common/utils/NumberUtils.java | 151 +++++++++++
.../com/hmall/common/utils/UserContext.java | 29 ++
.../java/com/hmall/common/utils/WebUtils.java | 151 +++++++++++
.../spring-configuration-metadata.json | 171 ++++++++++++
.../main/resources/META-INF/spring.factories | 3 +
23 files changed, 1451 insertions(+)
create mode 100644 hm-common/pom.xml
create mode 100644 hm-common/src/main/java/com/hmall/common/advice/CommonExceptionAdvice.java
create mode 100644 hm-common/src/main/java/com/hmall/common/config/JsonConfig.java
create mode 100644 hm-common/src/main/java/com/hmall/common/config/MyBatisConfig.java
create mode 100644 hm-common/src/main/java/com/hmall/common/domain/PageDTO.java
create mode 100644 hm-common/src/main/java/com/hmall/common/domain/PageQuery.java
create mode 100644 hm-common/src/main/java/com/hmall/common/domain/R.java
create mode 100644 hm-common/src/main/java/com/hmall/common/exception/BadRequestException.java
create mode 100644 hm-common/src/main/java/com/hmall/common/exception/BizIllegalException.java
create mode 100644 hm-common/src/main/java/com/hmall/common/exception/CommonException.java
create mode 100644 hm-common/src/main/java/com/hmall/common/exception/DbException.java
create mode 100644 hm-common/src/main/java/com/hmall/common/exception/ForbiddenException.java
create mode 100644 hm-common/src/main/java/com/hmall/common/exception/UnauthorizedException.java
create mode 100644 hm-common/src/main/java/com/hmall/common/utils/BeanUtils.java
create mode 100644 hm-common/src/main/java/com/hmall/common/utils/CollUtils.java
create mode 100644 hm-common/src/main/java/com/hmall/common/utils/Convert.java
create mode 100644 hm-common/src/main/java/com/hmall/common/utils/CookieBuilder.java
create mode 100644 hm-common/src/main/java/com/hmall/common/utils/DateUtils.java
create mode 100644 hm-common/src/main/java/com/hmall/common/utils/NumberUtils.java
create mode 100644 hm-common/src/main/java/com/hmall/common/utils/UserContext.java
create mode 100644 hm-common/src/main/java/com/hmall/common/utils/WebUtils.java
create mode 100644 hm-common/src/main/resources/META-INF/spring-configuration-metadata.json
create mode 100644 hm-common/src/main/resources/META-INF/spring.factories
diff --git a/hm-common/pom.xml b/hm-common/pom.xml
new file mode 100644
index 0000000..71cdebf
--- /dev/null
+++ b/hm-common/pom.xml
@@ -0,0 +1,95 @@
+
+
+
+ hmall-parent
+ com.hmall
+ 1.0.0
+
+ 4.0.0
+
+ hm-common
+
+
+ 11
+ 11
+
+
+
+
+ org.apache.commons
+ commons-pool2
+
+
+
+ cn.hutool
+ hutool-all
+
+
+
+ org.springframework
+ spring-webmvc
+ provided
+
+
+
+ org.springframework.boot
+ spring-boot-starter-logging
+
+
+ org.apache.tomcat.embed
+ tomcat-embed-core
+ 9.0.73
+ provided
+
+
+ com.baomidou
+ mybatis-plus-core
+ ${mybatis-plus.version}
+ provided
+
+
+ com.baomidou
+ mybatis-plus-extension
+ ${mybatis-plus.version}
+ provided
+
+
+ org.hibernate.validator
+ hibernate-validator
+
+
+ org.springframework.boot
+ spring-boot-autoconfigure
+
+
+ com.github.xiaoymin
+ knife4j-openapi2-spring-boot-starter
+ 4.1.0
+
+
+
+ com.github.ben-manes.caffeine
+ caffeine
+
+
+
+ org.springframework.amqp
+ spring-amqp
+ provided
+
+
+
+ org.springframework.amqp
+ spring-rabbit
+ provided
+
+
+
+ com.fasterxml.jackson.dataformat
+ jackson-dataformat-xml
+ provided
+
+
+
\ No newline at end of file
diff --git a/hm-common/src/main/java/com/hmall/common/advice/CommonExceptionAdvice.java b/hm-common/src/main/java/com/hmall/common/advice/CommonExceptionAdvice.java
new file mode 100644
index 0000000..75ae4ad
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/advice/CommonExceptionAdvice.java
@@ -0,0 +1,68 @@
+package com.hmall.common.advice;
+
+import com.hmall.common.domain.R;
+import com.hmall.common.exception.BadRequestException;
+import com.hmall.common.exception.CommonException;
+import com.hmall.common.exception.DbException;
+import com.hmall.common.utils.WebUtils;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.ResponseEntity;
+import org.springframework.validation.ObjectError;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import org.springframework.web.util.NestedServletException;
+
+import java.net.BindException;
+import java.util.stream.Collectors;
+
+@RestControllerAdvice
+@Slf4j
+public class CommonExceptionAdvice {
+
+ @ExceptionHandler(DbException.class)
+ public Object handleDbException(DbException e) {
+ log.error("mysql数据库操作异常 -> ", e);
+ return processResponse(e);
+ }
+
+ @ExceptionHandler(CommonException.class)
+ public Object handleBadRequestException(CommonException e) {
+ log.error("自定义异常 -> {} , 异常原因:{} ",e.getClass().getName(), e.getMessage());
+ log.debug("", e);
+ return processResponse(e);
+ }
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ public Object handleMethodArgumentNotValidException(MethodArgumentNotValidException e) {
+ String msg = e.getBindingResult().getAllErrors()
+ .stream().map(ObjectError::getDefaultMessage)
+ .collect(Collectors.joining("|"));
+ log.error("请求参数校验异常 -> {}", msg);
+ log.debug("", e);
+ return processResponse(new BadRequestException(msg));
+ }
+ @ExceptionHandler(BindException.class)
+ public Object handleBindException(BindException e) {
+ log.error("请求参数绑定异常 ->BindException, {}", e.getMessage());
+ log.debug("", e);
+ return processResponse(new BadRequestException("请求参数格式错误"));
+ }
+
+ @ExceptionHandler(NestedServletException.class)
+ public Object handleNestedServletException(NestedServletException e) {
+ log.error("参数异常 -> NestedServletException,{}", e.getMessage());
+ log.debug("", e);
+ return processResponse(new BadRequestException("请求参数处理异常"));
+ }
+
+ @ExceptionHandler(Exception.class)
+ public Object handleRuntimeException(Exception e) {
+ log.error("其他异常 uri : {} -> ", WebUtils.getRequest().getRequestURI(), e);
+ return processResponse(new CommonException("服务器内部异常", 500));
+ }
+
+ private ResponseEntity> processResponse(CommonException e){
+ return ResponseEntity.status(e.getCode()).body(R.error(e));
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/config/JsonConfig.java b/hm-common/src/main/java/com/hmall/common/config/JsonConfig.java
new file mode 100644
index 0000000..d8fa781
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/config/JsonConfig.java
@@ -0,0 +1,23 @@
+package com.hmall.common.config;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.math.BigInteger;
+
+@Configuration
+@ConditionalOnClass(ObjectMapper.class)
+public class JsonConfig {
+ @Bean
+ public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
+ return jacksonObjectMapperBuilder -> {
+ // long -> string
+ jacksonObjectMapperBuilder.serializerByType(Long.class, ToStringSerializer.instance);
+ jacksonObjectMapperBuilder.serializerByType(BigInteger.class, ToStringSerializer.instance);
+ };
+ }
+}
\ No newline at end of file
diff --git a/hm-common/src/main/java/com/hmall/common/config/MyBatisConfig.java b/hm-common/src/main/java/com/hmall/common/config/MyBatisConfig.java
new file mode 100644
index 0000000..8a2d463
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/config/MyBatisConfig.java
@@ -0,0 +1,25 @@
+package com.hmall.common.config;
+
+import com.baomidou.mybatisplus.annotation.DbType;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
+import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+@Configuration
+@ConditionalOnClass({MybatisPlusInterceptor.class, BaseMapper.class})
+public class MyBatisConfig {
+ @Bean
+ @ConditionalOnMissingBean
+ public MybatisPlusInterceptor mybatisPlusInterceptor() {
+ MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
+ // 1.分页拦截器
+ PaginationInnerInterceptor paginationInnerInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
+ paginationInnerInterceptor.setMaxLimit(1000L);
+ interceptor.addInnerInterceptor(paginationInnerInterceptor);
+ return interceptor;
+ }
+}
\ No newline at end of file
diff --git a/hm-common/src/main/java/com/hmall/common/domain/PageDTO.java b/hm-common/src/main/java/com/hmall/common/domain/PageDTO.java
new file mode 100644
index 0000000..98eaf2b
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/domain/PageDTO.java
@@ -0,0 +1,61 @@
+package com.hmall.common.domain;
+
+
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.hmall.common.utils.BeanUtils;
+import com.hmall.common.utils.CollUtils;
+import com.hmall.common.utils.Convert;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+
+@Data
+@NoArgsConstructor
+@AllArgsConstructor
+public class PageDTO {
+ protected Long total;
+ protected Long pages;
+ protected List list;
+
+ public static PageDTO empty(Long total, Long pages) {
+ return new PageDTO<>(total, pages, CollUtils.emptyList());
+ }
+ public static PageDTO empty(Page> page) {
+ return new PageDTO<>(page.getTotal(), page.getPages(), CollUtils.emptyList());
+ }
+
+ public static PageDTO of(Page page) {
+ if(page == null){
+ return new PageDTO<>();
+ }
+ if (CollUtils.isEmpty(page.getRecords())) {
+ return empty(page);
+ }
+ return new PageDTO<>(page.getTotal(), page.getPages(), page.getRecords());
+ }
+ public static PageDTO of(Page page, Function mapper) {
+ if(page == null){
+ return new PageDTO<>();
+ }
+ if (CollUtils.isEmpty(page.getRecords())) {
+ return empty(page);
+ }
+ return new PageDTO<>(page.getTotal(), page.getPages(),
+ page.getRecords().stream().map(mapper).collect(Collectors.toList()));
+ }
+ public static PageDTO of(Page> page, List list) {
+ return new PageDTO<>(page.getTotal(), page.getPages(), list);
+ }
+
+ public static PageDTO of(Page page, Class clazz) {
+ return new PageDTO<>(page.getTotal(), page.getPages(), BeanUtils.copyList(page.getRecords(), clazz));
+ }
+
+ public static PageDTO of(Page page, Class clazz, Convert convert) {
+ return new PageDTO<>(page.getTotal(), page.getPages(), BeanUtils.copyList(page.getRecords(), clazz, convert));
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/domain/PageQuery.java b/hm-common/src/main/java/com/hmall/common/domain/PageQuery.java
new file mode 100644
index 0000000..7abcd75
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/domain/PageQuery.java
@@ -0,0 +1,69 @@
+package com.hmall.common.domain;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.core.metadata.OrderItem;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+import lombok.experimental.Accessors;
+
+import javax.validation.constraints.Min;
+
+@Data
+@ApiModel(description = "分页查询条件")
+@Accessors(chain = true)
+public class PageQuery {
+ public static final Integer DEFAULT_PAGE_SIZE = 20;
+ public static final Integer DEFAULT_PAGE_NUM = 1;
+ @ApiModelProperty("页码")
+ @Min(value = 1, message = "页码不能小于1")
+ private Integer pageNo = DEFAULT_PAGE_NUM;
+ @ApiModelProperty("页码")
+ @Min(value = 1, message = "每页查询数量不能小于1")
+ private Integer pageSize = DEFAULT_PAGE_SIZE;
+ @ApiModelProperty("是否升序")
+ private Boolean isAsc = true;
+ @ApiModelProperty("排序方式")
+ private String sortBy;
+
+ public int from(){
+ return (pageNo - 1) * pageSize;
+ }
+
+ public Page toMpPage(OrderItem... orderItems) {
+ Page page = new Page<>(pageNo, pageSize);
+ // 是否手动指定排序方式
+ if (orderItems != null && orderItems.length > 0) {
+ for (OrderItem orderItem : orderItems) {
+ page.addOrder(orderItem);
+ }
+ return page;
+ }
+ // 前端是否有排序字段
+ if (StrUtil.isNotEmpty(sortBy)){
+ OrderItem orderItem = new OrderItem();
+ orderItem.setAsc(isAsc);
+ orderItem.setColumn(sortBy);
+ page.addOrder(orderItem);
+ }
+ return page;
+ }
+
+ public Page toMpPage(String defaultSortBy, boolean isAsc) {
+ if (StringUtils.isBlank(sortBy)){
+ sortBy = defaultSortBy;
+ this.isAsc = isAsc;
+ }
+ Page page = new Page<>(pageNo, pageSize);
+ OrderItem orderItem = new OrderItem();
+ orderItem.setAsc(this.isAsc);
+ orderItem.setColumn(sortBy);
+ page.addOrder(orderItem);
+ return page;
+ }
+ public Page toMpPageDefaultSortByCreateTimeDesc() {
+ return toMpPage("create_time", false);
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/domain/R.java b/hm-common/src/main/java/com/hmall/common/domain/R.java
new file mode 100644
index 0000000..33bda25
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/domain/R.java
@@ -0,0 +1,45 @@
+package com.hmall.common.domain;
+
+import com.hmall.common.exception.CommonException;
+import lombok.Data;
+
+
+@Data
+public class R {
+ private int code;
+ private String msg;
+ private T data;
+
+ public static R ok() {
+ return ok(null);
+ }
+
+ public static R ok(T data) {
+ return new R<>(200, "OK", data);
+ }
+
+ public static R error(String msg) {
+ return new R<>(500, msg, null);
+ }
+
+ public static R error(int code, String msg) {
+ return new R<>(code, msg, null);
+ }
+
+ public static R error(CommonException e) {
+ return new R<>(e.getCode(), e.getMessage(), null);
+ }
+
+ public R() {
+ }
+
+ public R(int code, String msg, T data) {
+ this.code = code;
+ this.msg = msg;
+ this.data = data;
+ }
+
+ public boolean success(){
+ return code == 200;
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/exception/BadRequestException.java b/hm-common/src/main/java/com/hmall/common/exception/BadRequestException.java
new file mode 100644
index 0000000..e0618e2
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/exception/BadRequestException.java
@@ -0,0 +1,16 @@
+package com.hmall.common.exception;
+
+public class BadRequestException extends CommonException{
+
+ public BadRequestException(String message) {
+ super(message, 400);
+ }
+
+ public BadRequestException(String message, Throwable cause) {
+ super(message, cause, 400);
+ }
+
+ public BadRequestException(Throwable cause) {
+ super(cause, 400);
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/exception/BizIllegalException.java b/hm-common/src/main/java/com/hmall/common/exception/BizIllegalException.java
new file mode 100644
index 0000000..584588e
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/exception/BizIllegalException.java
@@ -0,0 +1,16 @@
+package com.hmall.common.exception;
+
+public class BizIllegalException extends CommonException{
+
+ public BizIllegalException(String message) {
+ super(message, 500);
+ }
+
+ public BizIllegalException(String message, Throwable cause) {
+ super(message, cause, 500);
+ }
+
+ public BizIllegalException(Throwable cause) {
+ super(cause, 500);
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/exception/CommonException.java b/hm-common/src/main/java/com/hmall/common/exception/CommonException.java
new file mode 100644
index 0000000..871dcf3
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/exception/CommonException.java
@@ -0,0 +1,23 @@
+package com.hmall.common.exception;
+
+import lombok.Getter;
+
+@Getter
+public class CommonException extends RuntimeException{
+ private int code;
+
+ public CommonException(String message, int code) {
+ super(message);
+ this.code = code;
+ }
+
+ public CommonException(String message, Throwable cause, int code) {
+ super(message, cause);
+ this.code = code;
+ }
+
+ public CommonException(Throwable cause, int code) {
+ super(cause);
+ this.code = code;
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/exception/DbException.java b/hm-common/src/main/java/com/hmall/common/exception/DbException.java
new file mode 100644
index 0000000..635044a
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/exception/DbException.java
@@ -0,0 +1,16 @@
+package com.hmall.common.exception;
+
+public class DbException extends CommonException{
+
+ public DbException(String message) {
+ super(message, 500);
+ }
+
+ public DbException(String message, Throwable cause) {
+ super(message, cause, 500);
+ }
+
+ public DbException(Throwable cause) {
+ super(cause, 500);
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/exception/ForbiddenException.java b/hm-common/src/main/java/com/hmall/common/exception/ForbiddenException.java
new file mode 100644
index 0000000..05f9230
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/exception/ForbiddenException.java
@@ -0,0 +1,16 @@
+package com.hmall.common.exception;
+
+public class ForbiddenException extends CommonException{
+
+ public ForbiddenException(String message) {
+ super(message, 403);
+ }
+
+ public ForbiddenException(String message, Throwable cause) {
+ super(message, cause, 403);
+ }
+
+ public ForbiddenException(Throwable cause) {
+ super(cause, 403);
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/exception/UnauthorizedException.java b/hm-common/src/main/java/com/hmall/common/exception/UnauthorizedException.java
new file mode 100644
index 0000000..704f3d1
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/exception/UnauthorizedException.java
@@ -0,0 +1,16 @@
+package com.hmall.common.exception;
+
+public class UnauthorizedException extends CommonException{
+
+ public UnauthorizedException(String message) {
+ super(message, 401);
+ }
+
+ public UnauthorizedException(String message, Throwable cause) {
+ super(message, cause, 401);
+ }
+
+ public UnauthorizedException(Throwable cause) {
+ super(cause, 401);
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/utils/BeanUtils.java b/hm-common/src/main/java/com/hmall/common/utils/BeanUtils.java
new file mode 100644
index 0000000..eb33c2e
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/utils/BeanUtils.java
@@ -0,0 +1,59 @@
+package com.hmall.common.utils;
+
+import cn.hutool.core.bean.BeanUtil;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+/**
+ * 继承自 hutool 的BeanUtil,增加了bean转换时自定义转换器的功能
+ */
+public class BeanUtils extends BeanUtil {
+
+ /**
+ * 将原对象转换成目标对象,对于字段不匹配的字段可以使用转换器处理
+ *
+ * @param source 原对象
+ * @param clazz 目标对象的class
+ * @param convert 转换器
+ * @param 原对象类型
+ * @param 目标对象类型
+ * @return 目标对象
+ */
+ public static T copyBean(R source, Class clazz, Convert convert) {
+ T target = copyBean(source, clazz);
+ if (convert != null) {
+ convert.convert(source, target);
+ }
+ return target;
+ }
+ /**
+ * 将原对象转换成目标对象,对于字段不匹配的字段可以使用转换器处理
+ *
+ * @param source 原对象
+ * @param clazz 目标对象的class
+ * @param 原对象类型
+ * @param 目标对象类型
+ * @return 目标对象
+ */
+ public static T copyBean(R source, Class clazz){
+ if (source == null) {
+ return null;
+ }
+ return toBean(source, clazz);
+ }
+
+ public static List copyList(List list, Class clazz) {
+ if (list == null || list.size() == 0) {
+ return CollUtils.emptyList();
+ }
+ return copyToList(list, clazz);
+ }
+
+ public static List copyList(List list, Class clazz, Convert convert) {
+ if (list == null || list.size() == 0) {
+ return CollUtils.emptyList();
+ }
+ return list.stream().map(r -> copyBean(r, clazz, convert)).collect(Collectors.toList());
+ }
+}
\ No newline at end of file
diff --git a/hm-common/src/main/java/com/hmall/common/utils/CollUtils.java b/hm-common/src/main/java/com/hmall/common/utils/CollUtils.java
new file mode 100644
index 0000000..70121d4
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/utils/CollUtils.java
@@ -0,0 +1,72 @@
+package com.hmall.common.utils;
+
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.collection.IterUtil;
+import cn.hutool.core.util.NumberUtil;
+
+import java.util.*;
+import java.util.stream.Collectors;
+
+/**
+ * 继承自 hutool 的集合工具类
+ */
+public class CollUtils extends CollectionUtil {
+
+ public static List emptyList() {
+ return Collections.emptyList();
+ }
+
+ public static Set emptySet() {
+ return Collections.emptySet();
+ }
+
+ public static Map emptyMap() {
+ return Collections.emptyMap();
+ }
+
+ public static Set singletonSet(T t) {
+ return Collections.singleton(t);
+ }
+
+ public static List singletonList(T t) {
+ return Collections.singletonList(t);
+ }
+
+ public static List convertToInteger(List originList){
+ return CollUtils.isNotEmpty(originList) ? originList.stream().map(NumberUtil::parseInt).collect(Collectors.toList()) : null;
+ }
+
+ public static List convertToLong(List originLIst){
+ return CollUtils.isNotEmpty(originLIst) ? originLIst.stream().map(NumberUtil::parseLong).collect(Collectors.toList()) : null;
+ }
+
+ /**
+ * 以 conjunction 为分隔符将集合转换为字符串 如果集合元素为数组、Iterable或Iterator,则递归组合其为字符串
+ * @param collection 集合
+ * @param conjunction 分隔符
+ * @param 集合元素类型
+ * @return 连接后的字符串
+ * See Also: IterUtil.join(Iterator, CharSequence)
+ */
+ public static String join(Collection collection, CharSequence conjunction) {
+ if (null == collection || collection.isEmpty()) {
+ return null;
+ }
+ return IterUtil.join(collection.iterator(), conjunction);
+ }
+
+ public static String joinIgnoreNull(Collection collection, CharSequence conjunction) {
+ if (null == collection || collection.isEmpty()) {
+ return null;
+ }
+ StringBuilder sb = new StringBuilder();
+ for (T t : collection) {
+ if(t == null) continue;
+ sb.append(t).append(",");
+ }
+ if(sb.length() <= 0){
+ return null;
+ }
+ return sb.deleteCharAt(sb.length() - 1).toString();
+ }
+}
\ No newline at end of file
diff --git a/hm-common/src/main/java/com/hmall/common/utils/Convert.java b/hm-common/src/main/java/com/hmall/common/utils/Convert.java
new file mode 100644
index 0000000..0f56885
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/utils/Convert.java
@@ -0,0 +1,8 @@
+package com.hmall.common.utils;
+
+/**
+ * 对原对象进行计算,设置到目标对象中
+ **/
+public interface Convert{
+ void convert(R origin, T target);
+}
\ No newline at end of file
diff --git a/hm-common/src/main/java/com/hmall/common/utils/CookieBuilder.java b/hm-common/src/main/java/com/hmall/common/utils/CookieBuilder.java
new file mode 100644
index 0000000..8ac5dd5
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/utils/CookieBuilder.java
@@ -0,0 +1,67 @@
+package com.hmall.common.utils;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Data;
+import lombok.experimental.Accessors;
+import lombok.extern.slf4j.Slf4j;
+
+import javax.servlet.http.Cookie;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.net.URLDecoder;
+import java.net.URLEncoder;
+import java.nio.charset.Charset;
+import java.nio.charset.StandardCharsets;
+
+@Slf4j
+@Data
+@Accessors(chain = true, fluent = true)
+public class CookieBuilder {
+ private Charset charset = StandardCharsets.UTF_8;
+ private int maxAge = -1;
+ private String path = "/";
+ private boolean httpOnly;
+ private String name;
+ private String value;
+ private String domain;
+ private final HttpServletRequest request;
+ private final HttpServletResponse response;
+
+ public CookieBuilder(HttpServletRequest request, HttpServletResponse response) {
+ this.request = request;
+ this.response = response;
+ }
+
+ /**
+ * 构建cookie,会对cookie值用UTF-8做URL编码,避免中文乱码
+ */
+ public void build(){
+ if (response == null) {
+ log.error("response为null,无法写入cookie");
+ return;
+ }
+ Cookie cookie = new Cookie(name, URLEncoder.encode(value, charset));
+ if(StrUtil.isNotBlank(domain)) {
+ cookie.setDomain(domain);
+ }else if (request != null) {
+ String serverName = request.getServerName();
+ serverName = StrUtil.subAfter(serverName, ".", false);
+ cookie.setDomain("." + serverName);
+ }
+ cookie.setHttpOnly(httpOnly);
+ cookie.setMaxAge(maxAge);
+ cookie.setPath(path);
+ log.debug("生成cookie,编码方式:{},【{}={},domain:{};maxAge={};path={};httpOnly={}】",
+ charset.name(), name, value, domain, maxAge, path, httpOnly);
+ response.addCookie(cookie);
+ }
+
+ /**
+ * 利用UTF-8对cookie值解码,避免中文乱码问题
+ * @param cookieValue cookie原始值
+ * @return 解码后的值
+ */
+ public String decode(String cookieValue){
+ return URLDecoder.decode(cookieValue, charset);
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/utils/DateUtils.java b/hm-common/src/main/java/com/hmall/common/utils/DateUtils.java
new file mode 100644
index 0000000..3fea3f3
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/utils/DateUtils.java
@@ -0,0 +1,251 @@
+package com.hmall.common.utils;
+
+import cn.hutool.core.date.LocalDateTimeUtil;
+
+import java.text.SimpleDateFormat;
+import java.time.*;
+import java.time.format.DateTimeFormatter;
+import java.util.*;
+
+/**
+ * 时间工具类,用于本地时间操作,包含LocalDateTimeUtil的所有方法和自定义的LocalDateTime的操作方法及常量
+ *
+ * @author itheima
+ * @version 1.0.0 1.0
+ * @see 1.0
+ * @since 从哪个版本开始支持该类的功能
+ */
+public class DateUtils extends LocalDateTimeUtil {
+ public static final String DEFAULT_YEAR_FORMAT = "yyyy";
+ public static final String DEFAULT_MONTH_FORMAT = "yyyy-MM";
+ public static final String DEFAULT_MONTH_FORMAT_SLASH = "yyyy/MM";
+ public static final String DEFAULT_MONTH_FORMAT_EN = "yyyy年MM月";
+ public static final String DEFAULT_MONTH_FORMAT_COMPACT = "yyyyMM";
+ public static final String DEFAULT_WEEK_FORMAT = "yyyy-ww";
+ public static final String DEFAULT_WEEK_FORMAT_EN = "yyyy年ww周";
+ public static final String DEFAULT_DATE_FORMAT = "yyyy-MM-dd";
+ public static final String DEFAULT_DATE_FORMAT_EN = "yyyy年MM月dd日";
+ public static final String DEFAULT_DATE_FORMAT_COMPACT = "yyyyMMdd";
+ public static final String DEFAULT_DATE_TIME_FORMAT = "yyyy-MM-dd HH:mm:ss";
+ public static final String DEFAULT_DATE_TIME_COMPACT = "yyyyMMddHHmmss";
+ public static final String DEFAULT_TIME_FORMAT = "HH:mm:ss";
+ public static final String DATE_HOUR_FORMAT = "ddHH";
+ public static final String DAY = "DAY";
+ public static final String MONTH = "MONTH";
+ public static final String WEEK = "WEEK";
+ public static final long MAX_MONTH_DAY = 30L;
+ public static final long MAX_3_MONTH_DAY = 90L;
+ public static final long MAX_YEAR_DAY = 365L;
+
+
+ public static final DateTimeFormatter SIGN_DATE_SUFFIX_FORMATTER =
+ DateTimeFormatter.ofPattern(":yyyyMM");
+ public static final DateTimeFormatter POINTS_BOARD_SUFFIX_FORMATTER =
+ DateTimeFormatter.ofPattern("yyyyMM");
+
+ public static final String TIME_ZONE_8 = "GMT+8";
+
+ /**
+ * 获取utc时间
+ *
+ * @param localDateTime 转化时间
+ * @return utc时间
+ */
+ public static LocalDateTime getUTCTime(LocalDateTime localDateTime) {
+ ZoneId australia = ZoneId.of("Asia/Shanghai");
+ ZonedDateTime dateAndTimeInSydney = ZonedDateTime.of(localDateTime, australia);
+ ZonedDateTime utcDate = dateAndTimeInSydney.withZoneSameInstant(ZoneOffset.UTC);
+ return utcDate.toLocalDateTime();
+ }
+
+ /**
+ * 获取Asia时间
+ *
+ * @param localDateTime 转化时间
+ * @return Asia时间
+ */
+ public static LocalDateTime getAsiaTime(LocalDateTime localDateTime) {
+ ZoneId australia = ZoneId.of("Asia/Shanghai");
+ ZonedDateTime dateAndTimeInSydney = ZonedDateTime.of(localDateTime, ZoneOffset.UTC);
+ ZonedDateTime utcDate = dateAndTimeInSydney.withZoneSameInstant(australia);
+ return utcDate.toLocalDateTime();
+ }
+
+ /**
+ * 获取某一天的开始:0点0分
+ *
+ * @param localDateTime 指定日期
+ * @return 转换后的时间
+ */
+ public static LocalDateTime getDayStartTime(LocalDateTime localDateTime) {
+ if (localDateTime == null) {
+ return null;
+ }
+ return localDateTime.toLocalDate().atStartOfDay();
+ }
+
+ /**
+ * 获取某一天的结束:23点 59分 59秒的时间
+ *
+ * @param localDateTime 指定日期
+ * @return 转换后的时间
+ */
+ public static LocalDateTime getDayEndTime(LocalDateTime localDateTime) {
+ if (localDateTime == null) {
+ return null;
+ }
+ return LocalDateTime.of(localDateTime.toLocalDate(), LocalTime.MAX);
+ }
+
+ public static Date addDays(int i) {
+ Calendar c = Calendar.getInstance(TimeZone.getTimeZone(TIME_ZONE_8));
+ c.add(Calendar.DAY_OF_MONTH, i);
+ return c.getTime();
+ }
+
+
+ public static LocalDate getMonthBegin(LocalDate date) {
+ return LocalDate.of(date.getYear(), date.getMonth(), 1);
+ }
+
+ public static LocalDate getMonthEnd(LocalDate date) {
+ return LocalDate.of(date.getYear(), date.getMonthValue() + 1, 1).minusDays(1);
+ }
+
+ public static LocalDateTime getMonthBeginTime(LocalDate date) {
+ return LocalDate.of(date.getYear(), date.getMonth(), 1).atStartOfDay();
+ }
+
+ public static LocalDateTime getMonthEndTime(LocalDate date) {
+ return LocalDate.of(date.getYear(), date.getMonthValue() + 1, 1)
+ .minusDays(1).atTime(LocalTime.MAX);
+ }
+
+ public static LocalDateTime getWeekBeginTime(LocalDate now) {
+ return now.minusDays(now.getDayOfWeek().getValue() - 1).atStartOfDay();
+ }
+
+ public static LocalDateTime getWeekEndTime(LocalDate now) {
+ return LocalDateTime.of(now.plusDays(8 - now.getDayOfWeek().getValue()), LocalTime.MAX);
+ }
+
+ /**
+ * 获取最近15天日期(不包含当天),格式MM.dd
+ *
+ * @return
+ */
+ public static List last15Day() {
+ // 1.定义日期列表
+ List days = new ArrayList<>();
+ // 2.获取15天前的时间
+ LocalDateTime time = now().minusDays(15);
+ // 3.for循环遍历
+ for (int count = 0; count < 15; count++) {
+ // 3.1.格式化时间
+ days.add(String.format("%s.%s",
+ NumberUtils.repair0(time.getMonthValue(), 2), NumberUtils.repair0(time.getDayOfMonth(), 2)));
+ // 3.2.日期加1天
+ time = time.plusDays(1);
+ }
+ // 4.返回结果
+ return days;
+ }
+
+ /**
+ * 获取当前时间s
+ *
+ * @return
+ */
+ public static int getCurrentTime() {
+ return (int) (System.currentTimeMillis() / 1000);
+ }
+
+ public static int getDay() {
+ return getDay(null);
+ }
+
+ public static int getDay(LocalDateTime localDateTime) {
+ if (localDateTime == null) {
+ localDateTime = now();
+ }
+ String format = format(localDateTime, DEFAULT_DATE_FORMAT_COMPACT);
+ return NumberUtils.parseInt(format);
+ }
+
+ /**
+ * 获取数字格式的日志
+ *
+ * @param localDateTime 日期
+ * @param format 格式模板,只支持纯数字模板
+ * @return
+ */
+ public static Long getFormatDate(LocalDateTime localDateTime, String format) {
+ String date = format(localDateTime, format);
+ return date == null ? null : NumberUtils.parseLong(date);
+ }
+
+ /**
+ * 获取数字格式的日志
+ *
+ * @param localDateTime 日期
+ * @param format 格式模板,只支持纯数字模板
+ * @return
+ */
+ public static Integer getIntFormatDate(LocalDateTime localDateTime, String format) {
+ String date = format(localDateTime, format);
+ return date == null ? null : NumberUtils.parseInt(date);
+ }
+
+ /**
+ * 获取最小的一个时间
+ * @param localDateTimes
+ * @return
+ */
+ public static LocalDateTime getMin(LocalDateTime... localDateTimes) {
+ if(localDateTimes == null || localDateTimes.length <= 0) {
+ return null;
+ }
+ if(localDateTimes.length == 1) {
+ return localDateTimes[0];
+ }
+ List localDateTimeList = Arrays.asList(localDateTimes);
+ return localDateTimeList.stream().sorted().findFirst().orElse(null);
+ }
+
+ public static LocalDateTime getMax(LocalDateTime ... localDateTimes) {
+ if(localDateTimes == null || localDateTimes.length <= 0) {
+ return null;
+ }
+ if(localDateTimes.length == 1) {
+ return localDateTimes[0];
+ }
+ List localDateTimeList = Arrays.asList(localDateTimes);
+ return localDateTimeList.stream().sorted(Comparator.reverseOrder()).findFirst().orElse(null);
+ }
+
+ /**
+ * 将一个字符串转换成日期格式
+ *
+ * @param date 字符串日期
+ * @param pattern 日期格式
+ * @return date
+ */
+ public static Date toDate(String date, String pattern) {
+ if ("".equals("" + date)) {
+ return null;
+ }
+ if (pattern == null) {
+ pattern = DEFAULT_DATE_FORMAT;
+ }
+ SimpleDateFormat sdf = new SimpleDateFormat(pattern, Locale.ENGLISH);
+ Date newDate = new Date();
+ try {
+ newDate = sdf.parse(date);
+
+ } catch (Exception ex) {
+ ex.printStackTrace();
+ }
+ return newDate;
+ }
+
+}
diff --git a/hm-common/src/main/java/com/hmall/common/utils/NumberUtils.java b/hm-common/src/main/java/com/hmall/common/utils/NumberUtils.java
new file mode 100644
index 0000000..46eaa14
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/utils/NumberUtils.java
@@ -0,0 +1,151 @@
+package com.hmall.common.utils;
+
+import cn.hutool.core.util.NumberUtil;
+
+import java.math.BigDecimal;
+import java.util.Comparator;
+import java.util.List;
+import java.util.stream.Collectors;
+
+public class NumberUtils extends NumberUtil {
+
+
+ /**
+ * 如果number为空,将number转换为0,否则原数字返回
+ *
+ * @param number 原数值
+ * @return 整型数字,0或原数字
+ */
+ public static Integer null2Zero(Integer number){
+ return number == null ? 0 : number;
+ }
+
+ /**
+ * 如果number为空,将number转换为0,否则原数字返回
+ *
+ * @param number 原数值
+ * @return 整型数字,0或原数字
+ */
+ public static Double null2Zero(Double number){
+ return number == null ? 0 : number;
+ }
+
+ /**
+ * 如果是空值,返回默认数据;如果有值直接返回
+ * @param number
+ * @param defaultNumber
+ * @return
+ */
+ public static double null2Default(Double number, double defaultNumber) {
+ return number == null ? defaultNumber : number;
+ }
+
+ /**
+ * 如果number为空,将number转换为0L,否则原数字返回
+ *
+ * @param number 原数值
+ * @return 长整型数字,0L或原数字
+ */
+ public static Long null2Zero(Long number){
+ return number == null ? 0L : number;
+ }
+
+
+ public static Double setScale(Double number) {
+ return new BigDecimal(number)
+ .setScale(2, BigDecimal.ROUND_HALF_UP)
+ .doubleValue();
+ }
+ /**
+ * 比较两个数字是否相同,
+ * @param number1 数值1
+ * @param number2 数值2
+ * @return 是否一致
+ */
+ public static boolean equals(Integer number1, Integer number2) {
+ if(number1 == null || number2 == null){
+ return false;
+ }
+ return number1.equals(number2);
+ }
+
+ /**
+ * 数字除法保留指定小数位
+ * @param num1 被除数
+ * @param num2 除数
+ * @param scale 小数点位数
+ * @return 结果
+ */
+ public static Double divToDouble(Integer num1, Integer num2, int scale){
+ if(num2 == null || num2 ==0 || num1 == null || num1 == 0) {
+ return 0d;
+ }
+ return div(num1, num2, scale).doubleValue();
+ }
+
+ public static Double max(List data){
+ if(CollUtils.isEmpty(data)){
+ return null;
+ }
+ return data.stream()
+ .max(Comparator.comparingDouble(num -> num))
+ .orElse(0d);
+ }
+ public static Double min(List data){
+ if(CollUtils.isEmpty(data)){
+ return null;
+ }
+ return data.stream()
+ .min(Comparator.comparingDouble(num -> num))
+ .orElse(0d);
+ }
+
+ public static Double average(List data){
+ if(CollUtils.isEmpty(data)){
+ return 0d;
+ }
+ return data.stream()
+ .collect(Collectors.averagingDouble(Double::doubleValue));
+
+ }
+
+ public static Integer toInt(Object obj) {
+ return obj == null ? null
+ : obj instanceof Integer
+ ? (int) obj : null;
+ }
+
+ /**
+ * 取绝对值,如果为null,返回0
+ * @param number 数值
+ * @return 绝对值
+ */
+ public static int abs(Integer number) {
+ return number == null
+ ? 0
+ : Math.abs(number);
+ }
+
+ /**
+ * 数字格式化字符串,不足位数补0
+ *
+ * @param originNumber 原始数字
+ * @param digit 数字位数
+ * @return 字符串
+ */
+ public static String repair0(Integer originNumber, Integer digit){
+ StringBuilder number = new StringBuilder(originNumber + "");
+ while (number.length() < digit) {
+ number.insert(0, "0");
+ }
+ return number.toString();
+ }
+
+ public static Integer null2Default(Integer originNumber, int defaultNumber) {
+ return originNumber == null ? defaultNumber : originNumber;
+ }
+
+ public static Long null2Default(Long originNumber, long defaultNumber) {
+ return originNumber == null ? defaultNumber : originNumber;
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/utils/UserContext.java b/hm-common/src/main/java/com/hmall/common/utils/UserContext.java
new file mode 100644
index 0000000..eb4c858
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/utils/UserContext.java
@@ -0,0 +1,29 @@
+package com.hmall.common.utils;
+
+public class UserContext {
+ private static final ThreadLocal tl = new ThreadLocal<>();
+
+ /**
+ * 保存当前登录用户信息到ThreadLocal
+ * @param userId 用户id
+ */
+ public static void setUser(Long userId) {
+ tl.set(userId);
+ }
+
+ /**
+ * 获取当前登录用户信息
+ * @return 用户id
+ */
+ public static Long getUser() {
+ return 1L;
+// return tl.get();
+ }
+
+ /**
+ * 移除当前登录用户信息
+ */
+ public static void removeUser(){
+ tl.remove();
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/utils/WebUtils.java b/hm-common/src/main/java/com/hmall/common/utils/WebUtils.java
new file mode 100644
index 0000000..eaff08a
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/utils/WebUtils.java
@@ -0,0 +1,151 @@
+package com.hmall.common.utils;
+
+
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.util.StrUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+import org.springframework.web.context.request.RequestAttributes;
+import org.springframework.web.context.request.RequestContextHolder;
+import org.springframework.web.context.request.ServletRequestAttributes;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.util.Collection;
+import java.util.Map;
+
+@Slf4j
+public class WebUtils {
+
+ /**
+ * 获取ServletRequestAttributes
+ *
+ * @return ServletRequestAttributes
+ */
+ public static ServletRequestAttributes getServletRequestAttributes() {
+ RequestAttributes ra = RequestContextHolder.getRequestAttributes();
+ if (ra == null) {
+ return null;
+ }
+ return (ServletRequestAttributes) ra;
+ }
+
+ /**
+ * 获取request
+ *
+ * @return HttpServletRequest
+ */
+ public static HttpServletRequest getRequest() {
+ ServletRequestAttributes servletRequestAttributes = getServletRequestAttributes();
+ return servletRequestAttributes == null ? null : servletRequestAttributes.getRequest();
+ }
+
+ /**
+ * 获取response
+ *
+ * @return HttpServletResponse
+ */
+ public static HttpServletResponse getResponse() {
+ ServletRequestAttributes servletRequestAttributes = getServletRequestAttributes();
+ return servletRequestAttributes == null ? null : servletRequestAttributes.getResponse();
+ }
+
+ /**
+ * 获取request header中的内容
+ *
+ * @param headerName 请求头名称
+ * @return 请求头的值
+ */
+ public static String getHeader(String headerName) {
+ HttpServletRequest request = getRequest();
+ if (request == null) {
+ return null;
+ }
+ return getRequest().getHeader(headerName);
+ }
+
+ public static void setResponseHeader(String key, String value){
+ HttpServletResponse response = getResponse();
+ if (response == null) {
+ return;
+ }
+ response.setHeader(key, value);
+ }
+
+ public static boolean isSuccess() {
+ HttpServletResponse response = getResponse();
+ return response != null && response.getStatus() < 300;
+ }
+
+ /**
+ * 获取请求地址中的请求参数组装成 key1=value1&key2=value2
+ * 如果key对应多个值,中间使用逗号隔开例如 key1对应value1,key2对应value2,value3, key1=value1&key2=value2,value3
+ *
+ * @param request
+ * @return 返回拼接字符串
+ */
+ public static String getParameters(HttpServletRequest request) {
+ Map parameterMap = request.getParameterMap();
+ return getParameters(parameterMap);
+ }
+
+ /**
+ * 获取请求地址中的请求参数组装成 key1=value1&key2=value2
+ * 如果key对应多个值,中间使用逗号隔开例如 key1对应value1,key2对应value2,value3, key1=value1&key2=value2,value3
+ *
+ * @param queries
+ * @return
+ */
+ public static String getParameters(final Map queries) {
+ StringBuilder buffer = new StringBuilder();
+ for (Map.Entry entry : queries.entrySet()) {
+ if(entry.getValue() instanceof String[]){
+ buffer.append(entry.getKey()).append(String.join(",", ((String[])entry.getValue())))
+ .append("&");
+ }else if(entry.getValue() instanceof Collection){
+ buffer.append(entry.getKey()).append(
+ CollUtil.join(((Collection)entry.getValue()),",")
+ ).append("&");
+ }
+ }
+ return buffer.length() > 0 ? buffer.substring(0, buffer.length() - 1) : StrUtil.EMPTY;
+ }
+
+ /**
+ * 获取请求url中的uri
+ *
+ * @param url
+ * @return
+ */
+ public static String getUri(String url){
+ if(StringUtils.isEmpty(url)) {
+ return null;
+ }
+
+ String uri = url;
+ //uri中去掉 http:// 或者https
+ if(uri.contains("http://") ){
+ uri = uri.replace("http://", StrUtil.EMPTY);
+ }else if(uri.contains("https://")){
+ uri = uri.replace("https://", StrUtil.EMPTY);
+ }
+
+ int endIndex = uri.length(); //uri 在url中的最后一个字符的序号+1
+ if(uri.contains("?")){
+ endIndex = uri.indexOf("?");
+ }
+ return uri.substring(uri.indexOf("/"), endIndex);
+ }
+
+ public static String getRemoteAddr() {
+ HttpServletRequest request = getRequest();
+ if (request == null) {
+ return "";
+ }
+ return request.getRemoteAddr();
+ }
+
+ public static CookieBuilder cookieBuilder(){
+ return new CookieBuilder(getRequest(), getResponse());
+ }
+}
diff --git a/hm-common/src/main/resources/META-INF/spring-configuration-metadata.json b/hm-common/src/main/resources/META-INF/spring-configuration-metadata.json
new file mode 100644
index 0000000..c939abe
--- /dev/null
+++ b/hm-common/src/main/resources/META-INF/spring-configuration-metadata.json
@@ -0,0 +1,171 @@
+{
+ "groups": [
+ {
+ "name": "hm.db"
+ },
+ {
+ "name": "hm.mq"
+ },
+ {
+ "name": "hm.swagger"
+ },
+ {
+ "name": "hm.jwt",
+ "type": "com.hmall.config.SecurityConfig",
+ "sourceType": "com.hmall.config.JwtProperties"
+ },
+ {
+ "name": "hm.auth",
+ "type": "com.hmall.config.MvcConfig",
+ "sourceType": "com.hmall.config.AuthProperties"
+ }
+ ],
+ "properties": [
+ {
+ "name": "hm.mq.host",
+ "type": "java.lang.String",
+ "description": "rabbitmq的地址",
+ "defaultValue": "192.168.150.101"
+ },
+ {
+ "name": "hm.mq.port",
+ "type": "java.lang.Integer",
+ "description": "rabbitmq的端口",
+ "defaultValue": "5672"
+ },
+ {
+ "name": "hm.mq.vhost",
+ "type": "java.lang.String",
+ "description": "rabbitmq的virtual-host地址",
+ "defaultValue": "/hmxt"
+ },
+ {
+ "name": "hm.mq.username",
+ "type": "java.lang.String",
+ "description": "rabbitmq的用户名",
+ "defaultValue": "hmxt"
+ },
+ {
+ "name": "hm.mq.password",
+ "type": "java.lang.String",
+ "description": "rabbitmq的密码",
+ "defaultValue": "123321"
+ },
+ {
+ "name": "hm.mq.listener.retry.enable",
+ "type": "java.lang.Boolean",
+ "description": "是否开启rabbitmq的消费者重试机制",
+ "defaultValue": "true"
+ },
+ {
+ "name": "hm.mq.listener.retry.interval",
+ "type": "java.time.Duration",
+ "description": "消费者重试初始失败等待时长",
+ "defaultValue": "1000ms"
+ },
+ {
+ "name": "hm.mq.listener.retry.multiplier",
+ "type": "java.lang.Integer",
+ "description": "失败等待时长的递增倍数",
+ "defaultValue": "1"
+ },
+ {
+ "name": "hm.mq.listener.retry.max-attempts",
+ "type": "java.lang.Integer",
+ "description": "消费者重试最大重试次数",
+ "defaultValue": "3"
+ },
+ {
+ "name": "hm.mq.listener.retry.stateless",
+ "type": "java.lang.Boolean",
+ "description": "是否是无状态,默认true",
+ "defaultValue": "true"
+ },
+ {
+ "name": "hm.db.host",
+ "type": "java.lang.String",
+ "description": "数据库地址",
+ "defaultValue": "192.168.150.101"
+ },
+ {
+ "name": "hm.db.port",
+ "type": "java.lang.Integer",
+ "description": "数据库端口",
+ "defaultValue": "3306"
+ },
+ {
+ "name": "hm.db.database",
+ "type": "java.lang.String",
+ "description": "数据库database名",
+ "defaultValue": ""
+ },
+ {
+ "name": "hm.db.un",
+ "type": "java.lang.String",
+ "description": "数据库用户名",
+ "defaultValue": "root"
+ },
+ {
+ "name": "hm.db.pw",
+ "type": "java.lang.String",
+ "description": "数据库密码",
+ "defaultValue": "123"
+ },
+ {
+ "name": "hm.swagger.title",
+ "type": "java.lang.String",
+ "description": "接口文档标题"
+ },
+ {
+ "name": "hm.swagger.description",
+ "type": "java.lang.String",
+ "description": "接口文档描述"
+ },
+ {
+ "name": "hm.swagger.email",
+ "type": "java.lang.String",
+ "description": "接口文档联系人邮箱"
+ },
+ {
+ "name": "hm.swagger.concat",
+ "type": "java.lang.String",
+ "description": "接口文档联系人"
+ },
+ {
+ "name": "hm.swagger.package",
+ "type": "java.lang.String",
+ "description": "接口controller扫描包"
+ },
+ {
+ "name": "hm.jwt.location",
+ "type": "java.lang.String",
+ "description": "秘钥存储地址"
+ },
+ {
+ "name": "hm.jwt.alias",
+ "type": "java.lang.String",
+ "description": "秘钥别名"
+ },
+ {
+ "name": "hm.jwt.password",
+ "type": "java.lang.String",
+ "description": "秘钥文件密码"
+ },
+ {
+ "name": "hm.jwt.tokenTTL",
+ "type": "java.time.Duration",
+ "description": "登录有效期"
+ },
+ {
+ "name": "hm.auth.excludePaths",
+ "type": "java.util.List",
+ "description": "登录放行的路径"
+ },
+ {
+ "name": "hm.auth.includePaths",
+ "type": "java.util.List",
+ "description": "登录拦截的路径"
+ }
+ ],
+ "hints": []
+}
\ No newline at end of file
diff --git a/hm-common/src/main/resources/META-INF/spring.factories b/hm-common/src/main/resources/META-INF/spring.factories
new file mode 100644
index 0000000..46f5f33
--- /dev/null
+++ b/hm-common/src/main/resources/META-INF/spring.factories
@@ -0,0 +1,3 @@
+org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
+ com.hmall.common.config.MyBatisConfig,\
+ com.hmall.common.config.JsonConfig
\ No newline at end of file
--
Gitee
From beb0a29792931c1a308c1466d92f5bba45d13472 Mon Sep 17 00:00:00 2001
From: syd <1579670286@qq.com>
Date: Tue, 17 Jun 2025 19:34:11 +0800
Subject: [PATCH 4/8] =?UTF-8?q?=E5=B0=86item=E6=9C=8D=E5=8A=A1=E6=8A=BD?=
=?UTF-8?q?=E5=8F=96=E5=88=B0hm-api=E6=9C=8D=E5=8A=A1?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
cart-service/pom.xml | 22 ++++++++++
.../hmall/cart/CartServiceApplication.java | 2 +
.../cart/service/impl/CartServiceImpl.java | 10 ++---
.../src/main/resources/application.yaml | 3 ++
hm-api/pom.xml | 43 +++++++++++++++++++
.../java/com/hmall/api/item/ItemClient.java | 17 ++++++++
.../java/com/hmall/api/item}/dto/ItemDTO.java | 2 +-
item-service/pom.xml | 6 +++
.../hmall/item/controller/ItemController.java | 2 -
.../com/hmall/item/service/IItemService.java | 1 -
.../item/service/impl/ItemServiceImpl.java | 1 -
.../src/main/resources/application.yaml | 3 ++
pom.xml | 13 ++++++
13 files changed, 115 insertions(+), 10 deletions(-)
create mode 100644 hm-api/pom.xml
create mode 100644 hm-api/src/main/java/com/hmall/api/item/ItemClient.java
rename {item-service/src/main/java/com/hmall/item/domain => hm-api/src/main/java/com/hmall/api/item}/dto/ItemDTO.java (96%)
diff --git a/cart-service/pom.xml b/cart-service/pom.xml
index 24ce2d2..ca94a99 100644
--- a/cart-service/pom.xml
+++ b/cart-service/pom.xml
@@ -16,6 +16,28 @@
11
+
+
+
+ com.hmall
+ hm-api
+ 1.0.0
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-loadbalancer
+
com.hmall
diff --git a/cart-service/src/main/java/com/hmall/cart/CartServiceApplication.java b/cart-service/src/main/java/com/hmall/cart/CartServiceApplication.java
index 6a3ac40..2da23d7 100644
--- a/cart-service/src/main/java/com/hmall/cart/CartServiceApplication.java
+++ b/cart-service/src/main/java/com/hmall/cart/CartServiceApplication.java
@@ -3,7 +3,9 @@ package com.hmall.cart;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+@EnableFeignClients(basePackages = "com.hmall.api")
@MapperScan("com.hmall.cart.mapper")
@SpringBootApplication
public class CartServiceApplication {
diff --git a/cart-service/src/main/java/com/hmall/cart/service/impl/CartServiceImpl.java b/cart-service/src/main/java/com/hmall/cart/service/impl/CartServiceImpl.java
index fa2aaef..ab53f31 100644
--- a/cart-service/src/main/java/com/hmall/cart/service/impl/CartServiceImpl.java
+++ b/cart-service/src/main/java/com/hmall/cart/service/impl/CartServiceImpl.java
@@ -3,8 +3,9 @@ package com.hmall.cart.service.impl;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hmall.api.item.ItemClient;
+import com.hmall.api.item.dto.ItemDTO;
import com.hmall.cart.domain.dto.CartFormDTO;
-import com.hmall.cart.domain.dto.ItemDTO;
import com.hmall.cart.domain.po.Cart;
import com.hmall.cart.domain.vo.CartVO;
import com.hmall.cart.mapper.CartMapper;
@@ -32,7 +33,7 @@ import java.util.stream.Collectors;
@RequiredArgsConstructor
public class CartServiceImpl extends ServiceImpl implements ICartService {
-// private final IItemService itemService;
+ private final ItemClient itemClient;
@Override
public void addItem2Cart(CartFormDTO cartFormDTO) {
@@ -40,7 +41,7 @@ public class CartServiceImpl extends ServiceImpl implements IC
Long userId = UserContext.getUser();
// 2.判断是否已经存在
- if(checkItemExists(cartFormDTO.getItemId(), userId)){
+ if (checkItemExists(cartFormDTO.getItemId(), userId)) {
// 2.1.存在,则更新数量
baseMapper.updateNum(cartFormDTO.getItemId(), userId);
return;
@@ -79,8 +80,7 @@ public class CartServiceImpl extends ServiceImpl implements IC
// 1.获取商品id
Set itemIds = vos.stream().map(CartVO::getItemId).collect(Collectors.toSet());
// 2.查询商品
-// List items = itemService.queryItemByIds(itemIds);
- List items = new ArrayList<>();
+ List items = itemClient.queryItemByIds(itemIds);
if (CollUtils.isEmpty(items)) {
return;
}
diff --git a/cart-service/src/main/resources/application.yaml b/cart-service/src/main/resources/application.yaml
index e0a795a..5681cac 100644
--- a/cart-service/src/main/resources/application.yaml
+++ b/cart-service/src/main/resources/application.yaml
@@ -3,6 +3,9 @@ server:
spring:
application:
name: cart-service
+ cloud:
+ nacos:
+ server-addr: 192.168.101.68:8848 # nacos地址
profiles:
active: dev
datasource:
diff --git a/hm-api/pom.xml b/hm-api/pom.xml
new file mode 100644
index 0000000..57d57b4
--- /dev/null
+++ b/hm-api/pom.xml
@@ -0,0 +1,43 @@
+
+
+
+ com.hmall
+ hmall-parent
+ 1.0.0
+
+ 4.0.0
+
+ hm-api
+
+
+ 11
+ 11
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-loadbalancer
+
+
+ com.hmall
+ hm-common
+ 1.0.0
+
+
+
+ io.swagger
+ swagger-annotations
+ 1.6.6
+ compile
+
+
+
\ No newline at end of file
diff --git a/hm-api/src/main/java/com/hmall/api/item/ItemClient.java b/hm-api/src/main/java/com/hmall/api/item/ItemClient.java
new file mode 100644
index 0000000..0d4b6a4
--- /dev/null
+++ b/hm-api/src/main/java/com/hmall/api/item/ItemClient.java
@@ -0,0 +1,17 @@
+package com.hmall.api.item;
+
+import com.hmall.api.item.dto.ItemDTO;
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+import java.util.Collection;
+import java.util.List;
+
+@FeignClient(name = "item-service",path = "/items")
+public interface ItemClient {
+
+ @GetMapping
+ List queryItemByIds(@RequestParam("ids") Collection ids);
+
+}
diff --git a/item-service/src/main/java/com/hmall/item/domain/dto/ItemDTO.java b/hm-api/src/main/java/com/hmall/api/item/dto/ItemDTO.java
similarity index 96%
rename from item-service/src/main/java/com/hmall/item/domain/dto/ItemDTO.java
rename to hm-api/src/main/java/com/hmall/api/item/dto/ItemDTO.java
index 457f16d..5971871 100644
--- a/item-service/src/main/java/com/hmall/item/domain/dto/ItemDTO.java
+++ b/hm-api/src/main/java/com/hmall/api/item/dto/ItemDTO.java
@@ -1,4 +1,4 @@
-package com.hmall.item.domain.dto;
+package com.hmall.api.item.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
diff --git a/item-service/pom.xml b/item-service/pom.xml
index 6ffb080..5947d03 100644
--- a/item-service/pom.xml
+++ b/item-service/pom.xml
@@ -16,6 +16,12 @@
11
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
com.hmall
diff --git a/item-service/src/main/java/com/hmall/item/controller/ItemController.java b/item-service/src/main/java/com/hmall/item/controller/ItemController.java
index dc64e32..9ee51ee 100644
--- a/item-service/src/main/java/com/hmall/item/controller/ItemController.java
+++ b/item-service/src/main/java/com/hmall/item/controller/ItemController.java
@@ -5,8 +5,6 @@ import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hmall.common.domain.PageDTO;
import com.hmall.common.domain.PageQuery;
import com.hmall.common.utils.BeanUtils;
-
-import com.hmall.item.domain.dto.ItemDTO;
import com.hmall.item.domain.dto.OrderDetailDTO;
import com.hmall.item.domain.po.Item;
import com.hmall.item.service.IItemService;
diff --git a/item-service/src/main/java/com/hmall/item/service/IItemService.java b/item-service/src/main/java/com/hmall/item/service/IItemService.java
index d2b8b6b..830f8ca 100644
--- a/item-service/src/main/java/com/hmall/item/service/IItemService.java
+++ b/item-service/src/main/java/com/hmall/item/service/IItemService.java
@@ -2,7 +2,6 @@ package com.hmall.item.service;
import com.baomidou.mybatisplus.extension.service.IService;
-import com.hmall.item.domain.dto.ItemDTO;
import com.hmall.item.domain.dto.OrderDetailDTO;
import com.hmall.item.domain.po.Item;
diff --git a/item-service/src/main/java/com/hmall/item/service/impl/ItemServiceImpl.java b/item-service/src/main/java/com/hmall/item/service/impl/ItemServiceImpl.java
index 3b302c6..c14812c 100644
--- a/item-service/src/main/java/com/hmall/item/service/impl/ItemServiceImpl.java
+++ b/item-service/src/main/java/com/hmall/item/service/impl/ItemServiceImpl.java
@@ -4,7 +4,6 @@ import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.common.exception.BizIllegalException;
import com.hmall.common.utils.BeanUtils;
-import com.hmall.item.domain.dto.ItemDTO;
import com.hmall.item.domain.dto.OrderDetailDTO;
import com.hmall.item.domain.po.Item;
import com.hmall.item.mapper.ItemMapper;
diff --git a/item-service/src/main/resources/application.yaml b/item-service/src/main/resources/application.yaml
index 8615e3e..af2f175 100644
--- a/item-service/src/main/resources/application.yaml
+++ b/item-service/src/main/resources/application.yaml
@@ -3,6 +3,9 @@ server:
spring:
application:
name: item-service
+ cloud:
+ nacos:
+ server-addr: 192.168.101.68:8848 # nacos地址
profiles:
active: dev
datasource:
diff --git a/pom.xml b/pom.xml
index be4576e..a39ad8a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,6 +10,8 @@
item-service
cart-service
+ hm-common
+ hm-api
1.0.0
@@ -74,6 +76,17 @@
+
+
+ org.springframework.cloud
+ spring-cloud-starter-openfeign
+
+
+
+ com.alibaba.nacos
+ nacos-client
+ 2.4.0
+
org.projectlombok
--
Gitee
From d29482ae14d746565c370dbd4ad26c99944fe3cd Mon Sep 17 00:00:00 2001
From: syd <1579670286@qq.com>
Date: Wed, 18 Jun 2025 11:31:58 +0800
Subject: [PATCH 5/8] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E7=94=A8=E6=88=B7?=
=?UTF-8?q?=E3=80=81=E4=BA=A4=E6=98=93=E3=80=81=E6=94=AF=E4=BB=98=E5=BE=AE?=
=?UTF-8?q?=E6=9C=8D=E5=8A=A1=20=E5=AE=9E=E7=8E=B0cart=E6=9C=8D=E5=8A=A1?=
=?UTF-8?q?=E9=99=8D=E7=BA=A7=E3=80=81=E6=9C=8D=E5=8A=A1=E7=86=94=E6=96=AD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
cart-service/pom.xml | 10 ++
.../hmall/cart/CartServiceApplication.java | 2 +-
.../src/main/resources/application.yaml | 13 +-
.../java/com/hmall/api/cart/CartClient.java | 15 ++
.../java/com/hmall/api/item/ItemClient.java | 10 +-
.../api/item/ItemClientFallbackFactory.java | 34 +++++
.../hmall/api/item}/dto/OrderDetailDTO.java | 2 +-
.../java/com/hmall/api/trade/TradeClient.java | 14 ++
.../java/com/hmall/api/user/UserClient.java | 14 ++
item-service/pom.xml | 7 +-
.../hmall/item/controller/ItemController.java | 3 +-
.../com/hmall/item/mapper/ItemMapper.java | 2 +-
.../com/hmall/item/service/IItemService.java | 3 +-
.../item/service/impl/ItemServiceImpl.java | 3 +-
.../src/main/resources/application.yaml | 2 +-
pay-service/pom.xml | 62 +++++++++
.../src/main/java/PayServiceApplication.java | 15 ++
.../hmall/pay/controller/PayController.java | 40 ++++++
.../com/hmall/pay/domain/dto/PayApplyDTO.java | 30 ++++
.../hmall/pay/domain/dto/PayOrderFormDTO.java | 20 +++
.../com/hmall/pay/domain/po/PayOrder.java | 123 +++++++++++++++++
.../com/hmall/pay/domain/vo/PayOrderVO.java | 49 +++++++
.../java/com/hmall/pay/enums/PayChannel.java | 25 ++++
.../java/com/hmall/pay/enums/PayStatus.java | 27 ++++
.../java/com/hmall/pay/enums/PayType.java | 27 ++++
.../com/hmall/pay/mapper/PayOrderMapper.java | 17 +++
.../hmall/pay/service/IPayOrderService.java | 22 +++
.../pay/service/impl/PayOrderServiceImpl.java | 129 ++++++++++++++++++
.../src/main/resources/application-dev.yaml | 4 +
.../src/main/resources/application-local.yaml | 4 +
.../src/main/resources/application.yaml | 42 ++++++
.../main/resources/mapper/PayOrderMapper.xml | 5 +
pom.xml | 3 +
trade-service/pom.xml | 68 +++++++++
.../com/hmall/trade/TradeApplication.java | 15 ++
.../trade/controller/OrderController.java | 40 ++++++
.../hmall/trade/domain/dto/OrderFormDTO.java | 19 +++
.../java/com/hmall/trade/domain/po/Order.java | 91 ++++++++++++
.../hmall/trade/domain/po/OrderDetail.java | 81 +++++++++++
.../hmall/trade/domain/po/OrderLogistics.java | 86 ++++++++++++
.../com/hmall/trade/domain/vo/OrderVO.java | 34 +++++
.../hmall/trade/mapper/OrderDetailMapper.java | 17 +++
.../trade/mapper/OrderLogisticsMapper.java | 17 +++
.../com/hmall/trade/mapper/OrderMapper.java | 17 +++
.../trade/service/IOrderDetailService.java | 17 +++
.../trade/service/IOrderLogisticsService.java | 17 +++
.../hmall/trade/service/IOrderService.java | 21 +++
.../service/impl/OrderDetailServiceImpl.java | 21 +++
.../impl/OrderLogisticsServiceImpl.java | 21 +++
.../trade/service/impl/OrderServiceImpl.java | 114 ++++++++++++++++
.../src/main/resources/application-dev.yaml | 4 +
.../src/main/resources/application-local.yaml | 4 +
.../src/main/resources/application.yaml | 42 ++++++
user-service/pom.xml | 62 +++++++++
.../hmall/user/UserServiceApplication.java | 15 ++
.../com/hmall/user/config/JwtProperties.java | 17 +++
.../com/hmall/user/config/SecurityConfig.java | 33 +++++
.../user/controller/AddressController.java | 62 +++++++++
.../hmall/user/controller/UserController.java | 39 ++++++
.../com/hmall/user/domain/dto/AddressDTO.java | 28 ++++
.../hmall/user/domain/dto/LoginFormDTO.java | 20 +++
.../com/hmall/user/domain/po/Address.java | 77 +++++++++++
.../java/com/hmall/user/domain/po/User.java | 66 +++++++++
.../com/hmall/user/domain/vo/UserLoginVO.java | 11 ++
.../java/com/hmall/user/enums/UserStatus.java | 30 ++++
.../com/hmall/user/mapper/AddressMapper.java | 17 +++
.../com/hmall/user/mapper/UserMapper.java | 20 +++
.../hmall/user/service/IAddressService.java | 17 +++
.../com/hmall/user/service/IUserService.java | 22 +++
.../user/service/impl/AddressServiceImpl.java | 21 +++
.../user/service/impl/UserServiceImpl.java | 90 ++++++++++++
.../java/com/hmall/user/utils/JwtTool.java | 82 +++++++++++
.../src/main/resources/application-dev.yaml | 4 +
.../src/main/resources/application-local.yaml | 4 +
.../src/main/resources/application.yaml | 48 +++++++
user-service/src/main/resources/hmall.jks | Bin 0 -> 2711 bytes
.../src/main/resources/mapper/UserMapper.xml | 5 +
77 files changed, 2303 insertions(+), 11 deletions(-)
create mode 100644 hm-api/src/main/java/com/hmall/api/cart/CartClient.java
create mode 100644 hm-api/src/main/java/com/hmall/api/item/ItemClientFallbackFactory.java
rename {item-service/src/main/java/com/hmall/item/domain => hm-api/src/main/java/com/hmall/api/item}/dto/OrderDetailDTO.java (91%)
create mode 100644 hm-api/src/main/java/com/hmall/api/trade/TradeClient.java
create mode 100644 hm-api/src/main/java/com/hmall/api/user/UserClient.java
create mode 100644 pay-service/pom.xml
create mode 100644 pay-service/src/main/java/PayServiceApplication.java
create mode 100644 pay-service/src/main/java/com/hmall/pay/controller/PayController.java
create mode 100644 pay-service/src/main/java/com/hmall/pay/domain/dto/PayApplyDTO.java
create mode 100644 pay-service/src/main/java/com/hmall/pay/domain/dto/PayOrderFormDTO.java
create mode 100644 pay-service/src/main/java/com/hmall/pay/domain/po/PayOrder.java
create mode 100644 pay-service/src/main/java/com/hmall/pay/domain/vo/PayOrderVO.java
create mode 100644 pay-service/src/main/java/com/hmall/pay/enums/PayChannel.java
create mode 100644 pay-service/src/main/java/com/hmall/pay/enums/PayStatus.java
create mode 100644 pay-service/src/main/java/com/hmall/pay/enums/PayType.java
create mode 100644 pay-service/src/main/java/com/hmall/pay/mapper/PayOrderMapper.java
create mode 100644 pay-service/src/main/java/com/hmall/pay/service/IPayOrderService.java
create mode 100644 pay-service/src/main/java/com/hmall/pay/service/impl/PayOrderServiceImpl.java
create mode 100644 pay-service/src/main/resources/application-dev.yaml
create mode 100644 pay-service/src/main/resources/application-local.yaml
create mode 100644 pay-service/src/main/resources/application.yaml
create mode 100644 pay-service/src/main/resources/mapper/PayOrderMapper.xml
create mode 100644 trade-service/pom.xml
create mode 100644 trade-service/src/main/java/com/hmall/trade/TradeApplication.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/controller/OrderController.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/domain/dto/OrderFormDTO.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/domain/po/Order.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/domain/po/OrderDetail.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/domain/po/OrderLogistics.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/domain/vo/OrderVO.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/mapper/OrderDetailMapper.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/mapper/OrderLogisticsMapper.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/mapper/OrderMapper.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/service/IOrderDetailService.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/service/IOrderLogisticsService.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/service/IOrderService.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/service/impl/OrderDetailServiceImpl.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/service/impl/OrderLogisticsServiceImpl.java
create mode 100644 trade-service/src/main/java/com/hmall/trade/service/impl/OrderServiceImpl.java
create mode 100644 trade-service/src/main/resources/application-dev.yaml
create mode 100644 trade-service/src/main/resources/application-local.yaml
create mode 100644 trade-service/src/main/resources/application.yaml
create mode 100644 user-service/pom.xml
create mode 100644 user-service/src/main/java/com/hmall/user/UserServiceApplication.java
create mode 100644 user-service/src/main/java/com/hmall/user/config/JwtProperties.java
create mode 100644 user-service/src/main/java/com/hmall/user/config/SecurityConfig.java
create mode 100644 user-service/src/main/java/com/hmall/user/controller/AddressController.java
create mode 100644 user-service/src/main/java/com/hmall/user/controller/UserController.java
create mode 100644 user-service/src/main/java/com/hmall/user/domain/dto/AddressDTO.java
create mode 100644 user-service/src/main/java/com/hmall/user/domain/dto/LoginFormDTO.java
create mode 100644 user-service/src/main/java/com/hmall/user/domain/po/Address.java
create mode 100644 user-service/src/main/java/com/hmall/user/domain/po/User.java
create mode 100644 user-service/src/main/java/com/hmall/user/domain/vo/UserLoginVO.java
create mode 100644 user-service/src/main/java/com/hmall/user/enums/UserStatus.java
create mode 100644 user-service/src/main/java/com/hmall/user/mapper/AddressMapper.java
create mode 100644 user-service/src/main/java/com/hmall/user/mapper/UserMapper.java
create mode 100644 user-service/src/main/java/com/hmall/user/service/IAddressService.java
create mode 100644 user-service/src/main/java/com/hmall/user/service/IUserService.java
create mode 100644 user-service/src/main/java/com/hmall/user/service/impl/AddressServiceImpl.java
create mode 100644 user-service/src/main/java/com/hmall/user/service/impl/UserServiceImpl.java
create mode 100644 user-service/src/main/java/com/hmall/user/utils/JwtTool.java
create mode 100644 user-service/src/main/resources/application-dev.yaml
create mode 100644 user-service/src/main/resources/application-local.yaml
create mode 100644 user-service/src/main/resources/application.yaml
create mode 100644 user-service/src/main/resources/hmall.jks
create mode 100644 user-service/src/main/resources/mapper/UserMapper.xml
diff --git a/cart-service/pom.xml b/cart-service/pom.xml
index ca94a99..1e42024 100644
--- a/cart-service/pom.xml
+++ b/cart-service/pom.xml
@@ -17,6 +17,16 @@
+
+
+ io.github.openfeign
+ feign-okhttp
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-sentinel
+
com.hmall
diff --git a/cart-service/src/main/java/com/hmall/cart/CartServiceApplication.java b/cart-service/src/main/java/com/hmall/cart/CartServiceApplication.java
index 2da23d7..6cf5cf9 100644
--- a/cart-service/src/main/java/com/hmall/cart/CartServiceApplication.java
+++ b/cart-service/src/main/java/com/hmall/cart/CartServiceApplication.java
@@ -7,7 +7,7 @@ import org.springframework.cloud.openfeign.EnableFeignClients;
@EnableFeignClients(basePackages = "com.hmall.api")
@MapperScan("com.hmall.cart.mapper")
-@SpringBootApplication
+@SpringBootApplication(scanBasePackages = {"com.hmall.cart","com.hmall.api"})
public class CartServiceApplication {
public static void main(String[] args) {
diff --git a/cart-service/src/main/resources/application.yaml b/cart-service/src/main/resources/application.yaml
index 5681cac..4abd81b 100644
--- a/cart-service/src/main/resources/application.yaml
+++ b/cart-service/src/main/resources/application.yaml
@@ -6,6 +6,11 @@ spring:
cloud:
nacos:
server-addr: 192.168.101.68:8848 # nacos地址
+ sentinel:
+ transport:
+ dashboard: 192.168.101.68:9090
+ client-ip: 192.168.101.1
+ http-method-specify: true # 开启请求方式前缀可根据http请求方法区分簇点链路
profiles:
active: dev
datasource:
@@ -53,4 +58,10 @@ hm:
- /users/login
- /items/**
- /hi
-# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123
\ No newline at end of file
+feign:
+ okhttp:
+ enabled: true # 开启OKHttp功能
+ sentinel:
+ enabled: true # 开启feign对sentinel的支持
+
+# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123
diff --git a/hm-api/src/main/java/com/hmall/api/cart/CartClient.java b/hm-api/src/main/java/com/hmall/api/cart/CartClient.java
new file mode 100644
index 0000000..ccbcccb
--- /dev/null
+++ b/hm-api/src/main/java/com/hmall/api/cart/CartClient.java
@@ -0,0 +1,15 @@
+package com.hmall.api.cart;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.Collection;
+
+@FeignClient(name = "cart-service",path = "/carts")
+public interface CartClient {
+
+ // 清理购物车商品
+ @DeleteMapping
+ void deleteCartItemByIds(@RequestParam("ids") Collection ids);
+
+}
diff --git a/hm-api/src/main/java/com/hmall/api/item/ItemClient.java b/hm-api/src/main/java/com/hmall/api/item/ItemClient.java
index 0d4b6a4..1726eee 100644
--- a/hm-api/src/main/java/com/hmall/api/item/ItemClient.java
+++ b/hm-api/src/main/java/com/hmall/api/item/ItemClient.java
@@ -1,17 +1,23 @@
package com.hmall.api.item;
import com.hmall.api.item.dto.ItemDTO;
+import com.hmall.api.item.dto.OrderDetailDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
import java.util.List;
-@FeignClient(name = "item-service",path = "/items")
+@FeignClient(name = "item-service",path = "/items",fallbackFactory = ItemClientFallbackFactory.class )
public interface ItemClient {
-
+ // 查询商品
@GetMapping
List queryItemByIds(@RequestParam("ids") Collection ids);
+ // 扣减库存
+ @PutMapping("/stock/deduct")
+ void deductStock(@RequestBody List items);
}
diff --git a/hm-api/src/main/java/com/hmall/api/item/ItemClientFallbackFactory.java b/hm-api/src/main/java/com/hmall/api/item/ItemClientFallbackFactory.java
new file mode 100644
index 0000000..131dac0
--- /dev/null
+++ b/hm-api/src/main/java/com/hmall/api/item/ItemClientFallbackFactory.java
@@ -0,0 +1,34 @@
+package com.hmall.api.item;
+
+import com.hmall.api.item.dto.ItemDTO;
+import com.hmall.api.item.dto.OrderDetailDTO;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.cloud.openfeign.FallbackFactory;
+import org.springframework.stereotype.Component;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+/**
+ * @author syd
+ * @description itemClient的通用降级策略
+ */
+@Component
+@Slf4j
+public class ItemClientFallbackFactory implements FallbackFactory {
+ @Override
+ public ItemClient create(Throwable cause) {
+ return new ItemClient() {
+ @Override
+ public List queryItemByIds(Collection ids) {
+ log.warn("远程调用ItemClient#queryItemByIds方法出现异常,走降级,参数:{}", ids, cause);
+ return Collections.emptyList();
+ }
+
+ @Override
+ public void deductStock(List items) {
+ log.warn("远程调用ItemClient#deductStock扣减库存失败,走降级,参数:{}",items,cause);
+ }
+ };
+ }
+}
diff --git a/item-service/src/main/java/com/hmall/item/domain/dto/OrderDetailDTO.java b/hm-api/src/main/java/com/hmall/api/item/dto/OrderDetailDTO.java
similarity index 91%
rename from item-service/src/main/java/com/hmall/item/domain/dto/OrderDetailDTO.java
rename to hm-api/src/main/java/com/hmall/api/item/dto/OrderDetailDTO.java
index 6d2088b..94bb74c 100644
--- a/item-service/src/main/java/com/hmall/item/domain/dto/OrderDetailDTO.java
+++ b/hm-api/src/main/java/com/hmall/api/item/dto/OrderDetailDTO.java
@@ -1,4 +1,4 @@
-package com.hmall.item.domain.dto;
+package com.hmall.api.item.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
diff --git a/hm-api/src/main/java/com/hmall/api/trade/TradeClient.java b/hm-api/src/main/java/com/hmall/api/trade/TradeClient.java
new file mode 100644
index 0000000..0203220
--- /dev/null
+++ b/hm-api/src/main/java/com/hmall/api/trade/TradeClient.java
@@ -0,0 +1,14 @@
+package com.hmall.api.trade;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.PutMapping;
+
+@FeignClient(name = "trade-service",path = "/orders")
+public interface TradeClient {
+
+ // 标记订单已支付
+ @PutMapping("/{orderId}")
+ void markOrderPaySuccess(@PathVariable("orderId") Long orderId);
+
+}
diff --git a/hm-api/src/main/java/com/hmall/api/user/UserClient.java b/hm-api/src/main/java/com/hmall/api/user/UserClient.java
new file mode 100644
index 0000000..5ee6aee
--- /dev/null
+++ b/hm-api/src/main/java/com/hmall/api/user/UserClient.java
@@ -0,0 +1,14 @@
+package com.hmall.api.user;
+
+import org.springframework.cloud.openfeign.FeignClient;
+import org.springframework.web.bind.annotation.PutMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+
+@FeignClient(name = "user-service",path = "/users")
+public interface UserClient {
+
+ // 扣减余额
+ @PutMapping("/money/deduct")
+ void deductMoney(@RequestParam("pw") String pw,@RequestParam("amount") Integer amount);
+
+}
diff --git a/item-service/pom.xml b/item-service/pom.xml
index 5947d03..3cdb3d9 100644
--- a/item-service/pom.xml
+++ b/item-service/pom.xml
@@ -16,7 +16,12 @@
11
-
+
+
+ com.hmall
+ hm-api
+ 1.0.0
+
com.alibaba.cloud
diff --git a/item-service/src/main/java/com/hmall/item/controller/ItemController.java b/item-service/src/main/java/com/hmall/item/controller/ItemController.java
index 9ee51ee..1235745 100644
--- a/item-service/src/main/java/com/hmall/item/controller/ItemController.java
+++ b/item-service/src/main/java/com/hmall/item/controller/ItemController.java
@@ -2,10 +2,11 @@ package com.hmall.item.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.hmall.api.item.dto.ItemDTO;
+import com.hmall.api.item.dto.OrderDetailDTO;
import com.hmall.common.domain.PageDTO;
import com.hmall.common.domain.PageQuery;
import com.hmall.common.utils.BeanUtils;
-import com.hmall.item.domain.dto.OrderDetailDTO;
import com.hmall.item.domain.po.Item;
import com.hmall.item.service.IItemService;
import io.swagger.annotations.Api;
diff --git a/item-service/src/main/java/com/hmall/item/mapper/ItemMapper.java b/item-service/src/main/java/com/hmall/item/mapper/ItemMapper.java
index 73fea1b..2496dfa 100644
--- a/item-service/src/main/java/com/hmall/item/mapper/ItemMapper.java
+++ b/item-service/src/main/java/com/hmall/item/mapper/ItemMapper.java
@@ -2,7 +2,7 @@ package com.hmall.item.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
-import com.hmall.item.domain.dto.OrderDetailDTO;
+import com.hmall.api.item.dto.OrderDetailDTO;
import com.hmall.item.domain.po.Item;
import org.apache.ibatis.annotations.Update;
diff --git a/item-service/src/main/java/com/hmall/item/service/IItemService.java b/item-service/src/main/java/com/hmall/item/service/IItemService.java
index 830f8ca..680ef83 100644
--- a/item-service/src/main/java/com/hmall/item/service/IItemService.java
+++ b/item-service/src/main/java/com/hmall/item/service/IItemService.java
@@ -2,7 +2,8 @@ package com.hmall.item.service;
import com.baomidou.mybatisplus.extension.service.IService;
-import com.hmall.item.domain.dto.OrderDetailDTO;
+import com.hmall.api.item.dto.ItemDTO;
+import com.hmall.api.item.dto.OrderDetailDTO;
import com.hmall.item.domain.po.Item;
import java.util.Collection;
diff --git a/item-service/src/main/java/com/hmall/item/service/impl/ItemServiceImpl.java b/item-service/src/main/java/com/hmall/item/service/impl/ItemServiceImpl.java
index c14812c..88134ec 100644
--- a/item-service/src/main/java/com/hmall/item/service/impl/ItemServiceImpl.java
+++ b/item-service/src/main/java/com/hmall/item/service/impl/ItemServiceImpl.java
@@ -1,10 +1,11 @@
package com.hmall.item.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hmall.api.item.dto.ItemDTO;
+import com.hmall.api.item.dto.OrderDetailDTO;
import com.hmall.common.exception.BizIllegalException;
import com.hmall.common.utils.BeanUtils;
-import com.hmall.item.domain.dto.OrderDetailDTO;
import com.hmall.item.domain.po.Item;
import com.hmall.item.mapper.ItemMapper;
import com.hmall.item.service.IItemService;
diff --git a/item-service/src/main/resources/application.yaml b/item-service/src/main/resources/application.yaml
index af2f175..d2ed342 100644
--- a/item-service/src/main/resources/application.yaml
+++ b/item-service/src/main/resources/application.yaml
@@ -40,7 +40,7 @@ knife4j:
group-name: default
api-rule: package
api-rule-resources:
- - com.hmall.item.controller
+ - com.hmall.user.controller
hm:
jwt:
location: classpath:hmall.jks
diff --git a/pay-service/pom.xml b/pay-service/pom.xml
new file mode 100644
index 0000000..e221bcb
--- /dev/null
+++ b/pay-service/pom.xml
@@ -0,0 +1,62 @@
+
+
+
+ hmall-parent
+ com.hmall
+ 1.0.0
+
+ 4.0.0
+
+ pay-service
+
+
+ 11
+ 11
+
+
+
+
+
+ com.hmall
+ hm-common
+ 1.0.0
+
+
+
+ com.hmall
+ hm-api
+ 1.0.0
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
\ No newline at end of file
diff --git a/pay-service/src/main/java/PayServiceApplication.java b/pay-service/src/main/java/PayServiceApplication.java
new file mode 100644
index 0000000..4fe7fd6
--- /dev/null
+++ b/pay-service/src/main/java/PayServiceApplication.java
@@ -0,0 +1,15 @@
+package com.hmall.pay;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+@EnableFeignClients(basePackages = "com.hmall.api")
+@MapperScan("com.hmall.pay.mapper")
+@SpringBootApplication
+public class PayServiceApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(com.hmall.pay.PayServiceApplication.class, args);
+ }
+}
\ No newline at end of file
diff --git a/pay-service/src/main/java/com/hmall/pay/controller/PayController.java b/pay-service/src/main/java/com/hmall/pay/controller/PayController.java
new file mode 100644
index 0000000..a03b8b7
--- /dev/null
+++ b/pay-service/src/main/java/com/hmall/pay/controller/PayController.java
@@ -0,0 +1,40 @@
+package com.hmall.pay.controller;
+
+import com.hmall.common.exception.BizIllegalException;
+
+import com.hmall.pay.domain.dto.PayApplyDTO;
+import com.hmall.pay.domain.dto.PayOrderFormDTO;
+import com.hmall.pay.enums.PayType;
+import com.hmall.pay.service.IPayOrderService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+@Api(tags = "支付相关接口")
+@RestController
+@RequestMapping("pay-orders")
+@RequiredArgsConstructor
+public class PayController {
+
+ private final IPayOrderService payOrderService;
+
+ @ApiOperation("生成支付单")
+ @PostMapping
+ public String applyPayOrder(@RequestBody PayApplyDTO applyDTO){
+ if(!PayType.BALANCE.equalsValue(applyDTO.getPayType())){
+ // 目前只支持余额支付
+ throw new BizIllegalException("抱歉,目前只支持余额支付");
+ }
+ return payOrderService.applyPayOrder(applyDTO);
+ }
+
+ @ApiOperation("尝试基于用户余额支付")
+ @ApiImplicitParam(value = "支付单id", name = "id")
+ @PostMapping("{id}")
+ public void tryPayOrderByBalance(@PathVariable("id") Long id, @RequestBody PayOrderFormDTO payOrderFormDTO){
+ payOrderFormDTO.setId(id);
+ payOrderService.tryPayOrderByBalance(payOrderFormDTO);
+ }
+}
diff --git a/pay-service/src/main/java/com/hmall/pay/domain/dto/PayApplyDTO.java b/pay-service/src/main/java/com/hmall/pay/domain/dto/PayApplyDTO.java
new file mode 100644
index 0000000..49c24df
--- /dev/null
+++ b/pay-service/src/main/java/com/hmall/pay/domain/dto/PayApplyDTO.java
@@ -0,0 +1,30 @@
+package com.hmall.pay.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Builder;
+import lombok.Data;
+
+import javax.validation.constraints.Min;
+import javax.validation.constraints.NotNull;
+
+@Data
+@Builder
+@ApiModel(description = "支付下单表单实体")
+public class PayApplyDTO {
+ @ApiModelProperty("业务订单id不能为空")
+ @NotNull(message = "业务订单id不能为空")
+ private Long bizOrderNo;
+ @ApiModelProperty("支付金额必须为正数")
+ @Min(value = 1, message = "支付金额必须为正数")
+ private Integer amount;
+ @ApiModelProperty("支付渠道编码不能为空")
+ @NotNull(message = "支付渠道编码不能为空")
+ private String payChannelCode;
+ @ApiModelProperty("支付方式不能为空")
+ @NotNull(message = "支付方式不能为空")
+ private Integer payType;
+ @ApiModelProperty("订单中的商品信息不能为空")
+ @NotNull(message = "订单中的商品信息不能为空")
+ private String orderInfo;
+}
\ No newline at end of file
diff --git a/pay-service/src/main/java/com/hmall/pay/domain/dto/PayOrderFormDTO.java b/pay-service/src/main/java/com/hmall/pay/domain/dto/PayOrderFormDTO.java
new file mode 100644
index 0000000..214b797
--- /dev/null
+++ b/pay-service/src/main/java/com/hmall/pay/domain/dto/PayOrderFormDTO.java
@@ -0,0 +1,20 @@
+package com.hmall.pay.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Builder;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@Builder
+@ApiModel(description = "支付确认表单实体")
+public class PayOrderFormDTO {
+ @ApiModelProperty("支付订单id不能为空")
+ @NotNull(message = "支付订单id不能为空")
+ private Long id;
+ @ApiModelProperty("支付密码")
+ @NotNull(message = "支付密码")
+ private String pw;
+}
\ No newline at end of file
diff --git a/pay-service/src/main/java/com/hmall/pay/domain/po/PayOrder.java b/pay-service/src/main/java/com/hmall/pay/domain/po/PayOrder.java
new file mode 100644
index 0000000..b79960a
--- /dev/null
+++ b/pay-service/src/main/java/com/hmall/pay/domain/po/PayOrder.java
@@ -0,0 +1,123 @@
+package com.hmall.pay.domain.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ *
+ * 支付订单
+ *
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("pay_order")
+public class PayOrder implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * id
+ */
+ @TableId(value = "id", type = IdType.ASSIGN_ID)
+ private Long id;
+
+ /**
+ * 业务订单号
+ */
+ private Long bizOrderNo;
+
+ /**
+ * 支付单号
+ */
+ private Long payOrderNo;
+
+ /**
+ * 支付用户id
+ */
+ private Long bizUserId;
+
+ /**
+ * 支付渠道编码
+ */
+ private String payChannelCode;
+
+ /**
+ * 支付金额,单位分
+ */
+ private Integer amount;
+
+ /**
+ * 支付类型,1:h5,2:小程序,3:公众号,4:扫码,5:余额支付
+ */
+ private Integer payType;
+
+ /**
+ * 支付状态,0:待提交,1:待支付,2:支付超时或取消,3:支付成功
+ */
+ private Integer status;
+
+ /**
+ * 拓展字段,用于传递不同渠道单独处理的字段
+ */
+ private String expandJson;
+
+ /**
+ * 第三方返回业务码
+ */
+ private String resultCode;
+
+ /**
+ * 第三方返回提示信息
+ */
+ private String resultMsg;
+
+ /**
+ * 支付成功时间
+ */
+ private LocalDateTime paySuccessTime;
+
+ /**
+ * 支付超时时间
+ */
+ private LocalDateTime payOverTime;
+
+ /**
+ * 支付二维码链接
+ */
+ private String qrCodeUrl;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+
+ /**
+ * 创建人
+ */
+ private Long creater;
+
+ /**
+ * 更新人
+ */
+ private Long updater;
+
+ /**
+ * 逻辑删除
+ */
+ private Boolean isDelete;
+
+
+}
diff --git a/pay-service/src/main/java/com/hmall/pay/domain/vo/PayOrderVO.java b/pay-service/src/main/java/com/hmall/pay/domain/vo/PayOrderVO.java
new file mode 100644
index 0000000..5523cd6
--- /dev/null
+++ b/pay-service/src/main/java/com/hmall/pay/domain/vo/PayOrderVO.java
@@ -0,0 +1,49 @@
+package com.hmall.pay.domain.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+/**
+ *
+ * 支付订单
+ *
+ */
+@Data
+@ApiModel(description = "支付单vo实体")
+public class PayOrderVO {
+ @ApiModelProperty("id")
+ private Long id;
+ @ApiModelProperty("业务订单号")
+ private Long bizOrderNo;
+ @ApiModelProperty("支付单号")
+ private Long payOrderNo;
+ @ApiModelProperty("支付用户id")
+ private Long bizUserId;
+ @ApiModelProperty("支付渠道编码")
+ private String payChannelCode;
+ @ApiModelProperty("支付金额,单位分")
+ private Integer amount;
+ @ApiModelProperty("付类型,1:h5,2:小程序,3:公众号,4:扫码,5:余额支付")
+ private Integer payType;
+ @ApiModelProperty("付状态,0:待提交,1:待支付,2:支付超时或取消,3:支付成功")
+ private Integer status;
+ @ApiModelProperty("拓展字段,用于传递不同渠道单独处理的字段")
+ private String expandJson;
+ @ApiModelProperty("第三方返回业务码")
+ private String resultCode;
+ @ApiModelProperty("第三方返回提示信息")
+ private String resultMsg;
+ @ApiModelProperty("支付成功时间")
+ private LocalDateTime paySuccessTime;
+ @ApiModelProperty("支付超时时间")
+ private LocalDateTime payOverTime;
+ @ApiModelProperty("支付二维码链接")
+ private String qrCodeUrl;
+ @ApiModelProperty("创建时间")
+ private LocalDateTime createTime;
+ @ApiModelProperty("更新时间")
+ private LocalDateTime updateTime;
+}
diff --git a/pay-service/src/main/java/com/hmall/pay/enums/PayChannel.java b/pay-service/src/main/java/com/hmall/pay/enums/PayChannel.java
new file mode 100644
index 0000000..3d89a06
--- /dev/null
+++ b/pay-service/src/main/java/com/hmall/pay/enums/PayChannel.java
@@ -0,0 +1,25 @@
+package com.hmall.pay.enums;
+
+import cn.hutool.core.util.StrUtil;
+import lombok.Getter;
+
+@Getter
+public enum PayChannel {
+ wxPay("微信支付"),
+ aliPay("支付宝支付"),
+ balance("余额支付"),
+ ;
+
+ private final String desc;
+
+ PayChannel(String desc) {
+ this.desc = desc;
+ }
+
+ public static String desc(String value){
+ if (StrUtil.isBlank(value)) {
+ return "";
+ }
+ return PayChannel.valueOf(value).getDesc();
+ }
+}
diff --git a/pay-service/src/main/java/com/hmall/pay/enums/PayStatus.java b/pay-service/src/main/java/com/hmall/pay/enums/PayStatus.java
new file mode 100644
index 0000000..e64e184
--- /dev/null
+++ b/pay-service/src/main/java/com/hmall/pay/enums/PayStatus.java
@@ -0,0 +1,27 @@
+package com.hmall.pay.enums;
+
+import lombok.Getter;
+
+@Getter
+public enum PayStatus {
+ NOT_COMMIT(0, "未提交"),
+ WAIT_BUYER_PAY(1, "待支付"),
+ TRADE_CLOSED(2, "已关闭"),
+ TRADE_SUCCESS(3, "支付成功"),
+ TRADE_FINISHED(3, "支付成功"),
+ ;
+ private final int value;
+ private final String desc;
+
+ PayStatus(int value, String desc) {
+ this.value = value;
+ this.desc = desc;
+ }
+
+ public boolean equalsValue(Integer value){
+ if (value == null) {
+ return false;
+ }
+ return getValue() == value;
+ }
+}
\ No newline at end of file
diff --git a/pay-service/src/main/java/com/hmall/pay/enums/PayType.java b/pay-service/src/main/java/com/hmall/pay/enums/PayType.java
new file mode 100644
index 0000000..af68b9b
--- /dev/null
+++ b/pay-service/src/main/java/com/hmall/pay/enums/PayType.java
@@ -0,0 +1,27 @@
+package com.hmall.pay.enums;
+
+import lombok.Getter;
+
+@Getter
+public enum PayType{
+ JSAPI(1, "网页支付JS"),
+ MINI_APP(2, "小程序支付"),
+ APP(3, "APP支付"),
+ NATIVE(4, "扫码支付"),
+ BALANCE(5, "余额支付"),
+ ;
+ private final int value;
+ private final String desc;
+
+ PayType(int value, String desc) {
+ this.value = value;
+ this.desc = desc;
+ }
+
+ public boolean equalsValue(Integer value){
+ if (value == null) {
+ return false;
+ }
+ return getValue() == value;
+ }
+}
diff --git a/pay-service/src/main/java/com/hmall/pay/mapper/PayOrderMapper.java b/pay-service/src/main/java/com/hmall/pay/mapper/PayOrderMapper.java
new file mode 100644
index 0000000..c1156a5
--- /dev/null
+++ b/pay-service/src/main/java/com/hmall/pay/mapper/PayOrderMapper.java
@@ -0,0 +1,17 @@
+package com.hmall.pay.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hmall.pay.domain.po.PayOrder;
+
+/**
+ *
+ * 支付订单 Mapper 接口
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-16
+ */
+public interface PayOrderMapper extends BaseMapper {
+
+}
diff --git a/pay-service/src/main/java/com/hmall/pay/service/IPayOrderService.java b/pay-service/src/main/java/com/hmall/pay/service/IPayOrderService.java
new file mode 100644
index 0000000..4a3bb50
--- /dev/null
+++ b/pay-service/src/main/java/com/hmall/pay/service/IPayOrderService.java
@@ -0,0 +1,22 @@
+package com.hmall.pay.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hmall.pay.domain.dto.PayApplyDTO;
+import com.hmall.pay.domain.dto.PayOrderFormDTO;
+import com.hmall.pay.domain.po.PayOrder;
+
+/**
+ *
+ * 支付订单 服务类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-16
+ */
+public interface IPayOrderService extends IService {
+
+ String applyPayOrder(PayApplyDTO applyDTO);
+
+ void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO);
+}
diff --git a/pay-service/src/main/java/com/hmall/pay/service/impl/PayOrderServiceImpl.java b/pay-service/src/main/java/com/hmall/pay/service/impl/PayOrderServiceImpl.java
new file mode 100644
index 0000000..a27c8ee
--- /dev/null
+++ b/pay-service/src/main/java/com/hmall/pay/service/impl/PayOrderServiceImpl.java
@@ -0,0 +1,129 @@
+package com.hmall.pay.service.impl;
+
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import com.baomidou.mybatisplus.core.toolkit.StringUtils;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hmall.api.trade.TradeClient;
+import com.hmall.api.user.UserClient;
+import com.hmall.common.exception.BizIllegalException;
+import com.hmall.common.utils.BeanUtils;
+import com.hmall.common.utils.UserContext;
+
+import com.hmall.pay.domain.dto.PayApplyDTO;
+import com.hmall.pay.domain.dto.PayOrderFormDTO;
+import com.hmall.pay.domain.po.PayOrder;
+import com.hmall.pay.enums.PayStatus;
+import com.hmall.pay.mapper.PayOrderMapper;
+import com.hmall.pay.service.IPayOrderService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+
+/**
+ *
+ * 支付订单 服务实现类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-16
+ */
+@Service
+@RequiredArgsConstructor
+public class PayOrderServiceImpl extends ServiceImpl implements IPayOrderService {
+
+ private final UserClient userClient;
+
+ private final TradeClient tradeClient;
+
+ @Override
+ public String applyPayOrder(PayApplyDTO applyDTO) {
+ // 1.幂等性校验
+ PayOrder payOrder = checkIdempotent(applyDTO);
+ // 2.返回结果
+ return payOrder.getId().toString();
+ }
+
+ @Override
+ @Transactional
+ public void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO) {
+ // 1.查询支付单
+ PayOrder po = getById(payOrderFormDTO.getId());
+ // 2.判断状态
+ if(!PayStatus.WAIT_BUYER_PAY.equalsValue(po.getStatus())){
+ // 订单不是未支付,状态异常
+ throw new BizIllegalException("交易已支付或关闭!");
+ }
+ // 3.尝试扣减余额
+ userClient.deductMoney(payOrderFormDTO.getPw(), po.getAmount());
+ // 4.修改支付单状态
+ boolean success = markPayOrderSuccess(payOrderFormDTO.getId(), LocalDateTime.now());
+ if (!success) {
+ throw new BizIllegalException("交易已支付或关闭!");
+ }
+ // 5.修改订单状态
+ tradeClient.markOrderPaySuccess(po.getBizOrderNo());
+ }
+
+ public boolean markPayOrderSuccess(Long id, LocalDateTime successTime) {
+ return lambdaUpdate()
+ .set(PayOrder::getStatus, PayStatus.TRADE_SUCCESS.getValue())
+ .set(PayOrder::getPaySuccessTime, successTime)
+ .eq(PayOrder::getId, id)
+ // 支付状态的乐观锁判断
+ .in(PayOrder::getStatus, PayStatus.NOT_COMMIT.getValue(), PayStatus.WAIT_BUYER_PAY.getValue())
+ .update();
+ }
+
+
+ private PayOrder checkIdempotent(PayApplyDTO applyDTO) {
+ // 1.首先查询支付单
+ PayOrder oldOrder = queryByBizOrderNo(applyDTO.getBizOrderNo());
+ // 2.判断是否存在
+ if (oldOrder == null) {
+ // 不存在支付单,说明是第一次,写入新的支付单并返回
+ PayOrder payOrder = buildPayOrder(applyDTO);
+ payOrder.setPayOrderNo(IdWorker.getId());
+ save(payOrder);
+ return payOrder;
+ }
+ // 3.旧单已经存在,判断是否支付成功
+ if (PayStatus.TRADE_SUCCESS.equalsValue(oldOrder.getStatus())) {
+ // 已经支付成功,抛出异常
+ throw new BizIllegalException("订单已经支付!");
+ }
+ // 4.旧单已经存在,判断是否已经关闭
+ if (PayStatus.TRADE_CLOSED.equalsValue(oldOrder.getStatus())) {
+ // 已经关闭,抛出异常
+ throw new BizIllegalException("订单已关闭");
+ }
+ // 5.旧单已经存在,判断支付渠道是否一致
+ if (!StringUtils.equals(oldOrder.getPayChannelCode(), applyDTO.getPayChannelCode())) {
+ // 支付渠道不一致,需要重置数据,然后重新申请支付单
+ PayOrder payOrder = buildPayOrder(applyDTO);
+ payOrder.setId(oldOrder.getId());
+ payOrder.setQrCodeUrl("");
+ updateById(payOrder);
+ payOrder.setPayOrderNo(oldOrder.getPayOrderNo());
+ return payOrder;
+ }
+ // 6.旧单已经存在,且可能是未支付或未提交,且支付渠道一致,直接返回旧数据
+ return oldOrder;
+ }
+
+ private PayOrder buildPayOrder(PayApplyDTO payApplyDTO) {
+ // 1.数据转换
+ PayOrder payOrder = BeanUtils.toBean(payApplyDTO, PayOrder.class);
+ // 2.初始化数据
+ payOrder.setPayOverTime(LocalDateTime.now().plusMinutes(120L));
+ payOrder.setStatus(PayStatus.WAIT_BUYER_PAY.getValue());
+ payOrder.setBizUserId(UserContext.getUser());
+ return payOrder;
+ }
+ public PayOrder queryByBizOrderNo(Long bizOrderNo) {
+ return lambdaQuery()
+ .eq(PayOrder::getBizOrderNo, bizOrderNo)
+ .one();
+ }
+}
diff --git a/pay-service/src/main/resources/application-dev.yaml b/pay-service/src/main/resources/application-dev.yaml
new file mode 100644
index 0000000..1fb8f00
--- /dev/null
+++ b/pay-service/src/main/resources/application-dev.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: 192.168.101.68
+ pw: mysql
\ No newline at end of file
diff --git a/pay-service/src/main/resources/application-local.yaml b/pay-service/src/main/resources/application-local.yaml
new file mode 100644
index 0000000..87ff8f4
--- /dev/null
+++ b/pay-service/src/main/resources/application-local.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: localhost # 修改为你自己的虚拟机IP地址
+ pw: mysql # 修改为docker中的MySQL密码
\ No newline at end of file
diff --git a/pay-service/src/main/resources/application.yaml b/pay-service/src/main/resources/application.yaml
new file mode 100644
index 0000000..f9865ce
--- /dev/null
+++ b/pay-service/src/main/resources/application.yaml
@@ -0,0 +1,42 @@
+server:
+ port: 8086
+spring:
+ application:
+ name: pay-service
+ profiles:
+ active: dev
+ datasource:
+ url: jdbc:mysql://${hm.db.host}:3306/hm-pay?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ username: root
+ password: ${hm.db.pw}
+ cloud:
+ nacos:
+ server-addr: 192.168.101.68
+mybatis-plus:
+ configuration:
+ default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
+ global-config:
+ db-config:
+ update-strategy: not_null
+ id-type: auto
+logging:
+ level:
+ com.hmall: debug
+ pattern:
+ dateformat: HH:mm:ss:SSS
+ file:
+ path: "logs/${spring.application.name}"
+knife4j:
+ enable: true
+ openapi:
+ title: 支付服务接口文档
+ description: "支付服务接口文档"
+ url: https://www.itcast.cn
+ version: v1.0.0
+ group:
+ default:
+ group-name: default
+ api-rule: package
+ api-rule-resources:
+ - com.hmall.pay.controller
\ No newline at end of file
diff --git a/pay-service/src/main/resources/mapper/PayOrderMapper.xml b/pay-service/src/main/resources/mapper/PayOrderMapper.xml
new file mode 100644
index 0000000..2cbe4a8
--- /dev/null
+++ b/pay-service/src/main/resources/mapper/PayOrderMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/pom.xml b/pom.xml
index a39ad8a..8391108 100644
--- a/pom.xml
+++ b/pom.xml
@@ -12,6 +12,9 @@
cart-service
hm-common
hm-api
+ user-service
+ trade-service
+ pay-service
1.0.0
diff --git a/trade-service/pom.xml b/trade-service/pom.xml
new file mode 100644
index 0000000..a10e1d3
--- /dev/null
+++ b/trade-service/pom.xml
@@ -0,0 +1,68 @@
+
+
+
+ hmall-parent
+ com.hmall
+ 1.0.0
+
+ 4.0.0
+
+ trade-service
+
+
+ 11
+ 11
+
+
+
+
+
+ com.hmall
+ hm-api
+ 1.0.0
+
+
+
+ com.hmall
+ hm-common
+ 1.0.0
+
+
+
+ com.hmall
+ hm-api
+ 1.0.0
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
\ No newline at end of file
diff --git a/trade-service/src/main/java/com/hmall/trade/TradeApplication.java b/trade-service/src/main/java/com/hmall/trade/TradeApplication.java
new file mode 100644
index 0000000..522c07e
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/TradeApplication.java
@@ -0,0 +1,15 @@
+package com.hmall.trade;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.cloud.openfeign.EnableFeignClients;
+
+@EnableFeignClients(basePackages = "com.hmall.api")
+@MapperScan("com.hmall.trade.mapper")
+@SpringBootApplication
+public class TradeApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(TradeApplication.class, args);
+ }
+}
\ No newline at end of file
diff --git a/trade-service/src/main/java/com/hmall/trade/controller/OrderController.java b/trade-service/src/main/java/com/hmall/trade/controller/OrderController.java
new file mode 100644
index 0000000..593ce6a
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/controller/OrderController.java
@@ -0,0 +1,40 @@
+package com.hmall.trade.controller;
+
+import com.hmall.common.utils.BeanUtils;
+
+import com.hmall.trade.domain.dto.OrderFormDTO;
+import com.hmall.trade.domain.vo.OrderVO;
+import com.hmall.trade.service.IOrderService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.apache.ibatis.annotations.Param;
+import org.springframework.web.bind.annotation.*;
+
+@Api(tags = "订单管理接口")
+@RestController
+@RequestMapping("/orders")
+@RequiredArgsConstructor
+public class OrderController {
+ private final IOrderService orderService;
+
+ @ApiOperation("根据id查询订单")
+ @GetMapping("{id}")
+ public OrderVO queryOrderById(@Param ("订单id")@PathVariable("id") Long orderId) {
+ return BeanUtils.copyBean(orderService.getById(orderId), OrderVO.class);
+ }
+
+ @ApiOperation("创建订单")
+ @PostMapping
+ public Long createOrder(@RequestBody OrderFormDTO orderFormDTO){
+ return orderService.createOrder(orderFormDTO);
+ }
+
+ @ApiOperation("标记订单已支付")
+ @ApiImplicitParam(name = "orderId", value = "订单id", paramType = "path")
+ @PutMapping("/{orderId}")
+ public void markOrderPaySuccess(@PathVariable("orderId") Long orderId) {
+ orderService.markOrderPaySuccess(orderId);
+ }
+}
diff --git a/trade-service/src/main/java/com/hmall/trade/domain/dto/OrderFormDTO.java b/trade-service/src/main/java/com/hmall/trade/domain/dto/OrderFormDTO.java
new file mode 100644
index 0000000..a1f66ac
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/domain/dto/OrderFormDTO.java
@@ -0,0 +1,19 @@
+package com.hmall.trade.domain.dto;
+
+import com.hmall.api.item.dto.OrderDetailDTO;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.util.List;
+
+@Data
+@ApiModel(description = "交易下单表单实体")
+public class OrderFormDTO {
+ @ApiModelProperty("收货地址id")
+ private Long addressId;
+ @ApiModelProperty("支付类型")
+ private Integer paymentType;
+ @ApiModelProperty("下单商品列表")
+ private List details;
+}
diff --git a/trade-service/src/main/java/com/hmall/trade/domain/po/Order.java b/trade-service/src/main/java/com/hmall/trade/domain/po/Order.java
new file mode 100644
index 0000000..948e5e9
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/domain/po/Order.java
@@ -0,0 +1,91 @@
+package com.hmall.trade.domain.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ *
+ *
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("`order`")
+public class Order implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 订单id
+ */
+ @TableId(value = "id", type = IdType.ASSIGN_ID)
+ private Long id;
+
+ /**
+ * 总金额,单位为分
+ */
+ private Integer totalFee;
+
+ /**
+ * 支付类型,1、支付宝,2、微信,3、扣减余额
+ */
+ private Integer paymentType;
+
+ /**
+ * 用户id
+ */
+ private Long userId;
+
+ /**
+ * 订单的状态,1、未付款 2、已付款,未发货 3、已发货,未确认 4、确认收货,交易成功 5、交易取消,订单关闭 6、交易结束,已评价
+ */
+ private Integer status;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 支付时间
+ */
+ private LocalDateTime payTime;
+
+ /**
+ * 发货时间
+ */
+ private LocalDateTime consignTime;
+
+ /**
+ * 交易完成时间
+ */
+ private LocalDateTime endTime;
+
+ /**
+ * 交易关闭时间
+ */
+ private LocalDateTime closeTime;
+
+ /**
+ * 评价时间
+ */
+ private LocalDateTime commentTime;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+
+
+}
diff --git a/trade-service/src/main/java/com/hmall/trade/domain/po/OrderDetail.java b/trade-service/src/main/java/com/hmall/trade/domain/po/OrderDetail.java
new file mode 100644
index 0000000..e2a53f7
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/domain/po/OrderDetail.java
@@ -0,0 +1,81 @@
+package com.hmall.trade.domain.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ *
+ * 订单详情表
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("order_detail")
+public class OrderDetail implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 订单详情id
+ */
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 订单id
+ */
+ private Long orderId;
+
+ /**
+ * sku商品id
+ */
+ private Long itemId;
+
+ /**
+ * 购买数量
+ */
+ private Integer num;
+
+ /**
+ * 商品标题
+ */
+ private String name;
+
+ /**
+ * 商品动态属性键值集
+ */
+ private String spec;
+
+ /**
+ * 价格,单位:分
+ */
+ private Integer price;
+
+ /**
+ * 商品图片
+ */
+ private String image;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+
+
+}
diff --git a/trade-service/src/main/java/com/hmall/trade/domain/po/OrderLogistics.java b/trade-service/src/main/java/com/hmall/trade/domain/po/OrderLogistics.java
new file mode 100644
index 0000000..2d9885d
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/domain/po/OrderLogistics.java
@@ -0,0 +1,86 @@
+package com.hmall.trade.domain.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ *
+ *
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("order_logistics")
+public class OrderLogistics implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * 订单id,与订单表一对一
+ */
+ @TableId(value = "order_id", type = IdType.INPUT)
+ private Long orderId;
+
+ /**
+ * 物流单号
+ */
+ private String logisticsNumber;
+
+ /**
+ * 物流公司名称
+ */
+ private String logisticsCompany;
+
+ /**
+ * 收件人
+ */
+ private String contact;
+
+ /**
+ * 收件人手机号码
+ */
+ private String mobile;
+
+ /**
+ * 省
+ */
+ private String province;
+
+ /**
+ * 市
+ */
+ private String city;
+
+ /**
+ * 区
+ */
+ private String town;
+
+ /**
+ * 街道
+ */
+ private String street;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ /**
+ * 更新时间
+ */
+ private LocalDateTime updateTime;
+
+
+}
diff --git a/trade-service/src/main/java/com/hmall/trade/domain/vo/OrderVO.java b/trade-service/src/main/java/com/hmall/trade/domain/vo/OrderVO.java
new file mode 100644
index 0000000..18b2a08
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/domain/vo/OrderVO.java
@@ -0,0 +1,34 @@
+package com.hmall.trade.domain.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+@ApiModel(description = "订单页面VO")
+public class OrderVO {
+ @ApiModelProperty("订单id")
+ private Long id;
+ @ApiModelProperty("总金额,单位为分")
+ private Integer totalFee;
+ @ApiModelProperty("支付类型,1、支付宝,2、微信,3、扣减余额")
+ private Integer paymentType;
+ @ApiModelProperty("用户id")
+ private Long userId;
+ @ApiModelProperty("订单的状态,1、未付款 2、已付款,未发货 3、已发货,未确认 4、确认收货,交易成功 5、交易取消,订单关闭 6、交易结束,已评价")
+ private Integer status;
+ @ApiModelProperty("创建时间")
+ private LocalDateTime createTime;
+ @ApiModelProperty("支付时间")
+ private LocalDateTime payTime;
+ @ApiModelProperty("发货时间")
+ private LocalDateTime consignTime;
+ @ApiModelProperty("交易完成时间")
+ private LocalDateTime endTime;
+ @ApiModelProperty("交易关闭时间")
+ private LocalDateTime closeTime;
+ @ApiModelProperty("评价时间")
+ private LocalDateTime commentTime;
+}
diff --git a/trade-service/src/main/java/com/hmall/trade/mapper/OrderDetailMapper.java b/trade-service/src/main/java/com/hmall/trade/mapper/OrderDetailMapper.java
new file mode 100644
index 0000000..084129b
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/mapper/OrderDetailMapper.java
@@ -0,0 +1,17 @@
+package com.hmall.trade.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hmall.trade.domain.po.OrderDetail;
+
+/**
+ *
+ * 订单详情表 Mapper 接口
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface OrderDetailMapper extends BaseMapper {
+
+}
diff --git a/trade-service/src/main/java/com/hmall/trade/mapper/OrderLogisticsMapper.java b/trade-service/src/main/java/com/hmall/trade/mapper/OrderLogisticsMapper.java
new file mode 100644
index 0000000..bfc0277
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/mapper/OrderLogisticsMapper.java
@@ -0,0 +1,17 @@
+package com.hmall.trade.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hmall.trade.domain.po.OrderLogistics;
+
+/**
+ *
+ * Mapper 接口
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface OrderLogisticsMapper extends BaseMapper {
+
+}
diff --git a/trade-service/src/main/java/com/hmall/trade/mapper/OrderMapper.java b/trade-service/src/main/java/com/hmall/trade/mapper/OrderMapper.java
new file mode 100644
index 0000000..42fcb94
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/mapper/OrderMapper.java
@@ -0,0 +1,17 @@
+package com.hmall.trade.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hmall.trade.domain.po.Order;
+
+/**
+ *
+ * Mapper 接口
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface OrderMapper extends BaseMapper {
+
+}
diff --git a/trade-service/src/main/java/com/hmall/trade/service/IOrderDetailService.java b/trade-service/src/main/java/com/hmall/trade/service/IOrderDetailService.java
new file mode 100644
index 0000000..ede820a
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/service/IOrderDetailService.java
@@ -0,0 +1,17 @@
+package com.hmall.trade.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hmall.trade.domain.po.OrderDetail;
+
+/**
+ *
+ * 订单详情表 服务类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface IOrderDetailService extends IService {
+
+}
diff --git a/trade-service/src/main/java/com/hmall/trade/service/IOrderLogisticsService.java b/trade-service/src/main/java/com/hmall/trade/service/IOrderLogisticsService.java
new file mode 100644
index 0000000..ccec6b9
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/service/IOrderLogisticsService.java
@@ -0,0 +1,17 @@
+package com.hmall.trade.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hmall.trade.domain.po.OrderLogistics;
+
+/**
+ *
+ * 服务类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface IOrderLogisticsService extends IService {
+
+}
diff --git a/trade-service/src/main/java/com/hmall/trade/service/IOrderService.java b/trade-service/src/main/java/com/hmall/trade/service/IOrderService.java
new file mode 100644
index 0000000..832636b
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/service/IOrderService.java
@@ -0,0 +1,21 @@
+package com.hmall.trade.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hmall.trade.domain.dto.OrderFormDTO;
+import com.hmall.trade.domain.po.Order;
+
+/**
+ *
+ * 服务类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface IOrderService extends IService {
+
+ Long createOrder(OrderFormDTO orderFormDTO);
+
+ void markOrderPaySuccess(Long orderId);
+}
diff --git a/trade-service/src/main/java/com/hmall/trade/service/impl/OrderDetailServiceImpl.java b/trade-service/src/main/java/com/hmall/trade/service/impl/OrderDetailServiceImpl.java
new file mode 100644
index 0000000..a22f916
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/service/impl/OrderDetailServiceImpl.java
@@ -0,0 +1,21 @@
+package com.hmall.trade.service.impl;
+
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hmall.trade.domain.po.OrderDetail;
+import com.hmall.trade.mapper.OrderDetailMapper;
+import com.hmall.trade.service.IOrderDetailService;
+import org.springframework.stereotype.Service;
+
+/**
+ *
+ * 订单详情表 服务实现类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Service
+public class OrderDetailServiceImpl extends ServiceImpl implements IOrderDetailService {
+
+}
diff --git a/trade-service/src/main/java/com/hmall/trade/service/impl/OrderLogisticsServiceImpl.java b/trade-service/src/main/java/com/hmall/trade/service/impl/OrderLogisticsServiceImpl.java
new file mode 100644
index 0000000..9f31711
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/service/impl/OrderLogisticsServiceImpl.java
@@ -0,0 +1,21 @@
+package com.hmall.trade.service.impl;
+
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hmall.trade.domain.po.OrderLogistics;
+import com.hmall.trade.mapper.OrderLogisticsMapper;
+import com.hmall.trade.service.IOrderLogisticsService;
+import org.springframework.stereotype.Service;
+
+/**
+ *
+ * 服务实现类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Service
+public class OrderLogisticsServiceImpl extends ServiceImpl implements IOrderLogisticsService {
+
+}
diff --git a/trade-service/src/main/java/com/hmall/trade/service/impl/OrderServiceImpl.java b/trade-service/src/main/java/com/hmall/trade/service/impl/OrderServiceImpl.java
new file mode 100644
index 0000000..e85bbd9
--- /dev/null
+++ b/trade-service/src/main/java/com/hmall/trade/service/impl/OrderServiceImpl.java
@@ -0,0 +1,114 @@
+package com.hmall.trade.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hmall.api.cart.CartClient;
+import com.hmall.api.item.ItemClient;
+import com.hmall.api.item.dto.ItemDTO;
+import com.hmall.api.item.dto.OrderDetailDTO;
+import com.hmall.common.exception.BadRequestException;
+import com.hmall.common.utils.UserContext;
+
+import com.hmall.trade.domain.dto.OrderFormDTO;
+import com.hmall.trade.domain.po.Order;
+import com.hmall.trade.domain.po.OrderDetail;
+import com.hmall.trade.mapper.OrderMapper;
+import com.hmall.trade.service.IOrderDetailService;
+import com.hmall.trade.service.IOrderService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+/**
+ *
+ * 服务实现类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Service
+@RequiredArgsConstructor
+public class OrderServiceImpl extends ServiceImpl implements IOrderService {
+
+// private final IItemService itemService;
+ private final ItemClient itemClient;
+ private final IOrderDetailService detailService;
+ private final CartClient cartClient;
+
+ @Override
+ @Transactional
+ public Long createOrder(OrderFormDTO orderFormDTO) {
+ // 1.订单数据
+ Order order = new Order();
+ // 1.1.查询商品
+ List detailDTOS = orderFormDTO.getDetails();
+ // 1.2.获取商品id和数量的Map
+ Map itemNumMap = detailDTOS.stream()
+ .collect(Collectors.toMap(OrderDetailDTO::getItemId, OrderDetailDTO::getNum));
+ Set itemIds = itemNumMap.keySet();
+ // 1.3.查询商品
+ List items = itemClient.queryItemByIds(itemIds);
+ if (items == null || items.size() < itemIds.size()) {
+ throw new BadRequestException("商品不存在");
+ }
+ // 1.4.基于商品价格、购买数量计算商品总价:totalFee
+ int total = 0;
+ for (ItemDTO item : items) {
+ total += item.getPrice() * itemNumMap.get(item.getId());
+ }
+ order.setTotalFee(total);
+ // 1.5.其它属性
+ order.setPaymentType(orderFormDTO.getPaymentType());
+ order.setUserId(UserContext.getUser());
+ order.setStatus(1);
+ // 1.6.将Order写入数据库order表中
+ save(order);
+
+ // 2.保存订单详情
+ List details = buildDetails(order.getId(), items, itemNumMap);
+ detailService.saveBatch(details);
+
+ // 3.清理购物车商品
+ cartClient.deleteCartItemByIds(itemIds);
+
+ // 4.扣减库存
+ try {
+ itemClient.deductStock(detailDTOS);
+ } catch (Exception e) {
+ throw new RuntimeException("库存不足!");
+ }
+ return order.getId();
+ }
+
+ @Override
+ public void markOrderPaySuccess(Long orderId) {
+ Order order = new Order();
+ order.setId(orderId);
+ order.setStatus(2);
+ order.setPayTime(LocalDateTime.now());
+ updateById(order);
+ }
+
+ private List buildDetails(Long orderId, List items, Map numMap) {
+ List details = new ArrayList<>(items.size());
+ for (ItemDTO item : items) {
+ OrderDetail detail = new OrderDetail();
+ detail.setName(item.getName());
+ detail.setSpec(item.getSpec());
+ detail.setPrice(item.getPrice());
+ detail.setNum(numMap.get(item.getId()));
+ detail.setItemId(item.getId());
+ detail.setImage(item.getImage());
+ detail.setOrderId(orderId);
+ details.add(detail);
+ }
+ return details;
+ }
+}
diff --git a/trade-service/src/main/resources/application-dev.yaml b/trade-service/src/main/resources/application-dev.yaml
new file mode 100644
index 0000000..1fb8f00
--- /dev/null
+++ b/trade-service/src/main/resources/application-dev.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: 192.168.101.68
+ pw: mysql
\ No newline at end of file
diff --git a/trade-service/src/main/resources/application-local.yaml b/trade-service/src/main/resources/application-local.yaml
new file mode 100644
index 0000000..87ff8f4
--- /dev/null
+++ b/trade-service/src/main/resources/application-local.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: localhost # 修改为你自己的虚拟机IP地址
+ pw: mysql # 修改为docker中的MySQL密码
\ No newline at end of file
diff --git a/trade-service/src/main/resources/application.yaml b/trade-service/src/main/resources/application.yaml
new file mode 100644
index 0000000..ab2cfcd
--- /dev/null
+++ b/trade-service/src/main/resources/application.yaml
@@ -0,0 +1,42 @@
+server:
+ port: 8085
+spring:
+ application:
+ name: trade-service # 服务名称
+ profiles:
+ active: dev
+ datasource:
+ url: jdbc:mysql://${hm.db.host}:3306/hm-trade?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ username: root
+ password: ${hm.db.pw}
+ cloud:
+ nacos:
+ server-addr: 192.168.101.68 # nacos地址
+mybatis-plus:
+ configuration:
+ default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
+ global-config:
+ db-config:
+ update-strategy: not_null
+ id-type: auto
+logging:
+ level:
+ com.hmall: debug
+ pattern:
+ dateformat: HH:mm:ss:SSS
+ file:
+ path: "logs/${spring.application.name}"
+knife4j:
+ enable: true
+ openapi:
+ title: 交易服务接口文档
+ description: "信息"
+ url: https://www.itcast.cn
+ version: v1.0.0
+ group:
+ default:
+ group-name: default
+ api-rule: package
+ api-rule-resources:
+ - com.hmall.trade.controller
\ No newline at end of file
diff --git a/user-service/pom.xml b/user-service/pom.xml
new file mode 100644
index 0000000..5ffc3ae
--- /dev/null
+++ b/user-service/pom.xml
@@ -0,0 +1,62 @@
+
+
+
+ hmall-parent
+ com.hmall
+ 1.0.0
+
+ 4.0.0
+
+ user-service
+
+
+ 11
+ 11
+
+
+
+
+
+ com.hmall
+ hm-common
+ 1.0.0
+
+
+
+ com.hmall
+ hm-api
+ 1.0.0
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
\ No newline at end of file
diff --git a/user-service/src/main/java/com/hmall/user/UserServiceApplication.java b/user-service/src/main/java/com/hmall/user/UserServiceApplication.java
new file mode 100644
index 0000000..e9dcaf7
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/UserServiceApplication.java
@@ -0,0 +1,15 @@
+package com.hmall.user;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@MapperScan("com.hmall.user.mapper")
+@SpringBootApplication
+public class UserServiceApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(UserServiceApplication.class, args);
+ }
+
+}
\ No newline at end of file
diff --git a/user-service/src/main/java/com/hmall/user/config/JwtProperties.java b/user-service/src/main/java/com/hmall/user/config/JwtProperties.java
new file mode 100644
index 0000000..c903e0b
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/config/JwtProperties.java
@@ -0,0 +1,17 @@
+package com.hmall.user.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.core.io.Resource;
+import org.springframework.stereotype.Component;
+
+import java.time.Duration;
+
+@Data
+@ConfigurationProperties(prefix = "hm.jwt")
+public class JwtProperties {
+ private Resource location;
+ private String password;
+ private String alias;
+ private Duration tokenTTL = Duration.ofMinutes(10);
+}
diff --git a/user-service/src/main/java/com/hmall/user/config/SecurityConfig.java b/user-service/src/main/java/com/hmall/user/config/SecurityConfig.java
new file mode 100644
index 0000000..28ddb3b
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/config/SecurityConfig.java
@@ -0,0 +1,33 @@
+package com.hmall.user.config;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
+
+import java.security.KeyPair;
+
+@Configuration
+@EnableConfigurationProperties(JwtProperties.class)
+public class SecurityConfig {
+
+ @Bean
+ public PasswordEncoder passwordEncoder(){
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean
+ public KeyPair keyPair(JwtProperties properties){
+ // 获取秘钥工厂
+ KeyStoreKeyFactory keyStoreKeyFactory =
+ new KeyStoreKeyFactory(
+ properties.getLocation(),
+ properties.getPassword().toCharArray());
+ //读取钥匙对
+ return keyStoreKeyFactory.getKeyPair(
+ properties.getAlias(),
+ properties.getPassword().toCharArray());
+ }
+}
\ No newline at end of file
diff --git a/user-service/src/main/java/com/hmall/user/controller/AddressController.java b/user-service/src/main/java/com/hmall/user/controller/AddressController.java
new file mode 100644
index 0000000..d385827
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/controller/AddressController.java
@@ -0,0 +1,62 @@
+package com.hmall.user.controller;
+
+
+import com.hmall.common.exception.BadRequestException;
+import com.hmall.common.utils.BeanUtils;
+import com.hmall.common.utils.CollUtils;
+import com.hmall.common.utils.UserContext;
+
+import com.hmall.user.domain.dto.AddressDTO;
+import com.hmall.user.domain.po.Address;
+import com.hmall.user.service.IAddressService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+/**
+ *
+ * 前端控制器
+ *
+ *
+ * @author 虎哥
+ */
+@RestController
+@RequestMapping("/addresses")
+@RequiredArgsConstructor
+@Api(tags = "收货地址管理接口")
+public class AddressController {
+
+ private final IAddressService addressService;
+
+ @ApiOperation("根据id查询地址")
+ @GetMapping("{addressId}")
+ public AddressDTO findAddressById(@ApiParam("地址id") @PathVariable("addressId") Long id) {
+ // 1.根据id查询
+ Address address = addressService.getById(id);
+ // 2.判断当前用户
+ Long userId = UserContext.getUser();
+ if(!address.getUserId().equals(userId)){
+ throw new BadRequestException("地址不属于当前登录用户");
+ }
+ return BeanUtils.copyBean(address, AddressDTO.class);
+ }
+ @ApiOperation("查询当前用户地址列表")
+ @GetMapping
+ public List findMyAddresses() {
+ // 1.查询列表
+ List list = addressService.query().eq("user_id", UserContext.getUser()).list();
+ // 2.判空
+ if (CollUtils.isEmpty(list)) {
+ return CollUtils.emptyList();
+ }
+ // 3.转vo
+ return BeanUtils.copyList(list, AddressDTO.class);
+ }
+}
diff --git a/user-service/src/main/java/com/hmall/user/controller/UserController.java b/user-service/src/main/java/com/hmall/user/controller/UserController.java
new file mode 100644
index 0000000..9b49be3
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/controller/UserController.java
@@ -0,0 +1,39 @@
+package com.hmall.user.controller;
+
+
+import com.hmall.user.domain.dto.LoginFormDTO;
+import com.hmall.user.domain.vo.UserLoginVO;
+import com.hmall.user.service.IUserService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+@Api(tags = "用户相关接口")
+@RestController
+@RequestMapping("/users")
+@RequiredArgsConstructor
+public class UserController {
+
+ private final IUserService userService;
+
+ @ApiOperation("用户登录接口")
+ @PostMapping("login")
+ public UserLoginVO login(@RequestBody @Validated LoginFormDTO loginFormDTO){
+ return userService.login(loginFormDTO);
+ }
+
+ @ApiOperation("扣减余额")
+ @ApiImplicitParams({
+ @ApiImplicitParam(name = "pw", value = "支付密码"),
+ @ApiImplicitParam(name = "amount", value = "支付金额")
+ })
+ @PutMapping("/money/deduct")
+ public void deductMoney(@RequestParam("pw") String pw,@RequestParam("amount") Integer amount){
+ userService.deductMoney(pw, amount);
+ }
+}
+
diff --git a/user-service/src/main/java/com/hmall/user/domain/dto/AddressDTO.java b/user-service/src/main/java/com/hmall/user/domain/dto/AddressDTO.java
new file mode 100644
index 0000000..d21b7d0
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/domain/dto/AddressDTO.java
@@ -0,0 +1,28 @@
+package com.hmall.user.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+@Data
+@ApiModel(description = "收货地址实体")
+public class AddressDTO {
+ @ApiModelProperty("id")
+ private Long id;
+ @ApiModelProperty("省")
+ private String province;
+ @ApiModelProperty("市")
+ private String city;
+ @ApiModelProperty("县/区")
+ private String town;
+ @ApiModelProperty("手机")
+ private String mobile;
+ @ApiModelProperty("详细地址")
+ private String street;
+ @ApiModelProperty("联系人")
+ private String contact;
+ @ApiModelProperty("是否是默认 1默认 0否")
+ private Integer isDefault;
+ @ApiModelProperty("备注")
+ private String notes;
+}
diff --git a/user-service/src/main/java/com/hmall/user/domain/dto/LoginFormDTO.java b/user-service/src/main/java/com/hmall/user/domain/dto/LoginFormDTO.java
new file mode 100644
index 0000000..a1b6122
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/domain/dto/LoginFormDTO.java
@@ -0,0 +1,20 @@
+package com.hmall.user.domain.dto;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+
+@Data
+@ApiModel(description = "登录表单实体")
+public class LoginFormDTO {
+ @ApiModelProperty(value = "用户名", required = true)
+ @NotNull(message = "用户名不能为空")
+ private String username;
+ @NotNull(message = "密码不能为空")
+ @ApiModelProperty(value = "用户名", required = true)
+ private String password;
+ @ApiModelProperty(value = "是否记住我", required = false)
+ private Boolean rememberMe = false;
+}
diff --git a/user-service/src/main/java/com/hmall/user/domain/po/Address.java b/user-service/src/main/java/com/hmall/user/domain/po/Address.java
new file mode 100644
index 0000000..2e1728c
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/domain/po/Address.java
@@ -0,0 +1,77 @@
+package com.hmall.user.domain.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+
+/**
+ *
+ *
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("address")
+public class Address implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 用户ID
+ */
+ private Long userId;
+
+ /**
+ * 省
+ */
+ private String province;
+
+ /**
+ * 市
+ */
+ private String city;
+
+ /**
+ * 县/区
+ */
+ private String town;
+
+ /**
+ * 手机
+ */
+ private String mobile;
+
+ /**
+ * 详细地址
+ */
+ private String street;
+
+ /**
+ * 联系人
+ */
+ private String contact;
+
+ /**
+ * 是否是默认 1默认 0否
+ */
+ private Integer isDefault;
+
+ /**
+ * 备注
+ */
+ private String notes;
+
+
+}
diff --git a/user-service/src/main/java/com/hmall/user/domain/po/User.java b/user-service/src/main/java/com/hmall/user/domain/po/User.java
new file mode 100644
index 0000000..495c97f
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/domain/po/User.java
@@ -0,0 +1,66 @@
+package com.hmall.user.domain.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.hmall.user.enums.UserStatus;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+import java.io.Serializable;
+import java.time.LocalDateTime;
+
+/**
+ *
+ * 用户表
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Data
+@EqualsAndHashCode(callSuper = false)
+@Accessors(chain = true)
+@TableName("user")
+public class User implements Serializable {
+
+ private static final long serialVersionUID = 1L;
+
+ @TableId(value = "id", type = IdType.AUTO)
+ private Long id;
+
+ /**
+ * 用户名
+ */
+ private String username;
+
+ /**
+ * 密码,加密存储
+ */
+ private String password;
+
+ /**
+ * 注册手机号
+ */
+ private String phone;
+
+ /**
+ * 创建时间
+ */
+ private LocalDateTime createTime;
+
+ private LocalDateTime updateTime;
+
+ /**
+ * 使用状态(1正常 2冻结)
+ */
+ private UserStatus status;
+
+ /**
+ * 账户余额
+ */
+ private Integer balance;
+
+
+}
diff --git a/user-service/src/main/java/com/hmall/user/domain/vo/UserLoginVO.java b/user-service/src/main/java/com/hmall/user/domain/vo/UserLoginVO.java
new file mode 100644
index 0000000..d1dbcfa
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/domain/vo/UserLoginVO.java
@@ -0,0 +1,11 @@
+package com.hmall.user.domain.vo;
+
+import lombok.Data;
+
+@Data
+public class UserLoginVO {
+ private String token;
+ private Long userId;
+ private String username;
+ private Integer balance;
+}
diff --git a/user-service/src/main/java/com/hmall/user/enums/UserStatus.java b/user-service/src/main/java/com/hmall/user/enums/UserStatus.java
new file mode 100644
index 0000000..ad7f526
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/enums/UserStatus.java
@@ -0,0 +1,30 @@
+package com.hmall.user.enums;
+
+import com.baomidou.mybatisplus.annotation.EnumValue;
+import com.hmall.common.exception.BadRequestException;
+import lombok.Getter;
+
+@Getter
+public enum UserStatus {
+ FROZEN(0, "禁止使用"),
+ NORMAL(1, "已激活"),
+ ;
+ @EnumValue
+ int value;
+ String desc;
+
+ UserStatus(Integer value, String desc) {
+ this.value = value;
+ this.desc = desc;
+ }
+
+ public static UserStatus of(int value) {
+ if (value == 0) {
+ return FROZEN;
+ }
+ if (value == 1) {
+ return NORMAL;
+ }
+ throw new BadRequestException("账户状态错误");
+ }
+}
\ No newline at end of file
diff --git a/user-service/src/main/java/com/hmall/user/mapper/AddressMapper.java b/user-service/src/main/java/com/hmall/user/mapper/AddressMapper.java
new file mode 100644
index 0000000..eee6476
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/mapper/AddressMapper.java
@@ -0,0 +1,17 @@
+package com.hmall.user.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hmall.user.domain.po.Address;
+
+/**
+ *
+ * Mapper 接口
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface AddressMapper extends BaseMapper {
+
+}
diff --git a/user-service/src/main/java/com/hmall/user/mapper/UserMapper.java b/user-service/src/main/java/com/hmall/user/mapper/UserMapper.java
new file mode 100644
index 0000000..cfd0bf1
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/mapper/UserMapper.java
@@ -0,0 +1,20 @@
+package com.hmall.user.mapper;
+
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hmall.user.domain.po.User;
+import org.apache.ibatis.annotations.Param;
+import org.apache.ibatis.annotations.Update;
+
+/**
+ *
+ * 用户表 Mapper 接口
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface UserMapper extends BaseMapper {
+ @Update("update user set balance = balance - #{totalFee} where id = #{userId} and balance >= #{totalFee}")
+ int updateMoney(@Param("userId") Long userId, @Param("totalFee") Integer totalFee);
+}
diff --git a/user-service/src/main/java/com/hmall/user/service/IAddressService.java b/user-service/src/main/java/com/hmall/user/service/IAddressService.java
new file mode 100644
index 0000000..a62ca76
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/service/IAddressService.java
@@ -0,0 +1,17 @@
+package com.hmall.user.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hmall.user.domain.po.Address;
+
+
+/**
+ *
+ * 服务类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface IAddressService extends IService {
+
+}
diff --git a/user-service/src/main/java/com/hmall/user/service/IUserService.java b/user-service/src/main/java/com/hmall/user/service/IUserService.java
new file mode 100644
index 0000000..0721c08
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/service/IUserService.java
@@ -0,0 +1,22 @@
+package com.hmall.user.service;
+
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hmall.user.domain.dto.LoginFormDTO;
+import com.hmall.user.domain.po.User;
+import com.hmall.user.domain.vo.UserLoginVO;
+
+/**
+ *
+ * 用户表 服务类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface IUserService extends IService {
+
+ UserLoginVO login(LoginFormDTO loginFormDTO);
+
+ void deductMoney(String pw, Integer totalFee);
+}
diff --git a/user-service/src/main/java/com/hmall/user/service/impl/AddressServiceImpl.java b/user-service/src/main/java/com/hmall/user/service/impl/AddressServiceImpl.java
new file mode 100644
index 0000000..76485c7
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/service/impl/AddressServiceImpl.java
@@ -0,0 +1,21 @@
+package com.hmall.user.service.impl;
+
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hmall.user.domain.po.Address;
+import com.hmall.user.mapper.AddressMapper;
+import com.hmall.user.service.IAddressService;
+import org.springframework.stereotype.Service;
+
+/**
+ *
+ * 服务实现类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Service
+public class AddressServiceImpl extends ServiceImpl implements IAddressService {
+
+}
diff --git a/user-service/src/main/java/com/hmall/user/service/impl/UserServiceImpl.java b/user-service/src/main/java/com/hmall/user/service/impl/UserServiceImpl.java
new file mode 100644
index 0000000..c8f6bbc
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/service/impl/UserServiceImpl.java
@@ -0,0 +1,90 @@
+package com.hmall.user.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hmall.common.exception.BadRequestException;
+import com.hmall.common.exception.BizIllegalException;
+import com.hmall.common.exception.ForbiddenException;
+import com.hmall.common.utils.UserContext;
+import com.hmall.user.config.JwtProperties;
+import com.hmall.user.domain.dto.LoginFormDTO;
+import com.hmall.user.domain.po.User;
+import com.hmall.user.domain.vo.UserLoginVO;
+import com.hmall.user.enums.UserStatus;
+import com.hmall.user.mapper.UserMapper;
+import com.hmall.user.service.IUserService;
+import com.hmall.user.utils.JwtTool;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.stereotype.Service;
+import org.springframework.util.Assert;
+
+/**
+ *
+ * 用户表 服务实现类
+ *
+ *
+ * @author 虎哥
+ */
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class UserServiceImpl extends ServiceImpl implements IUserService {
+
+ private final PasswordEncoder passwordEncoder;
+
+ private final JwtTool jwtTool;
+
+ private final JwtProperties jwtProperties;
+
+ @Override
+ public UserLoginVO login(LoginFormDTO loginDTO) {
+ // 1.数据校验
+ String username = loginDTO.getUsername();
+ String password = loginDTO.getPassword();
+ // 2.根据用户名或手机号查询
+ User user = lambdaQuery().eq(User::getUsername, username).one();
+ Assert.notNull(user, "用户名错误");
+ // 3.校验是否禁用
+ if (user.getStatus() == UserStatus.FROZEN) {
+ throw new ForbiddenException("用户被冻结");
+ }
+ // 4.校验密码
+ if (!passwordEncoder.matches(password, user.getPassword())) {
+ throw new BadRequestException("用户名或密码错误");
+ }
+ // 5.生成TOKEN
+ String token = jwtTool.createToken(user.getId(), jwtProperties.getTokenTTL());
+ // 6.封装VO返回
+ UserLoginVO vo = new UserLoginVO();
+ vo.setUserId(user.getId());
+ vo.setUsername(user.getUsername());
+ vo.setBalance(user.getBalance());
+ vo.setToken(token);
+ return vo;
+ }
+ public static void main(String[] args) {
+ BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
+ String rawPassword = "123456";
+ String encodedPassword = encoder.encode(rawPassword);
+ System.out.println("Encoded password: " + encodedPassword);
+ }
+ @Override
+ public void deductMoney(String pw, Integer totalFee) {
+ log.info("开始扣款");
+ // 1.校验密码
+ User user = getById(UserContext.getUser());
+ if(user == null || !passwordEncoder.matches(pw, user.getPassword())){
+ // 密码错误
+ throw new BizIllegalException("用户密码错误");
+ }
+
+ // 2.尝试扣款
+ int i = baseMapper.updateMoney(UserContext.getUser(), totalFee);
+ if (i <= 0) {
+ throw new RuntimeException("扣款失败,可能是余额不足!");
+ }
+ log.info("扣款成功");
+ }
+}
diff --git a/user-service/src/main/java/com/hmall/user/utils/JwtTool.java b/user-service/src/main/java/com/hmall/user/utils/JwtTool.java
new file mode 100644
index 0000000..7261199
--- /dev/null
+++ b/user-service/src/main/java/com/hmall/user/utils/JwtTool.java
@@ -0,0 +1,82 @@
+package com.hmall.user.utils;
+
+import cn.hutool.core.exceptions.ValidateException;
+import cn.hutool.jwt.JWT;
+import cn.hutool.jwt.JWTValidator;
+import cn.hutool.jwt.signers.JWTSigner;
+import cn.hutool.jwt.signers.JWTSignerUtil;
+import com.hmall.common.exception.UnauthorizedException;
+import org.springframework.stereotype.Component;
+
+import java.security.KeyPair;
+import java.time.Duration;
+import java.util.Date;
+
+@Component
+public class JwtTool {
+ private final JWTSigner jwtSigner;
+
+ public JwtTool(KeyPair keyPair) {
+ this.jwtSigner = JWTSignerUtil.createSigner("rs256", keyPair);
+ }
+
+ /**
+ * 创建 access-token
+ *
+ * @param userId 用户信息
+ * @return access-token
+ */
+ public String createToken(Long userId, Duration ttl) {
+ // 1.生成jws
+ return JWT.create()
+ .setPayload("user", userId)
+ .setExpiresAt(new Date(System.currentTimeMillis() + ttl.toMillis()))
+ .setSigner(jwtSigner)
+ .sign();
+ }
+
+ /**
+ * 解析token
+ *
+ * @param token token
+ * @return 解析刷新token得到的用户信息
+ */
+ public Long parseToken(String token) {
+ // 1.校验token是否为空
+ if (token == null) {
+ throw new UnauthorizedException("未登录");
+ }
+ // 2.校验并解析jwt
+ JWT jwt;
+ try {
+ jwt = JWT.of(token).setSigner(jwtSigner);
+ } catch (Exception e) {
+ throw new UnauthorizedException("无效的token", e);
+ }
+ // 2.校验jwt是否有效
+ if (!jwt.verify()) {
+ // 验证失败
+ throw new UnauthorizedException("无效的token");
+ }
+ // 3.校验是否过期
+ try {
+ JWTValidator.of(jwt).validateDate();
+ } catch (ValidateException e) {
+ throw new UnauthorizedException("token已经过期");
+ }
+ // 4.数据格式校验
+ Object userPayload = jwt.getPayload("user");
+ if (userPayload == null) {
+ // 数据为空
+ throw new UnauthorizedException("无效的token");
+ }
+
+ // 5.数据解析
+ try {
+ return Long.valueOf(userPayload.toString());
+ } catch (RuntimeException e) {
+ // 数据格式有误
+ throw new UnauthorizedException("无效的token");
+ }
+ }
+}
\ No newline at end of file
diff --git a/user-service/src/main/resources/application-dev.yaml b/user-service/src/main/resources/application-dev.yaml
new file mode 100644
index 0000000..1fb8f00
--- /dev/null
+++ b/user-service/src/main/resources/application-dev.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: 192.168.101.68
+ pw: mysql
\ No newline at end of file
diff --git a/user-service/src/main/resources/application-local.yaml b/user-service/src/main/resources/application-local.yaml
new file mode 100644
index 0000000..87ff8f4
--- /dev/null
+++ b/user-service/src/main/resources/application-local.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: localhost # 修改为你自己的虚拟机IP地址
+ pw: mysql # 修改为docker中的MySQL密码
\ No newline at end of file
diff --git a/user-service/src/main/resources/application.yaml b/user-service/src/main/resources/application.yaml
new file mode 100644
index 0000000..c0d5e29
--- /dev/null
+++ b/user-service/src/main/resources/application.yaml
@@ -0,0 +1,48 @@
+server:
+ port: 8084
+spring:
+ application:
+ name: user-service # 服务名称
+ profiles:
+ active: dev
+ datasource:
+ url: jdbc:mysql://${hm.db.host}:3306/hm-user?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ username: root
+ password: ${hm.db.pw}
+ cloud:
+ nacos:
+ server-addr: 192.168.101.68 # nacos地址
+mybatis-plus:
+ configuration:
+ default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
+ global-config:
+ db-config:
+ update-strategy: not_null
+ id-type: auto
+logging:
+ level:
+ com.hmall: debug
+ pattern:
+ dateformat: HH:mm:ss:SSS
+ file:
+ path: "logs/${spring.application.name}"
+knife4j:
+ enable: true
+ openapi:
+ title: 用户服务接口文档
+ description: "信息"
+ url: https://www.itcast.cn
+ version: v1.0.0
+ group:
+ default:
+ group-name: default
+ api-rule: package
+ api-rule-resources:
+ - com.hmall.user.controller
+hm:
+ jwt:
+ location: classpath:hmall.jks
+ alias: hmall
+ password: hmall123
+ tokenTTL: 30m
\ No newline at end of file
diff --git a/user-service/src/main/resources/hmall.jks b/user-service/src/main/resources/hmall.jks
new file mode 100644
index 0000000000000000000000000000000000000000..a34bd33d09d4c33b13769213e6c07c2267cce162
GIT binary patch
literal 2711
zcma)+cQhM}8pe}ILhLW9P(T2^2!cTX2w^D9HO>_1K?erP
zf!Uc1xM1i=8tQB9Awe=@l?En&N0I{yr?ks8q1gUD&77!=1Il1w;7shw{+Js>*%oqg
zSaod~_JhF?7#x(IlDw`8d&6Rn{fmebw+~9^WwB($~{W{}M
zpGa6hl2804zbZ5mHOUf*nNvZHABX#t3QXF9<9Y*-0gO2do~=SNqIbGdb$}Y)1C8
zXyuL#^haq!isIQ=PhfX)K0*-oToJxIUrTVp-UBAmhGS%wbeA9{FxRv$+Zlf#2D|
zMJPQu*9*5PmUNM8e3M^%bkgcZQgd9U7qYXEdv5k)!7bFtdeIZcmx8??ATNnJa!L8_
zq>D3x$L$92p-sjtn0T9$LkF<>#xIN+!}2799QHL><8SfuG*Vo*Uw0dM+3D^J7RLGV
zEVdc_`3EY^N@%yzh@V8a?vqzkNa^f(>Fhkp2F#sE#o4g+33mG7RHuthC*=ki2k@VP
zI9Y{g%iz(_dUpMWE(~zYwi5-SopTQ(Ug-h8Geoe5vJhjvCQkdnw
zhQY@{A*x+HudUCz?t2zh-F9Nt<_>~A={_Evb9{1qtE$8XZX$z%b&tA-cWUfLL^$F<
zefSyUbsPieS>?MB9k2E?_h9O53vuM+98wx#CMb4&_SN0k>&3Lt{Lex1>n3vpS=hBt
z;iee+B4-058Pj9_=_&CHIYQDE)hYAVr~(YPeL#D*-u_K!`&rhW^-fqAA~B#et&y%3
zh+U42Ee#L}<_2DgXbsoYfSn<~&)
zPAuX;y7D?am}5j@gLPS|lf7r2oX&Jm`F8_#NGUx7a8Z6_;Ih-@@Xf8)8$_Eb-a{ug
zlbMlzTku5T;)B%kvQjA)ZX7T2=J+(PT^&!{>oQ-*iKjhp_mL-Cci_Z1Yt}IC;&Je_
zQGe!`FXu3mC>QG8<7v4rf<=))YP7EDZVauv!1B1U0;ilS8DDMQ92(7W2VVFi|-SfYKtfYRN9Wp1Gfyy=9BhjP7J
zzHpH6rn84Xd%1e)&Tu9}h>-AwXAVsAvgz;JpzhBV6W0wybUaF|-M7lF27g}Nz{RA`
ztLPyFe%B?8TZLJK5#R;z1z-R;01hGkkHn+G4d%R#_4N=_(a=&;)lySYQB_mdKoG$A
z-%s>VG68(|7xDuE0l${#KML@Fm2{gBo?s0-dQ(_!0nRM#r?rzVyk!q_YTy!Iq
z{qe%~o7z^B=4b0t!lLM1lQ2s9ovH!hdW`xdR0AH3!M^$PX5{1kh?cCrXVaQf8SpkV(G
z;$ih>F6ZPoId*Fmz4pd(rMG`9uUAV!Hdzbx@?)5FpUAJEG<+#kt-QV)M{~-tL3(OK
z0k@oWin}_43UlNem~W)VERM?YixKY`*FVa@eCx2|@V3pXS*QH1U|r$0l~ms59{OoG
z_hzw5*z40b8fI;mp40d?IIhcW8iV`6Bs1dhbW}ZnAm(h^3#7
z{sbPTc-9oy=)oB&kYL}$eJt|$uvgGAmn6;fv0mMu_o_LSu~c#zr0eexhge@A(MZnK
zYjzqltUoI`T5i65c`#ohqZaX`v<=%XnfyIsl@ZrLQUBt|kMe=)ugtQ$C*smS9>#`f
z^ABuErq!HX>EdW!&%%x|ZMJ)@jGZc$XiEd9bRAz&A>@$L
zxlxwP!)E&Vf?@t{vvo#Yc!b2kxb7O)SF4M+(=$+N17nGYwW{;rxWE~fJE%W7)cEIV
zGG)dUD;!+3mKSXCM@V7%wxYB}PU%@s7W8{KrzR1hhY&}=pb&XhI*=d)0Oo$!({e_C
uQpH62W+}}XVj|zYG4BNCPS~2Erd)e6g7$a0VPy&!oz{sz1
+
+
+
+
--
Gitee
From a0fea1d3883d22dbf67190ce4f5fbb313380e8d4 Mon Sep 17 00:00:00 2001
From: syd <1579670286@qq.com>
Date: Wed, 18 Jun 2025 21:04:27 +0800
Subject: [PATCH 6/8] =?UTF-8?q?=E5=88=86=E5=B8=83=E5=BC=8F=E4=BA=8B?=
=?UTF-8?q?=E5=8A=A1=EF=BC=8CCAP=EF=BC=8C=E9=9B=86=E6=88=90Seata=20?=
=?UTF-8?q?=E5=AF=B9=E4=B8=8B=E5=8D=95=E3=80=81=E6=94=AF=E4=BB=98=E6=96=B9?=
=?UTF-8?q?=E6=B3=95=E5=88=86=E5=B8=83=E5=BC=8F=E4=BA=8B=E5=8A=A1=E6=8E=A7?=
=?UTF-8?q?=E5=88=B6?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
item-service/pom.xml | 6 ++++++
.../com/hmall/item/controller/ItemController.java | 11 ++++++++---
item-service/src/main/resources/application.yaml | 12 ++++++++++++
pay-service/pom.xml | 6 ++++++
.../pay/service/impl/PayOrderServiceImpl.java | 8 ++++++--
pay-service/src/main/resources/application.yaml | 14 +++++++++++++-
trade-service/pom.xml | 6 ++++++
.../hmall/trade/service/impl/OrderServiceImpl.java | 7 ++++++-
trade-service/src/main/resources/application.yaml | 14 +++++++++++++-
user-service/pom.xml | 6 ++++++
user-service/src/main/resources/application.yaml | 14 +++++++++++++-
11 files changed, 95 insertions(+), 9 deletions(-)
diff --git a/item-service/pom.xml b/item-service/pom.xml
index 3cdb3d9..5f240b1 100644
--- a/item-service/pom.xml
+++ b/item-service/pom.xml
@@ -16,6 +16,12 @@
11
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-seata
+
com.hmall
diff --git a/item-service/src/main/java/com/hmall/item/controller/ItemController.java b/item-service/src/main/java/com/hmall/item/controller/ItemController.java
index 1235745..0d48eff 100644
--- a/item-service/src/main/java/com/hmall/item/controller/ItemController.java
+++ b/item-service/src/main/java/com/hmall/item/controller/ItemController.java
@@ -36,7 +36,12 @@ public class ItemController {
@ApiOperation("根据id批量查询商品")
@GetMapping
- public List queryItemByIds(@RequestParam("ids") Collection ids){
+ public List queryItemByIds(@RequestParam("ids") Collection ids) {
+ /*try {
+ Thread.sleep(1000);
+ } catch (InterruptedException e) {
+ e.printStackTrace();
+ }*/
return itemService.queryItemByIds(ids);
}
@@ -55,7 +60,7 @@ public class ItemController {
@ApiOperation("更新商品状态")
@PutMapping("/status/{id}/{status}")
- public void updateItemStatus(@PathVariable("id") Long id, @PathVariable("status") Integer status){
+ public void updateItemStatus(@PathVariable("id") Long id, @PathVariable("status") Integer status) {
Item item = new Item();
item.setId(id);
item.setStatus(status);
@@ -79,7 +84,7 @@ public class ItemController {
@ApiOperation("批量扣减库存")
@PutMapping("/stock/deduct")
- public void deductStock(@RequestBody List items){
+ public void deductStock(@RequestBody List items) {
itemService.deductStock(items);
}
}
diff --git a/item-service/src/main/resources/application.yaml b/item-service/src/main/resources/application.yaml
index d2ed342..61d3555 100644
--- a/item-service/src/main/resources/application.yaml
+++ b/item-service/src/main/resources/application.yaml
@@ -53,4 +53,16 @@ hm:
- /users/login
- /items/**
- /hi
+seata:
+ registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
+ type: nacos # 注册中心类型 nacos
+ nacos:
+ server-addr: 192.168.101.68:8848 # nacos地址
+ namespace: "" # namespace,默认为空
+ group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP
+ application: seata-server # seata服务名称
+ tx-service-group: hmall # 事务组名称
+ service:
+ vgroup-mapping: # 事务组与tc集群的映射关系
+ hmall: "default"
# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123
\ No newline at end of file
diff --git a/pay-service/pom.xml b/pay-service/pom.xml
index e221bcb..c0393f0 100644
--- a/pay-service/pom.xml
+++ b/pay-service/pom.xml
@@ -17,6 +17,12 @@
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-seata
+
com.hmall
diff --git a/pay-service/src/main/java/com/hmall/pay/service/impl/PayOrderServiceImpl.java b/pay-service/src/main/java/com/hmall/pay/service/impl/PayOrderServiceImpl.java
index a27c8ee..77b8cd7 100644
--- a/pay-service/src/main/java/com/hmall/pay/service/impl/PayOrderServiceImpl.java
+++ b/pay-service/src/main/java/com/hmall/pay/service/impl/PayOrderServiceImpl.java
@@ -15,6 +15,7 @@ import com.hmall.pay.domain.po.PayOrder;
import com.hmall.pay.enums.PayStatus;
import com.hmall.pay.mapper.PayOrderMapper;
import com.hmall.pay.service.IPayOrderService;
+import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -46,7 +47,7 @@ public class PayOrderServiceImpl extends ServiceImpl i
}
@Override
- @Transactional
+ @GlobalTransactional
public void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO) {
// 1.查询支付单
PayOrder po = getById(payOrderFormDTO.getId());
@@ -55,7 +56,7 @@ public class PayOrderServiceImpl extends ServiceImpl i
// 订单不是未支付,状态异常
throw new BizIllegalException("交易已支付或关闭!");
}
- // 3.尝试扣减余额
+ // 3.尝试扣减余额
userClient.deductMoney(payOrderFormDTO.getPw(), po.getAmount());
// 4.修改支付单状态
boolean success = markPayOrderSuccess(payOrderFormDTO.getId(), LocalDateTime.now());
@@ -64,6 +65,9 @@ public class PayOrderServiceImpl extends ServiceImpl i
}
// 5.修改订单状态
tradeClient.markOrderPaySuccess(po.getBizOrderNo());
+ if(true){
+ throw new RuntimeException("测试抛出异常");
+ }
}
public boolean markPayOrderSuccess(Long id, LocalDateTime successTime) {
diff --git a/pay-service/src/main/resources/application.yaml b/pay-service/src/main/resources/application.yaml
index f9865ce..3451df5 100644
--- a/pay-service/src/main/resources/application.yaml
+++ b/pay-service/src/main/resources/application.yaml
@@ -39,4 +39,16 @@ knife4j:
group-name: default
api-rule: package
api-rule-resources:
- - com.hmall.pay.controller
\ No newline at end of file
+ - com.hmall.pay.controller
+seata:
+ registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
+ type: nacos # 注册中心类型 nacos
+ nacos:
+ server-addr: 192.168.101.68:8848 # nacos地址
+ namespace: "" # namespace,默认为空
+ group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP
+ application: seata-server # seata服务名称
+ tx-service-group: hmall # 事务组名称
+ service:
+ vgroup-mapping: # 事务组与tc集群的映射关系
+ hmall: "default"
\ No newline at end of file
diff --git a/trade-service/pom.xml b/trade-service/pom.xml
index a10e1d3..aae76e3 100644
--- a/trade-service/pom.xml
+++ b/trade-service/pom.xml
@@ -17,6 +17,12 @@
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-seata
+
com.hmall
diff --git a/trade-service/src/main/java/com/hmall/trade/service/impl/OrderServiceImpl.java b/trade-service/src/main/java/com/hmall/trade/service/impl/OrderServiceImpl.java
index e85bbd9..0fa0687 100644
--- a/trade-service/src/main/java/com/hmall/trade/service/impl/OrderServiceImpl.java
+++ b/trade-service/src/main/java/com/hmall/trade/service/impl/OrderServiceImpl.java
@@ -14,6 +14,7 @@ import com.hmall.trade.domain.po.OrderDetail;
import com.hmall.trade.mapper.OrderMapper;
import com.hmall.trade.service.IOrderDetailService;
import com.hmall.trade.service.IOrderService;
+import io.seata.spring.annotation.GlobalTransactional;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@@ -43,7 +44,8 @@ public class OrderServiceImpl extends ServiceImpl implements
private final CartClient cartClient;
@Override
- @Transactional
+// @Transactional
+ @GlobalTransactional
public Long createOrder(OrderFormDTO orderFormDTO) {
// 1.订单数据
Order order = new Order();
@@ -84,6 +86,9 @@ public class OrderServiceImpl extends ServiceImpl implements
} catch (Exception e) {
throw new RuntimeException("库存不足!");
}
+ /*if(true){
+ throw new RuntimeException("测试抛出异常");
+ }*/
return order.getId();
}
diff --git a/trade-service/src/main/resources/application.yaml b/trade-service/src/main/resources/application.yaml
index ab2cfcd..910a0b7 100644
--- a/trade-service/src/main/resources/application.yaml
+++ b/trade-service/src/main/resources/application.yaml
@@ -39,4 +39,16 @@ knife4j:
group-name: default
api-rule: package
api-rule-resources:
- - com.hmall.trade.controller
\ No newline at end of file
+ - com.hmall.trade.controller
+seata:
+ registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
+ type: nacos # 注册中心类型 nacos
+ nacos:
+ server-addr: 192.168.101.68:8848 # nacos地址
+ namespace: "" # namespace,默认为空
+ group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP
+ application: seata-server # seata服务名称
+ tx-service-group: hmall # 事务组名称
+ service:
+ vgroup-mapping: # 事务组与tc集群的映射关系
+ hmall: "default"
\ No newline at end of file
diff --git a/user-service/pom.xml b/user-service/pom.xml
index 5ffc3ae..7439b0a 100644
--- a/user-service/pom.xml
+++ b/user-service/pom.xml
@@ -17,6 +17,12 @@
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-seata
+
com.hmall
diff --git a/user-service/src/main/resources/application.yaml b/user-service/src/main/resources/application.yaml
index c0d5e29..5aa6d50 100644
--- a/user-service/src/main/resources/application.yaml
+++ b/user-service/src/main/resources/application.yaml
@@ -45,4 +45,16 @@ hm:
location: classpath:hmall.jks
alias: hmall
password: hmall123
- tokenTTL: 30m
\ No newline at end of file
+ tokenTTL: 30m
+seata:
+ registry: # TC服务注册中心的配置,微服务根据这些信息去注册中心获取tc服务地址
+ type: nacos # 注册中心类型 nacos
+ nacos:
+ server-addr: 192.168.101.68:8848 # nacos地址
+ namespace: "" # namespace,默认为空
+ group: DEFAULT_GROUP # 分组,默认是DEFAULT_GROUP
+ application: seata-server # seata服务名称
+ tx-service-group: hmall # 事务组名称
+ service:
+ vgroup-mapping: # 事务组与tc集群的映射关系
+ hmall: "default"
\ No newline at end of file
--
Gitee
From 03c85ac9c008c8901639b08ba6c3bd42245c0c27 Mon Sep 17 00:00:00 2001
From: syd <1579670286@qq.com>
Date: Sat, 21 Jun 2025 09:03:45 +0800
Subject: [PATCH 7/8] =?UTF-8?q?=E5=88=9B=E5=BB=BA=E7=BD=91=E5=85=B3?=
=?UTF-8?q?=E6=A8=A1=E5=9D=97=E3=80=81=E8=87=AA=E5=AE=9A=E4=B9=89GlobalFil?=
=?UTF-8?q?ter=E3=80=81=20=E7=BD=91=E5=85=B3=E4=BC=A0=E9=80=92=E7=94=A8?=
=?UTF-8?q?=E6=88=B7=E4=BF=A1=E6=81=AF=E3=80=81=20Feign=E6=8E=A5=E5=8F=A3?=
=?UTF-8?q?=E4=BC=A0=E9=80=92=E7=94=A8=E6=88=B7=E3=80=81=E4=BF=AE=E6=94=B9?=
=?UTF-8?q?=E9=85=8D=E7=BD=AE=EF=BC=8C=E4=B8=8Enacos=E9=85=8D=E5=90=88?=
=?UTF-8?q?=E4=B8=80=E8=87=B4?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
cart-service/pom.xml | 10 +++
.../controller/inner/CartControllerInner.java | 33 +++++++
.../com/hmall/cart/service/ICartService.java | 4 +-
.../cart/service/impl/CartServiceImpl.java | 12 ++-
.../src/main/resources/application-dev.yaml | 4 -
.../src/main/resources/application-local.yaml | 4 -
.../src/main/resources/application.yaml | 67 +-------------
.../main/resources/backup/application1.yaml | 67 ++++++++++++++
.../src/main/resources/bootstrap.yaml | 21 +++++
.../java/com/hmall/api/cart/CartClient.java | 8 +-
.../java/com/hmall/api/trade/TradeClient.java | 2 +-
.../java/com/hmall/api/user/UserClient.java | 4 +-
.../com/hmall/common/config/MvcConfig.java | 21 +++++
.../interceptor/UserInfoInterceptor.java | 31 +++++++
.../com/hmall/common/utils/UserContext.java | 3 +-
.../main/resources/META-INF/spring.factories | 3 +-
hm-gateway/pom.xml | 50 +++++++++++
.../com/hmall/gateway/GatewayApplication.java | 11 +++
.../hmall/gateway/config/AuthProperties.java | 13 +++
.../hmall/gateway/config/JwtProperties.java | 16 ++++
.../hmall/gateway/config/SecurityConfig.java | 33 +++++++
.../gateway/filter/AuthGlobalFilter.java | 69 +++++++++++++++
.../java/com/hmall/gateway/utils/JwtTool.java | 82 ++++++++++++++++++
hm-gateway/src/main/resources/application.yml | 43 +++++++++
hm-gateway/src/main/resources/hmall.jks | Bin 0 -> 2711 bytes
item-service/pom.xml | 10 +++
.../item/controller/SearchController.java | 40 +++++++++
.../src/main/resources/application-dev.yaml | 4 -
.../src/main/resources/application-local.yaml | 4 -
.../src/main/resources/application.yaml | 68 +--------------
.../main/resources/backup/application.yaml | 70 +++++++++++++++
.../src/main/resources/bootstrap.yaml | 21 +++++
pay-service/pom.xml | 10 +++
.../pay/service/impl/PayOrderServiceImpl.java | 8 +-
.../src/main/resources/application-dev.yaml | 4 -
.../src/main/resources/application-local.yaml | 4 -
.../src/main/resources/application.yaml | 54 +-----------
.../main/resources/backup/application.yaml | 56 ++++++++++++
pay-service/src/main/resources/bootstrap.yaml | 21 +++++
pom.xml | 1 +
trade-service/pom.xml | 10 +++
.../trade/service/impl/OrderServiceImpl.java | 5 +-
.../src/main/resources/application-dev.yaml | 4 -
.../src/main/resources/application-local.yaml | 4 -
.../src/main/resources/application.yaml | 54 +-----------
.../main/resources/backup/application.yaml | 56 ++++++++++++
.../src/main/resources/bootstrap.yaml | 21 +++++
user-service/pom.xml | 10 +++
.../controller/inner/UserControllerInner.java | 36 ++++++++
.../com/hmall/user/service/IUserService.java | 2 +
.../user/service/impl/UserServiceImpl.java | 11 ++-
.../src/main/resources/application-dev.yaml | 4 -
.../src/main/resources/application-local.yaml | 4 -
.../src/main/resources/application.yaml | 55 +-----------
.../main/resources/backup/application.yaml | 62 +++++++++++++
.../src/main/resources/bootstrap.yaml | 21 +++++
56 files changed, 997 insertions(+), 348 deletions(-)
create mode 100644 cart-service/src/main/java/com/hmall/cart/controller/inner/CartControllerInner.java
delete mode 100644 cart-service/src/main/resources/application-dev.yaml
delete mode 100644 cart-service/src/main/resources/application-local.yaml
create mode 100644 cart-service/src/main/resources/backup/application1.yaml
create mode 100644 cart-service/src/main/resources/bootstrap.yaml
create mode 100644 hm-common/src/main/java/com/hmall/common/config/MvcConfig.java
create mode 100644 hm-common/src/main/java/com/hmall/common/interceptor/UserInfoInterceptor.java
create mode 100644 hm-gateway/pom.xml
create mode 100644 hm-gateway/src/main/java/com/hmall/gateway/GatewayApplication.java
create mode 100644 hm-gateway/src/main/java/com/hmall/gateway/config/AuthProperties.java
create mode 100644 hm-gateway/src/main/java/com/hmall/gateway/config/JwtProperties.java
create mode 100644 hm-gateway/src/main/java/com/hmall/gateway/config/SecurityConfig.java
create mode 100644 hm-gateway/src/main/java/com/hmall/gateway/filter/AuthGlobalFilter.java
create mode 100644 hm-gateway/src/main/java/com/hmall/gateway/utils/JwtTool.java
create mode 100644 hm-gateway/src/main/resources/application.yml
create mode 100644 hm-gateway/src/main/resources/hmall.jks
create mode 100644 item-service/src/main/java/com/hmall/item/controller/SearchController.java
delete mode 100644 item-service/src/main/resources/application-dev.yaml
delete mode 100644 item-service/src/main/resources/application-local.yaml
create mode 100644 item-service/src/main/resources/backup/application.yaml
create mode 100644 item-service/src/main/resources/bootstrap.yaml
delete mode 100644 pay-service/src/main/resources/application-dev.yaml
delete mode 100644 pay-service/src/main/resources/application-local.yaml
create mode 100644 pay-service/src/main/resources/backup/application.yaml
create mode 100644 pay-service/src/main/resources/bootstrap.yaml
delete mode 100644 trade-service/src/main/resources/application-dev.yaml
delete mode 100644 trade-service/src/main/resources/application-local.yaml
create mode 100644 trade-service/src/main/resources/backup/application.yaml
create mode 100644 trade-service/src/main/resources/bootstrap.yaml
create mode 100644 user-service/src/main/java/com/hmall/user/controller/inner/UserControllerInner.java
delete mode 100644 user-service/src/main/resources/application-dev.yaml
delete mode 100644 user-service/src/main/resources/application-local.yaml
create mode 100644 user-service/src/main/resources/backup/application.yaml
create mode 100644 user-service/src/main/resources/bootstrap.yaml
diff --git a/cart-service/pom.xml b/cart-service/pom.xml
index 1e42024..428e397 100644
--- a/cart-service/pom.xml
+++ b/cart-service/pom.xml
@@ -17,6 +17,16 @@
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-config
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-bootstrap
+
io.github.openfeign
diff --git a/cart-service/src/main/java/com/hmall/cart/controller/inner/CartControllerInner.java b/cart-service/src/main/java/com/hmall/cart/controller/inner/CartControllerInner.java
new file mode 100644
index 0000000..2de7c6a
--- /dev/null
+++ b/cart-service/src/main/java/com/hmall/cart/controller/inner/CartControllerInner.java
@@ -0,0 +1,33 @@
+package com.hmall.cart.controller.inner;
+
+
+import com.hmall.cart.service.ICartService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiImplicitParam;
+import io.swagger.annotations.ApiImplicitParams;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.DeleteMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.Collection;
+
+@Api(tags = "购物车相关接口")
+@RestController
+@RequestMapping("inner/carts")
+@RequiredArgsConstructor
+public class CartControllerInner {
+ private final ICartService cartService;
+
+ @ApiOperation("批量删除购物车中商品")
+ @ApiImplicitParams({
+ @ApiImplicitParam(name = "userId", value = "用户id"),
+ @ApiImplicitParam(name = "ids", value = "购物车条目id集合")
+ })
+ @DeleteMapping
+ public void deleteCartItemByIds(@RequestParam("userId") Long userId,@RequestParam("ids") Collection ids){
+ cartService.removeByItemIds(userId,ids);
+ }
+}
diff --git a/cart-service/src/main/java/com/hmall/cart/service/ICartService.java b/cart-service/src/main/java/com/hmall/cart/service/ICartService.java
index 381d220..386704a 100644
--- a/cart-service/src/main/java/com/hmall/cart/service/ICartService.java
+++ b/cart-service/src/main/java/com/hmall/cart/service/ICartService.java
@@ -23,5 +23,7 @@ public interface ICartService extends IService {
List queryMyCarts();
- void removeByItemIds(Collection itemIds);
+ void removeByItemIds(Collection ids);
+
+ void removeByItemIds(Long userId, Collection ids);
}
diff --git a/cart-service/src/main/java/com/hmall/cart/service/impl/CartServiceImpl.java b/cart-service/src/main/java/com/hmall/cart/service/impl/CartServiceImpl.java
index ab53f31..414b7e3 100644
--- a/cart-service/src/main/java/com/hmall/cart/service/impl/CartServiceImpl.java
+++ b/cart-service/src/main/java/com/hmall/cart/service/impl/CartServiceImpl.java
@@ -99,12 +99,18 @@ public class CartServiceImpl extends ServiceImpl implements IC
}
@Override
- public void removeByItemIds(Collection itemIds) {
+ public void removeByItemIds(Collection ids) {
+ Long userId = UserContext.getUser();
+ removeByItemIds(userId, ids);
+ }
+
+ @Override
+ public void removeByItemIds(Long userId, Collection ids) {
// 1.构建删除条件,userId和itemId
QueryWrapper queryWrapper = new QueryWrapper();
queryWrapper.lambda()
- .eq(Cart::getUserId, UserContext.getUser())
- .in(Cart::getItemId, itemIds);
+ .eq(Cart::getUserId, userId)
+ .in(Cart::getItemId, ids);
// 2.删除
remove(queryWrapper);
}
diff --git a/cart-service/src/main/resources/application-dev.yaml b/cart-service/src/main/resources/application-dev.yaml
deleted file mode 100644
index 1fb8f00..0000000
--- a/cart-service/src/main/resources/application-dev.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-hm:
- db:
- host: 192.168.101.68
- pw: mysql
\ No newline at end of file
diff --git a/cart-service/src/main/resources/application-local.yaml b/cart-service/src/main/resources/application-local.yaml
deleted file mode 100644
index 87ff8f4..0000000
--- a/cart-service/src/main/resources/application-local.yaml
+++ /dev/null
@@ -1,4 +0,0 @@
-hm:
- db:
- host: localhost # 修改为你自己的虚拟机IP地址
- pw: mysql # 修改为docker中的MySQL密码
\ No newline at end of file
diff --git a/cart-service/src/main/resources/application.yaml b/cart-service/src/main/resources/application.yaml
index 4abd81b..2b50838 100644
--- a/cart-service/src/main/resources/application.yaml
+++ b/cart-service/src/main/resources/application.yaml
@@ -1,67 +1,6 @@
server:
port: 8082
-spring:
- application:
- name: cart-service
- cloud:
- nacos:
- server-addr: 192.168.101.68:8848 # nacos地址
- sentinel:
- transport:
- dashboard: 192.168.101.68:9090
- client-ip: 192.168.101.1
- http-method-specify: true # 开启请求方式前缀可根据http请求方法区分簇点链路
- profiles:
- active: dev
- datasource:
- url: jdbc:mysql://${hm.db.host}:3306/hm-cart?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
- driver-class-name: com.mysql.cj.jdbc.Driver
- username: root
- password: ${hm.db.pw}
-mybatis-plus:
- configuration:
- default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
- global-config:
- db-config:
- update-strategy: not_null
- id-type: auto
-logging:
- level:
- com.hmall: debug
- pattern:
- dateformat: HH:mm:ss:SSS
- file:
- path: "logs/${spring.application.name}"
-knife4j:
- enable: true
- openapi:
- title: 黑马商城接口文档
- description: "黑马商城接口文档"
- email: 1579670286@qq.com
- concat: 栋哥
- version: v1.0.0
- group:
- default:
- group-name: default
- api-rule: package
- api-rule-resources:
- - com.hmall.cart.controller
hm:
- jwt:
- location: classpath:hmall.jks
- alias: hmall
- password: hmall123
- tokenTTL: 30m
- auth:
- excludePaths:
- - /search/**
- - /users/login
- - /items/**
- - /hi
-feign:
- okhttp:
- enabled: true # 开启OKHttp功能
- sentinel:
- enabled: true # 开启feign对sentinel的支持
-
-# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123
+ swagger:
+ title: 购物车服务接口文档
+ package: com.hmall.cart.controller
\ No newline at end of file
diff --git a/cart-service/src/main/resources/backup/application1.yaml b/cart-service/src/main/resources/backup/application1.yaml
new file mode 100644
index 0000000..4abd81b
--- /dev/null
+++ b/cart-service/src/main/resources/backup/application1.yaml
@@ -0,0 +1,67 @@
+server:
+ port: 8082
+spring:
+ application:
+ name: cart-service
+ cloud:
+ nacos:
+ server-addr: 192.168.101.68:8848 # nacos地址
+ sentinel:
+ transport:
+ dashboard: 192.168.101.68:9090
+ client-ip: 192.168.101.1
+ http-method-specify: true # 开启请求方式前缀可根据http请求方法区分簇点链路
+ profiles:
+ active: dev
+ datasource:
+ url: jdbc:mysql://${hm.db.host}:3306/hm-cart?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
+ driver-class-name: com.mysql.cj.jdbc.Driver
+ username: root
+ password: ${hm.db.pw}
+mybatis-plus:
+ configuration:
+ default-enum-type-handler: com.baomidou.mybatisplus.core.handlers.MybatisEnumTypeHandler
+ global-config:
+ db-config:
+ update-strategy: not_null
+ id-type: auto
+logging:
+ level:
+ com.hmall: debug
+ pattern:
+ dateformat: HH:mm:ss:SSS
+ file:
+ path: "logs/${spring.application.name}"
+knife4j:
+ enable: true
+ openapi:
+ title: 黑马商城接口文档
+ description: "黑马商城接口文档"
+ email: 1579670286@qq.com
+ concat: 栋哥
+ version: v1.0.0
+ group:
+ default:
+ group-name: default
+ api-rule: package
+ api-rule-resources:
+ - com.hmall.cart.controller
+hm:
+ jwt:
+ location: classpath:hmall.jks
+ alias: hmall
+ password: hmall123
+ tokenTTL: 30m
+ auth:
+ excludePaths:
+ - /search/**
+ - /users/login
+ - /items/**
+ - /hi
+feign:
+ okhttp:
+ enabled: true # 开启OKHttp功能
+ sentinel:
+ enabled: true # 开启feign对sentinel的支持
+
+# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123
diff --git a/cart-service/src/main/resources/bootstrap.yaml b/cart-service/src/main/resources/bootstrap.yaml
new file mode 100644
index 0000000..b3feff3
--- /dev/null
+++ b/cart-service/src/main/resources/bootstrap.yaml
@@ -0,0 +1,21 @@
+spring:
+ application:
+ name: cart-service # 服务名称
+ profiles:
+ active: dev
+ cloud:
+ nacos:
+ server-addr: ${NACOS_ADDR:192.168.101.68:8848} # nacos地址
+ discovery: #注册中心
+ namespace: ${NACOS_NAMESPACE:a58f68b9-414b-448c-b68a-5edfa8ced700} #命名空间
+ config: #配置中心
+ file-extension: yaml # 文件后缀名
+ namespace: ${NACOS_NAMESPACE:a58f68b9-414b-448c-b68a-5edfa8ced700} #命名空间
+ group: ${GROUP_NAME:DEFAULT_GROUP} # 配置分组
+ shared-configs: # 共享配置
+ - dataId: shared-jdbc.yaml # 共享mybatis配置
+ - dataId: shared-log.yaml # 共享日志配置
+ - dataId: shared-swagger.yaml # 共享日志配置
+ - dataId: shared-feign.yaml # 共享feign配置
+ - dataId: shared-seata.yaml # 共享seata配置
+ - dataId: shared-sentinel.yaml # 共享sentinel配置
\ No newline at end of file
diff --git a/hm-api/src/main/java/com/hmall/api/cart/CartClient.java b/hm-api/src/main/java/com/hmall/api/cart/CartClient.java
index ccbcccb..ff8b3bf 100644
--- a/hm-api/src/main/java/com/hmall/api/cart/CartClient.java
+++ b/hm-api/src/main/java/com/hmall/api/cart/CartClient.java
@@ -5,11 +5,9 @@ import org.springframework.web.bind.annotation.*;
import java.util.Collection;
-@FeignClient(name = "cart-service",path = "/carts")
+@FeignClient(name = "cart-service")
public interface CartClient {
-
- // 清理购物车商品
- @DeleteMapping
- void deleteCartItemByIds(@RequestParam("ids") Collection ids);
+ @DeleteMapping("/inner/carts")
+ void deleteCartItemByIds(@RequestParam("userId") Long userId,@RequestParam("ids") Collection ids);
}
diff --git a/hm-api/src/main/java/com/hmall/api/trade/TradeClient.java b/hm-api/src/main/java/com/hmall/api/trade/TradeClient.java
index 0203220..276e9e7 100644
--- a/hm-api/src/main/java/com/hmall/api/trade/TradeClient.java
+++ b/hm-api/src/main/java/com/hmall/api/trade/TradeClient.java
@@ -9,6 +9,6 @@ public interface TradeClient {
// 标记订单已支付
@PutMapping("/{orderId}")
- void markOrderPaySuccess(@PathVariable("orderId") Long orderId);
+ void markOrderPaySuccess(@PathVariable("userId") Long userId,@PathVariable("orderId") Long orderId);
}
diff --git a/hm-api/src/main/java/com/hmall/api/user/UserClient.java b/hm-api/src/main/java/com/hmall/api/user/UserClient.java
index 5ee6aee..24b0ed2 100644
--- a/hm-api/src/main/java/com/hmall/api/user/UserClient.java
+++ b/hm-api/src/main/java/com/hmall/api/user/UserClient.java
@@ -4,11 +4,11 @@ import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestParam;
-@FeignClient(name = "user-service",path = "/users")
+@FeignClient(name = "user-service",path = "inner/users")
public interface UserClient {
// 扣减余额
@PutMapping("/money/deduct")
- void deductMoney(@RequestParam("pw") String pw,@RequestParam("amount") Integer amount);
+ void deductMoney(@RequestParam("userId") Long userId,@RequestParam("pw") String pw,@RequestParam("amount") Integer amount);
}
diff --git a/hm-common/src/main/java/com/hmall/common/config/MvcConfig.java b/hm-common/src/main/java/com/hmall/common/config/MvcConfig.java
new file mode 100644
index 0000000..803771e
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/config/MvcConfig.java
@@ -0,0 +1,21 @@
+package com.hmall.common.config;
+
+import com.hmall.common.interceptor.UserInfoInterceptor;
+import lombok.RequiredArgsConstructor;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.DispatcherServlet;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+@Configuration
+@RequiredArgsConstructor
+@ConditionalOnClass(DispatcherServlet.class)
+public class MvcConfig implements WebMvcConfigurer {
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ // 1.添加拦截器
+ UserInfoInterceptor userInfoInterceptor = new UserInfoInterceptor();
+ registry.addInterceptor(userInfoInterceptor);
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/interceptor/UserInfoInterceptor.java b/hm-common/src/main/java/com/hmall/common/interceptor/UserInfoInterceptor.java
new file mode 100644
index 0000000..88794e8
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/interceptor/UserInfoInterceptor.java
@@ -0,0 +1,31 @@
+package com.hmall.common.interceptor;
+
+import cn.hutool.core.util.StrUtil;
+import com.hmall.common.utils.UserContext;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@RequiredArgsConstructor
+public class UserInfoInterceptor implements HandlerInterceptor {
+
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+ // 1.获取请求头中的 token
+ String userId = request.getHeader("user-info");
+ // 2.判断是否为空
+ if (StrUtil.isNotEmpty(userId)) {
+ // 3.保存到ThreadLocal
+ UserContext.setUser(Long.parseLong(userId));
+ }
+ return true;
+ }
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+ // 清理用户
+ UserContext.removeUser();
+ }
+}
diff --git a/hm-common/src/main/java/com/hmall/common/utils/UserContext.java b/hm-common/src/main/java/com/hmall/common/utils/UserContext.java
index eb4c858..d87af95 100644
--- a/hm-common/src/main/java/com/hmall/common/utils/UserContext.java
+++ b/hm-common/src/main/java/com/hmall/common/utils/UserContext.java
@@ -16,8 +16,7 @@ public class UserContext {
* @return 用户id
*/
public static Long getUser() {
- return 1L;
-// return tl.get();
+ return tl.get();
}
/**
diff --git a/hm-common/src/main/resources/META-INF/spring.factories b/hm-common/src/main/resources/META-INF/spring.factories
index 46f5f33..ea50f47 100644
--- a/hm-common/src/main/resources/META-INF/spring.factories
+++ b/hm-common/src/main/resources/META-INF/spring.factories
@@ -1,3 +1,4 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.hmall.common.config.MyBatisConfig,\
- com.hmall.common.config.JsonConfig
\ No newline at end of file
+ com.hmall.common.config.JsonConfig,\
+ com.hmall.common.config.MvcConfig
\ No newline at end of file
diff --git a/hm-gateway/pom.xml b/hm-gateway/pom.xml
new file mode 100644
index 0000000..5d9f023
--- /dev/null
+++ b/hm-gateway/pom.xml
@@ -0,0 +1,50 @@
+
+
+
+ hmall-parent
+ com.hmall
+ 1.0.0
+
+ 4.0.0
+
+ hm-gateway
+
+
+ 11
+ 11
+
+
+
+
+ com.hmall
+ hm-common
+ 1.0.0
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-gateway
+
+
+
+ com.alibaba.cloud
+ spring-cloud-starter-alibaba-nacos-discovery
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-loadbalancer
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
\ No newline at end of file
diff --git a/hm-gateway/src/main/java/com/hmall/gateway/GatewayApplication.java b/hm-gateway/src/main/java/com/hmall/gateway/GatewayApplication.java
new file mode 100644
index 0000000..d0d7f69
--- /dev/null
+++ b/hm-gateway/src/main/java/com/hmall/gateway/GatewayApplication.java
@@ -0,0 +1,11 @@
+package com.hmall.gateway;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@SpringBootApplication
+public class GatewayApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(GatewayApplication.class, args);
+ }
+}
diff --git a/hm-gateway/src/main/java/com/hmall/gateway/config/AuthProperties.java b/hm-gateway/src/main/java/com/hmall/gateway/config/AuthProperties.java
new file mode 100644
index 0000000..9c8e887
--- /dev/null
+++ b/hm-gateway/src/main/java/com/hmall/gateway/config/AuthProperties.java
@@ -0,0 +1,13 @@
+package com.hmall.gateway.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+
+import java.util.List;
+
+@Data
+@ConfigurationProperties(prefix = "hm.auth")
+public class AuthProperties {
+ private List includePaths;
+ private List excludePaths;
+}
diff --git a/hm-gateway/src/main/java/com/hmall/gateway/config/JwtProperties.java b/hm-gateway/src/main/java/com/hmall/gateway/config/JwtProperties.java
new file mode 100644
index 0000000..4e57220
--- /dev/null
+++ b/hm-gateway/src/main/java/com/hmall/gateway/config/JwtProperties.java
@@ -0,0 +1,16 @@
+package com.hmall.gateway.config;
+
+import lombok.Data;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.core.io.Resource;
+
+import java.time.Duration;
+
+@Data
+@ConfigurationProperties(prefix = "hm.jwt")
+public class JwtProperties {
+ private Resource location;
+ private String password;
+ private String alias;
+ private Duration tokenTTL = Duration.ofMinutes(10);
+}
diff --git a/hm-gateway/src/main/java/com/hmall/gateway/config/SecurityConfig.java b/hm-gateway/src/main/java/com/hmall/gateway/config/SecurityConfig.java
new file mode 100644
index 0000000..0048618
--- /dev/null
+++ b/hm-gateway/src/main/java/com/hmall/gateway/config/SecurityConfig.java
@@ -0,0 +1,33 @@
+package com.hmall.gateway.config;
+
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.crypto.password.PasswordEncoder;
+import org.springframework.security.rsa.crypto.KeyStoreKeyFactory;
+
+import java.security.KeyPair;
+
+@Configuration
+@EnableConfigurationProperties(JwtProperties.class)
+public class SecurityConfig {
+
+ @Bean
+ public PasswordEncoder passwordEncoder(){
+ return new BCryptPasswordEncoder();
+ }
+
+ @Bean
+ public KeyPair keyPair(JwtProperties properties){
+ // 获取秘钥工厂
+ KeyStoreKeyFactory keyStoreKeyFactory =
+ new KeyStoreKeyFactory(
+ properties.getLocation(),
+ properties.getPassword().toCharArray());
+ //读取钥匙对
+ return keyStoreKeyFactory.getKeyPair(
+ properties.getAlias(),
+ properties.getPassword().toCharArray());
+ }
+}
\ No newline at end of file
diff --git a/hm-gateway/src/main/java/com/hmall/gateway/filter/AuthGlobalFilter.java b/hm-gateway/src/main/java/com/hmall/gateway/filter/AuthGlobalFilter.java
new file mode 100644
index 0000000..b187853
--- /dev/null
+++ b/hm-gateway/src/main/java/com/hmall/gateway/filter/AuthGlobalFilter.java
@@ -0,0 +1,69 @@
+package com.hmall.gateway.filter;
+
+import cn.hutool.core.util.StrUtil;
+import com.hmall.gateway.config.AuthProperties;
+import com.hmall.gateway.utils.JwtTool;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.cloud.gateway.filter.GatewayFilterChain;
+import org.springframework.cloud.gateway.filter.GlobalFilter;
+import org.springframework.core.Ordered;
+import org.springframework.http.server.reactive.ServerHttpRequest;
+import org.springframework.http.server.reactive.ServerHttpResponse;
+import org.springframework.stereotype.Component;
+import org.springframework.util.AntPathMatcher;
+import org.springframework.web.server.ServerWebExchange;
+import reactor.core.publisher.Mono;
+
+import java.util.List;
+
+@Slf4j
+@Component
+@EnableConfigurationProperties(AuthProperties.class)
+@RequiredArgsConstructor
+public class AuthGlobalFilter implements GlobalFilter, Ordered {
+ private final JwtTool jwtTool;
+
+ private final AuthProperties authProperties;
+
+ private final AntPathMatcher antPathMatcher = new AntPathMatcher();
+
+ @Override
+ public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
+ //1. 获取request、response
+ ServerHttpRequest request = exchange.getRequest();
+ ServerHttpResponse response = exchange.getResponse();
+ // 请求路径
+ String path = request.getPath().toString();
+ // 白名单
+ List excludePaths = authProperties.getExcludePaths();
+ for (String excludePath : excludePaths) {
+ if (antPathMatcher.match(excludePath, path)) {
+ return chain.filter(exchange);
+ }
+ }
+ // 获取请求头的token
+ String token = exchange.getRequest().getHeaders().getFirst("authorization");
+ if (StrUtil.isEmpty(token)) {
+ // token为空则返回401报错
+ response.setRawStatusCode(401);
+ return response.setComplete();
+ }
+ // 如果不为空则校验token
+ Long userId;
+ try {
+ userId = jwtTool.parseToken(token);
+ } catch (Exception e) {
+ response.setRawStatusCode(401);
+ return response.setComplete();
+ }
+ // 5.如果有效,传递用户信息
+ request.mutate().header("user-info", userId.toString());
+ return chain.filter(exchange);
+ }
+ @Override
+ public int getOrder() {
+ return 0;
+ }
+}
diff --git a/hm-gateway/src/main/java/com/hmall/gateway/utils/JwtTool.java b/hm-gateway/src/main/java/com/hmall/gateway/utils/JwtTool.java
new file mode 100644
index 0000000..3717612
--- /dev/null
+++ b/hm-gateway/src/main/java/com/hmall/gateway/utils/JwtTool.java
@@ -0,0 +1,82 @@
+package com.hmall.gateway.utils;
+
+import cn.hutool.core.exceptions.ValidateException;
+import cn.hutool.jwt.JWT;
+import cn.hutool.jwt.JWTValidator;
+import cn.hutool.jwt.signers.JWTSigner;
+import cn.hutool.jwt.signers.JWTSignerUtil;
+import com.hmall.common.exception.UnauthorizedException;
+import org.springframework.stereotype.Component;
+
+import java.security.KeyPair;
+import java.time.Duration;
+import java.util.Date;
+
+@Component
+public class JwtTool {
+ private final JWTSigner jwtSigner;
+
+ public JwtTool(KeyPair keyPair) {
+ this.jwtSigner = JWTSignerUtil.createSigner("rs256", keyPair);
+ }
+
+ /**
+ * 创建 access-token
+ *
+ * @param userId 用户信息
+ * @return access-token
+ */
+ public String createToken(Long userId, Duration ttl) {
+ // 1.生成jws
+ return JWT.create()
+ .setPayload("user", userId)
+ .setExpiresAt(new Date(System.currentTimeMillis() + ttl.toMillis()))
+ .setSigner(jwtSigner)
+ .sign();
+ }
+
+ /**
+ * 解析token
+ *
+ * @param token token
+ * @return 解析刷新token得到的用户信息
+ */
+ public Long parseToken(String token) {
+ // 1.校验token是否为空
+ if (token == null) {
+ throw new UnauthorizedException("未登录");
+ }
+ // 2.校验并解析jwt
+ JWT jwt;
+ try {
+ jwt = JWT.of(token).setSigner(jwtSigner);
+ } catch (Exception e) {
+ throw new UnauthorizedException("无效的token", e);
+ }
+ // 2.校验jwt是否有效
+ if (!jwt.verify()) {
+ // 验证失败
+ throw new UnauthorizedException("无效的token");
+ }
+ // 3.校验是否过期
+ try {
+ JWTValidator.of(jwt).validateDate();
+ } catch (ValidateException e) {
+ throw new UnauthorizedException("token已经过期");
+ }
+ // 4.数据格式校验
+ Object userPayload = jwt.getPayload("user");
+ if (userPayload == null) {
+ // 数据为空
+ throw new UnauthorizedException("无效的token");
+ }
+
+ // 5.数据解析
+ try {
+ return Long.valueOf(userPayload.toString());
+ } catch (RuntimeException e) {
+ // 数据格式有误
+ throw new UnauthorizedException("无效的token");
+ }
+ }
+}
\ No newline at end of file
diff --git a/hm-gateway/src/main/resources/application.yml b/hm-gateway/src/main/resources/application.yml
new file mode 100644
index 0000000..49cad11
--- /dev/null
+++ b/hm-gateway/src/main/resources/application.yml
@@ -0,0 +1,43 @@
+server:
+ port: 8080
+spring:
+ application:
+ name: gateway
+ cloud:
+ nacos:
+ server-addr: 192.168.101.68:8848
+ discovery: #注册中心
+ namespace: ${NACOS_NAMESPACE:a58f68b9-414b-448c-b68a-5edfa8ced700} #命名空间z
+ gateway:
+ routes:
+ - id: item # 路由规则id,自定义,唯一
+ uri: lb://item-service # 路由的目标服务,lb代表负载均衡,会从注册中心拉取服务列表
+ predicates: # 路由断言,判断当前请求是否符合当前规则,符合则路由到目标服务
+ - Path=/items/**,/search/** # 这里是以请求路径作为判断规则
+ - id: cart
+ uri: lb://cart-service
+ predicates:
+ - Path=/carts/**
+ - id: user
+ uri: lb://user-service
+ predicates:
+ - Path=/users/**,/addresses/**
+ - id: trade
+ uri: lb://trade-service
+ predicates:
+ - Path=/orders/**
+ - id: pay
+ uri: lb://pay-service
+ predicates:
+ - Path=/pay-orders/**
+hm:
+ jwt:
+ location: classpath:hmall.jks # 秘钥地址
+ alias: hmall # 秘钥别名
+ password: hmall123 # 秘钥文件密码
+ tokenTTL: 30m # 登录有效期
+ auth:
+ excludePaths: # 无需身份校验的路径
+ - /search/**
+ - /users/login
+ - /items/**
\ No newline at end of file
diff --git a/hm-gateway/src/main/resources/hmall.jks b/hm-gateway/src/main/resources/hmall.jks
new file mode 100644
index 0000000000000000000000000000000000000000..a34bd33d09d4c33b13769213e6c07c2267cce162
GIT binary patch
literal 2711
zcma)+cQhM}8pe}ILhLW9P(T2^2!cTX2w^D9HO>_1K?erP
zf!Uc1xM1i=8tQB9Awe=@l?En&N0I{yr?ks8q1gUD&77!=1Il1w;7shw{+Js>*%oqg
zSaod~_JhF?7#x(IlDw`8d&6Rn{fmebw+~9^WwB($~{W{}M
zpGa6hl2804zbZ5mHOUf*nNvZHABX#t3QXF9<9Y*-0gO2do~=SNqIbGdb$}Y)1C8
zXyuL#^haq!isIQ=PhfX)K0*-oToJxIUrTVp-UBAmhGS%wbeA9{FxRv$+Zlf#2D|
zMJPQu*9*5PmUNM8e3M^%bkgcZQgd9U7qYXEdv5k)!7bFtdeIZcmx8??ATNnJa!L8_
zq>D3x$L$92p-sjtn0T9$LkF<>#xIN+!}2799QHL><8SfuG*Vo*Uw0dM+3D^J7RLGV
zEVdc_`3EY^N@%yzh@V8a?vqzkNa^f(>Fhkp2F#sE#o4g+33mG7RHuthC*=ki2k@VP
zI9Y{g%iz(_dUpMWE(~zYwi5-SopTQ(Ug-h8Geoe5vJhjvCQkdnw
zhQY@{A*x+HudUCz?t2zh-F9Nt<_>~A={_Evb9{1qtE$8XZX$z%b&tA-cWUfLL^$F<
zefSyUbsPieS>?MB9k2E?_h9O53vuM+98wx#CMb4&_SN0k>&3Lt{Lex1>n3vpS=hBt
z;iee+B4-058Pj9_=_&CHIYQDE)hYAVr~(YPeL#D*-u_K!`&rhW^-fqAA~B#et&y%3
zh+U42Ee#L}<_2DgXbsoYfSn<~&)
zPAuX;y7D?am}5j@gLPS|lf7r2oX&Jm`F8_#NGUx7a8Z6_;Ih-@@Xf8)8$_Eb-a{ug
zlbMlzTku5T;)B%kvQjA)ZX7T2=J+(PT^&!{>oQ-*iKjhp_mL-Cci_Z1Yt}IC;&Je_
zQGe!`FXu3mC>QG8<7v4rf<=))YP7EDZVauv!1B1U0;ilS8DDMQ92(7W2VVFi|-SfYKtfYRN9Wp1Gfyy=9BhjP7J
zzHpH6rn84Xd%1e)&Tu9}h>-AwXAVsAvgz;JpzhBV6W0wybUaF|-M7lF27g}Nz{RA`
ztLPyFe%B?8TZLJK5#R;z1z-R;01hGkkHn+G4d%R#_4N=_(a=&;)lySYQB_mdKoG$A
z-%s>VG68(|7xDuE0l${#KML@Fm2{gBo?s0-dQ(_!0nRM#r?rzVyk!q_YTy!Iq
z{qe%~o7z^B=4b0t!lLM1lQ2s9ovH!hdW`xdR0AH3!M^$PX5{1kh?cCrXVaQf8SpkV(G
z;$ih>F6ZPoId*Fmz4pd(rMG`9uUAV!Hdzbx@?)5FpUAJEG<+#kt-QV)M{~-tL3(OK
z0k@oWin}_43UlNem~W)VERM?YixKY`*FVa@eCx2|@V3pXS*QH1U|r$0l~ms59{OoG
z_hzw5*z40b8fI;mp40d?IIhcW8iV`6Bs1dhbW}ZnAm(h^3#7
z{sbPTc-9oy=)oB&kYL}$eJt|$uvgGAmn6;fv0mMu_o_LSu~c#zr0eexhge@A(MZnK
zYjzqltUoI`T5i65c`#ohqZaX`v<=%XnfyIsl@ZrLQUBt|kMe=)ugtQ$C*smS9>#`f
z^ABuErq!HX>EdW!&%%x|ZMJ)@jGZc$XiEd9bRAz&A>@$L
zxlxwP!)E&Vf?@t{vvo#Yc!b2kxb7O)SF4M+(=$+N17nGYwW{;rxWE~fJE%W7)cEIV
zGG)dUD;!+3mKSXCM@V7%wxYB}PU%@s7W8{KrzR1hhY&}=pb&XhI*=d)0Oo$!({e_C
uQpH62W+}}XVj|zYG4BNCPS~2Erd)e6g7$a0VPy&!oz{sz1
+
+