5.22 拆分交易、支付

This commit is contained in:
zhangsan 2025-05-22 16:32:45 +08:00
parent 9bd4c9beaf
commit 5698a1d597
59 changed files with 2071 additions and 45 deletions

View File

@ -44,6 +44,7 @@ public class CartController {
public List<CartVO> queryMyCarts(){
return cartService.queryMyCarts();
}
@ApiOperation("批量删除购物车中商品")
@ApiImplicitParam(name = "ids", value = "购物车条目id集合")
@DeleteMapping

View File

@ -6,7 +6,7 @@ spring:
profiles:
active: local
datasource:
url: jdbc:mysql://${hm.db.host}:3306/hm-cart?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
url: jdbc:mysql://${hm.db.host}:3307/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}

View File

@ -0,0 +1,13 @@
package com.hmall.api.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
@FeignClient("cart-service")
public interface CartClient {
@DeleteMapping("/carts")
void deleteCartItemByIds(@RequestParam("ids") Collection<Long> ids);
}

View File

@ -1,8 +1,11 @@
package com.hmall.api.client;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.Collection;
@ -13,4 +16,6 @@ public interface ItemClient {
@GetMapping("/items")
List<ItemDTO> queryItemByIds(@RequestParam("ids") Collection<Long> ids);
@PutMapping("/items/stock/deduct")
void deductStock(@RequestBody List<OrderDetailDTO> items);
}

View File

@ -0,0 +1,11 @@
package com.hmall.api.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
@FeignClient("trade-service")
public interface TradeClient {
@PutMapping("/orders/{orderId}")
void markOrderPaySuccess(@PathVariable("orderId") Long orderId);
}

View File

@ -0,0 +1,11 @@
package com.hmall.api.client;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestParam;
@FeignClient("user-service")
public interface UserClient {
@PutMapping("/users/money/deduct")
void deductMoney(@RequestParam("pw") String pw,@RequestParam("amount") Integer amount);
}

View File

@ -1,4 +1,4 @@
package com.hmall.item.domain.dto;
package com.hmall.api.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;

View File

@ -6,7 +6,7 @@ spring:
profiles:
active: local
datasource:
url: jdbc:mysql://${hm.db.host}:3306/hmall?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
url: jdbc:mysql://${hm.db.host}:3307/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}

View File

@ -16,6 +16,11 @@
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-api</artifactId>
<version>1.0.0</version>
</dependency>
<!--common-->
<dependency>
<groupId>com.heima</groupId>

View File

@ -1,10 +1,10 @@
package com.hmall.item.controller;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import com.hmall.common.domain.PageDTO;
import com.hmall.common.domain.PageQuery;
import com.hmall.common.utils.BeanUtils;
import com.hmall.item.domain.dto.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;

View File

@ -1,34 +0,0 @@
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;
}

View File

@ -1,8 +1,8 @@
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.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import com.hmall.item.domain.po.Item;
import java.util.Collection;

View File

@ -2,11 +2,11 @@ package com.hmall.item.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
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;

View File

@ -10,7 +10,7 @@ spring:
profiles:
active: local
datasource:
url: jdbc:mysql://${hm.db.host}:3306/hm-item?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
url: jdbc:mysql://${hm.db.host}:3307/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}

67
pay-service/pom.xml Normal file
View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>hmall</artifactId>
<groupId>com.heima</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>pay-service</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-api</artifactId>
<version>1.0.0</version>
</dependency>
<!--common-->
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-common</artifactId>
<version>1.0.0</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-service</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,16 @@
package com.hmall.pay;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@MapperScan("com.hmall.pay.mapper")
@EnableFeignClients(basePackages= "com.hmall.api.client")
@SpringBootApplication
public class PayApplication {
public static void main(String[] args) {
SpringApplication.run(PayApplication.class, args);
}
}

View File

