订单系统开发

一、订单系统基本框架的搭建

1、创建maven工程

pom.xml文件内容如下:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>com.enjoyshop.parent</groupId>
        <artifactId>enjoyshop-parent</artifactId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <groupId>com.enjoyshop.order</groupId>
    <artifactId>enjoyshop-order</artifactId>
    <version>1.0.0-SNAPSHOT</version>
    <packaging>war</packaging>
    <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
        </dependency>
        <dependency>
            <groupId>com.github.miemiedev</groupId>
            <artifactId>mybatis-paginator</artifactId>
            <version>1.2.9</version>
        </dependency>
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis-spring</artifactId>
        </dependency>
        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.0.9</version>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
        <!-- <dependency> <groupId>com.enjoyshop.common</groupId> <artifactId>enjoyshop-util</artifactId> <version>1.0.0-SNAPSHOT</version> </dependency> -->
        <dependency>
            <groupId>org.hibernate</groupId>
            <artifactId>hibernate-validator</artifactId>
            <version>5.1.2.Final</version>
        </dependency>
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.1</version>
        </dependency>
        <dependency>
            <groupId>org.quartz-scheduler</groupId>
            <artifactId>quartz</artifactId>
            <version>2.2.1</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context-support</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>joda-time</groupId>
            <artifactId>joda-time</artifactId>
        </dependency>
        <!-- RabbitMQ -->
        <dependency>
            <groupId>org.springframework.amqp</groupId>
            <artifactId>spring-rabbit</artifactId>
            <version>1.4.0.RELEASE</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <!-- 配置Tomcat插件 -->
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <configuration>
                    <port>8084</port>
                    <path>/</path>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

2、搭建SSM框架

这里不做详述,可参考具体源码。

3、订单相关的数据表结构

订单基本信息表

