Full Code of Grootzz/dis-seckill for AI

master 5864da23307f cached
115 files
517.8 KB
155.5k tokens
468 symbols
1 requests
Download .txt
Showing preview only (600K chars total). Download the full file or copy to clipboard to get everything.
Repository: Grootzz/dis-seckill
Branch: master
Commit: 5864da23307f
Files: 115
Total size: 517.8 KB

Directory structure:
gitextract_4juf6qor/

├── .gitignore
├── README.md
├── dis-seckill-cache/
│   ├── pom.xml
│   └── src/
│       └── main/
│           ├── java/
│           │   └── com/
│           │       └── seckill/
│           │           └── dis/
│           │               └── cache/
│           │                   ├── CacheApplication.java
│           │                   ├── config/
│           │                   │   ├── RedisConfig.java
│           │                   │   └── RedisPoolFactory.java
│           │                   └── service/
│           │                       ├── RedisLockImpl.java
│           │                       └── RedisServiceImpl.java
│           └── resources/
│               └── application.properties
├── dis-seckill-common/
│   ├── pom.xml
│   ├── schema/
│   │   └── seckill.sql
│   └── src/
│       └── main/
│           └── java/
│               └── com/
│                   └── seckill/
│                       └── dis/
│                           └── common/
│                               ├── api/
│                               │   ├── cache/
│                               │   │   ├── DLockApi.java
│                               │   │   ├── RedisServiceApi.java
│                               │   │   └── vo/
│                               │   │       ├── AccessKeyPrefix.java
│                               │   │       ├── BaseKeyPrefix.java
│                               │   │       ├── GoodsKeyPrefix.java
│                               │   │       ├── KeyPrefix.java
│                               │   │       ├── OrderKeyPrefix.java
│                               │   │       ├── SkKeyPrefix.java
│                               │   │       ├── SkUserKeyPrefix.java
│                               │   │       └── UserKey.java
│                               │   ├── goods/
│                               │   │   ├── GoodsServiceApi.java
│                               │   │   └── vo/
│                               │   │       ├── GoodsDetailVo.java
│                               │   │       └── GoodsVo.java
│                               │   ├── mq/
│                               │   │   ├── MqProviderApi.java
│                               │   │   └── vo/
│                               │   │       └── SkMessage.java
│                               │   ├── order/
│                               │   │   ├── OrderServiceApi.java
│                               │   │   └── vo/
│                               │   │       └── OrderDetailVo.java
│                               │   ├── seckill/
│                               │   │   ├── SeckillServiceApi.java
│                               │   │   └── vo/
│                               │   │       └── VerifyCodeVo.java
│                               │   └── user/
│                               │       ├── UserServiceApi.java
│                               │       └── vo/
│                               │           ├── LoginVo.java
│                               │           ├── RegisterVo.java
│                               │           ├── UserInfoVo.java
│                               │           └── UserVo.java
│                               ├── domain/
│                               │   ├── Goods.java
│                               │   ├── OrderInfo.java
│                               │   ├── SeckillGoods.java
│                               │   ├── SeckillOrder.java
│                               │   └── SeckillUser.java
│                               ├── exception/
│                               │   ├── GlobalException.java
│                               │   └── GlobalExceptionHandler.java
│                               ├── result/
│                               │   ├── CodeMsg.java
│                               │   └── Result.java
│                               ├── util/
│                               │   ├── DBUtil.java
│                               │   ├── JsonUtil.java
│                               │   ├── MD5Util.java
│                               │   ├── UUIDUtil.java
│                               │   ├── ValidatorUtil.java
│                               │   └── VerifyCodeUtil.java
│                               └── validator/
│                                   ├── IsMobile.java
│                                   └── IsMobileValidator.java
├── dis-seckill-gateway/
│   ├── pom.xml
│   └── src/
│       └── main/
│           ├── java/
│           │   └── com/
│           │       └── seckill/
│           │           └── dis/
│           │               └── gateway/
│           │                   ├── DisSeckillServletInitializer.java
│           │                   ├── GatewayApplication.java
│           │                   ├── config/
│           │                   │   ├── WebConfig.java
│           │                   │   ├── access/
│           │                   │   │   ├── AccessInterceptor.java
│           │                   │   │   ├── AccessLimit.java
│           │                   │   │   └── UserContext.java
│           │                   │   └── resolver/
│           │                   │       └── UserArgumentResolver.java
│           │                   ├── exception/
│           │                   │   ├── GlobalException.java
│           │                   │   └── GlobalExceptionHandler.java
│           │                   ├── goods/
│           │                   │   └── GoodsController.java
│           │                   ├── order/
│           │                   │   └── OrderController.java
│           │                   ├── seckill/
│           │                   │   └── SeckillController.java
│           │                   └── user/
│           │                       └── UserController.java
│           └── resources/
│               ├── application.properties
│               ├── static/
│               │   ├── bootstrap/
│               │   │   ├── css/
│               │   │   │   ├── bootstrap-theme.css
│               │   │   │   └── bootstrap.css
│               │   │   └── js/
│               │   │       ├── bootstrap.js
│               │   │       └── npm.js
│               │   ├── goods_detail.htm
│               │   ├── js/
│               │   │   └── common.js
│               │   ├── layer/
│               │   │   ├── layer.js
│               │   │   ├── mobile/
│               │   │   │   ├── layer.js
│               │   │   │   └── need/
│               │   │   │       └── layer.css
│               │   │   └── skin/
│               │   │       └── default/
│               │   │           └── layer.css
│               │   └── order_detail.htm
│               └── templates/
│                   ├── goods_detail.html
│                   ├── goods_list.html
│                   ├── login.html
│                   ├── order_detail.html
│                   └── register.html
├── dis-seckill-goods/
│   ├── pom.xml
│   └── src/
│       └── main/
│           ├── java/
│           │   └── com/
│           │       └── seckill/
│           │           └── dis/
│           │               └── goods/
│           │                   ├── GoodsApplication.java
│           │                   ├── persistence/
│           │                   │   └── GoodsMapper.java
│           │                   └── service/
│           │                       ├── GoodsServiceImpl.java
│           │                       └── SeckillServiceImpl.java
│           └── resources/
│               └── application.properties
├── dis-seckill-mq/
│   ├── pom.xml
│   └── src/
│       └── main/
│           ├── java/
│           │   └── com/
│           │       └── seckill/
│           │           └── dis/
│           │               └── mq/
│           │                   ├── MqApplication.java
│           │                   ├── config/
│           │                   │   └── MQConfig.java
│           │                   ├── receiver/
│           │                   │   └── MqConsumer.java
│           │                   └── service/
│           │                       └── MqProviderImpl.java
│           └── resources/
│               └── application.properties
├── dis-seckill-order/
│   ├── pom.xml
│   └── src/
│       └── main/
│           ├── java/
│           │   └── com/
│           │       └── seckill/
│           │           └── dis/
│           │               └── order/
│           │                   ├── OrderApplication.java
│           │                   ├── persistence/
│           │                   │   └── OrderMapper.java
│           │                   └── service/
│           │                       └── OrderServiceImpl.java
│           └── resources/
│               └── application.properties
├── dis-seckill-user/
│   ├── pom.xml
│   └── src/
│       └── main/
│           ├── java/
│           │   └── com/
│           │       └── seckill/
│           │           └── dis/
│           │               └── user/
│           │                   ├── UserApplication.java
│           │                   ├── domain/
│           │                   │   └── SeckillUser.java
│           │                   ├── persistence/
│           │                   │   ├── SeckillUserMapper.java
│           │                   │   └── SeckillUserMapper.xml
│           │                   ├── service/
│           │                   │   └── UserServiceImpl.java
│           │                   └── util/
│           │                       └── UserUtil.java
│           └── resources/
│               └── application.properties
├── doc/
│   ├── HandlerInterceptor的使用.md
│   ├── README.md
│   ├── Redis中存储的数据.md
│   ├── 使用分布式锁解决恶意用户重复注册问题.md
│   ├── 前后端交互接口定义.md
│   └── 前后端交互接口逻辑实现.md
└── pom.xml

================================================
FILE CONTENTS
================================================

================================================
FILE: .gitignore
================================================
# Created by .ignore support plugin (hsz.mobi)
### Example user template template
### Example user template

# IntelliJ project files
.idea
*.iml
out
gen### Java template
# Compiled class file
*.class

# Log file

*.log

# BlueJ files
*.ctxt

# Mobile Tools for Java (J2ME)
.mtj.tmp/

# Package Files #
*.jar
*.war
*.nar
*.ear
*.zip
*.tar.gz
*.rar

# virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml
hs_err_pid*


.idea/
dis-seckill-common/dis-seckill-common.iml
dis-seckill-common/src/test/
dis-seckill-common/target/
dis-seckill-gateway/dis-seckill-gateway.iml
dis-seckill-gateway/src/test/
dis-seckill-gateway/target/
dis-seckill-goods/dis-seckill-goods.iml
dis-seckill-goods/src/test/
dis-seckill-goods/target/
dis-seckill-order/dis-seckill-order.iml
dis-seckill-order/src/test/
dis-seckill-order/target/
dis-seckill-user/dis-seckill-user.iml
dis-seckill-user/src/test/
dis-seckill-user/target/
dis-seckill.iml


================================================
FILE: README.md
================================================
# 分布式高并发商品秒杀系统设计

