秒杀项目实践(一)——搭建基本功能

PunkLu 2019年12月22日 53次浏览

Spring Boot项目创建

使用IDEA自带的Spring Initializr创建最新的Spring Boot项目,填写完groupId、artifactId和项目文件位置之后点击Finish创建。

添加Spring Boot的maven依赖

<?xml version="1.0" encoding="UTF-8"?>

<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>

  <groupId>tech.punklu.seckill</groupId>
  <artifactId>seckill</artifactId>
  <version>1.0-SNAPSHOT</version>

  <!-- Spring Boot 父模块 -->
  <parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.1.4.RELEASE</version>
  </parent>

  <name>seckill</name>
  <!-- FIXME change it to the project's website -->
  <url>http://www.example.com</url>

  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <maven.compiler.source>1.7</maven.compiler.source>
    <maven.compiler.target>1.7</maven.compiler.target>
  </properties>

  <dependencies>

    <!-- Spring Boot web项目依赖 -->
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- mybatis 依赖 -->
    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>1.3.1</version>
    </dependency>

    <!-- mysql 连接器 -->
    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
    </dependency>

    <!-- druid连接池 -->
    <dependency>
      <groupId>com.alibaba</groupId>
      <artifactId>druid</artifactId>
      <version>1.1.3</version>
    </dependency>

    <dependency>
      <groupId>org.apache.commons</groupId>
      <artifactId>commons-lang3</artifactId>
      <version>3.7</version>
    </dependency>

    <dependency>
      <groupId>org.hibernate</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>5.2.4.Final</version>
    </dependency>

    <dependency>
      <groupId>joda-time</groupId>
      <artifactId>joda-time</artifactId>
      <version>2.9.1</version>
    </dependency>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
  </dependencies>

  <build>
    <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
      <plugins>
        <!-- clean lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#clean_Lifecycle -->
        <plugin>
          <artifactId>maven-clean-plugin</artifactId>
          <version>3.1.0</version>
        </plugin>
        <!-- default lifecycle, jar packaging: see https://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging -->
        <plugin>
          <artifactId>maven-resources-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-compiler-plugin</artifactId>
          <version>3.8.0</version>
        </plugin>
        <plugin>
          <artifactId>maven-surefire-plugin</artifactId>
          <version>2.22.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-jar-plugin</artifactId>
          <version>3.0.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-install-plugin</artifactId>
          <version>2.5.2</version>
        </plugin>
        <plugin>
          <artifactId>maven-deploy-plugin</artifactId>
          <version>2.8.2</version>
        </plugin>
        <!-- site lifecycle, see https://maven.apache.org/ref/current/maven-core/lifecycles.html#site_Lifecycle -->
        <plugin>
          <artifactId>maven-site-plugin</artifactId>
          <version>3.7.1</version>
        </plugin>
        <plugin>
          <artifactId>maven-project-info-reports-plugin</artifactId>
          <version>3.0.0</version>
        </plugin>
      </plugins>
    </pluginManagement>
    <plugins>
      <plugin>
        <groupId>org.apache.maven.plugins</groupId>
        <artifactId>maven-compiler-plugin</artifactId>
        <configuration>
          <source>8</source>
          <target>8</target>
        </configuration>
      </plugin>
    </plugins>
  </build>
</project>

修改application.properties

server.port=8090