@ -0,0 +1,49 @@
package com.hmall.pay.controller;
import com.hmall.common.exception.BizIllegalException;
import com.hmall.common.utils.BeanUtils;
import com.hmall.pay.domain.dto.PayApplyDTO;
import com.hmall.pay.domain.dto.PayOrderFormDTO;
import com.hmall.pay.domain.vo.PayOrderVO;
import com.hmall.pay.enums.PayType;
import com.hmall.pay.service.IPayOrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@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("查询支付单")
@GetMapping
public List<PayOrderVO> queryPayOrders(){
return BeanUtils.copyList(payOrderService.list(), PayOrderVO.class);
}
@ApiOperation("尝试基于用户余额支付")
@ApiImplicitParam(value = "支付单id", name = "id")
@PostMapping("{id}")
public void tryPayOrderByBalance(@PathVariable("id") Long id, @RequestBody PayOrderFormDTO payOrderFormDTO){
payOrderFormDTO.setId(id);
payOrderService.tryPayOrderByBalance(payOrderFormDTO);
}
}

View File

@ -0,0 +1,30 @@
package com.hmall.pay.domain.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
@Data
@Builder
@ApiModel(description = "支付下单表单实体")
public class PayApplyDTO {
@ApiModelProperty("业务订单id不能为空")
@NotNull(message = "业务订单id不能为空")
private Long bizOrderNo;
@ApiModelProperty("支付金额必须为正数")
@Min(value = 1, message = "支付金额必须为正数")
private Integer amount;
@ApiModelProperty("支付渠道编码不能为空")
@NotNull(message = "支付渠道编码不能为空")
private String payChannelCode;
@ApiModelProperty("支付方式不能为空")
@NotNull(message = "支付方式不能为空")
private Integer payType;
@ApiModelProperty("订单中的商品信息不能为空")
@NotNull(message = "订单中的商品信息不能为空")
private String orderInfo;
}

View File

@ -0,0 +1,20 @@
package com.hmall.pay.domain.dto;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Builder;
import lombok.Data;
import javax.validation.constraints.NotNull;
@Data
@Builder
@ApiModel(description = "支付确认表单实体")
public class PayOrderFormDTO {
@ApiModelProperty("支付订单id不能为空")
@NotNull(message = "支付订单id不能为空")
private Long id;
@ApiModelProperty("支付密码")
@NotNull(message = "支付密码")
private String pw;
}

View File

@ -0,0 +1,123 @@
package com.hmall.pay.domain.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 支付订单
* </p>
*/
@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;
/**
* 支付类型1h5,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;
}

View File

@ -0,0 +1,77 @@
package com.hmall.pay.domain.po.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;
/**
* <p>
*
* </p>
*
* @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;
}

View File

@ -0,0 +1,81 @@
package com.hmall.pay.domain.po.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;
/**
* <p>
* 订单详情表
* </p>
*
* @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;
}

View File

@ -0,0 +1,113 @@
package com.hmall.pay.domain.po.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;
/**
* <p>
* 商品表
* </p>
*
* @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;
}

View File

@ -0,0 +1,91 @@
package com.hmall.pay.domain.po.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;
/**
* <p>
*
* </p>
*
* @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;
}

View File

@ -0,0 +1,81 @@
package com.hmall.pay.domain.po.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;
/**
* <p>
* 订单详情表
* </p>
*
* @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;
}

View File

@ -0,0 +1,86 @@
package com.hmall.pay.domain.po.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;
/**
* <p>
*
* </p>
*
* @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;
}

View File

@ -0,0 +1,66 @@
package com.hmall.pay.domain.po.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;
/**
* <p>
* 用户表
* </p>
*
* @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;
}

View File

@ -0,0 +1,49 @@
package com.hmall.pay.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
/**
* <p>
* 支付订单
* </p>
*/
@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("付类型1h5,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;
}

View File

@ -0,0 +1,25 @@
package com.hmall.pay.enums;
import cn.hutool.core.util.StrUtil;
import lombok.Getter;
@Getter
public enum PayChannel {
wxPay("微信支付"),
aliPay("支付宝支付"),
balance("余额支付"),
;
private final String desc;
PayChannel(String desc) {
this.desc = desc;
}
public static String desc(String value){
if (StrUtil.isBlank(value)) {
return "";
}
return PayChannel.valueOf(value).getDesc();
}
}

