秒杀系统-service

Posted 懵懂的菜鸟

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了秒杀系统-service相关的知识,希望对你有一定的参考价值。


在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的主要内容,如果未能解决你的问题,请参考以下文章

从构建分布式秒杀系统聊聊Lock锁使用中的坑

从构建分布式秒杀系统聊聊Lock锁使用中的坑

02 整合IDEA+Maven+SSM框架的高并发的商品秒杀项目之Service层

秒杀系统个人总结

SSM实战——秒杀系统前言

Java实现高并发秒杀API--Service层2