# mybatis xml文件路径
mybatis.mapper-locations=classpath:mapping/*.xml

# 指定数据库
spring.datasource.name=seckill
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/seckill?serverTimezone=GMT%2B8
spring.datasource.username=root
spring.datasource.password=123456

# 使用druid数据源
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

# 配置日志打印级别为debug
logging.level.tech.punklu.seckill.dao=debug

在resources下新建mapping目录,以存放mybatis的xml文件。

初始化项目

新建以下package:

  1. controller

    spring mvc必需的控制器类所在的包

  2. dao

    MyBatis接口所在的包

  3. dataobject

    与数据库表对应的DO类型的类所在的包

  4. error

    通用异常相关类所在的包

  5. response

    通用请求响应相关封装类所在的包

  6. service

    秒杀系统相关服务层所在的包

  7. validator

    字段校验相关封装类所在的包

修改Spring Boot项目启动类如下:

@SpringBootApplication(scanBasePackages = {"tech.punklu.seckill"})
@MapperScan("tech.punklu.seckill.dao")  // 扫描mybatis mapper接口所在的包
@RestController
public class App {

    public static void main( String[] args )
    {
        System.out.println( "Hello World!" );
        SpringApplication.run(App.class);
    }
}

项目开发

创建数据库

创建用户表

-- 创建用户信息表
DROP TABLE IF EXISTS `user_info`;
CREATE TABLE `user_info` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(64) NOT NULL DEFAULT '',  -- 用户名
  `gender` tinyint(4) NOT NULL DEFAULT '0' COMMENT '//1代表男性,2代表女性',
  `age` int(11) NOT NULL DEFAULT '0', -- 年龄
  `telphone` varchar(255) NOT NULL DEFAULT '', -- 电话号码
  `register_mode` varchar(255) NOT NULL DEFAULT '' COMMENT '//byphone,bywechat,byalipay', -- 注册方式
  `third_party_id` varchar(64) NOT NULL DEFAULT '', -- 第三方账号id
  PRIMARY KEY (`id`),
  UNIQUE KEY `telphone_unique_index` (`telphone`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=23 DEFAULT CHARSET=utf8;

创建用户密码表

为了安全起见,将用户密码与用户表分开存储:

-- 创建用户密码表
DROP TABLE IF EXISTS `user_password`;
CREATE TABLE `user_password` (
  `id` int(11) NOT NULL AUTO_INCREMENT, -- 自增编号
  `encrpt_password` varchar(128) NOT NULL DEFAULT '', -- 加密后的密码
  `user_id` int(11) NOT NULL DEFAULT '0', -- 用户id
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=14 DEFAULT CHARSET=utf8;

创建订单信息表

-- 创建订单信息表
DROP TABLE IF EXISTS `order_info`;
CREATE TABLE `order_info` (
  `id` varchar(32) NOT NULL,  -- 订单编号
  `user_id` int(11) NOT NULL DEFAULT '0', -- 用户id
  `item_id` int(11) NOT NULL DEFAULT '0', -- 商品id
  `item_price` double NOT NULL DEFAULT '0',  -- 商品价格
  `amount` int(11) NOT NULL DEFAULT '0',   -- 数量
  `order_price` double NOT NULL DEFAULT '0', -- 订单金额
  `promo_id` int(11) NOT NULL DEFAULT '0', -- 秒杀活动编号
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

创建商品表

-- 创建商品表
DROP TABLE IF EXISTS `item`;
CREATE TABLE `item` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `title` varchar(64) NOT NULL DEFAULT '',
  `price` double(10,0) NOT NULL DEFAULT '0',
  `description` varchar(500) NOT NULL DEFAULT '',
  `sales` int(11) NOT NULL DEFAULT '0',
  `img_url` varchar(255) NOT NULL DEFAULT '',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

创建商品库存表

为了提高商品的查询详情性能与商品的下单性能,将商品库存字段从商品表中拿出来单独作为一个表

-- 创建商品库存表
DROP TABLE IF EXISTS `item_stock`;
CREATE TABLE `item_stock` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `stock` int(11) NOT NULL DEFAULT '0',
  `item_id` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`id`),
  UNIQUE KEY `item_id_index` (`item_id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=8 DEFAULT CHARSET=utf8;

创建秒杀活动信息表

-- 创建秒杀活动信息表
DROP TABLE IF EXISTS `promo`;
CREATE TABLE `promo` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `promo_name` varchar(255) NOT NULL DEFAULT '',
  `start_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  `item_id` int(11) NOT NULL DEFAULT '0',
  `promo_item_price` double NOT NULL DEFAULT '0',
  `end_date` datetime NOT NULL DEFAULT '0000-00-00 00:00:00',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

创建序列信息表

-- 创建序列信息表
DROP TABLE IF EXISTS `sequence_info`;
CREATE TABLE `sequence_info` (
  `name` varchar(255) NOT NULL,
  `current_value` int(11) NOT NULL DEFAULT '0',
  `step` int(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

编写与数据库表对应的DAO层代码

编写用户表对应的DAO层代码

DataObject类:

package tech.punklu.seckill.dataobject;

public class UserDO {

    // 用户编号
    private Integer id;

    // 用户名称
    private String name;

    // 性别
    private Byte gender;

    // 年龄
    private Integer age;

    // 手机号
    private String telphone;

    // 注册方式
    private String registerMode;

    // 第三方平台编号
    private String thirdPartyId;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }


    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public Byte getGender() {
        return gender;
    }

    public void setGender(Byte gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getTelphone() {
        return telphone;
    }

    public void setTelphone(String telphone) {
        this.telphone = telphone == null ? null : telphone.trim();
    }

    public String getRegisterMode() {
        return registerMode;
    }

    public void setRegisterMode(String registerMode) {
        this.registerMode = registerMode == null ? null : registerMode.trim();
    }

    public String getThirdPartyId() {
        return thirdPartyId;
    }

    public void setThirdPartyId(String thirdPartyId) {
        this.thirdPartyId = thirdPartyId == null ? null : thirdPartyId.trim();
    }
}

MyBatis接口:

import tech.punklu.seckill.dataobject.UserDO;

public interface UserDOMapper {
    
    int deleteByPrimaryKey(Integer id);

    int insert(UserDO record);

    int insertSelective(UserDO record);

    UserDO selectByPrimaryKey(Integer id);

    UserDO selectByTelphone(String telphone);

    int updateByPrimaryKeySelective(UserDO record);

    int updateByPrimaryKey(UserDO record);
}

用户表MyBatis的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="tech.punklu.seckill.dao.UserDOMapper">
  <resultMap id="BaseResultMap" type="tech.punklu.seckill.dataobject.UserDO">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="name" jdbcType="VARCHAR" property="name" />
    <result column="gender" jdbcType="TINYINT" property="gender" />
    <result column="age" jdbcType="INTEGER" property="age" />
    <result column="telphone" jdbcType="VARCHAR" property="telphone" />
    <result column="register_mode" jdbcType="VARCHAR" property="registerMode" />
    <result column="third_party_id" jdbcType="VARCHAR" property="thirdPartyId" />
  </resultMap>
  <sql id="Base_Column_List">
    id, name, gender, age, telphone, register_mode, third_party_id
  </sql>


  <select id="selectByTelphone" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from user_info
    where telphone = #{telphone,jdbcType=VARCHAR}
  </select>

  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from user_info
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from user_info
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="tech.punklu.seckill.dataobject.UserDO">
    insert into user_info (id, name, gender, 
      age, telphone, register_mode, 
      third_party_id)
    values (#{id,jdbcType=INTEGER}, #{name,jdbcType=VARCHAR}, #{gender,jdbcType=TINYINT}, 
      #{age,jdbcType=INTEGER}, #{telphone,jdbcType=VARCHAR}, #{registerMode,jdbcType=VARCHAR}, 
      #{thirdPartyId,jdbcType=VARCHAR})
  </insert>
  <insert id="insertSelective" parameterType="tech.punklu.seckill.dataobject.UserDO" keyProperty="id" useGeneratedKeys="true">
    insert into user_info
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="name != null">
        name,
      </if>
      <if test="gender != null">
        gender,
      </if>
      <if test="age != null">
        age,
      </if>
      <if test="telphone != null">
        telphone,
      </if>
      <if test="registerMode != null">
        register_mode,
      </if>
      <if test="thirdPartyId != null">
        third_party_id,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=INTEGER},
      </if>
      <if test="name != null">
        #{name,jdbcType=VARCHAR},
      </if>
      <if test="gender != null">
        #{gender,jdbcType=TINYINT},
      </if>
      <if test="age != null">
        #{age,jdbcType=INTEGER},
      </if>
      <if test="telphone != null">
        #{telphone,jdbcType=VARCHAR},
      </if>
      <if test="registerMode != null">
        #{registerMode,jdbcType=VARCHAR},
      </if>
      <if test="thirdPartyId != null">
        #{thirdPartyId,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="tech.punklu.seckill.dataobject.UserDO">
    update user_info
    <set>
      <if test="name != null">
        name = #{name,jdbcType=VARCHAR},
      </if>
      <if test="gender != null">
        gender = #{gender,jdbcType=TINYINT},
      </if>
      <if test="age != null">
        age = #{age,jdbcType=INTEGER},
      </if>
      <if test="telphone != null">
        telphone = #{telphone,jdbcType=VARCHAR},
      </if>
      <if test="registerMode != null">
        register_mode = #{registerMode,jdbcType=VARCHAR},
      </if>
      <if test="thirdPartyId != null">
        third_party_id = #{thirdPartyId,jdbcType=VARCHAR},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="tech.punklu.seckill.dataobject.UserDO">
    update user_info
    set name = #{name,jdbcType=VARCHAR},
      gender = #{gender,jdbcType=TINYINT},
      age = #{age,jdbcType=INTEGER},
      telphone = #{telphone,jdbcType=VARCHAR},
      register_mode = #{registerMode,jdbcType=VARCHAR},
      third_party_id = #{thirdPartyId,jdbcType=VARCHAR}
    where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>

编写用户密码表对应的DAO层代码

DataObject类:

package tech.punklu.seckill.dataobject;

public class UserPasswordDO {

    // 编号
    private Integer id;

    // 加密后的密码
    private String encrptPassword;

    // 用户id
    private Integer userId;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getEncrptPassword() {
        return encrptPassword;
    }

    public void setEncrptPassword(String encrptPassword) {
        this.encrptPassword = encrptPassword == null ? null : encrptPassword.trim();
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }
}

MyBatis接口:

import tech.punklu.seckill.dataobject.UserPasswordDO;

public interface UserPasswordDOMapper {
    
    int deleteByPrimaryKey(Integer id);

    int insert(UserPasswordDO record);

    int insertSelective(UserPasswordDO record);

    UserPasswordDO selectByPrimaryKey(Integer id);

    UserPasswordDO selectByUserId(Integer userId);

    int updateByPrimaryKeySelective(UserPasswordDO record);

    int updateByPrimaryKey(UserPasswordDO record);
}

用户密码表MyBatis的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="tech.punklu.seckill.dao.UserPasswordDOMapper">
  <resultMap id="BaseResultMap" type="tech.punklu.seckill.dataobject.UserPasswordDO">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="encrpt_password" jdbcType="VARCHAR" property="encrptPassword" />
    <result column="user_id" jdbcType="INTEGER" property="userId" />
  </resultMap>
  <sql id="Base_Column_List">
    id, encrpt_password, user_id
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from user_password
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from user_password
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="tech.punklu.seckill.dataobject.UserPasswordDO">
    insert into user_password (id, encrpt_password, user_id
      )
    values (#{id,jdbcType=INTEGER}, #{encrptPassword,jdbcType=VARCHAR}, #{userId,jdbcType=INTEGER}
      )
  </insert>
  <insert id="insertSelective" parameterType="tech.punklu.seckill.dataobject.UserPasswordDO">
    insert into user_password
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="encrptPassword != null">
        encrpt_password,
      </if>
      <if test="userId != null">
        user_id,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=INTEGER},
      </if>
      <if test="encrptPassword != null">
        #{encrptPassword,jdbcType=VARCHAR},
      </if>
      <if test="userId != null">
        #{userId,jdbcType=INTEGER},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="tech.punklu.seckill.dataobject.UserPasswordDO">
    update user_password
    <set>
      <if test="encrptPassword != null">
        encrpt_password = #{encrptPassword,jdbcType=VARCHAR},
      </if>
      <if test="userId != null">
        user_id = #{userId,jdbcType=INTEGER},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="tech.punklu.seckill.dataobject.UserPasswordDO">
    update user_password
    set encrpt_password = #{encrptPassword,jdbcType=VARCHAR},
      user_id = #{userId,jdbcType=INTEGER}
    where id = #{id,jdbcType=INTEGER}
  </update>

  <select id="selectByUserId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from user_password
    where user_id = #{userId,jdbcType=INTEGER}
  </select>
</mapper>

编写订单信息表对应的DAO层代码

DataObject对象:

package tech.punklu.seckill.dataobject;

public class OrderDO {

    // 订单编号
    private String id;

    // 用户id
    private Integer userId;

    // 商品id
    private Integer itemId;

    // 商品价格1
    private Double itemPrice;

    // 商品数量
    private Integer amount;

    // 订单价格
    private Double orderPrice;

    // 秒杀产品的编号
    private Integer promoId;

    public Integer getPromoId() {
        return promoId;
    }

    public void setPromoId(Integer promoId) {
        this.promoId = promoId;
    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id == null ? null : id.trim();
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public Integer getItemId() {
        return itemId;
    }

    public void setItemId(Integer itemId) {
        this.itemId = itemId;
    }

    public Double getItemPrice() {
        return itemPrice;
    }

    public void setItemPrice(Double itemPrice) {
        this.itemPrice = itemPrice;
    }

    public Integer getAmount() {
        return amount;
    }

    public void setAmount(Integer amount) {
        this.amount = amount;
    }

    public Double getOrderPrice() {
        return orderPrice;
    }

    public void setOrderPrice(Double orderPrice) {
        this.orderPrice = orderPrice;
    }
}

MyBatis接口文件:

package tech.punklu.seckill.dao;

import tech.punklu.seckill.dataobject.OrderDO;

public interface OrderDOMapper {

    int deleteByPrimaryKey(String id);

    int insert(OrderDO record);

    int insertSelective(OrderDO record);

    OrderDO selectByPrimaryKey(String id);

    int updateByPrimaryKeySelective(OrderDO record);

    int updateByPrimaryKey(OrderDO record);
}

订单信息表对应MyBatis的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="tech.punklu.seckill.dao.OrderDOMapper">
  <resultMap id="BaseResultMap" type="tech.punklu.seckill.dataobject.OrderDO">
    <id column="id" jdbcType="VARCHAR" property="id" />
    <result column="user_id" jdbcType="INTEGER" property="userId" />
    <result column="item_id" jdbcType="INTEGER" property="itemId" />
    <result column="promo_id" jdbcType="INTEGER" property="promoId" />
    <result column="item_price" jdbcType="DOUBLE" property="itemPrice" />
    <result column="amount" jdbcType="INTEGER" property="amount" />
    <result column="order_price" jdbcType="DOUBLE" property="orderPrice" />
  </resultMap>
  <sql id="Base_Column_List">
    id, user_id, item_id, promo_id,item_price, amount, order_price
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from order_info
    where id = #{id,jdbcType=VARCHAR}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.String">
    delete from order_info
    where id = #{id,jdbcType=VARCHAR}
  </delete>
  <insert id="insert" parameterType="tech.punklu.seckill.dataobject.OrderDO">
    insert into order_info (id, user_id, item_id, order_id,
      item_price, amount, order_price
      )
    values (#{id,jdbcType=VARCHAR}, #{userId,jdbcType=INTEGER}, #{itemId,jdbcType=INTEGER}, ,#{promoId,jdbcType=INTEGER},
    #{itemPrice,jdbcType=DOUBLE}, #{amount,jdbcType=INTEGER}, #{orderPrice,jdbcType=DOUBLE}
      )
  </insert>
  <insert id="insertSelective" parameterType="tech.punklu.seckill.dataobject.OrderDO">
    insert into order_info
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="userId != null">
        user_id,
      </if>
      <if test="itemId != null">
        item_id,
      </if>
      <if test="promoId != null">
        promo_id,
      </if>
      <if test="itemPrice != null">
        item_price,
      </if>
      <if test="amount != null">
        amount,
      </if>
      <if test="orderPrice != null">
        order_price,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=VARCHAR},
      </if>
      <if test="userId != null">
        #{userId,jdbcType=INTEGER},
      </if>
      <if test="itemId != null">
        #{itemId,jdbcType=INTEGER},
      </if>
      <if test="promoId != null">
        #{promoId,jdbcType=INTEGER},
      </if>
      <if test="itemPrice != null">
        #{itemPrice,jdbcType=DOUBLE},
      </if>
      <if test="amount != null">
        #{amount,jdbcType=INTEGER},
      </if>
      <if test="orderPrice != null">
        #{orderPrice,jdbcType=DOUBLE},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="tech.punklu.seckill.dataobject.OrderDO">
    update order_info
    <set>
      <if test="userId != null">
        user_id = #{userId,jdbcType=INTEGER},
      </if>
      <if test="itemId != null">
        item_id = #{itemId,jdbcType=INTEGER},
      </if>
      <if test="itemPrice != null">
        item_price = #{itemPrice,jdbcType=DOUBLE},
      </if>
      <if test="amount != null">
        amount = #{amount,jdbcType=INTEGER},
      </if>
      <if test="orderPrice != null">
        order_price = #{orderPrice,jdbcType=DOUBLE},
      </if>
    </set>
    where id = #{id,jdbcType=VARCHAR}
  </update>
  <update id="updateByPrimaryKey" parameterType="tech.punklu.seckill.dataobject.OrderDO">
    update order_info
    set user_id = #{userId,jdbcType=INTEGER},
      item_id = #{itemId,jdbcType=INTEGER},
      item_price = #{itemPrice,jdbcType=DOUBLE},
      amount = #{amount,jdbcType=INTEGER},
      order_price = #{orderPrice,jdbcType=DOUBLE}
    where id = #{id,jdbcType=VARCHAR}
  </update>
</mapper>

编写商品表对应的DAO层代码

DataObject对象:

package tech.punklu.seckill.dataobject;

public class ItemDO {

    // 商品编号
    private Integer id;

    // 标题
    private String title;

    // 价格
    private Double price;

    // 描述
    private String description;

    // 数量
    private Integer sales;

    // 商品图片
    private String imgUrl;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title == null ? null : title.trim();
    }

    public Double getPrice() {
        return price;
    }

    public void setPrice(Double price) {
        this.price = price;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description == null ? null : description.trim();
    }

    public Integer getSales() {
        return sales;
    }

    public void setSales(Integer sales) {
        this.sales = sales;
    }

    public String getImgUrl() {
        return imgUrl;
    }

    public void setImgUrl(String imgUrl) {
        this.imgUrl = imgUrl == null ? null : imgUrl.trim();
    }
}

MyBatis接口:

package tech.punklu.seckill.dao;

import tech.punklu.seckill.dataobject.ItemDO;
import org.apache.ibatis.annotations.Param;

import java.util.List;

public interface ItemDOMapper {

    List<ItemDO> listItem();

    int deleteByPrimaryKey(Integer id);

    int insert(ItemDO record);

    int insertSelective(ItemDO record);

    ItemDO selectByPrimaryKey(Integer id);

    int updateByPrimaryKeySelective(ItemDO record);

    int updateByPrimaryKey(ItemDO record);

    // 增加销量
    int increaseSales(@Param("id") Integer id, @Param("amount") Integer amount);
}

商品表对应MyBatis的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="tech.punklu.seckill.dao.ItemDOMapper">
  <resultMap id="BaseResultMap" type="tech.punklu.seckill.dataobject.ItemDO">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="title" jdbcType="VARCHAR" property="title" />
    <result column="price" jdbcType="DOUBLE" property="price" />
    <result column="description" jdbcType="VARCHAR" property="description" />
    <result column="sales" jdbcType="INTEGER" property="sales" />
    <result column="img_url" jdbcType="VARCHAR" property="imgUrl" />
  </resultMap>
  <sql id="Base_Column_List">
    id, title, price, description, sales, img_url
  </sql>

  <select id="listItem" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from item order by sales DESC;
  </select>


  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from item
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from item
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="tech.punklu.seckill.dataobject.ItemDO" useGeneratedKeys="true" keyProperty="id">
    insert into item (id, title, price, 
      description, sales, img_url
      )
    values (#{id,jdbcType=INTEGER}, #{title,jdbcType=VARCHAR}, #{price,jdbcType=DOUBLE}, 
      #{description,jdbcType=VARCHAR}, #{sales,jdbcType=INTEGER}, #{imgUrl,jdbcType=VARCHAR}
      )
  </insert>
  <insert id="insertSelective" parameterType="tech.punklu.seckill.dataobject.ItemDO" useGeneratedKeys="true" keyProperty="id"   >
    insert into item
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="title != null">
        title,
      </if>
      <if test="price != null">
        price,
      </if>
      <if test="description != null">
        description,
      </if>
      <if test="sales != null">
        sales,
      </if>
      <if test="imgUrl != null">
        img_url,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=INTEGER},
      </if>
      <if test="title != null">
        #{title,jdbcType=VARCHAR},
      </if>
      <if test="price != null">
        #{price,jdbcType=DOUBLE},
      </if>
      <if test="description != null">
        #{description,jdbcType=VARCHAR},
      </if>
      <if test="sales != null">
        #{sales,jdbcType=INTEGER},
      </if>
      <if test="imgUrl != null">
        #{imgUrl,jdbcType=VARCHAR},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="tech.punklu.seckill.dataobject.ItemDO">
    update item
    <set>
      <if test="title != null">
        title = #{title,jdbcType=VARCHAR},
      </if>
      <if test="price != null">
        price = #{price,jdbcType=DOUBLE},
      </if>
      <if test="description != null">
        description = #{description,jdbcType=VARCHAR},
      </if>
      <if test="sales != null">
        sales = #{sales,jdbcType=INTEGER},
      </if>
      <if test="imgUrl != null">
        img_url = #{imgUrl,jdbcType=VARCHAR},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="tech.punklu.seckill.dataobject.ItemDO">
    update item
    set title = #{title,jdbcType=VARCHAR},
      price = #{price,jdbcType=DOUBLE},
      description = #{description,jdbcType=VARCHAR},
      sales = #{sales,jdbcType=INTEGER},
      img_url = #{imgUrl,jdbcType=VARCHAR}
    where id = #{id,jdbcType=INTEGER}
  </update>

  <update id="increaseSales" >
    update item
    set sales = sales + #{amount}
    where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>

编写商品库存表对应的DAO层代码

DataObject对象:

package tech.punklu.seckill.dataobject;

public class ItemStockDO {

    // 编号
    private Integer id;

    // 库存数量
    private Integer stock;

    // 商品编号
    private Integer itemId;


    public Integer getId() {
        return id;
    }


    public void setId(Integer id) {
        this.id = id;
    }


    public Integer getStock() {
        return stock;
    }


    public void setStock(Integer stock) {
        this.stock = stock;
    }

    public Integer getItemId() {
        return itemId;
    }

    public void setItemId(Integer itemId) {
        this.itemId = itemId;
    }
}

MyBatis层接口:

package tech.punklu.seckill.dao;

import tech.punklu.seckill.dataobject.ItemStockDO;
import org.apache.ibatis.annotations.Param;

public interface ItemStockDOMapper {
   
    int deleteByPrimaryKey(Integer id);

    
    int insert(ItemStockDO record);

    
    int insertSelective(ItemStockDO record);

    
    ItemStockDO selectByPrimaryKey(Integer id);

    ItemStockDO selectByItemId(Integer itemId);

    /**
     * 扣减库存
     * @param itemId
     * @param amount
     * @return
     */
    int decreaseStock(@Param("itemId") Integer itemId, @Param("amount") Integer amount);

    int updateByPrimaryKeySelective(ItemStockDO record);


    int updateByPrimaryKey(ItemStockDO record);
}