View File

@ -0,0 +1,27 @@
package com.hmall.pay.enums;
import lombok.Getter;
@Getter
public enum PayStatus {
NOT_COMMIT(0, "未提交"),
WAIT_BUYER_PAY(1, "待支付"),
TRADE_CLOSED(2, "已关闭"),
TRADE_SUCCESS(3, "支付成功"),
TRADE_FINISHED(3, "支付成功"),
;
private final int value;
private final String desc;
PayStatus(int value, String desc) {
this.value = value;
this.desc = desc;
}
public boolean equalsValue(Integer value){
if (value == null) {
return false;
}
return getValue() == value;
}
}

View File

@ -0,0 +1,27 @@
package com.hmall.pay.enums;
import lombok.Getter;
@Getter
public enum PayType{
JSAPI(1, "网页支付JS"),
MINI_APP(2, "小程序支付"),
APP(3, "APP支付"),
NATIVE(4, "扫码支付"),
BALANCE(5, "余额支付"),
;
private final int value;
private final String desc;
PayType(int value, String desc) {
this.value = value;
this.desc = desc;
}
public boolean equalsValue(Integer value){
if (value == null) {
return false;
}
return getValue() == value;
}
}

View File

@ -0,0 +1,17 @@
package com.hmall.pay.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hmall.pay.domain.po.PayOrder;
/**
* <p>
* 支付订单 Mapper 接口
* </p>
*
* @author 虎哥
* @since 2023-05-16
*/
public interface PayOrderMapper extends BaseMapper<PayOrder> {
}

View File

@ -0,0 +1,22 @@
package com.hmall.pay.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hmall.pay.domain.dto.PayApplyDTO;
import com.hmall.pay.domain.dto.PayOrderFormDTO;
import com.hmall.pay.domain.po.PayOrder;
/**
* <p>
* 支付订单 服务类
* </p>
*
* @author 虎哥
* @since 2023-05-16
*/
public interface IPayOrderService extends IService<PayOrder> {
String applyPayOrder(PayApplyDTO applyDTO);
void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO);
}

View File

