commit a3e7faed944bd06c19a21c98ae3b9fb787662a21
Author: zhangsan <646228430@qq.com>
Date: Tue May 20 19:21:02 2025 +0800
first commit
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..141f938
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,72 @@
+# ========== IDEA ==========
+# IntelliJ IDEA 项目文件
+.idea/
+# IDEA 模块文件
+*.iml
+*.iws
+# IDEA 生成的工作区文件
+workspace.xml
+# 代码样式、运行配置等
+.idea/**/workspace.xml
+.idea/**/tasks.xml
+.idea/dictionaries
+.idea/vcs.xml
+# 防止提交本地历史
+.idea/**/localHistory/
+
+# ========== 编译输出 ==========
+# Maven 默认输出目录
+target/
+# Gradle 默认输出目录
+build/
+
+# ========== 操作系统 ==========
+# Windows 缓存文件
+Thumbs.db
+ehthumbs.db
+# macOS 缓存文件
+.DS_Store
+.AppleDouble
+.LSOverride
+# Linux 缓存文件
+*~
+
+# ========== 日志和临时文件 ==========
+*.log
+*.tmp
+*.temp
+*.swp
+
+# ========== Test 输出 ==========
+test-output/
+surefire-reports/
+failsafe-reports/
+
+# ========== 依赖管理 ==========
+# Maven 本地仓库
+.mvn/repository/
+# Gradle 缓存(可选)
+.gradle/
+
+# ========== 打包文件 ==========
+# Spring Boot 可执行包
+*.jar
+*.war
+# Spring Boot 可执行包目录
+BOOT-INF/
+
+# ========== IDE 外插件 ==========
+# Lombok 插件生成文件
+*.class
+# JRebel 缓存
+rebel.xml
+
+# ========== 其它 ==========
+# 环境变量文件(如包含敏感配置)
+*.env
+# Docker 相关
+docker-compose.override.yml
+
+# 如果你用到 IDEA 自带的 File-Based Storage(8+版本默认),可以添加:
+# .idea/.name
+# .idea/gradle.xml
diff --git a/cart-service/pom.xml b/cart-service/pom.xml
new file mode 100644
index 0000000..99e8c61
--- /dev/null
+++ b/cart-service/pom.xml
@@ -0,0 +1,58 @@
+
+
+
+ hmall
+ com.heima
+ 1.0.0
+
+ 4.0.0
+
+ cart-service
+
+
+ 17
+ 17
+
+
+
+
+ com.heima
+ hm-common
+ 1.0.0
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+
+
+ com.heima
+ hm-service
+ 1.0.0
+ compile
+
+
+
+
+ ${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/CartApplication.java b/cart-service/src/main/java/com/hmall/cart/CartApplication.java
new file mode 100644
index 0000000..502a1e7
--- /dev/null
+++ b/cart-service/src/main/java/com/hmall/cart/CartApplication.java
@@ -0,0 +1,14 @@
+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 CartApplication {
+ public static void main(String[] args) {
+
+ SpringApplication.run(CartApplication.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..bf8be0d
--- /dev/null
+++ b/cart-service/src/main/java/com/hmall/cart/controller/CartController.java
@@ -0,0 +1,53 @@
+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/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..90cb68d
--- /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..198e68f
--- /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..ccdec09
--- /dev/null
+++ b/cart-service/src/main/java/com/hmall/cart/service/impl/CartServiceImpl.java
@@ -0,0 +1,134 @@
+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.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 com.hmall.domain.dto.ItemDTO;
+import com.hmall.service.IItemService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+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,1L /*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);
+// 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) {
+ long count = lambdaQuery()
+ .eq(Cart::getUserId, userId)
+ .count(); // ← 这里返回 long
+ if (count >= 10) {
+ throw new BizIllegalException(
+ StrUtil.format("用户购物车课程不能超过{}", 10)
+ );
+ }
+ }
+
+ private boolean checkItemExists(Long itemId, Long userId) {
+ long 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..a6fa71e
--- /dev/null
+++ b/cart-service/src/main/resources/application-dev.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: mysql
+ pw: 123
\ 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..707fb24
--- /dev/null
+++ b/cart-service/src/main/resources/application-local.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: localhost # 修改为你自己的虚拟机IP地址
+ pw: 123456 # 修改为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..78d6525
--- /dev/null
+++ b/cart-service/src/main/resources/application.yaml
@@ -0,0 +1,43 @@
+server:
+ port: 8082
+spring:
+ application:
+ name: cart-service
+ profiles:
+ active: local
+ 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: zhangsan@itcast.cn
+ concat: 宇哥
+ url: https://www.itcast.cn
+ version: v1.0.0
+ group:
+ default:
+ group-name: default
+ api-rule: package
+ api-rule-resources:
+ - com.hmall.cart.controller
+
+# 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..f22854a
--- /dev/null
+++ b/cart-service/src/main/resources/mapper/CartMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/hm-common/pom.xml b/hm-common/pom.xml
new file mode 100644
index 0000000..07cc9b5
--- /dev/null
+++ b/hm-common/pom.xml
@@ -0,0 +1,95 @@
+
+
+
+ hmall
+ com.heima
+ 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/UserContext.java b/hm-common/src/main/java/com/hmall/common/utils/UserContext.java
new file mode 100644
index 0000000..d87af95
--- /dev/null
+++ b/hm-common/src/main/java/com/hmall/common/utils/UserContext.java
@@ -0,0 +1,28 @@
+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 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
diff --git a/hm-service/Dockerfile b/hm-service/Dockerfile
new file mode 100644
index 0000000..04922ae
--- /dev/null
+++ b/hm-service/Dockerfile
@@ -0,0 +1,9 @@
+# 基础镜像
+FROM openjdk:11.0-jre-buster
+# 设定时区
+ENV TZ=Asia/Shanghai
+RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
+# 拷贝jar包
+COPY hm-service.jar /app.jar
+# 入口
+ENTRYPOINT ["java", "-jar", "/app.jar"]
\ No newline at end of file
diff --git a/hm-service/pom.xml b/hm-service/pom.xml
new file mode 100644
index 0000000..00cc5b0
--- /dev/null
+++ b/hm-service/pom.xml
@@ -0,0 +1,71 @@
+
+
+
+ hmall
+ com.heima
+ 1.0.0
+
+ 4.0.0
+
+ hm-service
+
+
+ 11
+ 11
+
+
+
+
+
+ com.heima
+ hm-common
+ 1.0.0
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.security
+ spring-security-crypto
+
+
+ org.springframework.security
+ spring-security-rsa
+ 1.0.9.RELEASE
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+
+
+
+ org.springframework.boot
+ spring-boot-starter-test
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-redis
+
+
+
+ ${project.artifactId}
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
\ No newline at end of file
diff --git a/hm-service/src/main/java/com/hmall/HMallApplication.java b/hm-service/src/main/java/com/hmall/HMallApplication.java
new file mode 100644
index 0000000..067936c
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/HMallApplication.java
@@ -0,0 +1,13 @@
+package com.hmall;
+
+import org.mybatis.spring.annotation.MapperScan;
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+
+@MapperScan("com.hmall.mapper")
+@SpringBootApplication
+public class HMallApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(HMallApplication.class, args);
+ }
+}
\ No newline at end of file
diff --git a/hm-service/src/main/java/com/hmall/config/AuthProperties.java b/hm-service/src/main/java/com/hmall/config/AuthProperties.java
new file mode 100644
index 0000000..4002eb8
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/config/AuthProperties.java
@@ -0,0 +1,13 @@
+package com.hmall.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-service/src/main/java/com/hmall/config/JwtProperties.java b/hm-service/src/main/java/com/hmall/config/JwtProperties.java
new file mode 100644
index 0000000..eec705d
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/config/JwtProperties.java
@@ -0,0 +1,16 @@
+package com.hmall.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-service/src/main/java/com/hmall/config/MvcConfig.java b/hm-service/src/main/java/com/hmall/config/MvcConfig.java
new file mode 100644
index 0000000..0b797c7
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/config/MvcConfig.java
@@ -0,0 +1,54 @@
+package com.hmall.config;
+
+import cn.hutool.core.collection.CollUtil;
+import com.hmall.interceptor.LoginInterceptor;
+import com.hmall.utils.JwtTool;
+import lombok.RequiredArgsConstructor;
+import org.springframework.boot.context.properties.EnableConfigurationProperties;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistration;
+import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
+
+import java.util.List;
+
+@Configuration
+@RequiredArgsConstructor
+@EnableConfigurationProperties(AuthProperties.class)
+public class MvcConfig implements WebMvcConfigurer {
+
+ private final JwtTool jwtTool;
+ private final AuthProperties authProperties;
+
+/* @Bean
+ public CommonExceptionAdvice commonExceptionAdvice(){
+ return new CommonExceptionAdvice();
+ }*/
+
+ @Override
+ public void addInterceptors(InterceptorRegistry registry) {
+ // 1.添加拦截器
+ LoginInterceptor loginInterceptor = new LoginInterceptor(jwtTool);
+ InterceptorRegistration registration = registry.addInterceptor(loginInterceptor);
+ // 2.配置拦截路径
+ List includePaths = authProperties.getIncludePaths();
+ if (CollUtil.isNotEmpty(includePaths)) {
+ registration.addPathPatterns(includePaths);
+ }
+ // 3.配置放行路径
+ List excludePaths = authProperties.getExcludePaths();
+ if (CollUtil.isNotEmpty(excludePaths)) {
+ registration.excludePathPatterns(excludePaths);
+ }
+ registration.excludePathPatterns(
+ "/error",
+ "/favicon.ico",
+ "/v2/**",
+ "/v3/**",
+ "/swagger-resources/**",
+ "/webjars/**",
+ "/doc.html"
+ );
+
+ }
+}
diff --git a/hm-service/src/main/java/com/hmall/config/SecurityConfig.java b/hm-service/src/main/java/com/hmall/config/SecurityConfig.java
new file mode 100644
index 0000000..a8c385b
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/config/SecurityConfig.java
@@ -0,0 +1,33 @@
+package com.hmall.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-service/src/main/java/com/hmall/controller/AddressController.java b/hm-service/src/main/java/com/hmall/controller/AddressController.java
new file mode 100644
index 0000000..2769d0c
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/controller/AddressController.java
@@ -0,0 +1,61 @@
+package com.hmall.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.domain.dto.AddressDTO;
+import com.hmall.domain.po.Address;
+import com.hmall.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/hm-service/src/main/java/com/hmall/controller/CartController.java b/hm-service/src/main/java/com/hmall/controller/CartController.java
new file mode 100644
index 0000000..94b747c
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/controller/CartController.java
@@ -0,0 +1,54 @@
+package com.hmall.controller;
+
+
+import com.hmall.domain.dto.CartFormDTO;
+import com.hmall.domain.po.Cart;
+import com.hmall.domain.vo.CartVO;
+import com.hmall.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/hm-service/src/main/java/com/hmall/controller/HelloController.java b/hm-service/src/main/java/com/hmall/controller/HelloController.java
new file mode 100644
index 0000000..7cc259d
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/controller/HelloController.java
@@ -0,0 +1,29 @@
+package com.hmall.controller;
+
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import javax.servlet.http.HttpServletRequest;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+@RestController
+@RequestMapping("hi")
+public class HelloController {
+
+ private final Map countMap = new HashMap<>();
+
+ @GetMapping
+ public String hello(HttpServletRequest request) throws InterruptedException {
+ Thread.sleep(300);
+ String ip = request.getRemoteAddr();
+ AtomicInteger ai = countMap.get(ip);
+ if (ai == null) {
+ ai = new AtomicInteger(0);
+ countMap.put(ip, ai);
+ }
+ return String.format("欢迎访问黑马商城, 这是您第%d次访问", ai.incrementAndGet());
+ }
+}
diff --git a/hm-service/src/main/java/com/hmall/controller/ItemController.java b/hm-service/src/main/java/com/hmall/controller/ItemController.java
new file mode 100644
index 0000000..40003dd
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/controller/ItemController.java
@@ -0,0 +1,83 @@
+package com.hmall.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.domain.dto.ItemDTO;
+import com.hmall.domain.dto.OrderDetailDTO;
+import com.hmall.domain.po.Item;
+import com.hmall.service.IItemService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+
+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") List 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/hm-service/src/main/java/com/hmall/controller/OrderController.java b/hm-service/src/main/java/com/hmall/controller/OrderController.java
new file mode 100644
index 0000000..55f5b8d
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/controller/OrderController.java
@@ -0,0 +1,39 @@
+package com.hmall.controller;
+
+import com.hmall.common.utils.BeanUtils;
+import com.hmall.domain.dto.OrderFormDTO;
+import com.hmall.domain.vo.OrderVO;
+import com.hmall.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/hm-service/src/main/java/com/hmall/controller/PayController.java b/hm-service/src/main/java/com/hmall/controller/PayController.java
new file mode 100644
index 0000000..9316cdf
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/controller/PayController.java
@@ -0,0 +1,39 @@
+package com.hmall.controller;
+
+import com.hmall.common.exception.BizIllegalException;
+import com.hmall.domain.dto.PayApplyDTO;
+import com.hmall.domain.dto.PayOrderFormDTO;
+import com.hmall.enums.PayType;
+import com.hmall.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/hm-service/src/main/java/com/hmall/controller/SearchController.java b/hm-service/src/main/java/com/hmall/controller/SearchController.java
new file mode 100644
index 0000000..ca21873
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/controller/SearchController.java
@@ -0,0 +1,39 @@
+package com.hmall.controller;
+
+import cn.hutool.core.util.StrUtil;
+import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
+import com.hmall.common.domain.PageDTO;
+import com.hmall.domain.dto.ItemDTO;
+import com.hmall.domain.po.Item;
+import com.hmall.domain.query.ItemPageQuery;
+import com.hmall.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/hm-service/src/main/java/com/hmall/controller/UserController.java b/hm-service/src/main/java/com/hmall/controller/UserController.java
new file mode 100644
index 0000000..d328c9e
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/controller/UserController.java
@@ -0,0 +1,38 @@
+package com.hmall.controller;
+
+import com.hmall.domain.dto.LoginFormDTO;
+import com.hmall.domain.vo.UserLoginVO;
+import com.hmall.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/hm-service/src/main/java/com/hmall/domain/dto/AddressDTO.java b/hm-service/src/main/java/com/hmall/domain/dto/AddressDTO.java
new file mode 100644
index 0000000..8c3a617
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/dto/AddressDTO.java
@@ -0,0 +1,28 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/dto/CartFormDTO.java b/hm-service/src/main/java/com/hmall/domain/dto/CartFormDTO.java
new file mode 100644
index 0000000..28d0f71
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/dto/CartFormDTO.java
@@ -0,0 +1,20 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/dto/ItemDTO.java b/hm-service/src/main/java/com/hmall/domain/dto/ItemDTO.java
new file mode 100644
index 0000000..9a109a1
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/dto/ItemDTO.java
@@ -0,0 +1,34 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/dto/LoginFormDTO.java b/hm-service/src/main/java/com/hmall/domain/dto/LoginFormDTO.java
new file mode 100644
index 0000000..5a048d6
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/dto/LoginFormDTO.java
@@ -0,0 +1,20 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/dto/OrderDetailDTO.java b/hm-service/src/main/java/com/hmall/domain/dto/OrderDetailDTO.java
new file mode 100644
index 0000000..b8f7eed
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/dto/OrderDetailDTO.java
@@ -0,0 +1,16 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/dto/OrderFormDTO.java b/hm-service/src/main/java/com/hmall/domain/dto/OrderFormDTO.java
new file mode 100644
index 0000000..30fed22
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/dto/OrderFormDTO.java
@@ -0,0 +1,18 @@
+package com.hmall.domain.dto;
+
+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/hm-service/src/main/java/com/hmall/domain/dto/PayApplyDTO.java b/hm-service/src/main/java/com/hmall/domain/dto/PayApplyDTO.java
new file mode 100644
index 0000000..36dd256
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/dto/PayApplyDTO.java
@@ -0,0 +1,30 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/dto/PayOrderFormDTO.java b/hm-service/src/main/java/com/hmall/domain/dto/PayOrderFormDTO.java
new file mode 100644
index 0000000..5a4c49b
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/dto/PayOrderFormDTO.java
@@ -0,0 +1,20 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/po/Address.java b/hm-service/src/main/java/com/hmall/domain/po/Address.java
new file mode 100644
index 0000000..89c46d6
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/po/Address.java
@@ -0,0 +1,77 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/po/Cart.java b/hm-service/src/main/java/com/hmall/domain/po/Cart.java
new file mode 100644
index 0000000..a9aaf61
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/po/Cart.java
@@ -0,0 +1,80 @@
+package com.hmall.domain.po;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ *
+ * 订单详情表
+ *
+ *
+ * @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/hm-service/src/main/java/com/hmall/domain/po/Item.java b/hm-service/src/main/java/com/hmall/domain/po/Item.java
new file mode 100644
index 0000000..af468f9
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/po/Item.java
@@ -0,0 +1,113 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/po/Order.java b/hm-service/src/main/java/com/hmall/domain/po/Order.java
new file mode 100644
index 0000000..cc86949
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/po/Order.java
@@ -0,0 +1,91 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/po/OrderDetail.java b/hm-service/src/main/java/com/hmall/domain/po/OrderDetail.java
new file mode 100644
index 0000000..1ff6808
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/po/OrderDetail.java
@@ -0,0 +1,80 @@
+package com.hmall.domain.po;
+
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import java.time.LocalDateTime;
+import java.io.Serializable;
+import lombok.Data;
+import lombok.EqualsAndHashCode;
+import lombok.experimental.Accessors;
+
+/**
+ *
+ * 订单详情表
+ *
+ *
+ * @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/hm-service/src/main/java/com/hmall/domain/po/OrderLogistics.java b/hm-service/src/main/java/com/hmall/domain/po/OrderLogistics.java
new file mode 100644
index 0000000..790b7f7
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/po/OrderLogistics.java
@@ -0,0 +1,86 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/po/PayOrder.java b/hm-service/src/main/java/com/hmall/domain/po/PayOrder.java
new file mode 100644
index 0000000..7e8f40d
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/po/PayOrder.java
@@ -0,0 +1,123 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/po/User.java b/hm-service/src/main/java/com/hmall/domain/po/User.java
new file mode 100644
index 0000000..d45f74f
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/po/User.java
@@ -0,0 +1,66 @@
+package com.hmall.domain.po;
+
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.hmall.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/hm-service/src/main/java/com/hmall/domain/query/ItemPageQuery.java b/hm-service/src/main/java/com/hmall/domain/query/ItemPageQuery.java
new file mode 100644
index 0000000..852e643
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/query/ItemPageQuery.java
@@ -0,0 +1,23 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/vo/CartVO.java b/hm-service/src/main/java/com/hmall/domain/vo/CartVO.java
new file mode 100644
index 0000000..3247323
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/vo/CartVO.java
@@ -0,0 +1,43 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/vo/OrderVO.java b/hm-service/src/main/java/com/hmall/domain/vo/OrderVO.java
new file mode 100644
index 0000000..e4a5ba4
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/vo/OrderVO.java
@@ -0,0 +1,34 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/vo/PayOrderVO.java b/hm-service/src/main/java/com/hmall/domain/vo/PayOrderVO.java
new file mode 100644
index 0000000..c577a55
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/vo/PayOrderVO.java
@@ -0,0 +1,49 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/domain/vo/UserLoginVO.java b/hm-service/src/main/java/com/hmall/domain/vo/UserLoginVO.java
new file mode 100644
index 0000000..563a664
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/domain/vo/UserLoginVO.java
@@ -0,0 +1,11 @@
+package com.hmall.domain.vo;
+
+import lombok.Data;
+
+@Data
+public class UserLoginVO {
+ private String token;
+ private Long userId;
+ private String username;
+ private Integer balance;
+}
diff --git a/hm-service/src/main/java/com/hmall/enums/PayChannel.java b/hm-service/src/main/java/com/hmall/enums/PayChannel.java
new file mode 100644
index 0000000..a0e5f14
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/enums/PayChannel.java
@@ -0,0 +1,25 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/enums/PayStatus.java b/hm-service/src/main/java/com/hmall/enums/PayStatus.java
new file mode 100644
index 0000000..9540d4f
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/enums/PayStatus.java
@@ -0,0 +1,27 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/enums/PayType.java b/hm-service/src/main/java/com/hmall/enums/PayType.java
new file mode 100644
index 0000000..ad8dc6b
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/enums/PayType.java
@@ -0,0 +1,27 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/enums/UserStatus.java b/hm-service/src/main/java/com/hmall/enums/UserStatus.java
new file mode 100644
index 0000000..190d48e
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/enums/UserStatus.java
@@ -0,0 +1,30 @@
+package com.hmall.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/hm-service/src/main/java/com/hmall/interceptor/LoginInterceptor.java b/hm-service/src/main/java/com/hmall/interceptor/LoginInterceptor.java
new file mode 100644
index 0000000..f3fbe24
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/interceptor/LoginInterceptor.java
@@ -0,0 +1,33 @@
+package com.hmall.interceptor;
+
+import com.hmall.common.utils.UserContext;
+import com.hmall.utils.JwtTool;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.servlet.HandlerInterceptor;
+
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+
+@RequiredArgsConstructor
+public class LoginInterceptor implements HandlerInterceptor {
+
+ private final JwtTool jwtTool;
+
+ @Override
+ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
+ // 1.获取请求头中的 token
+ String token = request.getHeader("authorization");
+ // 2.校验token
+ Long userId = jwtTool.parseToken(token);
+ // 3.存入上下文
+ UserContext.setUser(userId);
+ // 4.放行
+ return true;
+ }
+
+ @Override
+ public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
+ // 清理用户
+ UserContext.removeUser();
+ }
+}
diff --git a/hm-service/src/main/java/com/hmall/mapper/AddressMapper.java b/hm-service/src/main/java/com/hmall/mapper/AddressMapper.java
new file mode 100644
index 0000000..09ac39e
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/mapper/AddressMapper.java
@@ -0,0 +1,16 @@
+package com.hmall.mapper;
+
+import com.hmall.domain.po.Address;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ *
+ * Mapper 接口
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface AddressMapper extends BaseMapper {
+
+}
diff --git a/hm-service/src/main/java/com/hmall/mapper/CartMapper.java b/hm-service/src/main/java/com/hmall/mapper/CartMapper.java
new file mode 100644
index 0000000..f4df4e1
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/mapper/CartMapper.java
@@ -0,0 +1,20 @@
+package com.hmall.mapper;
+
+import com.hmall.domain.po.Cart;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+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/hm-service/src/main/java/com/hmall/mapper/ItemMapper.java b/hm-service/src/main/java/com/hmall/mapper/ItemMapper.java
new file mode 100644
index 0000000..4e239a6
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/mapper/ItemMapper.java
@@ -0,0 +1,21 @@
+package com.hmall.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+import com.hmall.domain.dto.OrderDetailDTO;
+import com.hmall.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}")
+ void updateStock(OrderDetailDTO orderDetail);
+}
diff --git a/hm-service/src/main/java/com/hmall/mapper/OrderDetailMapper.java b/hm-service/src/main/java/com/hmall/mapper/OrderDetailMapper.java
new file mode 100644
index 0000000..747fb31
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/mapper/OrderDetailMapper.java
@@ -0,0 +1,16 @@
+package com.hmall.mapper;
+
+import com.hmall.domain.po.OrderDetail;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ *
+ * 订单详情表 Mapper 接口
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface OrderDetailMapper extends BaseMapper {
+
+}
diff --git a/hm-service/src/main/java/com/hmall/mapper/OrderLogisticsMapper.java b/hm-service/src/main/java/com/hmall/mapper/OrderLogisticsMapper.java
new file mode 100644
index 0000000..45cd6af
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/mapper/OrderLogisticsMapper.java
@@ -0,0 +1,16 @@
+package com.hmall.mapper;
+
+import com.hmall.domain.po.OrderLogistics;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ *
+ * Mapper 接口
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface OrderLogisticsMapper extends BaseMapper {
+
+}
diff --git a/hm-service/src/main/java/com/hmall/mapper/OrderMapper.java b/hm-service/src/main/java/com/hmall/mapper/OrderMapper.java
new file mode 100644
index 0000000..478940b
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/mapper/OrderMapper.java
@@ -0,0 +1,16 @@
+package com.hmall.mapper;
+
+import com.hmall.domain.po.Order;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ *
+ * Mapper 接口
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface OrderMapper extends BaseMapper {
+
+}
diff --git a/hm-service/src/main/java/com/hmall/mapper/PayOrderMapper.java b/hm-service/src/main/java/com/hmall/mapper/PayOrderMapper.java
new file mode 100644
index 0000000..f331c37
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/mapper/PayOrderMapper.java
@@ -0,0 +1,16 @@
+package com.hmall.mapper;
+
+import com.hmall.domain.po.PayOrder;
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+
+/**
+ *
+ * 支付订单 Mapper 接口
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-16
+ */
+public interface PayOrderMapper extends BaseMapper {
+
+}
diff --git a/hm-service/src/main/java/com/hmall/mapper/UserMapper.java b/hm-service/src/main/java/com/hmall/mapper/UserMapper.java
new file mode 100644
index 0000000..4ec0724
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/mapper/UserMapper.java
@@ -0,0 +1,19 @@
+package com.hmall.mapper;
+
+import com.baomidou.mybatisplus.core.mapper.BaseMapper;
+import com.hmall.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}")
+ void updateMoney(@Param("userId") Long userId, @Param("totalFee") Integer totalFee);
+}
diff --git a/hm-service/src/main/java/com/hmall/service/IAddressService.java b/hm-service/src/main/java/com/hmall/service/IAddressService.java
new file mode 100644
index 0000000..5cdb4e8
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/IAddressService.java
@@ -0,0 +1,16 @@
+package com.hmall.service;
+
+import com.hmall.domain.po.Address;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ *
+ * 服务类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface IAddressService extends IService {
+
+}
diff --git a/hm-service/src/main/java/com/hmall/service/ICartService.java b/hm-service/src/main/java/com/hmall/service/ICartService.java
new file mode 100644
index 0000000..32c9041
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/ICartService.java
@@ -0,0 +1,26 @@
+package com.hmall.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hmall.domain.dto.CartFormDTO;
+import com.hmall.domain.po.Cart;
+import com.hmall.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/hm-service/src/main/java/com/hmall/service/IItemService.java b/hm-service/src/main/java/com/hmall/service/IItemService.java
new file mode 100644
index 0000000..18514ee
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/IItemService.java
@@ -0,0 +1,24 @@
+package com.hmall.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hmall.domain.dto.ItemDTO;
+import com.hmall.domain.dto.OrderDetailDTO;
+import com.hmall.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/hm-service/src/main/java/com/hmall/service/IOrderDetailService.java b/hm-service/src/main/java/com/hmall/service/IOrderDetailService.java
new file mode 100644
index 0000000..cb25e96
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/IOrderDetailService.java
@@ -0,0 +1,16 @@
+package com.hmall.service;
+
+import com.hmall.domain.po.OrderDetail;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ *
+ * 订单详情表 服务类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface IOrderDetailService extends IService {
+
+}
diff --git a/hm-service/src/main/java/com/hmall/service/IOrderLogisticsService.java b/hm-service/src/main/java/com/hmall/service/IOrderLogisticsService.java
new file mode 100644
index 0000000..c815efb
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/IOrderLogisticsService.java
@@ -0,0 +1,16 @@
+package com.hmall.service;
+
+import com.hmall.domain.po.OrderLogistics;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ *
+ * 服务类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface IOrderLogisticsService extends IService {
+
+}
diff --git a/hm-service/src/main/java/com/hmall/service/IOrderService.java b/hm-service/src/main/java/com/hmall/service/IOrderService.java
new file mode 100644
index 0000000..0249fb3
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/IOrderService.java
@@ -0,0 +1,20 @@
+package com.hmall.service;
+
+import com.hmall.domain.dto.OrderFormDTO;
+import com.hmall.domain.po.Order;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ *
+ * 服务类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+public interface IOrderService extends IService {
+
+ Long createOrder(OrderFormDTO orderFormDTO);
+
+ void markOrderPaySuccess(Long orderId);
+}
diff --git a/hm-service/src/main/java/com/hmall/service/IPayOrderService.java b/hm-service/src/main/java/com/hmall/service/IPayOrderService.java
new file mode 100644
index 0000000..117f6fd
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/IPayOrderService.java
@@ -0,0 +1,21 @@
+package com.hmall.service;
+
+import com.hmall.domain.dto.PayApplyDTO;
+import com.hmall.domain.dto.PayOrderFormDTO;
+import com.hmall.domain.po.PayOrder;
+import com.baomidou.mybatisplus.extension.service.IService;
+
+/**
+ *
+ * 支付订单 服务类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-16
+ */
+public interface IPayOrderService extends IService {
+
+ String applyPayOrder(PayApplyDTO applyDTO);
+
+ void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO);
+}
diff --git a/hm-service/src/main/java/com/hmall/service/IUserService.java b/hm-service/src/main/java/com/hmall/service/IUserService.java
new file mode 100644
index 0000000..d7626f2
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/IUserService.java
@@ -0,0 +1,21 @@
+package com.hmall.service;
+
+import com.baomidou.mybatisplus.extension.service.IService;
+import com.hmall.domain.dto.LoginFormDTO;
+import com.hmall.domain.po.User;
+import com.hmall.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/hm-service/src/main/java/com/hmall/service/impl/AddressServiceImpl.java b/hm-service/src/main/java/com/hmall/service/impl/AddressServiceImpl.java
new file mode 100644
index 0000000..24c6d3e
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/impl/AddressServiceImpl.java
@@ -0,0 +1,20 @@
+package com.hmall.service.impl;
+
+import com.hmall.domain.po.Address;
+import com.hmall.mapper.AddressMapper;
+import com.hmall.service.IAddressService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ *
+ * 服务实现类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Service
+public class AddressServiceImpl extends ServiceImpl implements IAddressService {
+
+}
diff --git a/hm-service/src/main/java/com/hmall/service/impl/CartServiceImpl.java b/hm-service/src/main/java/com/hmall/service/impl/CartServiceImpl.java
new file mode 100644
index 0000000..9fc791a
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/impl/CartServiceImpl.java
@@ -0,0 +1,133 @@
+package com.hmall.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.common.exception.BizIllegalException;
+import com.hmall.common.utils.BeanUtils;
+import com.hmall.common.utils.CollUtils;
+import com.hmall.common.utils.UserContext;
+import com.hmall.domain.dto.CartFormDTO;
+import com.hmall.domain.dto.ItemDTO;
+import com.hmall.domain.po.Cart;
+import com.hmall.domain.vo.CartVO;
+import com.hmall.mapper.CartMapper;
+import com.hmall.service.ICartService;
+import com.hmall.service.IItemService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+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);
+ 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) {
+ long count = lambdaQuery()
+ .eq(Cart::getUserId, userId)
+ .count(); // ← 这里返回 long
+ if (count >= 10) {
+ throw new BizIllegalException(
+ StrUtil.format("用户购物车课程不能超过{}", 10)
+ );
+ }
+ }
+
+ private boolean checkItemExists(Long itemId, Long userId) {
+ long count = lambdaQuery()
+ .eq(Cart::getUserId, userId)
+ .eq(Cart::getItemId, itemId)
+ .count();
+ return count > 0;
+ }
+}
diff --git a/hm-service/src/main/java/com/hmall/service/impl/ItemServiceImpl.java b/hm-service/src/main/java/com/hmall/service/impl/ItemServiceImpl.java
new file mode 100644
index 0000000..60aa6db
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/impl/ItemServiceImpl.java
@@ -0,0 +1,46 @@
+package com.hmall.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.domain.dto.ItemDTO;
+import com.hmall.domain.dto.OrderDetailDTO;
+import com.hmall.domain.po.Item;
+import com.hmall.mapper.ItemMapper;
+import com.hmall.service.IItemService;
+import org.springframework.stereotype.Service;
+
+import java.util.Collection;
+import java.util.List;
+
+/**
+ *
+ * 商品表 服务实现类
+ *
+ *
+ * @author 虎哥
+ */
+@Service
+public class ItemServiceImpl extends ServiceImpl implements IItemService {
+
+ @Override
+ public void deductStock(List items) {
+ String sqlStatement = "com.hmall.item.mapper.ItemMapper.updateStock";
+ boolean r = false;
+ try {
+ r = executeBatch(items, (sqlSession, entity) -> sqlSession.update(sqlStatement, entity));
+ } catch (Exception e) {
+ throw new BizIllegalException("更新库存异常,可能是库存不足!", e);
+ }
+ if (!r) {
+ throw new BizIllegalException("库存不足!");
+ }
+ }
+
+ @Override
+ public List queryItemByIds(Collection ids) {
+ return BeanUtils.copyList(listByIds(ids), ItemDTO.class);
+ }
+}
diff --git a/hm-service/src/main/java/com/hmall/service/impl/OrderDetailServiceImpl.java b/hm-service/src/main/java/com/hmall/service/impl/OrderDetailServiceImpl.java
new file mode 100644
index 0000000..13bd1d9
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/impl/OrderDetailServiceImpl.java
@@ -0,0 +1,20 @@
+package com.hmall.service.impl;
+
+import com.hmall.domain.po.OrderDetail;
+import com.hmall.mapper.OrderDetailMapper;
+import com.hmall.service.IOrderDetailService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ *
+ * 订单详情表 服务实现类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Service
+public class OrderDetailServiceImpl extends ServiceImpl implements IOrderDetailService {
+
+}
diff --git a/hm-service/src/main/java/com/hmall/service/impl/OrderLogisticsServiceImpl.java b/hm-service/src/main/java/com/hmall/service/impl/OrderLogisticsServiceImpl.java
new file mode 100644
index 0000000..e8cd888
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/impl/OrderLogisticsServiceImpl.java
@@ -0,0 +1,20 @@
+package com.hmall.service.impl;
+
+import com.hmall.domain.po.OrderLogistics;
+import com.hmall.mapper.OrderLogisticsMapper;
+import com.hmall.service.IOrderLogisticsService;
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import org.springframework.stereotype.Service;
+
+/**
+ *
+ * 服务实现类
+ *
+ *
+ * @author 虎哥
+ * @since 2023-05-05
+ */
+@Service
+public class OrderLogisticsServiceImpl extends ServiceImpl implements IOrderLogisticsService {
+
+}
diff --git a/hm-service/src/main/java/com/hmall/service/impl/OrderServiceImpl.java b/hm-service/src/main/java/com/hmall/service/impl/OrderServiceImpl.java
new file mode 100644
index 0000000..c2e3411
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/impl/OrderServiceImpl.java
@@ -0,0 +1,112 @@
+package com.hmall.service.impl;
+
+import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
+import com.hmall.common.exception.BadRequestException;
+import com.hmall.common.utils.UserContext;
+import com.hmall.domain.dto.ItemDTO;
+import com.hmall.domain.dto.OrderDetailDTO;
+import com.hmall.domain.dto.OrderFormDTO;
+import com.hmall.domain.po.Order;
+import com.hmall.domain.po.OrderDetail;
+import com.hmall.mapper.OrderMapper;
+import com.hmall.service.ICartService;
+import com.hmall.service.IItemService;
+import com.hmall.service.IOrderDetailService;
+import com.hmall.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 IOrderDetailService detailService;
+ private final ICartService cartService;
+
+ @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 = itemService.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.清理购物车商品
+ cartService.removeByItemIds(itemIds);
+
+ // 4.扣减库存
+ try {
+ itemService.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/hm-service/src/main/java/com/hmall/service/impl/PayOrderServiceImpl.java b/hm-service/src/main/java/com/hmall/service/impl/PayOrderServiceImpl.java
new file mode 100644
index 0000000..5048e02
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/impl/PayOrderServiceImpl.java
@@ -0,0 +1,133 @@
+package com.hmall.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.common.exception.BizIllegalException;
+import com.hmall.common.utils.BeanUtils;
+import com.hmall.common.utils.UserContext;
+import com.hmall.domain.dto.PayApplyDTO;
+import com.hmall.domain.dto.PayOrderFormDTO;
+import com.hmall.domain.po.Order;
+import com.hmall.domain.po.PayOrder;
+import com.hmall.enums.PayStatus;
+import com.hmall.mapper.PayOrderMapper;
+import com.hmall.service.IOrderService;
+import com.hmall.service.IPayOrderService;
+import com.hmall.service.IUserService;
+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 IUserService userService;
+
+ private final IOrderService orderService;
+
+ @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.尝试扣减余额
+ userService.deductMoney(payOrderFormDTO.getPw(), po.getAmount());
+ // 4.修改支付单状态
+ boolean success = markPayOrderSuccess(payOrderFormDTO.getId(), LocalDateTime.now());
+ if (!success) {
+ throw new BizIllegalException("交易已支付或关闭!");
+ }
+ // 5.修改订单状态
+ Order order = new Order();
+ order.setId(po.getBizOrderNo());
+ order.setStatus(2);
+ order.setPayTime(LocalDateTime.now());
+ orderService.updateById(order);
+ }
+
+ 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/hm-service/src/main/java/com/hmall/service/impl/UserServiceImpl.java b/hm-service/src/main/java/com/hmall/service/impl/UserServiceImpl.java
new file mode 100644
index 0000000..57df208
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/service/impl/UserServiceImpl.java
@@ -0,0 +1,85 @@
+package com.hmall.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.config.JwtProperties;
+import com.hmall.domain.dto.LoginFormDTO;
+import com.hmall.domain.po.User;
+import com.hmall.domain.vo.UserLoginVO;
+import com.hmall.enums.UserStatus;
+import com.hmall.mapper.UserMapper;
+import com.hmall.service.IUserService;
+import com.hmall.utils.JwtTool;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+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;
+ }
+
+ @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.尝试扣款
+ try {
+ baseMapper.updateMoney(UserContext.getUser(), totalFee);
+ } catch (Exception e) {
+ throw new RuntimeException("扣款失败,可能是余额不足!", e);
+ }
+ log.info("扣款成功");
+ }
+}
diff --git a/hm-service/src/main/java/com/hmall/utils/JwtTool.java b/hm-service/src/main/java/com/hmall/utils/JwtTool.java
new file mode 100644
index 0000000..e321d04
--- /dev/null
+++ b/hm-service/src/main/java/com/hmall/utils/JwtTool.java
@@ -0,0 +1,82 @@
+package com.hmall.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 userDTO 用户信息
+ * @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-service/src/main/resources/application-dev.yaml b/hm-service/src/main/resources/application-dev.yaml
new file mode 100644
index 0000000..a6fa71e
--- /dev/null
+++ b/hm-service/src/main/resources/application-dev.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: mysql
+ pw: 123
\ No newline at end of file
diff --git a/hm-service/src/main/resources/application-local.yaml b/hm-service/src/main/resources/application-local.yaml
new file mode 100644
index 0000000..707fb24
--- /dev/null
+++ b/hm-service/src/main/resources/application-local.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: localhost # 修改为你自己的虚拟机IP地址
+ pw: 123456 # 修改为docker中的MySQL密码
\ No newline at end of file
diff --git a/hm-service/src/main/resources/application.yaml b/hm-service/src/main/resources/application.yaml
new file mode 100644
index 0000000..cac3313
--- /dev/null
+++ b/hm-service/src/main/resources/application.yaml
@@ -0,0 +1,54 @@
+server:
+ port: 8080
+spring:
+ application:
+ name: hm-service
+ profiles:
+ active: local
+ datasource:
+ url: jdbc:mysql://${hm.db.host}:3306/hmall?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: zhanghuyi@itcast.cn
+ concat: 虎哥
+ url: https://www.itcast.cn
+ version: v1.0.0
+ group:
+ default:
+ group-name: default
+ api-rule: package
+ api-rule-resources:
+ - com.hmall.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/hm-service/src/main/resources/hmall.jks b/hm-service/src/main/resources/hmall.jks
new file mode 100644
index 0000000..a34bd33
Binary files /dev/null and b/hm-service/src/main/resources/hmall.jks differ
diff --git a/hm-service/src/main/resources/mapper/CartMapper.xml b/hm-service/src/main/resources/mapper/CartMapper.xml
new file mode 100644
index 0000000..f22854a
--- /dev/null
+++ b/hm-service/src/main/resources/mapper/CartMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/hm-service/src/main/resources/mapper/ItemMapper.xml b/hm-service/src/main/resources/mapper/ItemMapper.xml
new file mode 100644
index 0000000..95597f1
--- /dev/null
+++ b/hm-service/src/main/resources/mapper/ItemMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/hm-service/src/main/resources/mapper/OrderDetailMapper.xml b/hm-service/src/main/resources/mapper/OrderDetailMapper.xml
new file mode 100644
index 0000000..c79277d
--- /dev/null
+++ b/hm-service/src/main/resources/mapper/OrderDetailMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/hm-service/src/main/resources/mapper/OrderLogisticsMapper.xml b/hm-service/src/main/resources/mapper/OrderLogisticsMapper.xml
new file mode 100644
index 0000000..bb4feb1
--- /dev/null
+++ b/hm-service/src/main/resources/mapper/OrderLogisticsMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/hm-service/src/main/resources/mapper/OrderMapper.xml b/hm-service/src/main/resources/mapper/OrderMapper.xml
new file mode 100644
index 0000000..a7969a5
--- /dev/null
+++ b/hm-service/src/main/resources/mapper/OrderMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/hm-service/src/main/resources/mapper/PayOrderMapper.xml b/hm-service/src/main/resources/mapper/PayOrderMapper.xml
new file mode 100644
index 0000000..9e40c40
--- /dev/null
+++ b/hm-service/src/main/resources/mapper/PayOrderMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/hm-service/src/main/resources/mapper/TradeClient.xml b/hm-service/src/main/resources/mapper/TradeClient.xml
new file mode 100644
index 0000000..28c06ab
--- /dev/null
+++ b/hm-service/src/main/resources/mapper/TradeClient.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/hm-service/src/main/resources/mapper/UserMapper.xml b/hm-service/src/main/resources/mapper/UserMapper.xml
new file mode 100644
index 0000000..e885a29
--- /dev/null
+++ b/hm-service/src/main/resources/mapper/UserMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/item-service/pom.xml b/item-service/pom.xml
new file mode 100644
index 0000000..eb305e1
--- /dev/null
+++ b/item-service/pom.xml
@@ -0,0 +1,58 @@
+
+
+
+ hmall
+ com.heima
+ 1.0.0
+
+ 4.0.0
+
+ item-service
+
+
+ 17
+ 17
+
+
+
+
+ com.heima
+ hm-common
+ 1.0.0
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+
+ mysql
+ mysql-connector-java
+
+
+
+ com.baomidou
+ mybatis-plus-boot-starter
+
+
+ com.heima
+ hm-service
+ 1.0.0
+ compile
+
+
+
+
+ ${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/ItemApplication.java b/item-service/src/main/java/com/hmall/item/ItemApplication.java
new file mode 100644
index 0000000..e76b736
--- /dev/null
+++ b/item-service/src/main/java/com/hmall/item/ItemApplication.java
@@ -0,0 +1,13 @@
+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 ItemApplication {
+ public static void main(String[] args) {
+ SpringApplication.run(ItemApplication.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..f1d3c4a
--- /dev/null
+++ b/item-service/src/main/java/com/hmall/item/controller/ItemController.java
@@ -0,0 +1,82 @@
+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.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") List 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..179ff7a
--- /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.po.Item;
+import com.hmall.item.domain.dto.OrderDetailDTO;
+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}")
+ void 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..dfe6a9e
--- /dev/null
+++ b/item-service/src/main/java/com/hmall/item/service/IItemService.java
@@ -0,0 +1,24 @@
+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..f7fcc1d
--- /dev/null
+++ b/item-service/src/main/java/com/hmall/item/service/impl/ItemServiceImpl.java
@@ -0,0 +1,46 @@
+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 java.util.Collection;
+import java.util.List;
+
+/**
+ *
+ * 商品表 服务实现类
+ *
+ *
+ * @author 虎哥
+ */
+@Service
+public class ItemServiceImpl extends ServiceImpl implements IItemService {
+
+ @Override
+ public void deductStock(List items) {
+ String sqlStatement = "com.hmall.item.mapper.ItemMapper.updateStock";
+ boolean r = false;
+ try {
+ r = executeBatch(items, (sqlSession, entity) -> sqlSession.update(sqlStatement, entity));
+ } catch (Exception e) {
+ throw new BizIllegalException("更新库存异常,可能是库存不足!", e);
+ }
+ if (!r) {
+ 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..a6fa71e
--- /dev/null
+++ b/item-service/src/main/resources/application-dev.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: mysql
+ pw: 123
\ 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..707fb24
--- /dev/null
+++ b/item-service/src/main/resources/application-local.yaml
@@ -0,0 +1,4 @@
+hm:
+ db:
+ host: localhost # 修改为你自己的虚拟机IP地址
+ pw: 123456 # 修改为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..b564328
--- /dev/null
+++ b/item-service/src/main/resources/application.yaml
@@ -0,0 +1,43 @@
+server:
+ port: 8081
+spring:
+ application:
+ name: item-service
+ profiles:
+ active: local
+ 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: zhangsan@itcast.cn
+ concat: 宇哥
+ url: https://www.itcast.cn
+ version: v1.0.0
+ group:
+ default:
+ group-name: default
+ api-rule: package
+ api-rule-resources:
+ - com.hmall.item.controller
+
+# 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..95597f1
--- /dev/null
+++ b/item-service/src/main/resources/mapper/ItemMapper.xml
@@ -0,0 +1,5 @@
+
+
+
+
+
diff --git a/logs/hm-service/spring.log.2024-06-01.0.gz b/logs/hm-service/spring.log.2024-06-01.0.gz
new file mode 100644
index 0000000..c0148e9
Binary files /dev/null and b/logs/hm-service/spring.log.2024-06-01.0.gz differ
diff --git a/logs/hm-service/spring.log.2025-05-19.0.gz b/logs/hm-service/spring.log.2025-05-19.0.gz
new file mode 100644
index 0000000..2bab01b
Binary files /dev/null and b/logs/hm-service/spring.log.2025-05-19.0.gz differ
diff --git a/pom.xml b/pom.xml
new file mode 100644
index 0000000..cb6c1c0
--- /dev/null
+++ b/pom.xml
@@ -0,0 +1,108 @@
+
+
+ 4.0.0
+
+ com.heima
+ hmall
+ pom
+ 1.0.0
+
+ hm-common
+ hm-service
+ item-service
+ cart-service
+ user-service
+
+
+
+ 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.5.2
+ 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
diff --git a/user-service/pom.xml b/user-service/pom.xml
new file mode 100644
index 0000000..4629c82
--- /dev/null
+++ b/user-service/pom.xml
@@ -0,0 +1,19 @@
+
+
+
+ hmall
+ com.heima
+ 1.0.0
+
+ 4.0.0
+
+ user-service
+
+
+ 17
+ 17
+
+
+
\ No newline at end of file