第1关:订单支付
任务描述
本关任务:实现订单支付功能。
相关知识
为了完成本关任务,你需要掌握:
- 订单支付;
- 功能实现。
订单支付
购物网站中查看的订单如下:可以看到这是一个未付款的订单。
当我们点击立即付款时,前端会请求服务器,服务器会调用支付接口,这时我们就可以使用支付宝或者微信进行扫码支付了。
功能实现
以下是 springboot + mybatis + redis 模拟实现支付也就是修改订单状态及用户余额扣款及销售数量的过程。
- dao 层: OrderDao:查询当前订单及修改订单状态。
/*付款,修改订单状态*/
@Update("update orders set pay_time=now(),pay_status=1 where user_id=#{user_id} and order_id=#{order_id}")
int orderPay(Integer user_id, Long order_id);
/*查询一个订单*/
@Select("select * from orders where order_id=#{order_id}")
List<Order> getOrders(Long order_id);
//查询用户的余额
@Select("select deposit from users where user_id=#{user_id}")
BigDecimal findUserDeposit(int user_id);
//修改用户余额
@Update("update users set deposit=#{deposit} where user_id=#{user_id}")
int editUserDeposit(BigDecimal deposit,int user_id);
- service 层: 定义 service 支付接口:
/*定义 service 的支付接口*/
int orderPay(Integer user_id, Long order_id);
- impl 实现类: 计算订单总金额并对用户余额进行扣款,并根据扣款的结果返回结果 true 或者 false。
/**
* 用户扣款
* @param user_id
* @return
*/
public boolean deduction(List<Order> orders,Integer user_id){
//初始化订单的总金额
BigDecimal sumMoney=new BigDecimal("0");
for (Order order:orders){
//循环累加总金额
sumMoney=sumMoney.add(order.getProduct_price());
}
//查询用户存款
BigDecimal userDeposit=userDao.findUserDeposit(user_id);
//空值判断
if (sumMoney==null||userDeposit==null){
return false;
}
//用户存款与消费金额比较
if (userDeposit.compareTo(sumMoney)<1){
//余额不足
return false;
}else {
//余额计算
userDeposit=userDeposit.subtract(sumMoney);
//修改用户余额
int row=userDao.editUserDeposit(userDeposit,user_id);
//通过判断受影响行数来判断是否修改成功
if (row==1){
return true;
}
return false;
}
}
/**
* 订单支付成功修改订单状态,redis 缓存
* @param user_id
* @param order_id
* @return
*/
public int editOrder(List<Order> orders,Integer user_id, Long order_id){
//从 redis 中去销售数量集合 sales_num,key=商品id,value=商品销售数量
Map<Object, Object> sales_num = redisTemplate.opsForHash().entries("sales_num");
//循环当前订单所有内容
for(Order order:orders){
//订单中的商品的 id
int product_id = order.getProduct_id();
//订单中的商品的数量
int product_num = order.getProduct_num();
//非空判断
if(sales_num.isEmpty()||sales_num.get(product_id+"")==null){
//如果订单集合中没有该商品信息就存入订单集合 sales_num
sales_num.put(product_id+"",product_num);
}else{
//如果订单集合中有该商品信息就重新取出来并计算销售数量存入订单集合 sales_num
sales_num.put(product_id+"",(int)sales_num.get(product_id+"")+product_num);
}
//集合存入 redis
redisTemplate.opsForHash().putAll("sales_num",sales_num);
}
//调用 dao 层支付方法并返回结果集
return orderDao.orderPay(user_id,order_id);
}
}
- controller 层执行后返回执行结果,RespBeanEnum是自定义的枚举类。
int excute = orderService.orderPay(user_id, order_id);
if(excute>0){
//支付执行成功,返回 RespBeanEnum 类的支付成功结果
return RespBeanEnum.PAY_SUCCESS.getMap();
}else{
//支付执行失败,返回 RespBeanEnum 类的支付失败结果
return RespBeanEnum.PAY_ERROR.getMap();
}
- 数据库结构: 订单表:
用户表字段名称 类型 备注 id
int(11) NOT NULL AUTO_INCREMENT '购物车id' user_id
int(11) NOT NULL '用户id' product_id
int(11) NOT NULL '商品id' product_num
int(11) NOT NULL '商品数量' product_price
decimal(10,0) NOT NULL '商品价格' order_time
datetime NOT NULL '订单创建时间' order_id
bigint(20) NOT NULL '订单编号' pay_time
datetime NOT NULL '付款时间' pay_status
int(2) NOT NULL '0为未付款,1为已付款' status
int(11) NOT NULL '0为失效,1为生效' 字段名称 类型 备注 约束 user_id int(11) 权限id 主键,自增 userName varchar(20) 用户名 无 password varchar(40) 密码 无 userPhoneNumber bigint(11) 电话号码 无 email varchar(20) 邮箱 无 gender char(2) 性别 无 salt varchar(20) 加密盐值 无 login_count int(11) 登录次数 无 register_time datetime 注册时间 无
编程要求
在右侧编辑器 OrderDao.java、UserDao.java、OrderService.java、OrderServiceImpl.java、OrderController.java 文件里 Begin-End 处根据提示补充代码。
测试说明
平台会对你编写的代码进行测试:运行项目后发送请求访问并检验结果集是否正确。
测试输入:
{
"user_id":"1018",
"order_id":"1631456416541"
}
预期输出:
{"msg":"付款成功","code":"001"}
开始你的任务吧,祝你成功!
OrderDao.java
package com.www.dao;
import com.www.entity.Order;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface OrderDao {
/*付款,修改订单状态*/
@Update("update orders set pay_time=now(),pay_status=1 where user_id=#{user_id} and order_id=#{order_id}")
int orderPay(Integer user_id, Long order_id);
/*查询一个订单*/
@Select("select * from orders where order_id=#{order_id}")
List<Order> getOrders(Long order_id);
}
UserDao.java
package com.www.dao;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import org.springframework.stereotype.Repository;
import java.math.BigDecimal;
@Repository
public interface UserDao {
//查询用户的余额
@Select("select deposit from users where user_id=#{user_id}")
BigDecimal findUserDeposit(int user_id);
//修改用户余额
@Update("update users set deposit=#{deposit} where user_id=#{user_id}")
int editUserDeposit(BigDecimal deposit,int user_id);
}
OrderService.java
package com.www.service;
import org.springframework.stereotype.Component;
@Component
public interface OrderService {
/*定义 service 的支付接口*/
int orderPay(Integer user_id, Long order_id);
}
OrderServiceImpl.java
package com.www.service.impl;
import com.www.dao.OrderDao;
import com.www.dao.UserDao;
import com.www.entity.Order;
import com.www.service.OrderService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
@Service("orderService")
public class OrderServiceImpl implements OrderService {
@Autowired
private OrderDao orderDao;
@Autowired
private UserDao userDao;
@Autowired
RedisTemplate redisTemplate;
/**
* 订单支付
* @param user_id
* @param order_id
* @return
*/
@Override
public int orderPay(Integer user_id, Long order_id) {
/*********************Begin*********************/
//查询当前订单的所有订单内容,一个订单有很多个商品
//判断金额计算是否执行成功
//返回修改订单信息的执行结果
//查询当前订单的所有订单内容,一个订单有很多个商品
List<Order> orders = orderDao.getOrders(order_id);
//判断金额计算是否执行成功
if(deduction(orders, user_id)){
//返回修改订单信息的执行结果
return editOrder(orders, user_id, order_id);
}else{
//支付失败
return 0;
}
/*********************End*********************/
}
/**
* 用户扣款
* @param user_id
* @return
*/
public boolean deduction(List<Order> orders,Integer user_id){
//初始化订单的总金额
BigDecimal sumMoney=new BigDecimal("0");
for (Order order:orders){
//循环累加总金额
sumMoney=sumMoney.add(order.getProduct_price());
}
//查询用户存款
BigDecimal userDeposit=userDao.findUserDeposit(user_id);
//空值判断
if (sumMoney==null||userDeposit==null){
return false;
}
//用户存款与消费金额比较
if (userDeposit.compareTo(sumMoney)<1){
//余额不足
return false;
}else {
//余额计算
userDeposit=userDeposit.subtract(sumMoney);
//修改用户余额
int row=userDao.editUserDeposit(userDeposit,user_id);
//通过判断受影响行数来判断是否修改成功
if (row==1){
return true;
}
return false;
}
}
/**
* 订单支付成功修改订单状态,redis 缓存
* @param user_id
* @param order_id
* @return
*/
public int editOrder(List<Order> orders,Integer user_id, Long order_id){
//从 redis 中去销售数量集合 sales_num,key=商品id,value=商品销售数量
Map<Object, Object> sales_num = redisTemplate.opsForHash().entries("sales_num");
//循环当前订单所有内容
for(Order order:orders){
//订单中的商品的 id
int product_id = order.getProduct_id();
//订单中的商品的数量
int product_num = order.getProduct_num();
//非空判断
if(sales_num.isEmpty()||sales_num.get(product_id+"")==null){
//如果订单集合中没有该商品信息就存入订单集合 sales_num
sales_num.put(product_id+"",product_num);
}else{
//如果订单集合中有该商品信息就重新取出来并计算销售数量存入订单集合 sales_num
sales_num.put(product_id+"",(int)sales_num.get(product_id+"")+product_num);
}
//集合存入 redis
redisTemplate.opsForHash().putAll("sales_num",sales_num);
}
//调用 dao 层支付方法并返回结果集
return orderDao.orderPay(user_id,order_id);
}
}
OrderController.java
package com.www.controller;
import com.www.service.OrderService;
import com.www.vo.RespBeanEnum;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;
@RestController
@RequestMapping(value = "/order")
public class OrderController {
@Autowired
private OrderService orderService;
/**
* 订单支付,其实就是修改订单状态
* @param paramMap
* @return
*/
@RequestMapping(value = "/orderPay")
public Map orderPay(@RequestBody Map<String,Object> paramMap){
//获取用户 id:user_id
Integer user_id=1018 ;
// 获取订单 id: order_id
Long order_id=1631456416541L ;
//调用 service 层的订单支付方法并判断是否执行成功
int excute = orderService.orderPay(user_id, order_id);
if(excute>0){
//支付执行成功,返回 RespBeanEnum 类的支付成功结果
return RespBeanEnum.PAY_SUCCESS.getMap();
}else{
//支付执行失败,返回 RespBeanEnum 类的支付失败结果
return RespBeanEnum.PAY_ERROR.getMap();
}
}
}
Order.java
package com.www.entity;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.Date;
@Data
public class Order implements Serializable {
private Long id;
private Long order_id;
private int product_num;
private int product_id;
private BigDecimal product_price;
private Date order_time;
private Date pay_time;
private int status; //0,失效;1,生效
private String product_name;
private String product_picture;
}
User.java
package com.www.entity;
import lombok.Data;
import org.springframework.stereotype.Component;
import java.io.Serializable;
/**
* @author liuxianchun
* @date 2021/1/17
* 用户
*/
@Data
@Component
public class User implements Serializable {
private int user_id;
private String userName;
private String password;
private String userPhoneNumber;
private String email;
private String gender;
}
RespBeanEnum.java
package com.www.vo;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.ToString;
import java.util.HashMap;
@Getter
@ToString
@AllArgsConstructor
public enum RespBeanEnum {
PAY_SUCCESS(new HashMap<String,Object>(){
{
put("code","001");
put("msg","付款成功");
}
}),
PAY_ERROR(new HashMap<String,Object>(){
{
put("code","004");
put("msg","付款失败");
}
});
private final HashMap<String,Object> map;
}
加油哦,同学们!