- [介绍](#介绍)
- [快速启动](#快速启动)
- [系统架构](#系统架构)
- [模块介绍](#模块介绍)
- [Q&A](#Q&A)
- [TODO LIST](#TODO)
- [参考](#参考)

## 介绍

本项目为另一个项目[seckill](https://github.com/Grootzz/seckill)的分布式改进版本,dis-seckill意为:distributed seckill,即分布式秒杀系统。

商品秒杀与其他业务最大的区别在于:

- 低廉价格;
- 大幅推广;
- 瞬时售空;
- 一般是定时上架;
- 时间短、瞬时并发量高、网络的流量也会瞬间变大。

除了具有以上特点,秒杀商品还需要完成正常的电子商务逻辑,即:(1)查询商品;(2)创建订单;(3)扣减库存;(4)更新订单;(5)付款;(6)卖家发货。

本项目正是基于上述业务特点进行设计的,在项目中引入诸多优化手段,使系统可以从容应对秒杀场景下的业务处理。

另外,项目[seckill](https://github.com/Grootzz/seckill)为单体应用,在大并发情形下处理能力有限,所以本项目对其进行分布式改造,对职责进行划分,降低单体应用的业务耦合性。

## 快速启动

- 构建工具

  apache-maven-3.6.1

- 开发环境

  JDK 1.8、Mysql 8.0.12、SpringBoot 2.1.5、zookeeper 3.4.10、dubbo 2.7.1、redis 5.0.5、rabbitmq 3.7.15

在安装之前,需要安装好上述构建工具和开发环境,推荐在linux下安装上述开发环境。

**第一步**;完成数据库的初始化,使用`./dis-seckill-common/schema/seckill.sql`初始化数据库。

**第二步**;如果安装了git,则可以采用下面的方式快速启动;

```properties
git clone git@github.com:Grootzz/dis-seckill.git
mvn clean package
```
启动缓存服务:

```properties
java -jar dis-seckill-cache/target/dis-seckill-cache-0.0.1-SNAPSHOT.jar
```

启动用户服务:

```properties
java -jar dis-seckill-user/target/dis-seckill-user-0.0.1-SNAPSHOT.jar
```

启动订单服务:

```properties
java -jar dis-seckill-order/target/dis-seckill-order-0.0.1-SNAPSHOT.jar
```

启动商品服务:

```properties
java -jar dis-seckill-goods/target/dis-seckill-goods-0.0.1-SNAPSHOT.jar
```

启动消息队列服务:

```properties
java -jar dis-seckill-mq/target/dis-seckill-mq-0.0.1-SNAPSHOT.jar
```

启动网关服务:

```properties
java -jar dis-seckill-gateway/target/dis-seckill-gateway-0.0.1-SNAPSHOT.jar
```

> 注:启动服务时最好按上面的顺序启动。

如果将项目导入IDE中进行构建,则分别按上面的顺序启动服务即可。

**第三步**;访问项目入口地址

<http://localhost:8082/user/index>

初始用户手机号码:18342390420,密码:000000

## 系统架构
![系统架构](doc/assets/SYSTEM_ARCHITECTURE.png)

- 注册中心使用zookeeper;
- 缓存采用redis;
- 消息队列采用RabbitMQ;
- 用户请求全部交由Gateway模块处理;
- Gateway模块使用RPC的方式调用其他模块提供的服务完成业务处理。

## 模块介绍

- dis-seckill-common:通用模块
- dis-seckill-user:用户模块
- dis-seckill-goods:商品模块
- dis-seckill-order:订单模块
- dis-seckill-gateway:网关模块
- dis-seckill-cache:缓存模块
- dis-seckill-mq:消息队列模块

## Q&A

- [前后端交互接口定义](https://github.com/Grootzz/dis-seckill/blob/master/doc/前后端交互接口定义.md)
- [前后端交互接口逻辑实现](https://github.com/Grootzz/dis-seckill/blob/master/doc/前后端交互接口逻辑实现.md)
- [Redis中存储的数据](https://github.com/Grootzz/dis-seckill/blob/master/doc/Redis中存储的数据.md)
- [使用分布式锁解决恶意用户重复注册问题](https://github.com/Grootzz/dis-seckill/blob/master/doc/使用分布式锁解决恶意用户重复注册问题.md)
- ......

## TODO

- [ ] 引入JWT简化权限验证;
- [x] 完成用户注册功能;
- [x] 引入分布式锁保证更改密码接口用户注册接口的幂等性,防止用户恶意访问;
- [ ] 服务模块横向扩展;
- [ ] 服务调用的负载均衡与服务降级;
- [ ] gateway模块横向扩展,降低单个应用的压力;
- [ ] Nginx水平扩展;
- [ ] 接口压测;
- [ ] ......

## 参考

- <https://blog.csdn.net/gxftry1st/article/details/78560816>

- <http://coding.imooc.com/class/168.html>
- <https://github.com/Grootzz/seckill>

================================================
FILE: dis-seckill-cache/pom.xml
================================================
<?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">
    <parent>
        <artifactId>dis-seckill</artifactId>
        <groupId>com.seckill.dis</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>dis-seckill-cache</artifactId>
    <name>dis-seckill-cache</name>
    <description>分布式缓存服务模块</description>

    <dependencyManagement>
        <dependencies>
            <!-- Spring Boot -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- Aapche Dubbo  -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-dependencies-bom</artifactId>
                <version>${dubbo.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>${dubbo.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>javax.servlet</groupId>
                        <artifactId>servlet-api</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>log4j</groupId>
                        <artifactId>log4j</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>com.seckill.dis</groupId>
            <artifactId>dis-seckill-common</artifactId>
            <version>1.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>

        <!-- Dubbo -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>${dubbo.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>

        <!-- Zookeeper dependencies -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <version>${dubbo.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
            <type>pom</type>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

<!--        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>-->

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

================================================
FILE: dis-seckill-cache/src/main/java/com/seckill/dis/cache/CacheApplication.java
================================================
package com.seckill.dis.cache;


import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

/**
 * 缓存服务
 */
@SpringBootApplication
public class CacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(CacheApplication.class, args);
    }
}


================================================
FILE: dis-seckill-cache/src/main/java/com/seckill/dis/cache/config/RedisConfig.java
================================================
package com.seckill.dis.cache.config;


import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "redis")
public class RedisConfig {

    private String host;
    private int port;
    private int timeout;//秒
    //    private String password;
    private int poolMaxTotal;
    private int poolMaxIdle;
    private int poolMaxWait;//秒


    public String getHost() {
        return host;
    }

    public void setHost(String host) {
        this.host = host;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }

    public int getTimeout() {
        return timeout;
    }

    public void setTimeout(int timeout) {
        this.timeout = timeout;
    }

    public int getPoolMaxTotal() {
        return poolMaxTotal;
    }

    public void setPoolMaxTotal(int poolMaxTotal) {
        this.poolMaxTotal = poolMaxTotal;
    }

    public int getPoolMaxIdle() {
        return poolMaxIdle;
    }

    public void setPoolMaxIdle(int poolMaxIdle) {
        this.poolMaxIdle = poolMaxIdle;
    }

    public int getPoolMaxWait() {
        return poolMaxWait;
    }

    public void setPoolMaxWait(int poolMaxWait) {
        this.poolMaxWait = poolMaxWait;
    }
}


================================================
FILE: dis-seckill-cache/src/main/java/com/seckill/dis/cache/config/RedisPoolFactory.java
================================================
package com.seckill.dis.cache.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

/**
 * jedis连接池
 */
@Component
public class RedisPoolFactory {

    @Autowired
    RedisConfig redisConfig;

    @Bean
    public JedisPool JedisPoolFactory() {
        // jedis连接池
        JedisPoolConfig poolConfig = new JedisPoolConfig();
        poolConfig.setMaxIdle(redisConfig.getPoolMaxIdle());
        poolConfig.setMaxTotal(redisConfig.getPoolMaxTotal());
        poolConfig.setMaxWaitMillis(redisConfig.getPoolMaxWait() * 1000);

        JedisPool pool = new JedisPool(poolConfig, redisConfig.getHost(), redisConfig.getPort(), redisConfig.getTimeout());
        return pool;
    }
}


================================================
FILE: dis-seckill-cache/src/main/java/com/seckill/dis/cache/service/RedisLockImpl.java
================================================
package com.seckill.dis.cache.service;

import com.seckill.dis.common.api.cache.DLockApi;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

import java.util.Collections;

/**
 * Redis分布式锁
 *
 * @author noodle
 */
@Service(interfaceClass = DLockApi.class)
public class RedisLockImpl implements DLockApi {

    /**
     * 通过连接池对象可以获得对redis的连接
     */
    @Autowired
    private JedisPool jedisPool;

    private final String LOCK_SUCCESS = "OK";
    private final String SET_IF_NOT_EXIST = "NX";
    private final String SET_WITH_EXPIRE_TIME = "PX";

    private final Long RELEASE_SUCCESS = 1L;

    /**
     * 获取锁
     *
     * @param lockKey     锁
     * @param uniqueValue 能够唯一标识请求的值,以此保证锁的加解锁是同一个客户端
     * @param expireTime  过期时间, 单位:milliseconds
     * @return
     */
    public boolean lock(String lockKey, String uniqueValue, int expireTime) {

        Jedis jedis = null;

        try {
            jedis = jedisPool.getResource();
            // 获取锁
            String result = jedis.set(lockKey, uniqueValue, SET_IF_NOT_EXIST, SET_WITH_EXPIRE_TIME, expireTime);

            if (LOCK_SUCCESS.equals(result)) {
                return true;
            }
            return false;
        } finally {
            if (jedis != null)
                jedis.close();
        }
    }

    /**
     * 使用Lua脚本保证解锁的原子性
     *
     * @param lockKey     锁
     * @param uniqueValue 能够唯一标识请求的值,以此保证锁的加解锁是同一个客户端
     * @return
     */
    public boolean unlock(String lockKey, String uniqueValue) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            // 使用Lua脚本保证操作的原子性
            String script = "if redis.call('get', KEYS[1]) == ARGV[1] then " +
                    "return redis.call('del', KEYS[1]) " +
                    "else return 0 " +
                    "end";
            Object result = jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(uniqueValue));

            if (RELEASE_SUCCESS.equals(result)) {
                return true;
            }
            return false;
        } finally {
            if (jedis != null)
                jedis.close();
        }
    }
}


================================================
FILE: dis-seckill-cache/src/main/java/com/seckill/dis/cache/service/RedisServiceImpl.java
================================================
package com.seckill.dis.cache.service;

import com.alibaba.fastjson.JSON;
import com.seckill.dis.common.api.cache.RedisServiceApi;
import com.seckill.dis.common.api.cache.vo.KeyPrefix;
import org.apache.dubbo.config.annotation.Service;
import org.springframework.beans.factory.annotation.Autowired;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;

/**
 * redis服务实现
 *
 * @author noodle
 */
@Service(interfaceClass = RedisServiceApi.class)
public class RedisServiceImpl implements RedisServiceApi {

    /**
     * 通过连接池对象可以获得对redis的连接
     */
    @Autowired
    JedisPool jedisPool;

    @Override
    public <T> T get(KeyPrefix prefix, String key, Class<T> clazz) {
        Jedis jedis = null;// redis连接

        try {
            jedis = jedisPool.getResource();
            // 生成真正的存储于redis中的key
            String realKey = prefix.getPrefix() + key;
            // 通过key获取存储于redis中的对象(这个对象是以json格式存储的,所以是字符串)
            String strValue = jedis.get(realKey);
            // 将json字符串转换为对应的对象
            T objValue = stringToBean(strValue, clazz);
            return objValue;
        } finally {
            // 归还redis连接到连接池
            returnToPool(jedis);
        }
    }

    @Override
    public <T> boolean set(KeyPrefix prefix, String key, T value) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            // 将对象转换为json字符串
            String strValue = beanToString(value);

            if (strValue == null || strValue.length() <= 0)
                return false;

            // 生成实际存储于redis中的key
            String realKey = prefix.getPrefix() + key;
            // 获取key的过期时间
            int expires = prefix.expireSeconds();

            if (expires <= 0) {
                // 设置key的过期时间为redis默认值(由redis的缓存策略控制)
                jedis.set(realKey, strValue);
            } else {
                // 设置key的过期时间
                jedis.setex(realKey, expires, strValue);
            }
            return true;
        } finally {
            returnToPool(jedis);
        }
    }

    @Override
    public boolean exists(KeyPrefix keyPrefix, String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String realKey = keyPrefix.getPrefix() + key;
            return jedis.exists(realKey);
        } finally {
            returnToPool(jedis);
        }
    }

    @Override
    public long incr(KeyPrefix keyPrefix, String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String realKey = keyPrefix.getPrefix() + key;
            return jedis.incr(realKey);
        } finally {
            returnToPool(jedis);
        }
    }

    @Override
    public long decr(KeyPrefix keyPrefix, String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String realKey = keyPrefix.getPrefix() + key;
            return jedis.decr(realKey);
        } finally {
            returnToPool(jedis);
        }
    }

    @Override
    public boolean delete(KeyPrefix prefix, String key) {
        Jedis jedis = null;
        try {
            jedis = jedisPool.getResource();
            String realKey = prefix.getPrefix() + key;
            Long del = jedis.del(realKey);
            return del > 0;
        } finally {
            returnToPool(jedis);
        }
    }

    /**
     * 将对象转换为对应的json字符串
     *
     * @param value 对象
     * @param <T>   对象的类型
     * @return 对象对应的json字符串
     */
    public static <T> String beanToString(T value) {
        if (value == null)
            return null;

        Class<?> clazz = value.getClass();
        /*首先对基本类型处理*/
        if (clazz == int.class || clazz == Integer.class)
            return "" + value;
        else if (clazz == long.class || clazz == Long.class)
            return "" + value;
        else if (clazz == String.class)
            return (String) value;
            /*然后对Object类型的对象处理*/
        else
            return JSON.toJSONString(value);
    }

    /**
     * 根据传入的class参数,将json字符串转换为对应类型的对象
     *
     * @param strValue json字符串
     * @param clazz    类型
     * @param <T>      类型参数
     * @return json字符串对应的对象
     */
    public static <T> T stringToBean(String strValue, Class<T> clazz) {

        if ((strValue == null) || (strValue.length() <= 0) || (clazz == null))
            return null;

        // int or Integer
        if ((clazz == int.class) || (clazz == Integer.class))
            return (T) Integer.valueOf(strValue);
            // long or Long
        else if ((clazz == long.class) || (clazz == Long.class))
            return (T) Long.valueOf(strValue);
            // String
        else if (clazz == String.class)
            return (T) strValue;
            // 对象类型
        else
            return JSON.toJavaObject(JSON.parseObject(strValue), clazz);
    }

    /**
     * 将redis连接对象归还到redis连接池
     *
     * @param jedis
     */
    private void returnToPool(Jedis jedis) {
        if (jedis != null)
            jedis.close();
    }

}


================================================
FILE: dis-seckill-cache/src/main/resources/application.properties
================================================
# --------------------------------
#   spring 
#---------------------------------
spring.application.name=dis-seckill-cache
# --------------------------------
#   web 
#---------------------------------
server.port=8085
#---------------------------------
#   Dubbo Application 
#---------------------------------
# Base packages to scan Dubbo Component: @org.apache.dubbo.config.annotation.Service
dubbo.scan.base-packages=com.seckill.dis.cache.service
# The default value of dubbo.application.name is ${spring.application.name}
dubbo.application.name=${spring.application.name}
# Dubbo Protocol
dubbo.protocol.name=dubbo
dubbo.protocol.port=12348
dubbo.registry.check=true
## Dubbo Registry
embedded.zookeeper.port=2181
dubbo.registry.address=zookeeper://127.0.0.1:${embedded.zookeeper.port}
#---------------------------------
#   redis 
#---------------------------------
#redis.host=192.168.216.128
redis.host=127.0.0.1
redis.port=6379
redis.timeout=1000
# redis.password=123456
redis.poolMaxTotal=1000
redis.poolMaxIdle=500
redis.poolMaxWait=500
#---------------------------------
#   ־
#---------------------------------
logging.level.root=info
logging.level.com.seckill.dis=debug
logging.path=logs/
logging.file=dis-seckill.log

================================================
FILE: dis-seckill-common/pom.xml
================================================
<?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">
    <parent>
        <artifactId>dis-seckill</artifactId>
        <groupId>com.seckill.dis</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>dis-seckill-common</artifactId>
    <version>${top-version}</version>
    <!--<packaging>pom</packaging>-->
    <packaging>jar</packaging>

    <name>dis-seckill-common</name>
    <description>通用模块</description>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>

        <!-- Dubbo -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>${dubbo.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>

        <!-- Zookeeper dependencies -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <version>${dubbo.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
            <type>pom</type>
        </dependency>

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

        <dependency>
            <groupId>commons-codec</groupId>
            <artifactId>commons-codec</artifactId>
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
        </dependency>
    </dependencies>

</project>

================================================
FILE: dis-seckill-common/schema/seckill.sql
================================================
# ------------------------------------------------------------------------
#
#   创建秒杀场景下的表, 包含以下几个表:
#
#   1. 秒杀用户表        seckill_user
#   2. 商品表            goods
#   3. 参与秒杀的商品表   seckill_goods
#   4. 秒杀订单表        seckill_order
#   5. 订单表            order_info
#
# ------------------------------------------------------------------------

CREATE DATABASE seckill;

USE seckill;

# ************************************************************************
# 秒杀用户表
DROP TABLE IF EXISTS `seckill_user`;
CREATE TABLE `seckill_user` (
  UUID            INT          AUTO_INCREMENT   COMMENT '主键编号,用户id',
  phone           BIGINT(20)   NOT NULL         COMMENT '手机号码',
  nickname        VARCHAR(255) NOT NULL         COMMENT '昵称',
  password        VARCHAR(32)  DEFAULT NULL     COMMENT 'MD5(MD5(password明文+固定salt) + salt)',
  salt            VARCHAR(10)  DEFAULT NULL     COMMENT '盐值',
  head            VARCHAR(128) DEFAULT NULL     COMMENT '头像,云存储的id',
  register_date   DATETIME     DEFAULT NULL     COMMENT '注册时间',
  last_login_date DATETIME     DEFAULT NULL     COMMENT '上次登录时间',
  login_count     INT(11)      DEFAULT 0        COMMENT '登录次数',
  PRIMARY KEY (UUID)
)
  ENGINE = InnoDB
  AUTO_INCREMENT = 2
  DEFAULT CHARSET = utf8mb4
  COMMENT '秒杀用户表';

# 插入一条记录(未经过MD5的密码为000000, 两次MD5后的密码为记录中的密码,两次MD5的salt一样)
INSERT INTO seckill_user (phone, nickname, password, salt)
VALUES (18342390420, 'Noodle', '5e7b3a9754c2777f96174d4ccb980d23', '1a2b3c4d');


# ************************************************************************
# 创建商品表
DROP TABLE IF EXISTS `goods`;
CREATE TABLE `goods` (
  `id`           BIGINT(20) NOT NULL AUTO_INCREMENT   COMMENT '商品ID',
  `goods_name`   VARCHAR(16)         DEFAULT NULL     COMMENT '商品名称',
  `goods_title`  VARCHAR(64)         DEFAULT NULL     COMMENT '商品标题',
  `goods_img`    VARCHAR(64)         DEFAULT NULL     COMMENT '商品的图片',
  `goods_detail` LONGTEXT            DEFAULT NULL     COMMENT '商品的详情介绍',
  `goods_price`  DECIMAL(10, 2)      DEFAULT '0.00'   COMMENT '商品单价',
  `goods_stock`  INT(11)             DEFAULT '0'      COMMENT '商品库存,-1表示没有限制',
  PRIMARY KEY (`id`)
)
  ENGINE = InnoDB
  AUTO_INCREMENT = 3
  DEFAULT CHARSET = utf8mb4;

# 在商品表中添加4条记录
INSERT INTO `goods` VALUES
  (1, 'iphoneX', 'Apple iPhone X (A1865) 64GB 银色 移动联通电信4G手机', '/img/iphonex.png','Apple iPhone X (A1865) 64GB 银色 移动联通电信4G手机', 8765.00, 10000),
  (2, '华为Meta9', '华为 Mate 9 4GB+32GB版 月光银 移动联通电信4G手机 双卡双待', '/img/meta10.png','华为 Mate 9 4GB+32GB版 月光银 移动联通电信4G手机 双卡双待', 3212.00, -1),
  (3, 'iphone8', 'Apple iPhone 8 (A1865) 64GB 银色 移动联通电信4G手机', '/img/iphone8.png','Apple iPhone 8 (A1865) 64GB 银色 移动联通电信4G手机', 5589.00, 10000),
  (4, '小米6', '小米6 4GB+32GB版 月光银 移动联通电信4G手机 双卡双待', '/img/mi6.png', '小米6 4GB+32GB版 月光银 移动联通电信4G手机 双卡双待', 3212.00, 10000);


# ************************************************************************
# 秒杀商品表
DROP TABLE IF EXISTS `seckill_goods`;
CREATE TABLE `seckill_goods` (
  `id`            BIGINT(20) NOT NULL AUTO_INCREMENT  COMMENT '秒杀的商品表',
  `goods_id`      BIGINT(20)          DEFAULT NULL    COMMENT '商品Id',
  `seckill_price` DECIMAL(10, 2)      DEFAULT '0.00'  COMMENT '秒杀价',
  `stock_count`   INT(11)             DEFAULT NULL    COMMENT '库存数量',
  `start_date`    DATETIME            DEFAULT NULL    COMMENT '秒杀开始时间',
  `end_date`      DATETIME            DEFAULT NULL    COMMENT '秒杀结束时间',
  PRIMARY KEY (`id`)
)
  ENGINE = InnoDB
  AUTO_INCREMENT = 5
  DEFAULT CHARSET = utf8mb4;

# 插入两条记录
INSERT INTO `seckill_goods` VALUES
  (1, 1, 0.01, 9, '2017-12-04 21:51:23', '2017-12-31 21:51:27'),
  (2, 2, 0.01, 9, '2017-12-04 21:40:14', '2017-12-31 14:00:24'),
  (3, 3, 0.01, 9, '2017-12-04 21:40:14', '2017-12-31 14:00:24'),
  (4, 4, 0.01, 9, '2017-12-04 21:40:14', '2017-12-31 14:00:24');


# ************************************************************************
# 秒杀订单表
DROP TABLE IF EXISTS `seckill_order`;
CREATE TABLE `seckill_order` (
  `id`       BIGINT(20) NOT NULL AUTO_INCREMENT,
  `user_id`  BIGINT(20)          DEFAULT NULL     COMMENT '用户ID',
  `order_id` BIGINT(20)          DEFAULT NULL     COMMENT '订单ID',
 `goods_id`  BIGINT(20)          DEFAULT NULL     COMMENT '商品ID',
  PRIMARY KEY (`id`),
  UNIQUE KEY `u_uid_gid` (`user_id`, `goods_id`) USING BTREE COMMENT '定义联合索引'
)
  ENGINE = InnoDB
  AUTO_INCREMENT = 1551
  DEFAULT CHARSET = utf8mb4;


# ************************************************************************
# 订单信息表
DROP TABLE IF EXISTS `order_info`;
CREATE TABLE `order_info` (
  `id`               BIGINT(20)          NOT NULL AUTO_INCREMENT,
  `user_id`          BIGINT(20)          DEFAULT NULL   COMMENT '用户ID',
  `goods_id`         BIGINT(20)          DEFAULT NULL   COMMENT '商品ID',
  `delivery_addr_id` BIGINT(20)          DEFAULT NULL   COMMENT '收获地址ID',
  `goods_name`       VARCHAR(16)         DEFAULT NULL   COMMENT '冗余过来的商品名称',
  `goods_count`      INT(11)             DEFAULT '0'    COMMENT '商品数量',
  `goods_price`      DECIMAL(10, 2)      DEFAULT '0.00' COMMENT '商品单价',
  `order_channel`    TINYINT(4)          DEFAULT '0'    COMMENT '1-pc,2-android,3-ios',
  `status`           TINYINT(4)          DEFAULT '0'    COMMENT '订单状态,0新建未支付,1已支付,2已发货,3已收货,4已退款,5已完成',
  `create_date`      DATETIME            DEFAULT NULL   COMMENT '订单的创建时间',
  `pay_date`         DATETIME            DEFAULT NULL   COMMENT '支付时间',
  PRIMARY KEY (`id`)
)
  ENGINE = InnoDB
  AUTO_INCREMENT = 1565
  DEFAULT CHARSET = utf8mb4;





================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/DLockApi.java
================================================
package com.seckill.dis.common.api.cache;

/**
 * 分布式锁接口
 *
 * @author noodle
 */
public interface DLockApi {
    /**
     * 获取锁
     *
     * @param lockKey     锁
     * @param uniqueValue 能够唯一标识请求的值,以此保证锁的加解锁是同一个客户端
     * @param expireTime  过期时间, 单位:milliseconds
     * @return
     */
    boolean lock(String lockKey, String uniqueValue, int expireTime);

    /**
     * 使用Lua脚本保证解锁的原子性
     *
     * @param lockKey     锁
     * @param uniqueValue 能够唯一标识请求的值,以此保证锁的加解锁是同一个客户端
     * @return
     */
    boolean unlock(String lockKey, String uniqueValue);
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/RedisServiceApi.java
================================================
package com.seckill.dis.common.api.cache;

import com.seckill.dis.common.api.cache.vo.KeyPrefix;

/**
 * redis 服务接口
 *
 * @author noodle
 */
public interface RedisServiceApi {


    /**
     * redis 的get操作,通过key获取存储在redis中的对象
     *
     * @param prefix key的前缀
     * @param key    业务层传入的key
     * @param clazz  存储在redis中的对象类型(redis中是以字符串存储的)
     * @param <T>    指定对象对应的类型
     * @return 存储于redis中的对象
     */
    <T> T get(KeyPrefix prefix, String key, Class<T> clazz);

    /**
     * redis的set操作
     *
     * @param prefix 键的前缀
     * @param key    键
     * @param value  值
     * @return 操作成功为true,否则为true
     */
    <T> boolean set(KeyPrefix prefix, String key, T value);

    /**
     * 判断key是否存在于redis中
     *
     * @param keyPrefix key的前缀
     * @param key
     * @return
     */
    boolean exists(KeyPrefix keyPrefix, String key);

    /**
     * 自增
     *
     * @param keyPrefix
     * @param key
     * @return
     */
    long incr(KeyPrefix keyPrefix, String key);

    /**
     * 自减
     *
     * @param keyPrefix
     * @param key
     * @return
     */
    long decr(KeyPrefix keyPrefix, String key);


    /**
     * 删除缓存中的用户数据
     *
     * @param prefix
     * @param key
     * @return
     */
    boolean delete(KeyPrefix prefix, String key);
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/AccessKeyPrefix.java
================================================
package com.seckill.dis.common.api.cache.vo;

import java.io.Serializable;

/**
 * 访问次数的key前缀
 *
 * @author noodle
 */
public class AccessKeyPrefix extends BaseKeyPrefix implements Serializable{
    public AccessKeyPrefix(String prefix) {
        super(prefix);
    }

    public AccessKeyPrefix(int expireSeconds, String prefix) {
        super(expireSeconds, prefix);
    }

    // 可灵活设置过期时间
    public static AccessKeyPrefix withExpire(int expireSeconds) {
        return new AccessKeyPrefix(expireSeconds, "access");
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/BaseKeyPrefix.java
================================================
package com.seckill.dis.common.api.cache.vo;


/**
 * 模板方法的基本类
 *
 * @author noodle
 */
public abstract class BaseKeyPrefix implements KeyPrefix {

    /**
     * 过期时间
     */
    int expireSeconds;

    /**
     * 前缀
     */
    String prefix;


    /**
     * 默认过期时间为0,即不过期,过期时间受到redis的缓存策略影响
     *
     * @param prefix 前缀
     */
    public BaseKeyPrefix(String prefix) {
        this(0, prefix);
    }


    public BaseKeyPrefix(int expireSeconds, String prefix) {
        this.expireSeconds = expireSeconds;
        this.prefix = prefix;
    }

    /**
     * 默认0代表永不过期
     *
     * @return
     */
    @Override
    public int expireSeconds() {
        return expireSeconds;
    }

    /**
     * 前缀为模板类的实现类的类名
     *
     * @return
     */
    @Override
    public String getPrefix() {
        String simpleName = getClass().getSimpleName();
        return simpleName + ":" + prefix;
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/GoodsKeyPrefix.java
================================================
package com.seckill.dis.common.api.cache.vo;


import java.io.Serializable;

/**
 * redis中,用于商品信息的key
 *
 * @author noodle
 */
public class GoodsKeyPrefix extends BaseKeyPrefix  implements Serializable {
    public GoodsKeyPrefix(int expireSeconds, String prefix) {
        super(expireSeconds, prefix);
    }

    // 缓存在redis中的商品列表页面的key的前缀
    public static GoodsKeyPrefix goodsListKeyPrefix = new GoodsKeyPrefix(60, "goodsList");
    public static GoodsKeyPrefix GOODS_LIST_HTML = new GoodsKeyPrefix(60, "goodsListHtml");

    // 缓存在redis中的商品详情页面的key的前缀
    public static GoodsKeyPrefix goodsDetailKeyPrefix = new GoodsKeyPrefix(60, "goodsDetail");

    // 缓存在redis中的商品库存的前缀(缓存过期时间为永久)
    public static GoodsKeyPrefix seckillGoodsStockPrefix = new GoodsKeyPrefix(0, "goodsStock");
    /**
     * 缓存在redis中的商品库存的前缀(缓存过期时间为永久)
     */
    public static GoodsKeyPrefix GOODS_STOCK = new GoodsKeyPrefix(0, "goodsStock");
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/KeyPrefix.java
================================================
package com.seckill.dis.common.api.cache.vo;

/**
 * redis 键的前缀
 * 之所以在key前面设置一个前缀,是因为如果出现设置相同的key情形,可以通过前缀加以区分
 *
 * @author noodle
 */
public interface KeyPrefix {

    /**
     * key的过期时间
     *
     * @return 过期时间
     */
    int expireSeconds();

    /**
     * key的前缀
     *
     * @return 前缀
     */
    String getPrefix();
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/OrderKeyPrefix.java
================================================
package com.seckill.dis.common.api.cache.vo;

import java.io.Serializable;

/**
 * 存储订单的key前缀
 *
 * @author noodle
 */
public class OrderKeyPrefix extends BaseKeyPrefix  implements Serializable {

    public OrderKeyPrefix(int expireSeconds, String prefix) {
        super(expireSeconds, prefix);
    }

    public OrderKeyPrefix(String prefix) {
        super(prefix);
    }

    /**
     * 秒杀订单信息的前缀
     */
    public static OrderKeyPrefix getSeckillOrderByUidGid = new OrderKeyPrefix("getSeckillOrderByUidGid");
    public static OrderKeyPrefix SK_ORDER = new OrderKeyPrefix("SK_ORDER");


}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/SkKeyPrefix.java
================================================
package com.seckill.dis.common.api.cache.vo;


import java.io.Serializable;

/**
 * 判断秒杀状态的key前缀
 */
public class SkKeyPrefix extends BaseKeyPrefix implements Serializable {
    public SkKeyPrefix(String prefix) {
        super(prefix);
    }

    public SkKeyPrefix(int expireSeconds, String prefix) {
        super(expireSeconds, prefix);
    }

    public static SkKeyPrefix isGoodsOver = new SkKeyPrefix("isGoodsOver");
    /**
     * 库存为0的商品的前缀
     */
    public static SkKeyPrefix GOODS_SK_OVER = new SkKeyPrefix("goodsSkOver");

    /**
     * 秒杀接口随机地址
     */
    public static SkKeyPrefix skPath = new SkKeyPrefix(60, "skPath");
    public static SkKeyPrefix SK_PATH = new SkKeyPrefix(60, "skPath");
    // 验证码5分钟有效
    public static SkKeyPrefix skVerifyCode = new SkKeyPrefix(300, "skVerifyCode");
    /**
     * 验证码5分钟有效
     */
    public static SkKeyPrefix VERIFY_RESULT = new SkKeyPrefix(300, "verifyResult");
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/SkUserKeyPrefix.java
================================================
package com.seckill.dis.common.api.cache.vo;

import java.io.Serializable;

/**
 * 秒杀用户信息的key前缀
 */

public class SkUserKeyPrefix extends BaseKeyPrefix  implements Serializable {

    public static final int TOKEN_EXPIRE = 30*60;// 缓存有效时间为30min

    public SkUserKeyPrefix(int expireSeconds, String prefix) {
        super(expireSeconds, prefix);
    }

    public static SkUserKeyPrefix token = new SkUserKeyPrefix(TOKEN_EXPIRE, "token");
    /**
     * 用户cookie
     */
    public static SkUserKeyPrefix TOKEN = new SkUserKeyPrefix(TOKEN_EXPIRE, "token");
    /**
     * 用于存储用户对象到redis的key前缀
     */
    public static SkUserKeyPrefix getSeckillUserById = new SkUserKeyPrefix(0, "id");
    public static SkUserKeyPrefix SK_USER_PHONE = new SkUserKeyPrefix(0, "id");

}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/UserKey.java
================================================
package com.seckill.dis.common.api.cache.vo;

import java.io.Serializable;

/**
 * redis中,用于管理用户表的key
 */
public class UserKey extends BaseKeyPrefix  implements Serializable {

    public UserKey(String prefix) {
        super(prefix);
    }

    public static UserKey getById = new UserKey("id");
    public static UserKey getByName = new UserKey("name");

}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/goods/GoodsServiceApi.java
================================================
package com.seckill.dis.common.api.goods;

import com.seckill.dis.common.api.goods.vo.GoodsVo;

import java.util.List;

/**
 * 商品服务接口
 *
 * @author noodle
 */
public interface GoodsServiceApi {

    /**
     * 获取商品列表
     *
     * @return
     */
    List<GoodsVo> listGoodsVo();

    /**
     * 通过商品的id查出商品的所有信息(包含该商品的秒杀信息)
     *
     * @param goodsId
     * @return
     */
    GoodsVo getGoodsVoByGoodsId(long goodsId);

    /**
     * 通过商品的id查出商品的所有信息(包含该商品的秒杀信息)
     *
     * @param goodsId
     * @return
     */
    GoodsVo getGoodsVoByGoodsId(Long goodsId);

    /**
     * order表减库存
     *
     * @param goods
     */
    boolean reduceStock(GoodsVo goods);
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/goods/vo/GoodsDetailVo.java
================================================
package com.seckill.dis.common.api.goods.vo;

import com.seckill.dis.common.api.user.vo.UserVo;

import java.io.Serializable;

/**
 * 商品详情
 * 用于将数据传递给客户端
 *
 * @author noodle
 */
public class GoodsDetailVo implements Serializable {


    private int seckillStatus = 0;
    private int remainSeconds = 0;
    private GoodsVo goods;
    private UserVo user;

    @Override
    public String toString() {
        return "GoodsDetailVo{" +
                "seckillStatus=" + seckillStatus +
                ", remainSeconds=" + remainSeconds +
                ", goods=" + goods +
                ", user=" + user +
                '}';
    }

    public int getSeckillStatus() {
        return seckillStatus;
    }

    public void setSeckillStatus(int seckillStatus) {
        this.seckillStatus = seckillStatus;
    }

    public int getRemainSeconds() {
        return remainSeconds;
    }

    public void setRemainSeconds(int remainSeconds) {
        this.remainSeconds = remainSeconds;
    }

    public GoodsVo getGoods() {
        return goods;
    }

    public void setGoods(GoodsVo goods) {
        this.goods = goods;
    }

    public UserVo getUser() {
        return user;
    }

    public void setUser(UserVo user) {
        this.user = user;
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/goods/vo/GoodsVo.java
================================================
package com.seckill.dis.common.api.goods.vo;

import com.seckill.dis.common.domain.Goods;

import java.io.Serializable;
import java.util.Date;

/**
 * 商品信息(并且包含商品的秒杀信息)
 * 商品信息和商品的秒杀信息是存储在两个表中的(goods 和 seckill_goods)
 * 继承 Goods 便具有了 goods 表的信息,再额外添加上 seckill_goods 的信息即可
 *
 * @author noodle
 */
public class GoodsVo extends Goods implements Serializable {

    /*只包含了部分 seckill_goods 表的信息*/
    private Double seckillPrice;
    private Integer stockCount;
    private Date startDate;
    private Date endDate;

    public Double getSeckillPrice() {
        return seckillPrice;
    }

    public void setSeckillPrice(Double seckillPrice) {
        this.seckillPrice = seckillPrice;
    }

    public Integer getStockCount() {
        return stockCount;
    }

    public void setStockCount(Integer stockCount) {
        this.stockCount = stockCount;
    }

    public Date getStartDate() {
        return startDate;
    }

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

    public Date getEndDate() {
        return endDate;
    }

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


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/mq/MqProviderApi.java
================================================
package com.seckill.dis.common.api.mq;

import com.seckill.dis.common.api.mq.vo.SkMessage;

/**
 * 消息队列服务
 *
 * @author noodle
 */
public interface MqProviderApi {

    /**
     * 将用户秒杀信息投递到MQ中(使用direct模式的exchange)
     *
     * @param message
     */
    void sendSkMessage(SkMessage message);
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/mq/vo/SkMessage.java
================================================
package com.seckill.dis.common.api.mq.vo;

import com.seckill.dis.common.api.user.vo.UserVo;

import java.io.Serializable;

/**
 * 在MQ中传递的秒杀信息
 * 包含参与秒杀的用户和商品的id
 *
 * @author noodle
 */
public class SkMessage implements Serializable{

    private UserVo user;

    private long goodsId;


    public UserVo getUser() {
        return user;
    }

    public void setUser(UserVo user) {
        this.user = user;
    }

    public long getGoodsId() {
        return goodsId;
    }

    public void setGoodsId(long goodsId) {
        this.goodsId = goodsId;
    }

    @Override
    public String toString() {
        return "SeckillMessage{" +
                "user=" + user +
                ", goodsId=" + goodsId +
                '}';
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/order/OrderServiceApi.java
================================================
package com.seckill.dis.common.api.order;

import com.seckill.dis.common.api.goods.vo.GoodsVo;
import com.seckill.dis.common.api.user.vo.UserVo;
import com.seckill.dis.common.domain.OrderInfo;
import com.seckill.dis.common.domain.SeckillOrder;

/**
 * 订单服务接口
 *
 * @author noodle
 */
public interface OrderServiceApi {
    /**
     * 通过订单id获取订单
     *
     * @param orderId
     * @return
     */
    OrderInfo getOrderById(long orderId);

    /**
     * 通过用户id与商品id从订单列表中获取订单信息,这个地方用到了唯一索引(unique index!!!!!)
     *
     * @param userId
     * @param goodsId
     * @return 秒杀订单信息
     */
    SeckillOrder getSeckillOrderByUserIdAndGoodsId(long userId, long goodsId);

    /**
     * 创建订单
     *
     * @param user
     * @param goods
     * @return
     */
    OrderInfo createOrder(UserVo user, GoodsVo goods);
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/order/vo/OrderDetailVo.java
================================================
package com.seckill.dis.common.api.order.vo;

import com.seckill.dis.common.api.goods.vo.GoodsVo;
import com.seckill.dis.common.api.user.vo.UserVo;
import com.seckill.dis.common.domain.OrderInfo;

/**
 * 订单详情,包含订单信息和商品信息
 * <p>
 * 用于将数据传递给客户端
 *
 * @author noodle
 */
public class OrderDetailVo {

    /**
     * 用户信息
     */
    private UserVo user;

    /**
     * 商品信息
     */
    private GoodsVo goods;
    /**
     * 订单信息
     */
    private OrderInfo order;

    public UserVo getUser() {
        return user;
    }

    public void setUser(UserVo user) {
        this.user = user;
    }

    public GoodsVo getGoods() {
        return goods;
    }

    public void setGoods(GoodsVo goods) {
        this.goods = goods;
    }

    public OrderInfo getOrder() {
        return order;
    }

    public void setOrder(OrderInfo order) {
        this.order = order;
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/seckill/SeckillServiceApi.java
================================================
package com.seckill.dis.common.api.seckill;

import com.seckill.dis.common.api.goods.vo.GoodsVo;
import com.seckill.dis.common.api.user.vo.UserVo;
import com.seckill.dis.common.domain.OrderInfo;

/**
 * 秒杀服务接口
 *
 * @author noodle
 */
public interface SeckillServiceApi {
    /**
     * 创建验证码
     *
     * @param user
     * @param goodsId
     * @return
     */
    String createVerifyCode(UserVo user, long goodsId);

    /**
     * 执行秒杀操作,包含以下两步:
     * 1. 从goods表中减库存
     * 2. 将生成的订单写入miaosha_order表中
     *
     * @param user  秒杀商品的用户
     * @param goods 所秒杀的商品
     * @return 生成的订单信息
     */
    OrderInfo seckill(UserVo user, GoodsVo goods);

    /**
     * 获取秒杀结果
     *
     * @param userId
     * @param goodsId
     * @return
     */
    long getSeckillResult(Long userId, long goodsId);
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/seckill/vo/VerifyCodeVo.java
================================================
package com.seckill.dis.common.api.seckill.vo;

import java.awt.image.BufferedImage;
import java.io.Serializable;

/**
 * 验证码图片及计算结果
 *
 * @author noodle
 */
public class VerifyCodeVo implements Serializable {
    /**
     * 验证码图片
     */
    private BufferedImage image;

    /**
     * 验证码计算结果
     */
    private int expResult;


    public VerifyCodeVo() {
    }

    public VerifyCodeVo(BufferedImage image, int expResult) {
        this.image = image;
        this.expResult = expResult;
    }

    public BufferedImage getImage() {
        return image;
    }

    public void setImage(BufferedImage image) {
        this.image = image;
    }

    public int getExpResult() {
        return expResult;
    }

    public void setExpResult(int expResult) {
        this.expResult = expResult;
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/UserServiceApi.java
================================================
package com.seckill.dis.common.api.user;

import com.seckill.dis.common.api.user.vo.LoginVo;
import com.seckill.dis.common.api.user.vo.RegisterVo;
import com.seckill.dis.common.api.user.vo.UserInfoVo;
import com.seckill.dis.common.api.user.vo.UserVo;
import com.seckill.dis.common.result.CodeMsg;

import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

/**
 * 用于用户交互api
 *
 * @author noodle
 */
public interface UserServiceApi {

    String COOKIE_NAME_TOKEN = "token";

    /**
     * 登录
     * 返回用户id;用户登录成功后,将用户id从后台传到前台,通过JWT的形式传到客户端,
     * 用户再次访问的时候,就携带JWT到服务端,服务端用一个缓存(如redis)将JWT缓存起来,
     * 并设置有效期,这样,用户不用每次访问都到数据库中查询用户id
     *
     * @param username
     * @param password
     * @return 用户id
     */
    int login(String username, String password);

    /**
     * 注册
     *
     * @param userModel
     * @return
     */
    CodeMsg register(RegisterVo userModel);

    /**
     * 检查用户名是否存在
     *
     * @param username
     * @return
     */
    boolean checkUsername(String username);

    /**
     * 获取用户信息
     *
     * @param uuid
     * @return
     */
    UserInfoVo getUserInfo(int uuid);

    /**
     * 更新用户信息
     *
     * @param userInfoVo
     * @return
     */
    UserInfoVo updateUserInfo(UserInfoVo userInfoVo);

    /**
     * 登录
     *
     * @param loginVo
     * @return
     */
    String login(@Valid LoginVo loginVo);

    /**
     * 根据phone获取用户
     *
     * @param phone
     * @return
     */
    UserVo getUserByPhone(long phone);
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/vo/LoginVo.java
================================================
package com.seckill.dis.common.api.user.vo;

import com.seckill.dis.common.validator.IsMobile;
import org.hibernate.validator.constraints.Length;

import javax.validation.constraints.NotNull;
import java.io.Serializable;

/**
 * 用于接收客户端请求中的表单数据
 * 使用JSR303完成参数校验
 *
 * @author noodle
 */
public class LoginVo implements Serializable{

    /**
     * 通过注解的方式校验手机号(JSR303)
     */
    @NotNull
    @IsMobile// 自定义的注解完成手机号的校验
    private String mobile;

    /**
     * 通过注解的方式校验密码(JSR303)
     */
    @NotNull
    @Length(min = 32)// 长度最小为32
    private String password;

    public String getMobile() {
        return mobile;
    }

    public void setMobile(String mobile) {
        this.mobile = mobile;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "LoginVo{" +
                "mobile='" + mobile + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/vo/RegisterVo.java
================================================
package com.seckill.dis.common.api.user.vo;

import javax.validation.constraints.NotNull;
import java.io.Serializable;

/**
 * 注册参数
 *
 * @author noodle
 */
public class RegisterVo implements Serializable {

    @NotNull
    private Long phone;
    @NotNull
    private String nickname;

    private String head;
    @NotNull
    private String password;

    public Long getPhone() {
        return phone;
    }

    public void setPhone(Long phone) {
        this.phone = phone;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getHead() {
        return head;
    }

    public void setHead(String head) {
        this.head = head;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    @Override
    public String toString() {
        return "RegisterVo{" +
                "phone=" + phone +
                ", nickname='" + nickname + '\'' +
                ", head='" + head + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/vo/UserInfoVo.java
================================================
package com.seckill.dis.common.api.user.vo;

import java.io.Serializable;

/**
 * 用户信息
 * <p>
 * 注:因为需要通过网络传输此model,所以需要序列化
 *
 * @author noodle
 */
public class UserInfoVo implements Serializable {

    private Integer uuid;
    private String username;
    private String nickname;
    private String email;
    private String phone;
    private int sex;
    private String birthday;
    private String lifeState;
    private String biography;
    private String address;
    private String headAddress;
    private long beginTime; // 创建时间
    private long updateTime;// 更新时间

    public Integer getUuid() {
        return uuid;
    }

    public void setUuid(Integer uuid) {
        this.uuid = uuid;
    }

    public String getUsername() {
        return username;
    }

    public void setUsername(String username) {
        this.username = username;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhone() {
        return phone;
    }

    public void setPhone(String phone) {
        this.phone = phone;
    }

    public int getSex() {
        return sex;
    }

    public void setSex(int sex) {
        this.sex = sex;
    }

    public String getBirthday() {
        return birthday;
    }

    public void setBirthday(String birthday) {
        this.birthday = birthday;
    }

    public String getLifeState() {
        return lifeState;
    }

    public void setLifeState(String lifeState) {
        this.lifeState = lifeState;
    }

    public String getBiography() {
        return biography;
    }

    public void setBiography(String biography) {
        this.biography = biography;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public String getHeadAddress() {
        return headAddress;
    }

    public void setHeadAddress(String headAddress) {
        this.headAddress = headAddress;
    }

    public long getBeginTime() {
        return beginTime;
    }

    public void setBeginTime(long beginTime) {
        this.beginTime = beginTime;
    }

    public long getUpdateTime() {
        return updateTime;
    }

    public void setUpdateTime(long updateTime) {
        this.updateTime = updateTime;
    }
}

================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/vo/UserVo.java
================================================
package com.seckill.dis.common.api.user.vo;

import java.io.Serializable;
import java.util.Date;

/**
 * 用户信息
 * <p>
 * 注:因为需要通过网络传输此model,所以需要序列化
 *
 * @author noodle
 */
public class UserVo implements Serializable {

    private Long uuid;
    private Long phone;
    private String nickname;
    private String password;
    private String salt;
    private String head;
    private Date registerDate;
    private Date lastLoginDate;
    private Integer loginCount;

    @Override
    public String toString() {
        return "UserVo{" +
                "uuid=" + uuid +
                ", phone=" + phone +
                ", nickname='" + nickname + '\'' +
                ", password='" + password + '\'' +
                ", salt='" + salt + '\'' +
                ", head='" + head + '\'' +
                ", registerDate=" + registerDate +
                ", lastLoginDate=" + lastLoginDate +
                ", loginCount=" + loginCount +
                '}';
    }

    public Long getUuid() {
        return uuid;
    }

    public void setUuid(Long uuid) {
        this.uuid = uuid;
    }

    public Long getPhone() {
        return phone;
    }

    public void setPhone(Long phone) {
        this.phone = phone;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getSalt() {
        return salt;
    }

    public void setSalt(String salt) {
        this.salt = salt;
    }

    public String getHead() {
        return head;
    }

    public void setHead(String head) {
        this.head = head;
    }

    public Date getRegisterDate() {
        return registerDate;
    }

    public void setRegisterDate(Date registerDate) {
        this.registerDate = registerDate;
    }

    public Date getLastLoginDate() {
        return lastLoginDate;
    }

    public void setLastLoginDate(Date lastLoginDate) {
        this.lastLoginDate = lastLoginDate;
    }

    public Integer getLoginCount() {
        return loginCount;
    }

    public void setLoginCount(Integer loginCount) {
        this.loginCount = loginCount;
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/domain/Goods.java
================================================
package com.seckill.dis.common.domain;

/**
 * goods 表
 *
 * @author noodle
 */
public class Goods {

    private Long id;
    private String goodsName;
    private String goodsTitle;
    private String goodsImg;
    private String goodsDetail;
    private Double goodsPrice;
    private Long goodsStock;


    public Long getId() {
        return id;
    }

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

    public String getGoodsName() {
        return goodsName;
    }

    public void setGoodsName(String goodsName) {
        this.goodsName = goodsName;
    }

    public String getGoodsTitle() {
        return goodsTitle;
    }

    public void setGoodsTitle(String goodsTitle) {
        this.goodsTitle = goodsTitle;
    }

    public String getGoodsImg() {
        return goodsImg;
    }

    public void setGoodsImg(String goodsImg) {
        this.goodsImg = goodsImg;
    }

    public String getGoodsDetail() {
        return goodsDetail;
    }

    public void setGoodsDetail(String goodsDetail) {
        this.goodsDetail = goodsDetail;
    }

    public Double getGoodsPrice() {
        return goodsPrice;
    }

    public void setGoodsPrice(Double goodsPrice) {
        this.goodsPrice = goodsPrice;
    }

    public Long getGoodsStock() {
        return goodsStock;
    }

    public void setGoodsStock(Long goodsStock) {
        this.goodsStock = goodsStock;
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/domain/OrderInfo.java
================================================
package com.seckill.dis.common.domain;


import java.io.Serializable;
import java.util.Date;

/**
 * order_info 订单信息
 *
 * @author noodle
 */
public class OrderInfo implements Serializable{

    private Long id;
    private Long userId;
    private Long goodsId;
    private Long deliveryAddrId;
    private String goodsName;
    private Integer goodsCount;
    private Double goodsPrice;
    private Integer orderChannel;
    private Integer status;
    private Date createDate;
    private Date payDate;


    public Long getId() {
        return id;
    }

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

    public Long getUserId() {
        return userId;
    }

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

    public Long getGoodsId() {
        return goodsId;
    }

    public void setGoodsId(Long goodsId) {
        this.goodsId = goodsId;
    }

    public Long getDeliveryAddrId() {
        return deliveryAddrId;
    }

    public void setDeliveryAddrId(Long deliveryAddrId) {
        this.deliveryAddrId = deliveryAddrId;
    }

    public String getGoodsName() {
        return goodsName;
    }

    public void setGoodsName(String goodsName) {
        this.goodsName = goodsName;
    }

    public Integer getGoodsCount() {
        return goodsCount;
    }

    public void setGoodsCount(Integer goodsCount) {
        this.goodsCount = goodsCount;
    }

    public Double getGoodsPrice() {
        return goodsPrice;
    }

    public void setGoodsPrice(Double goodsPrice) {
        this.goodsPrice = goodsPrice;
    }

    public Integer getOrderChannel() {
        return orderChannel;
    }

    public void setOrderChannel(Integer orderChannel) {
        this.orderChannel = orderChannel;
    }

    public Integer getStatus() {
        return status;
    }

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

    public Date getCreateDate() {
        return createDate;
    }

    public void setCreateDate(Date createDate) {
        this.createDate = createDate;
    }

    public Date getPayDate() {
        return payDate;
    }

    public void setPayDate(Date payDate) {
        this.payDate = payDate;
    }

    @Override
    public String toString() {
        return "OrderInfo{" +
                "id=" + id +
                ", userId=" + userId +
                ", goodsId=" + goodsId +
                ", deliveryAddrId=" + deliveryAddrId +
                ", goodsName='" + goodsName + '\'' +
                ", goodsCount=" + goodsCount +
                ", goodsPrice=" + goodsPrice +
                ", orderChannel=" + orderChannel +
                ", status=" + status +
                ", createDate=" + createDate +
                ", payDate=" + payDate +
                '}';
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/domain/SeckillGoods.java
================================================
package com.seckill.dis.common.domain;

import java.io.Serializable;
import java.util.Date;

/**
 * seckill_goods
 *
 * @author noodle
 */
public class SeckillGoods implements Serializable{

    private Long id;
    private Long goodsId;
    private Double seckillPrice;
    private Integer stockCount;
    private Date startDate;
    private Date endDate;

    public Long getId() {
        return id;
    }

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

    public Long getGoodsId() {
        return goodsId;
    }

    public void setGoodsId(Long goodsId) {
        this.goodsId = goodsId;
    }

    public Double getSeckillPrice() {
        return seckillPrice;
    }

    public void setSeckillPrice(Double seckillPrice) {
        this.seckillPrice = seckillPrice;
    }

    public Integer getStockCount() {
        return stockCount;
    }

    public void setStockCount(Integer stockCount) {
        this.stockCount = stockCount;
    }

    public Date getStartDate() {
        return startDate;
    }

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

    public Date getEndDate() {
        return endDate;
    }

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


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/domain/SeckillOrder.java
================================================
package com.seckill.dis.common.domain;

import java.io.Serializable;

/**
 * seckill_order 表
 *
 * @author noodle
 */
public class SeckillOrder implements Serializable{

    private Long id;
    private Long userId;
    private Long orderId;
    private Long goodsId;


    public Long getId() {
        return id;
    }

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

    public Long getUserId() {
        return userId;
    }

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

    public Long getOrderId() {
        return orderId;
    }

    public void setOrderId(Long orderId) {
        this.orderId = orderId;
    }

    public Long getGoodsId() {
        return goodsId;
    }

    public void setGoodsId(Long goodsId) {
        this.goodsId = goodsId;
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/domain/SeckillUser.java
================================================
package com.seckill.dis.common.domain;

import java.io.Serializable;
import java.util.Date;

/**
 * 秒杀用户信息
 *
 * @author noodle
 */
public class SeckillUser implements Serializable{

    private Long uuid;
    private Long phone;
    private String nickname;
    private String password;
    private String salt;
    private String head;
    private Date registerDate;
    private Date lastLoginDate;
    private Integer loginCount;

    public Long getUuid() {
        return uuid;
    }

    public void setUuid(Long uuid) {
        this.uuid = uuid;
    }

    public Long getPhone() {
        return phone;
    }

    public void setPhone(Long phone) {
        this.phone = phone;
    }

    public String getNickname() {
        return nickname;
    }

    public void setNickname(String nickname) {
        this.nickname = nickname;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }

    public String getSalt() {
        return salt;
    }

    public void setSalt(String salt) {
        this.salt = salt;
    }

    public String getHead() {
        return head;
    }

    public void setHead(String head) {
        this.head = head;
    }

    public Date getRegisterDate() {
        return registerDate;
    }

    public void setRegisterDate(Date registerDate) {
        this.registerDate = registerDate;
    }

    public Date getLastLoginDate() {
        return lastLoginDate;
    }

    public void setLastLoginDate(Date lastLoginDate) {
        this.lastLoginDate = lastLoginDate;
    }

    public Integer getLoginCount() {
        return loginCount;
    }

    public void setLoginCount(Integer loginCount) {
        this.loginCount = loginCount;
    }

    @Override
    public String toString() {
        return "SeckillUser{" +
                "uuid=" + uuid +
                ", phone=" + phone +
                ", nickname='" + nickname + '\'' +
                ", password='" + password + '\'' +
                ", salt='" + salt + '\'' +
                ", head='" + head + '\'' +
                ", registerDate=" + registerDate +
                ", lastLoginDate=" + lastLoginDate +
                ", loginCount=" + loginCount +
                '}';
    }
}



================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/exception/GlobalException.java
================================================
package com.seckill.dis.common.exception;

import com.seckill.dis.common.result.CodeMsg;

/**
 * 全局异常处理器
 *
 * @author noodle
 */
public class GlobalException extends RuntimeException {

    private CodeMsg codeMsg;

    /**
     * 使用构造器接收CodeMsg
     *
     * @param codeMsg
     */
    public GlobalException(CodeMsg codeMsg) {
        this.codeMsg = codeMsg;
    }

    public CodeMsg getCodeMsg() {
        return codeMsg;
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/exception/GlobalExceptionHandler.java
================================================
package com.seckill.dis.common.exception;

import com.seckill.dis.common.result.CodeMsg;
import com.seckill.dis.common.result.Result;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * 全局异常处理器(底层使用方法拦截的方式完成,和AOP一样)
 * 在异常发生时,将会调用这里面的方法给客户端一个响应
 *
 * @author noodle
 */

@ControllerAdvice // 通过Advice可知,这个处理器实际上是一个切面
@ResponseBody
public class GlobalExceptionHandler {

    private static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 异常处理
     *
     * @param request 绑定了出现异常的请求信息
     * @param e       该请求所产生的异常
     * @return 向客户端返回的结果(这里为json数据)
     */
    @ExceptionHandler(value = Exception.class)// 这个注解用指定这个方法对何种异常处理(这里默认所有异常都用这个方法处理)
    public Result<String> exceptionHandler(HttpServletRequest request, Exception e) {
        logger.info("出现异常");
        e.printStackTrace();// 打印原始的异常信息,方便调试

        // 如果所拦截的异常是自定义的全局异常,这按自定义异常的处理方式处理,否则按默认方式处理
        if (e instanceof GlobalException) {
            logger.debug("common 模块中的异常");
            GlobalException exception = (GlobalException) e;
            return Result.error(exception.getCodeMsg());// 向客户端返回异常信息

        } else if (e instanceof BindException) {
            BindException bindException = (BindException) e;
            List<ObjectError> errors = bindException.getAllErrors();
            ObjectError error = errors.get(0);// 这里只获取了第一个错误对象
            String message = error.getDefaultMessage();// 获取其中的信息
            return Result.error(CodeMsg.BIND_ERROR.fillArgs(message));// 将错误信息动态地拼接到已定义的部分信息上
        } else {
            return Result.error(CodeMsg.SERVER_ERROR);
        }
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/result/CodeMsg.java
================================================
package com.seckill.dis.common.result;

import java.io.Serializable;

/**
 * 响应结果状态码
 *
 * @author noodle
 */
public class CodeMsg implements Serializable {

    private int code;
    private String msg;

    /**
     * 通用异常
     */
    public static CodeMsg SUCCESS = new CodeMsg(0, "success");
    public static CodeMsg SERVER_ERROR = new CodeMsg(500100, "服务端异常");
    public static CodeMsg BIND_ERROR = new CodeMsg(500101, "参数校验异常:%s");
    public static CodeMsg REQUEST_ILLEGAL = new CodeMsg(500102, "请求非法");
    public static CodeMsg VERITF_FAIL = new CodeMsg(500103, "校验失败,请重新输入表达式结果或刷新校验码重新输入");
    public static CodeMsg ACCESS_LIMIT_REACHED = new CodeMsg(500104, "访问太频繁!");

    /**
     * 用户模块 5002XX
     */
    public static CodeMsg SESSION_ERROR = new CodeMsg(500210, "Session不存在或者已经失效,请返回登录!");
    public static CodeMsg PASSWORD_EMPTY = new CodeMsg(500211, "登录密码不能为空");
    public static CodeMsg MOBILE_EMPTY = new CodeMsg(500212, "手机号不能为空");
    public static CodeMsg MOBILE_ERROR = new CodeMsg(500213, "手机号格式错误");
    public static CodeMsg MOBILE_NOT_EXIST = new CodeMsg(500214, "手机号不存在");
    public static CodeMsg PASSWORD_ERROR = new CodeMsg(500215, "密码错误");
    public static CodeMsg USER_EXIST = new CodeMsg(500216, "用户已经存在,无需重复注册");
    public static CodeMsg REGISTER_SUCCESS = new CodeMsg(500217, "注册成功");
    public static CodeMsg REGISTER_FAIL = new CodeMsg(500218, "注册异常");
    public static CodeMsg FILL_REGISTER_INFO = new CodeMsg(500219, "请填写注册信息");
    public static CodeMsg WAIT_REGISTER_DONE = new CodeMsg(500220, "等待注册完成");

    //登录模块 5002XX

    //商品模块 5003XX

    //订单模块 5004XX
    public static CodeMsg ORDER_NOT_EXIST = new CodeMsg(500400, "订单不存在");

    /**
     * 秒杀模块 5005XX
     */
    public static CodeMsg SECKILL_OVER = new CodeMsg(500500, "商品已经秒杀完毕");
    public static CodeMsg REPEATE_SECKILL = new CodeMsg(500501, "不能重复秒杀");
    public static CodeMsg SECKILL_FAIL = new CodeMsg(500502, "秒杀失败");
    public static CodeMsg SECKILL_PARM_ILLEGAL = new CodeMsg(500503, "秒杀请求参数异常:%s");

    /**
     * 构造器定义为private是为了防止controller直接new
     *
     * @param code
     * @param msg
     */
    public CodeMsg(int code, String msg) {
        this.code = code;
        this.msg = msg;
    }

    /**
     * 动态地填充msg字段
     *
     * @param args
     * @return
     */
    public CodeMsg fillArgs(Object... args) {
        int code = this.code;
        String message = String.format(this.msg, args);// 将arg格式化到msg中,组合成一个message
        return new CodeMsg(code, message);
    }

    public int getCode() {
        return code;
    }

    public String getMsg() {
        return msg;
    }
}



================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/result/Result.java
================================================
package com.seckill.dis.common.result;

import java.io.Serializable;

/**
 * 用户接口返回结果
 *
 * @param <T> 数据实体类型
 * @author noodle
 */
public class Result<T> implements Serializable {

    /**
     * 状态码
     */
    private int code;

    /**
     * 状态短语
     */
    private String msg;

    /**
     * 数据实体
     */
    private T data;

    /**
     * 定义为private是为了在防止在controller中直接new
     *
     * @param data
     */
    private Result(T data) {
        this.code = 0;
        this.msg = "success";
        this.data = data;
    }

    private Result(CodeMsg codeMsg) {
        if (codeMsg == null)
            return;
        this.code = codeMsg.getCode();
        this.msg = codeMsg.getMsg();
    }

    /**
     * 只有get没有set,是为了防止在controller使用set对结果修改,从而达到一个更好的封装效果
     *
     * @return
     */
    public int getCode() {
        return code;
    }

    /**
     * 业务处理成功返回结果,直接返回业务数据
     *
     * @param data
     * @return
     */
    public static <T> Result<T> success(T data) {
        return new Result<T>(data);
    }

    /**
     * 业务处理信息
     *
     * @param serverError
     * @param <T>
     * @return
     */
    public static <T> Result<T> info(CodeMsg serverError) {
        return new Result<T>(serverError);
    }

    /**
     * 业务处理成功
     *
     * @param serverError
     * @param <T>
     * @return
     */
    public static <T> Result<T> success(CodeMsg serverError) {
        return new Result<T>(serverError);
    }

    /**
     * 业务处理失败
     *
     * @param serverError
     * @param <T>
     * @return
     */
    public static <T> Result<T> error(CodeMsg serverError) {
        return new Result<T>(serverError);
    }

    public String getMsg() {
        return msg;
    }

    public T getData() {
        return data;
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/util/DBUtil.java
================================================
package com.seckill.dis.common.util;

import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.util.Properties;

/**
 * 数据库连接工具
 *
 * @author noodle
 */
public class DBUtil {

    private static Properties properties;

    // 从属性文件中加载数据库配置信息
    static {
        try {
            InputStream resource = DBUtil.class.getClassLoader().getResourceAsStream("application.properties");
            properties = new Properties();
            properties.load(resource);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 获取数据库连接对象
     *
     * @return
     * @throws ClassNotFoundException
     * @throws SQLException
     */
    public static Connection getConn() throws ClassNotFoundException, SQLException {
        String url = properties.getProperty("spring.datasource.url");
        String username = properties.getProperty("spring.datasource.username");
        String password = properties.getProperty("spring.datasource.password");
        String driver = properties.getProperty("spring.datasource.driver-class-name");
        Class.forName(driver);// 加载驱动
        return DriverManager.getConnection(url, username, password);
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/util/JsonUtil.java
================================================
package com.seckill.dis.common.util;

import com.alibaba.fastjson.JSON;

/**
 * json 工具类
 *
 * @author noodle
 */
public class JsonUtil {

    /**
     * 根据传入的class参数,将json字符串转换为对应类型的对象
     *
     * @param strValue json字符串
     * @param clazz    类型
     * @param <T>      类型参数
     * @return json字符串对应的对象
     */
    public static <T> T stringToBean(String strValue, Class<T> clazz) {

        if ((strValue == null) || (strValue.length() <= 0) || (clazz == null))
            return null;

        // int or Integer
        if ((clazz == int.class) || (clazz == Integer.class))
            return (T) Integer.valueOf(strValue);
            // long or Long
        else if ((clazz == long.class) || (clazz == Long.class))
            return (T) Long.valueOf(strValue);
            // String
        else if (clazz == String.class)
            return (T) strValue;
            // 对象类型
        else
            return JSON.toJavaObject(JSON.parseObject(strValue), clazz);
    }

    /**
     * 将对象转换为对应的json字符串
     *
     * @param value 对象
     * @param <T>   对象的类型
     * @return 对象对应的json字符串
     */
    public static <T> String beanToString(T value) {
        if (value == null)
            return null;

        Class<?> clazz = value.getClass();
        /*首先对基本类型处理*/
        if (clazz == int.class || clazz == Integer.class)
            return "" + value;
        else if (clazz == long.class || clazz == Long.class)
            return "" + value;
        else if (clazz == String.class)
            return (String) value;
            /*然后对Object类型的对象处理*/
        else
            return JSON.toJSONString(value);
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/util/MD5Util.java
================================================
package com.seckill.dis.common.util;


import org.apache.commons.codec.digest.DigestUtils;
import org.junit.Test;

/**
 * MD5工具类
 *
 * @author noodle
 */
public class MD5Util {
    /**
     * 获取输入字符串的MD5
     *
     * @param src
     * @return
     */
    public static String md5(String src) {
        return DigestUtils.md5Hex(src);
    }

    // MD5的第一次加盐的salt值,客户端和服务端一致
    public static final String SALT = "1a2b3c4d";

    /**
     * 对客户端输入的密码加盐(第一次加盐),得到的MD5值为表单中传输的值
     * <p>
     * 在密码的传输和存储中,总共经历了两次MD5和两次salt
     * <p>
     * 第一次:客户输入到表单中的密码会经过一次MD5和加盐,即;pwd_md_1st = MD5(用户明文密码)+ salt_1st。
     * 其中,pwd_md_1st 是客户端真正接收到的密码。salt_1st在客户端和服务端都是一样的
     * <p>
     * 第二次:对客户端传递到服务器的 pwd_md_1st 再一次MD5和加盐,即;pwd_md_2nd = MD5(MD5和加盐)+ salt_2nd。
     * 其中,salt_2nd是存储在服务器端的,每个用户都有自己的salt_2nd,所以在使用salt_2nd时需要从数据库中查出
     * <p>
     * 最终存储在数据库中的用户密码实际为 pwd_md_2nd。
     *
     * @param inputPassword 用户输入的密码
     * @return Calculates the MD5 digest and returns the value as a 32 character hex string.
     */
    public static String inputPassToFormPass(String inputPassword) {
        // 加盐规则(自定义)
        String str = "" + SALT.charAt(0) + SALT.charAt(2) + inputPassword + SALT.charAt(5) + SALT.charAt(4);
        // Calculates the MD5 digest and returns the value as a 32 character hex string.
        return md5(str);
    }

    /**
     * 对表单中的密码md5加盐,加盐后的md5为存储在数据库中的密码md5
     *
     * @param formPassword 表单中填充的明文密码
     * @param saltDb       这里的salt是在数据库查出来的,而并非第一次加盐的盐值
     * @return
     */
    public static String formPassToDbPass(String formPassword, String saltDb) {
        String str = "" + saltDb.charAt(0) + saltDb.charAt(2) + formPassword + saltDb.charAt(5) + saltDb.charAt(4);
        return md5(str);
    }

    /**
     * 两次MD5:储存在数据库中的密码的md5 = MD5(MD5(input_password) + SALT) + db_salt
     *
     * @param inputPassword 用户输入密码
     * @param saltDb        数据库中用户的salt
     * @return
     */
    public static String inputPassToDbPass(String inputPassword, String saltDb) {
        String formPass = inputPassToFormPass(inputPassword);
        String dbPass = formPassToDbPass(formPass, saltDb);
        return dbPass;
    }


    /**
     * 测试
     */
    @Test
    public void TestMD5() {
        System.out.println(inputPassToFormPass("000000"));
        System.out.println(inputPassToDbPass("000000", SALT));
    }

}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/util/UUIDUtil.java
================================================
package com.seckill.dis.common.util;

import java.util.UUID;


/**
 * UUID工具类用于生成session
 *
 * @author noodle
 */
public class UUIDUtil {
    public static String uuid() {
        return UUID.randomUUID().toString().replace("-", "");
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/util/ValidatorUtil.java
================================================
package com.seckill.dis.common.util;


import org.apache.commons.lang3.StringUtils;

import java.util.regex.Pattern;

/**
 * 手机号码格式校验工具
 *
 * @noodle
 */
public class ValidatorUtil {

    // 手机号正则
    private static final Pattern mobilePattern = Pattern.compile("1\\d{10}");

    /**
     * 正则表达式:验证手机号
     */
    public static final String REGEX_MOBILE = "^((13[0-9])|(14[5,7,9])|(15([0-3]|[5-9]))|(166)|(17[0,1,3,5,6,7,8])|(18[0-9])|(19[8|9]))\\d{8}$";;

    /**
     * 校验手机号
     *
     * @param mobile
     * @return 校验通过返回true,否则返回false
     */
    public static boolean isMobile(String mobile) {
        if (StringUtils.isEmpty(mobile))
            return false;

        return Pattern.matches(REGEX_MOBILE, mobile);
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/util/VerifyCodeUtil.java
================================================
package com.seckill.dis.common.util;

import com.seckill.dis.common.api.seckill.vo.VerifyCodeVo;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;

public class VerifyCodeUtil {

    /**
     * 用于生成验证码中的运算符
     */
    private static char[] ops = new char[]{'+', '-', '*'};

    /**
     * 创建验证码
     * @return
     */
    public static VerifyCodeVo createVerifyCode() {

        // 验证码的宽高
        int width = 80;
        int height = 32;

        //create the image
        BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB);
        Graphics g = image.getGraphics();
        // set the background color
        g.setColor(new Color(0xDCDCDC));
        g.fillRect(0, 0, width, height);
        // draw the border
        g.setColor(Color.black);
        g.drawRect(0, 0, width - 1, height - 1);
        // create a random instance to generate the codes
        Random rdm = new Random();
        // make some confusion
        for (int i = 0; i < 50; i++) {
            int x = rdm.nextInt(width);
            int y = rdm.nextInt(height);
            g.drawOval(x, y, 0, 0);
        }
        // generate a random code
        String verifyCode = generateVerifyCode(rdm);
        g.setColor(new Color(0, 100, 0));
        g.setFont(new Font("Candara", Font.BOLD, 24));
        g.drawString(verifyCode, 8, 24);
        g.dispose();

        // 计算表达式值,并把把验证码值存到redis中
        int expResult = calc(verifyCode);
        //输出图片和结果
        return new VerifyCodeVo(image, expResult);
    }

    /**
     * 使用 ScriptEngine 计算验证码中的数学表达式的值
     *
     * @param exp
     * @return
     */
    private static int calc(String exp) {
        try {
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("JavaScript");
            return (Integer) engine.eval(exp);// 表达式计算
        } catch (Exception e) {
            e.printStackTrace();
            return 0;
        }
    }

    /**
     * 生成验证码,只含有+/-/*
     * <p>
     * 随机生成三个数字,然后生成表达式
     *
     * @param rdm
     * @return 验证码中的数学表达式
     */
    private static String generateVerifyCode(Random rdm) {
        int num1 = rdm.nextInt(10);
        int num2 = rdm.nextInt(10);
        int num3 = rdm.nextInt(10);
        char op1 = ops[rdm.nextInt(3)];
        char op2 = ops[rdm.nextInt(3)];
        String exp = "" + num1 + op1 + num2 + op2 + num3;
        return exp;
    }
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/validator/IsMobile.java
================================================
package com.seckill.dis.common.validator;


import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.*;
import static java.lang.annotation.RetentionPolicy.RUNTIME;


/**
 * 手机号码的校验注解
 *
 * @author
 */
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = {IsMobileValidator.class}) // 这个注解的参数指定用于校验工作的是哪个类
public @interface IsMobile {

    /**
     * 默认手机号码不可为空
     *
     * @return
     */
    boolean required() default true;

    /**
     * 如果校验不通过时的提示信息
     *
     * @return
     */
    String message() default "手机号码格式有误!";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}


================================================
FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/validator/IsMobileValidator.java
================================================
package com.seckill.dis.common.validator;

import com.seckill.dis.common.util.ValidatorUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.StringUtils;

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

/**
 * 真正用户手机号码检验的工具,会被注解@isMobile所使用
 * 这个类需要实现javax.validation.ConstraintValidator,否则不能被@Constraint参数使用
 *
 * @author noodle
 */
public class IsMobileValidator implements ConstraintValidator<IsMobile, String> {

    private static Logger logger = LoggerFactory.getLogger(IsMobileValidator.class);

    /**
     * 用于获取检验字段是否可以为空
     */
    private boolean required = false;

    /**
     * 用于获取注解
     *
     * @param constraintAnnotation
     */
    @Override
    public void initialize(IsMobile constraintAnnotation) {
        required = constraintAnnotation.required();
    }

    /**
     * 用于检验字段是否合法
     *
     * @param value   待校验的字段
     * @param context
     * @return 字段检验结果
     */
    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        logger.info("是否需要校验参数:" + required);
        // 如果所检验字段可以为空
        if (required) {
            return ValidatorUtil.isMobile(value);
        } else {
            return StringUtils.isEmpty(value) || ValidatorUtil.isMobile(value);
        }
    }
}


================================================
FILE: dis-seckill-gateway/pom.xml
================================================
<?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">
    <parent>
        <artifactId>dis-seckill</artifactId>
        <groupId>com.seckill.dis</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>dis-seckill-gateway</artifactId>
    <packaging>jar</packaging>


    <dependencyManagement>
        <dependencies>
            <!-- Spring Boot -->
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <!-- Aapche Dubbo  -->
            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo-dependencies-bom</artifactId>
                <version>${dubbo.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>

            <dependency>
                <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>${dubbo.version}</version>
                <exclusions>
                    <exclusion>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>javax.servlet</groupId>
                        <artifactId>servlet-api</artifactId>
                    </exclusion>
                    <exclusion>
                        <groupId>log4j</groupId>
                        <artifactId>log4j</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    </dependencyManagement>


    <dependencies>

        <dependency>
            <groupId>com.seckill.dis</groupId>
            <artifactId>dis-seckill-common</artifactId>
            <version>1.0.0</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-configuration-processor</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
        </dependency>

        <!-- Dubbo -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-spring-boot-starter</artifactId>
            <version>${dubbo.version}</version>
        </dependency>

        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo</artifactId>
        </dependency>

        <!-- Zookeeper dependencies -->
        <dependency>
            <groupId>org.apache.dubbo</groupId>
            <artifactId>dubbo-dependencies-zookeeper</artifactId>
            <version>${dubbo.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>org.slf4j</groupId>
                    <artifactId>slf4j-log4j12</artifactId>
                </exclusion>
            </exclusions>
            <type>pom</type>
        </dependency>

        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>

        <!--rabbitmq依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
        </dependency>

    </dependencies>


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

================================================
FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/DisSeckillServletInitializer.java
================================================
package com.seckill.dis.gateway;

import org.springframework.boot.builder.SpringApplicationBuilder;
import org.springframework.boot.web.servlet.support.SpringBootServletInitializer;

/**
 * web app 程序启动器
 *
 * @author noodle
 * @date 2019/7/21 11:26
 */
public class DisSeckillServletInitializer extends SpringBootServletInitializer {
    @Override
    protected SpringApplicationBuilder configure(SpringApplicationBuilder builder) {
        return builder.sources(GatewayApplication.class);
    }
}


================================================
FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/GatewayApplication.java
================================================
package com.seckill.dis.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;


/**
 * 网关服务
 *
 * @author noodle
 */
@SpringBootApplication
public class GatewayApplication {

    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}


================================================
FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/config/WebConfig.java
================================================
package com.seckill.dis.gateway.config;


import com.seckill.dis.gateway.config.access.AccessInterceptor;
import com.seckill.dis.gateway.config.resolver.UserArgumentResolver;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
 * 自定义web配置
 *
 * @author noodle
 */
@Configuration
public class WebConfig implements WebMvcConfigurer {

    private static Logger logger = LoggerFactory.getLogger(WebConfig.class);

    @Autowired
    UserArgumentResolver userArgumentResolver;

    @Autowired
    AccessInterceptor accessInterceptor;

    /**
     * 添加自定义的参数解析器到MVC配置中
     *
     * @param argumentResolvers
     */
    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
        logger.info("添加自定义的参数解析器");
        // 添加自定义的参数解析器
        argumentResolvers.add(userArgumentResolver);
    }

    /**
     * 添加自定义方法拦截器到MVC配置中
     *
     * @param registry
     */
    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        logger.info("添加请求拦截器");
        registry.addInterceptor(accessInterceptor);
    }
}


================================================
FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/config/access/AccessInterceptor.java
================================================
package com.seckill.dis.gateway.config.access;

import com.alibaba.fastjson.JSON;
import com.seckill.dis.common.api.cache.RedisServiceApi;
import com.seckill.dis.common.api.cache.vo.AccessKeyPrefix;
import com.seckill.dis.common.api.cache.vo.SkUserKeyPrefix;
import com.seckill.dis.common.api.user.UserServiceApi;
import com.seckill.dis.common.api.user.vo.UserVo;
import com.seckill.dis.common.result.CodeMsg;
import com.seckill.dis.common.result.Result;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.OutputStream;

/**
 * 用户访问拦截器
 *
 * @author noodle
 */
@Service
public class AccessInterceptor extends HandlerInterceptorAdapter {

    private static Logger logger = LoggerFactory.getLogger(AccessInterceptor.class);

    @Reference(interfaceClass = UserServiceApi.class)
    UserServiceApi userService;

    @Reference(interfaceClass = RedisServiceApi.class)
    RedisServiceApi redisService;

    /**
     * 目标方法执行前的处理
     * <p>
     * 查询访问次数,进行防刷请求拦截
     * 在 AccessLimit#seconds() 时间内频繁访问会有次数限制
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        logger.info(request.getRequestURL() + " 拦截请求");

        // 指明拦截的是方法
        if (handler instanceof HandlerMethod) {
            logger.info("HandlerMethod: " + ((HandlerMethod) handler).getMethod().getName());
            // 获取用户对象
            UserVo user = this.getUser(request, response);
            // 保存用户到ThreadLocal,这样,同一个线程访问的是同一个用户
            UserContext.setUser(user);

            // 获取标注了 @AccessLimit 的方法,没有注解,则直接返回
            HandlerMethod hm = (HandlerMethod) handler;
            AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);

            // 如果没有添加@AccessLimit注解,直接放行(true)
            if (accessLimit == null)
                return true;

            // 获取注解的元素值
            int seconds = accessLimit.seconds();
            int maxCount = accessLimit.maxAccessCount();
            boolean needLogin = accessLimit.needLogin();

            String key = request.getRequestURI();
            if (needLogin) {
                if (user == null) {
                    this.render(response, CodeMsg.SESSION_ERROR);
                    return false;
                }
                key += "_" + user.getPhone();
            } else {
                //do nothing
            }
            // 设置缓存过期时间
            AccessKeyPrefix accessKeyPrefix = AccessKeyPrefix.withExpire(seconds);
            // 在redis中存储的访问次数的key为请求的URI
            Integer count = redisService.get(accessKeyPrefix, key, Integer.class);
            // 第一次重复点击 秒杀按钮
            if (count == null) {
                redisService.set(accessKeyPrefix, key, 1);
                // 点击次数未达最大值
            } else if (count < maxCount) {
                redisService.incr(accessKeyPrefix, key);
                // 点击次数已满
            } else {
                this.render(response, CodeMsg.ACCESS_LIMIT_REACHED);
                return false;
            }
        }
        // 不是方法直接放行
        return true;
    }

    /**
     * 渲染返回信息
     * 以 json 格式返回
     *
     * @param response
     * @param cm
     * @throws Exception
     */
    private void render(HttpServletResponse response, CodeMsg cm) throws Exception {
        response.setContentType("application/json;charset=UTF-8");
        OutputStream out = response.getOutputStream();
        String str = JSON.toJSONString(Result.error(cm));
        out.write(str.getBytes("UTF-8"));
        out.flush();
        out.close();
    }

    /**
     * 和 UserArgumentResolver 功能类似,用于解析拦截的请求,获取 UserVo 对象
     *
     * @param request
     * @param response
     * @return UserVo 对象
     */
    private UserVo getUser(HttpServletRequest request, HttpServletResponse response) {
        logger.info(request.getRequestURL() + " 获取 UserVo 对象");

        // 从请求中获取token
        String paramToken = request.getParameter(UserServiceApi.COOKIE_NAME_TOKEN);
        String cookieToken = getCookieValue(request, UserServiceApi.COOKIE_NAME_TOKEN);

        if (StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
            return null;
        }

        String token = StringUtils.isEmpty(paramToken) ? cookieToken : paramToken;

        if (StringUtils.isEmpty(token)) {
            return null;
        }

        UserVo userVo = redisService.get(SkUserKeyPrefix.TOKEN, token, UserVo.class);

        // 在有效期内从redis获取到key之后,需要将key重新设置一下,从而达到延长有效期的效果
        if (userVo != null) {
            addCookie(response, token, userVo);
        }
        return userVo;
    }

    /**
     * 从众多的cookie中找出指定cookiName的cookie
     *
     * @param request
     * @param cookieName
     * @return cookiName对应的value
     */
    private String getCookieValue(HttpServletRequest request, String cookieName) {
        Cookie[] cookies = request.getCookies();
        if (cookies == null || cookies.length == 0)
            return null;

        for (Cookie cookie : cookies) {
            if (cookie.getName().equals(cookieName)) {
                return cookie.getValue();
            }
        }
        return null;
    }

    /**
     * 将cookie存入redis,并将cookie写入到请求的响应中
     *
     * @param response
     * @param token
     * @param user
     */
    private void addCookie(HttpServletResponse response, String token, UserVo user) {

        redisService.set(SkUserKeyPrefix.TOKEN, token, user);

        Cookie cookie = new Cookie(UserServiceApi.COOKIE_NAME_TOKEN, token);
        // 客户端cookie的有限期和缓存中的cookie有效期一致
        cookie.setMaxAge(SkUserKeyPrefix.TOKEN.expireSeconds());
        cookie.setPath("/");
        response.addCookie(cookie);
    }
}


================================================
FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/config/access/AccessLimit.java
================================================
package com.seckill.dis.gateway.config.access;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;


/**
 * 用户访问拦截的注解
 * 主要用于防止刷功能的实现
 *
 * @author noodle
 */
@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {

    /**
     * 两次请求的最大有效时间间隔,即视两次请求为同一状态的时间间隔
     *
     * @return
     */
    int seconds();

    /**
     * 最大请求次数
     *
     * @return
     */
    int maxAccessCount();

    /**
     * 是否需要重新登录
     *
     * @return
     */
    boolean needLogin() default true;
}


================================================
FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/config/access/UserContext.java
================================================
package com.seckill.dis.gateway.config.access;

import com.seckill.dis.common.api.user.vo.UserVo;

/**
 * 用于保存用户
 * 使用ThreadLocal保存用户,因为ThreadLocal是线程安全的,使用ThreadLocal可以保存当前线程持有的对象
 * 每个用户的请求对应一个线程,所以使用ThreadLocal以线程为键保存用户是合适的
 *
 * @author noodle
 */
public class UserContext {

    // 保存用户的容器
    private static ThreadLocal<UserVo> userHolder = new ThreadLocal<>();

    public static void setUser(UserVo user) {

        userHolder.set(user);
    }

    public static UserVo getUser() {
        return userHolder.get();
    }
}


================================================
FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/config/resolver/UserArgumentResolver.java
================================================
package com.seckill.dis.gateway.config.resolver;

import com.seckill.dis.common.api.cache.RedisServiceApi;
import com.seckill.dis.common.api.cache.vo.SkUserKeyPrefix;
import com.seckill.dis.common.api.user.UserServiceApi;
import com.seckill.dis.common.api.user.vo.UserVo;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Service;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

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

/**
 * 解析请求,并将请求的参数设置到方法参数中
 *
 * @author noodle
 */
@Service
public class UserArgumentResolver implements HandlerMethodArgumentResolver {

    private static Logger logger = LoggerFactory.getLogger(UserArgumentResolver.class);

    /**
     * 由于需要将一个cookie对应的用户存入第三方缓存中,这里用redis,所以需要引入redis service
     */
    @Reference(interfaceClass = RedisServiceApi.class)
    RedisServiceApi redisService;

    /**
     * 当请求参数为 UserVo 时,使用这个解析器处理
     * 客户端的请求到达某个 Controller 的方法时,判断这个方法的参数是否为 UserVo,
     * 如果是,则这个 UserVo 参数对象通过下面的 resolveArgument() 方法获取,
     * 然后,该 Controller 方法继续往下执行时所看到的 UserVo 对象就是在这里的 resolveArgument() 方法处理过的对象
     *
     * @param methodParameter
     * @return
     */
    @Override
    public boolean supportsParameter(MethodParameter methodParameter) {
        logger.info("supportsParameter");
        Class<?> parameterType = methodParameter.getParameterType();
        return parameterType == UserVo.class;
    }

    /**
     * 从分布式 session 中获取 UserVo 对象
     *
     * @param methodParameter
     * @param modelAndViewContainer
     * @param nativeWebRequest
     * @param webDataBinderFactory
     * @return
     * @throws Exception
     */
    @Override
    public Object resolveArgument(MethodParameter methodParameter,
                                  ModelAndViewContainer modelAndViewContainer,
                                  NativeWebRequest nativeWebRequest,
                                  WebDataBinderFactory webDataBinderFactory) throws Exception {
        // 获取请求和响应对象
        HttpServletRequest request = nativeWebRequest.getNativeRequest(HttpServletRequest.class);
        HttpServletResponse response = nativeWebRequest.getNativeResponse(HttpServletResponse.class);
        logger.info(request.getRequestURL() + " resolveArgument");

        // 从请求对象中获取token(token可能有两种方式从客户端返回,1:通过url的参数,2:通过set-Cookie字段)
        String paramToken = request.getParameter(UserServiceApi.COOKIE_NAME_TOKEN);
        String cookieToken = getCookieValue(request, UserServiceApi.COOKIE_NAME_TOKEN);

        if (StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
            return null;
        }

        // 判断是哪种方式返回的token,并由该种方式获取token(cookie)
        String token = StringUtils.isEmpty(paramToken) ? cookieToken : paramToken;
        if (StringUtils.isEmpty(token)) {
            return null;
        }

        // 通过token就可以在redis中查出该token对应的用户对象
        UserVo userVo = redisService.get(SkUserKeyPrefix.TOKEN, token, UserVo.class);
        logger.info("获取userVo:" + userVo.toString());

        // 在有效期内从redis获取到key之后,需要将key重新设置一下,从而达到延长有效期的效果
        if (userVo != null) {
            addCookie(response, token, userVo);
        }
        return userVo;
    }

    /**
     * 根据cookie名获取相应的cookie值
     *
     * @param request
     * @param cookieName
     * @return
     */
    private String getCookieValue(HttpServletRequest request, String cookieName) {
        logger.info("getCookieValue");
        Cookie[] cookies = request.getCookies();
        // null判断,否则并发时会发生异常
        if (cookies == null || cookies.length == 0) {
            logger.info("cookies is null");
            return null;
        }

        for (Cookie cookie : cookies) {
            if (cookie.getName().equals(cookieName)) {
                return cookie.getValue();
            }
        }
        return null;
    }

    /**
     * 将cookie存入redis,并将cookie写入到请求的响应中
     *
     * @param response
     * @param token
     * @param user
     */
    private void addCookie(HttpServletResponse response, String token, UserVo user) {

        redisService.set(SkUserKeyPrefix.TOKEN, token, user);

        Cookie cookie = new Cookie(UserServiceApi.COOKIE_NAME_TOKEN, token);
        cookie.setMaxAge(SkUserKeyPrefix.TOKEN.expireSeconds());
        cookie.setPath("/");
        response.addCookie(cookie);
    }
}


================================================
FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/exception/GlobalException.java
================================================
package com.seckill.dis.gateway.exception;

import com.seckill.dis.common.result.CodeMsg;

/**
 * 全局异常处理器
 *
 * @author noodle
 */
public class GlobalException extends RuntimeException {

    private CodeMsg codeMsg;

    /**
     * 使用构造器接收CodeMsg
     *
     * @param codeMsg
     */
    public GlobalException(CodeMsg codeMsg) {
        this.codeMsg = codeMsg;
    }

    public CodeMsg getCodeMsg() {
        return codeMsg;
    }
}


================================================
FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/exception/GlobalExceptionHandler.java
================================================
package com.seckill.dis.gateway.exception;

import com.seckill.dis.common.result.CodeMsg;
import com.seckill.dis.common.result.Result;
import com.seckill.dis.gateway.user.UserController;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.validation.BindException;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.HttpServletRequest;
import java.util.List;

/**
 * 全局异常处理器(底层使用方法拦截的方式完成,和AOP一样)
 * 在异常发生时,将会调用这里面的方法给客户端一个响应
 *
 * @author noodle
 */

@ControllerAdvice // 通过Advice可知,这个处理器实际上是一个切面
@ResponseBody
public class GlobalExceptionHandler {

    private static Logger logger = LoggerFactory.getLogger(GlobalExceptionHandler.class);

    /**
     * 异常处理
     *
     * @param request 绑定了出现异常的请求信息
     * @param e       该请求所产生的异常
     * @return 向客户端返回的结果(这里为json数据)
     */
    @ExceptionHandler(value = Exception.class)// 这个注解用指定这个方法对何种异常处理(这里默认所有异常都用这个方法处理)
    public Result<String> exceptionHandler(HttpServletRequest request, Exception e) {
        logger.info("出现异常");
        e.printStackTrace();// 打印原始的异常信息,方便调试

        // 如果所拦截的异常是自定义的全局异常,这按自定义异常的处理方式处理,否则按默认方式处理
        if (e instanceof GlobalException) {
            GlobalException exception = (GlobalException) e;
            // 向客户端返回异常信息
            return Result.error(exception.getCodeMsg());
        } else if (e instanceof BindException) {
            BindException bindException = (BindException) e;
            List<ObjectError> errors = bindException.getAllErrors();
            // 这里只获取了第一个错误对象
            ObjectError error = errors.get(0);
            // 获取其中的信息
            String message = error.getDefaultMessage();
            // 将错误信息动态地拼接到已定义的部分信息上
            return Result.error(CodeMsg.BIND_ERROR.fillArgs(message));
        } else {
            return Result.error(CodeMsg.SERVER_ERROR);
        }
    }
}


================================================
FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/goods/GoodsController.java
================================================
package com.seckill.dis.gateway.goods;

import com.seckill.dis.common.api.cache.RedisServiceApi;
import com.seckill.dis.common.api.cache.vo.GoodsKeyPrefix;
import com.seckill.dis.common.api.goods.GoodsServiceApi;
import com.seckill.dis.common.api.goods.vo.GoodsDetailVo;
import com.seckill.dis.common.api.goods.vo.GoodsVo;
import com.seckill.dis.common.api.user.vo.UserVo;
import com.seckill.dis.common.result.Result;
import com.seckill.dis.gateway.user.UserController;
import org.apache.commons.lang3.StringUtils;
import org.apache.dubbo.config.annotation.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.spring5.view.ThymeleafViewResolver;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;

/**
 * 商品模块接口
 *
 * @author noodle
 */

@Controller
@RequestMapping("/goods/")
public class GoodsController {

    private static Logger logger = LoggerFactory.getLogger(UserController.class);

    @Reference(interfaceClass = RedisServiceApi.class)
    RedisServiceApi redisService;

    @Reference(interfaceClass = GoodsServiceApi.class)
    GoodsServiceApi goodsService;

    /**
     * 因为在redis缓存中不存页面缓存时需要手动渲染,所以注入一个视图解析器,自定义渲染
     */
    @Autowired
    ThymeleafViewResolver thymeleafViewResolver;

    /**
     * 获取 SKUser 对象,并将其传递到页面解析器
     * 从数据库中获取商品信息(包含秒杀信息)
     * <p>
     * QPS: 1267, 用户数目5000,每个用户发起10次请求,共5000*10次请求
     * <p>
     * 页面级缓存实现;从redis中取页面,如果没有则需要手动渲染页面,并且将渲染的页面存储在redis中供下一次访问时获取
     *
     * @param request
     * @param response
     * @param model    响应的资源文件
     * @param user     通过自定义参数解析器UserArgumentResolver解析的 SKUser 对象
     * @return
     */
    @RequestMapping(value = "goodsList", produces = "text/html")// produces表明:这个请求会返回text/html媒体类型的数据
    @ResponseBody
    public String goodsList(HttpServletRequest request,
                            HttpServletResponse response,
                            Model model,
                            UserVo user) {

        logger.info("获取商品列表");

        // 1. 从redis缓存中取html
        String html = redisService.get(GoodsKeyPrefix.GOODS_LIST_HTML, "", String.class);
        if (!StringUtils.isEmpty(html))
            return html;

        // 2. 如果redis中不存在该缓存,则需要手动渲染
        // 查询商品列表,用于手动渲染时将商品数据填充到页面
        List<GoodsVo> goodsVoList = goodsService.listGoodsVo();
        model.addAttribute("goodsList", goodsVoList);
        model.addAttribute("user", user);

        // 3. 渲染html
        WebContext webContext = new WebContext(request, response, request.getServletContext(), request.getLocale(), model.asMap());
        // (第一个参数为渲染的html文件名,第二个为web上下文:里面封装了web应用的上下文)
        html = thymeleafViewResolver.getTemplateEngine().process("goods_list", webContext);

        if (!StringUtils.isEmpty(html)) // 如果html文件不为空,则将页面缓存在redis中
            redisService.set(GoodsKeyPrefix.GOODS_LIST_HTML, "", html);

        return html;
    }

    /**
     * 处理商品详情页(页面静态化处理, 直接将数据返回给客户端,交给客户端处理)
     * <p>
     * URL级缓存实现;从redis中取商品详情页面,如果没有则需要手动渲染页面,并且将渲染的页面存储在redis中供下一次访问时获取
     * 实际上URL级缓存和页面级缓存是一样的,只不过URL级缓存会根据url的参数从redis中取不同的数据
     *
     * @param user
     * @param goodsId
     * @return
     */
    @RequestMapping(value = "getDetails/{goodsId}")
    @ResponseBody
    public Result<GoodsDetailVo> getDetails(UserVo user, @PathVariable("goodsId") long goodsId) {
        logger.info("获取商品详情");

        // 通过商品id在数据库查询
        GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);

        // 获取商品的秒杀开始与结束的时间
        long startDate = goods.getStartDate().getTime();
        long endDate = goods.getEndDate().getTime();
        long now = System.currentTimeMillis();

        // 秒杀状态; 0: 秒杀未开始,1: 秒杀进行中,2: 秒杀已结束
        int skStatus = 0;
        // 秒杀剩余时间
        int remainSeconds = 0;

        if (now < startDate) { // 秒杀未开始
            skStatus = 0;
            remainSeconds = (int) ((startDate - now) / 1000);
        } else if (now > endDate) { // 秒杀已结束
            skStatus = 2;
            remainSeconds = -1;
        } else { // 秒杀进行中
            skStatus = 1;
            remainSeconds = 0;
        }

        // 服务端封装商品数据直接传递给客户端,而不用渲染页面
        GoodsDetailVo goodsDetailVo = new GoodsDetailVo();
        goodsDetailVo.setGoods(goods);
        goodsDetailVo.setUser(user);
        goodsDetailVo.setRemainSeconds(remainSeconds);
        goodsDetailVo.setSeckillStatus(skStatus);

        return Result.success(goodsDetailVo);
    }
}


================================================
FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/order/OrderController.java
================================================
package com.seckill.dis.gateway.order;

import com.seckill.dis.common.api.goods.GoodsServiceApi;
import com.seckill.dis.common.api.goods.vo.GoodsVo;
import com.seckill.dis.common.api.order.OrderServiceApi;
import com.seckill.dis.common.api.order.vo.OrderDetailVo;
import com.seckill.dis.common.api.user.vo.UserVo;
import com.seckill.dis.common.domain.OrderInfo;
import com.seckill.dis.common.result.CodeMsg;
import com.seckill.dis.common.result.Result;
import org.apache.dubbo.config.annotation.Reference;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

/**
 * 订单服务接口
 *
 * @author noodle
 */
@Controller
@RequestMapping("/order/")
public class OrderController {

    @Reference(interfaceClass = OrderServiceApi.class)
    OrderServiceApi orderService;

    @Reference(interfaceClass = GoodsServiceApi.class)
    GoodsServiceApi goodsService;

    /**
     * 获取订单详情
     *
     * @param model
     * @param user
     * @param orderId
     * @return
     */
    @RequestMapping("detail")
    @ResponseBody
    public Result<OrderDetailVo> orderInfo(Model model,
                                           UserVo user,
                                           @RequestParam("orderId") long orderId) {
        if (user == null) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }

        // 获取订单信息
        OrderInfo order = orderService.getOrderById(orderId);
        if (order == null) {
            return Result.error(CodeMsg.ORDER_NOT_EXIST);
        }

        // 如果订单存在,则根据订单信息获取商品信息
        long goodsId = order.getGoodsId();
        GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
        OrderDetailVo vo = new OrderDetailVo();
        vo.setUser(user);// 设置用户信息
        vo.setOrder(order); // 设置订单信息
        vo.setGoods(goods); // 设置商品信息
        return Result.success(vo);
    }
}


================================================
FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/seckill/SeckillController.java
================================================
package com.seckill.dis.gateway.seckill;

import com.seckill.dis.common.api.cache.RedisServiceApi;
import com.seckill.dis.common.api.cache.vo.GoodsKeyPrefix;
import com.seckill.dis.common.api.cache.vo.OrderKeyPrefix;
import com.seckill.dis.common.api.cache.vo.SkKeyPrefix;
import com.seckill.dis.common.api.goods.GoodsServiceApi;
import com.seckill.dis.common.api.goods.vo.GoodsVo;
import com.seckill.dis.common.api.mq.MqProviderApi;
import com.seckill.dis.common.api.mq.vo.SkMessage;
import com.seckill.dis.common.api.order.OrderServiceApi;
import com.seckill.dis.common.api.seckill.SeckillServiceApi;
import com.seckill.dis.common.api.seckill.vo.VerifyCodeVo;
import com.seckill.dis.common.api.user.vo.UserVo;
import com.seckill.dis.common.domain.SeckillOrder;
import com.seckill.dis.common.result.CodeMsg;
import com.seckill.dis.common.result.Result;
import com.seckill.dis.common.util.MD5Util;
import com.seckill.dis.common.util.UUIDUtil;
import com.seckill.dis.common.util.VerifyCodeUtil;
import com.seckill.dis.gateway.config.access.AccessLimit;
import org.apache.dubbo.config.annotation.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;

import javax.imageio.ImageIO;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.List;
import java.util.Map;


/**
 * 秒杀接口
 *
 * @author noodle
 */
@Controller
@RequestMapping("/seckill/")
public class SeckillController implements InitializingBean {

    private static Logger logger = LoggerFactory.getLogger(SeckillController.class);

    @Reference(interfaceClass = RedisServiceApi.class)
    RedisServiceApi redisService;

    @Reference(interfaceClass = GoodsServiceApi.class)
    GoodsServiceApi goodsService;

    @Reference(interfaceClass = SeckillServiceApi.class)
    SeckillServiceApi seckillService;

    @Reference(interfaceClass = OrderServiceApi.class)
    OrderServiceApi orderService;

    @Reference(interfaceClass = MqProviderApi.class)
    MqProviderApi sender;

    /**
     * 用于内存标记,标记库存是否为空,从而减少对redis的访问
     */
    private Map<Long, Boolean> localOverMap = new HashMap<>();

    /**
     * 获取秒杀接口地址
     * 1. 每一次点击秒杀,都会生成一个随机的秒杀地址返回给客户端
     * 2. 对秒杀的次数做限制(通过自定义拦截器注解完成)
     *
     * @param model
     * @param user
     * @param goodsId    秒杀的商品id
     * @param verifyCode 验证码
     * @return 被隐藏的秒杀接口路径
     */
    @AccessLimit(seconds = 5, maxAccessCount = 5, needLogin = true)
    @RequestMapping(value = "path", method = RequestMethod.GET)
    @ResponseBody
    public Result<String> getSeckillPath(Model model, UserVo user,
                                         @RequestParam("goodsId") long goodsId,
                                         @RequestParam(value = "verifyCode", defaultValue = "0") int verifyCode) {

        /** 在执行下面的逻辑之前,会先对path请求进行拦截处理(@AccessLimit, AccessInterceptor),防止访问次数过于频繁,对服务器造成过大的压力 */

        model.addAttribute("user", user);

        if (goodsId <= 0) {
            return Result.error(CodeMsg.SECKILL_PARM_ILLEGAL.fillArgs("商品id小于0"));
        }

        // 校验验证码
        boolean check = this.checkVerifyCode(user, goodsId, verifyCode);

        if (!check)
            return Result.error(CodeMsg.VERITF_FAIL);// 检验不通过,请求非法

        // 检验通过,获取秒杀路径
        String path = this.createSkPath(user, goodsId);

        // 向客户端回传随机生成的秒杀地址
        return Result.success(path);
    }

    /**
     * 秒杀逻辑(页面静态化分离,不需要直接将页面返回给客户端,而是返回客户端需要的页面动态数据,返回数据时json格式)
     * <p>
     * QPS:1306
     * 5000 * 10
     * <p>
     * GET/POST的@RequestMapping是有区别的
     * <p>
     * 通过随机的path,客户端隐藏秒杀接口
     * <p>
     * 优化: 不同于每次都去数据库中读取秒杀订单信息,而是在第一次生成秒杀订单成功后,
     * 将订单存储在redis中,再次读取订单信息的时候就直接从redis中读取
     *
     * @param model
     * @param user
     * @param goodsId
     * @param path    隐藏的秒杀地址,为客户端回传的path,最初也是有服务端产生的
     * @return 订单详情或错误码
     */
    // {path}为客户端回传的path,最初也是有服务端产生的
    @RequestMapping(value = "{path}/doSeckill", method = RequestMethod.POST)
    @ResponseBody
    public Result<Integer> doSeckill(Model model, UserVo user,
                                     @RequestParam("goodsId") long goodsId,
                                     @PathVariable("path") String path) {

        model.addAttribute("user", user);

        // 验证path是否正确
        boolean check = this.checkPath(user, goodsId, path);

        if (!check)
            return Result.error(CodeMsg.REQUEST_ILLEGAL);// 请求非法

        // 通过内存标记,减少对redis的访问,秒杀未结束才继续访问redis
        Boolean over = localOverMap.get(goodsId);
        if (over)
            return Result.error(CodeMsg.SECKILL_OVER);

        // 预减库存,同时在库存为0时标记该商品已经结束秒杀
        Long stock = redisService.decr(GoodsKeyPrefix.GOODS_STOCK, "" + goodsId);
        if (stock < 0) {
            localOverMap.put(goodsId, true);// 秒杀结束。标记该商品已经秒杀结束
            return Result.error(CodeMsg.SECKILL_OVER);
        }

        // 判断是否重复秒杀
        // 从redis中取缓存,减少数据库的访问
        SeckillOrder order = redisService.get(OrderKeyPrefix.SK_ORDER, ":" + user.getUuid() + "_" + goodsId, SeckillOrder.class);
        // 如果缓存中不存该数据,则从数据库中取
        if (order == null) {
            order = orderService.getSeckillOrderByUserIdAndGoodsId(user.getUuid(), goodsId);
        }

        if (order != null) {
            return Result.error(CodeMsg.REPEATE_SECKILL);
        }

        // 商品有库存且用户为秒杀商品,则将秒杀请求放入MQ
        SkMessage message = new SkMessage();
        message.setUser(user);
        message.setGoodsId(goodsId);

        // 放入MQ(对秒杀请求异步处理,直接返回)
        sender.sendSkMessage(message);

        // 排队中
        return Result.success(0);
    }

    /**
     * 用于返回用户秒杀的结果
     *
     * @param model
     * @param user
     * @param goodsId
     * @return orderId:成功, -1:秒杀失败, 0: 排队中
     */
    @RequestMapping(value = "result", method = RequestMethod.GET)
    @ResponseBody
    public Result<Long> getSeckillResult(Model model,
                                         UserVo user,
                                         @RequestParam("goodsId") long goodsId) {

        model.addAttribute("user", user);

        long result = seckillService.getSeckillResult(user.getUuid(), goodsId);
        return Result.success(result);
    }

    /**
     * goods_detail.htm: $("#verifyCodeImg").attr("src", "/seckill/verifyCode?goodsId=" + $("#goodsId").val());
     * 使用HttpServletResponse的输出流返回客户端异步获取的验证码(异步获取的代码如上所示)
     *
     * @param response
     * @param user
     * @param goodsId
     * @return
     */
    @RequestMapping(value = "verifyCode", method = RequestMethod.GET)
    @ResponseBody
    public Result<String> getVerifyCode(HttpServletResponse response, UserVo user,
                                        @RequestParam("goodsId") long goodsId) {
        logger.info("获取验证码");
        if (user == null || goodsId <= 0) {
            return Result.error(CodeMsg.SESSION_ERROR);
        }

        // 刷新验证码的时候置缓存中的随机地址无效
        String path = redisService.get(SkKeyPrefix.SK_PATH, "" + user.getUuid() + "_" + goodsId, String.class);
        if (path != null)
            redisService.delete(SkKeyPrefix.SK_PATH, "" + user.getUuid() + "_" + goodsId);

        // 创建验证码
        try {
            // String verifyCodeJsonString = seckillService.createVerifyCode(user, goodsId);
            VerifyCodeVo verifyCode = VerifyCodeUtil.createVerifyCode();

            // 验证码结果预先存到redis中
            redisService.set(SkKeyPrefix.VERIFY_RESULT, user.getUuid() + "_" + goodsId, verifyCode.getExpResult());
            ServletOutputStream out = response.getOutputStream();
            // 将图片写入到resp对象中
            ImageIO.write(verifyCode.getImage(), "JPEG", out);
            out.close();
            out.flush();

            return null;

        } catch (Exception e) {
            e.printStackTrace();
            return Result.error(CodeMsg.SECKILL_FAIL);
        }
    }

    /**
     * 检验检验码的计算结果
     *
     * @param user
     * @param goodsId
     * @param verifyCode
     * @return
     */
    private boolean checkVerifyCode(UserVo user, long goodsId, int verifyCode) {
        if (user == null || goodsId <= 0) {
            return false;
        }

        // 从redis中获取验证码计算结果
        Integer oldCode = redisService.get(SkKeyPrefix.VERIFY_RESULT, user.getUuid() + "_" + goodsId, Integer.class);
        if (oldCode == null || oldCode - verifyCode != 0) {// !!!!!!
            return false;
        }

        // 如果校验成功,则说明校验码过期,删除校验码缓存,防止重复提交同一个验证码
        redisService.delete(SkKeyPrefix.VERIFY_RESULT, user.getUuid() + "_" + goodsId);
        return true;
    }

    /**
     * 创建秒杀地址, 并将其存储在redis中
     *
     * @param user
     * @param goodsId
     * @return
     */
    public String createSkPath(UserVo user, long goodsId) {

        if (user == null || goodsId <= 0) {
            return null;
        }

        // 随机生成秒杀地址
        String path = MD5Util.md5(UUIDUtil.uuid() + "123456");
        // 将随机生成的秒杀地址存储在redis中(保证不同的用户和不同商品的秒杀地址是不一样的)
        redisService.set(SkKeyPrefix.SK_PATH, "" + user.getUuid() + "_" + goodsId, path);
        return path;
    }

    /**
     * 验证路径是否正确
     *
     * @param user
     * @param goodsId
     * @param path
     * @return
     */
    public boolean checkPath(UserVo user, long goodsId, String path) {
        if (user == null || path == null)
            return false;
        // 从redis中读取出秒杀的path变量是否为本次秒杀操作执行前写入redis中的path
        String oldPath = redisService.get(SkKeyPrefix.SK_PATH, "" + user.getUuid() + "_" + goodsId, String.class);

        return path.equals(oldPath);
    }

    /**
     * 服务器程序启动的时候加载商品列表信息
     */
    @Override
    public void afterPropertiesSet() {

        List<GoodsVo> goods = goodsService.listGoodsVo();
        if (goods == null) {
            return;
        }

        // 将商品的库存信息存储在redis中
        for (GoodsVo good : goods) {
            redisService.set(GoodsKeyPrefix.GOODS_STOCK, "" + good.getId(), good.getStockCount());
            // 在系统启动时,标记库存不为空
            localOverMap.put(good.getId(), false);
        }
    }
}


================================================
FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/user/UserController.java
================================================
package com.seckill.dis.gateway.user;

import com.seckill.dis.common.api.cache.vo.SkUserKeyPrefix;
import com.seckill.dis.common.api.user.UserServiceApi;
import com.seckill.dis.common.api.user.vo.LoginVo;
import com.seckill.dis.common.api.user.vo.RegisterVo;
import com.seckill.dis.common.api.user.vo.UserVo;
import com.seckill.dis.common.result.CodeMsg;
import com.seckill.dis.common.result.Result;
import com.seckill.dis.common.util.MD5Util;
import com.seckill.dis.gateway.exception.GlobalException;
import org.apache.dubbo.config.annotation.Reference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.ResponseBody;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
import javax.validation.Valid;

/**
 * 用户接口
 *
 * @author noodle
 */
@Controller
@RequestMapping("/user/")
public class UserController {
    /**
     * 日志记录:Logger是由slf4j接口规范创建的,对象有具体的实现类创建
     */
    private static Logger logger = LoggerFactory.getLogger(UserController.class);

    @Reference(interfaceClass = UserServiceApi.class)
    UserServiceApi userService;

    /**
     * 由于需要将一个cookie对应的用户存入第三方缓存中,这里用redis,所以需要引入redis serice
     */
//    @Reference(interfaceClass = RedisServiceApi.class)
//    RedisServiceApi redisService;

    /**
     * 首页
     *
     * @return
     */
    @RequestMapping(value = "index", method = RequestMethod.GET)
    public String index() {
        logger.info("首页接口");
        return "login";// login页面
    }

    /**
     * 用户登录接口
     *
     * @param response 响应
     * @param loginVo  用户登录请求的表单数据(将表单数据封装为了一个Vo:Value Object)
     *                 注解@Valid用于校验表单参数,校验成功才会继续执行业务逻辑,否则,
     *                 请求参数校验不成功抛出异常
     * @return
     */
    @RequestMapping(value = "login", method = RequestMethod.POST)
    @ResponseBody
    public Result<Boolean> login(HttpServletResponse response, @Valid LoginVo loginVo) {

        String token = userService.login(loginVo);
        logger.info("token: " + token);

        // 将token写入cookie中, 然后传给客户端(一个cookie对应一个用户,这里将这个cookie的用户信息写入redis中)
        Cookie cookie = new Cookie(UserServiceApi.COOKIE_NAME_TOKEN, token);
        cookie.setMaxAge(SkUserKeyPrefix.TOKEN.expireSeconds());// 保持与redis中的session一致
        cookie.setPath("/");
        response.addCookie(cookie);
        // 返回登陆成功
        return Result.success(true);
    }


    /**
     * 注册跳转
     *
     * @return
     */
    @RequestMapping(value = "doRegister", method = RequestMethod.GET)
    public String doRegister() {
        logger.info("doRegister()");
        return "register";
    }


    /**
     * 注册接口
     *
     * @param registerVo
     * @return
     */
    @RequestMapping(value = "register", method = RequestMethod.POST)
    @ResponseBody
    public Result<Boolean> register(RegisterVo registerVo) {
        logger.info("RegisterVo = " + registerVo);

        if (registerVo == null) {
            throw new GlobalException(CodeMsg.FILL_REGISTER_INFO);
        }

        CodeMsg codeMsg = userService.register(registerVo);

        return Result.info(codeMsg);
    }


}


================================================
FILE: dis-seckill-gateway/src/main/resources/application.properties
================================================
#---------------------------------
# web 
#---------------------------------
server.port=8082
#---------------------------------
# thymeleaf ģ
#---------------------------------
spring.thymeleaf.enabled=true
spring.thymeleaf.cache=false
spring.thymeleaf.servlet.content-type=text/html
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML5
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
#---------------------------------
#ҳ澲̬static
#---------------------------------
spring.resources.add-mappings=true
spring.resources.cache.period=3600s
spring.resources.chain.cache=true
spring.resources.chain.enabled=true
spring.resources.chain.compressed=true
spring.resources.chain.html-application-cache=true
spring.resources.static-locations=classpath:/static/
#---------------------------------
# dubbo 
#---------------------------------
# Spring boot application
spring.application.name=dis-seckill-gateway
# Base packages to scan Dubbo Component: @org.apache.dubbo.config.annotation.Service
#dubbo.scan.base-packages=com.example.bootdubboprovider
# Dubbo Application
## The default value of dubbo.application.name is ${spring.application.name}
## dubbo.application.name=${spring.application.name}
# Dubbo Protocol
#dubbo.protocol.name=dubbo
#dubbo.protocol.port=12345
## Dubbo Registry
embedded.zookeeper.port=2181
dubbo.registry.address=zookeeper://127.0.0.1:${embedded.zookeeper.port}
## DemoService version
#demo.service.version=1.0.0


================================================
FILE: dis-seckill-gateway/src/main/resources/static/bootstrap/css/bootstrap-theme.css
================================================
/*!
 * Bootstrap v3.3.7 (http://getbootstrap.com)
 * Copyright 2011-2016 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 */
.btn-default,
.btn-primary,
.btn-success,
.btn-info,
.btn-warning,
.btn-danger {
  text-shadow: 0 -1px 0 rgba(0, 0, 0, .2);
  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075);
}
.btn-default:active,
.btn-primary:active,
.btn-success:active,
.btn-info:active,
.btn-warning:active,
.btn-danger:active,
.btn-default.active,
.btn-primary.active,
.btn-success.active,
.btn-info.active,
.btn-warning.active,
.btn-danger.active {
  -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
          box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125);
}
.btn-default.disabled,
.btn-primary.disabled,
.btn-success.disabled,
.btn-info.disabled,
.btn-warning.disabled,
.btn-danger.disabled,
.btn-default[disabled],
.btn-primary[disabled],
.btn-success[disabled],
.btn-info[disabled],
.btn-warning[disabled],
.btn-danger[disabled],
fieldset[disabled] .btn-default,
fieldset[disabled] .btn-primary,
fieldset[disabled] .btn-success,
fieldset[disabled] .btn-info,
fieldset[disabled] .btn-warning,
fieldset[disabled] .btn-danger {
  -webkit-box-shadow: none;
          box-shadow: none;
}
.btn-default .badge,
.btn-primary .badge,
.btn-success .badge,
.btn-info .badge,
.btn-warning .badge,
.btn-danger .badge {
  text-shadow: none;
}
.btn:active,
.btn.active {
  background-image: none;
}
.btn-default {
  text-shadow: 0 1px 0 #fff;
  background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%);
  background-image:      -o-linear-gradient(top, #fff 0%, #e0e0e0 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0));
  background-image:         linear-gradient(to bottom, #fff 0%, #e0e0e0 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);
  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
  background-repeat: repeat-x;
  border-color: #dbdbdb;
  border-color: #ccc;
}
.btn-default:hover,
.btn-default:focus {
  background-color: #e0e0e0;
  background-position: 0 -15px;
}
.btn-default:active,
.btn-default.active {
  background-color: #e0e0e0;
  border-color: #dbdbdb;
}
.btn-default.disabled,
.btn-default[disabled],
fieldset[disabled] .btn-default,
.btn-default.disabled:hover,
.btn-default[disabled]:hover,
fieldset[disabled] .btn-default:hover,
.btn-default.disabled:focus,
.btn-default[disabled]:focus,
fieldset[disabled] .btn-default:focus,
.btn-default.disabled.focus,
.btn-default[disabled].focus,
fieldset[disabled] .btn-default.focus,
.btn-default.disabled:active,
.btn-default[disabled]:active,
fieldset[disabled] .btn-default:active,
.btn-default.disabled.active,
.btn-default[disabled].active,
fieldset[disabled] .btn-default.active {
  background-color: #e0e0e0;
  background-image: none;
}
.btn-primary {
  background-image: -webkit-linear-gradient(top, #337ab7 0%, #265a88 100%);
  background-image:      -o-linear-gradient(top, #337ab7 0%, #265a88 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#265a88));
  background-image:         linear-gradient(to bottom, #337ab7 0%, #265a88 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff265a88', GradientType=0);
  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
  background-repeat: repeat-x;
  border-color: #245580;
}
.btn-primary:hover,
.btn-primary:focus {
  background-color: #265a88;
  background-position: 0 -15px;
}
.btn-primary:active,
.btn-primary.active {
  background-color: #265a88;
  border-color: #245580;
}
.btn-primary.disabled,
.btn-primary[disabled],
fieldset[disabled] .btn-primary,
.btn-primary.disabled:hover,
.btn-primary[disabled]:hover,
fieldset[disabled] .btn-primary:hover,
.btn-primary.disabled:focus,
.btn-primary[disabled]:focus,
fieldset[disabled] .btn-primary:focus,
.btn-primary.disabled.focus,
.btn-primary[disabled].focus,
fieldset[disabled] .btn-primary.focus,
.btn-primary.disabled:active,
.btn-primary[disabled]:active,
fieldset[disabled] .btn-primary:active,
.btn-primary.disabled.active,
.btn-primary[disabled].active,
fieldset[disabled] .btn-primary.active {
  background-color: #265a88;
  background-image: none;
}
.btn-success {
  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%);
  background-image:      -o-linear-gradient(top, #5cb85c 0%, #419641 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641));
  background-image:         linear-gradient(to bottom, #5cb85c 0%, #419641 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);
  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
  background-repeat: repeat-x;
  border-color: #3e8f3e;
}
.btn-success:hover,
.btn-success:focus {
  background-color: #419641;
  background-position: 0 -15px;
}
.btn-success:active,
.btn-success.active {
  background-color: #419641;
  border-color: #3e8f3e;
}
.btn-success.disabled,
.btn-success[disabled],
fieldset[disabled] .btn-success,
.btn-success.disabled:hover,
.btn-success[disabled]:hover,
fieldset[disabled] .btn-success:hover,
.btn-success.disabled:focus,
.btn-success[disabled]:focus,
fieldset[disabled] .btn-success:focus,
.btn-success.disabled.focus,
.btn-success[disabled].focus,
fieldset[disabled] .btn-success.focus,
.btn-success.disabled:active,
.btn-success[disabled]:active,
fieldset[disabled] .btn-success:active,
.btn-success.disabled.active,
.btn-success[disabled].active,
fieldset[disabled] .btn-success.active {
  background-color: #419641;
  background-image: none;
}
.btn-info {
  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
  background-image:      -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2));
  background-image:         linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);
  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
  background-repeat: repeat-x;
  border-color: #28a4c9;
}
.btn-info:hover,
.btn-info:focus {
  background-color: #2aabd2;
  background-position: 0 -15px;
}
.btn-info:active,
.btn-info.active {
  background-color: #2aabd2;
  border-color: #28a4c9;
}
.btn-info.disabled,
.btn-info[disabled],
fieldset[disabled] .btn-info,
.btn-info.disabled:hover,
.btn-info[disabled]:hover,
fieldset[disabled] .btn-info:hover,
.btn-info.disabled:focus,
.btn-info[disabled]:focus,
fieldset[disabled] .btn-info:focus,
.btn-info.disabled.focus,
.btn-info[disabled].focus,
fieldset[disabled] .btn-info.focus,
.btn-info.disabled:active,
.btn-info[disabled]:active,
fieldset[disabled] .btn-info:active,
.btn-info.disabled.active,
.btn-info[disabled].active,
fieldset[disabled] .btn-info.active {
  background-color: #2aabd2;
  background-image: none;
}
.btn-warning {
  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
  background-image:      -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316));
  background-image:         linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);
  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
  background-repeat: repeat-x;
  border-color: #e38d13;
}
.btn-warning:hover,
.btn-warning:focus {
  background-color: #eb9316;
  background-position: 0 -15px;
}
.btn-warning:active,
.btn-warning.active {
  background-color: #eb9316;
  border-color: #e38d13;
}
.btn-warning.disabled,
.btn-warning[disabled],
fieldset[disabled] .btn-warning,
.btn-warning.disabled:hover,
.btn-warning[disabled]:hover,
fieldset[disabled] .btn-warning:hover,
.btn-warning.disabled:focus,
.btn-warning[disabled]:focus,
fieldset[disabled] .btn-warning:focus,
.btn-warning.disabled.focus,
.btn-warning[disabled].focus,
fieldset[disabled] .btn-warning.focus,
.btn-warning.disabled:active,
.btn-warning[disabled]:active,
fieldset[disabled] .btn-warning:active,
.btn-warning.disabled.active,
.btn-warning[disabled].active,
fieldset[disabled] .btn-warning.active {
  background-color: #eb9316;
  background-image: none;
}
.btn-danger {
  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
  background-image:      -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a));
  background-image:         linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);
  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
  background-repeat: repeat-x;
  border-color: #b92c28;
}
.btn-danger:hover,
.btn-danger:focus {
  background-color: #c12e2a;
  background-position: 0 -15px;
}
.btn-danger:active,
.btn-danger.active {
  background-color: #c12e2a;
  border-color: #b92c28;
}
.btn-danger.disabled,
.btn-danger[disabled],
fieldset[disabled] .btn-danger,
.btn-danger.disabled:hover,
.btn-danger[disabled]:hover,
fieldset[disabled] .btn-danger:hover,
.btn-danger.disabled:focus,
.btn-danger[disabled]:focus,
fieldset[disabled] .btn-danger:focus,
.btn-danger.disabled.focus,
.btn-danger[disabled].focus,
fieldset[disabled] .btn-danger.focus,
.btn-danger.disabled:active,
.btn-danger[disabled]:active,
fieldset[disabled] .btn-danger:active,
.btn-danger.disabled.active,
.btn-danger[disabled].active,
fieldset[disabled] .btn-danger.active {
  background-color: #c12e2a;
  background-image: none;
}
.thumbnail,
.img-thumbnail {
  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
          box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
}
.dropdown-menu > li > a:hover,
.dropdown-menu > li > a:focus {
  background-color: #e8e8e8;
  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
  background-image:      -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
  background-image:         linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
  background-repeat: repeat-x;
}
.dropdown-menu > .active > a,
.dropdown-menu > .active > a:hover,
.dropdown-menu > .active > a:focus {
  background-color: #2e6da4;
  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
  background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
  background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
  background-repeat: repeat-x;
}
.navbar-default {
  background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%);
  background-image:      -o-linear-gradient(top, #fff 0%, #f8f8f8 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8));
  background-image:         linear-gradient(to bottom, #fff 0%, #f8f8f8 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);
  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
  background-repeat: repeat-x;
  border-radius: 4px;
  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075);
}
.navbar-default .navbar-nav > .open > a,
.navbar-default .navbar-nav > .active > a {
  background-image: -webkit-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
  background-image:      -o-linear-gradient(top, #dbdbdb 0%, #e2e2e2 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#dbdbdb), to(#e2e2e2));
  background-image:         linear-gradient(to bottom, #dbdbdb 0%, #e2e2e2 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdbdbdb', endColorstr='#ffe2e2e2', GradientType=0);
  background-repeat: repeat-x;
  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
          box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075);
}
.navbar-brand,
.navbar-nav > li > a {
  text-shadow: 0 1px 0 rgba(255, 255, 255, .25);
}
.navbar-inverse {
  background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%);
  background-image:      -o-linear-gradient(top, #3c3c3c 0%, #222 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222));
  background-image:         linear-gradient(to bottom, #3c3c3c 0%, #222 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);
  filter: progid:DXImageTransform.Microsoft.gradient(enabled = false);
  background-repeat: repeat-x;
  border-radius: 4px;
}
.navbar-inverse .navbar-nav > .open > a,
.navbar-inverse .navbar-nav > .active > a {
  background-image: -webkit-linear-gradient(top, #080808 0%, #0f0f0f 100%);
  background-image:      -o-linear-gradient(top, #080808 0%, #0f0f0f 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#080808), to(#0f0f0f));
  background-image:         linear-gradient(to bottom, #080808 0%, #0f0f0f 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff080808', endColorstr='#ff0f0f0f', GradientType=0);
  background-repeat: repeat-x;
  -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
          box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25);
}
.navbar-inverse .navbar-brand,
.navbar-inverse .navbar-nav > li > a {
  text-shadow: 0 -1px 0 rgba(0, 0, 0, .25);
}
.navbar-static-top,
.navbar-fixed-top,
.navbar-fixed-bottom {
  border-radius: 0;
}
@media (max-width: 767px) {
  .navbar .navbar-nav .open .dropdown-menu > .active > a,
  .navbar .navbar-nav .open .dropdown-menu > .active > a:hover,
  .navbar .navbar-nav .open .dropdown-menu > .active > a:focus {
    color: #fff;
    background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
    background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
    background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
    background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
    filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
    background-repeat: repeat-x;
  }
}
.alert {
  text-shadow: 0 1px 0 rgba(255, 255, 255, .2);
  -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
          box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05);
}
.alert-success {
  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
  background-image:      -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc));
  background-image:         linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);
  background-repeat: repeat-x;
  border-color: #b2dba1;
}
.alert-info {
  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
  background-image:      -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0));
  background-image:         linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);
  background-repeat: repeat-x;
  border-color: #9acfea;
}
.alert-warning {
  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
  background-image:      -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0));
  background-image:         linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);
  background-repeat: repeat-x;
  border-color: #f5e79e;
}
.alert-danger {
  background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
  background-image:      -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3));
  background-image:         linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);
  background-repeat: repeat-x;
  border-color: #dca7a7;
}
.progress {
  background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
  background-image:      -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5));
  background-image:         linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);
  background-repeat: repeat-x;
}
.progress-bar {
  background-image: -webkit-linear-gradient(top, #337ab7 0%, #286090 100%);
  background-image:      -o-linear-gradient(top, #337ab7 0%, #286090 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#286090));
  background-image:         linear-gradient(to bottom, #337ab7 0%, #286090 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff286090', GradientType=0);
  background-repeat: repeat-x;
}
.progress-bar-success {
  background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%);
  background-image:      -o-linear-gradient(top, #5cb85c 0%, #449d44 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44));
  background-image:         linear-gradient(to bottom, #5cb85c 0%, #449d44 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);
  background-repeat: repeat-x;
}
.progress-bar-info {
  background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
  background-image:      -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5));
  background-image:         linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);
  background-repeat: repeat-x;
}
.progress-bar-warning {
  background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
  background-image:      -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f));
  background-image:         linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);
  background-repeat: repeat-x;
}
.progress-bar-danger {
  background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%);
  background-image:      -o-linear-gradient(top, #d9534f 0%, #c9302c 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c));
  background-image:         linear-gradient(to bottom, #d9534f 0%, #c9302c 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);
  background-repeat: repeat-x;
}
.progress-bar-striped {
  background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
  background-image:      -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
  background-image:         linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent);
}
.list-group {
  border-radius: 4px;
  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
          box-shadow: 0 1px 2px rgba(0, 0, 0, .075);
}
.list-group-item.active,
.list-group-item.active:hover,
.list-group-item.active:focus {
  text-shadow: 0 -1px 0 #286090;
  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2b669a 100%);
  background-image:      -o-linear-gradient(top, #337ab7 0%, #2b669a 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2b669a));
  background-image:         linear-gradient(to bottom, #337ab7 0%, #2b669a 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2b669a', GradientType=0);
  background-repeat: repeat-x;
  border-color: #2b669a;
}
.list-group-item.active .badge,
.list-group-item.active:hover .badge,
.list-group-item.active:focus .badge {
  text-shadow: none;
}
.panel {
  -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
          box-shadow: 0 1px 2px rgba(0, 0, 0, .05);
}
.panel-default > .panel-heading {
  background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
  background-image:      -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8));
  background-image:         linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);
  background-repeat: repeat-x;
}
.panel-primary > .panel-heading {
  background-image: -webkit-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
  background-image:      -o-linear-gradient(top, #337ab7 0%, #2e6da4 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#337ab7), to(#2e6da4));
  background-image:         linear-gradient(to bottom, #337ab7 0%, #2e6da4 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff337ab7', endColorstr='#ff2e6da4', GradientType=0);
  background-repeat: repeat-x;
}
.panel-success > .panel-heading {
  background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
  background-image:      -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6));
  background-image:         linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);
  background-repeat: repeat-x;
}
.panel-info > .panel-heading {
  background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
  background-image:      -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3));
  background-image:         linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);
  background-repeat: repeat-x;
}
.panel-warning > .panel-heading {
  background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
  background-image:      -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc));
  background-image:         linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);
  background-repeat: repeat-x;
}
.panel-danger > .panel-heading {
  background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
  background-image:      -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc));
  background-image:         linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);
  background-repeat: repeat-x;
}
.well {
  background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
  background-image:      -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%);
  background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5));
  background-image:         linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%);
  filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);
  background-repeat: repeat-x;
  border-color: #dcdcdc;
  -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
          box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1);
}
/*# sourceMappingURL=bootstrap-theme.css.map */


================================================
FILE: dis-seckill-gateway/src/main/resources/static/bootstrap/css/bootstrap.css
================================================
/*!
 * Bootstrap v3.3.7 (http://getbootstrap.com)
 * Copyright 2011-2016 Twitter, Inc.
 * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE)
 */
/*! normalize.css v3.0.3 | MIT License | github.com/necolas/normalize.css */
html {
  font-family: sans-serif;
  -webkit-text-size-adjust: 100%;
      -ms-text-size-adjust: 100%;
}
body {
  margin: 0;
}
article,
aside,
details,
figcaption,
figure,
footer,
header,
hgroup,
main,
menu,
nav,
section,
summary {
  display: block;
}
audio,
canvas,
progress,
video {
  display: inline-block;
  vertical-align: baseline;
}
audio:not([controls]) {
  display: none;
  height: 0;
}
[hidden],
template {
  display: none;
}
a {
  background-color: transparent;
}
a:active,
a:hover {
  outline: 0;
}
abbr[title] {
  border-bottom: 1px dotted;
}
b,
strong {
  font-weight: bold;
}
dfn {
  font-style: italic;
}
h1 {
  margin: .67em 0;
  font-size: 2em;
}
mark {
  color: #000;
  background: #ff0;
}
small {
  font-size: 80%;
}
sub,
sup {
  position: relative;
  font-size: 75%;
  line-height: 0;
  vertical-align: baseline;
}
sup {
  top: -.5em;
}
sub {
  bottom: -.25em;
}
img {
  border: 0;
}
svg:not(:root) {
  overflow: hidden;
}
figure {
  margin: 1em 40px;
}
hr {
  height: 0;
  -webkit-box-sizing: content-box;
     -moz-box-sizing: content-box;
          box-sizing: content-box;
}
pre {
  overflow: auto;
}
code,
kbd,
pre,
samp {
  font-family: monospace, monospace;
  font-size: 1em;
}
button,
input,
optgroup,
select,
textarea {
  margin: 0;
  font: inherit;
  color: inherit;
}
button {
  overflow: visible;
}
button,
select {
  text-transform: none;
}
button,
html input[type="button"],
input[type="reset"],
input[type="submit"] {
  -webkit-appearance: button;
  cursor: pointer;
}
button[disabled],
html input[disabled] {
  cursor: default;
}
button::-moz-focus-inner,
input::-moz-focus-inner {
  padding: 0;
  border: 0;
}
input {
  line-height: normal;
}
input[type="checkbox"],
input[type="radio"] {
  -webkit-box-sizing: border-box;
     -moz-box-sizing: border-box;
          box-sizing: border-box;
  padding: 0;
}
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  height: auto;
}
input[type="search"] {
  -webkit-box-sizing: content-box;
     -moz-box-sizing: content-box;
          box-sizing: content-box;
  -webkit-appearance: textfield;
}
input[type="search"]::-webkit-search-cancel-button,
input[type="search"]::-webkit-search-decoration {
  -webkit-appearance: none;
}
fieldset {
  padding: .35em .625em .75em;
  margin: 0 2px;
  border: 1px solid #c0c0c0;
}
legend {
  padding: 0;
  border: 0;
}
textarea {
  overflow: auto;
}
optgroup {
  font-weight: bold;
}
table {
  border-spacing: 0;
  border-collapse: collapse;
}
td,
th {
  padding: 0;
}
/*! Source: https://github.com/h5bp/html5-boilerplate/blob/master/src/css/main.css */
@media print {
  *,
  *:before,
  *:after {
    color: #000 !important;
    text-shadow: none !important;
    background: transparent !important;
    -webkit-box-shadow: none !important;
            box-shadow: none !important;
  }
  a,
  a:visited {
    text-decoration: underline;
  }
  a[href]:after {
    content: " (" attr(href) ")";
  }
  abbr[title]:after {
    content: " (" attr(title) ")";
  }
  a[href^="#"]:after,
  a[href^="javascript:"]:after {
    content: "";
  }
  pre,
  blockquote {
    border: 1px solid #999;

    page-break-inside: avoid;
  }
  thead {
    display: table-header-group;
  }
  tr,
  img {
    page-break-inside: avoid;
  }
  img {
    max-width: 100% !important;
  }
  p,
  h2,
  h3 {
    orphans: 3;
    widows: 3;
  }
  h2,
  h3 {
    page-break-after: avoid;
  }
  .navbar {
    display: none;
  }
  .btn > .caret,
  .dropup > .btn > .caret {
    border-top-color: #000 !important;
  }
  .label {
    border: 1px solid #000;
  }
  .table {
    border-collapse: collapse !important;
  }
  .table td,
  .table th {
    background-color: #fff !important;
  }
  .table-bordered th,
  .table-bordered td {
    border: 1px solid #ddd !important;
  }
}
@font-face {
  font-family: 'Glyphicons Halflings';

  src: url('../fonts/glyphicons-halflings-regular.eot');
  src: url('../fonts/glyphicons-halflings-regular.eot?#iefix') format('embedded-opentype'), url('../fonts/glyphicons-halflings-regular.woff2') format('woff2'), url('../fonts/glyphicons-halflings-regular.woff') format('woff'), url('../fonts/glyphicons-halflings-regular.ttf') format('truetype'), url('../fonts/glyphicons-halflings-regular.svg#glyphicons_halflingsregular') format('svg');
}
.glyphicon {
  position: relative;
  top: 1px;
  display: inline-block;
  font-family: 'Glyphicons Halflings';
  font-style: normal;
  font-weight: normal;
  line-height: 1;

  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}
.glyphicon-asterisk:before {
  content: "\002a";
}
.glyphicon-plus:before {
  content: "\002b";
}
.glyphicon-euro:before,
.glyphicon-eur:before {
  content: "\20ac";
}
.glyphicon-minus:before {
  content: "\2212";
}
.glyphicon-cloud:before {
  content: "\2601";
}
.glyphicon-envelope:before {
  content: "\2709";
}
.glyphicon-pencil:before {
  content: "\270f";
}
.glyphicon-glass:before {
  content: "\e001";
}
.glyphicon-music:before {
  content: "\e002";
}
.glyphicon-search:before {
  content: "\e003";
}
.glyphicon-heart:before {
  content: "\e005";
}
.glyphicon-star:before {
  content: "\e006";
}
.glyphicon-star-empty:before {
  content: "\e007";
}
.glyphicon-user:before {
  content: "\e008";
}
.glyphicon-film:before {
  content: "\e009";
}
.glyphicon-th-large:before {
  content: "\e010";
}
.glyphicon-th:before {
  content: "\e011";
}
.glyphicon-th-list:before {
  content: "\e012";
}
.glyphicon-ok:before {
  content: "\e013";
}
.glyphicon-remove:before {
  content: "\e014";
}
.glyphicon-zoom-in:before {
  content: "\e015";
}
.glyphicon-zoom-out:before {
  content: "\e016";
}
.glyphicon-off:before {
  content: "\e017";
}
.glyphicon-signal:before {
  content: "\e018";
}
.glyphicon-cog:before {
  content: "\e019";
}
.glyphicon-trash:before {
  content: "\e020";
}
.glyphicon-home:before {
  content: "\e021";
}
.glyphicon-file:before {
  content: "\e022";
}
.glyphicon-time:before {
  content: "\e023";
}
.glyphicon-road:before {
  content: "\e024";
}
.glyphicon-download-alt:before {
  content: "\e025";
}
.glyphicon-download:before {
  content: "\e026";
}
.glyphicon-upload:before {
  content: "\e027";
}
.glyphicon-inbox:before {
  content: "\e028";
}
.glyphicon-play-circle:before {
  content: "\e029";
}
.glyphicon-repeat:before {
  content: "\e030";
}
.glyphicon-refresh:before {
  content: "\e031";
}
.glyphicon-list-alt:before {
  content: "\e032";
}
.glyphicon-lock:before {
  content: "\e033";
}
.glyphicon-flag:before {
  content: "\e034";
}
.glyphicon-headphones:before {
  content: "\e035";
}
.glyphicon-volume-off:before {
  content: "\e036";
}
.glyphicon-volume-down:before {
  content: "\e037";
}
.glyphicon-volume-up:before {
  content: "\e038";
}
.glyphicon-qrcode:before {
  content: "\e039";
}
.glyphicon-barcode:before {
  content: "\e040";
}
.glyphicon-tag:before {
  content: "\e041";
}
.glyphicon-tags:before {
  content: "\e042";
}
.glyphicon-book:before {
  content: "\e043";
}
.glyphicon-bookmark:before {
  content: "\e044";
}
.glyphicon-print:before {
  content: "\e045";
}
.glyphicon-camera:before {
  content: "\e046";
}
.glyphicon-font:before {
  content: "\e047";
}
.glyphicon-bold:before {
  content: "\e048";
}
.glyphicon-italic:before {
  content: "\e049";
}
.glyphicon-text-height:before {
  content: "\e050";
}
.glyphicon-text-width:before {
  content: "\e051";
}
.glyphicon-align-left:before {
  content: "\e052";
}
.glyphicon-align-center:before {
  content: "\e053";
}
.glyphicon-align-right:before {
  content: "\e054";
}
.glyphicon-align-justify:before {
  content: "\e055";
}
.glyphicon-list:before {
  content: "\e056";
}
.glyphicon-indent-left:before {
  content: "\e057";
}
.glyphicon-indent-right:before {
  content: "\e058";
}
.glyphicon-facetime-video:before {
  content: "\e059";
}
.glyphicon-picture:before {
  content: "\e060";
}
.glyphicon-map-marker:before {
  content: "\e062";
}
.glyphicon-adjust:before {
  content: "\e063";
}
.glyphicon-tint:before {
  content: "\e064";
}
.glyphicon-edit:before {
  content: "\e065";
}
.glyphicon-share:before {
  content: "\e066";
}
.glyphicon-check:before {
  content: "\e067";
}
.glyphicon-move:before {
  content: "\e068";
}
.glyphicon-step-backward:before {
  content: "\e069";
}
.glyphicon-fast-backward:before {
  content: "\e070";
}
.glyphicon-backward:before {
  content: "\e071";
}
.glyphicon-play:before {
  content: "\e072";
}
.glyphicon-pause:before {
  content: "\e073";
}
.glyphicon-stop:before {
  content: "\e074";
}
.glyphicon-forward:before {
  content: "\e075";
}
.glyphicon-fast-forward:before {
  content: "\e076";
}
.glyphicon-step-forward:before {
  content: "\e077";
}
.glyphicon-eject:before {
  content: "\e078";
}
.glyphicon-chevron-left:before {
  content: "\e079";
}
.glyphicon-chevron-right:before {
  content: "\e080";
}
.glyphicon-plus-sign:before {
  content: "\e081";
}
.glyphicon-minus-sign:before {
  content: "\e082";
}
.glyphicon-remove-sign:before {
  content: "\e083";
}
.glyphicon-ok-sign:before {
  content: "\e084";
}
.glyphicon-question-sign:before {
  content: "\e085";
}
.glyphicon-info-sign:before {
  content: "\e086";
}
.glyphicon-screenshot:before {
  content: "\e087";
}
.glyphicon-remove-circle:before {
  content: "\e088";
}
.glyphicon-ok-circle:before {
  content: "\e089";
}
.glyphicon-ban-circle:before {
  content: "\e090";
}
.glyphicon-arrow-left:before {
  content: "\e091";
}
.glyphicon-arrow-right:before {
  content: "\e092";
}
.glyphicon-arrow-up:before {
  content: "\e093";
}
.glyphicon-arrow-down:before {
  content: "\e094";
}
.glyphicon-share-alt:before {
  content: "\e095";
}
.glyphicon-resize-full:before {
  content: "\e096";
}
.glyphicon-resize-small:before {
  content: "\e097";
}
.glyphicon-exclamation-sign:before {
  content: "\e101";
}
.glyphicon-gift:before {
  content: "\e102";
}
.glyphicon-leaf:before {
  content: "\e103";
}
.glyphicon-fire:before {
  content: "\e104";
}
.glyphicon-eye-open:before {
  content: "\e105";
}
.glyphicon-eye-close:before {
  content: "\e106";
}
.glyphicon-warning-sign:before {
  content: "\e107";
}
.glyphicon-plane:before {
  content: "\e108";
}
.glyphicon-calendar:before {
  content: "\e109";
}
.glyphicon-random:before {
  content: "\e110";
}
.glyphicon-comment:before {
  content: "\e111";
}
.glyphicon-magnet:before {
  content: "\e112";
}
.glyphicon-chevron-up:before {
  content: "\e113";
}
.glyphicon-chevron-down:before {
  content: "\e114";
}
.glyphicon-retweet:before {
  content: "\e115";
}
.glyphicon-shopping-cart:before {
  content: "\e116";
}
.glyphicon-folder-close:before {
  content: "\e117";
}
.glyphicon-folder-open:before {
  content: "\e118";
}
.glyphicon-resize-vertical:before {
  content: "\e119";
}
.glyphicon-resize-horizontal:before {
  content: "\e120";
}
.glyphicon-hdd:before {
  content: "\e121";
}
.glyphicon-bullhorn:before {
  content: "\e122";
}
.glyphicon-bell:before {
  content: "\e123";
}
.glyphicon-certificate:before {
  content: "\e124";
}
.glyphicon-thumbs-up:before {
  content: "\e125";
}
.glyphicon-thumbs-down:before {
  content: "\e126";
}
.glyphicon-hand-right:before {
  content: "\e127";
}
.glyphicon-hand-left:before {
  content: "\e128";
}
.glyphicon-hand-up:before {
  content: "\e129";
}
.glyphicon-hand-down:before {
  content: "\e130";
}
.glyphicon-circle-arrow-right:before {
  content: "\e131";
}
.glyphicon-circle-arrow-left:before {
  content: "\e132";
}
.glyphicon-circle-arrow-up:before {
  content: "\e133";
}
.glyphicon-circle-arrow-down:before {
  content: "\e134";
}
.glyphicon-globe:before {
  content: "\e135";
}
.glyphicon-wrench:before {
  content: "\e136";
}
.glyphicon-tasks:before {
  content: "\e137";
}
.glyphicon-filter:before {
  content: "\e138";
}
.glyphicon-briefcase:before {
  content: "\e139";
}
.glyphicon-fullscreen:before {
  content: "\e140";
}
.glyphicon-dashboard:before {
  content: "\e141";
}
.glyphicon-paperclip:before {
  content: "\e142";
}
.glyphicon-heart-empty:before {
  content: "\e143";
}
.glyphicon-link:before {
  content: "\e144";
}
.glyphicon-phone:before {
  content: "\e145";
}
.glyphicon-pushpin:before {
  content: "\e146";
}
.glyphicon-usd:before {
  content: "\e148";
}
.glyphicon-gbp:before {
  content: "\e149";
}
.glyphicon-sort:before {
  content: "\e150";
}
.glyphicon-sort-by-alphabet:before {
  content: "\e151";
}
.glyphicon-sort-by-alphabet-alt:before {
  content: "\e152";
}
.glyphicon-sort-by-order:before {
  content: "\e153";
}
.glyphicon-sort-by-order-alt:before {
  content: "\e154";
}
.glyphicon-sort-by-attributes:before {
  content: "\e155";
}
.glyphicon-sort-by-attributes-alt:before {
  content: "\e156";
}
.glyphicon-unchecked:before {
  content: "\e157";
}
.glyphicon-expand:before {
  content: "\e158";
}
.glyphicon-collapse-down:before {
  content: "\e159";
}
.glyphicon-collapse-up:before {
  content: "\e160";
}
.glyphicon-log-in:before {
  content: "\e161";
}
.glyphicon-flash:before {
  content: "\e162";
}
.glyphicon-log-out:before {
  content: "\e163";
}
.glyphicon-new-window:before {
  content: "\e164";
}
.glyphicon-record:before {
  content: "\e165";
}
.glyphicon-save:before {
  content: "\e166";
}
.glyphicon-open:before {
  content: "\e167";
}
.glyphicon-saved:before {
  content: "\e168";
}
.glyphicon-import:before {
  content: "\e169";
}
.glyphicon-export:before {
  content: "\e170";
}
.glyphicon-send:before {
  content: "\e171";
}
.glyphicon-floppy-disk:before {
  content: "\e172";
}
.glyphicon-floppy-saved:before {
  content: "\e173";
}
.glyphicon-floppy-remove:before {
  content: "\e174";
}
.glyphicon-floppy-save:before {
  content: "\e175";
}
.glyphicon-floppy-open:before {
  content: "\e176";
}
.glyphicon-credit-card:before {
  content: "\e177";
}
.glyphicon-transfer:before {
  content: "\e178";
}
.glyphicon-cutlery:before {
  content: "\e179";
}
.glyphicon-header:before {
  content: "\e180";
}
.glyphicon-compressed:before {
  content: "\e181";
}
.glyphicon-earphone:before {
  content: "\e182";
}
.glyphicon-phone-alt:before {
  content: "\e183";
}
.glyphicon-tower:before {
  content: "\e184";
}
.glyphicon-stats:before {
  content: "\e185";
}
.glyphicon-sd-video:before {
  content: "\e186";
}
.glyphicon-hd-video:before {
  content: "\e187";
}
.glyphicon-subtitles:before {
  content: "\e188";
}
.glyphicon-sound-stereo:before {
  content: "\e189";
}
.glyphicon-sound-dolby:before {
  content: "\e190";
}
.glyphicon-sound-5-1:before {
  content: "\e191";
}
.glyphicon-sound-6-1:before {
  content: "\e192";
}
.glyphicon-sound-7-1:before {
  content: "\e193";
}
.glyphicon-copyright-mark:before {
  content: "\e194";
}
.glyphicon-registration-mark:before {
  content: "\e195";
}
.glyphicon-cloud-download:before {
  content: "\e197";
}
.glyphicon-cloud-upload:before {
  content: "\e198";
}
.glyphicon-tree-conifer:before {
  content: "\e199";
}
.glyphicon-tree-deciduous:before {
  content: "\e200";
}
.glyphicon-cd:before {
  content: "\e201";
}
.glyphicon-save-file:before {
  content: "\e202";
}
.glyphicon-open-file:before {
  content: "\e203";
}
.glyphicon-level-up:before {
  content: "\e204";
}
.glyphicon-copy:before {
  content: "\e205";
}
.glyphicon-paste:before {
  content: "\e206";
}
.glyphicon-alert:before {
  content: "\e209";
}
.glyphicon-equalizer:before {
  content: "\e210";
}
.glyphicon-king:before {
  content: "\e211";
}
.glyphicon-queen:before {
  content: "\e212";
}
.glyphicon-pawn:before {
  content: "\e213";
}
.glyphicon-bishop:before {
  content: "\e214";
}
.glyphicon-knight:before {
  content: "\e215";
}
.glyphicon-baby-formula:before {
  content: "\e216";
}
.glyphicon-tent:before {
  content: "\26fa";
}
.glyphicon-blackboard:before {
  content: "\e218";
}
.glyphicon-bed:before {
  content: "\e219";
}
.glyphicon-apple:before {
  content: "\f8ff";
}
.glyphicon-erase:before {
  content: "\e221";
}
.glyphicon-hourglass:before {
  content: "\231b";
}
.glyphicon-lamp:before {
  content: "\e223";
}
.glyphicon-duplicate:before {
  content: "\e224";
}
.glyphicon-piggy-bank:before {
  content: "\e225";
}
.glyphicon-scissors:before {
  content: "\e226";
}
.glyphicon-bitcoin:before {
  content: "\e227";
}
.glyphicon-btc:before {
  content: "\e227";
}
.glyphicon-xbt:before {
  content: "\e227";
}
.glyphicon-yen:before {
  content: "\00a5";
}
.glyphicon-jpy:before {
  content: "\00a5";
}
.glyphicon-ruble:before {
  content: "\20bd";
}
.glyphicon-rub:before {
  content: "\20bd";
}
.glyphicon-scale:before {
  content: "\e230";
}
.glyphicon-ice-lolly:before {
  content: "\e231";
}
.glyphicon-ice-lolly-tasted:before {
  content: "\e232";
}
.glyphicon-education:before {
  content: "\e233";
}
.glyphicon-option-horizontal:before {
  content: "\e234";
}
.glyphicon-option-vertical:before {
  content: "\e235";
}
.glyphicon-menu-hamburger:before {
  content: "\e236";
}
.glyphicon-modal-window:before {
  content: "\e237";
}
.glyphicon-oil:before {
  content: "\e238";
}
.glyphicon-grain:before {
  content: "\e239";
}
.glyphicon-sunglasses:before {
  content: "\e240";
}
.glyphicon-text-size:before {
  content: "\e241";
}
.glyphicon-text-color:before {
  content: "\e242";
}
.glyphicon-text-background:before {
  content: "\e243";
}
.glyphicon-object-align-top:before {
  content: "\e244";
}
.glyphicon-object-align-bottom:before {
  content: "\e245";
}
.glyphicon-object-align-horizontal:before {
  content: "\e246";
}
.glyphicon-object-align-left:before {
  content: "\e247";
}
.glyphicon-object-align-vertical:before {
  content: "\e248";
}
.glyphicon-object-align-right:before {
  content: "\e249";
}
.glyphicon-triangle-right:before {
  content: "\e250";
}
.glyphicon-triangle-left:before {
  content: "\e251";
}
.glyphicon-triangle-bottom:before {
  content: "\e252";
}
.glyphicon-triangle-top:before {
  content: "\e253";
}
.glyphicon-console:before {
  content: "\e254";
}
.glyphicon-superscript:before {
  content: "\e255";
}
.glyphicon-subscript:before {
  content: "\e256";
}
.glyphicon-menu-left:before {
  content: "\e257";
}
.glyphicon-menu-right:before {
  content: "\e258";
}
.glyphicon-menu-down:before {
  content: "\e259";
}
.glyphicon-menu-up:before {
  content: "\e260";
}
* {
  -webkit-box-sizing: border-box;
     -moz-box-sizing: border-box;
          box-sizing: border-box;
}
*:before,
*:after {
  -webkit-box-sizing: border-box;
     -moz-box-sizing: border-box;
          box-sizing: border-box;
}
html {
  font-size: 10px;

  -webkit-tap-highlight-color: rgba(0, 0, 0, 0);
}
body {
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 14px;
  line-height: 1.42857143;
  color: #333;
  background-color: #fff;
}
input,
button,
select,
textarea {
  font-family: inherit;
  font-size: inherit;
  line-height: inherit;
}
a {
  color: #337ab7;
  text-decoration: none;
}
a:hover,
a:focus {
  color: #23527c;
  text-decoration: underline;
}
a:focus {
  outline: 5px auto -webkit-focus-ring-color;
  outline-offset: -2px;
}
figure {
  margin: 0;
}
img {
  vertical-align: middle;
}
.img-responsive,
.thumbnail > img,
.thumbnail a > img,
.carousel-inner > .item > img,
.carousel-inner > .item > a > img {
  display: block;
  max-width: 100%;
  height: auto;
}
.img-rounded {
  border-radius: 6px;
}
.img-thumbnail {
  display: inline-block;
  max-width: 100%;
  height: auto;
  padding: 4px;
  line-height: 1.42857143;
  background-color: #fff;
  border: 1px solid #ddd;
  border-radius: 4px;
  -webkit-transition: all .2s ease-in-out;
       -o-transition: all .2s ease-in-out;
          transition: all .2s ease-in-out;
}
.img-circle {
  border-radius: 50%;
}
hr {
  margin-top: 20px;
  margin-bottom: 20px;
  border: 0;
  border-top: 1px solid #eee;
}
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip: rect(0, 0, 0, 0);
  border: 0;
}
.sr-only-focusable:active,
.sr-only-focusable:focus {
  position: static;
  width: auto;
  height: auto;
  margin: 0;
  overflow: visible;
  clip: auto;
}
[role="button"] {
  cursor: pointer;
}
h1,
h2,
h3,
h4,
h5,
h6,
.h1,
.h2,
.h3,
.h4,
.h5,
.h6 {
  font-family: inherit;
  font-weight: 500;
  line-height: 1.1;
  color: inherit;
}
h1 small,
h2 small,
h3 small,
h4 small,
h5 small,
h6 small,
.h1 small,
.h2 small,
.h3 small,
.h4 small,
.h5 small,
.h6 small,
h1 .small,
h2 .small,
h3 .small,
h4 .small,
h5 .small,
h6 .small,
.h1 .small,
.h2 .small,
.h3 .small,
.h4 .small,
.h5 .small,
.h6 .small {
  font-weight: normal;
  line-height: 1;
  color: #777;
}
h1,
.h1,
h2,
.h2,
h3,
.h3 {
  margin-top: 20px;
  margin-bottom: 10px;
}
h1 small,
.h1 small,
h2 small,
.h2 small,
h3 small,
.h3 small,
h1 .small,
.h1 .small,
h2 .small,
.h2 .small,
h3 .small,
.h3 .small {
  font-size: 65%;
}
h4,
.h4,
h5,
.h5,
h6,
.h6 {
  margin-top: 10px;
  margin-bottom: 10px;
}
h4 small,
.h4 small,
h5 small,
.h5 small,
h6 small,
.h6 small,
h4 .small,
.h4 .small,
h5 .small,
.h5 .small,
h6 .small,
.h6 .small {
  font-size: 75%;
}
h1,
.h1 {
  font-size: 36px;
}
h2,
.h2 {
  font-size: 30px;
}
h3,
.h3 {
  font-size: 24px;
}
h4,
.h4 {
  font-size: 18px;
}
h5,
.h5 {
  font-size: 14px;
}
h6,
.h6 {
  font-size: 12px;
}
p {
  margin: 0 0 10px;
}
.lead {
  margin-bottom: 20px;
  font-size: 16px;
  font-weight: 300;
  line-height: 1.4;
}
@media (min-width: 768px) {
  .lead {
    font-size: 21px;
  }
}
small,
.small {
  font-size: 85%;
}
mark,
.mark {
  padding: .2em;
  background-color: #fcf8e3;
}
.text-left {
  text-align: left;
}
.text-right {
  text-align: right;
}
.text-center {
  text-align: center;
}
.text-justify {
  text-align: justify;
}
.text-nowrap {
  white-space: nowrap;
}
.text-lowercase {
  text-transform: lowercase;
}
.text-uppercase {
  text-transform: uppercase;
}
.text-capitalize {
  text-transform: capitalize;
}
.text-muted {
  color: #777;
}
.text-primary {
  color: #337ab7;
}
a.text-primary:hover,
a.text-primary:focus {
  color: #286090;
}
.text-success {
  color: #3c763d;
}
a.text-success:hover,
a.text-success:focus {
  color: #2b542c;
}
.text-info {
  color: #31708f;
}
a.text-info:hover,
a.text-info:focus {
  color: #245269;
}
.text-warning {
  color: #8a6d3b;
}
a.text-warning:hover,
a.text-warning:focus {
  color: #66512c;
}
.text-danger {
  color: #a94442;
}
a.text-danger:hover,
a.text-danger:focus {
  color: #843534;
}
.bg-primary {
  color: #fff;
  background-color: #337ab7;
}
a.bg-primary:hover,
a.bg-primary:focus {
  background-color: #286090;
}
.bg-success {
  background-color: #dff0d8;
}
a.bg-success:hover,
a.bg-success:focus {
  background-color: #c1e2b3;
}
.bg-info {
  background-color: #d9edf7;
}
a.bg-info:hover,
a.bg-info:focus {
  background-color: #afd9ee;
}
.bg-warning {
  background-color: #fcf8e3;
}
a.bg-warning:hover,
a.bg-warning:focus {
  background-color: #f7ecb5;
}
.bg-danger {
  background-color: #f2dede;
}
a.bg-danger:hover,
a.bg-danger:focus {
  background-color: #e4b9b9;
}
.page-header {
  padding-bottom: 9px;
  margin: 40px 0 20px;
  border-bottom: 1px solid #eee;
}
ul,
ol {
  margin-top: 0;
  margin-bottom: 10px;
}
ul ul,
ol ul,
ul ol,
ol ol {
  margin-bottom: 0;
}
.list-unstyled {
  padding-left: 0;
  list-style: none;
}
.list-inline {
  padding-left: 0;
  margin-left: -5px;
  list-style: none;
}
.list-inline > li {
  display: inline-block;
  padding-right: 5px;
  padding-left: 5px;
}
dl {
  margin-top: 0;
  margin-bottom: 20px;
}
dt,
dd {
  line-height: 1.42857143;
}
dt {
  font-weight: bold;
}
dd {
  margin-left: 0;
}
@media (min-width: 768px) {
  .dl-horizontal dt {
    float: left;
    width: 160px;
    overflow: hidden;
    clear: left;
    text-align: right;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  .dl-horizontal dd {
    margin-left: 180px;
  }
}
abbr[title],
abbr[data-original-title] {
  cursor: help;
  border-bottom: 1px dotted #777;
}
.initialism {
  font-size: 90%;
  text-transform: uppercase;
}
blockquote {
  padding: 10px 20px;
  margin: 0 0 20px;
  font-size: 17.5px;
  border-left: 5px solid #eee;
}
blockquote p:last-child,
blockquote ul:last-child,
blockquote ol:last-child {
  margin-bottom: 0;
}
blockquote footer,
blockquote small,
blockquote .small {
  display: block;
  font-size: 80%;
  line-height: 1.42857143;
  color: #777;
}
blockquote footer:before,
blockquote small:before,
blockquote .small:before {
  content: '\2014 \00A0';
}
.blockquote-reverse,
blockquote.pull-right {
  padding-right: 15px;
  padding-left: 0;
  text-align: right;
  border-right: 5px solid #eee;
  border-left: 0;
}
.blockquote-reverse footer:before,
blockquote.pull-right footer:before,
.blockquote-reverse small:before,
blockquote.pull-right small:before,
.blockquote-reverse .small:before,
blockquote.pull-right .small:before {
  content: '';
}
.blockquote-reverse footer:after,
blockquote.pull-right footer:after,
.blockquote-reverse small:after,
blockquote.pull-right small:after,
.blockquote-reverse .small:after,
blockquote.pull-right .small:after {
  content: '\00A0 \2014';
}
address {
  margin-bottom: 20px;
  font-style: normal;
  line-height: 1.42857143;
}
code,
kbd,
pre,
samp {
  font-family: Menlo, Monaco, Consolas, "Courier New", monospace;
}
code {
  padding: 2px 4px;
  font-size: 90%;
  color: #c7254e;
  background-color: #f9f2f4;
  border-radius: 4px;
}
kbd {
  padding: 2px 4px;
  font-size: 90%;
  color: #fff;
  background-color: #333;
  border-radius: 3px;
  -webkit-box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
          box-shadow: inset 0 -1px 0 rgba(0, 0, 0, .25);
}
kbd kbd {
  padding: 0;
  font-size: 100%;
  font-weight: bold;
  -webkit-box-shadow: none;
          box-shadow: none;
}
pre {
  display: block;
  padding: 9.5px;
  margin: 0 0 10px;
  font-size: 13px;
  line-height: 1.42857143;
  color: #333;
  word-break: break-all;
  word-wrap: break-word;
  background-color: #f5f5f5;
  border: 1px solid #ccc;
  border-radius: 4px;
}
pre code {
  padding: 0;
  font-size: inherit;
  color: inherit;
  white-space: pre-wrap;
  background-color: transparent;
  border-radius: 0;
}
.pre-scrollable {
  max-height: 340px;
  overflow-y: scroll;
}
.container {
  padding-right: 15px;
  padding-left: 15px;
  margin-right: auto;
  margin-left: auto;
}
@media (min-width: 768px) {
  .container {
    width: 750px;
  }
}
@media (min-width: 992px) {
  .container {
    width: 970px;
  }
}
@media (min-width: 1200px) {
  .container {
    width: 1170px;
  }
}
.container-fluid {
  padding-right: 15px;
  padding-left: 15px;
  margin-right: auto;
  margin-left: auto;
}
.row {
  margin-right: -15px;
  margin-left: -15px;
}
.col-xs-1, .col-sm-1, .col-md-1, .col-lg-1, .col-xs-2, .col-sm-2, .col-md-2, .col-lg-2, .col-xs-3, .col-sm-3, .col-md-3, .col-lg-3, .col-xs-4, .col-sm-4, .col-md-4, .col-lg-4, .col-xs-5, .col-sm-5, .col-md-5, .col-lg-5, .col-xs-6, .col-sm-6, .col-md-6, .col-lg-6, .col-xs-7, .col-sm-7, .col-md-7, .col-lg-7, .col-xs-8, .col-sm-8, .col-md-8, .col-lg-8, .col-xs-9, .col-sm-9, .col-md-9, .col-lg-9, .col-xs-10, .col-sm-10, .col-md-10, .col-lg-10, .col-xs-11, .col-sm-11, .col-md-11, .col-lg-11, .col-xs-12, .col-sm-12, .col-md-12, .col-lg-12 {
  position: relative;
  min-height: 1px;
  padding-right: 15px;
  padding-left: 15px;
}
.col-xs-1, .col-xs-2, .col-xs-3, .col-xs-4, .col-xs-5, .col-xs-6, .col-xs-7, .col-xs-8, .col-xs-9, .col-xs-10, .col-xs-11, .col-xs-12 {
  float: left;
}
.col-xs-12 {
  width: 100%;
}
.col-xs-11 {
  width: 91.66666667%;
}
.col-xs-10 {
  width: 83.33333333%;
}
.col-xs-9 {
  width: 75%;
}
.col-xs-8 {
  width: 66.66666667%;
}
.col-xs-7 {
  width: 58.33333333%;
}
.col-xs-6 {
  width: 50%;
}
.col-xs-5 {
  width: 41.66666667%;
}
.col-xs-4 {
  width: 33.33333333%;
}
.col-xs-3 {
  width: 25%;
}
.col-xs-2 {
  width: 16.66666667%;
}
.col-xs-1 {
  width: 8.33333333%;
}
.col-xs-pull-12 {
  right: 100%;
}
.col-xs-pull-11 {
  right: 91.66666667%;
}
.col-xs-pull-10 {
  right: 83.33333333%;
}
.col-xs-pull-9 {
  right: 75%;
}
.col-xs-pull-8 {
  right: 66.66666667%;
}
.col-xs-pull-7 {
  right: 58.33333333%;
}
.col-xs-pull-6 {
  right: 50%;
}
.col-xs-pull-5 {
  right: 41.66666667%;
}
.col-xs-pull-4 {
  right: 33.33333333%;
}
.col-xs-pull-3 {
  right: 25%;
}
.col-xs-pull-2 {
  right: 16.66666667%;
}
.col-xs-pull-1 {
  right: 8.33333333%;
}
.col-xs-pull-0 {
  right: auto;
}
.col-xs-push-12 {
  left: 100%;
}
.col-xs-push-11 {
  left: 91.66666667%;
}
.col-xs-push-10 {
  left: 83.33333333%;
}
.col-xs-push-9 {
  left: 75%;
}
.col-xs-push-8 {
  left: 66.66666667%;
}
.col-xs-push-7 {
  left: 58.33333333%;
}
.col-xs-push-6 {
  left: 50%;
}
.col-xs-push-5 {
  left: 41.66666667%;
}
.col-xs-push-4 {
  left: 33.33333333%;
}
.col-xs-push-3 {
  left: 25%;
}
.col-xs-push-2 {
  left: 16.66666667%;
}
.col-xs-push-1 {
  left: 8.33333333%;
}
.col-xs-push-0 {
  left: auto;
}
.col-xs-offset-12 {
  margin-left: 100%;
}
.col-xs-offset-11 {
  margin-left: 91.66666667%;
}
.col-xs-offset-10 {
  margin-left: 83.33333333%;
}
.col-xs-offset-9 {
  margin-left: 75%;
}
.col-xs-offset-8 {
  margin-left: 66.66666667%;
}
.col-xs-offset-7 {
  margin-left: 58.33333333%;
}
.col-xs-offset-6 {
  margin-left: 50%;
}
.col-xs-offset-5 {
  margin-left: 41.66666667%;
}
.col-xs-offset-4 {
  margin-left: 33.33333333%;
}
.col-xs-offset-3 {
  margin-left: 25%;
}
.col-xs-offset-2 {
  margin-left: 16.66666667%;
}
.col-xs-offset-1 {
  margin-left: 8.33333333%;
}
.col-xs-offset-0 {
  margin-left: 0;
}
@media (min-width: 768px) {
  .col-sm-1, .col-sm-2, .col-sm-3, .col-sm-4, .col-sm-5, .col-sm-6, .col-sm-7, .col-sm-8, .col-sm-9, .col-sm-10, .col-sm-11, .col-sm-12 {
    float: left;
  }
  .col-sm-12 {
    width: 100%;
  }
  .col-sm-11 {
    width: 91.66666667%;
  }
  .col-sm-10 {
    width: 83.33333333%;
  }
  .col-sm-9 {
    width: 75%;
  }
  .col-sm-8 {
    width: 66.66666667%;
  }
  .col-sm-7 {
    width: 58.33333333%;
  }
  .col-sm-6 {
    width: 50%;
  }
  .col-sm-5 {
    width: 41.66666667%;
  }
  .col-sm-4 {
    width: 33.33333333%;
  }
  .col-sm-3 {
    width: 25%;
  }
  .col-sm-2 {
    width: 16.66666667%;
  }
  .col-sm-1 {
    width: 8.33333333%;
  }
  .col-sm-pull-12 {
    right: 100%;
  }
  .col-sm-pull-11 {
    right: 91.66666667%;
  }
  .col-sm-pull-10 {
    right: 83.33333333%;
  }
  .col-sm-pull-9 {
    right: 75%;
  }
  .col-sm-pull-8 {
    right: 66.66666667%;
  }
  .col-sm-pull-7 {
    right: 58.33333333%;
  }
  .col-sm-pull-6 {
    right: 50%;
  }
  .col-sm-pull-5 {
    right: 41.66666667%;
  }
  .col-sm-pull-4 {
    right: 33.33333333%;
  }
  .col-sm-pull-3 {
    right: 25%;
  }
  .col-sm-pull-2 {
    right: 16.66666667%;
  }
  .col-sm-pull-1 {
    right: 8.33333333%;
  }
  .col-sm-pull-0 {
    right: auto;
  }
  .col-sm-push-12 {
    left: 100%;
  }
  .col-sm-push-11 {
    left: 91.66666667%;
  }
  .col-sm-push-10 {
    left: 83.33333333%;
  }
  .col-sm-push-9 {
    left: 75%;
  }
  .col-sm-push-8 {
    left: 66.66666667%;
  }
  .col-sm-push-7 {
    left: 58.33333333%;
  }
  .col-sm-push-6 {
    left: 50%;
  }
  .col-sm-push-5 {
    left: 41.66666667%;
  }
  .col-sm-push-4 {
    left: 33.33333333%;
  }
  .col-sm-push-3 {
    left: 25%;
  }
  .col-sm-push-2 {
    left: 16.66666667%;
  }
  .col-sm-push-1 {
    left: 8.33333333%;
  }
  .col-sm-push-0 {
    left: auto;
  }
  .col-sm-offset-12 {
    margin-left: 100%;
  }
  .col-sm-offset-11 {
    margin-left: 91.66666667%;
  }
  .col-sm-offset-10 {
    margin-left: 83.33333333%;
  }
  .col-sm-offset-9 {
    margin-left: 75%;
  }
  .col-sm-offset-8 {
    margin-left: 66.66666667%;
  }
  .col-sm-offset-7 {
    margin-left: 58.33333333%;
  }
  .col-sm-offset-6 {
    margin-left: 50%;
  }
  .col-sm-offset-5 {
    margin-left: 41.66666667%;
  }
  .col-sm-offset-4 {
    margin-left: 33.33333333%;
  }
  .col-sm-offset-3 {
    margin-left: 25%;
  }
  .col-sm-offset-2 {
    margin-left: 16.66666667%;
  }
  .col-sm-offset-1 {
    margin-left: 8.33333333%;
  }
  .col-sm-offset-0 {
    margin-left: 0;
  }
}
@media (min-width: 992px) {
  .col-md-1, .col-md-2, .col-md-3, .col-md-4, .col-md-5, .col-md-6, .col-md-7, .col-md-8, .col-md-9, .col-md-10, .col-md-11, .col-md-12 {
    float: left;
  }
  .col-md-12 {
    width: 100%;
  }
  .col-md-11 {
    width: 91.66666667%;
  }
  .col-md-10 {
    width: 83.33333333%;
  }
  .col-md-9 {
    width: 75%;
  }
  .col-md-8 {
    width: 66.66666667%;
  }
  .col-md-7 {
    width: 58.33333333%;
  }
  .col-md-6 {
    width: 50%;
  }
  .col-md-5 {
    width: 41.66666667%;
  }
  .col-md-4 {
    width: 33.33333333%;
  }
  .col-md-3 {
    width: 25%;
  }
  .col-md-2 {
    width: 16.66666667%;
  }
  .col-md-1 {
    width: 8.33333333%;
  }
  .col-md-pull-12 {
    right: 100%;
  }
  .col-md-pull-11 {
    right: 91.66666667%;
  }
  .col-md-pull-10 {
    right: 83.33333333%;
  }
  .col-md-pull-9 {
    right: 75%;
  }
  .col-md-pull-8 {
    right: 66.66666667%;
  }
  .col-md-pull-7 {
    right: 58.33333333%;
  }
  .col-md-pull-6 {
    right: 50%;
  }
  .col-md-pull-5 {
    right: 41.66666667%;
  }
  .col-md-pull-4 {
    right: 33.33333333%;
  }
  .col-md-pull-3 {
    right: 25%;
  }
  .col-md-pull-2 {
    right: 16.66666667%;
  }
  .col-md-pull-1 {
    right: 8.33333333%;
  }
  .col-md-pull-0 {
    right: auto;
  }
  .col-md-push-12 {
    left: 100%;
  }
  .col-md-push-11 {
    left: 91.66666667%;
  }
  .col-md-push-10 {
    left: 83.33333333%;
  }
  .col-md-push-9 {
    left: 75%;
  }
  .col-md-push-8 {
    left: 66.66666667%;
  }
  .col-md-push-7 {
    left: 58.33333333%;
  }
  .col-md-push-6 {
    left: 50%;
  }
  .col-md-push-5 {
    left: 41.66666667%;
  }
  .col-md-push-4 {
    left: 33.33333333%;
  }
  .col-md-push-3 {
    left: 25%;
  }
  .col-md-push-2 {
    left: 16.66666667%;
  }
  .col-md-push-1 {
    left: 8.33333333%;
  }
  .col-md-push-0 {
    left: auto;
  }
  .col-md-offset-12 {
    margin-left: 100%;
  }
  .col-md-offset-11 {
    margin-left: 91.66666667%;
  }
  .col-md-offset-10 {
    margin-left: 83.33333333%;
  }
  .col-md-offset-9 {
    margin-left: 75%;
  }
  .col-md-offset-8 {
    margin-left: 66.66666667%;
  }
  .col-md-offset-7 {
    margin-left: 58.33333333%;
  }
  .col-md-offset-6 {
    margin-left: 50%;
  }
  .col-md-offset-5 {
    margin-left: 41.66666667%;
  }
  .col-md-offset-4 {
    margin-left: 33.33333333%;
  }
  .col-md-offset-3 {
    margin-left: 25%;
  }
  .col-md-offset-2 {
    margin-left: 16.66666667%;
  }
  .col-md-offset-1 {
    margin-left: 8.33333333%;
  }
  .col-md-offset-0 {
    margin-left: 0;
  }
}
@media (min-width: 1200px) {
  .col-lg-1, .col-lg-2, .col-lg-3, .col-lg-4, .col-lg-5, .col-lg-6, .col-lg-7, .col-lg-8, .col-lg-9, .col-lg-10, .col-lg-11, .col-lg-12 {
    float: left;
  }
  .col-lg-12 {
    width: 100%;
  }
  .col-lg-11 {
    width: 91.66666667%;
  }
  .col-lg-10 {
    width: 83.33333333%;
  }
  .col-lg-9 {
    width: 75%;
  }
  .col-lg-8 {
    width: 66.66666667%;
  }
  .col-lg-7 {
    width: 58.33333333%;
  }
  .col-lg-6 {
    width: 50%;
  }
  .col-lg-5 {
    width: 41.66666667%;
  }
  .col-lg-4 {
    width: 33.33333333%;
  }
  .col-lg-3 {
    width: 25%;
  }
  .col-lg-2 {
    width: 16.66666667%;
  }
  .col-lg-1 {
    width: 8.33333333%;
  }
  .col-lg-pull-12 {
    right: 100%;
  }
  .col-lg-pull-11 {
    right: 91.66666667%;
  }
  .col-lg-pull-10 {
    right: 83.33333333%;
  }
  .col-lg-pull-9 {
    right: 75%;
  }
  .col-lg-pull-8 {
    right: 66.66666667%;
  }
  .col-lg-pull-7 {
    right: 58.33333333%;
  }
  .col-lg-pull-6 {
    right: 50%;
  }
  .col-lg-pull-5 {
    right: 41.66666667%;
  }
  .col-lg-pull-4 {
    right: 33.33333333%;
  }
  .col-lg-pull-3 {
    right: 25%;
  }
  .col-lg-pull-2 {
    right: 16.66666667%;
  }
  .col-lg-pull-1 {
    right: 8.33333333%;
  }
  .col-lg-pull-0 {
    right: auto;
  }
  .col-lg-push-12 {
    left: 100%;
  }
  .col-lg-push-11 {
    left: 91.66666667%;
  }
  .col-lg-push-10 {
    left: 83.33333333%;
  }
  .col-lg-push-9 {
    left: 75%;
  }
  .col-lg-push-8 {
    left: 66.66666667%;
  }
  .col-lg-push-7 {
    left: 58.33333333%;
  }
  .col-lg-push-6 {
    left: 50%;
  }
  .col-lg-push-5 {
    left: 41.66666667%;
  }
  .col-lg-push-4 {
    left: 33.33333333%;
  }
  .col-lg-push-3 {
    left: 25%;
  }
  .col-lg-push-2 {
    left: 16.66666667%;
  }
  .col-lg-push-1 {
    left: 8.33333333%;
  }
  .col-lg-push-0 {
    left: auto;
  }
  .col-lg-offset-12 {
    margin-left: 100%;
  }
  .col-lg-offset-11 {
    margin-left: 91.66666667%;
  }
  .col-lg-offset-10 {
    margin-left: 83.33333333%;
  }
  .col-lg-offset-9 {
    margin-left: 75%;
  }
  .col-lg-offset-8 {
    margin-left: 66.66666667%;
  }
  .col-lg-offset-7 {
    margin-left: 58.33333333%;
  }
  .col-lg-offset-6 {
    margin-left: 50%;
  }
  .col-lg-offset-5 {
    margin-left: 41.66666667%;
  }
  .col-lg-offset-4 {
    margin-left: 33.33333333%;
  }
  .col-lg-offset-3 {
    margin-left: 25%;
  }
  .col-lg-offset-2 {
    margin-left: 16.66666667%;
  }
  .col-lg-offset-1 {
    margin-left: 8.33333333%;
  }
  .col-lg-offset-0 {
    margin-left: 0;
  }
}
table {
  background-color: transparent;
}
caption {
  padding-top: 8px;
  padding-bottom: 8px;
  color: #777;
  text-align: left;
}
th {
  text-align: left;
}
.table {
  width: 100%;
  max-width: 100%;
  margin-bottom: 20px;
}
.table > thead > tr > th,
.table > tbody > tr > th,
.table > tfoot > tr > th,
.table > thead > tr > td,
.table > tbody > tr > td,
.table > tfoot > tr > td {
  padding: 8px;
  line-height: 1.42857143;
  vertical-align: top;
  border-top: 1px solid #ddd;
}
.table > thead > tr > th {
  vertical-align: bottom;
  border-bottom: 2px solid #ddd;
}
.table > caption + thead > tr:first-child > th,
.table > colgroup + thead > tr:first-child > th,
.table > thead:first-child > tr:first-child > th,
.table > caption + thead > tr:first-child > td,
.table > colgroup + thead > tr:first-child > td,
.table > thead:first-child > tr:first-child > td {
  border-top: 0;
}
.table > tbody + tbody {
  border-top: 2px solid #ddd;
}
.table .table {
  background-color: #fff;
}
.table-condensed > thead > tr > th,
.table-condensed > tbody > tr > th,
.table-condensed > tfoot > tr > th,
.table-condensed > thead > tr > td,
.table-condensed > tbody > tr > td,
.table-condensed > tfoot > tr > td {
  padding: 5px;
}
.table-bordered {
  border: 1px solid #ddd;
}
.table-bordered > thead > tr > th,
.table-bordered > tbody > tr > th,
.table-bordered > tfoot > tr > th,
.table-bordered > thead > tr > td,
.table-bordered > tbody > tr > td,
.table-bordered > tfoot > tr > td {
  border: 1px solid #ddd;
}
.table-bordered > thead > tr > th,
.table-bordered > thead > tr > td {
  border-bottom-width: 2px;
}
.table-striped > tbody > tr:nth-of-type(odd) {
  background-color: #f9f9f9;
}
.table-hover > tbody > tr:hover {
  background-color: #f5f5f5;
}
table col[class*="col-"] {
  position: static;
  display: table-column;
  float: none;
}
table td[class*="col-"],
table th[class*="col-"] {
  position: static;
  display: table-cell;
  float: none;
}
.table > thead > tr > td.active,
.table > tbody > tr > td.active,
.table > tfoot > tr > td.active,
.table > thead > tr > th.active,
.table > tbody > tr > th.active,
.table > tfoot > tr > th.active,
.table > thead > tr.active > td,
.table > tbody > tr.active > td,
.table > tfoot > tr.active > td,
.table > thead > tr.active > th,
.table > tbody > tr.active > th,
.table > tfoot > tr.active > th {
  background-color: #f5f5f5;
}
.table-hover > tbody > tr > td.active:hover,
.table-hover > tbody > tr > th.active:hover,
.table-hover > tbody > tr.active:hover > td,
.table-hover > tbody > tr:hover > .active,
.table-hover > tbody > tr.active:hover > th {
  background-color: #e8e8e8;
}
.table > thead > tr > td.success,
.table > tbody > tr > td.success,
.table > tfoot > tr > td.success,
.table > thead > tr > th.success,
.table > tbody > tr > th.success,
.table > tfoot > tr > th.success,
.table > thead > tr.success > td,
.table > tbody > tr.success > td,
.table > tfoot > tr.success > td,
.table > thead > tr.success > th,
.table > tbody > tr.success > th,
.table > tfoot > tr.success > th {
  background-color: #dff0d8;
}
.table-hover > tbody > tr > td.success:hover,
.table-hover > tbody > tr > th.success:hover,
.table-hover > tbody > tr.success:hover > td,
.table-hover > tbody > tr:hover > .success,
.table-hover > tbody > tr.success:hover > th {
  background-color: #d0e9c6;
}
.table > thead > tr > td.info,
.table > tbody > tr > td.info,
.table > tfoot > tr > td.info,
.table > thead > tr > th.info,
.table > tbody > tr > th.info,
.table > tfoot > tr > th.info,
.table > thead > tr.info > td,
.table > tbody > tr.info > td,
.table > tfoot > tr.info > td,
.table > thead > tr.info > th,
.table > tbody > tr.info > th,
.table > tfoot > tr.info > th {
  background-color: #d9edf7;
}
.table-hover > tbody > tr > td.info:hover,
.table-hover > tbody > tr > th.info:hover,
.table-hover > tbody > tr.info:hover > td,
.table-hover > tbody > tr:hover > .info,
.table-hover > tbody > tr.info:hover > th {
  background-color: #c4e3f3;
}
.table > thead > tr > td.warning,
.table > tbody > tr > td.warning,
.table > tfoot > tr > td.warning,
.table > thead > tr > th.warning,
.table > tbody > tr > th.warning
Download .txt
gitextract_4juf6qor/

├── .gitignore
├── README.md
├── dis-seckill-cache/
│   ├── pom.xml
│   └── src/
│       └── main/
│           ├── java/
│           │   └── com/
│           │       └── seckill/
│           │           └── dis/
│           │               └── cache/
│           │                   ├── CacheApplication.java
│           │                   ├── config/
│           │                   │   ├── RedisConfig.java
│           │                   │   └── RedisPoolFactory.java
│           │                   └── service/
│           │                       ├── RedisLockImpl.java
│           │                       └── RedisServiceImpl.java
│           └── resources/
│               └── application.properties
├── dis-seckill-common/
│   ├── pom.xml
│   ├── schema/
│   │   └── seckill.sql
│   └── src/
│       └── main/
│           └── java/
│               └── com/
│                   └── seckill/
│                       └── dis/
│                           └── common/
│                               ├── api/
│                               │   ├── cache/
│                               │   │   ├── DLockApi.java
│                               │   │   ├── RedisServiceApi.java
│                               │   │   └── vo/
│                               │   │       ├── AccessKeyPrefix.java
│                               │   │       ├── BaseKeyPrefix.java
│                               │   │       ├── GoodsKeyPrefix.java
│                               │   │       ├── KeyPrefix.java
│                               │   │       ├── OrderKeyPrefix.java
│                               │   │       ├── SkKeyPrefix.java
│                               │   │       ├── SkUserKeyPrefix.java
│                               │   │       └── UserKey.java
│                               │   ├── goods/
│                               │   │   ├── GoodsServiceApi.java
│                               │   │   └── vo/
│                               │   │       ├── GoodsDetailVo.java
│                               │   │       └── GoodsVo.java
│                               │   ├── mq/
│                               │   │   ├── MqProviderApi.java
│                               │   │   └── vo/
│                               │   │       └── SkMessage.java
│                               │   ├── order/
│                               │   │   ├── OrderServiceApi.java
│                               │   │   └── vo/
│                               │   │       └── OrderDetailVo.java
│                               │   ├── seckill/
│                               │   │   ├── SeckillServiceApi.java
│                               │   │   └── vo/
│                               │   │       └── VerifyCodeVo.java
│                               │   └── user/
│                               │       ├── UserServiceApi.java
│                               │       └── vo/
│                               │           ├── LoginVo.java
│                               │           ├── RegisterVo.java
│                               │           ├── UserInfoVo.java
│                               │           └── UserVo.java
│                               ├── domain/
│                               │   ├── Goods.java
│                               │   ├── OrderInfo.java
│                               │   ├── SeckillGoods.java
│                               │   ├── SeckillOrder.java
│                               │   └── SeckillUser.java
│                               ├── exception/
│                               │   ├── GlobalException.java
│                               │   └── GlobalExceptionHandler.java
│                               ├── result/
│                               │   ├── CodeMsg.java
│                               │   └── Result.java
│                               ├── util/
│                               │   ├── DBUtil.java
│                               │   ├── JsonUtil.java
│                               │   ├── MD5Util.java
│                               │   ├── UUIDUtil.java
│                               │   ├── ValidatorUtil.java
│                               │   └── VerifyCodeUtil.java
│                               └── validator/
│                                   ├── IsMobile.java
│                                   └── IsMobileValidator.java
├── dis-seckill-gateway/
│   ├── pom.xml
│   └── src/
│       └── main/
│           ├── java/
│           │   └── com/
│           │       └── seckill/
│           │           └── dis/
│           │               └── gateway/
│           │                   ├── DisSeckillServletInitializer.java
│           │                   ├── GatewayApplication.java
│           │                   ├── config/
│           │                   │   ├── WebConfig.java
│           │                   │   ├── access/
│           │                   │   │   ├── AccessInterceptor.java
│           │                   │   │   ├── AccessLimit.java
│           │                   │   │   └── UserContext.java
│           │                   │   └── resolver/
│           │                   │       └── UserArgumentResolver.java
│           │                   ├── exception/
│           │                   │   ├── GlobalException.java
│           │                   │   └── GlobalExceptionHandler.java
│           │                   ├── goods/
│           │                   │   └── GoodsController.java
│           │                   ├── order/
│           │                   │   └── OrderController.java
│           │                   ├── seckill/
│           │                   │   └── SeckillController.java
│           │                   └── user/
│           │                       └── UserController.java
│           └── resources/
│               ├── application.properties
│               ├── static/
│               │   ├── bootstrap/
│               │   │   ├── css/
│               │   │   │   ├── bootstrap-theme.css
│               │   │   │   └── bootstrap.css
│               │   │   └── js/
│               │   │       ├── bootstrap.js
│               │   │       └── npm.js
│               │   ├── goods_detail.htm
│               │   ├── js/
│               │   │   └── common.js
│               │   ├── layer/
│               │   │   ├── layer.js
│               │   │   ├── mobile/
│               │   │   │   ├── layer.js
│               │   │   │   └── need/
│               │   │   │       └── layer.css
│               │   │   └── skin/
│               │   │       └── default/
│               │   │           └── layer.css
│               │   └── order_detail.htm
│               └── templates/
│                   ├── goods_detail.html
│                   ├── goods_list.html
│                   ├── login.html
│                   ├── order_detail.html
│                   └── register.html
├── dis-seckill-goods/
│   ├── pom.xml
│   └── src/
│       └── main/
│           ├── java/
│           │   └── com/
│           │       └── seckill/
│           │           └── dis/
│           │               └── goods/
│           │                   ├── GoodsApplication.java
│           │                   ├── persistence/
│           │                   │   └── GoodsMapper.java
│           │                   └── service/
│           │                       ├── GoodsServiceImpl.java
│           │                       └── SeckillServiceImpl.java
│           └── resources/
│               └── application.properties
├── dis-seckill-mq/
│   ├── pom.xml
│   └── src/
│       └── main/
│           ├── java/
│           │   └── com/
│           │       └── seckill/
│           │           └── dis/
│           │               └── mq/
│           │                   ├── MqApplication.java
│           │                   ├── config/
│           │                   │   └── MQConfig.java
│           │                   ├── receiver/
│           │                   │   └── MqConsumer.java
│           │                   └── service/
│           │                       └── MqProviderImpl.java
│           └── resources/
│               └── application.properties
├── dis-seckill-order/
│   ├── pom.xml
│   └── src/
│       └── main/
│           ├── java/
│           │   └── com/
│           │       └── seckill/
│           │           └── dis/
│           │               └── order/
│           │                   ├── OrderApplication.java
│           │                   ├── persistence/
│           │                   │   └── OrderMapper.java
│           │                   └── service/
│           │                       └── OrderServiceImpl.java
│           └── resources/
│               └── application.properties
├── dis-seckill-user/
│   ├── pom.xml
│   └── src/
│       └── main/
│           ├── java/
│           │   └── com/
│           │       └── seckill/
│           │           └── dis/
│           │               └── user/
│           │                   ├── UserApplication.java
│           │                   ├── domain/
│           │                   │   └── SeckillUser.java
│           │                   ├── persistence/
│           │                   │   ├── SeckillUserMapper.java
│           │                   │   └── SeckillUserMapper.xml
│           │                   ├── service/
│           │                   │   └── UserServiceImpl.java
│           │                   └── util/
│           │                       └── UserUtil.java
│           └── resources/
│               └── application.properties
├── doc/
│   ├── HandlerInterceptor的使用.md
│   ├── README.md
│   ├── Redis中存储的数据.md
│   ├── 使用分布式锁解决恶意用户重复注册问题.md
│   ├── 前后端交互接口定义.md
│   └── 前后端交互接口逻辑实现.md
└── pom.xml
Download .txt
SYMBOL INDEX (468 symbols across 77 files)

FILE: dis-seckill-cache/src/main/java/com/seckill/dis/cache/CacheApplication.java
  class CacheApplication (line 10) | @SpringBootApplication
    method main (line 12) | public static void main(String[] args) {

FILE: dis-seckill-cache/src/main/java/com/seckill/dis/cache/config/RedisConfig.java
  class RedisConfig (line 7) | @Component
    method getHost (line 20) | public String getHost() {
    method setHost (line 24) | public void setHost(String host) {
    method getPort (line 28) | public int getPort() {
    method setPort (line 32) | public void setPort(int port) {
    method getTimeout (line 36) | public int getTimeout() {
    method setTimeout (line 40) | public void setTimeout(int timeout) {
    method getPoolMaxTotal (line 44) | public int getPoolMaxTotal() {
    method setPoolMaxTotal (line 48) | public void setPoolMaxTotal(int poolMaxTotal) {
    method getPoolMaxIdle (line 52) | public int getPoolMaxIdle() {
    method setPoolMaxIdle (line 56) | public void setPoolMaxIdle(int poolMaxIdle) {
    method getPoolMaxWait (line 60) | public int getPoolMaxWait() {
    method setPoolMaxWait (line 64) | public void setPoolMaxWait(int poolMaxWait) {

FILE: dis-seckill-cache/src/main/java/com/seckill/dis/cache/config/RedisPoolFactory.java
  class RedisPoolFactory (line 13) | @Component
    method JedisPoolFactory (line 19) | @Bean

FILE: dis-seckill-cache/src/main/java/com/seckill/dis/cache/service/RedisLockImpl.java
  class RedisLockImpl (line 16) | @Service(interfaceClass = DLockApi.class)
    method lock (line 39) | public boolean lock(String lockKey, String uniqueValue, int expireTime) {
    method unlock (line 65) | public boolean unlock(String lockKey, String uniqueValue) {

FILE: dis-seckill-cache/src/main/java/com/seckill/dis/cache/service/RedisServiceImpl.java
  class RedisServiceImpl (line 16) | @Service(interfaceClass = RedisServiceApi.class)
    method get (line 25) | @Override
    method set (line 44) | @Override
    method exists (line 73) | @Override
    method incr (line 85) | @Override
    method decr (line 97) | @Override
    method delete (line 109) | @Override
    method beanToString (line 129) | public static <T> String beanToString(T value) {
    method stringToBean (line 154) | public static <T> T stringToBean(String strValue, Class<T> clazz) {
    method returnToPool (line 178) | private void returnToPool(Jedis jedis) {

FILE: dis-seckill-common/schema/seckill.sql
  type `seckill_user` (line 20) | CREATE TABLE `seckill_user` (
  type `goods` (line 45) | CREATE TABLE `goods` (
  type `seckill_goods` (line 70) | CREATE TABLE `seckill_goods` (
  type `seckill_order` (line 94) | CREATE TABLE `seckill_order` (
  type `order_info` (line 110) | CREATE TABLE `order_info` (

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/DLockApi.java
  type DLockApi (line 8) | public interface DLockApi {
    method lock (line 17) | boolean lock(String lockKey, String uniqueValue, int expireTime);
    method unlock (line 26) | boolean unlock(String lockKey, String uniqueValue);

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/RedisServiceApi.java
  type RedisServiceApi (line 10) | public interface RedisServiceApi {
    method get (line 22) | <T> T get(KeyPrefix prefix, String key, Class<T> clazz);
    method set (line 32) | <T> boolean set(KeyPrefix prefix, String key, T value);
    method exists (line 41) | boolean exists(KeyPrefix keyPrefix, String key);
    method incr (line 50) | long incr(KeyPrefix keyPrefix, String key);
    method decr (line 59) | long decr(KeyPrefix keyPrefix, String key);
    method delete (line 69) | boolean delete(KeyPrefix prefix, String key);

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/AccessKeyPrefix.java
  class AccessKeyPrefix (line 10) | public class AccessKeyPrefix extends BaseKeyPrefix implements Serializable{
    method AccessKeyPrefix (line 11) | public AccessKeyPrefix(String prefix) {
    method AccessKeyPrefix (line 15) | public AccessKeyPrefix(int expireSeconds, String prefix) {
    method withExpire (line 20) | public static AccessKeyPrefix withExpire(int expireSeconds) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/BaseKeyPrefix.java
  class BaseKeyPrefix (line 9) | public abstract class BaseKeyPrefix implements KeyPrefix {
    method BaseKeyPrefix (line 27) | public BaseKeyPrefix(String prefix) {
    method BaseKeyPrefix (line 32) | public BaseKeyPrefix(int expireSeconds, String prefix) {
    method expireSeconds (line 42) | @Override
    method getPrefix (line 52) | @Override

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/GoodsKeyPrefix.java
  class GoodsKeyPrefix (line 11) | public class GoodsKeyPrefix extends BaseKeyPrefix  implements Serializab...
    method GoodsKeyPrefix (line 12) | public GoodsKeyPrefix(int expireSeconds, String prefix) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/KeyPrefix.java
  type KeyPrefix (line 9) | public interface KeyPrefix {
    method expireSeconds (line 16) | int expireSeconds();
    method getPrefix (line 23) | String getPrefix();

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/OrderKeyPrefix.java
  class OrderKeyPrefix (line 10) | public class OrderKeyPrefix extends BaseKeyPrefix  implements Serializab...
    method OrderKeyPrefix (line 12) | public OrderKeyPrefix(int expireSeconds, String prefix) {
    method OrderKeyPrefix (line 16) | public OrderKeyPrefix(String prefix) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/SkKeyPrefix.java
  class SkKeyPrefix (line 9) | public class SkKeyPrefix extends BaseKeyPrefix implements Serializable {
    method SkKeyPrefix (line 10) | public SkKeyPrefix(String prefix) {
    method SkKeyPrefix (line 14) | public SkKeyPrefix(int expireSeconds, String prefix) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/SkUserKeyPrefix.java
  class SkUserKeyPrefix (line 9) | public class SkUserKeyPrefix extends BaseKeyPrefix  implements Serializa...
    method SkUserKeyPrefix (line 13) | public SkUserKeyPrefix(int expireSeconds, String prefix) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/UserKey.java
  class UserKey (line 8) | public class UserKey extends BaseKeyPrefix  implements Serializable {
    method UserKey (line 10) | public UserKey(String prefix) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/goods/GoodsServiceApi.java
  type GoodsServiceApi (line 12) | public interface GoodsServiceApi {
    method listGoodsVo (line 19) | List<GoodsVo> listGoodsVo();
    method getGoodsVoByGoodsId (line 27) | GoodsVo getGoodsVoByGoodsId(long goodsId);
    method getGoodsVoByGoodsId (line 35) | GoodsVo getGoodsVoByGoodsId(Long goodsId);
    method reduceStock (line 42) | boolean reduceStock(GoodsVo goods);

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/goods/vo/GoodsDetailVo.java
  class GoodsDetailVo (line 13) | public class GoodsDetailVo implements Serializable {
    method toString (line 21) | @Override
    method getSeckillStatus (line 31) | public int getSeckillStatus() {
    method setSeckillStatus (line 35) | public void setSeckillStatus(int seckillStatus) {
    method getRemainSeconds (line 39) | public int getRemainSeconds() {
    method setRemainSeconds (line 43) | public void setRemainSeconds(int remainSeconds) {
    method getGoods (line 47) | public GoodsVo getGoods() {
    method setGoods (line 51) | public void setGoods(GoodsVo goods) {
    method getUser (line 55) | public UserVo getUser() {
    method setUser (line 59) | public void setUser(UserVo user) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/goods/vo/GoodsVo.java
  class GoodsVo (line 15) | public class GoodsVo extends Goods implements Serializable {
    method getSeckillPrice (line 23) | public Double getSeckillPrice() {
    method setSeckillPrice (line 27) | public void setSeckillPrice(Double seckillPrice) {
    method getStockCount (line 31) | public Integer getStockCount() {
    method setStockCount (line 35) | public void setStockCount(Integer stockCount) {
    method getStartDate (line 39) | public Date getStartDate() {
    method setStartDate (line 43) | public void setStartDate(Date startDate) {
    method getEndDate (line 47) | public Date getEndDate() {
    method setEndDate (line 51) | public void setEndDate(Date endDate) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/mq/MqProviderApi.java
  type MqProviderApi (line 10) | public interface MqProviderApi {
    method sendSkMessage (line 17) | void sendSkMessage(SkMessage message);

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/mq/vo/SkMessage.java
  class SkMessage (line 13) | public class SkMessage implements Serializable{
    method getUser (line 20) | public UserVo getUser() {
    method setUser (line 24) | public void setUser(UserVo user) {
    method getGoodsId (line 28) | public long getGoodsId() {
    method setGoodsId (line 32) | public void setGoodsId(long goodsId) {
    method toString (line 36) | @Override

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/order/OrderServiceApi.java
  type OrderServiceApi (line 13) | public interface OrderServiceApi {
    method getOrderById (line 20) | OrderInfo getOrderById(long orderId);
    method getSeckillOrderByUserIdAndGoodsId (line 29) | SeckillOrder getSeckillOrderByUserIdAndGoodsId(long userId, long goods...
    method createOrder (line 38) | OrderInfo createOrder(UserVo user, GoodsVo goods);

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/order/vo/OrderDetailVo.java
  class OrderDetailVo (line 14) | public class OrderDetailVo {
    method getUser (line 30) | public UserVo getUser() {
    method setUser (line 34) | public void setUser(UserVo user) {
    method getGoods (line 38) | public GoodsVo getGoods() {
    method setGoods (line 42) | public void setGoods(GoodsVo goods) {
    method getOrder (line 46) | public OrderInfo getOrder() {
    method setOrder (line 50) | public void setOrder(OrderInfo order) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/seckill/SeckillServiceApi.java
  type SeckillServiceApi (line 12) | public interface SeckillServiceApi {
    method createVerifyCode (line 20) | String createVerifyCode(UserVo user, long goodsId);
    method seckill (line 31) | OrderInfo seckill(UserVo user, GoodsVo goods);
    method getSeckillResult (line 40) | long getSeckillResult(Long userId, long goodsId);

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/seckill/vo/VerifyCodeVo.java
  class VerifyCodeVo (line 11) | public class VerifyCodeVo implements Serializable {
    method VerifyCodeVo (line 23) | public VerifyCodeVo() {
    method VerifyCodeVo (line 26) | public VerifyCodeVo(BufferedImage image, int expResult) {
    method getImage (line 31) | public BufferedImage getImage() {
    method setImage (line 35) | public void setImage(BufferedImage image) {
    method getExpResult (line 39) | public int getExpResult() {
    method setExpResult (line 43) | public void setExpResult(int expResult) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/UserServiceApi.java
  type UserServiceApi (line 17) | public interface UserServiceApi {
    method login (line 31) | int login(String username, String password);
    method register (line 39) | CodeMsg register(RegisterVo userModel);
    method checkUsername (line 47) | boolean checkUsername(String username);
    method getUserInfo (line 55) | UserInfoVo getUserInfo(int uuid);
    method updateUserInfo (line 63) | UserInfoVo updateUserInfo(UserInfoVo userInfoVo);
    method login (line 71) | String login(@Valid LoginVo loginVo);
    method getUserByPhone (line 79) | UserVo getUserByPhone(long phone);

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/vo/LoginVo.java
  class LoginVo (line 15) | public class LoginVo implements Serializable{
    method getMobile (line 31) | public String getMobile() {
    method setMobile (line 35) | public void setMobile(String mobile) {
    method getPassword (line 39) | public String getPassword() {
    method setPassword (line 43) | public void setPassword(String password) {
    method toString (line 47) | @Override

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/vo/RegisterVo.java
  class RegisterVo (line 11) | public class RegisterVo implements Serializable {
    method getPhone (line 22) | public Long getPhone() {
    method setPhone (line 26) | public void setPhone(Long phone) {
    method getNickname (line 30) | public String getNickname() {
    method setNickname (line 34) | public void setNickname(String nickname) {
    method getHead (line 38) | public String getHead() {
    method setHead (line 42) | public void setHead(String head) {
    method getPassword (line 46) | public String getPassword() {
    method setPassword (line 50) | public void setPassword(String password) {
    method toString (line 54) | @Override

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/vo/UserInfoVo.java
  class UserInfoVo (line 12) | public class UserInfoVo implements Serializable {
    method getUuid (line 28) | public Integer getUuid() {
    method setUuid (line 32) | public void setUuid(Integer uuid) {
    method getUsername (line 36) | public String getUsername() {
    method setUsername (line 40) | public void setUsername(String username) {
    method getNickname (line 44) | public String getNickname() {
    method setNickname (line 48) | public void setNickname(String nickname) {
    method getEmail (line 52) | public String getEmail() {
    method setEmail (line 56) | public void setEmail(String email) {
    method getPhone (line 60) | public String getPhone() {
    method setPhone (line 64) | public void setPhone(String phone) {
    method getSex (line 68) | public int getSex() {
    method setSex (line 72) | public void setSex(int sex) {
    method getBirthday (line 76) | public String getBirthday() {
    method setBirthday (line 80) | public void setBirthday(String birthday) {
    method getLifeState (line 84) | public String getLifeState() {
    method setLifeState (line 88) | public void setLifeState(String lifeState) {
    method getBiography (line 92) | public String getBiography() {
    method setBiography (line 96) | public void setBiography(String biography) {
    method getAddress (line 100) | public String getAddress() {
    method setAddress (line 104) | public void setAddress(String address) {
    method getHeadAddress (line 108) | public String getHeadAddress() {
    method setHeadAddress (line 112) | public void setHeadAddress(String headAddress) {
    method getBeginTime (line 116) | public long getBeginTime() {
    method setBeginTime (line 120) | public void setBeginTime(long beginTime) {
    method getUpdateTime (line 124) | public long getUpdateTime() {
    method setUpdateTime (line 128) | public void setUpdateTime(long updateTime) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/vo/UserVo.java
  class UserVo (line 13) | public class UserVo implements Serializable {
    method toString (line 25) | @Override
    method getUuid (line 40) | public Long getUuid() {
    method setUuid (line 44) | public void setUuid(Long uuid) {
    method getPhone (line 48) | public Long getPhone() {
    method setPhone (line 52) | public void setPhone(Long phone) {
    method getNickname (line 56) | public String getNickname() {
    method setNickname (line 60) | public void setNickname(String nickname) {
    method getPassword (line 64) | public String getPassword() {
    method setPassword (line 68) | public void setPassword(String password) {
    method getSalt (line 72) | public String getSalt() {
    method setSalt (line 76) | public void setSalt(String salt) {
    method getHead (line 80) | public String getHead() {
    method setHead (line 84) | public void setHead(String head) {
    method getRegisterDate (line 88) | public Date getRegisterDate() {
    method setRegisterDate (line 92) | public void setRegisterDate(Date registerDate) {
    method getLastLoginDate (line 96) | public Date getLastLoginDate() {
    method setLastLoginDate (line 100) | public void setLastLoginDate(Date lastLoginDate) {
    method getLoginCount (line 104) | public Integer getLoginCount() {
    method setLoginCount (line 108) | public void setLoginCount(Integer loginCount) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/domain/Goods.java
  class Goods (line 8) | public class Goods {
    method getId (line 19) | public Long getId() {
    method setId (line 23) | public void setId(Long id) {
    method getGoodsName (line 27) | public String getGoodsName() {
    method setGoodsName (line 31) | public void setGoodsName(String goodsName) {
    method getGoodsTitle (line 35) | public String getGoodsTitle() {
    method setGoodsTitle (line 39) | public void setGoodsTitle(String goodsTitle) {
    method getGoodsImg (line 43) | public String getGoodsImg() {
    method setGoodsImg (line 47) | public void setGoodsImg(String goodsImg) {
    method getGoodsDetail (line 51) | public String getGoodsDetail() {
    method setGoodsDetail (line 55) | public void setGoodsDetail(String goodsDetail) {
    method getGoodsPrice (line 59) | public Double getGoodsPrice() {
    method setGoodsPrice (line 63) | public void setGoodsPrice(Double goodsPrice) {
    method getGoodsStock (line 67) | public Long getGoodsStock() {
    method setGoodsStock (line 71) | public void setGoodsStock(Long goodsStock) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/domain/OrderInfo.java
  class OrderInfo (line 12) | public class OrderInfo implements Serializable{
    method getId (line 27) | public Long getId() {
    method setId (line 31) | public void setId(Long id) {
    method getUserId (line 35) | public Long getUserId() {
    method setUserId (line 39) | public void setUserId(Long userId) {
    method getGoodsId (line 43) | public Long getGoodsId() {
    method setGoodsId (line 47) | public void setGoodsId(Long goodsId) {
    method getDeliveryAddrId (line 51) | public Long getDeliveryAddrId() {
    method setDeliveryAddrId (line 55) | public void setDeliveryAddrId(Long deliveryAddrId) {
    method getGoodsName (line 59) | public String getGoodsName() {
    method setGoodsName (line 63) | public void setGoodsName(String goodsName) {
    method getGoodsCount (line 67) | public Integer getGoodsCount() {
    method setGoodsCount (line 71) | public void setGoodsCount(Integer goodsCount) {
    method getGoodsPrice (line 75) | public Double getGoodsPrice() {
    method setGoodsPrice (line 79) | public void setGoodsPrice(Double goodsPrice) {
    method getOrderChannel (line 83) | public Integer getOrderChannel() {
    method setOrderChannel (line 87) | public void setOrderChannel(Integer orderChannel) {
    method getStatus (line 91) | public Integer getStatus() {
    method setStatus (line 95) | public void setStatus(Integer status) {
    method getCreateDate (line 99) | public Date getCreateDate() {
    method setCreateDate (line 103) | public void setCreateDate(Date createDate) {
    method getPayDate (line 107) | public Date getPayDate() {
    method setPayDate (line 111) | public void setPayDate(Date payDate) {
    method toString (line 115) | @Override

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/domain/SeckillGoods.java
  class SeckillGoods (line 11) | public class SeckillGoods implements Serializable{
    method getId (line 20) | public Long getId() {
    method setId (line 24) | public void setId(Long id) {
    method getGoodsId (line 28) | public Long getGoodsId() {
    method setGoodsId (line 32) | public void setGoodsId(Long goodsId) {
    method getSeckillPrice (line 36) | public Double getSeckillPrice() {
    method setSeckillPrice (line 40) | public void setSeckillPrice(Double seckillPrice) {
    method getStockCount (line 44) | public Integer getStockCount() {
    method setStockCount (line 48) | public void setStockCount(Integer stockCount) {
    method getStartDate (line 52) | public Date getStartDate() {
    method setStartDate (line 56) | public void setStartDate(Date startDate) {
    method getEndDate (line 60) | public Date getEndDate() {
    method setEndDate (line 64) | public void setEndDate(Date endDate) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/domain/SeckillOrder.java
  class SeckillOrder (line 10) | public class SeckillOrder implements Serializable{
    method getId (line 18) | public Long getId() {
    method setId (line 22) | public void setId(Long id) {
    method getUserId (line 26) | public Long getUserId() {
    method setUserId (line 30) | public void setUserId(Long userId) {
    method getOrderId (line 34) | public Long getOrderId() {
    method setOrderId (line 38) | public void setOrderId(Long orderId) {
    method getGoodsId (line 42) | public Long getGoodsId() {
    method setGoodsId (line 46) | public void setGoodsId(Long goodsId) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/domain/SeckillUser.java
  class SeckillUser (line 11) | public class SeckillUser implements Serializable{
    method getUuid (line 23) | public Long getUuid() {
    method setUuid (line 27) | public void setUuid(Long uuid) {
    method getPhone (line 31) | public Long getPhone() {
    method setPhone (line 35) | public void setPhone(Long phone) {
    method getNickname (line 39) | public String getNickname() {
    method setNickname (line 43) | public void setNickname(String nickname) {
    method getPassword (line 47) | public String getPassword() {
    method setPassword (line 51) | public void setPassword(String password) {
    method getSalt (line 55) | public String getSalt() {
    method setSalt (line 59) | public void setSalt(String salt) {
    method getHead (line 63) | public String getHead() {
    method setHead (line 67) | public void setHead(String head) {
    method getRegisterDate (line 71) | public Date getRegisterDate() {
    method setRegisterDate (line 75) | public void setRegisterDate(Date registerDate) {
    method getLastLoginDate (line 79) | public Date getLastLoginDate() {
    method setLastLoginDate (line 83) | public void setLastLoginDate(Date lastLoginDate) {
    method getLoginCount (line 87) | public Integer getLoginCount() {
    method setLoginCount (line 91) | public void setLoginCount(Integer loginCount) {
    method toString (line 95) | @Override

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/exception/GlobalException.java
  class GlobalException (line 10) | public class GlobalException extends RuntimeException {
    method GlobalException (line 19) | public GlobalException(CodeMsg codeMsg) {
    method getCodeMsg (line 23) | public CodeMsg getCodeMsg() {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/exception/GlobalExceptionHandler.java
  class GlobalExceptionHandler (line 23) | @ControllerAdvice // 通过Advice可知,这个处理器实际上是一个切面
    method exceptionHandler (line 36) | @ExceptionHandler(value = Exception.class)// 这个注解用指定这个方法对何种异常处理(这里默认所有...

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/result/CodeMsg.java
  class CodeMsg (line 10) | public class CodeMsg implements Serializable {
    method CodeMsg (line 61) | public CodeMsg(int code, String msg) {
    method fillArgs (line 72) | public CodeMsg fillArgs(Object... args) {
    method getCode (line 78) | public int getCode() {
    method getMsg (line 82) | public String getMsg() {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/result/Result.java
  class Result (line 11) | public class Result<T> implements Serializable {
    method Result (line 33) | private Result(T data) {
    method Result (line 39) | private Result(CodeMsg codeMsg) {
    method getCode (line 51) | public int getCode() {
    method success (line 61) | public static <T> Result<T> success(T data) {
    method info (line 72) | public static <T> Result<T> info(CodeMsg serverError) {
    method success (line 83) | public static <T> Result<T> success(CodeMsg serverError) {
    method error (line 94) | public static <T> Result<T> error(CodeMsg serverError) {
    method getMsg (line 98) | public String getMsg() {
    method getData (line 102) | public T getData() {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/util/DBUtil.java
  class DBUtil (line 15) | public class DBUtil {
    method getConn (line 37) | public static Connection getConn() throws ClassNotFoundException, SQLE...

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/util/JsonUtil.java
  class JsonUtil (line 10) | public class JsonUtil {
    method stringToBean (line 20) | public static <T> T stringToBean(String strValue, Class<T> clazz) {
    method beanToString (line 46) | public static <T> String beanToString(T value) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/util/MD5Util.java
  class MD5Util (line 12) | public class MD5Util {
    method md5 (line 19) | public static String md5(String src) {
    method inputPassToFormPass (line 42) | public static String inputPassToFormPass(String inputPassword) {
    method formPassToDbPass (line 56) | public static String formPassToDbPass(String formPassword, String salt...
    method inputPassToDbPass (line 68) | public static String inputPassToDbPass(String inputPassword, String sa...
    method TestMD5 (line 78) | @Test

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/util/UUIDUtil.java
  class UUIDUtil (line 11) | public class UUIDUtil {
    method uuid (line 12) | public static String uuid() {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/util/ValidatorUtil.java
  class ValidatorUtil (line 13) | public class ValidatorUtil {
    method isMobile (line 29) | public static boolean isMobile(String mobile) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/util/VerifyCodeUtil.java
  class VerifyCodeUtil (line 11) | public class VerifyCodeUtil {
    method createVerifyCode (line 22) | public static VerifyCodeVo createVerifyCode() {
    method calc (line 64) | private static int calc(String exp) {
    method generateVerifyCode (line 83) | private static String generateVerifyCode(Random rdm) {

FILE: dis-seckill-common/src/main/java/com/seckill/dis/common/validator/IsMobileValidator.java
  class IsMobileValidator (line 17) | public class IsMobileValidator implements ConstraintValidator<IsMobile, ...
    method initialize (line 31) | @Override
    method isValid (line 43) | @Override

FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/DisSeckillServletInitializer.java
  class DisSeckillServletInitializer (line 12) | public class DisSeckillServletInitializer extends SpringBootServletIniti...
    method configure (line 13) | @Override

FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/GatewayApplication.java
  class GatewayApplication (line 13) | @SpringBootApplication
    method main (line 16) | public static void main(String[] args) {

FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/config/WebConfig.java
  class WebConfig (line 21) | @Configuration
    method addArgumentResolvers (line 37) | @Override
    method addInterceptors (line 49) | @Override

FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/config/access/AccessInterceptor.java
  class AccessInterceptor (line 29) | @Service
    method preHandle (line 52) | @Override
    method render (line 115) | private void render(HttpServletResponse response, CodeMsg cm) throws E...
    method getUser (line 131) | private UserVo getUser(HttpServletRequest request, HttpServletResponse...
    method getCookieValue (line 164) | private String getCookieValue(HttpServletRequest request, String cooki...
    method addCookie (line 184) | private void addCookie(HttpServletResponse response, String token, Use...

FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/config/access/UserContext.java
  class UserContext (line 12) | public class UserContext {
    method setUser (line 17) | public static void setUser(UserVo user) {
    method getUser (line 22) | public static UserVo getUser() {

FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/config/resolver/UserArgumentResolver.java
  class UserArgumentResolver (line 27) | @Service
    method supportsParameter (line 47) | @Override
    method resolveArgument (line 64) | @Override
    method getCookieValue (line 106) | private String getCookieValue(HttpServletRequest request, String cooki...
    method addCookie (line 130) | private void addCookie(HttpServletResponse response, String token, Use...

FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/exception/GlobalException.java
  class GlobalException (line 10) | public class GlobalException extends RuntimeException {
    method GlobalException (line 19) | public GlobalException(CodeMsg codeMsg) {
    method getCodeMsg (line 23) | public CodeMsg getCodeMsg() {

FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/exception/GlobalExceptionHandler.java
  class GlobalExceptionHandler (line 24) | @ControllerAdvice // 通过Advice可知,这个处理器实际上是一个切面
    method exceptionHandler (line 37) | @ExceptionHandler(value = Exception.class)// 这个注解用指定这个方法对何种异常处理(这里默认所有...

FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/goods/GoodsController.java
  class GoodsController (line 34) | @Controller
    method goodsList (line 66) | @RequestMapping(value = "goodsList", produces = "text/html")// produce...
    method getDetails (line 107) | @RequestMapping(value = "getDetails/{goodsId}")

FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/order/OrderController.java
  class OrderController (line 23) | @Controller
    method orderInfo (line 41) | @RequestMapping("detail")

FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/seckill/SeckillController.java
  class SeckillController (line 43) | @Controller
    method getSeckillPath (line 80) | @AccessLimit(seconds = 5, maxAccessCount = 5, needLogin = true)
    method doSeckill (line 128) | @RequestMapping(value = "{path}/doSeckill", method = RequestMethod.POST)
    method getSeckillResult (line 186) | @RequestMapping(value = "result", method = RequestMethod.GET)
    method getVerifyCode (line 207) | @RequestMapping(value = "verifyCode", method = RequestMethod.GET)
    method checkVerifyCode (line 250) | private boolean checkVerifyCode(UserVo user, long goodsId, int verifyC...
    method createSkPath (line 273) | public String createSkPath(UserVo user, long goodsId) {
    method checkPath (line 294) | public boolean checkPath(UserVo user, long goodsId, String path) {
    method afterPropertiesSet (line 306) | @Override

FILE: dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/user/UserController.java
  class UserController (line 29) | @Controller
    method index (line 51) | @RequestMapping(value = "index", method = RequestMethod.GET)
    method login (line 66) | @RequestMapping(value = "login", method = RequestMethod.POST)
    method doRegister (line 88) | @RequestMapping(value = "doRegister", method = RequestMethod.GET)
    method register (line 101) | @RequestMapping(value = "register", method = RequestMethod.POST)

FILE: dis-seckill-gateway/src/main/resources/static/bootstrap/js/bootstrap.js
  function transitionEnd (line 34) | function transitionEnd() {
  function removeElement (line 126) | function removeElement() {
  function Plugin (line 142) | function Plugin(option) {
  function Plugin (line 251) | function Plugin(option) {
  function Plugin (line 475) | function Plugin(option) {
  function getTargetFromTrigger (line 695) | function getTargetFromTrigger($trigger) {
  function Plugin (line 707) | function Plugin(option) {
  function getParent (line 774) | function getParent($this) {
  function clearMenus (line 787) | function clearMenus(e) {
  function Plugin (line 880) | function Plugin(option) {
  function Plugin (line 1208) | function Plugin(option, _relatedTarget) {
  function complete (line 1574) | function complete() {
  function Plugin (line 1750) | function Plugin(option) {
  function Plugin (line 1859) | function Plugin(option) {
  function ScrollSpy (line 1902) | function ScrollSpy(element, options) {
  function Plugin (line 2022) | function Plugin(option) {
  function next (line 2131) | function next() {
  function Plugin (line 2177) | function Plugin(option) {
  function Plugin (line 2334) | function Plugin(option) {

FILE: dis-seckill-gateway/src/main/resources/static/js/common.js
  function g_showLoading (line 5) | function g_showLoading() {
  function g_getQueryString (line 15) | function g_getQueryString(name) {

FILE: dis-seckill-gateway/src/main/resources/static/layer/layer.js
  function t (line 2) | function t(e){e=s.find(e),e.height(f[1]-c-u-2*(0|parseFloat(e.css("paddi...
  function e (line 2) | function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}
  function o (line 2) | function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onlo...

FILE: dis-seckill-goods/src/main/java/com/seckill/dis/goods/GoodsApplication.java
  class GoodsApplication (line 11) | @SpringBootApplication
    method main (line 13) | public static void main(String[] args) {

FILE: dis-seckill-goods/src/main/java/com/seckill/dis/goods/persistence/GoodsMapper.java
  type GoodsMapper (line 15) | @Mapper
    method listGoodsVo (line 25) | @Select("SELECT g.*, mg.stock_count, mg.start_date, mg.end_date, mg.se...
    method getGoodsVoByGoodsId (line 34) | @Select("SELECT g.*, mg.stock_count, mg.start_date, mg.end_date, mg.se...
    method reduceStack (line 44) | @Update("UPDATE seckill_goods SET stock_count = stock_count-1 WHERE go...

FILE: dis-seckill-goods/src/main/java/com/seckill/dis/goods/service/GoodsServiceImpl.java
  class GoodsServiceImpl (line 17) | @Service(interfaceClass = GoodsServiceApi.class)
    method listGoodsVo (line 23) | @Override
    method getGoodsVoByGoodsId (line 28) | @Override
    method getGoodsVoByGoodsId (line 39) | @Override
    method reduceStock (line 51) | @Override

FILE: dis-seckill-goods/src/main/java/com/seckill/dis/goods/service/SeckillServiceImpl.java
  class SeckillServiceImpl (line 32) | @Service(interfaceClass = SeckillServiceApi.class)
    method seckill (line 57) | @Transactional
    method setGoodsOver (line 81) | private void setGoodsOver(long goodsId) {
    method getGoodsOver (line 85) | private boolean getGoodsOver(long goodsId) {
    method getSeckillResult (line 96) | public long getSeckillResult(Long userId, long goodsId) {
    method createVerifyCode (line 118) | @Override
    method calc (line 166) | private int calc(String exp) {
    method generateVerifyCode (line 185) | private String generateVerifyCode(Random rdm) {

FILE: dis-seckill-mq/src/main/java/com/seckill/dis/mq/MqApplication.java
  class MqApplication (line 12) | @SpringBootApplication
    method main (line 14) | public static void main(String[] args) {

FILE: dis-seckill-mq/src/main/java/com/seckill/dis/mq/config/MQConfig.java
  class MQConfig (line 15) | @Configuration
    method seckillQueue (line 34) | @Bean
    method rabbitTemplate (line 45) | @Bean

FILE: dis-seckill-mq/src/main/java/com/seckill/dis/mq/receiver/MqConsumer.java
  class MqConsumer (line 26) | @Service
    method receiveSkInfo (line 48) | @RabbitListener(queues = MQConfig.SECKILL_QUEUE)
    method getSkOrderByUserIdAndGoodsId (line 83) | private SeckillOrder getSkOrderByUserIdAndGoodsId(Long userId, long go...

FILE: dis-seckill-mq/src/main/java/com/seckill/dis/mq/service/MqProviderImpl.java
  class MqProviderImpl (line 22) | @Service(interfaceClass = MqProviderApi.class)
    method MqProviderImpl (line 29) | @Autowired
    method sendSkMessage (line 36) | @Override
    method confirm (line 49) | @Override

FILE: dis-seckill-order/src/main/java/com/seckill/dis/order/OrderApplication.java
  class OrderApplication (line 11) | @SpringBootApplication
    method main (line 13) | public static void main(String[] args) {

FILE: dis-seckill-order/src/main/java/com/seckill/dis/order/persistence/OrderMapper.java
  type OrderMapper (line 12) | @Mapper
    method getSeckillOrderByUserIdAndGoodsId (line 22) | @Select("SELECT * FROM seckill_order WHERE user_id=#{userId} AND goods...
    method insert (line 31) | @Insert("INSERT INTO order_info (user_id, goods_id, goods_name, goods_...
    method insertSeckillOrder (line 42) | @Insert("INSERT INTO seckill_order(user_id, order_id, goods_id) VALUES...
    method getOrderById (line 51) | @Select("select * from order_info where id = #{orderId}")

FILE: dis-seckill-order/src/main/java/com/seckill/dis/order/service/OrderServiceImpl.java
  class OrderServiceImpl (line 25) | @Service(interfaceClass = OrderServiceApi.class)
    method getOrderById (line 36) | @Override
    method getSeckillOrderByUserIdAndGoodsId (line 41) | @Override
    method createOrder (line 58) | @Transactional

FILE: dis-seckill-user/src/main/java/com/seckill/dis/user/UserApplication.java
  class UserApplication (line 8) | @EnableDubbo
    method main (line 12) | public static void main(String[] args) {

FILE: dis-seckill-user/src/main/java/com/seckill/dis/user/domain/SeckillUser.java
  class SeckillUser (line 11) | public class SeckillUser implements Serializable{
    method getUuid (line 23) | public Long getUuid() {
    method setUuid (line 27) | public void setUuid(Long uuid) {
    method getPhone (line 31) | public Long getPhone() {
    method setPhone (line 35) | public void setPhone(Long phone) {
    method getNickname (line 39) | public String getNickname() {
    method setNickname (line 43) | public void setNickname(String nickname) {
    method getPassword (line 47) | public String getPassword() {
    method setPassword (line 51) | public void setPassword(String password) {
    method getSalt (line 55) | public String getSalt() {
    method setSalt (line 59) | public void setSalt(String salt) {
    method getHead (line 63) | public String getHead() {
    method setHead (line 67) | public void setHead(String head) {
    method getRegisterDate (line 71) | public Date getRegisterDate() {
    method setRegisterDate (line 75) | public void setRegisterDate(Date registerDate) {
    method getLastLoginDate (line 79) | public Date getLastLoginDate() {
    method setLastLoginDate (line 83) | public void setLastLoginDate(Date lastLoginDate) {
    method getLoginCount (line 87) | public Integer getLoginCount() {
    method setLoginCount (line 91) | public void setLoginCount(Integer loginCount) {
    method toString (line 95) | @Override

FILE: dis-seckill-user/src/main/java/com/seckill/dis/user/persistence/SeckillUserMapper.java
  type SeckillUserMapper (line 11) | @Mapper
    method getUserByPhone (line 20) | SeckillUser getUserByPhone(@Param("phone") Long phone);
    method updatePassword (line 27) | @Update("UPDATE seckill_user SET password=#{password} WHERE id=#{id}")
    method insertUser (line 37) | long insertUser(SeckillUser seckillUser);
    method findPhone (line 45) | long findPhone(long phone);

FILE: dis-seckill-user/src/main/java/com/seckill/dis/user/service/UserServiceImpl.java
  class UserServiceImpl (line 26) | @Service(interfaceClass = UserServiceApi.class)
    method login (line 43) | @Override
    method register (line 54) | @Override
    method checkUsername (line 105) | @Override
    method getUserInfo (line 110) | @Override
    method updateUserInfo (line 115) | @Override
    method login (line 128) | @Override
    method getUserByPhone (line 158) | @Override
    method getSeckillUserByPhone (line 185) | private SeckillUser getSeckillUserByPhone(long phone) {

FILE: dis-seckill-user/src/main/java/com/seckill/dis/user/util/UserUtil.java
  class UserUtil (line 25) | public class UserUtil {
    method createUser (line 28) | public static void createUser(int count) throws IOException {
    method generateMiaoshaUserList (line 92) | private static void generateMiaoshaUserList(int count, List<SeckillUse...
    method insertSeckillUserToDB (line 112) | private static void insertSeckillUserToDB(List<SeckillUser> users) thr...
Condensed preview — 115 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (602K chars).
[
  {
    "path": ".gitignore",
    "chars": 953,
    "preview": "# Created by .ignore support plugin (hsz.mobi)\n### Example user template template\n### Example user template\n\n# IntelliJ "
  },
  {
    "path": "README.md",
    "chars": 2753,
    "preview": "# 分布式高并发商品秒杀系统设计\n\n- [介绍](#介绍)\n- [快速启动](#快速启动)\n- [系统架构](#系统架构)\n- [模块介绍](#模块介绍)\n- [Q&A](#Q&A)\n- [TODO LIST](#TODO)\n- [参考]("
  },
  {
    "path": "dis-seckill-cache/pom.xml",
    "chars": 3947,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "dis-seckill-cache/src/main/java/com/seckill/dis/cache/CacheApplication.java",
    "chars": 339,
    "preview": "package com.seckill.dis.cache;\n\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.auto"
  },
  {
    "path": "dis-seckill-cache/src/main/java/com/seckill/dis/cache/config/RedisConfig.java",
    "chars": 1352,
    "preview": "package com.seckill.dis.cache.config;\n\n\nimport org.springframework.boot.context.properties.ConfigurationProperties;\nimpo"
  },
  {
    "path": "dis-seckill-cache/src/main/java/com/seckill/dis/cache/config/RedisPoolFactory.java",
    "chars": 926,
    "preview": "package com.seckill.dis.cache.config;\n\nimport org.springframework.beans.factory.annotation.Autowired;\nimport org.springf"
  },
  {
    "path": "dis-seckill-cache/src/main/java/com/seckill/dis/cache/service/RedisLockImpl.java",
    "chars": 2299,
    "preview": "package com.seckill.dis.cache.service;\n\nimport com.seckill.dis.common.api.cache.DLockApi;\nimport org.apache.dubbo.config"
  },
  {
    "path": "dis-seckill-cache/src/main/java/com/seckill/dis/cache/service/RedisServiceImpl.java",
    "chars": 5063,
    "preview": "package com.seckill.dis.cache.service;\n\nimport com.alibaba.fastjson.JSON;\nimport com.seckill.dis.common.api.cache.RedisS"
  },
  {
    "path": "dis-seckill-cache/src/main/resources/application.properties",
    "chars": 1233,
    "preview": "# --------------------------------\n#   spring \n#---------------------------------\nspring.application.name=dis-seckill-ca"
  },
  {
    "path": "dis-seckill-common/pom.xml",
    "chars": 2644,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "dis-seckill-common/schema/seckill.sql",
    "chars": 5405,
    "preview": "# ------------------------------------------------------------------------\n#\n#   创建秒杀场景下的表, 包含以下几个表:\n#\n#   1. 秒杀用户表     "
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/DLockApi.java",
    "chars": 561,
    "preview": "package com.seckill.dis.common.api.cache;\n\n/**\n * 分布式锁接口\n *\n * @author noodle\n */\npublic interface DLockApi {\n    /**\n  "
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/RedisServiceApi.java",
    "chars": 1272,
    "preview": "package com.seckill.dis.common.api.cache;\n\nimport com.seckill.dis.common.api.cache.vo.KeyPrefix;\n\n/**\n * redis 服务接口\n *\n "
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/AccessKeyPrefix.java",
    "chars": 529,
    "preview": "package com.seckill.dis.common.api.cache.vo;\n\nimport java.io.Serializable;\n\n/**\n * 访问次数的key前缀\n *\n * @author noodle\n */\np"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/BaseKeyPrefix.java",
    "chars": 901,
    "preview": "package com.seckill.dis.common.api.cache.vo;\n\n\n/**\n * 模板方法的基本类\n *\n * @author noodle\n */\npublic abstract class BaseKeyPre"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/GoodsKeyPrefix.java",
    "chars": 923,
    "preview": "package com.seckill.dis.common.api.cache.vo;\n\n\nimport java.io.Serializable;\n\n/**\n * redis中,用于商品信息的key\n *\n * @author nood"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/KeyPrefix.java",
    "chars": 333,
    "preview": "package com.seckill.dis.common.api.cache.vo;\n\n/**\n * redis 键的前缀\n * 之所以在key前面设置一个前缀,是因为如果出现设置相同的key情形,可以通过前缀加以区分\n *\n * @a"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/OrderKeyPrefix.java",
    "chars": 596,
    "preview": "package com.seckill.dis.common.api.cache.vo;\n\nimport java.io.Serializable;\n\n/**\n * 存储订单的key前缀\n *\n * @author noodle\n */\np"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/SkKeyPrefix.java",
    "chars": 927,
    "preview": "package com.seckill.dis.common.api.cache.vo;\n\n\nimport java.io.Serializable;\n\n/**\n * 判断秒杀状态的key前缀\n */\npublic class SkKeyP"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/SkUserKeyPrefix.java",
    "chars": 770,
    "preview": "package com.seckill.dis.common.api.cache.vo;\n\nimport java.io.Serializable;\n\n/**\n * 秒杀用户信息的key前缀\n */\n\npublic class SkUser"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/cache/vo/UserKey.java",
    "chars": 360,
    "preview": "package com.seckill.dis.common.api.cache.vo;\n\nimport java.io.Serializable;\n\n/**\n * redis中,用于管理用户表的key\n */\npublic class U"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/goods/GoodsServiceApi.java",
    "chars": 671,
    "preview": "package com.seckill.dis.common.api.goods;\n\nimport com.seckill.dis.common.api.goods.vo.GoodsVo;\n\nimport java.util.List;\n\n"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/goods/vo/GoodsDetailVo.java",
    "chars": 1265,
    "preview": "package com.seckill.dis.common.api.goods.vo;\n\nimport com.seckill.dis.common.api.user.vo.UserVo;\n\nimport java.io.Serializ"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/goods/vo/GoodsVo.java",
    "chars": 1160,
    "preview": "package com.seckill.dis.common.api.goods.vo;\n\nimport com.seckill.dis.common.domain.Goods;\n\nimport java.io.Serializable;\n"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/mq/MqProviderApi.java",
    "chars": 297,
    "preview": "package com.seckill.dis.common.api.mq;\n\nimport com.seckill.dis.common.api.mq.vo.SkMessage;\n\n/**\n * 消息队列服务\n *\n * @author "
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/mq/vo/SkMessage.java",
    "chars": 747,
    "preview": "package com.seckill.dis.common.api.mq.vo;\n\nimport com.seckill.dis.common.api.user.vo.UserVo;\n\nimport java.io.Serializabl"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/order/OrderServiceApi.java",
    "chars": 816,
    "preview": "package com.seckill.dis.common.api.order;\n\nimport com.seckill.dis.common.api.goods.vo.GoodsVo;\nimport com.seckill.dis.co"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/order/vo/OrderDetailVo.java",
    "chars": 876,
    "preview": "package com.seckill.dis.common.api.order.vo;\n\nimport com.seckill.dis.common.api.goods.vo.GoodsVo;\nimport com.seckill.dis"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/seckill/SeckillServiceApi.java",
    "chars": 803,
    "preview": "package com.seckill.dis.common.api.seckill;\n\nimport com.seckill.dis.common.api.goods.vo.GoodsVo;\nimport com.seckill.dis."
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/seckill/vo/VerifyCodeVo.java",
    "chars": 806,
    "preview": "package com.seckill.dis.common.api.seckill.vo;\n\nimport java.awt.image.BufferedImage;\nimport java.io.Serializable;\n\n/**\n "
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/UserServiceApi.java",
    "chars": 1499,
    "preview": "package com.seckill.dis.common.api.user;\n\nimport com.seckill.dis.common.api.user.vo.LoginVo;\nimport com.seckill.dis.comm"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/vo/LoginVo.java",
    "chars": 1063,
    "preview": "package com.seckill.dis.common.api.user.vo;\n\nimport com.seckill.dis.common.validator.IsMobile;\nimport org.hibernate.vali"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/vo/RegisterVo.java",
    "chars": 1211,
    "preview": "package com.seckill.dis.common.api.user.vo;\n\nimport javax.validation.constraints.NotNull;\nimport java.io.Serializable;\n\n"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/vo/UserInfoVo.java",
    "chars": 2521,
    "preview": "package com.seckill.dis.common.api.user.vo;\n\nimport java.io.Serializable;\n\n/**\n * 用户信息\n * <p>\n * 注:因为需要通过网络传输此model,所以需要"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/api/user/vo/UserVo.java",
    "chars": 2328,
    "preview": "package com.seckill.dis.common.api.user.vo;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 用户信息\n * <p>\n * "
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/domain/Goods.java",
    "chars": 1402,
    "preview": "package com.seckill.dis.common.domain;\n\n/**\n * goods 表\n *\n * @author noodle\n */\npublic class Goods {\n\n    private Long i"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/domain/OrderInfo.java",
    "chars": 2807,
    "preview": "package com.seckill.dis.common.domain;\n\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * order_info 订单信息\n *\n"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/domain/SeckillGoods.java",
    "chars": 1263,
    "preview": "package com.seckill.dis.common.domain;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * seckill_goods\n *\n * "
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/domain/SeckillOrder.java",
    "chars": 811,
    "preview": "package com.seckill.dis.common.domain;\n\nimport java.io.Serializable;\n\n/**\n * seckill_order 表\n *\n * @author noodle\n */\npu"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/domain/SeckillUser.java",
    "chars": 2298,
    "preview": "package com.seckill.dis.common.domain;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 秒杀用户信息\n *\n * @author"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/exception/GlobalException.java",
    "chars": 435,
    "preview": "package com.seckill.dis.common.exception;\n\nimport com.seckill.dis.common.result.CodeMsg;\n\n/**\n * 全局异常处理器\n *\n * @author n"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/exception/GlobalExceptionHandler.java",
    "chars": 1990,
    "preview": "package com.seckill.dis.common.exception;\n\nimport com.seckill.dis.common.result.CodeMsg;\nimport com.seckill.dis.common.r"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/result/CodeMsg.java",
    "chars": 2629,
    "preview": "package com.seckill.dis.common.result;\n\nimport java.io.Serializable;\n\n/**\n * 响应结果状态码\n *\n * @author noodle\n */\npublic cla"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/result/Result.java",
    "chars": 1763,
    "preview": "package com.seckill.dis.common.result;\n\nimport java.io.Serializable;\n\n/**\n * 用户接口返回结果\n *\n * @param <T> 数据实体类型\n * @author"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/util/DBUtil.java",
    "chars": 1285,
    "preview": "package com.seckill.dis.common.util;\n\nimport java.io.IOException;\nimport java.io.InputStream;\nimport java.sql.Connection"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/util/JsonUtil.java",
    "chars": 1628,
    "preview": "package com.seckill.dis.common.util;\n\nimport com.alibaba.fastjson.JSON;\n\n/**\n * json 工具类\n *\n * @author noodle\n */\npublic"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/util/MD5Util.java",
    "chars": 2354,
    "preview": "package com.seckill.dis.common.util;\n\n\nimport org.apache.commons.codec.digest.DigestUtils;\nimport org.junit.Test;\n\n/**\n "
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/util/UUIDUtil.java",
    "chars": 242,
    "preview": "package com.seckill.dis.common.util;\n\nimport java.util.UUID;\n\n\n/**\n * UUID工具类用于生成session\n *\n * @author noodle\n */\npublic"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/util/ValidatorUtil.java",
    "chars": 733,
    "preview": "package com.seckill.dis.common.util;\n\n\nimport org.apache.commons.lang3.StringUtils;\n\nimport java.util.regex.Pattern;\n\n/*"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/util/VerifyCodeUtil.java",
    "chars": 2532,
    "preview": "package com.seckill.dis.common.util;\n\nimport com.seckill.dis.common.api.seckill.vo.VerifyCodeVo;\n\nimport javax.script.Sc"
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/validator/IsMobile.java",
    "chars": 879,
    "preview": "package com.seckill.dis.common.validator;\n\n\nimport javax.validation.Constraint;\nimport javax.validation.Payload;\nimport "
  },
  {
    "path": "dis-seckill-common/src/main/java/com/seckill/dis/common/validator/IsMobileValidator.java",
    "chars": 1340,
    "preview": "package com.seckill.dis.common.validator;\n\nimport com.seckill.dis.common.util.ValidatorUtil;\nimport org.slf4j.Logger;\nim"
  },
  {
    "path": "dis-seckill-gateway/pom.xml",
    "chars": 4733,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/DisSeckillServletInitializer.java",
    "chars": 500,
    "preview": "package com.seckill.dis.gateway;\n\nimport org.springframework.boot.builder.SpringApplicationBuilder;\nimport org.springfra"
  },
  {
    "path": "dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/GatewayApplication.java",
    "chars": 438,
    "preview": "package com.seckill.dis.gateway;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.aut"
  },
  {
    "path": "dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/config/WebConfig.java",
    "chars": 1468,
    "preview": "package com.seckill.dis.gateway.config;\n\n\nimport com.seckill.dis.gateway.config.access.AccessInterceptor;\nimport com.sec"
  },
  {
    "path": "dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/config/access/AccessInterceptor.java",
    "chars": 6196,
    "preview": "package com.seckill.dis.gateway.config.access;\n\nimport com.alibaba.fastjson.JSON;\nimport com.seckill.dis.common.api.cach"
  },
  {
    "path": "dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/config/access/AccessLimit.java",
    "chars": 637,
    "preview": "package com.seckill.dis.gateway.config.access;\n\nimport java.lang.annotation.Retention;\nimport java.lang.annotation.Targe"
  },
  {
    "path": "dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/config/access/UserContext.java",
    "chars": 531,
    "preview": "package com.seckill.dis.gateway.config.access;\n\nimport com.seckill.dis.common.api.user.vo.UserVo;\n\n/**\n * 用于保存用户\n * 使用Th"
  },
  {
    "path": "dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/config/resolver/UserArgumentResolver.java",
    "chars": 4774,
    "preview": "package com.seckill.dis.gateway.config.resolver;\n\nimport com.seckill.dis.common.api.cache.RedisServiceApi;\nimport com.se"
  },
  {
    "path": "dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/exception/GlobalException.java",
    "chars": 436,
    "preview": "package com.seckill.dis.gateway.exception;\n\nimport com.seckill.dis.common.result.CodeMsg;\n\n/**\n * 全局异常处理器\n *\n * @author "
  },
  {
    "path": "dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/exception/GlobalExceptionHandler.java",
    "chars": 2051,
    "preview": "package com.seckill.dis.gateway.exception;\n\nimport com.seckill.dis.common.result.CodeMsg;\nimport com.seckill.dis.common."
  },
  {
    "path": "dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/goods/GoodsController.java",
    "chars": 4823,
    "preview": "package com.seckill.dis.gateway.goods;\n\nimport com.seckill.dis.common.api.cache.RedisServiceApi;\nimport com.seckill.dis."
  },
  {
    "path": "dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/order/OrderController.java",
    "chars": 2043,
    "preview": "package com.seckill.dis.gateway.order;\n\nimport com.seckill.dis.common.api.goods.GoodsServiceApi;\nimport com.seckill.dis."
  },
  {
    "path": "dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/seckill/SeckillController.java",
    "chars": 10152,
    "preview": "package com.seckill.dis.gateway.seckill;\n\nimport com.seckill.dis.common.api.cache.RedisServiceApi;\nimport com.seckill.di"
  },
  {
    "path": "dis-seckill-gateway/src/main/java/com/seckill/dis/gateway/user/UserController.java",
    "chars": 3273,
    "preview": "package com.seckill.dis.gateway.user;\n\nimport com.seckill.dis.common.api.cache.vo.SkUserKeyPrefix;\nimport com.seckill.di"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/application.properties",
    "chars": 1467,
    "preview": "#---------------------------------\n# web \n#---------------------------------\nserver.port=8082\n#-------------------------"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/static/bootstrap/css/bootstrap-theme.css",
    "chars": 26132,
    "preview": "/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://gi"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/static/bootstrap/css/bootstrap.css",
    "chars": 146010,
    "preview": "/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under MIT (https://gi"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/static/bootstrap/js/bootstrap.js",
    "chars": 69707,
    "preview": "/*!\n * Bootstrap v3.3.7 (http://getbootstrap.com)\n * Copyright 2011-2016 Twitter, Inc.\n * Licensed under the MIT license"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/static/bootstrap/js/npm.js",
    "chars": 484,
    "preview": "// This file is autogenerated via the `commonjs` Grunt task. You can require() this file in a CommonJS environment.\nrequ"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/static/goods_detail.htm",
    "chars": 8599,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n    <title>商品详情</title>\n\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=U"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/static/js/common.js",
    "chars": 1151,
    "preview": "/**\n * 展示loading弹框\n * @returns {*}\n */\nfunction g_showLoading() {\n    var idx = layer.msg('处理中...', {icon: 16, shade: [0"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/static/layer/layer.js",
    "chars": 21602,
    "preview": "/*! layer-v3.0.3 Web弹层组件 MIT License  http://layer.layui.com/  By 贤心 */\n ;!function(e,t){\"use strict\";var i,n,a=e.layui"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/static/layer/mobile/layer.js",
    "chars": 3290,
    "preview": "/*! layer mobile-v2.0.0 Web弹层组件 MIT License  http://layer.layui.com/mobile  By 贤心 */\n ;!function(e){\"use strict\";var t=d"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/static/layer/mobile/need/layer.css",
    "chars": 5260,
    "preview": ".layui-m-layer{position:relative;z-index:19891014}.layui-m-layer *{-webkit-box-sizing:content-box;-moz-box-sizing:conten"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/static/layer/skin/default/layer.css",
    "chars": 14499,
    "preview": ".layui-layer-imgbar,.layui-layer-imgtit a,.layui-layer-tab .layui-layer-title span,.layui-layer-title{text-overflow:elli"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/static/order_detail.htm",
    "chars": 3740,
    "preview": "<!DOCTYPE html>\n<html>\n<head>\n    <title>订单详情</title>\n    <meta http-equiv=\"Content-Type\" content=\"text/html; charset=UT"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/templates/goods_detail.html",
    "chars": 3876,
    "preview": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n    <title>商品详情</title>\n\n    <meta http-equiv=\"Content"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/templates/goods_list.html",
    "chars": 2423,
    "preview": "<!DOCTYPE HTML>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n    <title>商品列表</title>\n    <meta http-equiv=\"Content-"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/templates/login.html",
    "chars": 4008,
    "preview": "<!DOCTYPE HTML>\n<!--引入thymeleaf命名空间-->\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n    <title>登录</title>\n    <meta"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/templates/order_detail.html",
    "chars": 2545,
    "preview": "<!DOCTYPE html>\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n    <title>订单详情</title>\n    <meta http-equiv=\"Content-"
  },
  {
    "path": "dis-seckill-gateway/src/main/resources/templates/register.html",
    "chars": 5474,
    "preview": "<!DOCTYPE HTML>\n<!--引入thymeleaf命名空间-->\n<html xmlns:th=\"http://www.thymeleaf.org\">\n<head>\n    <title>注册</title>\n    <meta"
  },
  {
    "path": "dis-seckill-goods/pom.xml",
    "chars": 4202,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "dis-seckill-goods/src/main/java/com/seckill/dis/goods/GoodsApplication.java",
    "chars": 359,
    "preview": "package com.seckill.dis.goods;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoc"
  },
  {
    "path": "dis-seckill-goods/src/main/java/com/seckill/dis/goods/persistence/GoodsMapper.java",
    "chars": 1333,
    "preview": "package com.seckill.dis.goods.persistence;\n\nimport com.seckill.dis.common.api.goods.vo.GoodsVo;\nimport com.seckill.dis.c"
  },
  {
    "path": "dis-seckill-goods/src/main/java/com/seckill/dis/goods/service/GoodsServiceImpl.java",
    "chars": 1408,
    "preview": "package com.seckill.dis.goods.service;\n\nimport com.seckill.dis.common.api.goods.GoodsServiceApi;\nimport com.seckill.dis."
  },
  {
    "path": "dis-seckill-goods/src/main/java/com/seckill/dis/goods/service/SeckillServiceImpl.java",
    "chars": 5578,
    "preview": "package com.seckill.dis.goods.service;\n\n\nimport com.alibaba.fastjson.JSONObject;\nimport com.seckill.dis.common.api.cache"
  },
  {
    "path": "dis-seckill-goods/src/main/resources/application.properties",
    "chars": 2872,
    "preview": "# --------------------------------\n#   spring \n#---------------------------------\nspring.application.name=dis-seckill-go"
  },
  {
    "path": "dis-seckill-mq/pom.xml",
    "chars": 4316,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "dis-seckill-mq/src/main/java/com/seckill/dis/mq/MqApplication.java",
    "chars": 353,
    "preview": "package com.seckill.dis.mq;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoconf"
  },
  {
    "path": "dis-seckill-mq/src/main/java/com/seckill/dis/mq/config/MQConfig.java",
    "chars": 1191,
    "preview": "package com.seckill.dis.mq.config;\n\nimport org.springframework.amqp.core.Queue;\nimport org.springframework.amqp.rabbit.c"
  },
  {
    "path": "dis-seckill-mq/src/main/java/com/seckill/dis/mq/receiver/MqConsumer.java",
    "chars": 2886,
    "preview": "package com.seckill.dis.mq.receiver;\n\nimport com.seckill.dis.common.api.cache.RedisServiceApi;\nimport com.seckill.dis.co"
  },
  {
    "path": "dis-seckill-mq/src/main/java/com/seckill/dis/mq/service/MqProviderImpl.java",
    "chars": 1874,
    "preview": "package com.seckill.dis.mq.service;\n\nimport com.seckill.dis.common.api.mq.MqProviderApi;\nimport com.seckill.dis.common.a"
  },
  {
    "path": "dis-seckill-mq/src/main/resources/application.properties",
    "chars": 1988,
    "preview": "# --------------------------------\n#   spring \n#---------------------------------\nspring.application.name=dis-seckill-mq"
  },
  {
    "path": "dis-seckill-order/pom.xml",
    "chars": 4164,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "dis-seckill-order/src/main/java/com/seckill/dis/order/OrderApplication.java",
    "chars": 359,
    "preview": "package com.seckill.dis.order;\n\nimport org.springframework.boot.SpringApplication;\nimport org.springframework.boot.autoc"
  },
  {
    "path": "dis-seckill-order/src/main/java/com/seckill/dis/order/persistence/OrderMapper.java",
    "chars": 1641,
    "preview": "package com.seckill.dis.order.persistence;\n\nimport com.seckill.dis.common.domain.OrderInfo;\nimport com.seckill.dis.commo"
  },
  {
    "path": "dis-seckill-order/src/main/java/com/seckill/dis/order/service/OrderServiceImpl.java",
    "chars": 2925,
    "preview": "package com.seckill.dis.order.service;\n\nimport com.seckill.dis.common.api.cache.RedisServiceApi;\nimport com.seckill.dis."
  },
  {
    "path": "dis-seckill-order/src/main/resources/application.properties",
    "chars": 2872,
    "preview": "# --------------------------------\n#   spring \n#---------------------------------\nspring.application.name=dis-seckill-or"
  },
  {
    "path": "dis-seckill-user/pom.xml",
    "chars": 4337,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\"\n         xmlns:xsi=\"http://www"
  },
  {
    "path": "dis-seckill-user/src/main/java/com/seckill/dis/user/UserApplication.java",
    "chars": 511,
    "preview": "package com.seckill.dis.user;\n\nimport org.apache.dubbo.config.spring.context.annotation.EnableDubbo;\nimport org.springfr"
  },
  {
    "path": "dis-seckill-user/src/main/java/com/seckill/dis/user/domain/SeckillUser.java",
    "chars": 2296,
    "preview": "package com.seckill.dis.user.domain;\n\nimport java.io.Serializable;\nimport java.util.Date;\n\n/**\n * 秒杀用户信息\n *\n * @author n"
  },
  {
    "path": "dis-seckill-user/src/main/java/com/seckill/dis/user/persistence/SeckillUserMapper.java",
    "chars": 798,
    "preview": "package com.seckill.dis.user.persistence;\n\nimport com.seckill.dis.user.domain.SeckillUser;\nimport org.apache.ibatis.anno"
  },
  {
    "path": "dis-seckill-user/src/main/java/com/seckill/dis/user/persistence/SeckillUserMapper.xml",
    "chars": 1706,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\" ?>\n<!DOCTYPE mapper\n        PUBLIC \"-//mybatis.org//DTD Mapper 3.0//EN\"\n        \"ht"
  },
  {
    "path": "dis-seckill-user/src/main/java/com/seckill/dis/user/service/UserServiceImpl.java",
    "chars": 6140,
    "preview": "package com.seckill.dis.user.service;\n\nimport com.seckill.dis.common.api.cache.DLockApi;\nimport com.seckill.dis.common.a"
  },
  {
    "path": "dis-seckill-user/src/main/java/com/seckill/dis/user/util/UserUtil.java",
    "chars": 4968,
    "preview": "package com.seckill.dis.user.util;\n\nimport com.alibaba.fastjson.JSON;\nimport com.alibaba.fastjson.JSONObject;\nimport com"
  },
  {
    "path": "dis-seckill-user/src/main/resources/application.properties",
    "chars": 2867,
    "preview": "# --------------------------------\n#   spring \n#---------------------------------\nspring.application.name=dis-seckill-us"
  },
  {
    "path": "doc/HandlerInterceptor的使用.md",
    "chars": 1465,
    "preview": "## 简介\n\nSpringMVC的处理器拦截器,类似于Servlet开发中的过滤器Filter,用于对请求进行拦截和处理。\n\n## 常见应用场景\n\n1、**权限检查**:如检测请求是否具有登录权限,如果没有直接返回到登陆页面。 \n2、**性"
  },
  {
    "path": "doc/README.md",
    "chars": 2102,
    "preview": "# 分布式高并发商品秒杀系统设计\n\n- [介绍](#介绍)\n- [快速启动](#快速启动)\n- [系统架构](#系统架构)\n- [模块介绍](#模块介绍)\n- [TODO LIST](#TODO)\n\n## 介绍\n\n本项目为另一个项目[sec"
  },
  {
    "path": "doc/Redis中存储的数据.md",
    "chars": 906,
    "preview": "# Redis中存储的数据\n\n```properties\n# 1. redis中缓存通过用户手机号码获取的用户信息\nkey: SkUserKeyPrefix:id_{phone}\nvalue: {SeckillUser}\nexpire: 0"
  },
  {
    "path": "doc/使用分布式锁解决恶意用户重复注册问题.md",
    "chars": 5103,
    "preview": "# 使用分布式锁解决恶意用户重复注册问题\n\n[TOC]\n\n## 问题分析\n\n考虑用户模块下的用户服务实现(`com.seckill.dis.user.service.UserServiceImpl`):\n\n```java\n@Override"
  },
  {
    "path": "doc/前后端交互接口定义.md",
    "chars": 3496,
    "preview": "# 前后端交互接口\n\n[TOC]\n\n## 用户模块接口\n\n### 首页\n\n- 请求地址:/user/index\n- 请求方式:get\n- 返回响应:login.html\n\n### 登录接口\n\n- 请求地址:/user/login\n- 请求方"
  },
  {
    "path": "doc/前后端交互接口逻辑实现.md",
    "chars": 6748,
    "preview": "# 前后端交互接口逻辑实现\n\n[TOC]\n\n## 用户登录逻辑\n\n请求url:`/user/login`\n\n1. 通过请求参数解析器获取用户请求参数,获取用户名和用户密码,判断用户是否存在(首先从缓存中获取该用户是否存在,如果用户存在,则从"
  },
  {
    "path": "pom.xml",
    "chars": 7597,
    "preview": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<project xmlns=\"http://maven.apache.org/POM/4.0.0\" xmlns:xsi=\"http://www.w3.org/2"
  }
]

About this extraction

This page contains the full source code of the Grootzz/dis-seckill GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 115 files (517.8 KB), approximately 155.5k tokens, and a symbol index with 468 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.

Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.

Copied to clipboard!