商品库存表对应MyBatis的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="tech.punklu.seckill.dao.ItemStockDOMapper">
  <resultMap id="BaseResultMap" type="tech.punklu.seckill.dataobject.ItemStockDO">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="stock" jdbcType="INTEGER" property="stock" />
    <result column="item_id" jdbcType="INTEGER" property="itemId" />
  </resultMap>
  <sql id="Base_Column_List">
    id, stock, item_id
  </sql>

  <select id="selectByItemId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from item_stock
    where item_id = #{itemId,jdbcType=INTEGER}
  </select>

  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from item_stock
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from item_stock
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="tech.punklu.seckill.dataobject.ItemStockDO" useGeneratedKeys="true" keyProperty="id">
    insert into item_stock (id, stock, item_id
      )
    values (#{id,jdbcType=INTEGER}, #{stock,jdbcType=INTEGER}, #{itemId,jdbcType=INTEGER}
      )
  </insert>
  <insert id="insertSelective" parameterType="tech.punklu.seckill.dataobject.ItemStockDO" useGeneratedKeys="true" keyProperty="id">
    insert into item_stock
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="stock != null">
        stock,
      </if>
      <if test="itemId != null">
        item_id,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=INTEGER},
      </if>
      <if test="stock != null">
        #{stock,jdbcType=INTEGER},
      </if>
      <if test="itemId != null">
        #{itemId,jdbcType=INTEGER},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="tech.punklu.seckill.dataobject.ItemStockDO">
    update item_stock
    <set>
      <if test="stock != null">
        stock = #{stock,jdbcType=INTEGER},
      </if>
      <if test="itemId != null">
        item_id = #{itemId,jdbcType=INTEGER},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="tech.punklu.seckill.dataobject.ItemStockDO">
    update item_stock
    set stock = #{stock,jdbcType=INTEGER},
      item_id = #{itemId,jdbcType=INTEGER}
    where id = #{id,jdbcType=INTEGER}
  </update>


  <update id="decreaseStock">
    update item_stock
    set stock = stock - #{amount}
    where item_id = #{itemId} and stock >= #{amount}
  </update>
</mapper>

编写秒杀活动信息表对应的DAO层代码

DataObject对象:

package tech.punklu.seckill.dataobject;

import java.util.Date;

public class PromoDO {

    // 秒杀活动编号
    private Integer id;

    // 秒杀活动名称
    private String promoName;

    // 开始时间
    private Date startDate;

    // 结束时间
    private Date endDate;

    // 商品编号
    private Integer itemId;

    // 秒杀价格
    private Double promoItemPrice;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getPromoName() {
        return promoName;
    }

    public void setPromoName(String promoName) {
        this.promoName = promoName == null ? null : promoName.trim();
    }

    public Date getStartDate() {
        return startDate;
    }

    public void setStartDate(Date startDate) {
        this.startDate = startDate;
    }

    public Integer getItemId() {
        return itemId;
    }

    public void setItemId(Integer itemId) {
        this.itemId = itemId;
    }

    public Double getPromoItemPrice() {
        return promoItemPrice;
    }

    public Date getEndDate() {
        return endDate;
    }

    public void setEndDate(Date endDate) {
        this.endDate = endDate;
    }

    public void setPromoItemPrice(Double promoItemPrice) {
        this.promoItemPrice = promoItemPrice;
    }
}

MyBatis对应的接口:

package tech.punklu.seckill.dao;

import tech.punklu.seckill.dataobject.PromoDO;

public interface PromoDOMapper {
    
    int deleteByPrimaryKey(Integer id);

    
    int insert(PromoDO record);

    int insertSelective(PromoDO record);

    PromoDO selectByPrimaryKey(Integer id);

    /**
     * 根据商品编号查找对应的商品秒杀信息
     * @param itemId
     * @return
     */
    PromoDO selectByItemId(Integer itemId);

    int updateByPrimaryKeySelective(PromoDO record);

    int updateByPrimaryKey(PromoDO record);
}

秒杀活动信息表对应MyBatis的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="tech.punklu.seckill.dao.PromoDOMapper">
  <resultMap id="BaseResultMap" type="tech.punklu.seckill.dataobject.PromoDO">
    <id column="id" jdbcType="INTEGER" property="id" />
    <result column="promo_name" jdbcType="VARCHAR" property="promoName" />
    <result column="start_date" jdbcType="TIMESTAMP" property="startDate" />
    <result column="end_date" jdbcType="TIMESTAMP" property="endDate" />
    <result column="item_id" jdbcType="INTEGER" property="itemId" />
    <result column="promo_item_price" jdbcType="DOUBLE" property="promoItemPrice" />
  </resultMap>
  <sql id="Base_Column_List">
    id, promo_name, start_date, end_date,item_id, promo_item_price
  </sql>

  <select id="selectByItemId" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from promo
    where item_id = #{itemId,jdbcType=INTEGER}
  </select>


  <select id="selectByPrimaryKey" parameterType="java.lang.Integer" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from promo
    where id = #{id,jdbcType=INTEGER}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.Integer">
    delete from promo
    where id = #{id,jdbcType=INTEGER}
  </delete>
  <insert id="insert" parameterType="tech.punklu.seckill.dataobject.PromoDO">
    insert into promo (id, promo_name, start_date,end_date,
      item_id, promo_item_price)
    values (#{id,jdbcType=INTEGER}, #{promoName,jdbcType=VARCHAR}, #{startDate,jdbcType=TIMESTAMP},  #{endDate,jdbcType=TIMESTAMP},
    #{itemId,jdbcType=INTEGER}, #{promoItemPrice,jdbcType=DOUBLE})
  </insert>
  <insert id="insertSelective" parameterType="tech.punklu.seckill.dataobject.PromoDO">
    insert into promo
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="id != null">
        id,
      </if>
      <if test="promoName != null">
        promo_name,
      </if>
      <if test="startDate != null">
        start_date,
      </if>
      <if test="endDate != null">
        end_date,
      </if>
      <if test="itemId != null">
        item_id,
      </if>
      <if test="promoItemPrice != null">
        promo_item_price,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="id != null">
        #{id,jdbcType=INTEGER},
      </if>
      <if test="promoName != null">
        #{promoName,jdbcType=VARCHAR},
      </if>
      <if test="startDate != null">
        #{startDate,jdbcType=TIMESTAMP},
      </if>
      <if test="endDate != null">
        #{endDate,jdbcType=TIMESTAMP},
      </if>
      <if test="itemId != null">
        #{itemId,jdbcType=INTEGER},
      </if>
      <if test="promoItemPrice != null">
        #{promoItemPrice,jdbcType=DOUBLE},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="tech.punklu.seckill.dataobject.PromoDO">
    update promo
    <set>
      <if test="promoName != null">
        promo_name = #{promoName,jdbcType=VARCHAR},
      </if>
      <if test="startDate != null">
        start_date = #{startDate,jdbcType=TIMESTAMP},
      </if>
      <if test="endDate != null">
        end_date = #{endDate,jdbcType=TIMESTAMP},
      </if>
      <if test="itemId != null">
        item_id = #{itemId,jdbcType=INTEGER},
      </if>
      <if test="promoItemPrice != null">
        promo_item_price = #{promoItemPrice,jdbcType=DOUBLE},
      </if>
    </set>
    where id = #{id,jdbcType=INTEGER}
  </update>
  <update id="updateByPrimaryKey" parameterType="tech.punklu.seckill.dataobject.PromoDO">
    update promo
    set promo_name = #{promoName,jdbcType=VARCHAR},
      start_date = #{startDate,jdbcType=TIMESTAMP},
      end_date = #{endDate,jdbcType=TIMESTAMP},
      item_id = #{itemId,jdbcType=INTEGER},
      promo_item_price = #{promoItemPrice,jdbcType=DOUBLE}
    where id = #{id,jdbcType=INTEGER}
  </update>
</mapper>

编写序列信息表对应的DAO层代码

DataObject:

package tech.punklu.seckill.dataobject;

public class SequenceDO {


    // 序列名
    private String name;

    // 当前值
    private Integer currentValue;

    // 步长
    private Integer step;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name == null ? null : name.trim();
    }

    public Integer getCurrentValue() {
        return currentValue;
    }

    public void setCurrentValue(Integer currentValue) {
        this.currentValue = currentValue;
    }

    public Integer getStep() {
        return step;
    }

    public void setStep(Integer step) {
        this.step = step;
    }
}

MyBatis接口:

package tech.punklu.seckill.dao;

import tech.punklu.seckill.dataobject.SequenceDO;

public interface SequenceDOMapper {

    int deleteByPrimaryKey(String name);

    int insert(SequenceDO record);

    int insertSelective(SequenceDO record);

    SequenceDO selectByPrimaryKey(String name);

    /**
     *
     * @param name
     * @return
     */
    SequenceDO getSequenceByName(String name);

    int updateByPrimaryKeySelective(SequenceDO record);

    int updateByPrimaryKey(SequenceDO record);
}

序列信息表对应MyBatis的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="tech.punklu.seckill.dao.SequenceDOMapper">
  <resultMap id="BaseResultMap" type="tech.punklu.seckill.dataobject.SequenceDO">
    <id column="name" jdbcType="VARCHAR" property="name" />
    <result column="current_value" jdbcType="INTEGER" property="currentValue" />
    <result column="step" jdbcType="INTEGER" property="step" />
  </resultMap>
  <sql id="Base_Column_List">
    name, current_value, step
  </sql>

  <select id="getSequenceByName" parameterType="java.lang.String" resultMap="BaseResultMap">
    select
    <include refid="Base_Column_List" />
    from sequence_info
    where name = #{name,jdbcType=VARCHAR} for update
  </select>

  <select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from sequence_info
    where name = #{name,jdbcType=VARCHAR}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.String">
    delete from sequence_info
    where name = #{name,jdbcType=VARCHAR}
  </delete>
  <insert id="insert" parameterType="tech.punklu.seckill.dataobject.SequenceDO">
    insert into sequence_info (name, current_value, step
      )
    values (#{name,jdbcType=VARCHAR}, #{currentValue,jdbcType=INTEGER}, #{step,jdbcType=INTEGER}
      )
  </insert>
  <insert id="insertSelective" parameterType="tech.punklu.seckill.dataobject.SequenceDO">
    insert into sequence_info
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="name != null">
        name,
      </if>
      <if test="currentValue != null">
        current_value,
      </if>
      <if test="step != null">
        step,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="name != null">
        #{name,jdbcType=VARCHAR},
      </if>
      <if test="currentValue != null">
        #{currentValue,jdbcType=INTEGER},
      </if>
      <if test="step != null">
        #{step,jdbcType=INTEGER},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="tech.punklu.seckill.dataobject.SequenceDO">
    update sequence_info
    <set>
      <if test="currentValue != null">
        current_value = #{currentValue,jdbcType=INTEGER},
      </if>
      <if test="step != null">
        step = #{step,jdbcType=INTEGER},
      </if>
    </set>
    where name = #{name,jdbcType=VARCHAR}
  </update>
  <update id="updateByPrimaryKey" parameterType="tech.punklu.seckill.dataobject.SequenceDO">

    update sequence_info
    set current_value = #{currentValue,jdbcType=INTEGER},
      step = #{step,jdbcType=INTEGER}
    where name = #{name,jdbcType=VARCHAR}
  </update>
</mapper>

定义通用返回对象

返回正确信息

定义通用返回类:

package tech.punklu.seckill.response;

/**
 * 通用返回类
 */
public class CommonReturnType {

    // 对应请求的返回处理结果 "success" "fail"
    private String status;

    // 若status=success,则data内返回前端需要的json数据
    // 若status=fail,则data内使用通用的错误码格式
    private Object data;

    // 定义一个通用的创建方法
    public static CommonReturnType create(Object result){
        return CommonReturnType.create(result,"success");
    }

    public static CommonReturnType create(Object result,String status){
        CommonReturnType type = new CommonReturnType();
        type.setStatus(status);
        type.setData(result);
        return type;
    }

    public String getStatus() {
        return status;
    }

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

    public Object getData() {
        return data;
    }

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


返回错误信息

声明通用异常接口CommonError:

package tech.punklu.seckill.error;

public interface CommonError {

    int getErrCode();

    String getErrMsg();