CREATE TABLE `tb_order` ( `order_id` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '订单id', `payment` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '实付金额。精确到2位小数;单位:元。如:200.07,表示:200元7分', `payment_type` int(2) DEFAULT NULL COMMENT '支付类型,1、在线支付,2、货到付款', `post_fee` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '邮费。精确到2位小数;单位:元。如:200.07,表示:200元7分', `status` int(10) DEFAULT NULL COMMENT '状态:1、未付款,2、已付款,3、未发货,4、已发货,5、交易成功,6、交易关闭', `create_time` datetime DEFAULT NULL COMMENT '订单创建时间', `update_time` datetime DEFAULT NULL COMMENT '订单更新时间', `payment_time` datetime DEFAULT NULL COMMENT '付款时间', `consign_time` datetime DEFAULT NULL COMMENT '发货时间', `end_time` datetime DEFAULT NULL COMMENT '交易完成时间', `close_time` datetime DEFAULT NULL COMMENT '交易关闭时间', `shipping_name` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT '物流名称', `shipping_code` varchar(20) COLLATE utf8_bin DEFAULT NULL COMMENT '物流单号', `user_id` bigint(20) DEFAULT NULL COMMENT '用户id', `buyer_message` varchar(100) COLLATE utf8_bin DEFAULT NULL COMMENT '买家留言', `buyer_nick` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '买家昵称', `buyer_rate` int(2) DEFAULT NULL COMMENT '买家是否已经评价', UNIQUE KEY `order_id` (`order_id`) USING BTREE, KEY `create_time` (`create_time`), KEY `buyer_nick` (`buyer_nick`), KEY `status` (`status`), KEY `payment_type` (`payment_type`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='订单基本信息表' 

订单商品表

CREATE TABLE `tb_order_item` ( `item_id` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '商品id', `order_id` varchar(50) COLLATE utf8_bin DEFAULT NULL COMMENT '订单id', `num` int(10) DEFAULT NULL COMMENT '商品购买数量', `title` varchar(200) COLLATE utf8_bin DEFAULT NULL COMMENT '商品标题', `price` bigint(50) DEFAULT NULL COMMENT '商品单价', `total_fee` bigint(50) DEFAULT NULL COMMENT '商品总金额', `pic_path` varchar(600) COLLATE utf8_bin DEFAULT NULL COMMENT '商品图片地址', KEY `order_id` (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin COMMENT='订单商品表' 

订单用户基本信息表

CREATE TABLE `tb_order_shipping` ( `order_id` varchar(50) NOT NULL COMMENT '订单ID', `receiver_name` varchar(20) DEFAULT NULL COMMENT '收货人全名', `receiver_phone` varchar(20) DEFAULT NULL COMMENT '固定电话', `receiver_mobile` varchar(30) DEFAULT NULL COMMENT '移动电话', `receiver_state` varchar(10) DEFAULT NULL COMMENT '省份', `receiver_city` varchar(10) DEFAULT NULL COMMENT '城市', `receiver_district` varchar(20) DEFAULT NULL COMMENT '区/县', `receiver_address` varchar(200) DEFAULT NULL COMMENT '收货地址,如:xx路xx号', `receiver_zip` varchar(6) DEFAULT NULL COMMENT '邮政编码,如:310001', `created` datetime DEFAULT NULL, `updated` datetime DEFAULT NULL, PRIMARY KEY (`order_id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='订单用户基本信息表' 

二、订单的创建

1、pojo

这里采用Hibernate校验框架进行校验

package com.enjoyshop.store.order.pojo;

import java.util.Date;
import java.util.List;

import javax.validation.constraints.Min;

import org.hibernate.validator.constraints.NotBlank;
import org.hibernate.validator.constraints.NotEmpty;

import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.databind.deser.std.DateDeserializers.DateDeserializer;

public class Order {
    private String orderId;//id,rowKye:id+时间戳
    @NotBlank
    private String payment;//实付金额
    private Integer paymentType; //支付类型,1、在线支付,2、货到付款
    private String postFee;//邮费
    /** * 初始阶段:1、未付款、未发货;初始化所有数据 * 付款阶段:2、已付款;更改付款时间 * 发货阶段:3、已发货;更改发货时间、更新时间、物流名称、物流单号 * 成功阶段:4、已成功;更改更新时间,交易结束时间,买家留言,是否已评价 * 关闭阶段:5、关闭; 更改更新时间,交易关闭时间。 * */
    private Integer status;//状态:1、未付款,2、已付款,3、未发货,4、已发货,5、交易成功,6、交易关闭
    @JsonDeserialize(using = DateDeserializer.class)
    private Date createTime;//创建时间
    @JsonDeserialize(using = DateDeserializer.class)
    private Date updateTime;//更新时间
    @JsonDeserialize(using = DateDeserializer.class)
    private Date paymentTime;//付款时间
    @JsonDeserialize(using = DateDeserializer.class)
    private Date consignTime;//发货时间
    @JsonDeserialize(using = DateDeserializer.class)
    private Date endTime;//交易结束时间
    @JsonDeserialize(using = DateDeserializer.class)
    private Date closeTime;//交易关闭时间
    private String shippingName;//物流名称
    private String shippingCode;//物流单号
    @Min(value = 1L)
    private Long userId;//用户id
    private String buyerMessage;//买家留言
    @NotBlank
    private String buyerNick;//买家昵称
    private Integer buyerRate;//买家是否已经评价
    @NotEmpty
    private List<OrderItem> orderItems;//商品详情

    private OrderShipping orderShipping; //物流地址信息

    public Integer getPaymentType() {
        return paymentType;
    }
    public void setPaymentType(Integer paymentType) {
        this.paymentType = paymentType;
    }

    public OrderShipping getOrderShipping() {
        return orderShipping;
    }
    public void setOrderShipping(OrderShipping orderShipping) {
        this.orderShipping = orderShipping;
    }
    public String getBuyerMessage() {
        return buyerMessage;
    }
    public void setBuyerMessage(String buyerMessage) {
        this.buyerMessage = buyerMessage;
    }
    public String getBuyerNick() {
        return buyerNick;
    }
    public void setBuyerNick(String buyerNick) {
        this.buyerNick = buyerNick;
    }
    public Integer getBuyerRate() {
        return buyerRate;
    }
    public void setBuyerRate(Integer buyerRate) {
        this.buyerRate = buyerRate;
    }
    public String getOrderId() {
        return orderId;
    }
    public void setOrderId(String orderId) {
        this.orderId = orderId;
    }
    public String getPayment() {
        return payment;
    }
    public void setPayment(String payment) {
        this.payment = payment;
    }
    public String getPostFee() {
        return postFee;
    }
    public void setPostFee(String postFee) {
        this.postFee = postFee;
    }
    public Integer getStatus() {
        return status;
    }
    public void setStatus(Integer status) {
        this.status = status;
    }
    public Date getCreateTime() {
        return createTime;
    }
    public void setCreateTime(Date createTime) {
        this.createTime = createTime;
    }
    public Date getUpdateTime() {
        return updateTime;
    }
    public void setUpdateTime(Date updateTime) {
        this.updateTime = updateTime;
    }
    public Date getPaymentTime() {
        return paymentTime;
    }
    public void setPaymentTime(Date paymentTime) {
        this.paymentTime = paymentTime;
    }
    public Date getConsignTime() {
        return consignTime;
    }
    public void setConsignTime(Date consignTime) {
        this.consignTime = consignTime;
    }
    public Date getEndTime() {
        return endTime;
    }
    public void setEndTime(Date endTime) {
        this.endTime = endTime;
    }
    public Date getCloseTime() {
        return closeTime;
    }
    public void setCloseTime(Date closeTime) {
        this.closeTime = closeTime;
    }
    public String getShippingName() {
        return shippingName;
    }
    public void setShippingName(String shippingName) {
        this.shippingName = shippingName;
    }
    public String getShippingCode() {
        return shippingCode;
    }
    public void setShippingCode(String shippingCode) {
        this.shippingCode = shippingCode;
    }
    public Long getUserId() {
        return userId;
    }
    public void setUserId(Long userId) {
        this.userId = userId;
    }
    public List<OrderItem> getOrderItems() {
        return orderItems;
    }
    public void setOrderItems(List<OrderItem> orderItems) {
        this.orderItems = orderItems;
    }





}

2、mapper

  • 抽取通用mapper接口
package com.enjoyshop.store.order.mapper;

import org.apache.ibatis.annotations.Param;

import com.enjoyshop.store.order.bean.Where;
import com.github.miemiedev.mybatis.paginator.domain.PageBounds;
import com.github.miemiedev.mybatis.paginator.domain.PageList;

public interface IMapper<T> {

    /** * 按照分页查询 * * @param bounds * @return */
    public PageList<T> queryList(PageBounds bounds);

    /** * 按照where条件分页查询 * * @param bounds * @param where * @return */
    public PageList<T> queryListByWhere(PageBounds bounds, @Param("where")Where where);

    /** * 通过ID查询数据 * * @param id * @return */
    public T queryByID(@Param("id") String id);

    /** * 通过where条件查询 * * @param where * @return */
    public T queryByWhere(@Param("where") Where where);

    /** * 新增数据 * * @param t */
    public void save(T t);

    /** * 更新数据 * * @param t */
    public void update(T t);

    /** * 通过ID删除数据 * * @param id * @return 返回受影响的条数 */
    public Integer deleteByID(@Param("id") Long id);

    /** * 通过IDs删除数据 * * @param ids * @return 返回受影响的条数 */
    public Integer deleteByIDS(@Param("ids") Long[] ids);

}
  • 订单mapper

package com.enjoyshop.store.order.mapper;

import java.util.Date;




import org.apache.ibatis.annotations.Param;

import com.enjoyshop.store.order.pojo.Order;

public interface OrderMapper extends IMapper<Order>{

    public void paymentOrderScan(@Param("date") Date date);

}
  • 相应的mapper.xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.enjoyshop.store.order.mapper.OrderMapper">

    <sql id="tableName">tb_order</sql>

    <resultMap type="Order" id="pojoResultMap" autoMapping="true">
        <id column="order_id" property="orderId"/>
        <association property="orderShipping" javaType="OrderShipping" column="order_id" select="queryOrderShippingByOrderId" autoMapping="true"></association>
        <collection property="orderItems" javaType="Arraylist" ofType="OrderItem" autoMapping="true" select="queryOrderItemByOrderId" column="order_id">
        </collection>
    </resultMap>

    <select id="queryOrderItemByOrderId" resultType="OrderItem" parameterType="String">
        SELECT * FROM tb_order_item WHERE order_id = #{orderId};
    </select>

    <select id="queryOrderShippingByOrderId" resultType="OrderShipping" parameterType="String">
        SELECT * FROM tb_order_shipping WHERE order_id = #{orderId};
    </select>

    <select id="queryList" resultMap="pojoResultMap">
        SELECT
            *
        FROM
            <include refid="tableName"/>
    </select>

    <select id="queryByID" resultMap="pojoResultMap">
        SELECT
            *
        FROM
            <include refid="tableName"/>
        WHERE order_id = #{id};
    </select>

    <select id="queryByWhere" parameterType="Where" resultMap="pojoResultMap">
        SELECT
            *
        FROM
            <include refid="tableName"/>
        WHERE  ${where.column} ${where.operater} #{where.value} LIMIT 1;
    </select>

    <select id="queryListByWhere" parameterType="Where" resultMap="pojoResultMap">
        SELECT
            *
        FROM
            <include refid="tableName"/>
        WHERE  ${where.column} ${where.operater} #{where.value};
    </select>

    <insert id="save"> 
        INSERT INTO <include refid="tableName"/> VALUES (#{orderId},#{payment},#{paymentType},#{postFee},#{status},#{createTime},#{updateTime},#{paymentTime},#{consignTime},#{endTime},#{closeTime},#{shippingName},#{shippingCode},#{userId},#{buyerMessage},#{buyerNick},#{buyerRate});
        INSERT INTO tb_order_item VALUES 
        <foreach collection="orderItems" item="item" separator=",">
            (#{item.itemId},#{orderId},#{item.num},#{item.title},#{item.price},#{item.totalFee},#{item.picPath})
        </foreach>
        ;
        INSERT INTO tb_order_shipping VALUES (#{orderId},#{orderShipping.receiverName},#{orderShipping.receiverPhone},#{orderShipping.receiverMobile},#{orderShipping.receiverState},#{orderShipping.receiverCity},#{orderShipping.receiverDistrict},#{orderShipping.receiverAddress},#{orderShipping.receiverZip},NOW(),NOW());
    </insert>

    <update id="update">
        UPDATE <include refid="tableName"/> 
        <set>
            <if test="payment !=null and payment != ''">
                payment = #{payment},
            </if>
            <if test="postFee !=null and postFee != ''">
                post_fee = #{postFee},
            </if>
            <if test="status !=null and status != ''">
                status = #{status},
            </if>
            <if test="updateTime !=null and updateTime != ''">
                update_time = #{updateTime},
            </if>
            <if test="paymentTime !=null and paymentTime != ''">
                payment_time = #{paymentTime},
            </if>
            <if test="consignTime !=null and consignTime != ''">
                consign_time = #{consignTime},
            </if>
            <if test="endTime !=null and endTime != ''">
                end_time = #{endTime},
            </if>
            <if test="closeTime !=null and closeTime != ''">
                close_time = #{closeTime},
            </if>
            <if test="shippingName !=null and shippingName != ''">
                shipping_name = #{shippingName},
            </if>
            <if test="shippingCode !=null and shippingCode != ''">
                shipping_code = #{shippingCode},
            </if>
            <if test="buyerMessage !=null and buyerMessage != ''">
                buyer_message = #{buyerMessage},
            </if>
            <if test="buyerRate !=null and buyerRate != ''">
                buyer_rate = #{buyerRate},
            </if>
        </set>
        WHERE order_id = #{orderId};
    </update>

    <delete id="deleteByID" parameterType="Long">
        DELETE FROM <include refid="tableName"/> WHERE order_id = #{orderId};
        DELETE FROM tb_order_item WHERE order_id = #{orderId};
    </delete>

    <delete id="deleteByIDS" parameterType="list">
        DELETE FROM <include refid="tableName"/> WHERE order_id IN 
        <foreach collection="ids" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>;
        DELETE FROM tb_order_item WHERE order_id IN 
        <foreach collection="ids" item="id" open="(" close=")" separator=",">
            #{id}
        </foreach>;
    </delete>

    <update id="paymentOrderScan" parameterType="Date">
        UPDATE tb_order SET
            status = 6,
            update_time = NOW(),
            close_time = NOW(),
            end_time = NOW()
        WHERE status = 1 AND payment_type = 1 AND create_time &lt;= #{date}
    </update>

</mapper>

需要注意的是:默认情况下,一个statement执行多条SQL语句是不支持的。需要在jdbc的连接字符串中设置,如下:

jdbc.url=jdbc:mysql://127.0.0.1:3306/enjoyshop?useUnicode=true&characterEncoding=utf8&autoReconnect=true&allowMultiQueries=true

假如项目使用UFT-8编码,mysql使用GBK编码,那么使用如下两个设置useUnicode=true&characterEncoding=UTF-8 ,作用有如下两个方面:
存数据时:数据库在存放项目数据的时候会先用UTF-8格式将数据解码成字节码,然后再将解码后的字节码重新使用GBK编码存放到数据库中。
取数据时:在从数据库中取数据的时候,数据库会先将数据库中的数据按GBK格式解码成字节码,然后再将解码后的字节码重新按UTF-8格式编码数据,最后再将数据返回给客户端。
autoReconnect=true的作用是当数据库连接异常中断时,是否自动重新连接?默认为false。
allowMultiQueries=true的作用是设置允许批量操作,这样可以在 Mapper 中执行多条SQL语句。而且一个statement执行多条SQL语句时是在同一个事务中执行的。

3、Dao层

  • 接口Dao

抽取接口可以使得程序具有扩展性,实现Dao可以提供多种策略和方法。

    /** * 创建订单 * * @param order */
    public void createOrder(Order order);
  • 实现Dao
public class OrderDAO implements IOrder{

    @Autowired
    private OrderMapper orderMapper;

    @Override
    public void createOrder(Order order) {
        this.orderMapper.save(order);
    }

}

4、service层

public EnjoyshopResult createOrder(String json) {
        Order order = null;
        try {
            order = objectMapper.readValue(json, Order.class);
            // 校验Order对象
            ValidateUtil.validate(order);
        } catch (Exception e) {
            return EnjoyshopResult.build(400, "请求参数有误!");
        }

        try {
            // 生成订单ID,规则为:userid+当前时间戳
            String orderId = order.getUserId() + "" + System.currentTimeMillis();
            order.setOrderId(orderId);

            // 设置订单的初始状态为未付款
            order.setStatus(1);

            // 设置订单的创建时间
            order.setCreateTime(new Date());
            order.setUpdateTime(order.getCreateTime());

            // 设置买家评价状态,初始为未评价
            order.setBuyerRate(0);

            // 持久化order对象
            orderDao.createOrder(order);

            //发送消息到RabbitMQ
// Map<String, Object> msg = new HashMap<String, Object>(3);
// msg.put("userId", order.getUserId());
// msg.put("orderId", order.getOrderId());
// List<Long> itemIds = new ArrayList<Long>(order.getOrderItems().size());
// for (OrderItem orderItem : order.getOrderItems()) {
// itemIds.add(orderItem.getItemId());
// }
// msg.put("itemIds", itemIds);
// this.rabbitTemplate.convertAndSend(objectMapper.writeValueAsString(msg));

            return EnjoyshopResult.ok(orderId);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return EnjoyshopResult.build(400, "保存订单失败!");
    }

这里设计了一个自定义的返回值格式EnjoyshopResult

package com.enjoyshop.store.order.bean;

import java.util.List;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;

/** * 乐购商城自定义响应结构 */
public class EnjoyshopResult {

    // 定义jackson对象
    private static final ObjectMapper MAPPER = new ObjectMapper();

    // 响应业务状态
    private Integer status;

    // 响应消息
    private String msg;

    // 响应中的数据
    private Object data;

    public static EnjoyshopResult build(Integer status, String msg, Object data) {
        return new EnjoyshopResult(status, msg, data);
    }

    public static EnjoyshopResult ok(Object data) {
        return new EnjoyshopResult(data);
    }

    public static EnjoyshopResult ok() {
        return new EnjoyshopResult(null);
    }

    public EnjoyshopResult() {

    }

    public static EnjoyshopResult build(Integer status, String msg) {
        return new EnjoyshopResult(status, msg, null);
    }

    public EnjoyshopResult(Integer status, String msg, Object data) {
        this.status = status;
        this.msg = msg;
        this.data = data;
    }

    public EnjoyshopResult(Object data) {
        this.status = 200;
        this.msg = "OK";
        this.data = data;
    }

    public Boolean isOK() {
        return this.status == 200;
    }

    public Integer getStatus() {
        return status;
    }

    public void setStatus(Integer status) {
        this.status = status;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    /** * 将json结果集转化为EnjoyshopResult对象 * * @param jsonData json数据 * @param clazz EnjoyshopResult中的object类型 * @return */
    public static EnjoyshopResult formatToPojo(String jsonData, Class<?> clazz) {
        try {
            if (clazz == null) {
                return MAPPER.readValue(jsonData, EnjoyshopResult.class);
            }
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (clazz != null) {
                if (data.isObject()) {
                    obj = MAPPER.readValue(data.traverse(), clazz);
                } else if (data.isTextual()) {
                    obj = MAPPER.readValue(data.asText(), clazz);
                }
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }

    /** * 没有object对象的转化 * * @param json * @return */
    public static EnjoyshopResult format(String json) {
        try {
            return MAPPER.readValue(json, EnjoyshopResult.class);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

    /** * Object是集合转化 * * @param jsonData json数据 * @param clazz 集合中的类型 * @return */
    public static EnjoyshopResult formatToList(String jsonData, Class<?> clazz) {
        try {
            JsonNode jsonNode = MAPPER.readTree(jsonData);
            JsonNode data = jsonNode.get("data");
            Object obj = null;
            if (data.isArray() && data.size() > 0) {
                obj = MAPPER.readValue(data.traverse(),
                        MAPPER.getTypeFactory().constructCollectionType(List.class, clazz));
            }
            return build(jsonNode.get("status").intValue(), jsonNode.get("msg").asText(), obj);
        } catch (Exception e) {
            return null;
        }
    }

}

5、controller层

    @Autowired
    private OrderService orderService;

    /** * 创建订单 * @param json * @return */
    @ResponseBody
    @RequestMapping(value = "/create" , method = RequestMethod.POST)
    public EnjoyshopResult createOrder(@RequestBody String json) {
        return orderService.createOrder(json);
    }

注意这里的两个重要注解:
@ResponseBody表示返回参数的数据类型是json数据。
@RequestBody表示接受参数的数据类型是json数据。

三、订单的查询

1、mapper接口对应方法

/** * 通过ID查询数据 * * @param id * @return */
    public T queryByID(@Param("id") String id);

对应的mapper.xml内容


    <select id="queryByID" resultMap="pojoResultMap">
        SELECT
            *
        FROM
            <include refid="tableName"/>
        WHERE order_id = #{id};
    </select>

2、Dao层实现


    @Override
    public Order queryOrderById(String orderId) {
        return this.orderMapper.queryByID(orderId);
    }

3、service层

public Order queryOrderById(String orderId) {
        Order order = orderDao.queryOrderById(orderId);
        return order;
    }

4、controller层


    /** * 根据订单ID查询订单 * @param orderId * @return */
    @ResponseBody
    @RequestMapping(value = "/query/{orderId}" ,method = RequestMethod.GET)
    public Order queryOrderById(@PathVariable("orderId") String orderId) {
        return orderService.queryOrderById(orderId);
    }

四、根据用户名进行订单的分页查询

1、mapper接口设计


    /** * 按照where条件分页查询 * * @param bounds * @param where * @return */
    public PageList<T> queryListByWhere(PageBounds bounds, @Param("where")Where where);

对应的xml内容

<select id="queryListByWhere" parameterType="Where" resultMap="pojoResultMap">
        SELECT
            *
        FROM
            <include refid="tableName"/>
        WHERE  ${where.column} ${where.operater} #{where.value};
    </select>

2、Dao层


    @Override
    public PageResult<Order> queryOrderByUserNameAndPage(String buyerNick, Integer page, Integer count) {
        PageBounds bounds = new PageBounds();
        bounds.setContainsTotalCount(true);
        bounds.setLimit(count);
        bounds.setPage(page);
        bounds.setOrders(com.github.miemiedev.mybatis.paginator.domain.Order.formString("create_time.desc"));
        PageList<Order> list = this.orderMapper.queryListByWhere(bounds, Where.build("buyer_nick", buyerNick));
        return new PageResult<Order>(list.getPaginator().getTotalCount(), list);
    }

3、service层

 public PageResult<Order> queryOrderByUserNameAndPage(String buyerNick, int page, int count) {
        return orderDao.queryOrderByUserNameAndPage(buyerNick, page, count);
    }

4、controller层

    /** * 根据用户名分页查询订单 * @param buyerNick * @param page * @param count * @return */
    @ResponseBody
    @RequestMapping("/query/{buyerNick}/{page}/{count}")
    public PageResult<Order> queryOrderByUserNameAndPage(@PathVariable("buyerNick") String buyerNick,@PathVariable("page") Integer page,@PathVariable("count") Integer count) {
        return orderService.queryOrderByUserNameAndPage(buyerNick, page, count);
    }

五、修改订单状态

1、mapper接口设计

    /** * 更新数据 * * @param t */
    public void update(T t);

对应的xml内容如下:

    <update id="update">
        UPDATE <include refid="tableName"/> 
        <set>
            <if test="payment !=null and payment != ''">
                payment = #{payment},
            </if>
            <if test="postFee !=null and postFee != ''">
                post_fee = #{postFee},
            </if>
            <if test="status !=null and status != ''">
                status = #{status},
            </if>
            <if test="updateTime !=null and updateTime != ''">
                update_time = #{updateTime},
            </if>
            <if test="paymentTime !=null and paymentTime != ''">
                payment_time = #{paymentTime},
            </if>
            <if test="consignTime !=null and consignTime != ''">
                consign_time = #{consignTime},
            </if>
            <if test="endTime !=null and endTime != ''">
                end_time = #{endTime},
            </if>
            <if test="closeTime !=null and closeTime != ''">
                close_time = #{closeTime},
            </if>
            <if test="shippingName !=null and shippingName != ''">
                shipping_name = #{shippingName},
            </if>
            <if test="shippingCode !=null and shippingCode != ''">
                shipping_code = #{shippingCode},
            </if>
            <if test="buyerMessage !=null and buyerMessage != ''">
                buyer_message = #{buyerMessage},
            </if>
            <if test="buyerRate !=null and buyerRate != ''">
                buyer_rate = #{buyerRate},
            </if>
        </set>
        WHERE order_id = #{orderId};
    </update>

2、Dao层

    @Override
    public ResultMsg changeOrderStatus(Order order) {
        try {
            order.setUpdateTime(new Date());
            this.orderMapper.update(order);
        } catch (Exception e) {
            e.printStackTrace();
            return new ResultMsg("500", "更新订单出错!");
        }
        return new ResultMsg("200", "更新成功!");
    }

3、service层

 public ResultMsg changeOrderStatus(String json) {
        Order order = null;
        try {
            order = objectMapper.readValue(json, Order.class);
        } catch (Exception e) {
            e.printStackTrace();
            return new ResultMsg("400", "请求参数有误!");
        }
        return this.orderDao.changeOrderStatus(order);
    }

4、controller层

    /** * 修改订单状态 * @param json * @return */
    @ResponseBody
    @RequestMapping(value="/changeOrderStatus",method = RequestMethod.POST)
    public ResultMsg changeOrderStatus(@RequestBody String json) {
        return orderService.changeOrderStatus(json);
    }

五、前台系统集成订单功能

1、订单页面controller

页面通过ModelAndView对象来封装数据到前端页面来展示。

@RequestMapping(value = "{itemId}", method = RequestMethod.GET)
    public ModelAndView toOrder(@PathVariable("itemId") Long itemId) {
        ModelAndView mv = new ModelAndView("order");
        Item item = this.itemService.queryItemById(itemId);
        mv.addObject("item", item);
        return mv;
    }

2、使用SpringMVC拦截器来实现校验用户登录状态。

关于SpringMVC拦截器的使用请参考这里。点我
在使用拦截器先解决一个问题:
当我们使用拦截器后,通过token查询用户信息会进行了2次。一次是在拦截器中查询,一次是在Controller查询,存在性能和资源浪费问题。那么如何将拦截器中的数据传递到Controller?答案是使用ThreadLocal实现,因为进入tomcat和产生响应前,都处于同一个线程中。

  • ThreadLocal解决方案

定义ThreadLocal

package com.enjoyshop.web.threadlocal;

import com.enjoyshop.web.bean.User;

public class UserThreadLocal {

    private static final ThreadLocal<User> LOCAL = new ThreadLocal<User>();

    public static void set(User user) {
        LOCAL.set(user);
    }

    public static User get() {
        return LOCAL.get();
    }

}

这样我们只需要在拦截器中将User对象放置到ThreadLocal中就可以了。

  • 自定义拦截器
package com.enjoyshop.web.handlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import com.enjoyshop.common.utils.CookieUtils;
import com.enjoyshop.web.bean.User;
import com.enjoyshop.web.service.PropertieService;
import com.enjoyshop.web.service.UserService;
import com.enjoyshop.web.threadlocal.UserThreadLocal;

public class UserLoginHandlerInterceptor implements HandlerInterceptor {

    public static final String COOKIE_NAME = "TT_TOKEN";

    @Autowired
    private PropertieService propertieService;

    @Autowired
    private UserService userService;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {

        UserThreadLocal.set(null);//清空当前线程中的User对象

        String loginUrl = propertieService.ENJOYSHOP_SSO_URL + "/user/login.html";
        String token = CookieUtils.getCookieValue(request, COOKIE_NAME);
        if (StringUtils.isEmpty(token)) {
            // 未登录状态
            response.sendRedirect(loginUrl);
            return false;
        }

        User user = this.userService.queryUserByToken(token);
        if (null == user) {
            // 未登录状态
            response.sendRedirect(loginUrl);
            return false;
        }
        //处于登录状态

        UserThreadLocal.set(user); //将User对象放置到ThreadLocal中
        return true;
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
            ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler,
            Exception ex) throws Exception {

    }

}
  • 配置拦截器
    <mvc:interceptors>
      <mvc:interceptor>
        <mvc:mapping path="/order/**"/>
        <bean class="com.enjoyshop.web.handlerInterceptor.UserLoginHandlerInterceptor"></bean>
      </mvc:interceptor>
    </mvc:interceptors>

3、接受订单请求

  • controller层实现
    @RequestMapping(value = "submit", method = RequestMethod.POST)
    @ResponseBody
    public Map<String, Object> submitOrder(Order order) {
        Map<String, Object> result = new HashMap<String, Object>();
        String orderId = this.orderService.submitOrder(order);
        if (StringUtils.isEmpty(orderId)) {
            result.put("status", 300);
        } else {
            result.put("status", 200);
            result.put("data", orderId);
        }

        return result;
    }
  • service层实现
 public String submitOrder(Order order) {
        User user = UserThreadLocal.get(); // 从本地线程中获取User对象
        order.setUserId(user.getId());
        order.setBuyerNick(user.getUsername());
        try {
            String url = ENJOYSHOP_ORDER_URL + "/order/create";
            HttpResult httpResult = this.apiService.doPostJson(url, MAPPER.writeValueAsString(order));
            if (httpResult.getCode().intValue() == 200) {
                String jsonData = httpResult.getData();
                JsonNode jsonNode = MAPPER.readTree(jsonData);
                if (jsonNode.get("status").intValue() == 200) {
                    // 订单提交成功,返回订单号
                    return jsonNode.get("data").asText();
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }
  • 在ApiService中扩展doPostJson方法来支持提交json数据
public HttpResult doPostJson(String url, String json) throws IOException {
        // 创建http POST请求
        HttpPost httpPost = new HttpPost(url);
        httpPost.setConfig(requestConfig);
        if (null != json) {
            // 构造一个字符串式的实体
            StringEntity stringEntity = new StringEntity(json, ContentType.APPLICATION_JSON);
            // 将请求实体设置到httpPost对象中
            httpPost.setEntity(stringEntity);
        }

        CloseableHttpResponse response = null;
        try {
            // 执行请求
            response = getHttpClient().execute(httpPost);
            return new HttpResult(response.getStatusLine().getStatusCode(),
                    EntityUtils.toString(response.getEntity(), "UTF-8"));
        } finally {
            if (response != null) {
                response.close();
            }
        }
    }
  • 订单成功页面控制逻辑

controller层

@RequestMapping("success")
    public ModelAndView success(@RequestParam("id") String orderId) {
        ModelAndView mv = new ModelAndView("success");
        Order order = this.orderService.queryOrderById(orderId);
        mv.addObject("order", order);
        // 当前时间推后2天,格式化:xx月xx日
        mv.addObject("date", new DateTime().plusDays(2).toString("MM月dd日"));
        return mv;
    }

service层

public Order queryOrderById(String orderId) {
        String url = ENJOYSHOP_ORDER_URL + "/order/query/" + orderId;
        try {
            String jsonData = this.apiService.doGet(url);
            if (StringUtils.isNotEmpty(jsonData)) {
                return MAPPER.readValue(jsonData, Order.class);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

你可能感兴趣的:(java,spring,javaweb)