谷粒学院订单管理 server-order 模块
模块介绍
主要实现前台页面购买课程后,生成订单,并实现微信支付的功能。
![]](https://img-blog.csdnimg.cn/7b821f31df1c426ba91bc14ff49d4ad8.png)
思维导图(属性图)
库表设计
详细设计
订单
生成订单功能
接口设计
接受参数:课程号和HTTP 请求
请求类型:POST
请求体:JSON 格式的数据
返回值:订单号
流程
- 远程调用 根据用户id获取用户信息和根据课程id获取课程信息的方法,获取用户信息和课程信息。
- 创建订单对象,把信息传入到订单对象中去。
- 返回订单号
代码
//1.生成订单 controller层
@PostMapping("createOder/{courseId}")
public R saveOrder(
@PathVariable String courseId,
HttpServletRequest request) {
//创建订单,返回订单号
String orderNo = orderService.createOrders(
courseId,
JwtUtils.getMemberIdByJwtToken(request));
return R.ok().data("orderId", orderNo);
}
//1.生成订单 service层
@Autowired
private EduClient eduClient;
@Autowired
private UcenterClient ucenterClient;
@Override
public String createOrders(String courseId, String memberId) {
//通过远程调用根据用户id获取用户信息
UcenterMemberOrder userInfoOrder = ucenterClient.getUserInfoOrder(memberId);
//通过远程调用根据课程id获取课程信息
CourseWebVoOrder courseInfoOrder = eduClient.getCourseInfoOrder(courseId);
//创建订单对象
Order order = new Order();
order.setOrderNo(OrderNoUtil.getOrderNo()); //订单号
order.setCourseId(courseId); //课程id
order.setCourseTitle(courseInfoOrder.getTitle()); //课程名称
order.setCourseCover(courseInfoOrder.getCover()); //课程封面
order.setTeacherName(courseInfoOrder.getTeacherName()); //课程所属讲师
order.setTotalFee(courseInfoOrder.getPrice()); //订单金额(也就是课程价格)
order.setMemberId(memberId); //用户id
order.setMobile(userInfoOrder.getMobile());//用户手机号
order.setNickname(userInfoOrder.getNickname()); //用户昵称
order.setStatus(0); //支付状态(0:未支付 1:已支付)
order.setPayType(1); //支付类型(1:微信 2:支付宝)
baseMapper.insert(order);
//返回订单号
return order.getOrderNo();
}
//EduClient接口
@Component
@FeignClient("service-edu")
public interface EduClient {
//根据课程id获取课程信息(该方法供远程调用使用)
@PostMapping("/serviceedu/coursefront/getCourseInfoOrder/{id}")
public CourseWebVoOrder getCourseInfoOrder(@PathVariable("id") String id);
}
//UcenterClient接口
@Component
@FeignClient("service-ucenter")
public interface UcenterClient {
//根据用户id获取用户信息(该方法供远程调用使用)
@PostMapping("/educenter/member/getUserInfoOrder/{id}")
public UcenterMemberOrder getUserInfoOrder(@PathVariable("id") String id);
}
根据订单号查询订单信息功能
接口设计
接受参数:订单号
请求类型:GET
返回值:订单
代码
//2.根据订单号查询订单信息
@GetMapping("getOrderInfo/{orderId}")
public R getOrderInfo(@PathVariable String orderId) {
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("order_no", orderId);
Order order = orderService.getOne(wrapper);
return R.ok().data("item", order);
}
查询订单支付状态功能
在用户登录后,查询课程是否被用户购买过。
接口设计
接受参数:课程id和用户id
请求类型:GET
返回值:布尔值
代码
//3.根据课程id和用户id查询订单表status值是否为1(已支付)
@GetMapping("isBuyCourse/{courseId}/{memberId}")
public boolean isBuyCourse(
@PathVariable String courseId,
@PathVariable String memberId) {
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("course_id", courseId);
wrapper.eq("member_id", memberId);
wrapper.eq("status", 1); //1代表已经支付
int count = orderService.count(wrapper);
return count > 0;
}
生成微信支付二维码
接口设计
接受参数:订单号
请求类型:GET
返回值:Map集合
流程
- 先根据订单号查询订单信息
- 用map封装二维码需要的参数
- 发送httpclient请求,请求的参数是xml格式
- 设置请求地址(请求地址是微信给的固定的)
- 设置请求参数(xml格式)
- 解决无法请求https问题
- 发送xml格式参数的请求
- 获取请求返回的数据,并将xml数据转化为map集合。
- 最终返回数据的封装,把其他没在resultMap中的数据进行封装。
代码
//1.生成微信支付二维码 controller层
@GetMapping("createNative/{orderNo}")
public R createNative(@PathVariable String orderNo) {
//业务层返回的信息中包含二维码地址,还有一些其它我们需要的信息
Map map = payLogService.createNative(orderNo);
return R.ok().data(map);
}
//service层
@Autowired
private OrderService orderService;
//1.生成微信支付二维码
@Override
public Map createNative(String orderNo) {
try {
//1.根据订单号查询订单信息
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("order_no", orderNo);
Order order = orderService.getOne(wrapper);
//2.使用map集合设置生成二维码需要的参数
Map m = new HashMap();
m.put("appid", "wx74862e0dfcf69954"); //支付id
m.put("mch_id", "1558950191"); //商户号
m.put("nonce_str", WXPayUtil.generateNonceStr()); //生成随机唯一字符串,使得生成的每个二维码都不同
m.put("body", order.getCourseTitle()); //生成的二维码显示什么名字
m.put("out_trade_no", orderNo); //二维码的唯一标识,我们的订单号都是唯一的,所以一般赋值订单号
m.put("total_fee", order.getTotalFee().multiply(new BigDecimal("100")).longValue()+""); //扫码的价格
m.put("spbill_create_ip", "127.0.0.1"); //支付服务的ip地址(域名也行),我们这里是本地,所以赋值127.0.0.1
m.put("notify_url", "http://guli.shop/api/order/weixinPay/weixinNotify\n");//支付后回调的地址,老师说目前用不到
m.put("trade_type", "NATIVE"); //支付类型,NATIVE就表示根据价格生成一个支付二维码
//3.发送httpclient请求,请求的参数是xml格式
//3.1设置请求地址(请求地址是微信给的固定的)
HttpClient client = new HttpClient("https://api.mch.weixin.qq.com/pay/unifiedorder");
//3.2设置请求参数(xml格式)
// generateSignedXml方法作用:根据商户key对map集合做加密并将加密后的map集合转为xml格式
// setXmlParam方法作用:将得到的xml格式字符串设置为请求参数
client.setXmlParam(WXPayUtil.generateSignedXml(m, "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
//3.3因为请求的地址是https的,默认无法请求,有了下面这行代码就可以请求了
client.setHttps(true);
//3.4执行发送请求(发送xml格式参数的请求)
client.post();
//4.获取请求返回的数据
//4.1获取数据
String xml = client.getContent();
//4.2将xml数据转为map集合
//发送请求后微信返回的内容是xml格式字符串,为了方便前端取值,我们把xml格式转换为map集合,把这个map集合返回给控制层
Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
//5.最终返回数据的封装
//但此时我们需要的其它数据并没有在resultMap集合中(如:订单号、课程id...),需要我们手动封装
Map map = new HashMap<>();
map.put("out_trade_no", orderNo); //订单号
map.put("course_id", order.getCourseId()); //课程id
map.put("total_fee", order.getTotalFee()); //订单金额
map.put("result_code", resultMap.get("result_code")); //二维码操作状态码
map.put("code_url", resultMap.get("code_url")); //二维码地址
return map;
} catch (Exception e) {
throw new GuliException(20001, "生成支付二维码失败");
}
}
查询订单支付状态
在生成订单后,查询是否完成支付。
接口设计
接受参数:订单号
请求类型:GET
返回值:String
流程
-
先根据订单号查询订单支付状态
-
用map封装二维码需要的参数
-
发送httpclient请求,请求的参数是xml格式
-
设置请求地址(请求地址是微信给的固定的)
-
设置请求参数(xml格式)
-
解决无法请求https问题
-
发送xml格式参数的请求
-
-
获取请求返回的数据,并将xml数据转化为map集合。
-
最终返回数据的封装,把其他没在resultMap中的数据进行封装。
-
-
改变订单状态,根据订单状态向支付日志记录表添加记录
- 获取订单号,根据订单号去订单表查询订单信息
- 订单曾经已经支付了,不再需要做其它操作,订单曾经未支付,修改t_order(订单表)的status字段为1(已支付)
- 向支付日志记录表添加一条数据
代码
//查询订单支付状态
@GetMapping("queryPayStatus/{orderNo}")
public R queryPayStatus(@PathVariable String orderNo) {
//请求微信给的地址后返回很多数据,为了方便取值,这里我们也用map集合来接收
Map<String, String> map = payLogService.queryPayStatus(orderNo);
if (map == null) {
return R.error().message("支付出错了");
}
//map不为空,那就从map中获取订单状态
if (map.get("trade_state").equals("SUCCESS")) {
//向t_pay_log(支付日志记录表)添加一条记录
//并且修改t_order(订单表)的status字段为1(已支付)
payLogService.updateOrderStatus(map);
return R.ok().message("支付成功");
}
return R.ok().message("支付中").code(25000);
}
//根据订单号查询订单支付状态
@Override
public Map<String, String> queryPayStatus(String orderNo) {
try {
//1.封装参数
Map m = new HashMap<>();
m.put("appid", "wx74862e0dfcf69954");
m.put("mch_id", "1558950191");
m.put("out_trade_no", orderNo);
m.put("nonce_str", WXPayUtil.generateNonceStr());
//2.发送httpclient请求
HttpClient client = new HttpClient(
"https://api.mch.weixin.qq.com/pay/orderquery");
client.setXmlParam(WXPayUtil.generateSignedXml(
m, "T6m9iK73b0kn9g5v426MKfHQH7X8rKwb"));
client.setHttps(true);
client.post();
//3.获取微信返回的数据,并将xml格式数据转为map集合
String xml = client.getContent();
Map<String, String> resultMap = WXPayUtil.xmlToMap(xml);
return resultMap;
} catch (Exception e) {
return null;
}
}
//向t_pay_log(支付日志记录表)添加一条记录
//并且修改t_order(订单表)的status字段为1(已支付)
@Override
public void updateOrderStatus(Map<String, String> map) {
//1.获取订单号
String orderNo = map.get("out_trade_no");
//2.根据订单号去订单表查询订单信息
QueryWrapper<Order> wrapper = new QueryWrapper<>();
wrapper.eq("order_no", orderNo);
Order order = orderService.getOne(wrapper);
//3.修改t_order(订单表)的status字段为1(已支付)
//3.1订单曾经已经支付了,不再需要做其它操作
if(order.getStatus().intValue() == 1) return;
//3.2订单曾经未支付
order.setStatus(1); //1代表已支付
orderService.updateById(order);
//4.向支付日志记录表添加一条数据
PayLog payLog=new PayLog();
payLog.setOrderNo(order.getOrderNo()); //订单号
payLog.setPayTime(new Date()); //支付时间
payLog.setPayType(1); //支付类型(1代表微信)
payLog.setTotalFee(order.getTotalFee()); //支付金额(分)
payLog.setTradeState(map.get("trade_state")); //支付状态
payLog.setTransactionId(map.get("transaction_id")); //交易流水号
payLog.setAttr(JSONObject.toJSONString(map)); //其它属性
baseMapper.insert(payLog);//插入到支付日志记录表
}
工具类OrderNoUtil
生成订单对象时用到,生成订单号。
public class OrderNoUtil {
/**
* 获取订单号
* @return
*/
public static String getOrderNo() {
//自定义格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyyMMddHHmmss");
//使用format()方法将日期转换为字符串
String newDate = sdf.format(new Date());
String result = "";
Random random = new Random();
//随机生成三次 随机生成的随机数范围就变成[0,a)
for (int i = 0; i < 3; i++) {
result += random.nextInt(10);
}
//随机数和日期 结合生成的数字 返回成为订单号
return newDate + result;
}
}
yyyyMMddHHmmss");
//使用format()方法将日期转换为字符串
String newDate = sdf.format(new Date());
String result = "";
Random random = new Random();
//随机生成三次 随机生成的随机数范围就变成[0,a)
for (int i = 0; i < 3; i++) {
result += random.nextInt(10);
}
//随机数和日期 结合生成的数字 返回成为订单号
return newDate + result;
}
}
pom.xml
<dependencies>
<dependency>
<groupId>com.github.wxpay</groupId>
<artifactId>wxpay-sdk</artifactId>
<version>0.0.3</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
</dependency>
</dependencies>
```