    CommonError setErrMsg(String errMsg);
}


定义各种异常的枚举类(实现通用异常接口CommonError):

package tech.punklu.seckill.error;


public enum EmBusinessError implements CommonError {

    UNKNOWN_ERROR(10002,"未知错误"),
    PARAMETER_VALIDATION_ERROR(10001,"参数不合法"),
    USER_NOT_EXIST(20001,"用户不存在"),
    USER_LOGIN_FAIL(20002,"用户手机号或密码不正确"),
    USER_NOT_LOGIN(20003,"用户还未登录"),

    // 3000开头为交易信息错误
    STOCK_NOT_ENOUGH(30001,"库存不足");

    private EmBusinessError(int errCode,String errMsg){
        this.errCode = errCode;
        this.errMsg = errMsg;
    }

    private int errCode;

    private String errMsg;


    @Override
    public int getErrCode() {
        return this.errCode;
    }

    @Override
    public String getErrMsg() {
        return this.errMsg;
    }

    @Override
    public CommonError setErrMsg(String errMsg) {
        this.errMsg = errMsg;
        return this;
    }


}


定义业务异常类:

package tech.punklu.seckill.error;

/**
 * 包装器业务异常类实现
 */
public class BusinessException  extends Exception implements CommonError {

    private CommonError commonError;

    // 直接接受EmBusinessError的传参用于构造业务异常
    public BusinessException(CommonError commonError){
        super();
        this.commonError = commonError;
    }

    // 接收自定义errMsg的方式构造业务异常
    public BusinessException(CommonError commonError,String errMsg){
        super();
        this.commonError = commonError;
        this.commonError.setErrMsg(errMsg);
    }

    @Override
    public int getErrCode() {
        return this.commonError.getErrCode();
    }

    @Override
    public String getErrMsg() {
        return this.commonError.getErrMsg();
    }

    @Override
    public CommonError setErrMsg(String errMsg) {
        this.commonError.setErrMsg(errMsg);
        return this;
    }
}


异常处理类

定义异常捕获处理类:

package tech.punklu.seckill.controller;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
import tech.punklu.seckill.error.BusinessException;
import tech.punklu.seckill.error.EmBusinessError;
import tech.punklu.seckill.response.CommonReturnType;

import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;

public class BaseController {

    public static final String CONTENT_TYPE_FORMED = "application/x-www-form-urlencoded";

    // 定义exceptionhandler解决未被controller层吸收的exception
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody
    public Object handlerException(HttpServletRequest request, Exception ex){
        Map<String,Object> responseData = new HashMap<>();
        if (ex instanceof BusinessException){
            BusinessException businessException = (BusinessException)ex;
            responseData.put("errCode",businessException.getErrCode());
            responseData.put("errMsg",businessException.getErrMsg());
        }else {
            responseData.put("errCode", EmBusinessError.UNKNOWN_ERROR.getErrCode());
            responseData.put("errMsg",EmBusinessError.UNKNOWN_ERROR.getErrMsg());
        }
        return CommonReturnType.create(responseData,"fail");

    }
}


具体的Controller类继承这个BaseController即可实现捕获异常并按照定义好的格式返回给前端预期格式响应的功能。

编写字段校验类

定义校验结果类

package tech.punklu.seckill.validator;

import org.apache.commons.lang3.StringUtils;

import java.util.HashMap;
import java.util.Map;

public class ValidationResult {

    // 校验结果是否有错
    private boolean hasErrors = false;

    // 存放错误信息的map
    private Map<String,String> errMsgMap = new HashMap<>();

    public boolean isHasErrors() {
        return hasErrors;
    }

    public void setHasErrors(boolean hasErrors) {
        this.hasErrors = hasErrors;
    }

    public Map<String, String> getErrMsgMap() {
        return errMsgMap;
    }

    public void setErrMsgMap(Map<String, String> errMsgMap) {
        this.errMsgMap = errMsgMap;
    }

    // 实现通用的通过格式化字符串获取错误结果的msg方法
    public String getErrMsg(){
        return StringUtils.join(errMsgMap.values().toArray(),",");
    }
}


定义校验类

package tech.punklu.seckill.validator;

import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;

import javax.validation.ConstraintViolation;
import javax.validation.Validation;
import javax.validation.Validator;
import java.util.Set;

/**
 * 实现InitializingBean,spring 初始化bean之后会调用afterPropertiesSet方法
 */
@Component
public class ValidatorImpl implements InitializingBean {

    private Validator validator;

    // 实现校验方法并返回校验结果
    public ValidationResult validate(Object bean){
        final ValidationResult result = new ValidationResult();
        Set<ConstraintViolation<Object>> constraintViolationSet = validator.validate(bean);
        if (constraintViolationSet.size() > 0 ){
            // 有错误
            result.setHasErrors(true);
            constraintViolationSet.forEach(constraintViolation -> {
                String errMsg = constraintViolation.getMessage();
                String propertyName = constraintViolation.getPropertyPath().toString();
                result.getErrMsgMap().put(propertyName,errMsg);
            });
        }
        return result;
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        // 将hibernate validator通过工厂的初始化方式使其实例化
        this.validator = Validation.buildDefaultValidatorFactory().getValidator();
    }
}


编写用户相关代码

Controller层

UserController类:

package tech.punklu.seckill.controller;

import com.alibaba.druid.util.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import sun.misc.BASE64Encoder;
import tech.punklu.seckill.controller.viewobject.UserVo;
import tech.punklu.seckill.error.BusinessException;
import tech.punklu.seckill.error.EmBusinessError;
import tech.punklu.seckill.response.CommonReturnType;
import tech.punklu.seckill.service.UserService;
import tech.punklu.seckill.service.model.UserModel;

import javax.servlet.http.HttpServletRequest;
import java.io.UnsupportedEncodingException;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Random;

/**
 * 用户Controller
 */
@Controller("user")
@RequestMapping("/user")
// 设置可以跨域,但是单纯的@CrossOrigin无法做到跨域共享。
// allowCredentials = true
// @CrossOrigin中的DEFAULT_ALLOWED_HEADERS:允许跨域传输所有的headers参数,将用于使用token放入header域做session共享的跨域请求
// allowCredentials设置为true之后,需要配合前端设置xhrFields:{withCreditials:true}授信后使得跨域session共享
@CrossOrigin(allowCredentials = "true", allowedHeaders = "*")
public class UserController extends BaseController{

    @Autowired
    private UserService userService;

    @Autowired
    private HttpServletRequest request;

