第一章 秒杀业务接口设计与实现
---1-1----------------------------------
1.DAO层:接口设计+SQL编写(不需要其他杂七杂八的功能)
2.代码和SQL的分离,方便Review(浏览)
3.DAO拼接等逻辑在Service完成(DAO只需负责SQL语句,其他都由Service层完成)
---1-2 秒杀Service接口设计----------------------------------
DTO设计:
Exposer.java 暴露秒杀地址DTO
package org.seckill.dto; /* * 暴露秒杀地址DTO * * * */ public class Exposer { //是否开启秒杀 private boolean exposed; //一种加密措施 private String md5; private long seckillId; //系统当前时间(毫秒) private long now; private long start; private long end; public Exposer(boolean exposed,String md5,long seckillId){ this.exposed = exposed; this.md5 = md5; this.seckillId = seckillId; } public Exposer(boolean exposed, long seckillId) { this.exposed = exposed; this.seckillId = seckillId; }
public Exposer(boolean exposed, long seckillId, long now, long start, long end) {
super();
this.exposed = exposed;
this.md5 = md5;
this.seckillId = seckillId;
this.now = now;
this.start = start;
this.end = end;
}
public boolean isExposed() { return exposed; } public void setExposed(boolean exposed) { this.exposed = exposed; } public String getMd5() { return md5; } public void setMd5(String md5) { this.md5 = md5; } public long getSeckillId() { return seckillId; } public void setSeckillId(long seckillId) { this.seckillId = seckillId; } public long getNow() { return now; } public void setNow(long now) { this.now = now; } public long getStart() { return start; } public void setStart(long start) { this.start = start; } public long getEnd() { return end; } public void setEnd(long end) { this.end = end; } }
SeckillExecution.java 封装秒杀执行结果
package org.seckill.dto; import org.seckill.entity.SuccessKilled; /* * 封装秒杀执行结果*/ public class SeckillExecution { private long seckillId; //秒杀结果状态 private int state; //状态表示 private String stateInfo; //秒杀成功对象 private SuccessKilled successKilled; public SeckillExecution(long seckillId, int state, String stateInfo, SuccessKilled successKilled) { this.seckillId = seckillId; this.state = state; this.stateInfo = stateInfo; this.successKilled = successKilled; } public SeckillExecution(long seckillId, int state, String stateInfo) { super(); this.seckillId = seckillId; this.state = state; this.stateInfo = stateInfo; } public long getSeckillId() { return seckillId; } public void setSeckillId(long seckillId) { this.seckillId = seckillId; } public int getState() { return state; } public void setState(int state) { this.state = state; } public SuccessKilled getSuccessKilled() { return successKilled; } public void setSuccessKilled(SuccessKilled successKilled) { this.successKilled = successKilled; } public String getStateInfo() { return stateInfo; } public void setStateInfo(String stateInfo) { this.stateInfo = stateInfo; } }
Service层:
业务接口:站在"使用者角"度设计接口
三个方面:方法定义粒度,参数,返回类型(return 类型/exception )友好
SeckillServic.java
package org.seckill.service; import java.util.List; import javax.swing.text.html.parser.Entity; import org.seckill.dto.Exposer; import org.seckill.dto.SeckillExection; import org.seckill.entity.Seckill; import org.seckill.exception.RepeatKillExeception; import org.seckill.exception.SeckillCloseException; import org.seckill.exception.SeckillException; /* * 业务接口:站在"使用者角"度设计接口 * 三个方面:方法定义粒度,参数,返回类型(return 类型/exception )友好 * */ public interface SeckillService { /* * 查询所有秒杀记录 * @return * */ List<Seckill> getSeckillList(); /*查询单个秒杀记录 * @param seckillId * @retrun */ Seckill getById(long seckillId); /* * 秒杀开启时输出秒杀接口地址, * 否则输出系统时间和秒杀 时间 * @param seckillId * @retrun * */ Exposer exportSeckillUrl(long seckillId); /* * 执行秒杀操作 * @param seckillId * @param userPhone * @param md5 * */ SeckillExecution executeSeckill(long seckillId,long userPhone,String md5) throws SeckillException,RepeatKillExeception,SeckillCloseException; }
Exception:
package org.seckill.exception; /* * 秒杀相关业务异常 * */ public class SeckillException extends RuntimeException{ public SeckillException(String message, Throwable cause) { super(message, cause); // TODO Auto-generated constructor stub } public SeckillException(String message) { super(message); // TODO Auto-generated constructor stub } }
package org.seckill.exception; /* * 重复秒杀异常(运行期异常,spring的声明式事务处理的是runtime异常,如果不是这类异常,那么他是不会回滚的) * */ public class RepeatKillExeception extends SeckillException{ public RepeatKillExeception(String message){ super(message); } public RepeatKillExeception(String message,Throwable cause){ super(message,cause); } }
package org.seckill.exception; /* *秒杀关闭异常 * */ public class SeckillCloseException extends SeckillException{ public SeckillCloseException(String message, Throwable cause) { super(message, cause); // TODO Auto-generated constructor stub } public SeckillCloseException(String message) { super(message); // TODO Auto-generated constructor stub } }
---1-3 1-4秒杀Service接口实现1----------------------------------
SeckillServiceImpl.java
package org.seckill.service.impl; import java.util.Date; import java.util.List; import org.seckill.dao.SeckillDao; import org.seckill.dao.SuccessKilledDao; import org.seckill.dto.Exposer; import org.seckill.dto.SeckillExecution; import org.seckill.entity.Seckill; import org.seckill.entity.SuccessKilled; import org.seckill.enums.SeckillStatEnum; import org.seckill.exception.RepeatKillExeception; import org.seckill.exception.SeckillCloseException; import org.seckill.exception.SeckillException; import org.seckill.service.SeckillService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.DigestUtils; public class SeckillServiceImpl implements SeckillService { private Logger logger = LoggerFactory.getLogger(this.getClass()); private SeckillDao seckillDao; private SuccessKilledDao successKilledDao; //md5盐值字符串,用于混淆MD5 private final String slat = "sldijfldkjfpaojj@#(#$sldfj`123"; @Override public List<Seckill> getSeckillList() { return seckillDao.queryAll(0, 4); } @Override public Seckill getById(long seckillId) { // TODO Auto-generated method stub return seckillDao.queryById(seckillId); } @Override public Exposer exportSeckillUrl(long seckillId) { Seckill seckill = seckillDao.queryById(seckillId); if(seckill == null){ return new Exposer(false,seckillId); } Date startTime = seckill.getStartTime(); Date endTime = seckill.getEndTime(); //系统当前时间 Date nowTime = new Date(); if(nowTime.getTime() < startTime.getTime() || nowTime.getTime() > endTime.getTime()){ return new Exposer(false,seckillId,nowTime.getTime(),startTime.getTime(), endTime.getTime()); } //转化特定字符串的过程,不可逆 String md5 = getMD5(seckillId);//TODO return new Exposer(true,md5,seckillId); } private String getMD5(long seckillId){ String base = seckillId+"/"+slat; String md5 = DigestUtils.md5DigestAsHex(base.getBytes()); return md5; } @Override public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillException, RepeatKillExeception, SeckillCloseException { if(md5==null|| !md5.equals(getMD5(seckillId))){ throw new SeckillException("seckill data rewrite"); } //执行秒杀逻辑:减库存 + 记录购买行为 Date nowTime = new Date(); try{ int updateCount = seckillDao.reduceNumber(seckillId, nowTime); if(updateCount <=0){ //没有更新记录 throw new SeckillCloseException("seckill is closed"); } else { //记录购买行为 int insertCount= successKilledDao.insertSuccessKilled(seckillId, userPhone); //唯一:insert ignore if(insertCount <=0){ //重复秒杀 throw new RepeatKillExeception("seckill repeated"); }else { //秒杀成功 SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone); //return new SeckillExecution(seckillId,1,"秒杀成功",successKilled); return new SeckillExecution(seckillId,SeckillStatEnum.SUCCESS); } } } catch (SeckillCloseException e1) { throw e1; } catch (RepeatKillExeception e2) { throw e2; }catch (Exception e){ logger.error(e.getMessage(),e); //所有编译期异常 转化为运行期异常 //spring事务会做roll back throw new SeckillException("seckill inner error : "+e.getMessage()); } return null; } }
SeckillExecution.java
package org.seckill.dto; import org.seckill.entity.SuccessKilled; import org.seckill.enums.SeckillStatEnum; /* * 封装秒杀执行结果*/ public class SeckillExecution { private long seckillId; /*此处不做枚举类型的原因:会把这个对象转换为json,通过ajax返回给前端浏览器。 * 默认的json的类库在转换枚举时有问题。 * (可以做一个把枚举类型转换为json的transfer,不过比较复杂,此处不使用此方法。 */ //秒杀结果状态 private int state; //状态表示 private String stateInfo; //秒杀成功对象 private SuccessKilled successKilled; public SeckillExecution(long seckillId, SeckillStatEnum statEnum) { super(); this.seckillId = seckillId; this.state = statEnum.getState(); this.stateInfo = statEnum.getStateInfo(); } //public SeckillExecution(long seckillId, int state, String stateInfo, SuccessKilled successKilled) { public SeckillExecution(long seckillId, SeckillStatEnum statEnum, SuccessKilled successKilled) { super(); this.seckillId = seckillId; this.state = statEnum.getState(); this.stateInfo = statEnum.getStateInfo(); this.successKilled = successKilled; } public long getSeckillId() { return seckillId; } public void setSeckillId(long seckillId) { this.seckillId = seckillId; } public int getState() { return state; } public void setState(int state) { this.state = state; } public SuccessKilled getSuccessKilled() { return successKilled; } public void setSuccessKilled(SuccessKilled successKilled) { this.successKilled = successKilled; } public String getStateInfo() { return stateInfo; } public void setStateInfo(String stateInfo) { this.stateInfo = stateInfo; } }
SeckillStatEnum.enum
数据字典放入枚举中。
package org.seckill.enums; //使用枚举表示常量数据 public enum SeckillStatEnum { SUCCESS(1,"秒杀成功"), END(1,"秒杀结束"), REPEAT_KILL(-1,"重复"), INNER_ERROR(-2,"系统异常"), DATA_REWRITE(-3,"数据篡改"); private int state; private String stateInfo; SeckillStatEnum(int state,String stateInfo) { this.state = state; this.stateInfo = stateInfo; } public int getState() { return state; } public String getStateInfo() { return stateInfo; } public static SeckillStatEnum stateOf(int index){ for(SeckillStatEnum state :values()){ if(state.getState() == index){ return state; } } return null; } }
---1-1----------------------------------
---1-1----------------------------------