乐优商城之下单(二十一)

文章目录

(一)启动leyou-order
(二)Swagger-UI
(三)测试订单接口
(四)生成ID的方式
(五)跳转到订单结算页
(六)订单页的渲染
(七)完成下单功能

(一)启动leyou-order

导入leyou-order后发现它变成root了,如下:
乐优商城之下单(二十一)_第1张图片
我们要修改最顶层的pom.xml,让它成为子工程,如下:
乐优商城之下单(二十一)_第2张图片

(二)Swagger-UI

什么是OpenAPI?

随着互联网技术的发展,现在的网站架构基本都由原来的后端渲染,变成了:前端渲染、前后端分离的形态,而且前端技术和后端技术在各自的道路上越走越远。 前端和后端的唯一联系,变成了API接口;API文档变成了前后端开发人员联系的纽带,变得越来越重要。

没有API文档工具之前,大家都是手写API文档的,在什么地方书写的都有,而且API文档没有统一规范和格式,每个公司都不一样。这无疑给开发带来了灾难。

OpenAPI规范(OpenAPI Specification 简称OAS)是Linux基金会的一个项目,试图通过定义一种用来描述API格式或API定义的语言,来规范RESTful服务开发过程。目前V3.0版本的OpenAPI规范已经发布并开源在github上 。

官网:https://github.com/OAI/OpenAPI-Specification

什么是Swagger?

OpenAPI是一个编写API文档的规范,然而如果手动去编写OpenAPI规范的文档,是非常麻烦的。而Swagger就是一个实现了OpenAPI规范的工具集。

官网:https://swagger.io/

看官方的说明:
乐优商城之下单(二十一)_第3张图片
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的团队构建。

快速入门:引入依赖

<dependency>
    <groupId>io.springfoxgroupId>
    <artifactId>springfox-swagger2artifactId>
    <version>2.8.0version>
dependency>
<dependency>
    <groupId>io.springfoxgroupId>
    <artifactId>springfox-swagger-uiartifactId>
    <version>2.8.0version>
dependency>

快速入门:编写配置

@Configuration
@EnableSwagger2
public class SwaggerConfig {
     
    @Bean
    public Docket api() {
     
        return new Docket(DocumentationType.SWAGGER_2)
                .host("http://order.leyou.com")
                .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();
    }
}

快速入门:接口声明

@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"),
    })
    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

(三)测试订单接口

在商城页面登录后,复制cookie信息到swagger页面来伪造cookie,把笔记中的测试json字符串复制进去测试,生成订单成功,如下:
乐优商城之下单(二十一)_第4张图片

(四)生成ID的方式

订单id的特殊性

订单数据非常庞大,将来一定会做分库分表。那么这种情况下, 要保证id的唯一,就不能靠数据库自增,而是自己来实现算法,生成唯一id。

要求:id唯一可读性好长度一致

雪花算法

这里的订单id是通过一个工具类生成的:
乐优商城之下单(二十一)_第5张图片
工具类所采用的生成id算法,是由Twitter公司开源的snowflake(雪花)算法。
雪花算法会生成一个64位的二进制数据,为一个Long型(转换成字符串后长度最多19) ,其基本结构:
乐优商城之下单(二十一)_第6张图片

  • 第一部分:为未使用
  • 第二部分:41位为毫秒级时间(41位的长度可以使用69年)
  • 第三部分:5位datacenterId和5位workerId(10位的长度最多支持部署1024个节点)
  • 第四部分:最后12位是毫秒内的计数(12位的计数顺序号支持每个节点每毫秒产生4096个ID序号)

snowflake生成的ID整体上按照时间自增排序,并且整个分布式系统内不会产生ID碰撞(由datacenter和workerId作区分),并且效率较高。经测试snowflake每秒能够产生26万个ID

(五)跳转到订单结算页

在购物车页面的最下方,有一个去结算按钮:
乐优商城之下单(二十一)_第7张图片
当点击结算,我们应该跳转到订单结算页,即:getOrderInfo.html
乐优商城之下单(二十一)_第8张图片
可以看到,地址是正确的。但是只有登录用户才可以去结算付款,因此我们不能直接跳转,而是在跳转前校验用户的登录状态,如果发现是未登录,应该重定向到登录页!

我们给这个按钮绑定点击事件:
乐优商城之下单(二十一)_第9张图片
事件中判断登录状态,进行页面跳转:

toOrderInfo() {
     
    // 判断是否登录
    ly.verifyUser().then(() => {
     
        // 已登录
        window.location.href = "/getOrderInfo.html"
    }).catch(() => {
     
        // 未登录
        window.location.href = "/login.html?returnUrl=" + window.location.href;
    })
}

登录后测试:
乐优商城之下单(二十一)_第10张图片

此处页面需要渲染的内容主要包含3部分:

  • 收货人信息
  • 支付方式
  • 商品信息

