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 + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + com.alibaba.cloud diff --git a/item-service/src/main/java/com/hmall/item/controller/SearchController.java b/item-service/src/main/java/com/hmall/item/controller/SearchController.java new file mode 100644 index 0000000..eaa819c --- /dev/null +++ b/item-service/src/main/java/com/hmall/item/controller/SearchController.java @@ -0,0 +1,40 @@ +package com.hmall.item.controller; + + +import cn.hutool.core.util.StrUtil; +import com.baomidou.mybatisplus.extension.plugins.pagination.Page; +import com.hmall.api.item.dto.ItemDTO; +import com.hmall.common.domain.PageDTO; +import com.hmall.item.domain.po.Item; +import com.hmall.item.domain.query.ItemPageQuery; +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.GetMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = "搜索相关接口") +@RestController +@RequestMapping("/search") +@RequiredArgsConstructor +public class SearchController { + + private final IItemService itemService; + + @ApiOperation("搜索商品") + @GetMapping("/list") + public PageDTO search(ItemPageQuery query) { + // 分页查询 + Page result = itemService.lambdaQuery() + .like(StrUtil.isNotBlank(query.getKey()), Item::getName, query.getKey()) + .eq(StrUtil.isNotBlank(query.getBrand()), Item::getBrand, query.getBrand()) + .eq(StrUtil.isNotBlank(query.getCategory()), Item::getCategory, query.getCategory()) + .eq(Item::getStatus, 1) + .between(query.getMaxPrice() != null, Item::getPrice, query.getMinPrice(), query.getMaxPrice()) + .page(query.toMpPage("update_time", false)); + // 封装并返回 + return PageDTO.of(result, ItemDTO.class); + } +} diff --git a/item-service/src/main/resources/application-dev.yaml b/item-service/src/main/resources/application-dev.yaml deleted file mode 100644 index 1fb8f00..0000000 --- a/item-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/item-service/src/main/resources/application-local.yaml b/item-service/src/main/resources/application-local.yaml deleted file mode 100644 index 87ff8f4..0000000 --- a/item-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/item-service/src/main/resources/application.yaml b/item-service/src/main/resources/application.yaml index 61d3555..d91d6b0 100644 --- a/item-service/src/main/resources/application.yaml +++ b/item-service/src/main/resources/application.yaml @@ -1,68 +1,6 @@ server: port: 8081 -spring: - application: - name: item-service - cloud: - nacos: - server-addr: 192.168.101.68:8848 # nacos地址 - 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.user.controller hm: - jwt: - location: classpath:hmall.jks - alias: hmall - password: hmall123 - tokenTTL: 30m - auth: - excludePaths: - - /search/** - - /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 + swagger: + title: 商品服务接口文档 + package: com.hmall.item.controller \ No newline at end of file diff --git a/item-service/src/main/resources/backup/application.yaml b/item-service/src/main/resources/backup/application.yaml new file mode 100644 index 0000000..fff4cbb --- /dev/null +++ b/item-service/src/main/resources/backup/application.yaml @@ -0,0 +1,70 @@ +server: + port: 8081 +spring: + application: + name: item-service + cloud: + nacos: + server-addr: 192.168.101.68:8848 # nacos地址 + discovery: #注册中心 + namespace: ${NACOS_NAMESPACE:a58f68b9-414b-448c-b68a-5edfa8ced700} #命名空间 + 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.user.controller +hm: + jwt: + location: classpath:hmall.jks + alias: hmall + password: hmall123 + tokenTTL: 30m + auth: + excludePaths: + - /search/** + - /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/item-service/src/main/resources/bootstrap.yaml b/item-service/src/main/resources/bootstrap.yaml new file mode 100644 index 0000000..9a4333c --- /dev/null +++ b/item-service/src/main/resources/bootstrap.yaml @@ -0,0 +1,21 @@ +spring: + application: + name: item-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/pay-service/pom.xml b/pay-service/pom.xml index c0393f0..54d2e78 100644 --- a/pay-service/pom.xml +++ b/pay-service/pom.xml @@ -18,6 +18,16 @@ + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + com.alibaba.cloud 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 77b8cd7..e648723 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 @@ -56,18 +56,16 @@ public class PayOrderServiceImpl extends ServiceImpl i // 订单不是未支付,状态异常 throw new BizIllegalException("交易已支付或关闭!"); } + Long userId = UserContext.getUser(); // 3.尝试扣减余额 - userClient.deductMoney(payOrderFormDTO.getPw(), po.getAmount()); + userClient.deductMoney(userId,payOrderFormDTO.getPw(), po.getAmount()); // 4.修改支付单状态 boolean success = markPayOrderSuccess(payOrderFormDTO.getId(), LocalDateTime.now()); if (!success) { throw new BizIllegalException("交易已支付或关闭!"); } // 5.修改订单状态 - tradeClient.markOrderPaySuccess(po.getBizOrderNo()); - if(true){ - throw new RuntimeException("测试抛出异常"); - } + tradeClient.markOrderPaySuccess(userId,po.getBizOrderNo()); } public boolean markPayOrderSuccess(Long id, LocalDateTime successTime) { diff --git a/pay-service/src/main/resources/application-dev.yaml b/pay-service/src/main/resources/application-dev.yaml deleted file mode 100644 index 1fb8f00..0000000 --- a/pay-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/pay-service/src/main/resources/application-local.yaml b/pay-service/src/main/resources/application-local.yaml deleted file mode 100644 index 87ff8f4..0000000 --- a/pay-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/pay-service/src/main/resources/application.yaml b/pay-service/src/main/resources/application.yaml index 3451df5..01d6c77 100644 --- a/pay-service/src/main/resources/application.yaml +++ b/pay-service/src/main/resources/application.yaml @@ -1,54 +1,6 @@ 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: +hm: + swagger: 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 -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 + package: com.hmall.pay.controller \ No newline at end of file diff --git a/pay-service/src/main/resources/backup/application.yaml b/pay-service/src/main/resources/backup/application.yaml new file mode 100644 index 0000000..fba48eb --- /dev/null +++ b/pay-service/src/main/resources/backup/application.yaml @@ -0,0 +1,56 @@ +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 + discovery: #注册中心 + namespace: ${NACOS_NAMESPACE:a58f68b9-414b-448c-b68a-5edfa8ced700} #命名空间 +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 +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/pay-service/src/main/resources/bootstrap.yaml b/pay-service/src/main/resources/bootstrap.yaml new file mode 100644 index 0000000..4fb537f --- /dev/null +++ b/pay-service/src/main/resources/bootstrap.yaml @@ -0,0 +1,21 @@ +spring: + application: + name: pay-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/pom.xml b/pom.xml index 8391108..bbd4947 100644 --- a/pom.xml +++ b/pom.xml @@ -15,6 +15,7 @@ user-service trade-service pay-service + hm-gateway 1.0.0 diff --git a/trade-service/pom.xml b/trade-service/pom.xml index aae76e3..24c1226 100644 --- a/trade-service/pom.xml +++ b/trade-service/pom.xml @@ -18,6 +18,16 @@ + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + com.alibaba.cloud 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 0fa0687..73d22a2 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 @@ -68,7 +68,8 @@ public class OrderServiceImpl extends ServiceImpl implements order.setTotalFee(total); // 1.5.其它属性 order.setPaymentType(orderFormDTO.getPaymentType()); - order.setUserId(UserContext.getUser()); + Long userId = UserContext.getUser(); + order.setUserId(userId); order.setStatus(1); // 1.6.将Order写入数据库order表中 save(order); @@ -78,7 +79,7 @@ public class OrderServiceImpl extends ServiceImpl implements detailService.saveBatch(details); // 3.清理购物车商品 - cartClient.deleteCartItemByIds(itemIds); + cartClient.deleteCartItemByIds(userId,itemIds); // 4.扣减库存 try { diff --git a/trade-service/src/main/resources/application-dev.yaml b/trade-service/src/main/resources/application-dev.yaml deleted file mode 100644 index 1fb8f00..0000000 --- a/trade-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/trade-service/src/main/resources/application-local.yaml b/trade-service/src/main/resources/application-local.yaml deleted file mode 100644 index 87ff8f4..0000000 --- a/trade-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/trade-service/src/main/resources/application.yaml b/trade-service/src/main/resources/application.yaml index 910a0b7..3421692 100644 --- a/trade-service/src/main/resources/application.yaml +++ b/trade-service/src/main/resources/application.yaml @@ -1,54 +1,6 @@ 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: +hm: + swagger: 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 -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 + package: com.hmall.trade.controller \ No newline at end of file diff --git a/trade-service/src/main/resources/backup/application.yaml b/trade-service/src/main/resources/backup/application.yaml new file mode 100644 index 0000000..6cae43a --- /dev/null +++ b/trade-service/src/main/resources/backup/application.yaml @@ -0,0 +1,56 @@ +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地址 + discovery: #注册中心 + namespace: ${NACOS_NAMESPACE:a58f68b9-414b-448c-b68a-5edfa8ced700} #命名空间 +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 +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/src/main/resources/bootstrap.yaml b/trade-service/src/main/resources/bootstrap.yaml new file mode 100644 index 0000000..f2aa9af --- /dev/null +++ b/trade-service/src/main/resources/bootstrap.yaml @@ -0,0 +1,21 @@ +spring: + application: + name: trade-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/user-service/pom.xml b/user-service/pom.xml index 7439b0a..7380f77 100644 --- a/user-service/pom.xml +++ b/user-service/pom.xml @@ -18,6 +18,16 @@ + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + com.alibaba.cloud diff --git a/user-service/src/main/java/com/hmall/user/controller/inner/UserControllerInner.java b/user-service/src/main/java/com/hmall/user/controller/inner/UserControllerInner.java new file mode 100644 index 0000000..ac24be7 --- /dev/null +++ b/user-service/src/main/java/com/hmall/user/controller/inner/UserControllerInner.java @@ -0,0 +1,36 @@ +package com.hmall.user.controller.inner; + + +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.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +@Api(tags = "用户相关接口") +@RestController +@RequestMapping("inner/users") +@RequiredArgsConstructor +public class UserControllerInner { + + private final IUserService userService; + + @ApiOperation("扣减余额") + @ApiImplicitParams({ + @ApiImplicitParam(name = "userId", value = "用户id"), + @ApiImplicitParam(name = "pw", value = "支付密码"), + @ApiImplicitParam(name = "amount", value = "支付金额") + }) + @PutMapping("/money/deduct") + public void deductMoney(@RequestParam("userId") Long userId, + @RequestParam("pw") String pw, + @RequestParam("amount") Integer amount) { + userService.deductMoney(userId, pw, amount); + } +} + 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 index 0721c08..768ccfb 100644 --- a/user-service/src/main/java/com/hmall/user/service/IUserService.java +++ b/user-service/src/main/java/com/hmall/user/service/IUserService.java @@ -19,4 +19,6 @@ public interface IUserService extends IService { UserLoginVO login(LoginFormDTO loginFormDTO); void deductMoney(String pw, Integer totalFee); + + void deductMoney(Long userId,String pw, Integer totalFee); } 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 index c8f6bbc..8c1f3c2 100644 --- 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 @@ -74,14 +74,19 @@ public class UserServiceImpl extends ServiceImpl implements IU public void deductMoney(String pw, Integer totalFee) { log.info("开始扣款"); // 1.校验密码 - User user = getById(UserContext.getUser()); + Long userId = UserContext.getUser(); + deductMoney(userId, pw, totalFee); + } + + @Override + public void deductMoney(Long userId, String pw, Integer totalFee) { + User user = getById(userId); if(user == null || !passwordEncoder.matches(pw, user.getPassword())){ // 密码错误 throw new BizIllegalException("用户密码错误"); } - // 2.尝试扣款 - int i = baseMapper.updateMoney(UserContext.getUser(), totalFee); + int i = baseMapper.updateMoney(userId, totalFee); if (i <= 0) { throw new RuntimeException("扣款失败,可能是余额不足!"); } diff --git a/user-service/src/main/resources/application-dev.yaml b/user-service/src/main/resources/application-dev.yaml deleted file mode 100644 index 1fb8f00..0000000 --- a/user-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/user-service/src/main/resources/application-local.yaml b/user-service/src/main/resources/application-local.yaml deleted file mode 100644 index 87ff8f4..0000000 --- a/user-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/user-service/src/main/resources/application.yaml b/user-service/src/main/resources/application.yaml index 5aa6d50..e1cde39 100644 --- a/user-service/src/main/resources/application.yaml +++ b/user-service/src/main/resources/application.yaml @@ -1,60 +1,11 @@ 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: + swagger: + title: 用户服务接口文档 + package: com.hmall.user.controller jwt: location: classpath:hmall.jks alias: hmall password: hmall123 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 diff --git a/user-service/src/main/resources/backup/application.yaml b/user-service/src/main/resources/backup/application.yaml new file mode 100644 index 0000000..96129b6 --- /dev/null +++ b/user-service/src/main/resources/backup/application.yaml @@ -0,0 +1,62 @@ +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地址 + discovery: #注册中心 + namespace: ${NACOS_NAMESPACE:a58f68b9-414b-448c-b68a-5edfa8ced700} #命名空间 +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 +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/src/main/resources/bootstrap.yaml b/user-service/src/main/resources/bootstrap.yaml new file mode 100644 index 0000000..dee063a --- /dev/null +++ b/user-service/src/main/resources/bootstrap.yaml @@ -0,0 +1,21 @@ +spring: + application: + name: user-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 -- Gitee From cd519250f329e53da7654b54a4754be740051f6b Mon Sep 17 00:00:00 2001 From: syd <1579670286@qq.com> Date: Sat, 21 Jun 2025 10:02:43 +0800 Subject: [PATCH 8/8] =?UTF-8?q?=E5=AE=9E=E7=8E=B0=20=E7=BD=91=E5=85=B3?= =?UTF-8?q?=E9=85=8D=E7=BD=AE=E3=80=81=E9=85=8D=E7=BD=AE=E7=83=AD=E6=9B=B4?= =?UTF-8?q?=E6=96=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/hmall/cart/config/CartProperties.java | 12 ++++++ .../cart/service/impl/CartServiceImpl.java | 8 +++- hm-gateway/pom.xml | 11 +++++ hm-gateway/src/main/resources/application.yml | 43 +------------------ .../src/main/resources/backup/application.yml | 43 +++++++++++++++++++ hm-gateway/src/main/resources/bootstrap.yaml | 16 +++++++ 6 files changed, 89 insertions(+), 44 deletions(-) create mode 100644 cart-service/src/main/java/com/hmall/cart/config/CartProperties.java create mode 100644 hm-gateway/src/main/resources/backup/application.yml create mode 100644 hm-gateway/src/main/resources/bootstrap.yaml diff --git a/cart-service/src/main/java/com/hmall/cart/config/CartProperties.java b/cart-service/src/main/java/com/hmall/cart/config/CartProperties.java new file mode 100644 index 0000000..1f8386b --- /dev/null +++ b/cart-service/src/main/java/com/hmall/cart/config/CartProperties.java @@ -0,0 +1,12 @@ +package com.hmall.cart.config; + +import lombok.Data; +import org.springframework.boot.context.properties.ConfigurationProperties; +import org.springframework.stereotype.Component; + +@Data +@Component +@ConfigurationProperties(prefix = "hm.cart") +public class CartProperties { + private Integer maxAmount; +} 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 414b7e3..80782d6 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 @@ -5,6 +5,7 @@ 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.config.CartProperties; import com.hmall.cart.domain.dto.CartFormDTO; import com.hmall.cart.domain.po.Cart; import com.hmall.cart.domain.vo.CartVO; @@ -35,6 +36,8 @@ public class CartServiceImpl extends ServiceImpl implements IC private final ItemClient itemClient; + private final CartProperties cartProperties; + @Override public void addItem2Cart(CartFormDTO cartFormDTO) { // 1.获取登录用户 @@ -117,8 +120,9 @@ public class CartServiceImpl extends ServiceImpl implements IC private void checkCartsFull(Long userId) { int count = lambdaQuery().eq(Cart::getUserId, userId).count(); - if (count >= 10) { - throw new BizIllegalException(StrUtil.format("用户购物车课程不能超过{}", 10)); + Integer maxAmount = cartProperties.getMaxAmount(); + if (count >= maxAmount) { + throw new BizIllegalException(StrUtil.format("用户购物车课程不能超过{}", maxAmount)); } } diff --git a/hm-gateway/pom.xml b/hm-gateway/pom.xml index 5d9f023..0502a40 100644 --- a/hm-gateway/pom.xml +++ b/hm-gateway/pom.xml @@ -16,6 +16,17 @@ 11 + + + + com.alibaba.cloud + spring-cloud-starter-alibaba-nacos-config + + + + org.springframework.cloud + spring-cloud-starter-bootstrap + com.hmall diff --git a/hm-gateway/src/main/resources/application.yml b/hm-gateway/src/main/resources/application.yml index 49cad11..47fbb02 100644 --- a/hm-gateway/src/main/resources/application.yml +++ b/hm-gateway/src/main/resources/application.yml @@ -1,43 +1,2 @@ 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 + port: 8080 \ No newline at end of file diff --git a/hm-gateway/src/main/resources/backup/application.yml b/hm-gateway/src/main/resources/backup/application.yml new file mode 100644 index 0000000..49cad11 --- /dev/null +++ b/hm-gateway/src/main/resources/backup/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/bootstrap.yaml b/hm-gateway/src/main/resources/bootstrap.yaml new file mode 100644 index 0000000..df84832 --- /dev/null +++ b/hm-gateway/src/main/resources/bootstrap.yaml @@ -0,0 +1,16 @@ +spring: + application: + name: gateway # 服务名称 + 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-log.yaml # 共享日志配置 \ No newline at end of file -- Gitee