@ -0,0 +1,131 @@
package com.hmall.pay.service.impl;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.core.toolkit.StringUtils;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.api.client.TradeClient;
import com.hmall.api.client.UserClient;
import com.hmall.common.exception.BizIllegalException;
import com.hmall.common.utils.BeanUtils;
import com.hmall.common.utils.UserContext;
import com.hmall.enums.PayStatus;
import com.hmall.pay.domain.dto.PayApplyDTO;
import com.hmall.pay.domain.dto.PayOrderFormDTO;
import com.hmall.pay.domain.po.PayOrder;
import com.hmall.pay.mapper.PayOrderMapper;
import com.hmall.pay.service.IPayOrderService;
import com.hmall.service.IOrderService;
import com.hmall.service.IUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
/**
* <p>
* 支付订单 服务实现类
* </p>
*
* @author 虎哥
* @since 2023-05-16
*/
@Service
@RequiredArgsConstructor
public class PayOrderServiceImpl extends ServiceImpl<PayOrderMapper, PayOrder> implements IPayOrderService {
private final UserClient userClient;
private final TradeClient tradeClient;
@Override
public String applyPayOrder(PayApplyDTO applyDTO) {
// 1.幂等性校验
PayOrder payOrder = checkIdempotent(applyDTO);
// 2.返回结果
return payOrder.getId().toString();
}
@Override
@Transactional
public void tryPayOrderByBalance(PayOrderFormDTO payOrderFormDTO) {
// 1.查询支付单
PayOrder po = getById(payOrderFormDTO.getId());
// 2.判断状态
if(!PayStatus.WAIT_BUYER_PAY.equalsValue(po.getStatus())){
// 订单不是未支付状态异常
throw new BizIllegalException("交易已支付或关闭!");
}
// 3.尝试扣减余额
userClient.deductMoney(payOrderFormDTO.getPw(), po.getAmount());
// 4.修改支付单状态
boolean success = markPayOrderSuccess(payOrderFormDTO.getId(), LocalDateTime.now());
if (!success) {
throw new BizIllegalException("交易已支付或关闭!");
}
// 5.修改订单状态
tradeClient.markOrderPaySuccess(po.getBizOrderNo());
}
public boolean markPayOrderSuccess(Long id, LocalDateTime successTime) {
return lambdaUpdate()
.set(PayOrder::getStatus, PayStatus.TRADE_SUCCESS.getValue())
.set(PayOrder::getPaySuccessTime, successTime)
.eq(PayOrder::getId, id)
// 支付状态的乐观锁判断
.in(PayOrder::getStatus, PayStatus.NOT_COMMIT.getValue(), PayStatus.WAIT_BUYER_PAY.getValue())
.update();
}
private PayOrder checkIdempotent(PayApplyDTO applyDTO) {
// 1.首先查询支付单
PayOrder oldOrder = queryByBizOrderNo(applyDTO.getBizOrderNo());
// 2.判断是否存在
if (oldOrder == null) {
// 不存在支付单说明是第一次写入新的支付单并返回
PayOrder payOrder = buildPayOrder(applyDTO);
payOrder.setPayOrderNo(IdWorker.getId());
save(payOrder);
return payOrder;
}
// 3.旧单已经存在判断是否支付成功
if (PayStatus.TRADE_SUCCESS.equalsValue(oldOrder.getStatus())) {
// 已经支付成功抛出异常
throw new BizIllegalException("订单已经支付!");
}
// 4.旧单已经存在判断是否已经关闭
if (PayStatus.TRADE_CLOSED.equalsValue(oldOrder.getStatus())) {
// 已经关闭抛出异常
throw new BizIllegalException("订单已关闭");
}
// 5.旧单已经存在判断支付渠道是否一致
if (!StringUtils.equals(oldOrder.getPayChannelCode(), applyDTO.getPayChannelCode())) {
// 支付渠道不一致需要重置数据然后重新申请支付单
PayOrder payOrder = buildPayOrder(applyDTO);
payOrder.setId(oldOrder.getId());
payOrder.setQrCodeUrl("");
updateById(payOrder);
payOrder.setPayOrderNo(oldOrder.getPayOrderNo());
return payOrder;
}
// 6.旧单已经存在且可能是未支付或未提交且支付渠道一致直接返回旧数据
return oldOrder;
}
private PayOrder buildPayOrder(PayApplyDTO payApplyDTO) {
// 1.数据转换
PayOrder payOrder = BeanUtils.toBean(payApplyDTO, PayOrder.class);
// 2.初始化数据
payOrder.setPayOverTime(LocalDateTime.now().plusMinutes(120L));
payOrder.setStatus(PayStatus.WAIT_BUYER_PAY.getValue());
payOrder.setBizUserId(UserContext.getUser());
return payOrder;
}
public PayOrder queryByBizOrderNo(Long bizOrderNo) {
return lambdaQuery()
.eq(PayOrder::getBizOrderNo, bizOrderNo)
.one();
}
}

View File

@ -0,0 +1,4 @@
hm:
db:
host: mysql
pw: 123

View File

@ -0,0 +1,4 @@
hm:
db:
host: 124.71.159.195 # 修改为你自己的虚拟机IP地址
pw: 123456 # 修改为docker中的MySQL密码

View File

@ -0,0 +1,51 @@
server:
port: 8086
spring:
application:
name: pay-service
cloud:
nacos:
server-addr: 124.71.159.195:8848 # nacos地址
profiles:
active: local
datasource:
url: jdbc:mysql://${hm.db.host}:3307/hm-pay?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: ${hm.db.pw}
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.pay.controller
feign:
httpclient:
enabled: true # 使用 Apache HttpClient默认关闭
# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123

View File

@ -15,6 +15,8 @@
<module>cart-service</module>
<module>user-service</module>
<module>hm-api</module>
<module>trade-service</module>
<module>pay-service</module>
</modules>
<parent>

