目录
一、创建订单微服务
1.1 创建module
1.2 pom依赖
1.3 配置文件
1.4 启动类
1.5 配置导入
1.6 属性读取
1.7 支付工具类
1.8 修改网关配置
二、实体类准备
2.1 Order.java
2.2 OrderDetail.java
2.3 OrderStatus.java
2.4 关系
三、Mapper
四、创建订单
4.1 Controller
4.1.1 分析
4.1.2 代码
4.2 Service
4.2.1 接口
4.2.2 实现
五、订单查询
5.1 Controller
5.1.1 分析
5.1.2 代码
5.2 Service
5.2.1 接口
5.2.2 实现
六、分页查询用户订单
6.1 Controller
6.1.1 分析
6.1.2 代码
6.2 Service
6.2.1 接口
6.2.2 实现
七、更新订单状态
7.1 Controller
7.1.1 分析
7.1.2 代码
7.2 Service
7.2.1 接口
7.2.2 实现
八、生成微信支付链接
8.1 Controller
8.1.1 分析
8.1.2 代码
8.2 Component
九、查询付款状态
9.1 Controller
9.1.1 分析
9.1.2 代码
9.2 Component
十、项目结构
leyou
com.leyou.parent
1.0.0-SNAPSHOT
4.0.0
com.leyou.order
leyou-order
org.springframework.cloud
spring-cloud-starter-netflix-eureka-client
org.springframework.boot
spring-boot-starter-web
org.mybatis.spring.boot
mybatis-spring-boot-starter
tk.mybatis
mapper-spring-boot-starter
mysql
mysql-connector-java
com.leyou.item.interface
leyou-item-interface
1.0.0-SNAPSHOT
com.leyou.common
leyou-common
1.0.0-SNAPSHOT
com.leyou.authentication
leyou-authentication
1.0.0-SNAPSHOT
org.springframework.cloud
spring-cloud-starter-openfeign
com.github.pagehelper
pagehelper-spring-boot-starter
com.github.wxpay
wxpay-sdk
org.springframework.boot
spring-boot-starter-data-redis
io.springfox
springfox-swagger2
io.springfox
springfox-swagger-ui
server:
port: 8089
spring:
application:
name: order-service
datasource:
url: jdbc:mysql://127.0.0.1:3306/leyou?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true
username: root
password: 123456
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 1000
test-on-borrow: true
rabbitmq:
host: 192.168.19.121
username: /leyou
password: leyou
virtual-host: /leyou
template:
retry:
enabled: true
initial-interval: 10000ms
max-interval: 300000ms
multiplier: 2
exchange: leyou.item.exchange
publisher-confirms: true
redis:
host: 192.168.19.121
eureka:
client:
service-url:
defaultZone: http://127.0.0.1:10086/eureka
instance:
prefer-ip-address: true
ip-address: 127.0.0.1
instance-id: ${eureka.instance.ip-address}.${server.port}
lease-renewal-interval-in-seconds: 3
lease-expiration-duration-in-seconds: 10
mybatis:
type-aliases-package: com.leyou.item.pojo
mapper-locations: mapper/OrderMapper.xml
configuration:
map-underscore-to-camel-case: true
mapper:
not-empty: false
identity: mysql
leyou:
worker:
workerId: 1
datacenterId: 1
jwt:
cookieName: LY_TOKEN
pubKeyPath: G:\\tmp\\rsa\\rsa.pub # 公钥地址
pay:
appId:
mchId:
key:
connectTimeoutMs: 5000
readTimeoutMs: 10000
package com.leyou.order;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.openfeign.EnableFeignClients;
/**
* @Author: 98050
* @Time: 2018-10-27 11:36
* @Feature: 订单服务启动器
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableFeignClients
public class LyOrderApplication {
public static void main(String[] args) {
SpringApplication.run(LyOrderApplication.class,args);
}
}
订单号生成器,拦截器配置,支付配置,swagger配置,下载
读取配置文件中的相关属性,下载
下载
修改leyou-api-gateway中的配置
package com.leyou.order.pojo;
import org.springframework.data.annotation.Id;
import javax.persistence.Table;
import javax.persistence.Transient;
import javax.validation.constraints.NotNull;
import java.util.Date;
import java.util.List;
/**
* @Author: 98050
* @Time: 2018-10-27 16:33
* @Feature: 订单类实体
*/
@Table(name = "tb_order")
public class Order {
@Id
private Long orderId;
/**
* 总金额
*/
@NotNull
private Long totalPay;
/**
* 实付金额
*/
@NotNull
private Long actualPay;
/**
* 支付类型,1、在线支付,2、货到付款
*/
@NotNull
private Integer paymentType;
/**
* 参与促销活动的id
*/
private String promotionIds;
/**
* 邮费
*/
private String postFee;
/**
* 创建时间
*/
private Date createTime;
/**
* 物流名称
*/
private String shippingName;
/**
* 物流单号
*/
private String shippingCode;
/**
* 用户id
*/
private Long userId;
/**
* 买家留言
*/
private String buyerMessage;
/**
* 买家昵称
*/
private String buyerNick;
/**
* 买家是否已经评价
*/
private Boolean buyerRate;
/**
* 收货人全名
*/
private String receiver;
/**
* 移动电话
*/
private String receiverMobile;
/**
* 省份
*/
private String receiverState;
/**
* 城市
*/
private String receiverCity;
/**
* 区/县
*/
private String receiverDistrict;
/**
* 收货地址,如:xx路xx号
*/
private String receiverAddress;
/**
* 邮政编码,如:310001
*/
private String receiverZip;
/**
* 发票类型,0无发票,1普通发票,2电子发票,3增值税发票
*/
private Integer invoiceType;
/**
* 订单来源 1:app端,2:pc端,3:M端,4:微信端,5:手机qq端
*/
private Integer sourceType;
@Transient
private List orderDetails;
@Transient
private Integer status;
//省略get和set
}
package com.leyou.order.pojo;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import javax.persistence.Table;
/**
* @Author: 98050
* @Time: 2018-10-27 16:34
* @Feature: 订单详情信息实体类
*/
@Table(name = "tb_order_detail")
public class OrderDetail {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
/**
* 订单id
*/
private Long orderId;
/**
* 商品id
*/
private Long skuId;
/**
* 商品购买数量
*/
private Integer num;
/**
* 商品标题
*/
private String title;
/**
* 商品单价
*/
private Long price;
/**
* 商品规格数据
*/
private String ownSpec;
/**
* 图片
*/
private String image;
//省略get和set
}
订单状态:
package com.leyou.order.pojo;
import javax.persistence.Id;
import javax.persistence.Table;
import java.util.Date;
/**
* @Author: 98050
* @Time: 2018-10-27 16:34
* @Feature: 订单状态实体类
*/
@Table(name = "tb_order_status")
public class OrderStatus {
/**
* 初始阶段:1、未付款、未发货;初始化所有数据
* 付款阶段:2、已付款、未发货;更改付款时间
* 发货阶段:3、已发货,未确认;更改发货时间、物流名称、物流单号
* 成功阶段:4、已确认,未评价;更改交易结束时间
* 关闭阶段:5、关闭; 更改更新时间,交易关闭时间。
* 评价阶段:6、已评价
*/
@Id
private Long orderId;
private Integer status;
/**
* 创建时间
*/
private Date createTime;
/**
* 付款时间
*/
private Date paymentTime;
/**
* 发货时间
*/
private Date consignTime;
/**
* 交易结束时间
*/
private Date endTime;
/**
* 交易关闭时间
*/
private Date closeTime;
/**
* 评价时间
*/
private Date commentTime;
//省略get和set
}
一个订单对应好几种状态,通过tb_order_status来记录订单所处的不同状态;一个订单有好多订单项,即多个商品信息,这个用tb_order_detail来记录。
所需的通用mapper
package com.leyou.order.mapper;
import tk.mybatis.mapper.common.Mapper;
/**
* @Author: 98050
* @Time: 2018-10-27 16:31
* @Feature:
*/
public interface OrderMapper extends Mapper {
}
package com.leyou.order.mapper;
import com.leyou.order.pojo.OrderDetail;
import tk.mybatis.mapper.common.Mapper;
import tk.mybatis.mapper.common.special.InsertListMapper;
/**
* @Author: 98050
* @Time: 2018-10-27 16:33
* @Feature:
*/
public interface OrderDetailMapper extends Mapper, InsertListMapper {
}
package com.leyou.order.mapper;
import com.leyou.order.pojo.OrderStatus;
import tk.mybatis.mapper.common.Mapper;
/**
* @Author: 98050
* @Time: 2018-10-27 16:31
* @Feature:
*/
public interface OrderStatusMapper extends Mapper {
}
在启动类中添加mapper扫描:
请求方式:POST
请求路径:api.leyou.com/api/order
请求参数:order对象
返回结果:创建好的订单id
/**
* 创建订单
* @param order 订单对象
* @return 订单编号
*/
@PostMapping
public ResponseEntity createOrder(@RequestBody @Valid Order order){
Long id = this.orderService.createOrder(order);
return new ResponseEntity<>(id, HttpStatus.CREATED);
}
逻辑:
package com.leyou.order.service.impl;
import com.leyou.auth.entity.UserInfo;
import com.leyou.order.interceptor.LoginInterceptor;
import com.leyou.order.mapper.OrderDetailMapper;
import com.leyou.order.mapper.OrderMapper;
import com.leyou.order.mapper.OrderStatusMapper;
import com.leyou.order.pojo.Order;
import com.leyou.order.pojo.OrderStatus;
import com.leyou.order.service.OrderService;
import com.leyou.utils.IdWorker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
/**
* @Author: 98050
* @Time: 2018-10-27 16:37
* @Feature:
*/
@Service
public class OrderServiceImpl implements OrderService {
@Autowired
private IdWorker idWorker;
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderStatusMapper orderStatusMapper;
@Autowired
private OrderDetailMapper orderDetailMapper;
private static final Logger logger = LoggerFactory.getLogger(OrderServiceImpl.class);
@Transactional(rollbackFor = Exception.class)
@Override
public Long createOrder(Order order) {
//1.生成orderId
long orderId = idWorker.nextId();
//2.获取登录的用户
UserInfo userInfo = LoginInterceptor.getLoginUser();
//3.初始化数据
order.setBuyerNick(userInfo.getUsername());
order.setBuyerRate(false);
order.setCreateTime(new Date());
order.setOrderId(orderId);
order.setUserId(userInfo.getId());
//4.保存数据
this.orderMapper.insertSelective(order);
//5.保存订单状态
OrderStatus orderStatus = new OrderStatus();
orderStatus.setOrderId(orderId);
orderStatus.setCreateTime(order.getCreateTime());
//初始状态未未付款:1
orderStatus.setStatus(1);
//6.保存数据
this.orderStatusMapper.insertSelective(orderStatus);
//7.在订单详情中添加orderId
order.getOrderDetails().forEach(orderDetail -> orderDetail.setOrderId(orderId));
//8.保存订单详情,使用批量插入功能
this.orderDetailMapper.insertList(order.getOrderDetails());
//9.减库存
logger.debug("生成订单,订单编号:{},用户id:{}", orderId, userInfo.getId());
return orderId;
}
}
请求方式:GET
请求路径:api.leyou.com/api/order/id
请求参数:订单编号
返回结果:订单对象
/**
* 查询订单
* @param id 订单编号
* @return 订单对象
*/
@GetMapping("{id}")
public ResponseEntity queryOrderById(@PathVariable("id") Long id){
Order order = this.orderService.queryOrderById(id);
if (order == null){
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
return ResponseEntity.ok(order);
}
逻辑:
/**
* 根据订单号查询订单
* @param id
* @return
*/
@Override
public Order queryOrderById(Long id) {
//1.查询订单
Order order = this.orderMapper.selectByPrimaryKey(id);
//2.查询订单详情
Example example = new Example(OrderDetail.class);
example.createCriteria().andEqualTo("orderId",id);
List orderDetail = this.orderDetailMapper.selectByExample(example);
//3.查询订单状态
OrderStatus orderStatus = this.orderStatusMapper.selectByPrimaryKey(order.getOrderId());
//4.order对象填充订单详情
order.setOrderDetails(orderDetail);
//5.order对象设置订单状态
order.setStatus(orderStatus.getStatus());
//6.返回order
return order;
}
请求方式:GET
请求路径:api.leyou.com/api/order/list
请求参数:page(页数)、rows(每页大小)、status(页面状态)
返回结果:分页对象
/**
* 分页查询当前已经登录的用户订单
* @param page 页数
* @param rows 每页大小
* @param status 订单状态
* @return
*/
@GetMapping("list")
public ResponseEntity> queryUserOrderList(
@RequestParam(value = "page",defaultValue = "1")Integer page,
@RequestParam(value = "rows",defaultValue = "5")Integer rows,
@RequestParam(value = "status",required = false)Integer status
){
PageResult result = this.orderService.queryUserOrderList(page,rows,status);
if (result == null){
return new ResponseEntity<>(HttpStatus.NOT_FOUND);
}
return ResponseEntity.ok(result);
}
需求:
订单的分页查询还需要根据订单状态来筛选,因为后期要分别进行展示,而订单状态是在单独一个表中,要进行分页查询的话,只能是tb_order表和tb_order_status两个表先进行连接,然后对其进行分页查询,查询完毕后再填充订单详情。这里用mybatis的xml配置文件方式来进行查询
在resources中创建mapper文件夹
创建OrderMapper.xml数据库映射文件
在mapper中定义方法
package com.leyou.order.mapper;
import com.leyou.order.pojo.Order;
import org.apache.ibatis.annotations.Param;
import tk.mybatis.mapper.common.Mapper;
import java.util.List;
/**
* @Author: 98050
* @Time: 2018-10-27 16:31
* @Feature:
*/
public interface OrderMapper extends Mapper {
/**
* 分页查询订单
* @param userId
* @param status
* @return
*/
List queryOrderList(@Param("userId") Long userId, @Param("status") Integer status);
}
逻辑:
代码
/**
* 查询当前登录用户的订单,通过订单状态进行筛选
* @param page
* @param rows
* @param status
* @return
*/
@Override
public PageResult queryUserOrderList(Integer page, Integer rows, Integer status) {
try{
//1.分页
PageHelper.startPage(page,rows);
//2.获取登录用户
UserInfo userInfo = LoginInterceptor.getLoginUser();
//3.查询
Page pageInfo = (Page) this.orderMapper.queryOrderList(userInfo.getId(), status);
//4.填充orderDetail
List orderList = pageInfo.getResult();
orderList.forEach(order -> {
Example example = new Example(OrderDetail.class);
example.createCriteria().andEqualTo("orderId",order.getOrderId());
List orderDetailList = this.orderDetailMapper.selectByExample(example);
order.setOrderDetails(orderDetailList);
});
return new PageResult<>(pageInfo.getTotal(),(long)pageInfo.getPages(), orderList);
}catch (Exception e){
logger.error("查询订单出错",e);
return null;
}
}
请求方式:PUT
请求路径:api.leyou.com/api/order/id/status
请求参数:id(订单编号)、status(订单状态)
返回结果:true或false
/**
* 更新订单状态
* @param id
* @param status
* @return
*/
@PutMapping("{id}/{status}")
public ResponseEntity updateOrderStatus(@PathVariable("id") Long id,@PathVariable("status") Integer status){
Boolean result = this.orderService.updateOrderStatus(id,status);
if (result == null){
//返回400
return new ResponseEntity<>(HttpStatus.BAD_REQUEST);
}
//返回204
return new ResponseEntity<>(result,HttpStatus.NO_CONTENT);
}
逻辑:
/**
* 更新订单状态
* @param id
* @param status
* @return
*/
@Override
public Boolean updateOrderStatus(Long id, Integer status) {
OrderStatus orderStatus = new OrderStatus();
orderStatus.setOrderId(id);
orderStatus.setStatus(status);
//1.根据状态判断要修改的时间
switch (status){
case 2:
//2.付款时间
orderStatus.setPaymentTime(new Date());
break;
case 3:
//3.发货时间
orderStatus.setConsignTime(new Date());
break;
case 4:
//4.确认收货,订单结束
orderStatus.setEndTime(new Date());
break;
case 5:
//5.交易失败,订单关闭
orderStatus.setCloseTime(new Date());
break;
case 6:
//6.评价时间
orderStatus.setCommentTime(new Date());
break;
default:
return null;
}
int count = this.orderStatusMapper.updateByPrimaryKeySelective(orderStatus);
return count == 1;
}
请求方式:GET
请求路径:api.leyou.com/api/order/url/id
请求参数:id(订单编号)
返回结果:支付链接
/**
* 根据订单id生成付款链接
* @param orderId
* @return
*/
@GetMapping("url/{id}")
public ResponseEntity generateUrl(@PathVariable("id") Long orderId){
String url = this.payHelper.createPayUrl(orderId);
if (StringUtils.isNotBlank(url)){
return ResponseEntity.ok(url);
}else {
return ResponseEntity.status(HttpStatus.NOT_FOUND).build();
}
}
public String createPayUrl(Long orderId) {
String key = "leyou.pay.url." + orderId;
try {
String url = this.redisTemplate.opsForValue().get(key);
if (StringUtils.isNotBlank(url)) {
return url;
}
} catch (Exception e) {
logger.error("查询缓存付款链接异常,订单编号:{}", orderId, e);
}
try {
Map data = new HashMap<>();
// 商品描述
data.put("body", "乐优商城测试");
// 订单号
data.put("out_trade_no", orderId.toString());
//货币
data.put("fee_type", "CNY");
//金额,单位是分
data.put("total_fee", "1");
//调用微信支付的终端IP(estore商城的IP)
data.put("spbill_create_ip", "127.0.0.1");
//回调地址
data.put("notify_url", "http://test.leyou.com/wxpay/notify");
// 交易类型为扫码支付
data.put("trade_type", "NATIVE");
//商品id,使用假数据
data.put("product_id", "1234567");
Map result = this.wxPay.unifiedOrder(data);
if ("SUCCESS".equals(result.get("return_code"))) {
String url = result.get("code_url");
// 将付款地址缓存,时间为10分钟
try {
this.redisTemplate.opsForValue().set(key, url, 10, TimeUnit.MINUTES);
} catch (Exception e) {
logger.error("缓存付款链接异常,订单编号:{}", orderId, e);
}
return url;
} else {
logger.error("创建预交易订单失败,错误信息:{}", result.get("return_msg"));
return null;
}
} catch (Exception e) {
logger.error("创建预交易订单异常", e);
return null;
}
}
请求方式:GET
请求路径:api.leyou.com/api/order/state/id
请求参数:id(订单编号)
返回结果:支付状态(未支付:0;支付成功:1;支付失败:2)
/**
* 查询付款状态
* @param orderId
* @return
*/
@GetMapping("state/{id}")
public ResponseEntity queryPayState(@PathVariable("id") Long orderId){
PayState payState = this.payHelper.queryOrder(orderId);
return ResponseEntity.ok(payState.getValue());
}
/**
* 查询订单状态
*
* @param orderId
* @return
*/
public PayState queryOrder(Long orderId) {
Map data = new HashMap<>();
// 订单号
data.put("out_trade_no", orderId.toString());
try {
Map result = this.wxPay.orderQuery(data);
if (result == null) {
// 未查询到结果,认为是未付款
return PayState.NOT_PAY;
}
String state = result.get("trade_state");
if ("SUCCESS".equals(state)) {
// success,则认为付款成功
// 修改订单状态
this.orderService.updateOrderStatus(orderId, 2);
return PayState.SUCCESS;
} else if (StringUtils.equals("USERPAYING", state) || StringUtils.equals("NOTPAY", state)) {
// 未付款或正在付款,都认为是未付款
return PayState.NOT_PAY;
} else {
// 其它状态认为是付款失败
return PayState.FAIL;
}
} catch (Exception e) {
logger.error("查询订单状态异常", e);
return PayState.NOT_PAY;
}
}
项目中用到的一些工具类,会在下一篇做具体介绍