    /**
     * 用户登录
     * @param telphone 手机号
     * @param password 密码
     * @return
     * @throws BusinessException
     * @throws UnsupportedEncodingException
     * @throws NoSuchAlgorithmException
     */
    @RequestMapping(value = "/login",method = RequestMethod.POST,consumes = {CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType login(@RequestParam(name = "telphone")String telphone,
                                  @RequestParam(name = "password")String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
        // 入参校验
        if (org.apache.commons.lang3.StringUtils.isEmpty(telphone) || org.apache.commons.lang3.StringUtils.isEmpty(password)){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
        }
        // 用户登录服务,用来校验用户登录是否合法
        UserModel userModel = userService.validateLogin(telphone,this.EncodeByMD5(password));
        // 将登陆凭证加入到用户登录成功的session内
        this.request.getSession().setAttribute("IS_LOGIN",true);
        this.request.getSession().setAttribute("LOGIN_USER",userModel);
        return CommonReturnType.create(null);
    }

    /**
     * 用户注册
     * @param telphone
     * @param otpCode
     * @param name
     * @param gender
     * @param age
     * @return
     */
    @RequestMapping(value = "/register",method = {RequestMethod.POST},consumes = {CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType register(@RequestParam(name = "telphone")String telphone,
                                     @RequestParam(name = "otpCode") String otpCode,
                                     @RequestParam(name = "name") String name,
                                     @RequestParam(name = "gender") Integer gender,
                                     @RequestParam(name = "age") Integer age,
                                     @RequestParam(name = "password") String password) throws BusinessException, UnsupportedEncodingException, NoSuchAlgorithmException {
        // 验证手机号和对应的optcode相符合
        String inSessionOtpCode = (String)this.request.getSession().getAttribute(telphone);
        if (!StringUtils.equals(otpCode,inSessionOtpCode)){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"短信验证码不符合");
        }
        // 用户的注册流程
        UserModel userModel = new UserModel();
        userModel.setName(name);
        userModel.setGender(gender);
        userModel.setAge(age);
        userModel.setTelphone(telphone);
        userModel.setRegisterMode("byphone");
        userModel.setEncrptPassword(this.EncodeByMD5(password));

        userService.register(userModel);
        return CommonReturnType.create(null);
    }

    /**
     * 密码加密
     * @param str
     * @return
     * @throws NoSuchAlgorithmException
     * @throws UnsupportedEncodingException
     */
    public String EncodeByMD5(String str) throws NoSuchAlgorithmException, UnsupportedEncodingException {
        // 确定计算方法
        MessageDigest md5 = MessageDigest.getInstance("MD5");
        BASE64Encoder base64Encoder = new BASE64Encoder();
        // 加密字符串
        String newStr = base64Encoder.encode(md5.digest(str.getBytes("UTF-8")));
        return newStr;
    }

    /**
     * 用户获取opt短信接口
     * @param telphone
     * @return
     */
    @RequestMapping(value = "/getotp",method = RequestMethod.POST,
                                        consumes = {CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType getOtp(@RequestParam(name = "telphone")String telphone){
        // 需要按照一定的规则生成otp验证码
        Random random = new Random();
        // 设置随机数取值区间为 10000-109999
        int randomInt = random.nextInt(99999);
        randomInt += 10000;

        String otpCode = String.valueOf(randomInt);

        // 将otp验证码同对应用户的手机号关联,使用httpsession的方式绑定它的手机号与otpcode
        request.getSession().setAttribute(telphone,otpCode);

        // 将OTP验证码通过短信通道发送给用户,省略
        System.out.println("telphone = " + telphone + "& otpcode = "+otpCode);

        return CommonReturnType.create(null);
    }


    @RequestMapping("get")
    @ResponseBody
    public CommonReturnType getUser(@RequestParam(name = "id") Integer id) throws Exception {
        // 调用service服务获取对应的用户对象并返回给前端
        UserModel userModel = userService.getUserById(id);

        // 若获取的对应用户信息不存在
        if (userModel != null){
            throw new Exception(EmBusinessError.USER_NOT_EXIST.getErrMsg());
        }

        UserVo userVo =  convertFromModel(userModel);
        // 返回通用对象
        return CommonReturnType.create(userVo);
    }

    private UserVo convertFromModel(UserModel userModel){
        if (userModel == null){
            return null;
        }
        UserVo userVo = new UserVo();

        // BeanUtils.copyProperties 必须字段名和类型全都一致才可以转换成功
        BeanUtils.copyProperties(userModel,userVo);
        return userVo;
    }


}


用户VO对象

Controller层需要封装对应的VO类来完成从service层对象到controller层对象的映射,这里用户VO类代码如下:

package tech.punklu.seckill.controller.viewobject;

public class UserVo {

    // 编号
    private Integer id;

    // 名称
    private String name;

    // 性别
    private Byte gender;

    // 年龄
    private Integer age;

    // 手机号
    private String telphone;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Byte getGender() {
        return gender;
    }

    public void setGender(Byte gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getTelphone() {
        return telphone;
    }

    public void setTelphone(String telphone) {
        this.telphone = telphone;
    }
}


Service层

接口:

package tech.punklu.seckill.service;

import tech.punklu.seckill.error.BusinessException;
import tech.punklu.seckill.service.model.UserModel;

/**
 * 用户服务接口
 */
public interface UserService {

    /**
     * 通过用户id获取用户对象的方法
     * @param id
     */
    UserModel getUserById(Integer id);

    /**
     * 用户注册
     * @param userModel
     */
    void register(UserModel userModel) throws BusinessException;

    /**
     * 校验登录
     * @param telphone
     * @param password
     */
    UserModel validateLogin(String telphone,String password) throws BusinessException;
}


接口实现:

package tech.punklu.seckill.service.impl;

import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.punklu.seckill.dao.UserDOMapper;
import tech.punklu.seckill.dao.UserPasswordDOMapper;
import tech.punklu.seckill.dataobject.UserDO;
import tech.punklu.seckill.dataobject.UserPasswordDO;
import tech.punklu.seckill.error.BusinessException;
import tech.punklu.seckill.error.EmBusinessError;
import tech.punklu.seckill.service.UserService;
import tech.punklu.seckill.service.model.UserModel;
import tech.punklu.seckill.validator.ValidationResult;
import tech.punklu.seckill.validator.ValidatorImpl;

@Service
public class UserServiceImpl implements UserService {

    @Autowired
    private UserDOMapper userDOMapper;

    @Autowired
    private UserPasswordDOMapper userPasswordDOMapper;

    @Autowired
    private ValidatorImpl validator;

    @Override
    public UserModel getUserById(Integer id) {
        // 通过userdomapper获取到对应的用户dataobject
        UserDO userDO = userDOMapper.selectByPrimaryKey(id);

        if (userDO == null){
            return null;
        }
        // 通过用户id获取对应的用户加密密码信息
        UserPasswordDO userPasswordDO = userPasswordDOMapper.selectByUserId(userDO.getId());
        return convertFromDataObject(userDO,userPasswordDO);
    }

    @Override
    @Transactional
    public void register(UserModel userModel) throws BusinessException {
        if (userModel == null){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR);
        }
        ValidationResult result = validator.validate(userModel);
        if (result.isHasErrors()){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,result.getErrMsg());
        }

        // 实现model -> do方法
        UserDO userDO = convertFromModel(userModel);
        userDOMapper.insertSelective(userDO);

        userModel.setId(userDO.getId());

        UserPasswordDO userPasswordDO = convertPasswordFromModel(userModel);
        userPasswordDOMapper.insertSelective(userPasswordDO);
        return;
    }

    @Override
    public UserModel validateLogin(String telphone, String password) throws BusinessException {
        // 通过用户的手机获取用户信息
        UserDO userDO = userDOMapper.selectByTelphone(telphone);
        if (userDO == null){
            throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL);
        }
        UserPasswordDO userPasswordDO = userPasswordDOMapper.selectByUserId(userDO.getId());
        UserModel userModel = convertFromDataObject(userDO,userPasswordDO);
        // 比对用户信息内加密的密码是否和传进来的相匹配
        if (!StringUtils.equals(password,userModel.getEncrptPassword())){
            throw new BusinessException(EmBusinessError.USER_LOGIN_FAIL);
        }
        return userModel;
    }

    private UserPasswordDO convertPasswordFromModel(UserModel userModel){
        if (userModel == null){
            return null;
        }
        UserPasswordDO userPasswordDO = new UserPasswordDO();
        userPasswordDO.setEncrptPassword(userModel.getEncrptPassword());
        userPasswordDO.setUserId(userModel.getId());
        return userPasswordDO;
    }

    private UserDO convertFromModel(UserModel userModel){
        if (userModel == null){
            return null;
        }
        UserDO userDO = new UserDO();
        BeanUtils.copyProperties(userModel,userDO);
        return userDO;
    }

    private UserModel convertFromDataObject(UserDO userDO, UserPasswordDO userPasswordDO){

        if (userDO == null){
            return null;
        }
        UserModel userModel = new UserModel();
        BeanUtils.copyProperties(userDO,userModel);
        if (userPasswordDO != null){
            userModel.setEncrptPassword(userPasswordDO.getEncrptPassword());
        }
        return userModel;
    }
}


Model对象

同理,Service层也需要对应的类处理由DO对象到Model对象的映射,这里用户的Model类为:

package tech.punklu.seckill.service.model;


import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;

public class UserModel {

    private Integer id;

    @NotBlank(message = "用户名不能为空")
    private String name;

    @NotNull(message = "性别不能不填写")
    private Integer gender;

    @NotNull(message = "年龄不能不填写")
    @Min(value = 0,message = "年龄必须大于0")
    @Max(value = 150,message = "年龄必须小于150岁")
    private Integer age;

    @NotBlank(message = "手机号不能为空")
    private String telphone;

    private String registerMode;

    private String thirdPartyId;

    @NotBlank(message = "密码不能为空")
    private String encrptPassword;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getGender() {
        return gender;
    }

    public void setGender(Integer gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }

    public String getRegisterMode() {
        return registerMode;
    }

    public void setRegisterMode(String registerMode) {
        this.registerMode = registerMode;
    }

    public String getThirdPartyId() {
        return thirdPartyId;
    }

    public void setThirdPartyId(String thirdPartyId) {
        this.thirdPartyId = thirdPartyId;
    }

    public String getEncrptPassword() {
        return encrptPassword;
    }

    public void setEncrptPassword(String encrptPassword) {
        this.encrptPassword = encrptPassword;
    }

    public String getTelphone() {
        return telphone;
    }

    public void setTelphone(String telphone) {
        this.telphone = telphone;
    }
}


通过在UserModel对象的字段上添加Hibernate Validator注解、并在Service层中通过调用ValidatorImpl校验类的validate方法的方式来校验字段是否符合条件。

编写商品相关代码

Controller层

商品Controller类:

package tech.punklu.seckill.controller;

import org.joda.time.format.DateTimeFormat;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import tech.punklu.seckill.controller.viewobject.ItemVo;
import tech.punklu.seckill.error.BusinessException;
import tech.punklu.seckill.response.CommonReturnType;
import tech.punklu.seckill.service.ItemService;
import tech.punklu.seckill.service.model.ItemModel;

import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 商品controller
 */
@Controller("/item")
@RequestMapping("/item")
// 设置可以跨域,但是单纯的@CrossOrigin无法做到跨域共享。
// allowCredentials = true
// @CrossOrigin中的DEFAULT_ALLOWED_HEADERS:允许跨域传输所有的headers参数,将用于使用token放入header域做session共享的跨域请求
// allowCredentials设置为true之后,需要配合前端设置xhrFields:{withCreditials:true}授信后使得跨域session共享
@CrossOrigin(allowCredentials = "true", allowedHeaders = "*")
public class ItemController extends BaseController {

    @Autowired
    private ItemService itemService;

    /**
     * 创建商品
     * @param title
     * @param description
     * @param price
     * @param stock
     * @param imgUrl
     * @return
     */
    @RequestMapping(value = "/create",method = RequestMethod.POST,consumes = {CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType createItem(@RequestParam(name = "title")String title,
                                       @RequestParam(name = "description")String description,
                                       @RequestParam(name = "price")BigDecimal price,
                                       @RequestParam(name = "stock")Integer stock,
                                       @RequestParam(name = "imgUrl")String imgUrl) throws BusinessException {
        // 封装service请求用来创建商品
        ItemModel itemModel = new ItemModel();
        itemModel.setTitle(title);
        itemModel.setDescription(description);
        itemModel.setPrice(price);
        itemModel.setStock(stock);
        itemModel.setImgUrl(imgUrl);

        ItemModel itemModelForReturn = itemService.createItem(itemModel);
        ItemVo itemVo = convertVOFromModel(itemModel);

        return CommonReturnType.create(itemVo);
    }

    /**
     * 商品详情查询
     * @param id
     * @return
     */
    @RequestMapping(value = "/get",method = RequestMethod.GET)
    @ResponseBody
    public CommonReturnType getItem(@RequestParam(name = "id")Integer id){
        ItemModel itemModel = itemService.getItemById(id);
        ItemVo itemVo = convertVOFromModel(itemModel);
        return CommonReturnType.create(itemVo);
    }

    /**
     * 查询商品列表
     * @return
     */
    @RequestMapping(value = "/list",method = RequestMethod.GET)
    @ResponseBody
    public CommonReturnType listItem(){
        List<ItemModel> itemModelList = itemService.listItem();
        List<ItemVo> itemVoList = itemModelList.stream().map(itemModel -> {
           ItemVo itemVo = this.convertVOFromModel(itemModel);
           return itemVo;
        }).collect(Collectors.toList());
        return CommonReturnType.create(itemVoList);
    }

    /**
     * 将ItemModel转换为ItemVO
     * @param itemModel
     * @return
     */
    private ItemVo convertVOFromModel(ItemModel itemModel){
        if (itemModel == null){
            return null;
        }
        ItemVo itemVo = new ItemVo();
        BeanUtils.copyProperties(itemModel,itemVo);
        if (itemModel.getPromoModel() != null){
            // 有正在进行或即将进行的秒杀活动
            itemVo.setPromoStatus(itemModel.getPromoModel().getStatus());
            itemVo.setPromoId(itemModel.getPromoModel().getId());
            itemVo.setStartDate(itemModel.getPromoModel().getStartDate().toString(DateTimeFormat.forPattern("yyyy-MM-dd HH:mm:ss")));
            itemVo.setPromoPrice(itemModel.getPromoModel().getPromoItemPrice());
        }else {
            // 没有秒杀活动将,秒杀状态设为0
            itemVo.setPromoStatus(0);
        }
        return itemVo;
    }
}


商品VO对象

package tech.punklu.seckill.controller.viewobject;

import org.joda.time.DateTime;

import javax.validation.constraints.NotBlank;
import java.math.BigDecimal;

/**
 * 商品VO
 */
public class ItemVo {

    private Integer id;

    // 商品名
    private String title;

    // 商品价格
    private BigDecimal price;

    // 商品库存
    private Integer stock;

    // 商品的描述
    private String description;

    // 商品的销量
    private Integer sales;

    // 商品描述图片的URL
    private String imgUrl;

    // 记录商品是否在秒杀活动中,以及对应的状态,0:没有秒杀活动,1表示秒杀活动待开始,为2表示秒杀活动进行中
    private Integer promoStatus;

    // 秒杀活动价格
    private BigDecimal promoPrice;

    // 秒杀活动id
    private Integer promoId;

    // 秒杀活动开始时间
    private String startDate;

    public Integer getPromoStatus() {
        return promoStatus;
    }

    public void setPromoStatus(Integer promoStatus) {
        this.promoStatus = promoStatus;
    }

    public BigDecimal getPromoPrice() {
        return promoPrice;
    }

    public void setPromoPrice(BigDecimal promoPrice) {
        this.promoPrice = promoPrice;
    }

    public Integer getPromoId() {
        return promoId;
    }

    public void setPromoId(Integer promoId) {
        this.promoId = promoId;
    }

    public String getStartDate() {
        return startDate;
    }

    public void setStartDate(String startDate) {
        this.startDate = startDate;
    }

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public Integer getStock() {
        return stock;
    }

    public void setStock(Integer stock) {
        this.stock = stock;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Integer getSales() {
        return sales;
    }

    public void setSales(Integer sales) {
        this.sales = sales;
    }

    public String getImgUrl() {
        return imgUrl;
    }

    public void setImgUrl(String imgUrl) {
        this.imgUrl = imgUrl;
    }
}


Service层

商品接口:

package tech.punklu.seckill.service;

import tech.punklu.seckill.error.BusinessException;
import tech.punklu.seckill.service.model.ItemModel;

import java.util.List;

public interface ItemService {

    // 创建商品
    ItemModel createItem(ItemModel itemModel) throws BusinessException;

    // 商品列表浏览
    List<ItemModel> listItem();

    // 商品详情浏览
    ItemModel getItemById(Integer id);

    // 库存扣减
    boolean decreaseStock(Integer itemId,Integer amount);

    // 商品销量增加
    void increaseSales(Integer itemId,Integer amount) throws BusinessException;
}


商品接口实现类:

package tech.punklu.seckill.service.impl;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import tech.punklu.seckill.dao.ItemDOMapper;
import tech.punklu.seckill.dao.ItemStockDOMapper;
import tech.punklu.seckill.dataobject.ItemDO;
import tech.punklu.seckill.dataobject.ItemStockDO;
import tech.punklu.seckill.error.BusinessException;
import tech.punklu.seckill.error.EmBusinessError;
import tech.punklu.seckill.service.ItemService;
import tech.punklu.seckill.service.PromoServive;
import tech.punklu.seckill.service.model.ItemModel;
import tech.punklu.seckill.service.model.PromoModel;
import tech.punklu.seckill.validator.ValidationResult;
import tech.punklu.seckill.validator.ValidatorImpl;

import java.math.BigDecimal;
import java.util.List;
import java.util.stream.Collectors;

@Service
public class ItemServiceImpl implements ItemService {

    @Autowired
    private ValidatorImpl validator;

    @Autowired
    private ItemDOMapper itemDOMapper;

    @Autowired
    private ItemStockDOMapper itemStockDOMapper;

    @Autowired
    private PromoServive promoServive;

    @Override
    @Transactional
    public ItemModel createItem(ItemModel itemModel) throws BusinessException {
        // 校验入参
        ValidationResult result = validator.validate(itemModel);
        if (result.isHasErrors()){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,result.getErrMsg());
        }
        // 转换itemmodel-> dataobject
        ItemDO itemDO = this.convertItemDOFromItemModel(itemModel);
        // ItemDO写入数据库
        itemDOMapper.insertSelective(itemDO);
        itemModel.setId(itemDO.getId());

        // 转换对应的商品库存ItemStockDO
        ItemStockDO itemStockDO = this.convertItemStockDOFromItemModel(itemModel);
        itemStockDOMapper.insertSelective(itemStockDO);

        // 返回创建完成的对象
        return this.getItemById(itemModel.getId());
    }

    @Override
    public List<ItemModel> listItem() {
        List<ItemDO> itemDOList = itemDOMapper.listItem();
        List<ItemModel> itemModelList = itemDOList.stream().map(itemDO -> {
            ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(itemDO.getId());
           ItemModel itemModel = this.convertModelFromDataObject(itemDO,itemStockDO);
           return itemModel;
        }).collect(Collectors.toList());
        return itemModelList;
    }

    @Override
    public ItemModel getItemById(Integer id) {
        ItemDO itemDO = itemDOMapper.selectByPrimaryKey(id);
        if (itemDO == null){
            return null;
        }
        // 操作获得库存数量
        ItemStockDO itemStockDO = itemStockDOMapper.selectByItemId(itemDO.getId());

        // 将dataobject -> model
        ItemModel itemModel = convertModelFromDataObject(itemDO,itemStockDO);

        // 获取活动商品信息
        PromoModel promoModel = promoServive.getPromoByItemId(itemModel.getId());
        // status为3的秒杀代表已结束,不应展示给用户
        if (promoModel != null && promoModel.getStatus().intValue() != 3){
            itemModel.setPromoModel(promoModel);
        }
        return itemModel;
    }

    /**
     * 将商品ItemModel转为商品ItemDO
     * @param itemModel
     * @return
     */
    private ItemDO convertItemDOFromItemModel(ItemModel itemModel){
        if (itemModel == null){
            return null;
        }
        ItemDO itemDO = new ItemDO();
        BeanUtils.copyProperties(itemModel,itemDO);
        // BeanUtils.copyProperties不转换类型不一样的参数,手动设置price,避免精度问题
        itemDO.setPrice(itemModel.getPrice().doubleValue());
        return itemDO;
    }

    /**
     * 将商品ItemModel转换为商品库存ItemStockDO
     * @param itemModel
     * @return
     */
    private ItemStockDO convertItemStockDOFromItemModel(ItemModel itemModel){
        if (itemModel == null){
            return null;
        }
        ItemStockDO itemStockDO = new ItemStockDO();
        itemStockDO.setItemId(itemModel.getId());
        itemStockDO.setStock(itemModel.getStock());
        return itemStockDO;
    }

    /**
     * 将ItemStockDO和ItemDO中的内容汇总到ItemModel中
     * @param itemDO
     * @param itemStockDO
     * @return
     */
    private ItemModel convertModelFromDataObject(ItemDO itemDO,ItemStockDO itemStockDO){
        ItemModel itemModel = new ItemModel();
        BeanUtils.copyProperties(itemDO,itemModel);
        itemModel.setPrice(new BigDecimal(itemDO.getPrice()));
        itemModel.setStock(itemStockDO.getStock());

        return itemModel;
    }

    @Override
    public boolean decreaseStock(Integer itemId, Integer amount) {
        int affectRow = itemStockDOMapper.decreaseStock(itemId,amount);
        if (affectRow > 0 ){
            // 受影响行数大于0,说明扣减库存成功
            return true;
        }else {
            // 受影响行数小于0,说明扣减库存失败
            return false;
        }
    }

    @Override
    @Transactional
    public void increaseSales(Integer itemId, Integer amount) throws BusinessException {
        itemDOMapper.increaseSales(itemId,amount);
    }
}


Model对象

package tech.punklu.seckill.service.model;

import javax.validation.constraints.Min;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.math.BigDecimal;

/**
 * 商品模型
 */
public class ItemModel {

    private Integer id;

    // 商品名
    @NotBlank(message = "商品名称不能为空")
    private String title;

    // 商品价格
    @NotNull(message = "商品价格不能为空")
    @Min(value = 0,message = "商品价格必须大于0")
    private BigDecimal price;

    // 商品库存
    @NotNull(message = "库存不能不填")
    private Integer stock;

    // 商品的描述
    @NotBlank(message = "商品描述信息不能为空")
    private String description;

    // 商品的销量
    private Integer sales;

    // 商品描述图片的URL
    @NotBlank(message = "商品图片信息不能为空")
    private String imgUrl;

    // 使用聚合模型,如果promoModel不为空,则表示其拥有还未结束的秒杀活动
    private PromoModel promoModel;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getTitle() {
        return title;
    }

    public void setTitle(String title) {
        this.title = title;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public Integer getStock() {
        return stock;
    }

    public void setStock(Integer stock) {
        this.stock = stock;
    }

    public String getDescription() {
        return description;
    }

    public void setDescription(String description) {
        this.description = description;
    }

    public Integer getSales() {
        return sales;
    }

    public void setSales(Integer sales) {
        this.sales = sales;
    }

    public String getImgUrl() {
        return imgUrl;
    }

    public void setImgUrl(String imgUrl) {
        this.imgUrl = imgUrl;
    }

    public PromoModel getPromoModel() {
        return promoModel;
    }

    public void setPromoModel(PromoModel promoModel) {
        this.promoModel = promoModel;
    }
}


编写订单相关代码

Controller层

package tech.punklu.seckill.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.*;
import tech.punklu.seckill.error.BusinessException;
import tech.punklu.seckill.error.EmBusinessError;
import tech.punklu.seckill.response.CommonReturnType;
import tech.punklu.seckill.service.OrderService;
import tech.punklu.seckill.service.model.OrderModel;
import tech.punklu.seckill.service.model.UserModel;

import javax.servlet.http.HttpServletRequest;

@Controller("/order")
@RequestMapping("/order")
// 设置可以跨域,但是单纯的@CrossOrigin无法做到跨域共享。
// allowCredentials = true
// @CrossOrigin中的DEFAULT_ALLOWED_HEADERS:允许跨域传输所有的headers参数,将用于使用token放入header域做session共享的跨域请求
// allowCredentials设置为true之后,需要配合前端设置xhrFields:{withCreditials:true}授信后使得跨域session共享
@CrossOrigin(allowCredentials = "true", allowedHeaders = "*")
public class OrderController extends BaseController{

    @Autowired
    private HttpServletRequest httpServletRequest;

    @Autowired
    private OrderService orderService;

    /**
     * 封装下单请求
     * @param itemId
     * @param amount
     * @return
     */
    @RequestMapping(value = "/createorder",method = RequestMethod.POST,consumes = CONTENT_TYPE_FORMED)
    @ResponseBody
    public CommonReturnType createOrder(@RequestParam(name = "itemId")Integer itemId,
                                        @RequestParam(name = "amount")Integer amount,
                                        @RequestParam(name = "promoId")Integer promoId) throws BusinessException {
       Boolean isLogin =  (Boolean) httpServletRequest.getSession().getAttribute("IS_LOGIN");
       if (isLogin == null || !isLogin.booleanValue()){
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户未登录");
       }
       // 获取用户的登录信息
        UserModel userModel = (UserModel)httpServletRequest.getSession().getAttribute("LOGIN_USER");
        OrderModel orderModel = orderService.createOrder(userModel.getId(),itemId,promoId,amount);
        return CommonReturnType.create(null);
    }


}


Service层

订单接口:

package tech.punklu.seckill.service;

import tech.punklu.seckill.error.BusinessException;
import tech.punklu.seckill.service.model.OrderModel;

public interface OrderService {

    /**
     * 创建订单
     * @param userId
     * @param itemId
     * @param amount
     * @return
     */
    OrderModel createOrder(Integer userId,Integer itemId,Integer promoId,Integer amount) throws BusinessException;
}


订单接口实现类:

package tech.punklu.seckill.service.impl;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import tech.punklu.seckill.dao.OrderDOMapper;
import tech.punklu.seckill.dao.SequenceDOMapper;
import tech.punklu.seckill.dataobject.OrderDO;
import tech.punklu.seckill.dataobject.SequenceDO;
import tech.punklu.seckill.error.BusinessException;
import tech.punklu.seckill.error.EmBusinessError;
import tech.punklu.seckill.service.ItemService;
import tech.punklu.seckill.service.OrderService;
import tech.punklu.seckill.service.UserService;
import tech.punklu.seckill.service.model.ItemModel;
import tech.punklu.seckill.service.model.OrderModel;
import tech.punklu.seckill.service.model.UserModel;

import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.Date;

@Service
public class OrderServiceImpl implements OrderService {

    @Autowired
    private ItemService itemService;

    @Autowired
    private UserService userService;

    @Autowired
    private OrderDOMapper orderDOMapper;

    @Autowired
    private SequenceDOMapper sequenceDOMapper;

    @Override
    @Transactional
    public OrderModel createOrder(Integer userId, Integer itemId,Integer promoId, Integer amount) throws BusinessException {
        // 校验下单状态,下单的商品是否存在,用户是否合法,购买数量是否正确
        ItemModel itemModel = itemService.getItemById(itemId);
        if (itemModel == null){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"商品信息不存在");
        }

        UserModel userModel = userService.getUserById(userId);
        if (userModel == null){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"用户信息不存在");
        }
        if (amount <= 0 || amount > 99){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"数量信息不正确");
        }

        // 校验秒杀活动信息
        if (promoId != null){
            // 校验对应活动是否存在这个适用商品
            if (promoId.intValue() != itemModel.getPromoModel().getId()){
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"秒杀活动信息不正确");
            }else if (itemModel.getPromoModel().getStatus().intValue() !=2){
                // 校验秒杀活动是否正在进行中
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"秒杀活动还未开始");
            }

        }
        // 落单减库存
        boolean result = itemService.decreaseStock(itemId,amount);
        if (!result){
            throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH);
        }
        // 订单入库
        OrderModel orderModel = new OrderModel();
        orderModel.setUserId(userId);
        orderModel.setItemId(itemId);
        orderModel.setAmount(amount);
        // 若promoId不为null,说明是秒杀活动相关下单,以秒杀活动价格计算,否则,以正常价格计算
        if (promoId != null){
            orderModel.setItemPrice(itemModel.getPromoModel().getPromoItemPrice());
        }else {
            orderModel.setItemPrice(itemModel.getPrice());
        }
        orderModel.setPromoId(promoId);
        orderModel.setOrderPrice(orderModel.getItemPrice().multiply(new BigDecimal(amount)));

        // 生成交易流水号,订单号
        orderModel.setId(generateOrderNo());
        OrderDO orderDO = convertFromOrderModel(orderModel);
        orderDOMapper.insertSelective(orderDO);
        // 增加商品销量
        itemService.increaseSales(itemId,amount);
        // 返回前端
        return orderModel;
    }

