1. 程式人生 > >秒殺系統-service

秒殺系統-service

ports map 地址 execute stat ces 處理 text dex


在Dao層我們只完成了針對表的相關操作,包括寫了接口方法和映射文件中的sql語句,並沒有編寫邏輯的代碼,例如對多個Dao層方法的拼接,當我們用戶成功秒殺商品時我們需要進行商品的減庫存操作(調用SeckillDao接口)和增加用戶明細(調用SuccessKilledDao接口),這些邏輯我們都需要在Service層完成。Dao層只進行數據的訪問操作,接下來我們便進行Service層代碼的編寫。秒殺Service接口設計如下: (
1)創建service包用於存放我們的Service接口和其實現類。 (2)創建exception包用於存放service層出現的異常,例如重復秒殺商品異常、秒殺已關閉等異常。 (
3)創建dto包作為傳輸層, 用於完成web和service層的數據傳遞。 (4)創建entity包用於業務數據的封裝。 service包需要的相關類名和函數名如表6-10所示。

技術分享圖片

SeckillService.java:
public interface SeckillService {
   List<Seckill> getSeckillList();
   Seckill getById(long seckillId);
   Exposer exportSeckillUrl(long seckillId);
   SeckillExecution executeSeckill(
long seckillId, long userPhone, String md5) throws SeckillException,RepeatKillException,SeckillCloseException; SeckillExecution executeSeckillProcedure(long seckillId, long userPhone, String md5); } SeckillServiceImpl.java: public class SeckillServiceImpl implements SeckillService {
private Logger logger = LoggerFactory.getLogger(this.getClass()); @Autowired private SeckillDao seckillDao; @Autowired private SuccessKilledDao successKilledDao; @Autowired private RedisDao redisDao; private final String salt = "sadkfjalsdjfalksj23423^&*^&%&!EBJKH#e™£4"; @Override public List<Seckill> getSeckillList() { return seckillDao.queryAll(0, 4); } @Override public Seckill getById(long seckillId) { return seckillDao.queryById(seckillId); } @Override public Exposer exportSeckillUrl(long seckillId) { Seckill seckill = redisDao.getSeckill(seckillId); if (seckill == null) { seckill = seckillDao.queryById(seckillId); if (seckill == null) { return new Exposer(false, seckillId); } else { redisDao.putSeckill(seckill); } } 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); return new Exposer(true, md5, seckillId); } private String getMD5(long seckillId) { String base = seckillId + "/" + salt; String md5 = DigestUtils.md5DigestAsHex(base.getBytes()); return md5; } @Override @Transactional public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillException, RepeatKillException, SeckillCloseException { if (md5 == null || !md5.equals(getMD5(seckillId))) { throw new SeckillException("seckill data rewrite"); } Date nowTime = new Date(); try { int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone); if (insertCount <= 0) { throw new RepeatKillException("seckill repeated"); } else { int updateCount = seckillDao.reduceNumber(seckillId, nowTime); if (updateCount <= 0) { throw new SeckillCloseException("seckill is closed"); } else { SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone); return new SeckillExecution(seckillId, SeckillStatEnum.SUCCESS, successKilled); } } } catch (SeckillCloseException e1) { throw e1; } catch (RepeatKillException e2) { throw e2; } catch (Exception e) { logger.error(e.getMessage(), e); throw new SeckillException("seckill inner error:" + e.getMessage()); } } @Override public SeckillExecution executeSeckillProcedure(long seckillId, long userPhone, String md5) { if (md5 == null || !md5.equals(getMD5(seckillId))) { return new SeckillExecution(seckillId, SeckillStatEnum.DATA_REWRITE); } Date killTime = new Date(); Map<String, Object> map = new HashMap<String, Object>(); map.put("seckillId", seckillId); map.put("phone", userPhone); map.put("killTime", killTime); map.put("result", null); try { seckillDao.killByProcedure(map); int result = MapUtils.getInteger(map, "result", -2); if (result == 1) { SuccessKilled sk = successKilledDao. queryByIdWithSeckill(seckillId, userPhone); return new SeckillExecution(seckillId, SeckillStatEnum.SUCCESS, sk); } else { return new SeckillExecution(seckillId, SeckillStatEnum.stateOf(result)); } } catch (Exception e) { logger.error(e.getMessage(), e); return new SeckillExecution(seckillId, SeckillStatEnum.INNER_ERROR); } } } 在dto包中創建Exposer.java,用於封裝秒殺的地址信息。SeckillExecution.java,用於判斷秒殺是否成功,成功就返回秒殺成功的所有信息(包括秒殺的商品id、秒殺成功狀態、成功信息、用戶明細),失敗就拋出一個我們允許的異常(重復秒殺異常、秒殺結束異常)。 Exposer.java: public class Exposer { private boolean exposed; private String md5; private long seckillId; private long now; private long start; private long end; @Override public String toString() { return "Exposer{" + "exposed=" + exposed + ", md5=‘" + md5 + \‘ + ", seckillId=" + seckillId + ", now=" + now + ", start=" + start + ", end=" + end + }; } public Exposer(boolean exposed, String md5, long seckillId) { this.exposed = exposed; this.md5 = md5; this.seckillId = seckillId; } public Exposer(boolean exposed, long seckillId, long now, long start, long end) { this.exposed = exposed; this.seckillId = seckillId; this.now = now; this.start = start; this.end = end; } public Exposer(boolean exposed, long seckillId) { this.exposed = exposed; this.seckillId = seckillId; } 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: public class SeckillExecution { private long seckillId; private int state; private String stateInfo; private SuccessKilled successKilled; @Override public String toString() { return "SeckillExecution{" + "seckillId=" + seckillId + ", state=" + state + ", stateInfo=‘" + stateInfo + \‘ + ", successKilled=" + successKilled + }; } public SeckillExecution(long seckillId, SeckillStatEnum statEnum, SuccessKilled successKilled) { this.seckillId = seckillId; this.state = statEnum.getState(); this.stateInfo = statEnum.getStateInfo(); this.successKilled = successKilled; } public SeckillExecution(long seckillId, SeckillStatEnum statEnum) { this.seckillId = seckillId; this.state = statEnum.getState(); this.stateInfo = statEnum.getStateInfo(); } 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 String getStateInfo() { return stateInfo; } public void setStateInfo(String stateInfo) { this.stateInfo = stateInfo; } public SuccessKilled getSuccessKilled() { return successKilled; } public void setSuccessKilled(SuccessKilled successKilled) { this.successKilled = successKilled; } } 然後需要在exception包下創建我們在秒殺業務過程中允許的異常,RepeatKillException.java用於處理重復的秒殺異常;SeckillCloseException.java用於處理秒殺關閉異常;SeckillException.java用於處理秒殺相關業務的異常。 RepeatKillException.java: public class RepeatKillException extends SeckillException { public RepeatKillException(String message) { super(message); } public RepeatKillException(String message, Throwable cause) { super(message, cause); } } SeckillCloseException.java: public class SeckillCloseException extends SeckillException { public SeckillCloseException(String message) { super(message); } public SeckillCloseException(String message, Throwable cause) { super(message, cause); } } SeckillException.java: public class SeckillException extends RuntimeException { public SeckillException(String message) { super(message); } public SeckillException(String message, Throwable cause) { super(message, cause); } } 在實現類SeckillServiceImpl.java中,我們用枚舉的方式將異常函數中返回的常量進行封裝,在enums包下創建一個枚舉類型SeckillStatEnum.java,用於返回state和stateInfo這兩個參數的相關數據。 SeckillStatEnum.java: public enum SeckillStatEnum { SUCCESS(1,"秒殺成功"), END(0,"秒殺結束"), 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; } } 使用Spring托管Service依賴配置: 在spring包下創建一個spring-service.xml文件,然後采用註解的方式將Service的實現類加入到Spring IOC容器中,然後在Service實現類的方法中,在需要進行事務聲明的方法上加上事務的註解,配置如下。 spring-service.xml: <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"> <context:component-scan base-package="org.seckill.service"/> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> <tx:annotation-driven transaction-manager="transactionManager"/> </beans>

秒殺系統-service