前言
該篇主要實現秒殺業務層,秒殺業務邏輯里主要包括暴露秒殺接口地址、實現秒殺業務邏輯。同時聲明了三個業務類:Exposer、SeckillExecution、SeckillResult。 Exposer主要用來實現暴露接口時一個md5的加密,防止用戶在客戶端篡改數據。根據seckillid生成md5,提交秒殺請求時會根據這個md5和seckillid比對是否是合法的請求。SeckillExecution主要封裝秒殺時的返回值。
SeckillExecution有2個屬性,state、stateinfo,這里我沒有封裝枚舉值,還是用整型和字符串給客戶端傳值,在Service里看着也直觀些。
准備工作
1、spring-service.xml
業務邏輯里的關鍵是開啟事務,這里推薦用注解的方式實現。
<?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:context="http://www.springframework.org/schema/context" xmlns:tx="http://www.springframework.org/schema/tx" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd"> <!--1、注解包掃描--> <context:component-scan base-package="com.seckill.service"/> <!--2、配置聲明式事務--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <!--注入數據庫--> <property name="dataSource" ref="dataSource"/> </bean> <!--3、配置基於注解的聲明式事務--> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>
實現秒殺業務
秒殺相關的關鍵方法就是最后兩個方法,一個是對外暴漏秒殺地址,一個是秒殺方法。
public interface SeckillService {
List<Seckill> getSeckillList();
Seckill getById(long seckillId);
/**對外暴漏秒殺接口**/
Exposer exposeSeckillUrl(long seckillId);
/**
* 執行秒殺操作,有可能成功,有可能失敗,所以這里我們拋出自自定義異常
* ***/
SeckillExecution executeSeckill(long seckillId,long phone,String md5) throws SeckillException,
RepeatKillException,
SeckillCloseException;
}
@Service
public class SeckillServiceImpl implements SeckillService {
/***
* 秒殺行為的枚舉放在這里說明
* 1、 秒殺成功
* 0、 秒殺結束
* -1、重復秒殺
* -2、系統異常
* -3、數據篡改
* ***/
private Logger logger = LoggerFactory.getLogger(this.getClass());
@Autowired
SeckillDao seckillDao;
@Autowired
SuccessKillDao successKillDao;
private String salt = "zhangfei";
@Override
public List<Seckill> getSeckillList() {
return seckillDao.queryAll(0, 100);
}
@Override
public Seckill getById(long seckillId) {
return seckillDao.queryById(seckillId);
}
@Override
public Exposer exposeSeckillUrl(long seckillId) {
Seckill seckill = getById(seckillId);
Date startTime = seckill.getStartTime();
Date endTime = seckill.getEndTime();
Date now = new Date();
if (now.getTime() < startTime.getTime() || now.getTime() > endTime.getTime()) {
return new Exposer(false, seckillId, startTime.getTime(), endTime.getTime(), now.getTime());
}
String md5 = getMd5(seckillId);
return new Exposer(true, md5, seckillId);
}
@Override
@Transactional
public SeckillExecution executeSeckill(long seckillId, long phone, String md5)
throws SeckillException,RepeatKillException,SeckillCloseException {
if (md5 == null || !md5.equals(getMd5(seckillId))) {
throw new SeckillException("非法請求");
}
Date now = new Date();
try {
int insertCount = successKillDao.insertSuccessKilled(seckillId, phone);
if (insertCount <= 0) {
throw new RepeatKillException("重復秒殺");
} else {
int updateCount = seckillDao.reduceNumber(seckillId, now);
if (updateCount <= 0) {
throw new SeckillCloseException("秒殺已關閉");
} else {
//秒殺成功,可以把秒殺詳情和商品詳情實體返回
SuccessKilled successKilled = successKillDao.queryByIdWithSeckill(seckillId, phone);
return new SeckillExecution(seckillId, 1, "秒殺成功", successKilled);
}
}
} catch (SeckillCloseException e) {
throw e;
} catch (RepeatKillException e1) {
throw e1;
} catch (SeckillException e2) {
logger.error(e2.getMessage(), e2);
throw new SeckillException("Unkonwn error:" + e2.getMessage());
}
}
private String getMd5(long seckillId) {
String base = seckillId + "/" + salt;
String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
return md5;
}
}