    /**
     * 将OrderModel转为OrderDO
     * @param orderModel
     * @return
     */
    private OrderDO convertFromOrderModel(OrderModel orderModel){
        if (orderModel == null){
            return null;
        }
        OrderDO orderDO = new OrderDO();
        BeanUtils.copyProperties(orderModel,orderDO);
        orderDO.setItemPrice(orderModel.getItemPrice().doubleValue());
        orderDO.setOrderPrice(orderModel.getOrderPrice().doubleValue());
        return orderDO;
    }

    /**
     * 生成订单号
     * @return
     */
    // propagation = Propagation.REQUIRES_NEW:无论此方法是否在一个事务中,都新开启一个事务,不管外部的事务是否成功,
    // 只要此方法的事务执行成功就commit
    @Transactional(propagation = Propagation.REQUIRES_NEW)
    String generateOrderNo(){
        // 订单号为16位
        StringBuilder stringBuilder = new StringBuilder();
        // 前8位为时间信息,年月日
        LocalDateTime now = LocalDateTime.now();
        String nowDate = now.format(DateTimeFormatter.ISO_DATE).replace("-","");
        stringBuilder.append(nowDate);
        // 中间6位为自增序列
        // 获取当前sequence
        int sequence = 0;
        SequenceDO sequenceDO = sequenceDOMapper.getSequenceByName("order_info");
        sequence = sequenceDO.getCurrentValue();
        sequenceDO.setCurrentValue(sequenceDO.getCurrentValue() + sequenceDO.getStep());
        sequenceDOMapper.updateByPrimaryKeySelective(sequenceDO);
        String sequenceStr = String.valueOf(sequence);
        for (int i = 0; i < 6-sequenceStr.length(); i++) {
            stringBuilder.append(0);
        }
        stringBuilder.append(sequenceStr);
        // 最后两位为分库分表位,暂时写死
        stringBuilder.append("00");
        return stringBuilder.toString();
    }
}


Model对象

package tech.punklu.seckill.service.model;

import java.math.BigDecimal;

/**
 * 用户下单的交易模型
 */
public class OrderModel {

    private String id;

    // 用户编号
    private Integer userId;

    // 商品编号
    private Integer itemId;

    // 购买商品的单价,若promoId非空,则表示秒杀商品价格
    private BigDecimal itemPrice;

    // 秒杀的编号,若非空,表示是以秒杀商品的方式下单
    private Integer promoId;

    // 购买的件数
    private Integer amount;

    // 购买的金额,若promoId非空,则表示秒杀商品价格
    private BigDecimal orderPrice;

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public Integer getItemId() {
        return itemId;
    }

    public void setItemId(Integer itemId) {
        this.itemId = itemId;
    }

    public BigDecimal getItemPrice() {
        return itemPrice;
    }

    public void setItemPrice(BigDecimal itemPrice) {
        this.itemPrice = itemPrice;
    }

    public Integer getAmount() {
        return amount;
    }

    public void setAmount(Integer amount) {
        this.amount = amount;
    }

    public BigDecimal getOrderPrice() {
        return orderPrice;
    }

    public void setOrderPrice(BigDecimal orderPrice) {
        this.orderPrice = orderPrice;
    }

    public Integer getPromoId() {
        return promoId;
    }

    public void setPromoId(Integer promoId) {
        this.promoId = promoId;
    }
}


编写秒杀活动相关代码

Service层

秒杀活动接口:

package tech.punklu.seckill.service;

import tech.punklu.seckill.service.model.PromoModel;

/**
 * 秒杀service
 */
public interface PromoServive {

    /**
     * 根据商品编号获取即将进行的或正在进行的秒杀活动
     * @param itemId
     * @return
     */
    PromoModel getPromoByItemId(Integer itemId);
}


秒杀活动实现类:

package tech.punklu.seckill.service.impl;

import org.joda.time.DateTime;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import tech.punklu.seckill.dao.PromoDOMapper;
import tech.punklu.seckill.dataobject.PromoDO;
import tech.punklu.seckill.service.PromoServive;
import tech.punklu.seckill.service.model.PromoModel;

import java.math.BigDecimal;

@Service
public class PromoServiceImpl implements PromoServive {