67
trade-service/pom.xml Normal file
View File

@ -0,0 +1,67 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>hmall</artifactId>
<groupId>com.heima</groupId>
<version>1.0.0</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>trade-service</artifactId>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-api</artifactId>
<version>1.0.0</version>
</dependency>
<!--common-->
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-common</artifactId>
<version>1.0.0</version>
</dependency>
<!--web-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--数据库-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>com.heima</groupId>
<artifactId>hm-service</artifactId>
<version>1.0.0</version>
<scope>compile</scope>
</dependency>
<!--nacos 服务注册发现-->
<dependency>
<groupId>com.alibaba.cloud</groupId>
<artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>

View File

@ -0,0 +1,16 @@
package com.hmall.trade;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
@MapperScan("com.hmall.trade.mapper")
@EnableFeignClients(basePackages= "com.hmall.api.client")
@SpringBootApplication
public class TradeApplication {
public static void main(String[] args) {
SpringApplication.run(TradeApplication.class, args);
}
}

View File

@ -0,0 +1,40 @@
package com.hmall.trade.controller;
import com.hmall.common.utils.BeanUtils;
import com.hmall.trade.domain.dto.OrderFormDTO;
import com.hmall.trade.domain.vo.OrderVO;
import com.hmall.trade.service.IOrderService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiOperation;
import lombok.RequiredArgsConstructor;
import org.apache.ibatis.annotations.Param;
import org.springframework.web.bind.annotation.*;
@Api(tags = "订单管理接口")
@RestController
@RequestMapping("/orders")
@RequiredArgsConstructor
public class OrderController {
private final IOrderService orderService;
@ApiOperation("根据id查询订单")
@GetMapping("{id}")
public OrderVO queryOrderById(@Param ("订单id")@PathVariable("id") Long orderId) {
return BeanUtils.copyBean(orderService.getById(orderId), OrderVO.class);
}
@ApiOperation("创建订单")
@PostMapping
public Long createOrder(@RequestBody OrderFormDTO orderFormDTO){
return orderService.createOrder(orderFormDTO);
}
@ApiOperation("标记订单已支付")
@ApiImplicitParam(name = "orderId", value = "订单id", paramType = "path")
@PutMapping("/{orderId}")
public void markOrderPaySuccess(@PathVariable("orderId") Long orderId) {
orderService.markOrderPaySuccess(orderId);
}
}

View File

@ -0,0 +1,19 @@
package com.hmall.trade.domain.dto;
import com.hmall.api.dto.OrderDetailDTO;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.util.List;
@Data
@ApiModel(description = "交易下单表单实体")
public class OrderFormDTO {
@ApiModelProperty("收货地址id")
private Long addressId;
@ApiModelProperty("支付类型")
private Integer paymentType;
@ApiModelProperty("下单商品列表")
private List<OrderDetailDTO> details;
}

View File

@ -0,0 +1,91 @@
package com.hmall.trade.domain.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
*
* </p>
*
* @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;
}

View File

@ -0,0 +1,81 @@
package com.hmall.trade.domain.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
* 订单详情表
* </p>
*
* @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;
}

View File

