文章目录
1. 订单接口系统
之前我们尝试过基于前端系统开发后台接口,也尝试过基于接口文档开发后台接口。现在我们尝试一下别人已经开发好的后台接口,该如何调用。
1.1 导入订单微服务
-
复制 leyou-order 到我们的 leyou 工程下
-
打开 IDEA,导入 leyou-order 模块
-
在父工程 leyou 的 pom 文件中添加 leyou-order 模块
-
修改一下 pom 配置文件,成功启动 leyou-order
-
最后在 leyou-gateway 中添加 leyou-order 的路由,并重启 leyou-gateway
1.2 Swagger-UI
1.2.1 OpenAPI 介绍
随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了前端渲染、前后端分离的形态,而且前端技术和后端技术在各自的道路上越走越远。 前端和后端的唯一联系,变成了 API 接口,API 文档变成了前后端开发人员联系的纽带,变得越来越重要。没有 API 文档工具之前,大家都是手写 API 文档的, API 文档没有统一规范和格式,这无疑给开发带来了灾难。
OpenAPI 规范(OpenAPI Specification 简称 OAS)是 Linux 基金会的一个项目,OpenAPI 试图通过定义一种用来描述 API 格式的语言,来规范 RESTful 服务开发过程。
1.2.2 Swagger 介绍
前面说了 OpenAPI 是一个编写 API 文档的规范,然而如果手动去编写 OpenAPI 规范的文档,是非常麻烦的。Swagger 就是一个实现了 OpenAPI 规范的工具集。
Swagger包含的工具集:
- Swagger 编辑器: Swagger Editor 允许您在浏览器中编辑 YAML 中的 OpenAPI 规范并实时预览文档。
- Swagger UI: Swagger UI 是 HTML,Javascript 和 CSS 资产的集合,可以从符合 OAS 标准的 API 动态生成漂亮的文档。
- Swagger Codegen:允许根据 OpenAPI 规范自动生成 API 客户端库(SDK 生成),服务器存根和文档。
- Swagger Parser:用于解析来自 Java 的 OpenAPI 定义的独立库
- Swagger Core:与 Java 相关的库,用于创建,使用和使用 OpenAPI 定义
- Swagger Inspector(免费): API 测试工具,可让您验证您的 API 并从现有 API 生成 OpenAPI 定义
- SwaggerHub(免费和商业):API 设计和文档,为使用 OpenAPI 的团队构建。
1.2.3.快速入门
SpringBoot 已经集成了 Swagger,使用简单注解即可生成 Swagger 的 API 文档。
-
引入依赖
<dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger2</artifactId> <version>2.8.0</version> </dependency> <dependency> <groupId>io.springfox</groupId> <artifactId>springfox-swagger-ui</artifactId> <version>2.8.0</version> </dependency>
-
编写配置类
@Configuration @EnableSwagger2 public class SwaggerConfig { @Bean public Docket api() { return new Docket(DocumentationType.SWAGGER_2) .host("localhost:8089") .apiInfo(apiInfo()) .select() .apis(RequestHandlerSelectors.basePackage("com.leyou.order.controller")) .paths(PathSelectors.any()) .build(); } private ApiInfo apiInfo() { return new ApiInfoBuilder() .title("乐优商城订单系统") .description("乐优商城订单系统接口文档") .version("1.0") .build(); } }
-
接口声明
在 Controller 的每个 handler 上添加接口说明注解:
@RestController @RequestMapping("order") @Api("订单服务接口") public class OrderController { @Autowired private OrderService orderService; @Autowired private PayHelper payHelper; /** * 创建订单 * * @param order 订单对象 * @return 订单编号 */ @PostMapping @ApiOperation(value = "创建订单接口,返回订单编号", notes = "创建订单") @ApiImplicitParam(name = "order", required = true, value = "订单的json对象,包含订单条目和物流信息") public ResponseEntity<Long> createOrder(@RequestBody @Valid Order order) { Long id = this.orderService.createOrder(order); return new ResponseEntity<>(id, HttpStatus.CREATED); } /** * 分页查询当前用户订单 * * @param status 订单状态 * @return 分页订单数据 */ @GetMapping("list") @ApiOperation(value = "分页查询当前用户订单,并且可以根据订单状态过滤", notes = "分页查询当前用户订单") @ApiImplicitParams({ @ApiImplicitParam(name = "page", value = "当前页", defaultValue = "1", type = "Integer"), @ApiImplicitParam(name = "rows", value = "每页大小", defaultValue = "5", type = "Integer"), @ApiImplicitParam(name = "status", value = "订单状态:1未付款,2已付款未发货,3已发货未确认,4已确认未评价,5交易关闭,6交易成功,已评价", type = "Integer"), }) @ApiResponses({ @ApiResponse(code = 200, message = "订单的分页结果"), @ApiResponse(code = 404, message = "没有查询到结果"), @ApiResponse(code = 500, message = "查询失败"), }) public ResponseEntity<PageResult<Order>> queryUserOrderList( @RequestParam(value = "page", defaultValue = "1") Integer page, @RequestParam(value = "rows", defaultValue = "5") Integer rows, @RequestParam(value = "status", required = false) Integer status) { PageResult<Order> result = this.orderService.queryUserOrderList(page, rows, status); if (result == null) { return new ResponseEntity<>(HttpStatus.NOT_FOUND); } return ResponseEntity.ok(result); } }
常用注解说明:
/** @Api:修饰整个类,描述Controller的作用 @ApiOperation:描述一个类的一个方法,或者说一个接口 @ApiParam:单个参数描述 @ApiModel:用对象来接收参数 @ApiProperty:用对象接收参数时,描述对象的一个字段 @ApiResponse:HTTP响应其中1个描述 @ApiResponses:HTTP响应整体描述 @ApiIgnore:使用该注解忽略这个API @ApiError :发生错误返回的信息 @ApiImplicitParam:一个请求参数 @ApiImplicitParams:多个请求参数 */
-
打开浏览器,访问下面网址
http://localhost:8089/swagger-ui.html
-
点击 order-controller,查看接口信息
-
点击任意一个接口,即可看到详细信息
1.3 测试接口
1.3.1 创建订单接口
接口说明
- 请求方式:POST
- 请求路径:/order
- 请求参数:包含订单、订单详情等数据的 JSON 对象。
- 返回结果:订单编号
测试
-
点击 Try It Out 测试
-
输入 JSON 对象
{ "totalPay": 236800, "postFee": 0, "paymentType": 2, "actualPay": 236800, "buyerMessage": null, "buyerNick": "huge", "orderDetails": [ { "skuId": 3893493, "num": 1, "title": "苹果(Apple)iPhone 6 (A1586) 16GB 金色 移动联通电信4G手机3", "price": 236800, "ownSpec": "{\"机身颜色\":\"钻雕蓝\",\"内存\":\"4GB\",\"机身存储\":\"64GB\"}", "image": "http://image.leyou.com/images/9/4/1524297342728.jpg" } ], "receiver": "锋哥", "receiverMobile": "15800000000", "receiverState": "上海", "receiverCity": "上海", "receiverDistrict": "浦东新签", "receiverAddress": "航头镇航头路18号传智播客3号楼", "receiverZip": "210000", "invoiceType": 0, "sourceType":2 }
-
点击 execute,成功响应订单编号
-
查看 MySQL 中的订单编号
注意
浏览器订单编号:1260526782893920300
MySQL订单编号:1260526782893920256
我们发现浏览器响应的订单编号和 MySQL 订单编号后三位不一样,这是因为 JS 的长整数精度有限,java 的 Long 类型数据超出了范围,所以出现了精度损失。解决办法是 JS 将订单编号转换为字符串处理,就不会精度损失了。
1.3.2 查询订单接口
接口说明
- 请求方式:GET
- 请求路径:/order/{id}
- 请求参数:id,订单编号
- 返回结果:Order,订单的 JSON 对象
测试
-
输入订单编号
-
点击 execute,成功响应订单对象
1.3.3 更新订单状态
接口说明
- 请求参数:PUT
- 请求路径:/order/{id}/{status}
- 请求参数:
- id:订单编号,String 类型,不能为空
- status:订单状态,不能为空
- 返回结果:null
测试
-
更新前的订单状态,值为 1
-
输入订单编号和订单状态
-
点击 execute,响应 204
-
再次查看订单状态,已经修改值为 2
1.3.4 分页查询订单
接口说明
- 请求方式:Get
- 请求路径:/order/list
- 请求参数:
- page:当前页,Integer 类型,默认为 1
- rows:每页大小,Integer 类型,默认为 5
- status:订单状态,String 类型,默认查询全部状态订单
- 返回结果:PageResult 对象,包含下面属性:
- total:总条数
- items:当前页订单数组
测试
-
输入当前页、每页大小、订单状态
-
点击 execute,成功响应 PageResult 对象
1.3.5 查询支付状态
接口说明
- 请求方式: Get
- 请求路径: /state/{id}
- 请求参数: id,订单编号
- 返回结果:0,未查询到支付信息;1,支付成功;2,支付失败
测试
-
输入订单编号
-
点击 execute,成功响应支付结果
1.3.6 生成微信付款链接
接口说明
- 请求方式:Get
- 请求路径:/order/url/{id}
- 请求参数:id,订单编号
- 返回结果:String 类型,生成的微信支付链接
测试
-
输入订单编号
-
点击 execute,成功响应微信支付链接
2. 订单结算页
2.1 页面跳转
我们接下来实现点击结算按钮跳转到订单结算页:
-
找到 cart.html 中的结算按钮,我们给这个按钮绑定点击事件
-
创建点击事件方法:如果未登录,跳转到登录页面;如果已登陆,保存已选购的购物车到 Local Storage,再跳转到订单结算页。
测试
-
登录后点击加入购物车
-
成功跳转到结算页
-
查看 Local Storage,成功保存已选购物车
2.2 渲染订单结算页
分析一下订单结算页需要渲染的内容:
- 收货人信息
- 支付方式
- 商品信息
2.2.1 收货人信息
这里的收货人信息肯定是当前登录用户的收货地址。所以需要根据当前登录用户去查询,目前我们在页面是写的假数据:
2.2.2 支付方式
支付方式有 2 种:
- 微信支付
- 货到付款
与我们订单数据中的 paymentType 关联:
所以我们可以在 Vue 实例中定义一个属性来记录支付方式:
然后在页面渲染时与这个变量关联:
2.2.3 商品信息
-
在 created 钩子函数中获取购物车数据,保存到 carts 数组中。
-
将 carts 中的数据渲染到页面上
-
计算商品总价,总数,最终实际价格
-
将计算数据渲染到页面
2.2.4 最终效果
2.3 提交订单
-
找到提交订单按钮,我们给这个按钮绑定点击事件
-
然后编写方法,组织数据并提交,提交成功则跳转到支付页面,并在 url 中携带 orderId 和 actualPay
submit() { //1.判断是否登录 ly.verify().then(({data}) => { //2.已登录 const address = this.addresses[this.selectedAddress]; let addr = { receiver: address.name, receiverMobile: address.phone, receiverState: address.state, receiverCity: address.city, receiverDistrict: address.district, receiverAddress: address.address, receiverZip: address.zipCode, invoiceType: 0, sourceType: 2 }; const orderDetail = {orderDetails: this.carts}; Object.assign(this.order, addr, orderDetail, { totalPay: this.totalPrice, actualPay: this.actualPrice, buyerMessage: null, buyerNick: data.username }); ly.http.post("/order/order", this.order, { transformResponse: [ function (data) { return data; } ] }).then(({data}) => { window.location.href = "http://www.leyou.com/pay.html?orderId=" + data+"&actualPay="+ly.formatPrice(this.order.actualPay); }); }).catch(() => { window.location.href = "http://www.leyou.com/login.html?returnUrl=http://www.leyou.com/getOrderInfo.html"; }) }
测试
-
点击提交订单,成功跳转到支付页面
-
查看数据库,生成订单成功
-
3. 支付页面
支付页面我们一共需要展示三个部分:
- 支付二维码:根据付款链接生成二维码
- 订单号:从 url 中获取 orderId
- 应付金额:从 url 中获取 actualPay
3.1 生成二维码
- 调用后台的生成微信付款链接接口,生成付款链接
- 根据付款链接生成二维码
- 因为不清楚用户何时会付款,因此这里开启定时任务,查询付款状态
代码如下:
ly.verify().then(res => {
this.orderId = ly.getUrlParam("orderId");
this.actualPay = ly.getUrlParam("actualPay");
ly.http.get("/order/order/url/" + this.orderId).then(({data}) => {
new QRCode("qrcode", {
text: data,
width: 256,
height: 256,
colorDark: "#000000",
colorLight: "#ffffff",
correctLevel: QRCode.CorrectLevel.H
});
})
// 开启定时任务,查询付款状态
const taskId = setInterval(() => {
ly.http.get("/order/order/state/" + this.orderId)
.then(resp => {
let i = resp.data;
if (i === 1) {
// 付款成功
clearInterval(taskId);
// 跳转到付款成功页
location.href = "/paysuccess.html?orderId=" + this.orderId + "&actualPay=" + this.actualPay;
} else if (i === 2) {
// 付款失败
clearInterval(taskId);
// 跳转到付款失败页
location.href = "/payfail.html";
}
})
}, 3000);
}).catch(() => {
window.location.href = "http://www.leyou.com/login.html?returnUrl=http://www.leyou.com/getOrderInfo.html";
})