    @Autowired
    private PromoDOMapper promoDOMapper;

    @Override
    public PromoModel getPromoByItemId(Integer itemId) {
        // 获取对应商品的秒杀活动信息
        PromoDO promoDO = promoDOMapper.selectByItemId(itemId);
        // dataobject -> model
        PromoModel promoModel = convertFromDataObject(promoDO);
        if (promoModel == null){
            return null;
        }
        // 判断当前时间是否秒杀活动即将开始或正在进行
        DateTime now = new DateTime();
        if (promoModel.getStartDate().isAfterNow()){
            // 还未开始
            promoModel.setStatus(1);
        }else if (promoModel.getEndDate().isBeforeNow()){
            // 已经结束
            promoModel.setStatus(3);
        }else {
            promoModel.setStatus(2);
        }
        return promoModel;
    }


    private PromoModel convertFromDataObject(PromoDO promoDO){
        if (promoDO == null){
            return null;
        }
        PromoModel promoModel = new PromoModel();
        BeanUtils.copyProperties(promoDO,promoModel);
        promoModel.setPromoItemPrice(new BigDecimal(promoDO.getPromoItemPrice()));
        promoModel.setStartDate(new DateTime(promoDO.getStartDate()));
        promoModel.setEndDate(new DateTime(promoDO.getEndDate()));
        return promoModel;
    }
}


Model对象

package tech.punklu.seckill.service.model;

import org.joda.time.DateTime;

import java.math.BigDecimal;


public class PromoModel {


    private Integer id;

    // 秒杀活动状态,为1表示还是开始,2表示进行中,3表示已结束
    private Integer status;

    // 秒杀活动名称
    private String promoName;

    // 秒杀活动的开始时间
    private DateTime startDate;

    // 秒杀活动的结束时间
    private DateTime endDate;

    // 秒杀活动的试用商品
    private Integer itemId;

    // 秒杀活动的商品价格
    private BigDecimal promoItemPrice;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getPromoName() {
        return promoName;
    }

    public void setPromoName(String promoName) {
        this.promoName = promoName;
    }

    public DateTime getStartDate() {
        return startDate;
    }

    public void setStartDate(DateTime startDate) {
        this.startDate = startDate;
    }

    public DateTime getEndDate() {
        return endDate;
    }

    public void setEndDate(DateTime endDate) {
        this.endDate = endDate;
    }

    public Integer getItemId() {
        return itemId;
    }

    public void setItemId(Integer itemId) {
        this.itemId = itemId;
    }

    public BigDecimal getPromoItemPrice() {
        return promoItemPrice;
    }

    public void setPromoItemPrice(BigDecimal promoItemPrice) {
        this.promoItemPrice = promoItemPrice;
    }

    public Integer getStatus() {
        return status;
    }

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


前端相关代码

获取项目地址的JS脚本

var g_host = "localhost:8090";

创建商品

<html>
<head>
<meta charset="UTF-8">
<link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/global/css/components.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css"/>
<script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
<script src="./gethost.js" type="text/javascript"></script>


</head>

<body class="login">
	<div class="content">
		<h3 class="form-title">创建商品</h3>
		<div class="form-group">
			<label class="control-label">商品名</label>
			<div>
				<input  class="form-control" type="text" name="title" id="title"/>
			</div>	
		</div>
		<div class="form-group">
			<label class="control-label">商品描述</label>
			<div>
				<input  class="form-control" type="text" name="description" id="description"/>
			</div>	
		</div>	
		<div class="form-group">
			<label class="control-label">价格</label>
			<div>
				<input  class="form-control" type="text" name="price" id="price"/>
			</div>	
		</div>	
		<div class="form-group">
			<label class="control-label">图片</label>
			<div>
				<input  class="form-control" type="text" name="imgUrl" id="imgUrl"/>
			</div>	
		</div>
		<div class="form-group">
			<label class="control-label">库存</label>
			<div>
				<input  class="form-control" type="text"  name="stock" id="stock"/>
			</div>	
		</div>		
		<div class="form-actions">
			<button class="btn blue" id="create" type="submit">
				提交创建
			</button>	
		</div>	
	</div>	


</body>


<script>

	jQuery(document).ready(function(){

		//绑定otp的click时间用于向后端发送获取手机验证码的请求
		$("#create").on("click",function(){
			var title = $("#title").val();
			var description = $("#description").val();
			var imgUrl = $("#imgUrl").val();
			var price = $("#price").val();
			var stock = $("#stock").val();


			if(title == null || title == ""){
				alert("商品名不能为空");
				return false;
			}
			if(description == null || description == ""){
				alert("描述不能为空");
				return false;
			}
			if(imgUrl == null || imgUrl == ""){
				alert("图片url不能为空");
				return false;
			}
			if(price == null || price == ""){
				alert("价格不能为空");
				return false;
			}
			if(stock == null || stock == ""){
				alert("库存不能为空");
				return false;
			}

			$.ajax({
				type:"POST",
				contentType:"application/x-www-form-urlencoded",
				url:"http://"+g_host+"/item/create",
				data:{
					"title":title,
					"description":description,
					"imgUrl":imgUrl,
					"price":price,
					"stock":stock,
					"name":name
				},
				xhrFields:{withCredentials:true},
				success:function(data){
					if(data.status == "success"){
						alert("创建成功");
					}else{
						alert("创建失败,原因为"+data.data.errMsg);
					}
				},
				error:function(data){
					alert("创建失败,原因为"+data.responseText);
				}
			});
			return false;
		});


	});


</script>




</html>

获取商品

<html>
<head>
<meta charset="UTF-8">
<link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/global/css/components.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css"/>
<script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
<script src="./gethost.js" type="text/javascript"></script>

</head>

<body class="login">
	<div class="content">
		<h3 class="form-title">商品详情</h3>
		<div id="promoStartDateContainer" class="form-group">
			<label style="color:blue" id="promoStatus" class="control-label"></label>
			<div>
				<label style="color:red" class="control-label" id="promoStartDate"/>
			</div>	
		</div>	
		<div class="form-group">
			<div>
				<label class="control-label" id="title"/>
			</div>	
		</div>
		<div class="form-group">
			<label class="control-label">商品描述</label>
			<div>
				<label class="control-label" id="description"/>
			</div>	
		</div>	
		<div id="normalPriceContainer" class="form-group">
			<label class="control-label">价格</label>
			<div>
				<label class="control-label" id="price"/>
			</div>	
		</div>	
		<div id="promoPriceContainer" class="form-group">
			<label style="color:red" class="control-label">秒杀价格</label>
			<div>
				<label style="color:red" class="control-label" id="promoPrice"/>
			</div>	
		</div>	
		<div class="form-group">
			<div>
				<img style="width:200px;height:auto" id="imgUrl"/>
			</div>	
		</div>
		<div class="form-group">
			<label class="control-label">库存</label>
			<div>
				<label class="control-label" id="stock"/>
			</div>	
		</div>	
		<div class="form-group">
			<label class="control-label">销量</label>
			<div>
				<label class="control-label" id="sales"/>
			</div>	
		</div>		
		<div class="form-actions">
			<button class="btn blue" id="createorder" type="submit">
				下单
			</button>	
		</div>

		<div id="verifyDiv" style="display:none;" class="form-actions">
			<img src=""/>
			<input type="text" id="verifyContent" value=""/>
			<button class="btn blue" id="verifyButton" type="submit">
				验证
			</button>	
		</div>

	</div>	

	<input type="hidden" id="isInit" value="0"/>

</body>


<script>

function getParam(paramName) { 
    paramValue = "", isFound = !1; 
    if (this.location.search.indexOf("?") == 0 && this.location.search.indexOf("=") > 1) { 
        arrSource = unescape(this.location.search).substring(1, this.location.search.length).split("&"), i = 0; 
        while (i < arrSource.length && !isFound) arrSource[i].indexOf("=") > 0 && arrSource[i].split("=")[0].toLowerCase() == paramName.toLowerCase() && (paramValue = arrSource[i].split("=")[1], isFound = !0), i++ 
    } 
    return paramValue == "" && (paramValue = null), paramValue 
} 

var g_itemVO = {};

function hasInit(){
	var isInit = $("#isInit").val();
	return isInit;
}

function setHasInit(){
	$("#isInit").val("1");
}

function initView(){
	var isInit = hasInit();
	if(isInit == "1"){
		return;
	}
			//获取商品详情
			$.ajax({
				type:"GET",
				url:"http://"+g_host+"/item/get",
				data:{
					"id":getParam("id"),
				},
				xhrFields:{withCredentials:true},
				success:function(data){
					if(data.status == "success"){
						g_itemVO = data.data;
						reloadDom();
						setInterval(reloadDom,1000);
						setHasInit();
					}else{
						alert("获取信息失败,原因为"+data.data.errMsg);
					}
				},
				error:function(data){
					alert("获取信息失败,原因为"+data.responseText);
				}
			});
}

	jQuery(document).ready(function(){
		$("#verifyButton").on("click",function(){
			var token = window.localStorage["token"];
			$.ajax({
				type:"POST",
				contentType:"application/x-www-form-urlencoded",
				url:"http://"+g_host+"/order/generatetoken?token="+token,
				data:{
					"itemId":g_itemVO.id,
					"promoId":g_itemVO.promoId,
					"verifyCode":$("#verifyContent").val()
				},
				xhrFields:{withCredentials:true},
				success:function(data){
					if(data.status == "success"){
						var promoToken = data.data;
						$.ajax({
							type:"POST",
							contentType:"application/x-www-form-urlencoded",
							url:"http://"+g_host+"/order/createorder?token="+token,
							data:{
								"itemId":g_itemVO.id,
								"amount":1,
								"promoId":g_itemVO.promoId,
								"promoToken":promoToken
							},
							xhrFields:{withCredentials:true},
							success:function(data){
								if(data.status == "success"){
									alert("下单成功");
									window.location.reload();
								}else{
									alert("下单失败,原因为"+data.data.errMsg);
									if(data.data.errCode == 20003){
										window.location.href="login.html";
									}
								}
							},
							error:function(data){
								alert("下单失败,原因为"+data.responseText);
							}
						});


					}else{
						alert("获取令牌失败,原因为"+data.data.errMsg);
								if(data.data.errCode == 20003){
										window.location.href="login.html";
									}
					}
				},
				error:function(data){
					alert("获取令牌失败,原因为"+data.responseText);
				}
			});

		});
		$("#createorder").on("click",function(){
			var token = window.localStorage["token"];
			if(token == null){
				alert("没有登录,不能下单");
				window.location.href="login.html";
				return false;
			}
			$("#verifyDiv img").attr("src","http://"+g_host+"/order/generateverifycode?token="+token);
			$("#verifyDiv").show();

			


		});

		initView();


	});

	function reloadDom(){
		$("#title").text(g_itemVO.title);
		$("#description").text(g_itemVO.description);
		$("#stock").text(g_itemVO.stock);
		$("#price").text(g_itemVO.price);
		$("#imgUrl").attr("src",g_itemVO.imgUrl);
		$("#sales").text(g_itemVO.sales);
		if(g_itemVO.promoStatus == 1){
			//秒杀活动还未开始
			var startTime = g_itemVO.startDate.replace(new RegExp("-","gm"),"/");
			startTime = (new Date(startTime)).getTime();
			var nowTime = Date.parse(new Date());
			var delta = (startTime - nowTime)/1000;

			if(delta <= 0){
				//活动开始了
				g_itemVO.promoStatus = 2;
				reloadDom();
			}
			$("#promoStartDate").text("秒杀活动将于: "+g_itemVO.startDate+" 开始售卖 倒计时:"+delta+" 秒");
			$("#promoPrice").text(g_itemVO.promoPrice);

			$("#createorder").attr("disabled",true);
		}else if(g_itemVO.promoStatus == 2){
			//秒杀活动正在进行中
			$("#promoStartDate").text("秒杀正在进行中");
			$("#promoPrice").text(g_itemVO.promoPrice);

			$("#createorder").attr("disabled",false);
			$("#normalPriceContainer").hide();
		}
	}


</script>




</html>

获取OTP信息

<html>
<head>
<meta charset="UTF-8">
<link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/global/css/components.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css"/>
<script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
<script src="./gethost.js" type="text/javascript"></script>

</head>

<body class="login">
	<div class="content">
		<h3 class="form-title">获取otp信息</h3>
		<div class="form-group">
			<label class="control-label">手机号</label>
			<div>
				<input  class="form-control" type="text" placeholder="手机号" name="telphone" id="telphone"/>
			</div>	
		</div>	
		<div class="form-actions">
			<button class="btn blue" id="getotp" type="submit">
				获取otp短信
			</button>	
		</div>	
	</div>	


</body>


<script>