@ -0,0 +1,86 @@
package com.hmall.trade.domain.po;
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.experimental.Accessors;
import java.io.Serializable;
import java.time.LocalDateTime;
/**
* <p>
*
* </p>
*
* @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;
}

View File

@ -0,0 +1,34 @@
package com.hmall.trade.domain.vo;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.time.LocalDateTime;
@Data
@ApiModel(description = "订单页面VO")
public class OrderVO {
@ApiModelProperty("订单id")
private Long id;
@ApiModelProperty("总金额,单位为分")
private Integer totalFee;
@ApiModelProperty("支付类型1、支付宝2、微信3、扣减余额")
private Integer paymentType;
@ApiModelProperty("用户id")
private Long userId;
@ApiModelProperty("订单的状态1、未付款 2、已付款,未发货 3、已发货,未确认 4、确认收货交易成功 5、交易取消订单关闭 6、交易结束已评价")
private Integer status;
@ApiModelProperty("创建时间")
private LocalDateTime createTime;
@ApiModelProperty("支付时间")
private LocalDateTime payTime;
@ApiModelProperty("发货时间")
private LocalDateTime consignTime;
@ApiModelProperty("交易完成时间")
private LocalDateTime endTime;
@ApiModelProperty("交易关闭时间")
private LocalDateTime closeTime;
@ApiModelProperty("评价时间")
private LocalDateTime commentTime;
}

View File

@ -0,0 +1,17 @@
package com.hmall.trade.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hmall.trade.domain.po.OrderDetail;
/**
* <p>
* 订单详情表 Mapper 接口
* </p>
*
* @author 虎哥
* @since 2023-05-05
*/
public interface OrderDetailMapper extends BaseMapper<OrderDetail> {
}

View File

@ -0,0 +1,17 @@
package com.hmall.trade.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hmall.trade.domain.po.OrderLogistics;
/**
* <p>
* Mapper 接口
* </p>
*
* @author 虎哥
* @since 2023-05-05
*/
public interface OrderLogisticsMapper extends BaseMapper<OrderLogistics> {
}

View File

@ -0,0 +1,17 @@
package com.hmall.trade.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.hmall.trade.domain.po.Order;
/**
* <p>
* Mapper 接口
* </p>
*
* @author 虎哥
* @since 2023-05-05
*/
public interface OrderMapper extends BaseMapper<Order> {
}

View File

@ -0,0 +1,17 @@
package com.hmall.trade.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hmall.trade.domain.po.OrderDetail;
/**
* <p>
* 订单详情表 服务类
* </p>
*
* @author 虎哥
* @since 2023-05-05
*/
public interface IOrderDetailService extends IService<OrderDetail> {
}

View File

@ -0,0 +1,17 @@
package com.hmall.trade.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hmall.trade.domain.po.OrderLogistics;
/**
* <p>
* 服务类
* </p>
*
* @author 虎哥
* @since 2023-05-05
*/
public interface IOrderLogisticsService extends IService<OrderLogistics> {
}

View File

@ -0,0 +1,21 @@
package com.hmall.trade.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.hmall.trade.domain.dto.OrderFormDTO;
import com.hmall.trade.domain.po.Order;
/**
* <p>
* 服务类
* </p>
*
* @author 虎哥
* @since 2023-05-05
*/
public interface IOrderService extends IService<Order> {
Long createOrder(OrderFormDTO orderFormDTO);
void markOrderPaySuccess(Long orderId);
}

View File

@ -0,0 +1,21 @@
package com.hmall.trade.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.trade.domain.po.OrderDetail;
import com.hmall.trade.mapper.OrderDetailMapper;
import com.hmall.trade.service.IOrderDetailService;
import org.springframework.stereotype.Service;
/**
* <p>
* 订单详情表 服务实现类
* </p>
*
* @author 虎哥
* @since 2023-05-05
*/
@Service
public class OrderDetailServiceImpl extends ServiceImpl<OrderDetailMapper, OrderDetail> implements IOrderDetailService {
}

View File

@ -0,0 +1,21 @@
package com.hmall.trade.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.trade.domain.po.OrderLogistics;
import com.hmall.trade.mapper.OrderLogisticsMapper;
import com.hmall.trade.service.IOrderLogisticsService;
import org.springframework.stereotype.Service;
/**
* <p>
* 服务实现类
* </p>
*
* @author 虎哥
* @since 2023-05-05
*/
@Service
public class OrderLogisticsServiceImpl extends ServiceImpl<OrderLogisticsMapper, OrderLogistics> implements IOrderLogisticsService {
}

View File