(六)订单页的渲染

我们修改cart.html中的页面跳转逻辑,把用户选中的购物车信息传递过来:
乐优商城之下单(二十一)_第11张图片
然后在created钩子函数中获取购物车数据,保存到本地属性,要注意的是,我们应该在获取数据前校验用户登录状态,如果发现未登录,则直接重定向到登录页:
乐优商城之下单(二十一)_第12张图片

支付方式

支付方式有2种:

  • 微信支付
  • 货到付款

与我们订单数据中的paymentType关联:
乐优商城之下单(二十一)_第13张图片
所以我们可以在Vue实例中定义一个属性来记录支付方式:
乐优商城之下单(二十一)_第14张图片
然后在页面渲染时与这个变量关联:
乐优商城之下单(二十一)_第15张图片

商品列表

要修改的页面位置:每一个li就是一件商品
乐优商城之下单(二十一)_第16张图片
修改为:

<ul class="send-detail">
    <li v-for="(cart,index) in carts" :key="index">
        <div class="sendGoods">
            <ul class="yui3-g">
                <li class="yui3-u-1-6">
                    <span><img width="70px" height="70px" :src="cart.image"/>span>
                li>
                <li class="yui3-u-7-12">
                    <div class="desc">{
    {cart.title}}div>
                    <div class="seven">
                        <span v-for="(v) in JSON.parse(cart.ownSpec)">{
    {v + "  "}} span>
                    div>
                li>
                <li class="yui3-u-1-12">
                    <div class="price">¥{
    {ly.formatPrice(cart.price * cart.num)}}div>
                li>
                <li class="yui3-u-1-12">
                    <div class="num">{
    {cart.num}}div>
                li>
                <li class="yui3-u-1-12">
                    <div class="exit">有货div>
                li>
            ul>
        div>
    li>
ul>

效果如下:
乐优商城之下单(二十一)_第17张图片

总金额

另外在商品列表下面,还有一个总金额的计算:
乐优商城之下单(二十一)_第18张图片
可以看出这里主要有4个数据:

  • 总金额:totalPay
  • 优惠返现:discount
  • 运费:postFee
  • 实付金额:actualPay

不过我们没有做优惠活动,另外运费需要结合物流系统来计算,暂时我们都设置为0,在order属性中写死:
乐优商城之下单(二十一)_第19张图片
我们通过计算属性来得到totalPayactualPay值:

computed: {
     
    totalNum(){
     
        return this.carts.reduce((c1, c2) => c1 + c2.num, 0)
    },
    totalPay(){
     
        return this.carts.reduce((c1, c2) => c1 + c2.price * c2.num, 0);
    },
    actualPay(){
     
        return this.totalPay + this.order.postFee - this.order.discount;
    }
},

然后在页面渲染:
乐优商城之下单(二十一)_第20张图片
效果如下:
乐优商城之下单(二十一)_第21张图片

(七)完成下单功能

页面提交

给提交按钮绑定事件:
在这里插入图片描述
然后编写方法,组织数据并提交:

methods: {
     
    submit() {
     
        // 把购物车数据处理成订单详情
        const orderDetails = this.carts.map(({
     userId, ...rest}) => rest);
        // 处理物流信息
        const addr = this.addresses[this.selectedAddress];
        const obj = {
     
            receiver: addr.name,
            receiverState: addr.state,
            receiverCity: addr.city,
            receiverAddress: addr.address,
            receiverDistrict: addr.district,
            receiverMobile: addr.phone,
            receiverZip: addr.zipCode
        };
        // 复制到订单对象
        Object.assign(this.order, obj, {
     
            orderDetails,
            totalPay: this.totalPay,
            actualPay: this.actualPay,
        });
        // 提交订单
        ly.http.post("/order/order", this.order).then(({
     data}) => {
     
            // 在线支付,需要到付款页
            window.location = "pay.html?orderId=" + data;
        }).catch((resp) => {
     
            alert("订单提交失败,可能是缺货!")
        })
    }
},

精度损失问题

在页面点击提交测试:
乐优商城之下单(二十一)_第22张图片
成功生成订单!下面看看数据库的订单号,如下:
乐优商城之下单(二十一)_第23张图片
好像有什么不对?订单号的最后2位不正确啊!

这其实是因为JS的长整数精度有限,java的Long类型数据超出了范围,所以出现了精度损失。

我们后台返回的是Json的字符串,在axios内部会自动调用 JSON.parse()方法把json字符串转为JS数据,就会出现进度损失。如果不进行转换,依然当做字符串来使用,就不会有问题了。

因此,我们重写axios对响应的处理回调函数:
乐优商城之下单(二十一)_第24张图片
效果如下:
乐优商城之下单(二十一)_第25张图片

你可能感兴趣的:(乐优商城,python,java,vue,大数据,javascript)