	jQuery(document).ready(function(){

		//绑定otp的click时间用于向后端发送获取手机验证码的请求
		$("#getotp").on("click",function(){
			var telphone = $("#telphone").val();
			if(telphone == null || telphone == ""){
				alert("手机号不能为空");
				return false;
			}
			$.ajax({
				type:"POST",
				contentType:"application/x-www-form-urlencoded",
				url:"http://"+g_host+"/user/getotp",
				data:{
					"telphone":$("#telphone").val(),
				},
				xhrFields:{withCredentials:true},
				success:function(data){
					if(data.status == "success"){
						alert("otp已经发送到了您的手机上,请注意查收");
						window.location.href="./register.html";
					}else{
						alert("otp发送失败,原因为"+data.data.errMsg);
					}
				},
				error:function(data){
					alert("otp发送失败,原因为"+data.responseText);
				}
			});
			return false;
		});


	});


</script>




</html>

商品列表页

<html>
<head>
<meta charset="UTF-8">
<link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/global/css/components.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css"/>
<script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
<script src="./gethost.js" type="text/javascript"></script>

</head>

<body class="login">

	<div class="content">
	<h3 class="form-title">商品列表页</h3>
	</div>	


</body>


<script>
	//定义全局商品数组信息
	var g_itemList = [];
	jQuery(document).ready(function(){
			$.ajax({
				type:"GET",
				url:"http://"+g_host+"/item/list",
				xhrFields:{withCredentials:true},
				success:function(data){
					if(data.status == "success"){
						g_itemList = data.data;
						reloadDom();
					}else{
						alert("获取商品信息失败,原因为"+data.data.errMsg);
					}
				},
				error:function(data){
					alert("获取商品信息失败失败,原因为"+data.responseText);
				}
			});
	
		});


function reloadDom(){
	for(var i = 0; i < g_itemList.length; i++){
		var itemVO = g_itemList[i];
			var dom = "<div data-id='"+itemVO.id+"' id='itemDetail"+itemVO.id+"'><div class='form-group'><div><img style='width:200px;height:auto' src='"+itemVO.imgUrl+"'/></div></div><div class='form-group'><div><label class='control-label'>"+itemVO.title+"</label></div></div><div class='form-group'><label class='control-label'>价格</label><div><label class='control-label'>"+itemVO.price+"</label></div></div><div class='form-group'><label class='control-label'>销量</label><div><label class='control-label'>"+itemVO.sales+"</label></div></div></div>";




		$(".content").append($(dom));

		$("#itemDetail"+itemVO.id).on("click",function(e){
			window.location.href="getitem.html?id="+$(this).data("id");
		});
	}

}


</script>




</html>

注册页面

<html>
<head>
<meta charset="UTF-8">
<link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/global/css/components.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css"/>
<script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
<script src="./gethost.js" type="text/javascript"></script>

</head>

<body class="login">
	<div class="content">
		<h3 class="form-title">用户注册</h3>
		<div class="form-group">
			<label class="control-label">手机号</label>
			<div>
				<input  class="form-control" type="text" placeholder="手机号" name="telphone" id="telphone"/>
			</div>	
		</div>
		<div class="form-group">
			<label class="control-label">验证码</label>
			<div>
				<input  class="form-control" type="text" placeholder="验证码" name="otpCode" id="otpCode"/>
			</div>	
		</div>	
		<div class="form-group">
			<label class="control-label">用户昵称</label>
			<div>
				<input  class="form-control" type="text" placeholder="用户昵称" name="name" id="name"/>
			</div>	
		</div>	
		<div class="form-group">
			<label class="control-label">性别</label>
			<div>
				<input  class="form-control" type="text" placeholder="性别" name="gender" id="gender"/>
			</div>	
		</div>
		<div class="form-group">
			<label class="control-label">年龄</label>
			<div>
				<input  class="form-control" type="text" placeholder="年龄" name="age" id="age"/>
			</div>	
		</div>
		<div class="form-group">
		<label class="control-label">密码</label>
			<div>
				<input  class="form-control" type="password" placeholder="密码" name="password" id="password"/>
			</div>	
		</div>			
		<div class="form-actions">
			<button class="btn blue" id="register" type="submit">
				提交注册
			</button>	
		</div>	
	</div>	


</body>


<script>

	jQuery(document).ready(function(){

		//绑定otp的click时间用于向后端发送获取手机验证码的请求
		$("#register").on("click",function(){
			var telphone = $("#telphone").val();
			var password = $("#password").val();
			var age = $("#age").val();
			var gender = $("#gender").val();
			var name = $("#name").val();
			var otpCode = $("#otpCode").val();
			if(telphone == null || telphone == ""){
				alert("手机号不能为空");
				return false;
			}
			if(password == null || password == ""){
				alert("密码不能为空");
				return false;
			}
			if(age == null || age == ""){
				alert("年龄不能为空");
				return false;
			}
			if(gender == null || gender == ""){
				alert("性别不能为空");
				return false;
			}
			if(otpCode == null || otpCode == ""){
				alert("密码不能为空");
				return false;
			}

			$.ajax({
				type:"POST",
				contentType:"application/x-www-form-urlencoded",
				url:"http://"+g_host+"/user/register",
				data:{
					"telphone":$("#telphone").val(),
					"password":password,
					"age":age,
					"gender":gender,
					"otpCode":otpCode,
					"name":name
				},
				xhrFields:{withCredentials:true},
				success:function(data){
					if(data.status == "success"){
						alert("注册成功");
						window.location.href="./login.html";
					}else{
						alert("注册失败,原因为"+data.data.errMsg);
					}
				},
				error:function(data){
					alert("注册失败,原因为"+data.responseText);
				}
			});
			return false;
		});


	});


</script>




</html>

登录页面

<html>
<head>
<meta charset="UTF-8">
<link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/global/css/components.css" rel="stylesheet" type="text/css"/>
<link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css"/>
<script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
<script src="./gethost.js" type="text/javascript"></script>

</head>

<body class="login">
	<div class="content">
		<h3 class="form-title">用户登陆</h3>
		<div class="form-group">
			<label class="control-label">手机号</label>
			<div>
				<input  class="form-control" type="text" placeholder="手机号" name="telphone" id="telphone"/>
			</div>	
		</div>
		<div class="form-group">
		<label class="control-label">密码</label>
			<div>
				<input  class="form-control" type="password" placeholder="密码" name="password" id="password"/>
			</div>	
		</div>			
		<div class="form-actions">
			<button class="btn blue" id="login" type="submit">
				登陆
			</button>	
			<button class="btn green" id="register" type="submit">
				注册
			</button>	
		</div>	
	</div>	


</body>


<script>

	jQuery(document).ready(function(){

		$("#register").on("click",function(){
			window.location.href="getotp.html";
		});


		$("#login").on("click",function(){
			var telphone = $("#telphone").val();
			var password = $("#password").val();
			if(telphone == null || telphone == ""){
				alert("手机号不能为空");
				return false;
			}
			if(password == null || password == ""){
				alert("密码不能为空");
				return false;
			}
	

			$.ajax({
				type:"POST",
				contentType:"application/x-www-form-urlencoded",
				url:"http://"+g_host+"/user/login",
				data:{
					"telphone":$("#telphone").val(),
					"password":password
				},
				xhrFields:{withCredentials:true},
				success:function(data){
					if(data.status == "success"){
						alert("登陆成功");
						var token = data.data;
						window.localStorage["token"] = token;
						window.location.href="listitem.html";
					}else{
						alert("登陆失败,原因为"+data.data.errMsg);
					}
				},
				error:function(data){
					alert("登陆失败,原因为"+data.responseText);
				}
			});
			return false;
		});


	});


</script>




</html>

获取秒杀信息页面

<html><head>
<meta charset="UTF-8">
<link href="static/assets/global/plugins/bootstrap/css/bootstrap.min.css" rel="stylesheet" type="text/css">
<link href="static/assets/global/css/components.css" rel="stylesheet" type="text/css">
<link href="static/assets/admin/pages/css/login.css" rel="stylesheet" type="text/css">
<script src="static/assets/global/plugins/jquery-1.11.0.min.js" type="text/javascript"></script>
<script src="./gethost.js" type="text/javascript"></script>

</head>

<body class="login">
	<div class="content">
		<h3 class="form-title">商品详情</h3>
		<div id="promoStartDateContainer" class="form-group">
			<label style="color:blue" id="promoStatus" class="control-label"></label>
			<div>
				<label style="color:red" class="control-label" id="promoStartDate">秒杀正在进行中</label></div>	
		</div>	
		<div class="form-group">
			<div>
				<label class="control-label" id="title">iphone99</label></div>	
		</div>
		<div class="form-group">
			<label class="control-label">商品描述</label>
			<div>
				<label class="control-label" id="description">最好用的苹果手机</label></div>	
		</div>	
		<div id="normalPriceContainer" class="form-group" style="display: none;">
			<label class="control-label">价格</label>
			<div>
				<label class="control-label" id="price">800</label></div>	
		</div>	
		<div id="promoPriceContainer" class="form-group">
			<label style="color:red" class="control-label">秒杀价格</label>
			<div>
				<label style="color:red" class="control-label" id="promoPrice">100</label></div>	
		</div>	
		<div class="form-group">
			<div>
				<img style="width:200px;height:auto" id="imgUrl" src="https://ss1.bdstatic.com/70cFuXSh_Q1YnxGkpoWK1HF6hhy/it/u=3974550569,4161544558&amp;fm=27&amp;gp=0.jpg">
			</div>	
		</div>
		<div class="form-group">
			<label class="control-label">库存</label>
			<div>
				<label class="control-label" id="stock">82</label></div>	
		</div>	
		<div class="form-group">
			<label class="control-label">销量</label>
			<div>
				<label class="control-label" id="sales">115</label></div>	
		</div>		
		<div class="form-actions">
			<button class="btn blue" id="createorder" type="submit">
				下单
			</button>	
		</div>
	</div>	

	<input type="hidden" id="isInit" value="1">




<script>

function getParam(paramName) { 
    paramValue = "", isFound = !1; 
    if (this.location.search.indexOf("?") == 0 && this.location.search.indexOf("=") > 1) { 
        arrSource = unescape(this.location.search).substring(1, this.location.search.length).split("&"), i = 0; 
        while (i < arrSource.length && !isFound) arrSource[i].indexOf("=") > 0 && arrSource[i].split("=")[0].toLowerCase() == paramName.toLowerCase() && (paramValue = arrSource[i].split("=")[1], isFound = !0), i++ 
    } 
    return paramValue == "" && (paramValue = null), paramValue 
} 

var g_itemVO = {};

function hasInit(){
	var isInit = $("#isInit").val();
	return isInit;
}

function setHasInit(){
	$("#isInit").val("1");
}

function initView(){
	var isInit = hasInit();
	if(isInit == "1"){
		return;
	}
			//获取商品详情
			$.ajax({
				type:"GET",
				url:"http://"+g_host+"/item/get",
				data:{
					"id":getParam("id"),
				},
				xhrFields:{withCredentials:true},
				success:function(data){
					if(data.status == "success"){
						g_itemVO = data.data;
						reloadDom();
						setInterval(reloadDom,1000);
						setHasInit();
					}else{
						alert("获取信息失败,原因为"+data.data.errMsg);
					}
				},
				error:function(data){
					alert("获取信息失败,原因为"+data.responseText);
				}
			});
}

	jQuery(document).ready(function(){
		$("#createorder").on("click",function(){
			var token = window.localStorage["token"];
			if(token == null){
				alert("没有登录,不能下单");
				window.location.href="login.html";
				return false;
			}

			$.ajax({
				type:"POST",
				contentType:"application/x-www-form-urlencoded",
				url:"http://"+g_host+"/order/createorder?token="+token,
				data:{
					"itemId":g_itemVO.id,
					"amount":1,
					"promoId":g_itemVO.promoId
				},
				xhrFields:{withCredentials:true},
				success:function(data){
					if(data.status == "success"){
						alert("下单成功");
						window.location.reload();
					}else{
						alert("下单失败,原因为"+data.data.errMsg);
						if(data.data.errCode == 20003){
							window.location.href="login.html";
						}
					}
				},
				error:function(data){
					alert("下单失败,原因为"+data.responseText);
				}
			});

		});

		initView();


	});

	function reloadDom(){
		$("#title").text(g_itemVO.title);
		$("#description").text(g_itemVO.description);
		$("#stock").text(g_itemVO.stock);
		$("#price").text(g_itemVO.price);
		$("#imgUrl").attr("src",g_itemVO.imgUrl);
		$("#sales").text(g_itemVO.sales);
		if(g_itemVO.promoStatus == 1){
			//秒杀活动还未开始
			var startTime = g_itemVO.startDate.replace(new RegExp("-","gm"),"/");
			startTime = (new Date(startTime)).getTime();
			var nowTime = Date.parse(new Date());
			var delta = (startTime - nowTime)/1000;

			if(delta <= 0){
				//活动开始了
				g_itemVO.promoStatus = 2;
				reloadDom();
			}
			$("#promoStartDate").text("秒杀活动将于: "+g_itemVO.startDate+" 开始售卖 倒计时:"+delta+" 秒");
			$("#promoPrice").text(g_itemVO.promoPrice);

			$("#createorder").attr("disabled",true);
		}else if(g_itemVO.promoStatus == 2){
			//秒杀活动正在进行中
			$("#promoStartDate").text("秒杀正在进行中");
			$("#promoPrice").text(g_itemVO.promoPrice);

			$("#createorder").attr("disabled",false);
			$("#normalPriceContainer").hide();
		}
	}


</script>




</body></html>