@ -0,0 +1,114 @@
package com.hmall.trade.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.hmall.api.client.CartClient;
import com.hmall.api.client.ItemClient;
import com.hmall.api.dto.ItemDTO;
import com.hmall.api.dto.OrderDetailDTO;
import com.hmall.common.exception.BadRequestException;
import com.hmall.common.utils.UserContext;
import com.hmall.trade.domain.dto.OrderFormDTO;
import com.hmall.trade.domain.po.Order;
import com.hmall.trade.domain.po.OrderDetail;
import com.hmall.trade.mapper.OrderMapper;
import com.hmall.trade.service.IOrderDetailService;
import com.hmall.trade.service.IOrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
/**
* <p>
* 服务实现类
* </p>
*
* @author 虎哥
* @since 2023-05-05
*/
@Service
@RequiredArgsConstructor
public class OrderServiceImpl extends ServiceImpl<OrderMapper, Order> implements IOrderService {
private final ItemClient itemClient;
private final IOrderDetailService detailService;
private final CartClient cartClient;
@Override
@Transactional
public Long createOrder(OrderFormDTO orderFormDTO) {
// 1.订单数据
Order order = new Order();
// 1.1.查询商品
List<OrderDetailDTO> detailDTOS = orderFormDTO.getDetails();
// 1.2.获取商品id和数量的Map
Map<Long, Integer> itemNumMap = detailDTOS.stream()
.collect(Collectors.toMap(OrderDetailDTO::getItemId, OrderDetailDTO::getNum));
Set<Long> itemIds = itemNumMap.keySet();
// 1.3.查询商品
List<ItemDTO> items = itemClient.queryItemByIds(itemIds);
if (items == null || items.size() < itemIds.size()) {
throw new BadRequestException("商品不存在");
}
// 1.4.基于商品价格购买数量计算商品总价totalFee
int total = 0;
for (ItemDTO item : items) {
total += item.getPrice() * itemNumMap.get(item.getId());
}
order.setTotalFee(total);
// 1.5.其它属性
order.setPaymentType(orderFormDTO.getPaymentType());
order.setUserId(UserContext.getUser());
order.setStatus(1);
// 1.6.将Order写入数据库order表中
save(order);
// 2.保存订单详情
List<OrderDetail> details = buildDetails(order.getId(), items, itemNumMap);
detailService.saveBatch(details);
// 3.清理购物车商品
cartClient.deleteCartItemByIds(itemIds);
// 4.扣减库存
try {
itemClient.deductStock(detailDTOS);
} catch (Exception e) {
throw new RuntimeException("库存不足!");
}
return order.getId();
}
@Override
public void markOrderPaySuccess(Long orderId) {
Order order = new Order();
order.setId(orderId);
order.setStatus(2);
order.setPayTime(LocalDateTime.now());
updateById(order);
}
private List<OrderDetail> buildDetails(Long orderId, List<ItemDTO> items, Map<Long, Integer> numMap) {
List<OrderDetail> 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;
}
}

View File

@ -0,0 +1,4 @@
hm:
db:
host: mysql
pw: 123

View File

@ -0,0 +1,4 @@
hm:
db:
host: 124.71.159.195 # 修改为你自己的虚拟机IP地址
pw: 123456 # 修改为docker中的MySQL密码

View File

@ -0,0 +1,51 @@
server:
port: 8085
spring:
application:
name: trade-service
cloud:
nacos:
server-addr: 124.71.159.195:8848 # nacos地址
profiles:
active: local
datasource:
url: jdbc:mysql://${hm.db.host}:3307/hm-trade?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: ${hm.db.pw}
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.trade.controller
feign:
httpclient:
enabled: true # 使用 Apache HttpClient默认关闭
# keytool -genkeypair -alias hmall -keyalg RSA -keypass hmall123 -keystore hmall.jks -storepass hmall123

View File

@ -9,7 +9,7 @@ spring:
profiles:
active: local
datasource:
url: jdbc:mysql://${hm.db.host}:3306/hm-user?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
url: jdbc:mysql://${hm.db.host}:3307/hm-user?useUnicode=true&characterEncoding=UTF-8&autoReconnect=true&serverTimezone=Asia/Shanghai
driver-class-name: com.mysql.cj.jdbc.Driver
username: root
password: ${hm.db.pw}