提交 24acc809 编写于 作者: J jay1991115@126.com

init

上级
# Created by .ignore support plugin (hsz.mobi)
### Example user template template
### Example user template
# IntelliJ project files
.idea
*.iml
out
gen### JetBrains template
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and WebStorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839
# User-specific stuff
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/**/usage.statistics.xml
.idea/**/dictionaries
.idea/**/shelf
# Sensitive or high-churn files
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml
.idea/**/dbnavigator.xml
# Gradle
.idea/**/gradle.xml
.idea/**/libraries
# Gradle and Maven with auto-import
# When using Gradle or Maven with auto-import, you should exclude module files,
# since they will be recreated, and may cause churn. Uncomment if using
# auto-import.
# .idea/modules.xml
# .idea/*.iml
# .idea/modules
# CMake
cmake-build-*/
# Mongo Explorer plugin
.idea/**/mongoSettings.xml
# File-based project format
*.iws
# IntelliJ
out/
# mpeltonen/sbt-idea plugin
.idea_modules/
# JIRA plugin
atlassian-ide-plugin.xml
# Cursive Clojure plugin
.idea/replstate.xml
# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
# Editor-based Rest Client
.idea/httpRequests
### 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*
### Maven template
target/
pom.xml.tag
pom.xml.releaseBackup
pom.xml.versionsBackup
pom.xml.next
release.properties
dependency-reduced-pom.xml
buildNumber.properties
.mvn/timing.properties
.mvn/wrapper/maven-wrapper.jar
### 前言
大家好,之前在公司自己设计并开发了一套完整的秒杀系统,自己构建了一个小的demo,希望和大家分享一下,希望大家能从中收益,如果有意见和好的想法请加我!
QQ:3341386488
邮箱:QiuRunZe_key@163.com
我会不断完善,希望大家有好的想法拉一个分支提高,一起合作!
觉得不错对您有帮助,麻烦右上角点下star以示鼓励!长期维护不易 多次想放弃 坚持是一种信仰 专注是一种态度!
## 秒杀设计整体流程
![整体流程](http://i2.bvimg.com/601558/886c867d6488dfc2.png)
#### 需注意几点:
### <font color=#0099ff size=3 >1.如何解决卖超问题<br></font><br>
① sql加上判断如果防止数据变为负数<br>
② 数据库加唯一索引防止用户重复购买<br>
③ redis预减库存减少数据库访问 内存标记减少redis访问 请求先入队列缓冲,异步下单,增强用户体验
### <font color=#0099ff size=3 >如何解决分布式session<br></font><br>
① 生成一个随机的uuid一类的写回到cookie中<br>
② redis 内存写入<br>
③ 下一个页面拿到uuid 内存取对象
### <font color=#0099ff size=3 >3.如何优雅解决接口防刷限流<br></font><br>
如果有缓存的话 这个功能实现起来就和简单,在一个用户访问接口的时候我们把访问次数写到缓存中,在加上一个有效期。
通过拦截器. 做一个注解 @AccessLimit 然后封装这个注解,可以有效的设置每次访问多少次,有效时间是否需要登录!
### <font color=#0099ff size=3 >4.通用缓存key的封装采用什么设计模式?<br></font><br>
模板模式的优点<br>
①具体细节步骤实现定义在子类中,子类定义详细处理算法是不会改变算法整体结构。<br>
②代码复用的基本技术,在数据库设计中尤为重要。<br>
③存在一种反向的控制结构,通过一个父类调用其子类的操作,通过子类对父类进行扩展增加新的行为,符合“开闭原则”。<br>
缺点: 每个不同的实现都需要定义一个子类,会导致类的个数增加,系统更加庞大
### <font color=#0099ff size=3 >5.LVS , tomcat(apr) , keepalive 高可用 --待更新</font><br>
### <font color=#0099ff size=3 >6.限流算法 令牌桶,漏桶算法--待更新</font><br>
### <font color=#0099ff size=3 >7.Nginx优化(前端缓存)</font><br>
①并发优化<br>
②keepAlive长链接(nginx,tomcat默认没有配置长链接)<br>
③压缩优化.配置缓存<br>
监控工具:1.nginx_status并发统计,Ngxtop缓存统计
### nginx负载均衡
![整体流程](http://i2.bvimg.com/601558/23f54a389b2b23e8.png)
### <font color=#0099ff size=3 >8.服务降级--服务熔断(过载保护)</font><br>
自动降级: 超时.失败次数,故障,限流<br>
人工降级:秒杀,双11<br>
9.所有秒杀相关的接口比如:秒杀,获取秒杀地址,获取秒杀结果,获取秒杀验证码都需要加上<br>
秒杀是否开始结束的判断
### <font color=#0099ff size=3 >10.redis的库存如何与数据库的库存保持一致?</font><br>
redis的数量不是库存,他的作用仅仅只是为了阻挡多余的请求透穿到DB,起到一个保护的作用<br>因为秒杀的商品有限,比如10个,让1万个请求区访问DB是没有意义的,因为最多也就只能10<br>
个请求下单成功,所有这个是一个伪命题,我们是不需要保持一致的。<br>
### <font color=#0099ff size=3 >11.redis 预减成功,DB扣减库存失败怎么办?</font><br>
其实我们可以不用太在意,对用户而言,秒杀不中是正常现象,秒杀中才是意外,单个用户秒杀中<br>
本来就是小概率事件,出现这种情况对于用户而言没有任何影响<br>
2.对于商户而言,本来就是为了活动拉流量人气的,卖不完还可以省一部分费用,但是活动还参与了,也就没有了任何影响<br>
3.对网站而言,最重要的是体验,只要网站不崩溃,对用户而言没有任何影响<br>
### <font color=#0099ff size=3 >12.为什么redis数量会减少为负数?</font><br>
//预见库存
long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock,""+goodsId) ;
if(stock <0){
localOverMap.put(goodsId, true);
return Result.error(CodeMsg.MIAO_SHA_OVER);
假如redis的数量为1,这个时候同时过来100个请求,大家一起执行decr数量就会减少成-99这个是正常的
### <font color=#0099ff size=3 >13.为什么要单独维护一个秒杀结束标志?</font><br>
1.前提所有的秒杀相关的接口都要加上活动是否结束的标志,如果结束就直接返回,包括轮寻的接口防止一直轮寻<br>
2.管理后台也可以手动的更改这个标志,防止出现活动开始以后就没办法结束这种意外的事件
### <font color=#0099ff size=3 >14.redis挂掉了怎么办?</font><br>
1.具体我会有时间更新关于redis的知识
### <font color=#0099ff size=3 >15.rabbitmq如何做到消息不重复不丢失即使服务器重启?</font><br>
1.exchange持久化2.queue持久化3.发送消息设置MessageDeliveryMode.persisent这个也是默认的行为4.手动确认
### <font color=#0099ff size=3 >15.为什么threadlocal存储user对象,原理??</font><br>
![整体流程](http://i2.bvimg.com/601558/3293e36cc2c7e303.png)
1.并发编程中重要的问题就是数据共享,当你在一个线程中改变任意属性时,所有的线程都会因此受到影响,同时会看到第一个线程修改后的值<br>
有时我们希望如此,比如:多个线程增大或减小同一个计数器变量<br>
但是,有时我们希望确保每个线程,只能工作在它自己的线程实例的拷贝上,同时不会影响其他线程的数据<br>
举例: 举个例子,想象你在开发一个电子商务应用,你需要为每一个控制器处理的顾客请求,生成一个唯一的事务ID,同时将其传到管理器或DAO的业务方法中,以便记录日志。一种方案是将事务ID作为一个参数,传到所有的业务方法中。但这并不是一个好的方案,它会使代码变得冗余。
你可以使用ThreadLocal类型的变量解决这个问题。首先在控制器或者任意一个预处理器拦截器中生成一个事务ID
然后在ThreadLocal中 设置事务ID,最后,不论这个控制器调用什么方法,都能从threadlocal中获取事务ID
而且这个应用的控制器可以同时处理多个请求,
同时在框架 层面,因为每一个请求都是在一个单独的线程中处理的,所以事务ID对于每一个线程都是唯一的,而且可以从所有线程的执行路径获取
运行结果可以看出每个线程都在维护自己的变量:
Starting Thread: 0 : Fri Sep 21 23:05:34 CST 2018<br>
Starting Thread: 2 : Fri Sep 21 23:05:34 CST 2018<br>
Starting Thread: 1 : Fri Jan 02 05:36:17 CST 1970<br>
Thread Finished: 1 : Fri Jan 02 05:36:17 CST 1970<br>
Thread Finished: 0 : Fri Sep 21 23:05:34 CST 2018<br>
Thread Finished: 2 : Fri Sep 21 23:05:34 CST 2018<br>
局部线程通常使用在这样的情况下,当你有一些对象并不满足线程安全,但是你想避免在使用synchronized关键字<br>
块时产生的同步访问,那么,让每个线程拥有它自己的对象实例<br>
注意:局部变量是同步或局部线程的一个好的替代,它总是能够保证线程安全。唯一可能限制你这样做的是你的应用设计约束<br>
所以设计threadlocal存储user不会对对象产生影响,每次进来一个请求都会产生自身的线程变量来存储
### <font color=#0099ff size=3 >15.mybatis如何使用注解与xml配置?</font><br>
本文使用的是注解方法开发所以不做过多解释<br>
下面仔细讲解以下如何详细使用xml开发在目录里面有一个与本文无挂的类似于微信自动回复的功能br>
里面有mybatis的全部解析和用法,大家可以简单的当做一个demo来使用<br>
### maven 隔离
maven隔离就是在开发中,把各个环境的隔离开来,一般分为
本地(local)
开发(dev)
测试(test)
线上(prod)
在环境部署中为了防止人工修改的弊端! spring.profiles.active=@activatedProperties@
### redis 分布式锁实现方法
我用了四种方法 , 分别指出了不同版本的缺陷以及演进的过程 orderclosetask
v1---->>版本没有操作,在分布式系统中会造成同一时间,资源浪费而且很容易出现并发问题
V2--->>版本加了分布式redis锁,在访问核心方法前,加入redis锁可以阻塞其他线程访问,可以
很好的处理并发问题,但是缺陷就是如果机器突然宕机,或者线路波动等,就会造成死锁,一直
不释放等问题
V3版本-->>很好的解决了这个问题v2的问题,就是加入时间对比如果当前时间已经大与释放锁的时间
说明已经可以释放这个锁重新在获取锁,setget方法可以把之前的锁去掉在重新获取,旧值在于之前的
值比较,如果无变化说明这个期间没有人获取或者操作这个redis锁,则可以重新获取
V4---->>采用成熟的框架redisson,封装好的方法则可以直接处理,但是waittime记住要这只为0
此差异已折叠。
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.geekq</groupId>
<artifactId>miaosha</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.8.RELEASE</version>
</parent>
<name>miaosha</name>
<url>http://maven.apache.org</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>dubbo</artifactId>
<version>2.5.3</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.apache.zookeeper</groupId>
<artifactId>zookeeper</artifactId>
<version>3.4.6</version>
<exclusions>
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
</exclusion>
<exclusion>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.0.5</version>
</dependency>
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.38</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.6</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>joda-time</groupId>
<artifactId>joda-time</artifactId>
<version>2.3</version>
</dependency>
<!-- https://mvnrepository.com/artifact/org.redisson/redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson</artifactId>
<version>2.14.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!--lombook引用-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.2</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<finalName>${project.artifactId}</finalName>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>1.5.2.RELEASE</version>
<configuration>
<mainClass>com.geekq.miaosha.GeekQMainApplication</mainClass><!--springboot启动类目录-->
</configuration>
<executions>
<execution>
<goals> <!--创建一个自动可执行的jar或war文件 -->
<goal>repackage</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource> <!--配置文件路径 -->
<directory>src/main/resources</directory> <!--这里对应项目存放配置文件的目录--> <!--开启filtering功能 -->
<filtering>true</filtering>
<excludes>
<exclude>application-local.properties</exclude>
<exclude>application-dev.properties</exclude>
<exclude>application-prod.properties</exclude>
<exclude>application-test.properties</exclude>
</excludes>
</resource>
<resource> <!--配置文件路径 -->
<directory>src/main/resources</directory> <!--这里对应项目存放配置文件的目录--> <!--开启filtering功能 -->
<filtering>true</filtering>
<includes>
<include>application-${activatedProperties}.properties</include>
<include>application.properties</include>
</includes>
</resource>
</resources>
</build>
<profiles>
<profile>
<id>local</id>
<properties> <!-- 环境标识,需要与配置文件的名称相对应 -->
<activatedProperties>local</activatedProperties>
</properties>
<activation> <!-- 默认环境 -->
<activeByDefault>true</activeByDefault>
</activation>
</profile>
<profile>
<id>dev</id>
<properties> <!-- 环境标识,需要与配置文件的名称相对应 -->
<activatedProperties>dev</activatedProperties>
</properties>
</profile>
<profile>
<id>test</id>
<properties>
<activatedProperties>test</activatedProperties>
</properties>
</profile>
<profile>
<id>prod</id>
<properties>
<activatedProperties>prod</activatedProperties>
</properties>
</profile>
</profiles>
</project>
package com.geekq.miaosha;
import java.util.Date;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
public class DemoTask implements Runnable
{
// Atomic integer containing the next thread ID to be assigned
private static final AtomicInteger nextId = new AtomicInteger(0);
// Thread local variable containing each thread's ID
private static final ThreadLocal<Integer> threadId = new ThreadLocal<Integer>()
{
@Override
protected Integer initialValue()
{
return nextId.getAndIncrement();
}
};
// Returns the current thread's unique ID, assigning it if necessary
public int getThreadId()
{
return threadId.get();
}
// Returns the current thread's starting timestamp
private static final ThreadLocal<Date> startDate = new ThreadLocal<Date>()
{
protected Date initialValue()
{
if(threadId.get()==1){
return new Date(77777777L);
}
return new Date();
}
};
@Override
public void run()
{
System.out.printf("Starting Thread: %s : %s\n", getThreadId(), startDate.get());
try
{
TimeUnit.SECONDS.sleep((int) Math.rint(Math.random() * 10));
} catch (InterruptedException e)
{
e.printStackTrace();
}
System.out.printf("Thread Finished: %s : %s\n", getThreadId(), startDate.get());
}
}
\ No newline at end of file
package com.geekq.miaosha;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling
public class GeekQMainApplication {
public static void main(String[] args) throws Exception {
SpringApplication.run(GeekQMainApplication.class, args);
}
}
package com.geekq.miaosha.Md5Utils;
import org.apache.commons.codec.digest.DigestUtils;
public class MD5Utils {
public static String md5 ( String key) {
return DigestUtils.md5Hex(key) ;
}
public static final String salt = "1a2b3c4d" ;
/**
*
* @param inputPass
* @return
*/
public static String inputPassFormPass ( String inputPass) {
String str = "" + salt.charAt(0) + salt.charAt(2)+ inputPass + salt.charAt(5) + salt.charAt(4) ;
return md5(str);
}
/**
* 第二次md5 salt 可随机 
* @param formPass
* @param salt
* @return
*/
public static String formPassToDBPass ( String formPass ,String salt ) {
String str = "" + salt.charAt(0) + salt.charAt(2)+ formPass + salt.charAt(5) + salt.charAt(4) ;
return md5(str);
}
public static String inputPassToDBPass ( String inputPass ,String saltDB ) {
String formPass = inputPassFormPass(inputPass);
String dbPass = formPassToDBPass(formPass ,saltDB ) ;
return dbPass ;
}
public static void main(String[] args) {
System.out.println(inputPassToDBPass("123456","1a2b3c4d"));
}
}
package com.geekq.miaosha;
import java.util.ArrayList;
import java.util.List;
/**
* mybatis xml 写法的配置测试类
*/
public class Test {
public static List<Double> list = new ArrayList<Double>();
public static void main(String[] args) {
double[] keys = {1, 2,3};
System.err.println(getNum(keys, 5));
// 举例:3 个元素:a, b, c。所以有2 ^ 3 = 8 个组合结果:所以i = 0, 1, 2,....7.对应应输出 a, b, ab, c...abc(注意a表示001,不是100.)
//
// 将i变成2进制:
// i = 1 = 001
//   i = 2 = 010
// i = 3 = 011
//
//    (1) j = 0 (1) j = 0 (1) j = 0
//   移1位:1 << j == 001 1 << j == 001 1 << j == 001
//   和i = 001 相与,两个位都为1,返回1 与i无相同位 和i = 001 相与,两个位都为1,返回1
//   输出:a 输出a
//
// (2) j = 1 (2) j = 1 (2) j = 1
// 再移一位:1 << j == 010 1 << j == 010   1 << j == 010
// 与i = 001 相与。无相同1 和i相与,两个位都为1,返回1 和i相与,两个位都为1,返回1
// 输出b 输出b
//
// (3) j = 2 3)j = 2 (3) j = 2
// 移一位 1 << j == 100 1 << j == 100
// 与i无相同位 与i无相同位 与i无相同位
//
// 所以i = 001, 只输出a.所以i = 010, 只输出b.所以011,输出ab
//
// *************************************
// *可见上面每一个数字i, 只会判断判断3次,因为只需要移三次位,二进制就遍历完了
}
static List<Double> getNum(double[] keys, double kill) {
int n = keys.length;//数字长度
System.out.println(n);
int nbit = 1 << n; // 8
System.out.println(nbit);
double in;
List<Double> list = new ArrayList<Double>();
for (int i = 0; i < nbit; i++) {
System.out.println("nbit======="+i);
in = 0;
list.clear();
for (int j = 0; j < n; j++) {
System.out.println("j======"+j);
int tmp = 1 << j; // 由0到n右移位
System.out.println("tmp===="+tmp);
if ((tmp & i) != 0) { // 与运算,同为1时才会是1
System.out.println("keys[j]=========="+keys[j]);
in += keys[j];
list.add(keys[j]);
}
}
if (in == kill){
return list;
}
}
return list;
}
}
\ No newline at end of file
package com.geekq.miaosha.access;
import com.alibaba.fastjson.JSON;
import com.geekq.miaosha.domain.MiaoshaUser;
import com.geekq.miaosha.redis.RedisService;
import com.geekq.miaosha.result.CodeMsg;
import com.geekq.miaosha.result.Result;
import com.geekq.miaosha.service.MiaoShaUserService;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
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;
@Service
public class AccessInterceptor extends HandlerInterceptorAdapter{
@Autowired
MiaoShaUserService userService;
@Autowired
RedisService redisService;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
if(handler instanceof HandlerMethod) {
MiaoshaUser user = getUser(request, response);
UserContext.setUser(user);
HandlerMethod hm = (HandlerMethod)handler;
AccessLimit accessLimit = hm.getMethodAnnotation(AccessLimit.class);
if(accessLimit == null) {
return true;
}
int seconds = accessLimit.seconds();
int maxCount = accessLimit.maxCount();
boolean needLogin = accessLimit.needLogin();
String key = request.getRequestURI();
if(needLogin) {
if(user == null) {
render(response, CodeMsg.SESSION_ERROR);
return false;
}
key += "_" + user.getId();
}else {
//do nothing
}
AccessKey ak = AccessKey.withExpire(seconds);
Integer count = redisService.get(ak, key, Integer.class);
if(count == null) {
redisService.set(ak, key, 1);
}else if(count < maxCount) {
redisService.incr(ak, key);
}else {
render(response, CodeMsg.ACCESS_LIMIT_REACHED);
return false;
}
}
return true;
}
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();
}
private MiaoshaUser getUser(HttpServletRequest request, HttpServletResponse response) {
String paramToken = request.getParameter(MiaoShaUserService.COOKIE_NAME_TOKEN);
String cookieToken = getCookieValue(request, MiaoShaUserService.COOKIE_NAME_TOKEN);
if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
return null;
}
String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
return userService.getByToken(response, token);
}
private String getCookieValue(HttpServletRequest request, String cookiName) {
Cookie[] cookies = request.getCookies();
if(cookies == null || cookies.length <= 0){
return null;
}
for(Cookie cookie : cookies) {
if(cookie.getName().equals(cookiName)) {
return cookie.getValue();
}
}
return null;
}
}
package com.geekq.miaosha.access;
import com.geekq.miaosha.redis.BasePrefix;
public class AccessKey extends BasePrefix {
private AccessKey( int expireSeconds, String prefix) {
super(expireSeconds, prefix);
}
public static AccessKey withExpire(int expireSeconds) {
return new AccessKey(expireSeconds, "access");
}
}
package com.geekq.miaosha.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;
@Retention(RUNTIME)
@Target(METHOD)
public @interface AccessLimit {
int seconds();
int maxCount();
boolean needLogin() default true;
}
package com.geekq.miaosha.access;
import com.geekq.miaosha.domain.MiaoshaUser;
public class UserContext {
private static ThreadLocal<MiaoshaUser> userHolder = new ThreadLocal<MiaoshaUser>();
public static void setUser(MiaoshaUser user) {
userHolder.set(user);
}
public static MiaoshaUser getUser() {
return userHolder.get();
}
}
package com.geekq.miaosha.common;
public class Constanst {
public static String CLOSE_ORDER_INFO_TASK_LOCK = "CLOSE_ORDER_INFO_KEY";
public enum orderStaus{
ORDER_NOT_PAY("新建未支付");
orderStaus(String name){
this.name=name;
}
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
package com.geekq.miaosha.common;
/**
* 响应类
*/
public enum ResponseCode {
SUCCESS(0, "SUCCESS"),
ERROR(1, "ERROR"),
NEED_LOGIN(10, "NEED_LOGIN"),
ILLEGAL_ARGUMENT(2, "ILLEGAL_ARGUMENT");
private final int code;
private final String desc;
private ResponseCode(int code, String desc) {
this.code = code;
this.desc = desc;
}
public int getCode() {
return code;
}
public String getDesc() {
return desc;
}
}
package com.geekq.miaosha.common;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import java.io.Serializable;
@JsonSerialize(include = JsonSerialize.Inclusion.NON_NULL)
//保证序列化的时候 不会有空
public class ServerResponse<T> implements Serializable {
private int status ;
private String msg ;
private T data ;
private ServerResponse( int status ){
this.status =status;
}
private ServerResponse( int status ,T data){
this.status =status;
this.data = data;
}
private ServerResponse( int status ,String msg ,T data){
this.status =status;
this.msg = msg ;
this.data = data;
}
private ServerResponse( int status ,String msg ){
this.status =status;
this.msg = msg ;
}
@JsonIgnore
public boolean isSuccess(){
return this.status == ResponseCode.SUCCESS.getCode();
}
public int getStatus() {
return status;
}
public String getMsg() {
return msg;
}
public T getData() {
return data;
}
public static <T> ServerResponse<T> createBySuccess(){
return new ServerResponse<T>(ResponseCode.SUCCESS.getCode());
}
public static <T> ServerResponse<T> createBySuccessMessage(String msg){
return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(),msg);
}
public static <T> ServerResponse<T> createBySuccess(T data){
return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(),data);
}
public static <T> ServerResponse<T> createBySuccess(String msg,T data){
return new ServerResponse<T>(ResponseCode.SUCCESS.getCode(),msg,data);
}
public static <T> ServerResponse<T> createByError(){
return new ServerResponse<T>(ResponseCode.ERROR.getCode(),ResponseCode.ERROR.getDesc());
}
public static <T> ServerResponse<T> createByErrorMessage(String errorMsg){
return new ServerResponse<T>(ResponseCode.ERROR.getCode(),errorMsg);
}
public static <T> ServerResponse<T> createByErrorCodeMessage(int errorCode , String errorMsg){
return new ServerResponse<T>(errorCode,errorMsg);
}
}
package com.geekq.miaosha.config;
import com.geekq.miaosha.access.UserContext;
import com.geekq.miaosha.domain.MiaoshaUser;
import com.geekq.miaosha.service.MiaoShaUserService;
import org.springframework.beans.factory.annotation.Autowired;
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;
@Service
public class UserArgumentResolver implements HandlerMethodArgumentResolver {
@Autowired
private MiaoShaUserService userService;
@Override
public boolean supportsParameter(MethodParameter methodParameter) {
Class<?> clazz = methodParameter.getParameterType() ;
return clazz == MiaoshaUser.class ;
}
@Override
public Object resolveArgument(MethodParameter methodParameter, ModelAndViewContainer modelAndViewContainer, NativeWebRequest webRequest, WebDataBinderFactory webDataBinderFactory) throws Exception {
// HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
// HttpServletResponse response = webRequest.getNativeResponse(HttpServletResponse.class);
//
// String paramToken = request.getParameter(MiaoShaUserService.COOKIE_NAME_TOKEN);
// String cookieToken = getCookieValue(request, MiaoShaUserService.COOKIE_NAME_TOKEN);
// if(StringUtils.isEmpty(cookieToken) && StringUtils.isEmpty(paramToken)) {
// return null;
// }
// String token = StringUtils.isEmpty(paramToken)?cookieToken:paramToken;
// return userService.getByToken(response, token);
//threadlocal 存储线程副本 保证线程不冲突
return UserContext.getUser();
}
private String getCookieValue(HttpServletRequest request, String cookiName) {
Cookie[] cookies = request.getCookies();
for(Cookie cookie : cookies) {
if(cookie.getName().equals(cookiName)) {
return cookie.getValue();
}
}
return null;
}
}
package com.geekq.miaosha.config;
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.WebMvcConfigurerAdapter;
import java.util.List;
@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {
@Autowired
UserArgumentResolver userArgumentResolver;
@Override
public void addArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
argumentResolvers.add(userArgumentResolver);
}
}
package com.geekq.miaosha.controller;
import com.geekq.miaosha.domain.User;
import com.geekq.miaosha.redis.RedisService;
import com.geekq.miaosha.redis.Userkey;
import com.geekq.miaosha.result.CodeMsg;
import com.geekq.miaosha.result.Result;
import com.geekq.miaosha.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/demo")
public class DemoController {
@Autowired
private UserService userService ;
@Autowired
private RedisService redisService;
@RequestMapping("/")
@ResponseBody
String home(){
return "hello world" ;
}
@RequestMapping("/user")
@ResponseBody
public Result<User> user(){
User user = userService.getById(1);
return Result.success(user) ;
}
@RequestMapping("/hello")
@ResponseBody
public Result<String> hello(){
return Result.success("hello world!");
}
@RequestMapping("/helloError")
@ResponseBody
public Result<String> helloError(){
Map<String,String> map = new HashMap<String, String>() ;
map.put("a","1") ;
return Result.error(CodeMsg.SERVER_ERROR) ;
}
@RequestMapping("/thymeleaf")
public String thymeleaf(Model model ){
model.addAttribute("name","world") ;
return "hello" ;
}
@RequestMapping("/redis/set")
@ResponseBody
public Result<Boolean> redis(){
User user = userService.getById(1);
boolean ret = redisService.set(Userkey.getById,""+1,user);
return Result.success(ret);
}
@RequestMapping("/redis/get")
@ResponseBody
public Result<User> redisG(){
User user = redisService.get(Userkey.getById,""+1,User.class) ;
return Result.success(user) ;
}
}
package com.geekq.miaosha.controller;
import com.geekq.miaosha.domain.MiaoshaUser;
import com.geekq.miaosha.redis.GoodsKey;
import com.geekq.miaosha.redis.RedisService;
import com.geekq.miaosha.result.Result;
import com.geekq.miaosha.service.GoodsService;
import com.geekq.miaosha.service.MiaoShaUserService;
import com.geekq.miaosha.vo.GoodsDetailVo;
import com.geekq.miaosha.vo.GoodsVo;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
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.spring4.context.SpringWebContext;
import org.thymeleaf.spring4.view.ThymeleafViewResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.List;
@Controller
@RequestMapping("/goods")
public class GoodsController {
private static Logger log = LoggerFactory.getLogger(GoodsController.class);
@Autowired
private MiaoShaUserService userService;
@Autowired
private RedisService redisService;
@Autowired
private GoodsService goodsService;
@Autowired
ThymeleafViewResolver thymeleafViewResolver;
@Autowired
ApplicationContext applicationContext;
/**
* QPS:1267 load:15 mysql
* 5000 * 10
* QPS:2884, load:5
* */
@RequestMapping(value="/to_list", produces="text/html")
@ResponseBody
public String list(HttpServletRequest request, HttpServletResponse response, Model model, MiaoshaUser user) {
model.addAttribute("user", user);
//取缓存
String html = redisService.get(GoodsKey.getGoodsList, "", String.class);
if(!StringUtils.isEmpty(html)) {
return html;
}
List<GoodsVo> goodsList = goodsService.listGoodsVo();
model.addAttribute("goodsList", goodsList);
SpringWebContext ctx = new SpringWebContext(request,response,
request.getServletContext(),request.getLocale(), model.asMap(), applicationContext );
//手动渲染
html = thymeleafViewResolver.getTemplateEngine().process("goods_list", ctx);
if(!StringUtils.isEmpty(html)) {
redisService.set(GoodsKey.getGoodsList, "", html);
}
return html;
}
@RequestMapping(value="/to_detail2/{goodsId}",produces="text/html")
@ResponseBody
public String detail2(HttpServletRequest request, HttpServletResponse response, Model model,MiaoshaUser user,
@PathVariable("goodsId")long goodsId) {
model.addAttribute("user", user);
//取缓存
String html = redisService.get(GoodsKey.getGoodsDetail, ""+goodsId, String.class);
if(!StringUtils.isEmpty(html)) {
return html;
}
//手动渲染
GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
model.addAttribute("goods", goods);
long startAt = goods.getStartDate().getTime();
long endAt = goods.getEndDate().getTime();
long now = System.currentTimeMillis();
int miaoshaStatus = 0;
int remainSeconds = 0;
if(now < startAt ) {//秒杀还没开始,倒计时
miaoshaStatus = 0;
remainSeconds = (int)((startAt - now )/1000);
}else if(now > endAt){//秒杀已经结束
miaoshaStatus = 2;
remainSeconds = -1;
}else {//秒杀进行中
miaoshaStatus = 1;
remainSeconds = 0;
}
model.addAttribute("miaoshaStatus", miaoshaStatus);
model.addAttribute("remainSeconds", remainSeconds);
// return "goods_detail";
SpringWebContext ctx = new SpringWebContext(request,response,
request.getServletContext(),request.getLocale(), model.asMap(), applicationContext );
html = thymeleafViewResolver.getTemplateEngine().process("goods_detail", ctx);
if(!StringUtils.isEmpty(html)) {
redisService.set(GoodsKey.getGoodsDetail, ""+goodsId, html);
}
return html;
}
/**
* 数据库很少使用long的 , id 正常使一般使用 snowflake 分布式自增id
* @param model
* @param user
* @param goodsId
* @return
*/
@RequestMapping(value="/detail/{goodsId}")
@ResponseBody
public Result<GoodsDetailVo> detail(HttpServletRequest request, HttpServletResponse response, Model model,MiaoshaUser user,
@PathVariable("goodsId")long goodsId) {
GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
long startAt = goods.getStartDate().getTime();
long endAt = goods.getEndDate().getTime();
long now = System.currentTimeMillis();
int miaoshaStatus = 0;
int remainSeconds = 0;
if(now < startAt ) {//秒杀还没开始,倒计时
miaoshaStatus = 0;
remainSeconds = (int)((startAt - now )/1000);
}else if(now > endAt){//秒杀已经结束
miaoshaStatus = 2;
remainSeconds = -1;
}else {//秒杀进行中
miaoshaStatus = 1;
remainSeconds = 0;
}
GoodsDetailVo vo = new GoodsDetailVo();
vo.setGoods(goods);
vo.setUser(user);
vo.setRemainSeconds(remainSeconds);
vo.setMiaoshaStatus(miaoshaStatus);
return Result.success(vo);
}
}
package com.geekq.miaosha.controller;
import com.geekq.miaosha.dao.UserMapper;
import com.geekq.miaosha.domain.MiaoshaUser;
import com.geekq.miaosha.result.Result;
import com.geekq.miaosha.service.MiaoShaUserService;
import com.geekq.miaosha.vo.LoginVo;
import org.apache.ibatis.annotations.Mapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
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.HttpServletResponse;
import javax.validation.Valid;
import java.util.ArrayList;
import java.util.List;
@Controller
@RequestMapping("/login")
public class LoginController {
private static Logger log = LoggerFactory.getLogger(LoginController.class);
@Autowired
private MiaoShaUserService userService;
@Autowired
private UserMapper userMapper;
@RequestMapping("/to_login")
public String tologin(LoginVo loginVo) {
log.info(loginVo.toString());
return "login";
}
@RequestMapping("/do_login")
@ResponseBody
public Result<Boolean> dologin(HttpServletResponse response, @Valid LoginVo loginVo) {
log.info(loginVo.toString());
userService.login(response, loginVo);
return Result.success(true);
}
@RequestMapping(value = "/register", method = RequestMethod.POST)
public Result<String> register(MiaoshaUser user) {
return userService.insertMiaoShaUser(user);
}
@RequestMapping("/mybatis")
public void mybatisXml() {
//
int count = userMapper.getMiaoShaUserNum();
System.out.println(count);
MiaoshaUser user = userMapper.getMiaoShaUserById(Long.valueOf("18612766134"));
System.out.println(user);
//
// MiaoshaUser miaoshaUser =new MiaoshaUser();
// miaoshaUser.setId(Long.valueOf("1234569879"));
// miaoshaUser.setNickname("test");
// Long num = userMapper.insertMiaoShaUser(miaoshaUser);
// MiaoshaUser upmiaoshaUser =new MiaoshaUser();
// upmiaoshaUser.setId(Long.valueOf("1234569879"));
// upmiaoshaUser.setNickname("test1");
// userMapper.updateMiaoShaUser(upmiaoshaUser);
//
// userMapper.deleteMiaoShaUser(Long.valueOf("1234569879"));
List<MiaoshaUser> lists = new ArrayList<MiaoshaUser>();
MiaoshaUser upmiaoshaUser =new MiaoshaUser();
upmiaoshaUser.setId(Long.valueOf("1234569879"));
upmiaoshaUser.setNickname("test1");
MiaoshaUser upmiaoshaUser1 =new MiaoshaUser();
upmiaoshaUser1.setId(Long.valueOf("1234569872"));
upmiaoshaUser1.setNickname("test1");
lists.add(upmiaoshaUser);
lists.add(upmiaoshaUser1);
userMapper.insertMiaoShaUserValues(lists);
}
}
package com.geekq.miaosha.controller;
import com.geekq.miaosha.access.AccessLimit;
import com.geekq.miaosha.domain.MiaoshaOrder;
import com.geekq.miaosha.domain.MiaoshaUser;
import com.geekq.miaosha.rabbitmq.MQSender;
import com.geekq.miaosha.rabbitmq.MiaoshaMessage;
import com.geekq.miaosha.redis.GoodsKey;
import com.geekq.miaosha.redis.RedisService;
import com.geekq.miaosha.result.CodeMsg;
import com.geekq.miaosha.result.Result;
import com.geekq.miaosha.service.GoodsService;
import com.geekq.miaosha.service.MiaoShaUserService;
import com.geekq.miaosha.service.MiaoshaService;
import com.geekq.miaosha.service.OrderService;
import com.geekq.miaosha.vo.GoodsVo;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.*;
import javax.imageio.ImageIO;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.image.BufferedImage;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.List;
@Controller
@RequestMapping("/miaosha")
public class MiaoshaController implements InitializingBean {
@Autowired
MiaoShaUserService userService;
@Autowired
RedisService redisService;
@Autowired
GoodsService goodsService;
@Autowired
OrderService orderService;
@Autowired
MiaoshaService miaoshaService;
@Autowired
MQSender mqSender ;
private HashMap<Long, Boolean> localOverMap = new HashMap<Long, Boolean>();
/**
* QPS:1306
* 5000 * 10
* get post get 幂等 从服务端获取数据 不会产生影响  post 对服务端产生变化 
* */
@AccessLimit(seconds = 5,maxCount = 5,needLogin = true)
@RequestMapping(value="/{path}/do_miaosha", method= RequestMethod.POST)
@ResponseBody
public Result<Integer> miaosha(Model model, MiaoshaUser user,
@RequestParam("goodsId")long goodsId,@PathVariable("path") String path) {
if (user == null) {
return Result.error(CodeMsg.SESSION_ERROR);
}
//验证path
boolean check = miaoshaService.checkPath(user, goodsId, path);
if(!check){
return Result.error(CodeMsg.REQUEST_ILLEGAL);
}
// //使用RateLimiter 限流
// RateLimiter rateLimiter = RateLimiter.create(10);
// //判断能否在1秒内得到令牌,如果不能则立即返回false,不会阻塞程序
// if (!rateLimiter.tryAcquire(1000, TimeUnit.MILLISECONDS)) {
// System.out.println("短期无法获取令牌,真不幸,排队也瞎排");
// return Result.error(CodeMsg.MIAOSHA_FAIL);
//
// }
//是否已经秒杀到
MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
if(order!=null){
return Result.error(CodeMsg.REPEATE_MIAOSHA) ;
}
//内存标记,减少redis访问
boolean over = localOverMap.get(goodsId);
if(over) {
return Result.error(CodeMsg.MIAO_SHA_OVER);
}
//预见库存
long stock = redisService.decr(GoodsKey.getMiaoshaGoodsStock,""+goodsId) ;
if(stock <0){
localOverMap.put(goodsId, true);
return Result.error(CodeMsg.MIAO_SHA_OVER);
}
MiaoshaMessage mm = new MiaoshaMessage();
mm.setGoodsId(goodsId);
mm.setUser(user);
mqSender.sendMiaoshaMessage(mm);
return Result.success(0);
}
/**
* orderId:成功
* -1:秒杀失败
* 0: 排队中
* */
@AccessLimit(seconds = 5,maxCount = 5,needLogin = true)
@RequestMapping(value="/result", method=RequestMethod.GET)
@ResponseBody
public Result<Long> miaoshaResult(Model model,MiaoshaUser user,
@RequestParam("goodsId")long goodsId) {
model.addAttribute("user", user);
if(user == null) {
return Result.error(CodeMsg.SESSION_ERROR);
}
long result =miaoshaService.getMiaoshaResult(user.getId(), goodsId);
return Result.success(result);
}
@AccessLimit(seconds = 5,maxCount = 5,needLogin = true)
@RequestMapping(value="/path", method=RequestMethod.GET)
@ResponseBody
public Result<String> getMiaoshaPath(HttpServletRequest request, MiaoshaUser user,
@RequestParam("goodsId")long goodsId,
@RequestParam(value="verifyCode", defaultValue="0")int verifyCode
) {
if(user == null) {
return Result.error(CodeMsg.SESSION_ERROR);
}
boolean check = miaoshaService.checkVerifyCode(user, goodsId, verifyCode);
if(!check) {
return Result.error(CodeMsg.REQUEST_ILLEGAL);
}
String path =miaoshaService.createMiaoshaPath(user, goodsId);
return Result.success(path);
}
@RequestMapping(value="/verifyCode", method=RequestMethod.GET)
@ResponseBody
public Result<String> getMiaoshaVerifyCod(HttpServletResponse response, MiaoshaUser user,
@RequestParam("goodsId")long goodsId) {
if(user == null) {
return Result.error(CodeMsg.SESSION_ERROR);
}
try {
BufferedImage image = miaoshaService.createVerifyCode(user, goodsId);
OutputStream out = response.getOutputStream();
ImageIO.write(image, "JPEG", out);
out.flush();
out.close();
return null;
}catch(Exception e) {
e.printStackTrace();
return Result.error(CodeMsg.MIAOSHA_FAIL);
}
}
@Override
public void afterPropertiesSet() throws Exception {
List<GoodsVo> goodsList= goodsService.listGoodsVo();
if(goodsList ==null){
return;
}
for (GoodsVo goods :goodsList ){
redisService.set(GoodsKey.getMiaoshaGoodsStock,""+goods.getId(),goods.getStockCount()) ;
localOverMap.put(goods.getId(), false);
}
}
}
package com.geekq.miaosha.controller;
import com.geekq.miaosha.domain.MiaoshaUser;
import com.geekq.miaosha.domain.OrderInfo;
import com.geekq.miaosha.redis.RedisService;
import com.geekq.miaosha.result.CodeMsg;
import com.geekq.miaosha.result.Result;
import com.geekq.miaosha.service.GoodsService;
import com.geekq.miaosha.service.MiaoShaUserService;
import com.geekq.miaosha.service.OrderService;
import com.geekq.miaosha.vo.GoodsVo;
import com.geekq.miaosha.vo.OrderDetailVo;
import org.springframework.beans.factory.annotation.Autowired;
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;
@Controller
@RequestMapping("/order")
public class OrderController {
@Autowired
MiaoShaUserService userService;
@Autowired
RedisService redisService;
@Autowired
OrderService orderService;
@Autowired
GoodsService goodsService;
@RequestMapping("/detail")
@ResponseBody
public Result<OrderDetailVo> info(Model model, MiaoshaUser 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.setOrder(order);
vo.setGoods(goods);
return Result.success(vo);
}
}
package com.geekq.miaosha.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/ratelimiter")
public class RateLimiterController {
}
package com.geekq.miaosha.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import javax.servlet.http.HttpSession;
@Controller
@RequestMapping("/user/")
public class UserController {
/**
* 用户登录
* @param username
* @param password
* @param session
* @return
*/
@RequestMapping(value = "/login",method = RequestMethod.POST)
public Object login(String username , String password , HttpSession session){
return null;
}
}
package com.geekq.miaosha.dao;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
@Configuration
@ConfigurationProperties(prefix="spring.datasource")
public class DruidConfig {
private String url;
private String username;
private String password;
private String driverClassName;
private String type;
private String filters;
private int maxActive;
private int initialSize;
private int minIdle;
private long maxWait;
private long timeBetweenEvictionRunsMillis;
private long minEvictableIdleTimeMillis;
private String validationQuery;
private boolean testWhileIdle;
private boolean testOnBorrow;
private boolean testOnReturn;
private boolean poolPreparedStatements;
private int maxOpenPreparedStatements;
@Bean
public ServletRegistrationBean druidSverlet() {
ServletRegistrationBean reg = new ServletRegistrationBean();
reg.setServlet(new StatViewServlet());
reg.addUrlMappings("/druid/*");
reg.addInitParameter("loginUsername", "joshua");
reg.addInitParameter("loginPassword", "123456");
reg.addInitParameter("logSlowSql", "true");
reg.addInitParameter("slowSqlMillis", "1000");
return reg;
}
@Bean
public DataSource druidDataSource() {
DruidDataSource datasource = new DruidDataSource();
datasource.setUrl(url);
datasource.setUsername(username);
datasource.setPassword(password);
datasource.setDriverClassName(driverClassName);
datasource.setInitialSize(initialSize);
datasource.setMinIdle(minIdle);
datasource.setMaxActive(maxActive);
datasource.setMaxWait(maxWait);
datasource.setTimeBetweenEvictionRunsMillis(timeBetweenEvictionRunsMillis);
datasource.setMinEvictableIdleTimeMillis(minEvictableIdleTimeMillis);
datasource.setValidationQuery(validationQuery);
datasource.setTestWhileIdle(testWhileIdle);
datasource.setTestOnBorrow(testOnBorrow);
datasource.setTestOnReturn(testOnReturn);
datasource.setPoolPreparedStatements(poolPreparedStatements);
datasource.setMaxOpenPreparedStatements(maxOpenPreparedStatements);
try {
datasource.setFilters(filters);
} catch (SQLException e) {
e.printStackTrace();
}
return datasource;
}
public String getUrl() {
return url;
}
public void setUrl(String url) {
this.url = url;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getDriverClassName() {
return driverClassName;
}
public void setDriverClassName(String driverClassName) {
this.driverClassName = driverClassName;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getFilters() {
return filters;
}
public void setFilters(String filters) {
this.filters = filters;
}
public int getMaxActive() {
return maxActive;
}
public void setMaxActive(int maxActive) {
this.maxActive = maxActive;
}
public int getInitialSize() {
return initialSize;
}
public void setInitialSize(int initialSize) {
this.initialSize = initialSize;
}
public int getMinIdle() {
return minIdle;
}
public void setMinIdle(int minIdle) {
this.minIdle = minIdle;
}
public long getMaxWait() {
return maxWait;
}
public void setMaxWait(long maxWait) {
this.maxWait = maxWait;
}
public long getTimeBetweenEvictionRunsMillis() {
return timeBetweenEvictionRunsMillis;
}
public void setTimeBetweenEvictionRunsMillis(long timeBetweenEvictionRunsMillis) {
this.timeBetweenEvictionRunsMillis = timeBetweenEvictionRunsMillis;
}
public long getMinEvictableIdleTimeMillis() {
return minEvictableIdleTimeMillis;
}
public void setMinEvictableIdleTimeMillis(long minEvictableIdleTimeMillis) {
this.minEvictableIdleTimeMillis = minEvictableIdleTimeMillis;
}
public String getValidationQuery() {
return validationQuery;
}
public void setValidationQuery(String validationQuery) {
this.validationQuery = validationQuery;
}
public boolean isTestWhileIdle() {
return testWhileIdle;
}
public void setTestWhileIdle(boolean testWhileIdle) {
this.testWhileIdle = testWhileIdle;
}
public boolean isTestOnBorrow() {
return testOnBorrow;
}
public void setTestOnBorrow(boolean testOnBorrow) {
this.testOnBorrow = testOnBorrow;
}
public boolean isTestOnReturn() {
return testOnReturn;
}
public void setTestOnReturn(boolean testOnReturn) {
this.testOnReturn = testOnReturn;
}
public boolean isPoolPreparedStatements() {
return poolPreparedStatements;
}
public void setPoolPreparedStatements(boolean poolPreparedStatements) {
this.poolPreparedStatements = poolPreparedStatements;
}
public int getMaxOpenPreparedStatements() {
return maxOpenPreparedStatements;
}
public void setMaxOpenPreparedStatements(int maxOpenPreparedStatements) {
this.maxOpenPreparedStatements = maxOpenPreparedStatements;
}
}
package com.geekq.miaosha.dao;
import com.geekq.miaosha.domain.MiaoshaGoods;
import com.geekq.miaosha.vo.GoodsVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;
import java.util.List;
@Mapper
public interface GoodsDao {
@Select("select g.*,mg.stock_count, mg.start_date, mg.end_date,mg.miaosha_price from miaosha_goods mg left join goods g on mg.goods_id = g.id")
public List<GoodsVo> listGoodsVo();
@Select("select g.*,mg.stock_count, mg.start_date, mg.end_date,mg.miaosha_price from miaosha_goods mg left join goods g on mg.goods_id = g.id where g.id = #{goodsId}")
public GoodsVo getGoodsVoByGoodsId(@Param("goodsId") long goodsId);
@Update("update miaosha_goods set stock_count = stock_count - 1 where goods_id = #{goodsId} and stock_count > 0")
public int reduceStock(MiaoshaGoods g);
}
package com.geekq.miaosha.dao;
import com.geekq.miaosha.domain.MiaoshaUser;
import org.apache.ibatis.annotations.*;
@Mapper
public interface MiaoShaUserDao {
@Select("select * from miaosha_user where id = #{id}")
public MiaoshaUser getById(@Param("id") long id ) ;
@Update("update miaosha_user set password = #{password} where id = #{id}")
public void update(MiaoshaUser toBeUpdate);
@Insert("insert into miaosha_user (id , nickname ,password , salt ,head,register_date,last_login_date)value (#{id},#{nickname},#{password},#{salt},#{nickname},#{head},#{register_date},#{last_login_date}) ")
public long insertMiaoShaUser(MiaoshaUser miaoshaUser);
}
\ No newline at end of file
package com.geekq.miaosha.dao;
import com.geekq.miaosha.domain.MiaoshaOrder;
import com.geekq.miaosha.domain.OrderInfo;
import org.apache.ibatis.annotations.*;
import java.util.List;
@Mapper
public interface OrderDao {
@Select("select * from miaosha_order where user_id=#{userId} and goods_id=#{goodsId}")
public MiaoshaOrder getMiaoshaOrderByUserIdGoodsId(@Param("userId") long userId, @Param("goodsId") long goodsId);
@Insert("insert into order_info(user_id, goods_id, goods_name, goods_count, goods_price, order_channel, status, create_date)values("
+ "#{userId}, #{goodsId}, #{goodsName}, #{goodsCount}, #{goodsPrice}, #{orderChannel},#{status},#{createDate} )")
@SelectKey(keyColumn="id", keyProperty="id", resultType=long.class, before=false, statement="select last_insert_id()")
public long insert(OrderInfo orderInfo);
@Insert("insert into miaosha_order (user_id, goods_id, order_id)values(#{userId}, #{goodsId}, #{orderId})")
public int insertMiaoshaOrder(MiaoshaOrder miaoshaOrder);
@Select("select * from order_info where id = #{orderId}")
public OrderInfo getOrderById(@Param("orderId")long orderId);
@Select("select * from order_info where status=#{status} and create_Date<=#{createDate}")
public List<OrderInfo> selectOrderStatusByCreateTime(@Param("status")Integer status, @Param("createDate") String createDate);
@Select("update order_info set status=0 where id=#{id}")
public int closeOrderByOrderInfo();
}
package com.geekq.miaosha.dao;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;
import com.geekq.miaosha.domain.User;
@Mapper
public interface UserDao {
@Select("select * from user where id = #{id}")
public User getById(@Param("id")int id);
@Insert("insert into user (id, name) values(#{id}, #{name})")
public int insert(User user);
}
package com.geekq.miaosha.dao;
import com.geekq.miaosha.domain.MiaoshaUser;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
/**
* mybatis xml 写法
*/
@Mapper
public interface UserMapper {
int countbyMenuId();
MiaoshaUser getMiaoShaUserById(Long phoneId);
//秒杀对象获取 mybatis的#与$的区别
List<MiaoshaUser> queryMiaoShaUserOrderByColumn(String column);
//查询总条数
public int getMiaoShaUserNum();
public Long insertMiaoShaUser(MiaoshaUser user);
void insertMiaoShaUserValues(List<MiaoshaUser> miaoshaUsers);
//更改表数据
void updateMiaoShaUser(MiaoshaUser user) ;
//删除表数据
void deleteMiaoShaUser(long id );
}
package com.geekq.miaosha.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class Goods {
private Long id;
private String goodsName;
private String goodsTitle;
private String goodsImg;
private String goodsDetail;
private Double goodsPrice;
private Integer goodsStock;
}
package com.geekq.miaosha.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.Date;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class MiaoshaGoods {
private Long id;
private Long goodsId;
private Integer stockCount;
private Date startDate;
private Date endDate;
}
package com.geekq.miaosha.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class MiaoshaOrder {
private Long id;
private Long userId;
private Long orderId;
private Long goodsId;
}
package com.geekq.miaosha.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.Date;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class MiaoshaUser {
private Long id;
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 "MiaoshaUser{" +
"id=" + id +
", nickname='" + nickname + '\'' +
", password='" + password + '\'' +
", salt='" + salt + '\'' +
", head='" + head + '\'' +
", registerDate=" + registerDate +
", lastLoginDate=" + lastLoginDate +
", loginCount=" + loginCount +
'}';
}
}
package com.geekq.miaosha.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import java.util.Date;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class OrderInfo {
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;
}
package com.geekq.miaosha.domain;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class User {
private int id;
private String name;
}
\ No newline at end of file
package com.geekq.miaosha.exception;
import com.geekq.miaosha.result.CodeMsg;
public class GlobleException extends RuntimeException {
private CodeMsg cm ;
public GlobleException(CodeMsg cm){
super();
this.cm = cm;
}
public CodeMsg getCm() {
return cm;
}
}
package com.geekq.miaosha.exception;
import com.geekq.miaosha.result.CodeMsg;
import com.geekq.miaosha.result.Result;
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;
@ControllerAdvice
@ResponseBody
public class GlobleExceptionHandler {
@ExceptionHandler(value=Exception.class)
public Result<String> exceptionHandler(HttpServletRequest request , Exception e){
e.printStackTrace();
if(e instanceof GlobleException){
GlobleException ex= (GlobleException)e;
return Result.error(ex.getCm());
}else if( e instanceof BindException){
BindException ex = (BindException) e ;
List<ObjectError> errors = ex.getAllErrors();
ObjectError error = errors.get(0);
String msg = error.getDefaultMessage();
return Result.error(CodeMsg.BIND_ERROR.fillArgs(msg));
}else {
return Result.error(CodeMsg.SERVER_ERROR);
}
}
}
### 前言 mybatis的一些使用
SqlSession 的作用<br>
1.像sql中传入语句<br>
2.执行SQL语句<br>
3.获取执行SQL语句的结果<br>
4.事物的控制<br>
### 如何获得sqlsession?
1. 配置文件获取数据库链接相关信息<br>
2.通过配置文件来构建sqlsessionfactory<br>
3.通过sqlsessionfactory打开数据库会话<br>
package com.geekq.miaosha.mybatis.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping("/message")
public class MessageController {
@RequestMapping("index")
public String index(){
return "message";
}
}
package com.geekq.miaosha.rabbitmq;
import org.springframework.amqp.core.*;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
import java.util.Map;
@Configuration
public class MQConfig {
public static final String MIAOSHA_QUEUE = "miaosha.queue";
public static final String QUEUE = "queue";
public static final String TOPIC_QUEUE1 = "topic.queue1";
public static final String TOPIC_QUEUE2 = "topic.queue2";
public static final String HEADER_QUEUE = "header.queue";
public static final String TOPIC_EXCHANGE = "topicExchage";
public static final String FANOUT_EXCHANGE = "fanoutxchage";
public static final String HEADERS_EXCHANGE = "headersExchage";
/**
* Direct模式 交换机Exchange
* */
@Bean
public Queue queue() {
return new Queue(QUEUE, true);
}
/**
* Topic模式 交换机Exchange
* */
@Bean
public Queue topicQueue1() {
return new Queue(TOPIC_QUEUE1, true);
}
@Bean
public Queue topicQueue2() {
return new Queue(TOPIC_QUEUE2, true);
}
@Bean
public TopicExchange topicExchage(){
return new TopicExchange(TOPIC_EXCHANGE);
}
@Bean
public Binding topicBinding1() {
return BindingBuilder.bind(topicQueue1()).to(topicExchage()).with("topic.key1");
}
@Bean
public Binding topicBinding2() {
return BindingBuilder.bind(topicQueue2()).to(topicExchage()).with("topic.#");
}
/**
* Fanout模式 交换机Exchange
* */
@Bean
public FanoutExchange fanoutExchage(){
return new FanoutExchange(FANOUT_EXCHANGE);
}
@Bean
public Binding FanoutBinding1() {
return BindingBuilder.bind(topicQueue1()).to(fanoutExchage());
}
@Bean
public Binding FanoutBinding2() {
return BindingBuilder.bind(topicQueue2()).to(fanoutExchage());
}
/**
* Header模式 交换机Exchange
* */
@Bean
public HeadersExchange headersExchage(){
return new HeadersExchange(HEADERS_EXCHANGE);
}
@Bean
public Queue headerQueue1() {
return new Queue(HEADER_QUEUE, true);
}
@Bean
public Binding headerBinding() {
Map<String, Object> map = new HashMap<String, Object>();
map.put("header1", "value1");
map.put("header2", "value2");
return BindingBuilder.bind(headerQueue1()).to(headersExchage()).whereAll(map).match();
}
}
package com.geekq.miaosha.rabbitmq;
import com.geekq.miaosha.domain.MiaoshaOrder;
import com.geekq.miaosha.domain.MiaoshaUser;
import com.geekq.miaosha.redis.RedisService;
import com.geekq.miaosha.service.GoodsService;
import com.geekq.miaosha.service.MiaoshaService;
import com.geekq.miaosha.service.OrderService;
import com.geekq.miaosha.vo.GoodsVo;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MQReceiver {
private static Logger log = LoggerFactory.getLogger(MQReceiver.class);
@Autowired
RedisService redisService;
@Autowired
GoodsService goodsService;
@Autowired
OrderService orderService;
@Autowired
MiaoshaService miaoshaService;
@RabbitListener(queues=MQConfig.MIAOSHA_QUEUE)
public void receive(String message) {
log.info("receive message:"+message);
MiaoshaMessage mm = RedisService.stringToBean(message, MiaoshaMessage.class);
MiaoshaUser user = mm.getUser();
long goodsId = mm.getGoodsId();
GoodsVo goods = goodsService.getGoodsVoByGoodsId(goodsId);
int stock = goods.getStockCount();
if(stock <= 0) {
return;
}
//判断是否已经秒杀到了
MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(user.getId(), goodsId);
if(order != null) {
return;
}
//减库存 下订单 写入秒杀订单
miaoshaService.miaosha(user, goods);
}
}
package com.geekq.miaosha.rabbitmq;
import com.geekq.miaosha.redis.RedisService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class MQSender {
private static Logger log = LoggerFactory.getLogger(MQSender.class);
@Autowired
AmqpTemplate amqpTemplate ;
public void sendMiaoshaMessage(MiaoshaMessage mm) {
String msg = RedisService.beanToString(mm);
log.info("send message:"+msg);
amqpTemplate.convertAndSend(MQConfig.MIAOSHA_QUEUE, msg);
}
}
package com.geekq.miaosha.rabbitmq;
import com.geekq.miaosha.domain.MiaoshaUser;
public class MiaoshaMessage {
private MiaoshaUser user;
private long goodsId;
public MiaoshaUser getUser() {
return user;
}
public void setUser(MiaoshaUser user) {
this.user = user;
}
public long getGoodsId() {
return goodsId;
}
public void setGoodsId(long goodsId) {
this.goodsId = goodsId;
}
}
package com.geekq.miaosha.redis;
public abstract class BasePrefix implements KeyPrefix {
private int expireSeconds;
private String prefix ;
public BasePrefix(int expireSeconds , String prefix ){
this.expireSeconds = expireSeconds ;
this.prefix = prefix;
}
public BasePrefix(String prefix) {
this(0,prefix);
}
@Override
public int expireSeconds() {//默认0代表永远过期
return expireSeconds;
}
/**
* 可确定获取唯一key
* @return
*/
@Override
public String getPrefix() {
String className = getClass().getSimpleName();
return className+":" +prefix;
}
}
package com.geekq.miaosha.redis;
import java.util.concurrent.TimeUnit;
public interface DistributedLocker {
void lock(String lockKey);
void unlock(String lockKey);
void lock(String lockKey, int timeout);
void lock(String lockKey, TimeUnit unit , int timeout);
}
package com.geekq.miaosha.redis;
public class GoodsKey extends BasePrefix{
private GoodsKey(int expireSeconds, String prefix) {
super(expireSeconds, prefix);
}
public static GoodsKey getGoodsList = new GoodsKey(60, "gl");
public static GoodsKey getGoodsDetail = new GoodsKey(60, "gd");
public static GoodsKey getMiaoshaGoodsStock = new GoodsKey(0, "gs");
}
package com.geekq.miaosha.redis;
public interface KeyPrefix {
public int expireSeconds() ;
public String getPrefix() ;
}
package com.geekq.miaosha.redis;
public class MiaoShaUserKey extends BasePrefix{
public static final int TOKEN_EXPIRE = 3600 *24*2;
public static MiaoShaUserKey token = new MiaoShaUserKey(TOKEN_EXPIRE,"tk") ;
public static MiaoShaUserKey getById = new MiaoShaUserKey(0, "id");
public MiaoShaUserKey(int expireSeconds ,String prefix) {
super(expireSeconds,prefix);
}
}
package com.geekq.miaosha.redis;
public class MiaoshaKey extends BasePrefix{
private MiaoshaKey( int expireSeconds, String prefix) {
super(expireSeconds, prefix);
}
public static MiaoshaKey isGoodsOver = new MiaoshaKey(0, "go");
public static MiaoshaKey getMiaoshaPath = new MiaoshaKey(60, "mp");
public static MiaoshaKey getMiaoshaVerifyCode = new MiaoshaKey(300, "vc");
}
package com.geekq.miaosha.redis;
public class OrderKey extends BasePrefix {
public OrderKey( String prefix) {
super( prefix);
}
public static OrderKey getMiaoshaOrderByUidGid = new OrderKey("moug");
}
package com.geekq.miaosha.redis;
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 String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
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;
}
}
package com.geekq.miaosha.redis;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;
@Service
public class RedisPoolFactory {
@Autowired
RedisConfig redisConfig;
@Bean
public JedisPool JedisPoolFactory() {
JedisPoolConfig poolConfig = new JedisPoolConfig();
poolConfig.setMaxIdle(redisConfig.getPoolMaxIdle());
poolConfig.setMaxTotal(redisConfig.getPoolMaxTotal());
poolConfig.setMaxWaitMillis(redisConfig.getPoolMaxWait() * 1000);
JedisPool jp = new JedisPool(poolConfig, redisConfig.getHost(), redisConfig.getPort(),
redisConfig.getTimeout()*1000, redisConfig.getPassword(), 0);
return jp;
}
}
package com.geekq.miaosha.redis;
import com.alibaba.fastjson.JSON;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.*;
import java.util.ArrayList;
import java.util.List;
@Service
@Slf4j
public class RedisService {
@Autowired
JedisPool jedisPool;
/**
* 设置失效时间
* @param key
* @param value
* @return
*/
public Long setnx(String key ,String value){
Jedis jedis =null;
Long result = null;
try {
jedis = jedisPool.getResource();
result = jedis.setnx(key,value);
}catch (Exception e){
log.error("expire key:{} error",key,e);
jedisPool.returnResource(jedis);
return result;
}
jedisPool.returnResource(jedis);
return result;
}
/**
* 设置key的有效期,单位是秒
* @param key
* @param exTime
* @return
*/
public Long expire(String key,int exTime){
Jedis jedis = null;
Long result = null;
try {
jedis = jedisPool.getResource();
result = jedis.expire(key,exTime);
} catch (Exception e) {
log.error("expire key:{} error",key,e);
jedisPool.returnBrokenResource(jedis);
return result;
}
jedisPool.returnResource(jedis);
return result;
}
/**
* 获取当个对象
* */
public <T> T get(KeyPrefix prefix, String key, Class<T> clazz) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
String str = jedis.get(realKey);
T t = stringToBean(str, clazz);
return t;
}finally {
returnToPool(jedis);
}
}
public String get(String key){
Jedis jedis = null;
String result = null;
try {
jedis = jedisPool.getResource();
result = jedis.get(key);
} catch (Exception e) {
log.error("expire key:{} error",key,e);
jedisPool.returnBrokenResource(jedis);
return result;
}
jedisPool.returnResource(jedis);
return result;
}
public String getset(String key,String value){
Jedis jedis = null;
String result = null;
try {
jedis = jedisPool.getResource();
result = jedis.getSet(key,value);
} catch (Exception e) {
log.error("expire key:{} error",key,e);
jedisPool.returnBrokenResource(jedis);
return result;
}
jedisPool.returnResource(jedis);
return result;
}
/**
* 设置对象
* */
public <T> boolean set(KeyPrefix prefix, String key, T value) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
String str = beanToString(value);
if(str == null || str.length() <= 0) {
return false;
}
//生成真正的key
String realKey = prefix.getPrefix() + key;
int seconds = prefix.expireSeconds();
if(seconds <= 0) {
jedis.set(realKey, str);
}else {
jedis.setex(realKey, seconds, str);
}
return true;
}finally {
returnToPool(jedis);
}
}
/**
* 判断key是否存在
* */
public <T> boolean exists(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
return jedis.exists(realKey);
}finally {
returnToPool(jedis);
}
}
/**
* 删除
* */
public boolean delete(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
long ret = jedis.del(realKey);
return ret > 0;
}finally {
returnToPool(jedis);
}
}
/**
* 增加值
* */
public <T> Long incr(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
return jedis.incr(realKey);
}finally {
returnToPool(jedis);
}
}
/**
* 减少值
* */
public <T> Long decr(KeyPrefix prefix, String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
//生成真正的key
String realKey = prefix.getPrefix() + key;
return jedis.decr(realKey);
}finally {
returnToPool(jedis);
}
}
public Long del(String key){
Jedis jedis = null;
Long result = null;
try {
jedis = jedisPool.getResource();
result = jedis.del(key);
} catch (Exception e) {
log.error("del key:{} error",key,e);
jedisPool.returnBrokenResource(jedis);
return result;
}
jedisPool.returnResource(jedis);
return result;
}
public boolean delete(KeyPrefix prefix) {
if(prefix == null) {
return false;
}
List<String> keys = scanKeys(prefix.getPrefix());
if(keys==null || keys.size() <= 0) {
return true;
}
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
jedis.del(keys.toArray(new String[0]));
return true;
} catch (final Exception e) {
e.printStackTrace();
return false;
} finally {
if(jedis != null) {
jedis.close();
}
}
}
public List<String> scanKeys(String key) {
Jedis jedis = null;
try {
jedis = jedisPool.getResource();
List<String> keys = new ArrayList<String>();
String cursor = "0";
ScanParams sp = new ScanParams();
sp.match("*"+key+"*");
sp.count(100);
do{
ScanResult<String> ret = jedis.scan(cursor, sp);
List<String> result = ret.getResult();
if(result!=null && result.size() > 0){
keys.addAll(result);
}
//再处理cursor
cursor = ret.getStringCursor();
}while(!cursor.equals("0"));
return keys;
} finally {
if (jedis != null) {
jedis.close();
}
}
}
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 == String.class) {
return (String)value;
}else if(clazz == long.class || clazz == Long.class) {
return ""+value;
}else {
return JSON.toJSONString(value);
}
}
@SuppressWarnings("unchecked")
public static <T> T stringToBean(String str, Class<T> clazz) {
if(str == null || str.length() <= 0 || clazz == null) {
return null;
}
if(clazz == int.class || clazz == Integer.class) {
return (T)Integer.valueOf(str);
}else if(clazz == String.class) {
return (T)str;
}else if(clazz == long.class || clazz == Long.class) {
return (T)Long.valueOf(str);
}else {
return JSON.toJavaObject(JSON.parseObject(str), clazz);
}
}
private void returnToPool(Jedis jedis) {
if(jedis != null) {
jedis.close();
}
}
}
package com.geekq.miaosha.redis;
import java.util.concurrent.TimeUnit;
/**
* redis分布式锁帮助类
*
*/
public class RedissLockUtil {
private static DistributedLocker redissLock;
public static void setLocker(DistributedLocker locker) {
redissLock = locker;
}
public static void lock(String lockKey) {
redissLock.lock(lockKey);
}
public static void unlock(String lockKey) {
redissLock.unlock(lockKey);
}
/**
* 带超时的锁
* @param lockKey
* @param timeout 超时时间 单位:秒
*/
public static void lock(String lockKey, int timeout) {
redissLock.lock(lockKey, timeout);
}
/**
* 带超时的锁
* @param lockKey
* @param unit 时间单位
* @param timeout 超时时间
*/
public static void lock(String lockKey, TimeUnit unit , int timeout) {
redissLock.lock(lockKey, unit, timeout);
}
}
\ No newline at end of file
package com.geekq.miaosha.redis;
import org.apache.commons.lang3.StringUtils;
import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.SentinelServersConfig;
import org.redisson.config.SingleServerConfig;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConditionalOnClass(Config.class)
@EnableConfigurationProperties(RedissonProperties.class)
public class RedissonAutoConfiguration {
@Autowired
private RedissonProperties redssionProperties;
/**
* 哨兵模式自动装配
* @return
*/
@Bean
@ConditionalOnProperty(name="redisson.master-name")
RedissonClient redissonSentinel() {
Config config = new Config();
SentinelServersConfig serverConfig = config.useSentinelServers().addSentinelAddress(redssionProperties.getSentinelAddresses())
.setMasterName(redssionProperties.getMasterName())
.setTimeout(redssionProperties.getTimeout())
.setMasterConnectionPoolSize(redssionProperties.getMasterConnectionPoolSize())
.setSlaveConnectionPoolSize(redssionProperties.getSlaveConnectionPoolSize());
if(StringUtils.isNotBlank(redssionProperties.getPassword())) {
serverConfig.setPassword(redssionProperties.getPassword());
}
return Redisson.create(config);
}
/**
* 单机模式自动装配
* @return
*/
@Bean
@ConditionalOnProperty(name="redisson.address")
RedissonClient redissonSingle() {
Config config = new Config();
SingleServerConfig serverConfig = config.useSingleServer()
.setAddress(redssionProperties.getAddress())
.setTimeout(redssionProperties.getTimeout())
.setConnectionPoolSize(redssionProperties.getConnectionPoolSize())
.setConnectionMinimumIdleSize(redssionProperties.getConnectionMinimumIdleSize());
if(StringUtils.isNotBlank(redssionProperties.getPassword())) {
serverConfig.setPassword(redssionProperties.getPassword());
}
return Redisson.create(config);
}
/**
* 装配locker类,并将实例注入到RedissLockUtil中
* @return
*/
@Bean
DistributedLocker distributedLocker(RedissonClient redissonSingle) {
RedissonDistributedLocker locker = new RedissonDistributedLocker();
locker.setRedissonClient(redissonSingle);
RedissLockUtil.setLocker(locker);
return locker;
}
}
package com.geekq.miaosha.redis;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import java.util.concurrent.TimeUnit;
public class RedissonDistributedLocker implements DistributedLocker {
private RedissonClient redissonClient;
@Override
public void lock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock();
}
@Override
public void unlock(String lockKey) {
RLock lock = redissonClient.getLock(lockKey);
lock.unlock();
}
@Override
public void lock(String lockKey, int leaseTime) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(leaseTime, TimeUnit.SECONDS);
}
@Override
public void lock(String lockKey, TimeUnit unit ,int timeout) {
RLock lock = redissonClient.getLock(lockKey);
lock.lock(timeout, unit);
}
public void setRedissonClient(RedissonClient redissonClient) {
this.redissonClient = redissonClient;
}
}
\ No newline at end of file
package com.geekq.miaosha.redis;
import org.springframework.boot.context.properties.ConfigurationProperties;
@ConfigurationProperties(prefix = "redisson")
public class RedissonProperties {
private int timeout = 3000;
private String address;
private String password;
private int connectionPoolSize = 64;
private int connectionMinimumIdleSize=10;
private int slaveConnectionPoolSize = 250;
private int masterConnectionPoolSize = 250;
private String[] sentinelAddresses;
private String masterName;
public int getTimeout() {
return timeout;
}
public void setTimeout(int timeout) {
this.timeout = timeout;
}
public int getSlaveConnectionPoolSize() {
return slaveConnectionPoolSize;
}
public void setSlaveConnectionPoolSize(int slaveConnectionPoolSize) {
this.slaveConnectionPoolSize = slaveConnectionPoolSize;
}
public int getMasterConnectionPoolSize() {
return masterConnectionPoolSize;
}
public void setMasterConnectionPoolSize(int masterConnectionPoolSize) {
this.masterConnectionPoolSize = masterConnectionPoolSize;
}
public String[] getSentinelAddresses() {
return sentinelAddresses;
}
public void setSentinelAddresses(String sentinelAddresses) {
this.sentinelAddresses = sentinelAddresses.split(",");
}
public String getMasterName() {
return masterName;
}
public void setMasterName(String masterName) {
this.masterName = masterName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
public int getConnectionPoolSize() {
return connectionPoolSize;
}
public void setConnectionPoolSize(int connectionPoolSize) {
this.connectionPoolSize = connectionPoolSize;
}
public int getConnectionMinimumIdleSize() {
return connectionMinimumIdleSize;
}
public void setConnectionMinimumIdleSize(int connectionMinimumIdleSize) {
this.connectionMinimumIdleSize = connectionMinimumIdleSize;
}
}
package com.geekq.miaosha.redis;
import org.redisson.api.*;
import org.redisson.config.Config;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.io.IOException;
/**
* redisson操作类
*/
@Service("redissonService")
public class RedissonService {
@Autowired
private RedissonClient redissonClient;
public void getRedissonClient() throws IOException {
Config config = redissonClient.getConfig();
System.out.println(config.toJSON().toString());
}
/**`
* 获取字符串对象
*
* @param objectName
* @return
*/
public <T> RBucket<T> getRBucket(String objectName) {
RBucket<T> bucket = redissonClient.getBucket(objectName);
return bucket;
}
/**
* 获取Map对象
*
* @param objectName
* @return
*/
public <K, V> RMap<K, V> getRMap(String objectName) {
RMap<K, V> map = redissonClient.getMap(objectName);
return map;
}
/**
* 获取有序集合
*
* @param objectName
* @return
*/
public <V> RSortedSet<V> getRSortedSet(String objectName) {
RSortedSet<V> sortedSet = redissonClient.getSortedSet(objectName);
return sortedSet;
}
/**
* 获取集合
*
* @param objectName
* @return
*/
public <V> RSet<V> getRSet(String objectName) {
RSet<V> rSet = redissonClient.getSet(objectName);
return rSet;
}
/**
* 获取列表
*
* @param objectName
* @return
*/
public <V> RList<V> getRList(String objectName) {
RList<V> rList = redissonClient.getList(objectName);
return rList;
}
/**
* 获取队列
*
* @param objectName
* @return
*/
public <V> RQueue<V> getRQueue(String objectName) {
RQueue<V> rQueue = redissonClient.getQueue(objectName);
return rQueue;
}
/**
* 获取双端队列
*
* @param objectName
* @return
*/
public <V> RDeque<V> getRDeque(String objectName) {
RDeque<V> rDeque = redissonClient.getDeque(objectName);
return rDeque;
}
/**
* 获取锁
*
* @param objectName
* @return
*/
public RLock getRLock(String objectName) {
RLock rLock = redissonClient.getLock(objectName);
return rLock;
}
/**
* 获取读取锁
*
* @param objectName
* @return
*/
public RReadWriteLock getRWLock(String objectName) {
RReadWriteLock rwlock = redissonClient.getReadWriteLock(objectName);
return rwlock;
}
/**
* 获取原子数
*
* @param objectName
* @return
*/
public RAtomicLong getRAtomicLong(String objectName) {
RAtomicLong rAtomicLong = redissonClient.getAtomicLong(objectName);
return rAtomicLong;
}
/**
* 获取记数锁
*
* @param objectName
* @return
*/
public RCountDownLatch getRCountDownLatch(String objectName) {
RCountDownLatch rCountDownLatch = redissonClient.getCountDownLatch(objectName);
return rCountDownLatch;
}
}
\ No newline at end of file
package com.geekq.miaosha.redis;
public class Userkey extends BasePrefix {
private Userkey(String prefix) {
super( prefix);
}
public static Userkey getById = new Userkey("id") ;
public static Userkey getByName = new Userkey("name") ;
}
package com.geekq.miaosha.result;
public class CodeMsg {
private int code;
private String msg;
//通用的错误码
public static CodeMsg SUCCESS = new CodeMsg(0, "success");
public static CodeMsg SUCCESS_RESIGETER = new CodeMsg(200,"注册成功!");
public static CodeMsg RESIGETER_FAIL = new CodeMsg(300,"注册失败!");
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 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, "密码错误");
//商品模块 5003XX
//订单模块 5004XX
public static CodeMsg ORDER_NOT_EXIST = new CodeMsg(500400, "订单不存在");
//秒杀模块 5005XX
public static CodeMsg MIAO_SHA_OVER = new CodeMsg(500500, "商品已经秒杀完毕");
public static CodeMsg REPEATE_MIAOSHA = new CodeMsg(500501, "不能重复秒杀");
public static CodeMsg MIAOSHA_FAIL = new CodeMsg(500502, "秒杀失败");
private CodeMsg( ) {
}
private CodeMsg( int code,String msg ) {
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public CodeMsg fillArgs(Object... args) {
int code = this.code;
//拼接错误信息
String message = String.format(this.msg, args);
return new CodeMsg(code, message);
}
@Override
public String toString() {
return "CodeMsg [code=" + code + ", msg=" + msg + "]";
}
}
package com.geekq.miaosha.result;
public class Result<T> {
private int code;
private String msg;
private T data;
/**
* 成功时候的调用
*
* @param data
* @param <T>
* @return
*/
public static <T> Result<T> success(T data) {
return new Result<T>(data);
}
/**
* 注册时候调用
*
* @param msg
* @param data
* @param <T>
* @return
*/
public static <T> Result<T> success(CodeMsg msg) {
return new Result<T>(CodeMsg.SUCCESS_RESIGETER);
}
/**
* 失败的时候调用
*
* @param cm
* @param <T>
* @return
*/
public static <T> Result<T> error(CodeMsg cm) {
return new Result<T>(cm);
}
// public Result(CodeMsg msg, T data) {
// this.code = msg.getCode();
// this.msg = msg.getMsg();
// this.data = data;
// }
private Result(CodeMsg cm) {
if (cm == null) {
return;
}
this.code = cm.getCode();
this.msg = cm.getMsg();
}
private Result(T data) {
this.code = 0;
this.msg = "success";
this.data = data;
}
public int getCode() {
return code;
}
public void setCode(int code) {
this.code = code;
}
public String getMsg() {
return msg;
}
public void setMsg(String msg) {
this.msg = msg;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
package com.geekq.miaosha.service;
import com.geekq.miaosha.dao.GoodsDao;
import com.geekq.miaosha.domain.MiaoshaGoods;
import com.geekq.miaosha.vo.GoodsVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class GoodsService {
@Autowired
GoodsDao goodsDao;
public List<GoodsVo> listGoodsVo(){
return goodsDao.listGoodsVo();
}
public GoodsVo getGoodsVoByGoodsId(long goodsId) {
return goodsDao.getGoodsVoByGoodsId(goodsId);
}
public boolean reduceStock(GoodsVo goods) {
MiaoshaGoods g = new MiaoshaGoods();
g.setGoodsId(goods.getId());
int ret = goodsDao.reduceStock(g);
return ret > 0;
}
}
package com.geekq.miaosha.service;
public interface IUserService {
Object login(String username , String password) ;
}
package com.geekq.miaosha.service;
import com.geekq.miaosha.Md5Utils.MD5Utils;
import com.geekq.miaosha.dao.MiaoShaUserDao;
import com.geekq.miaosha.domain.MiaoshaUser;
import com.geekq.miaosha.exception.GlobleException;
import com.geekq.miaosha.redis.MiaoShaUserKey;
import com.geekq.miaosha.redis.RedisService;
import com.geekq.miaosha.result.CodeMsg;
import com.geekq.miaosha.result.Result;
import com.geekq.miaosha.utils.UUIDUtil;
import com.geekq.miaosha.vo.LoginVo;
import com.sun.org.apache.bcel.internal.classfile.Code;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletResponse;
@Service
public class MiaoShaUserService {
public static final String COOKIE_NAME_TOKEN = "token" ;
@Autowired
private MiaoShaUserDao miaoShaUserDao ;
@Autowired
private RedisService redisService ;
public MiaoshaUser getByToken(HttpServletResponse response , String token) {
if(StringUtils.isEmpty(token)){
return null ;
}
MiaoshaUser user =redisService.get(MiaoShaUserKey.token,token,MiaoshaUser.class) ;
if(user!=null) {
addCookie(response, user);
}
return user ;
}
public MiaoshaUser getById(long id) {
//取缓存
MiaoshaUser user = redisService.get(MiaoShaUserKey.getById, ""+id, MiaoshaUser.class);
if(user != null) {
return user;
}
//取数据库
user = miaoShaUserDao.getById(id);
if(user != null) {
redisService.set(MiaoShaUserKey.getById, ""+id, user);
}
return user;
}
// http://blog.csdn.net/tTU1EvLDeLFq5btqiK/article/details/78693323
public boolean updatePassword(String token, long id, String formPass) {
//取user
MiaoshaUser user = getById(id);
if(user == null) {
throw new GlobleException(CodeMsg.MOBILE_NOT_EXIST);
}
//更新数据库
MiaoshaUser toBeUpdate = new MiaoshaUser();
toBeUpdate.setId(id);
toBeUpdate.setPassword(MD5Utils.formPassToDBPass(formPass, user.getSalt()));
miaoShaUserDao.update(toBeUpdate);
//处理缓存
redisService.delete(MiaoShaUserKey.getById, ""+id);
user.setPassword(toBeUpdate.getPassword());
redisService.set(MiaoShaUserKey.token, token, user);
return true;
}
public Result<String> insertMiaoShaUser(MiaoshaUser miaoshaUser){
long resultRegister = miaoShaUserDao.insertMiaoShaUser(miaoshaUser);
if(resultRegister == 0){
throw new GlobleException(CodeMsg.RESIGETER_FAIL);
}
return Result.success(CodeMsg.SUCCESS_RESIGETER);
}
public boolean login(HttpServletResponse response , LoginVo loginVo) {
if(loginVo ==null){
throw new GlobleException(CodeMsg.SERVER_ERROR);
}
String mobile =loginVo.getMobile();
String password =loginVo.getPassword();
MiaoshaUser user = getById(Long.valueOf(mobile));
if(user == null) {
throw new GlobleException(CodeMsg.MOBILE_NOT_EXIST);
}
String dbPass = user.getPassword();
String saltDb = user.getSalt();
String calcPass = MD5Utils.formPassToDBPass(password,saltDb);
if(!calcPass.equals(dbPass)){
throw new GlobleException(CodeMsg.PASSWORD_ERROR);
}
//生成cookie
addCookie(response,user);
return true ;
}
private void addCookie(HttpServletResponse response ,MiaoshaUser user){
String token = UUIDUtil.uuid();
redisService.set(MiaoShaUserKey.token,token,user) ;
Cookie cookie = new Cookie(COOKIE_NAME_TOKEN , token) ;
cookie.setMaxAge(MiaoShaUserKey.token.expireSeconds());
cookie.setPath("/");
response.addCookie(cookie);
}
}
package com.geekq.miaosha.service;
import com.geekq.miaosha.Md5Utils.MD5Utils;
import com.geekq.miaosha.domain.MiaoshaOrder;
import com.geekq.miaosha.domain.MiaoshaUser;
import com.geekq.miaosha.domain.OrderInfo;
import com.geekq.miaosha.redis.MiaoshaKey;
import com.geekq.miaosha.redis.RedisService;
import com.geekq.miaosha.utils.UUIDUtil;
import com.geekq.miaosha.vo.GoodsVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.util.Random;
@Service
public class MiaoshaService {
@Autowired
GoodsService goodsService;
@Autowired
OrderService orderService;
@Autowired
RedisService redisService;
@Transactional
public OrderInfo miaosha(MiaoshaUser user, GoodsVo goods) {
//减库存 下订单 写入秒杀订单
boolean success = goodsService.reduceStock(goods);
if(success){
return orderService.createOrder(user,goods) ;
}else {
//如果库存不存在则内存标记为true
setGoodsOver(goods.getId());
return null;
}
}
public long getMiaoshaResult(Long userId, long goodsId) {
MiaoshaOrder order = orderService.getMiaoshaOrderByUserIdGoodsId(userId, goodsId);
if(order != null) {//秒杀成功
return order.getOrderId();
}else {
boolean isOver = getGoodsOver(goodsId);
if(isOver) {
return -1;
}else {
return 0;
}
}
}
private void setGoodsOver(Long goodsId) {
redisService.set(MiaoshaKey.isGoodsOver, ""+goodsId, true);
}
private boolean getGoodsOver(long goodsId) {
return redisService.exists(MiaoshaKey.isGoodsOver, ""+goodsId);
}
public boolean checkPath(MiaoshaUser user, long goodsId, String path) {
if(user == null || path == null) {
return false;
}
String pathOld = redisService.get(MiaoshaKey.getMiaoshaPath, ""+user.getId() + "_"+ goodsId, String.class);
return path.equals(pathOld);
}
public String createMiaoshaPath(MiaoshaUser user, long goodsId) {
if(user == null || goodsId <=0) {
return null;
}
String str = MD5Utils.md5(UUIDUtil.uuid()+"123456");
redisService.set(MiaoshaKey.getMiaoshaPath, ""+user.getId() + "_"+ goodsId, str);
return str;
}
public BufferedImage createVerifyCode(MiaoshaUser user, long goodsId) {
if(user == null || goodsId <=0) {
return null;
}
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 rnd = calc(verifyCode);
redisService.set(MiaoshaKey.getMiaoshaVerifyCode, user.getId()+","+goodsId, rnd);
//输出图片
return image;
}
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;
}
}
public boolean checkVerifyCode(MiaoshaUser user, long goodsId, int verifyCode) {
if(user == null || goodsId <=0) {
return false;
}
Integer codeOld = redisService.get(MiaoshaKey.getMiaoshaVerifyCode, user.getId()+","+goodsId, Integer.class);
if(codeOld == null || codeOld - verifyCode != 0 ) {
return false;
}
redisService.delete(MiaoshaKey.getMiaoshaVerifyCode, user.getId()+","+goodsId);
return true;
}
private static char[] ops = new char[] {'+', '-', '*'};
/**
* + - *
* */
private 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;
}
}
package com.geekq.miaosha.service;
import com.geekq.miaosha.dao.OrderDao;
import com.geekq.miaosha.domain.MiaoshaOrder;
import com.geekq.miaosha.domain.MiaoshaUser;
import com.geekq.miaosha.domain.OrderInfo;
import com.geekq.miaosha.redis.OrderKey;
import com.geekq.miaosha.redis.RedisService;
import com.geekq.miaosha.utils.DateTimeUtils;
import com.geekq.miaosha.vo.GoodsVo;
import org.apache.commons.lang3.time.DateUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.Date;
import java.util.List;
import static com.geekq.miaosha.common.Constanst.orderStaus.ORDER_NOT_PAY;
@Service
public class OrderService {
@Autowired
OrderDao orderDao;
@Autowired
private RedisService redisService ;
public MiaoshaOrder getMiaoshaOrderByUserIdGoodsId(long userId, long goodsId) {
return redisService.get(OrderKey.getMiaoshaOrderByUidGid,""+userId+"_"+goodsId,MiaoshaOrder.class) ;
}
public OrderInfo getOrderById(long orderId) {
return orderDao.getOrderById(orderId);
}
@Transactional
public OrderInfo createOrder(MiaoshaUser user, GoodsVo goods) {
OrderInfo orderInfo = new OrderInfo();
orderInfo.setCreateDate(new Date());
orderInfo.setDeliveryAddrId(0L);
orderInfo.setGoodsCount(1);
orderInfo.setGoodsId(goods.getId());
orderInfo.setGoodsName(goods.getGoodsName());
orderInfo.setGoodsPrice(goods.getMiaoshaPrice());
orderInfo.setOrderChannel(1);
orderInfo.setStatus(0);
orderInfo.setUserId(user.getId());
orderDao.insert(orderInfo);
MiaoshaOrder miaoshaOrder = new MiaoshaOrder();
miaoshaOrder.setGoodsId(goods.getId());
miaoshaOrder.setOrderId(orderInfo.getId());
miaoshaOrder.setUserId(user.getId());
orderDao.insertMiaoshaOrder(miaoshaOrder);
redisService.set(OrderKey.getMiaoshaOrderByUidGid,""+user.getId()+"_"+goods.getId(),miaoshaOrder) ;
return orderInfo;
}
public void closeOrder(int hour){
Date closeDateTime = DateUtils.addHours(new Date(),-hour);
List<OrderInfo> orderInfoList = orderDao.selectOrderStatusByCreateTime(Integer.valueOf(ORDER_NOT_PAY.ordinal()), DateTimeUtils.dateToStr(closeDateTime));
for (OrderInfo orderInfo:orderInfoList){
System.out.println("orderinfo infomation "+orderInfo.getGoodsName());
}
}
}
package com.geekq.miaosha.service;
import com.geekq.miaosha.dao.UserDao;
import com.geekq.miaosha.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
@Service
public class UserService {
@Autowired
private UserDao userDao ;
public User getById(int id) {
return userDao.getById(id);
}
}
package com.geekq.miaosha.timeTask;
import com.geekq.miaosha.dao.OrderDao;
import com.geekq.miaosha.redis.RedisService;
import com.geekq.miaosha.redis.RedissonService;
import com.geekq.miaosha.service.OrderService;
import jodd.util.PropertiesUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.redisson.api.RLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.util.concurrent.TimeUnit;
import static com.geekq.miaosha.common.Constanst.CLOSE_ORDER_INFO_TASK_LOCK;
@Component
@Slf4j
public class OrderCloseTask {
@Autowired
private OrderDao orderDao ;
@Autowired
private RedisService redisService;
@Autowired
private OrderService orderService;
@Autowired
private RedissonService redissonService;
// @Scheduled(cron = "0/1 * * * * ?")
private void closeOrderTaskv1(){
int hour = 2;
orderService.closeOrder(hour);
log.info("关闭订单定时任务结束");
}
// @Scheduled(cron = "0/1 * * * * ?")
// public void closeOrderTaskV2(){
// log.info("关闭订单定时任务启动");
// long lockTime = 5000;
// Long setnxResult = redisService.setnx(CLOSE_ORDER_INFO_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTime));
// //代表获取了锁
// if(setnxResult !=null && setnxResult ==1){
// closeOrder(CLOSE_ORDER_INFO_TASK_LOCK);
// }else {
// log.info("没有获得分布式锁:{}",CLOSE_ORDER_INFO_TASK_LOCK);
// }
// log.info("关闭订单定时任务结束");
// }
// @Scheduled(cron = "0/1 * * * * ?")
// public void closeOrderTaskV3(){
// log.info("关闭订单定时任务启动");
// long lockTime = 5000;
// Long setnxResult = redisService.setnx(CLOSE_ORDER_INFO_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTime));
// //代表获取了锁
// if(setnxResult !=null && setnxResult ==1){
// closeOrder(CLOSE_ORDER_INFO_TASK_LOCK);
// }else {
// log.info("没有获得分布式锁:{}",CLOSE_ORDER_INFO_TASK_LOCK);
// String lockValueStr = redisService.get(CLOSE_ORDER_INFO_TASK_LOCK);
// if(lockValueStr!=null&&System.currentTimeMillis() > Long.parseLong(lockValueStr)){
// //把之前的释放在新加入锁
// String getSetResult = redisService.getset(CLOSE_ORDER_INFO_TASK_LOCK,String.valueOf(System.currentTimeMillis()+lockTime));
//
// if(getSetResult == null || (getSetResult != null && StringUtils.equals(lockValueStr,getSetResult))){
// closeOrder(CLOSE_ORDER_INFO_TASK_LOCK);
// }else {
// log.info("没有获取到分布式锁:{}",CLOSE_ORDER_INFO_TASK_LOCK);
// }
// }else {
// log.info("没有获取到分布式锁:{}",CLOSE_ORDER_INFO_TASK_LOCK);
// }
// }
// log.info("关闭订单定时任务结束");
// }
@Scheduled(cron="0 */1 * * * ?")
public void closeOrderTaskV4(){
RLock lock = redissonService.getRLock(CLOSE_ORDER_INFO_TASK_LOCK);
boolean getLock = false;
try {
if(getLock = lock.tryLock(0,50, TimeUnit.SECONDS)){
log.info("Redisson获取到分布式锁:{},ThreadName:{}",CLOSE_ORDER_INFO_TASK_LOCK,Thread.currentThread().getName());
int hour = 2;
// iOrderService.closeOrder(hour);
}else{
log.info("Redisson没有获取到分布式锁:{},ThreadName:{}",CLOSE_ORDER_INFO_TASK_LOCK,Thread.currentThread().getName());
}
} catch (InterruptedException e) {
log.error("Redisson分布式锁获取异常",e);
} finally {
if(!getLock){
return;
}
lock.unlock();
log.info("Redisson分布式锁释放锁");
}
}
private void closeOrder(String lockName){
redisService.expire(lockName,5);
log.info("获取{},当前线程名称!" ,lockName,Thread.currentThread().getName());
int hour = 2;
orderService.closeOrder(hour);
redisService.del(CLOSE_ORDER_INFO_TASK_LOCK);
log.info("===============================");
}
}
package com.geekq.miaosha.utils;
import org.apache.commons.lang3.StringUtils;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;
import java.util.Date;
/**
* Created by geely
*/
public class DateTimeUtils {
//joda-time
//str->Date
//Date->str
public static final String STANDARD_FORMAT = "yyyy-MM-dd HH:mm:ss";
public static Date strToDate(String dateTimeStr,String formatStr){
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(formatStr);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate();
}
public static String dateToStr(Date date,String formatStr){
if(date == null){
return StringUtils.EMPTY;
}
DateTime dateTime = new DateTime(date);
return dateTime.toString(formatStr);
}
public static Date strToDate(String dateTimeStr){
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern(STANDARD_FORMAT);
DateTime dateTime = dateTimeFormatter.parseDateTime(dateTimeStr);
return dateTime.toDate();
}
public static String dateToStr(Date date){
if(date == null){
return StringUtils.EMPTY;
}
DateTime dateTime = new DateTime(date);
return dateTime.toString(STANDARD_FORMAT);
}
public static void main(String[] args) {
System.out.println(DateTimeUtils.dateToStr(new Date(),"yyyy-MM-dd HH:mm:ss"));
System.out.println(DateTimeUtils.strToDate("2010-01-01 11:11:11","yyyy-MM-dd HH:mm:ss"));
}
}
\ No newline at end of file
package com.geekq.miaosha.utils;
import java.util.UUID;
public class UUIDUtil {
public static String uuid(){
return UUID.randomUUID().toString().replace("-","");
}
}
package com.geekq.miaosha.utils;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.lang3.StringUtils;
public class ValidatorUtil {
private static final Pattern mobile_pattern = Pattern.compile("1\\d{10}");
public static boolean isMobile(String src) {
if(StringUtils.isEmpty(src)) {
return false;
}
Matcher m = mobile_pattern.matcher(src);
return m.matches();
}
}
package com.geekq.miaosha.validator;
import javax.validation.Constraint;
import javax.validation.Payload;
import java.lang.annotation.*;
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {IsMobileValidator.class})
public @interface IsMobile {
boolean required() default true ;
String message() default "手机号码格式有误!";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}
package com.geekq.miaosha.validator;
import com.geekq.miaosha.utils.ValidatorUtil;
import org.apache.commons.lang3.StringUtils;
import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;
public class IsMobileValidator implements ConstraintValidator<IsMobile , String> {
private boolean require = false ;
@Override
public void initialize(IsMobile isMobile) {
require = isMobile.required() ;
}
@Override
public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
if(require){
return ValidatorUtil.isMobile(value) ;
}else{
if(StringUtils.isEmpty(value)){
return true ;
}else {
return ValidatorUtil.isMobile(value) ;
}
}
}
}
package com.geekq.miaosha.vo;
import com.geekq.miaosha.domain.MiaoshaUser;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class GoodsDetailVo {
private int miaoshaStatus = 0;
private int remainSeconds = 0;
private GoodsVo goods ;
private MiaoshaUser user;
}
package com.geekq.miaosha.vo;
import com.geekq.miaosha.domain.Goods;
import lombok.*;
import java.util.Date;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class GoodsVo extends Goods {
private Double miaoshaPrice;
private Integer stockCount;
private Date startDate;
private Date endDate;
}
package com.geekq.miaosha.vo;
import com.geekq.miaosha.validator.IsMobile;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotNull;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class LoginVo {
@NotNull
@IsMobile
private String mobile ;
@NotNull
@Length(min=32)
private String password;
@Override
public String toString() {
return "LoginVo{" +
"mobile='" + mobile + '\'' +
", password='" + password + '\'' +
'}';
}
}
package com.geekq.miaosha.vo;
import com.geekq.miaosha.domain.OrderInfo;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;
@Setter
@Getter
@AllArgsConstructor
@NoArgsConstructor
public class OrderDetailVo {
private GoodsVo goods;
private OrderInfo order;
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;
}
}
#thymeleaf
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false
spring.thymeleaf.content-type=text/html
spring.thymeleaf.enabled=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML5
#mybatis
mybatis.type-aliases-package=com.geekq.miaosha.domain
#开启驼峰转换
mybatis.configuration.map-underscore-to-camel-case=true
#mybatis.mapperLocations = classpath:com/geekq/miaosha/dao/*.xml
mybatis.mapperLocations=classpath:mybatis/mapper/*.xml
#add mybatis
mybatis.config-locations=classpath:mybatis/config.xml
#datasource
spring.datasource.url=jdbc:mysql://localhost:3306/miaosha?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=aixiyue11
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#druid
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.filters=stat
spring.datasource.maxActive=1000
spring.datasource.initialSize=100
spring.datasource.maxWait=60000
spring.datasource.minIdle=500
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=select 'x'
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxOpenPreparedStatements=20
#static
spring.resources.add-mappings=true
spring.resources.cache-period= 3600
spring.resources.chain.cache=true
spring.resources.chain.enabled=true
spring.resources.chain.gzipped=true
spring.resources.chain.html-application-cache=true
spring.resources.static-locations=classpath:/static/
#redis
redis.host=localhost
redis.port=6379
redis.timeout=100
redis.password=123456
redis.poolMaxTotal=1000
redis.poolMaxIdle=500
redis.poolMaxWait=500
# redisson lock
redisson.address=redis://localhost:6379
redisson.password=123456
#rabbitmq
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.listener.simple.concurrency= 10
spring.rabbitmq.listener.simple.max-concurrency= 10
spring.rabbitmq.listener.simple.prefetch= 1
spring.rabbitmq.listener.simple.auto-startup=true
spring.rabbitmq.listener.simple.default-requeue-rejected= true
spring.rabbitmq.template.retry.enabled=true
spring.rabbitmq.template.retry.initial-interval=1000
spring.rabbitmq.template.retry.max-attempts=3
spring.rabbitmq.template.retry.max-interval=10000
spring.rabbitmq.template.retry.multiplier=1.0
server.port=8003
## maven隔离
spring.profiles.active=@activatedProperties@
\ No newline at end of file
#thymeleaf
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false
spring.thymeleaf.content-type=text/html
spring.thymeleaf.enabled=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML5
#mybatis
mybatis.type-aliases-package=com.geekq.miaosha.domain
#开启驼峰转换
mybatis.configuration.map-underscore-to-camel-case=true
#mybatis.mapperLocations = classpath:com/geekq/miaosha/dao/*.xml
mybatis.mapperLocations=classpath:mybatis/mapper/*.xml
#add mybatis
mybatis.config-locations=classpath:mybatis/config.xml
#datasource
spring.datasource.url=jdbc:mysql://localhost:3306/miaosha?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=aixiyue11
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#druid
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.filters=stat
spring.datasource.maxActive=1000
spring.datasource.initialSize=100
spring.datasource.maxWait=60000
spring.datasource.minIdle=500
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=select 'x'
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxOpenPreparedStatements=20
#static
spring.resources.add-mappings=true
spring.resources.cache-period= 3600
spring.resources.chain.cache=true
spring.resources.chain.enabled=true
spring.resources.chain.gzipped=true
spring.resources.chain.html-application-cache=true
spring.resources.static-locations=classpath:/static/
#redis
redis.host=192.168.1.116
redis.port=6379
redis.timeout=100
redis.password=123456
redis.poolMaxTotal=1000
redis.poolMaxIdle=500
redis.poolMaxWait=500
#rabbitmq
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.listener.simple.concurrency= 10
spring.rabbitmq.listener.simple.max-concurrency= 10
spring.rabbitmq.listener.simple.prefetch= 1
spring.rabbitmq.listener.simple.auto-startup=true
spring.rabbitmq.listener.simple.default-requeue-rejected= true
spring.rabbitmq.template.retry.enabled=true
spring.rabbitmq.template.retry.initial-interval=1000
spring.rabbitmq.template.retry.max-attempts=3
spring.rabbitmq.template.retry.max-interval=10000
spring.rabbitmq.template.retry.multiplier=1.0
## maven隔离
spring.profiles.active=@activatedProperties@
\ No newline at end of file
#thymeleaf test
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false
spring.thymeleaf.content-type=text/html
spring.thymeleaf.enabled=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML5
#mybatis
mybatis.type-aliases-package=com.geekq.miaosha.domain
#开启驼峰转换
mybatis.configuration.map-underscore-to-camel-case=true
#mybatis.mapperLocations = classpath:com/geekq/miaosha/dao/*.xml
mybatis.mapperLocations=classpath:mybatis/mapper/*.xml
#add mybatis
mybatis.config-locations=classpath:mybatis/config.xml
#datasource
spring.datasource.url=jdbc:mysql://localhost:3306/miaosha?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=aixiyue11
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#druid
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.filters=stat
spring.datasource.maxActive=1000
spring.datasource.initialSize=100
spring.datasource.maxWait=60000
spring.datasource.minIdle=500
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=select 'x'
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxOpenPreparedStatements=20
#static
spring.resources.add-mappings=true
spring.resources.cache-period= 3600
spring.resources.chain.cache=true
spring.resources.chain.enabled=true
spring.resources.chain.gzipped=true
spring.resources.chain.html-application-cache=true
spring.resources.static-locations=classpath:/static/
#redis
redis.host=192.168.1.116
redis.port=6379
redis.timeout=100
redis.password=123456
redis.poolMaxTotal=1000
redis.poolMaxIdle=500
redis.poolMaxWait=500
#rabbitmq
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.listener.simple.concurrency= 10
spring.rabbitmq.listener.simple.max-concurrency= 10
spring.rabbitmq.listener.simple.prefetch= 1
spring.rabbitmq.listener.simple.auto-startup=true
spring.rabbitmq.listener.simple.default-requeue-rejected= true
spring.rabbitmq.template.retry.enabled=true
spring.rabbitmq.template.retry.initial-interval=1000
spring.rabbitmq.template.retry.max-attempts=3
spring.rabbitmq.template.retry.max-interval=10000
spring.rabbitmq.template.retry.multiplier=1.0
## maven隔离
spring.profiles.active=@activatedProperties@
\ No newline at end of file
#thymeleaf
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false
spring.thymeleaf.content-type=text/html
spring.thymeleaf.enabled=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML5
#mybatis
mybatis.type-aliases-package=com.geekq.miaosha.domain
#开启驼峰转换
mybatis.configuration.map-underscore-to-camel-case=true
#mybatis.mapperLocations = classpath:com/geekq/miaosha/dao/*.xml
mybatis.mapperLocations=classpath:mybatis/mapper/*.xml
#add mybatis
mybatis.config-locations=classpath:mybatis/config.xml
#datasource
spring.datasource.url=jdbc:mysql://localhost:3306/miaosha?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=aixiyue11
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#druid
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.filters=stat
spring.datasource.maxActive=1000
spring.datasource.initialSize=100
spring.datasource.maxWait=60000
spring.datasource.minIdle=500
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=select 'x'
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxOpenPreparedStatements=20
#static
spring.resources.add-mappings=true
spring.resources.cache-period= 3600
spring.resources.chain.cache=true
spring.resources.chain.enabled=true
spring.resources.chain.gzipped=true
spring.resources.chain.html-application-cache=true
spring.resources.static-locations=classpath:/static/
#redis
redis.host=192.168.1.116
redis.port=6379
redis.timeout=100
redis.password=123456
redis.poolMaxTotal=1000
redis.poolMaxIdle=500
redis.poolMaxWait=500
#server.port=8003
#rabbitmq
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.listener.simple.concurrency= 10
spring.rabbitmq.listener.simple.max-concurrency= 10
spring.rabbitmq.listener.simple.prefetch= 1
spring.rabbitmq.listener.simple.auto-startup=true
spring.rabbitmq.listener.simple.default-requeue-rejected= true
spring.rabbitmq.template.retry.enabled=true
spring.rabbitmq.template.retry.initial-interval=1000
spring.rabbitmq.template.retry.max-attempts=3
spring.rabbitmq.template.retry.max-interval=10000
spring.rabbitmq.template.retry.multiplier=1.0
## maven隔离
spring.profiles.active=dev
\ No newline at end of file
#thymeleaf test
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.cache=false
spring.thymeleaf.content-type=text/html
spring.thymeleaf.enabled=true
spring.thymeleaf.encoding=UTF-8
spring.thymeleaf.mode=HTML5
#mybatis
mybatis.type-aliases-package=com.geekq.miaosha.domain
#开启驼峰转换
mybatis.configuration.map-underscore-to-camel-case=true
#mybatis.mapperLocations = classpath:com/geekq/miaosha/dao/*.xml
mybatis.mapperLocations=classpath:mybatis/mapper/*.xml
#add mybatis
mybatis.config-locations=classpath:mybatis/config.xml
#datasource
spring.datasource.url=jdbc:mysql://localhost:3306/miaosha?useUnicode=true&characterEncoding=utf-8&allowMultiQueries=true&useSSL=false
spring.datasource.username=root
spring.datasource.password=aixiyue11
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#druid
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.filters=stat
spring.datasource.maxActive=1000
spring.datasource.initialSize=100
spring.datasource.maxWait=60000
spring.datasource.minIdle=500
spring.datasource.timeBetweenEvictionRunsMillis=60000
spring.datasource.minEvictableIdleTimeMillis=300000
spring.datasource.validationQuery=select 'x'
spring.datasource.testWhileIdle=true
spring.datasource.testOnBorrow=false
spring.datasource.testOnReturn=false
spring.datasource.poolPreparedStatements=true
spring.datasource.maxOpenPreparedStatements=20
#static
spring.resources.add-mappings=true
spring.resources.cache-period= 3600
spring.resources.chain.cache=true
spring.resources.chain.enabled=true
spring.resources.chain.gzipped=true
spring.resources.chain.html-application-cache=true
spring.resources.static-locations=classpath:/static/
#redis
redis.host=192.168.1.116
redis.port=6379
redis.timeout=100
redis.password=123456
redis.poolMaxTotal=1000
redis.poolMaxIdle=500
redis.poolMaxWait=500
#rabbitmq
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
spring.rabbitmq.virtual-host=/
spring.rabbitmq.listener.simple.concurrency= 10
spring.rabbitmq.listener.simple.max-concurrency= 10
spring.rabbitmq.listener.simple.prefetch= 1
spring.rabbitmq.listener.simple.auto-startup=true
spring.rabbitmq.listener.simple.default-requeue-rejected= true
spring.rabbitmq.template.retry.enabled=true
spring.rabbitmq.template.retry.initial-interval=1000
spring.rabbitmq.template.retry.max-attempts=3
spring.rabbitmq.template.retry.max-interval=10000
spring.rabbitmq.template.retry.multiplier=1.0
## maven隔离
pring.profiles.active=@activatedProperties@
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://code.alibabatech.com/schema/dubbo
http://code.alibabatech.com/schema/dubbo/dubbo.xsd ">
<!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
<dubbo:application name="dubbo_consumer" />
<!-- 使用multicast广播注册中心暴露发现服务地址 -->
<dubbo:registry protocol="zookeeper" address="zookeeper://192.168.1.116:2181" />
<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
<dubbo:reference id="demoService" interface="com.geekq.miaosha.api.DemoService" />
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:aop="http://www.springframework.org/schema/aop"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:util="http://www.springframework.org/schema/util" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd"
default-autowire="byName">
<aop:aspectj-autoproxy />
<context:component-scan base-package="com.geekq.miaosha" />
<import resource="classpath:/dubbo_consumer.xml" />
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<typeAlias alias="miaoshauser1" type="com.geekq.miaosha.domain.MiaoshaUser"/>
</typeAliases>
<!-- <mappers>
<mapper resource="/mybatis/mapper/UserMapper.xml"></mapper>
</mappers>-->
</configuration>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.geekq.miaosha.dao.UserMapper">
<!--结果集映射-->
<resultMap id="BaseResultMap" type="com.geekq.miaosha.domain.MiaoshaUser">
<id column="id" property="id" jdbcType="BIGINT"/>
<result column="nickname" property="nickname" jdbcType="VARCHAR"/>
<result column="salt" property="salt" jdbcType="VARCHAR"/>
<result column="head" property="head" jdbcType="VARCHAR"/>
<result column="register_date" property="registerDate" jdbcType="TIMESTAMP"/>
<result column="last_login_date" property="lastLoginDate" jdbcType="TIMESTAMP"/>
<result column="login_Count" property="loginCount" jdbcType="INTEGER"/>
</resultMap>
<!--增加一条数据-->
<insert id="insertMiaoShaUser" parameterType="com.geekq.miaosha.domain.MiaoshaUser" keyProperty="id">
insert into miaosha_user (id, nickname, salt, register_date,last_login_date,login_Count ) values
(#{id,jdbcType=BIGINT},#{nickname,jdbcType=VARCHAR},
#{salt,jdbcType=VARCHAR},#{registerDate,jdbcType=DATE},#{lastLoginDate,jdbcType=DATE},
#{loginCount,jdbcType=INTEGER}
)
</insert>
<!--批量插入-->
<insert id="insertMiaoShaUserValues" useGeneratedKeys="true"
keyProperty="id">
insert into miaosha_user (id, nickname, salt, register_date,last_login_date,login_Count ) values
<foreach item="item" collection="list" separator=",">
(#{item.id,jdbcType=BIGINT},#{item.nickname,jdbcType=VARCHAR},
#{item.salt,jdbcType=VARCHAR},#{item.registerDate,jdbcType=DATE},#{item.lastLoginDate,jdbcType=DATE},
#{item.loginCount,jdbcType=INTEGER}
)
</foreach>
</insert>
<!--更新一条数据-->
<update id="updateMiaoShaUser" >
update miaosha_user set nickname = #{nickname},
login_Count = #{loginCount}
where id=#{id}
</update>
<!--删除一条数据-->
<delete id="deleteMiaoShaUser" parameterType="Long">
delete from miaosha_user
where id = #{id,jdbcType=BIGINT}
</delete>
<sql id="Base_Column_List"> id, nickname, salt, register_date,last_login_date,login_Count </sql>
<select id="getMiaoShaUserById" parameterType="LONG" resultType="miaoshauser">
SELECT
<include refid="Base_Column_List"/>
FROM miaosha_user WHERE id = #{id}
</select>
<select id="getMiaoShaUserNum" resultType="int">
select count(*) from miaosha_user
</select>
<select id="countbyMenuId" parameterType="java.lang.Integer"
resultType="java.lang.Integer"> select count(*) from miaosha_user
</select>
</mapper>
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
此差异已折叠。
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册