接上一篇 https://www.cnblogs.com/taiguyiba/p/9829191.html 封装了系统传递的数据类和异常类
本文继续设计service层设计:
1.SeckillService.java
package com.seckill.service; import java.util.List; import com.seckill.dto.Exposer; import com.seckill.dto.SeckillExecution; import com.seckill.entity.Seckill; import com.seckill.exception.RepeatKillException; import com.seckill.exception.SeckillCloseException; import com.seckill.exception.SeckillException; /** * 三个方面:1.方法定义的粒度,参数,返回类型(return ,类型/异常) * @author kangjie * */
public interface SeckillService { /** * * 查询所有秒杀记录 * @return */ List <Seckill> getSeckillList(); /** * * 查询单个秒杀记录 * @param seckillId * @return */ Seckill getById(long seckillId); /** * 秒杀开启时输出秒杀接口地址 否则输出系统时间和秒杀时间 * @param seckillId */ Exposer exportSeckillUrl(long seckillId); //执行秒杀操作
SeckillExecution excuteSeckill(long seckillId,long userPhone,String md5) throws RepeatKillException,SeckillCloseException,SeckillException; //执行秒杀操作by存储过程
SeckillExecution excuteSeckillProcedure(long seckillId,long userPhone,String md5); }
2. SeckillServiceImpl
秒杀事务控制,在函数中如果更新出现问题,就会抛出异常,进行事务回滚。
package com.seckill.service.impl; import java.util.Date; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.commons.collections.MapUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.util.DigestUtils; import com.seckill.dao.SeckillDao; import com.seckill.dao.SuccessKilledDao; import com.seckill.dao.cache.RedisDao; import com.seckill.dto.Exposer; import com.seckill.dto.SeckillExecution; import com.seckill.entity.Seckill; import com.seckill.entity.SuccessKilled; import com.seckill.enums.SeckillStateEnum; import com.seckill.exception.RepeatKillException; import com.seckill.exception.SeckillCloseException; import com.seckill.exception.SeckillException; import com.seckill.service.SeckillService; @Service public class SeckillServiceImpl implements SeckillService { private Logger logger=LoggerFactory.getLogger(this.getClass()); @Autowired private SeckillDao seckillDao; @Autowired private SuccessKilledDao successKilledDao; @Autowired private RedisDao redisDao; //md5盐值,用于混淆md5
private final String salt = "!@#!@#!@#$ASDFASDFzxcv_|+)+)(("; @Override public List<Seckill> getSeckillList() { // TODO Auto-generated method stub
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) { // TODO Auto-generated method stub //访问redis:缓存优化,超时的基础上维护一致性
Seckill seckill = redisDao.getSeckill(seckillId); if(seckill == null) { //访问数据库
seckill = seckillDao.queryById(seckillId); if(seckill == null) { return new Exposer(false, seckillId); }else { logger.error("放入redis中: " + seckill.toString()); redisDao.putSeckill(seckill); } } Date startTime = seckill.getStartTime(); Date endTime = seckill.getEndTime(); Date nowTime = new Date(); if(nowTime.getTime() > endTime.getTime() ||nowTime.getTime() < startTime.getTime()) { return new Exposer(false, nowTime.getTime(), startTime.getTime(), endTime.getTime(),seckillId); } String md5 = getMD5(seckillId); return new Exposer(true, md5, seckillId); // return null;
} private String getMD5(long seckillId) { String base = seckillId + "/" + salt; String md5 = DigestUtils.md5DigestAsHex(base.getBytes()); return md5; } @Override @Transactional public SeckillExecution excuteSeckill(long seckillId, long userPhone, String md5) throws RepeatKillException, SeckillCloseException, SeckillException { // TODO Auto-generated method stub
if(md5 == null || !md5.equals(getMD5(seckillId))) { throw new SeckillException("seckill data rewirte"); } try { int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone); if (insertCount <= 0) { // 重复秒杀
throw new RepeatKillException("repeat seckill"); } else { int updateCount = seckillDao.reduceNumber(seckillId, new Date()); if (updateCount <= 0) { throw new SeckillCloseException("seckill closed"); } else { // 秒杀成功
SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone); return new SeckillExecution(seckillId,SeckillStateEnum.SUCCESSD, successKilled); } } } catch (RepeatKillException e) { // TODO: handle exception
logger.error(e.getMessage(),e); throw new RepeatKillException("repeat seckill"); }catch (SeckillCloseException e) { // TODO: handle exception
logger.error(e.getMessage(),e); throw new SeckillCloseException("seckill closed"); }catch (Exception e) { logger.error(e.getMessage(), e);; throw new SeckillException("Inner error: " + e.getMessage()); } } @Override public SeckillExecution excuteSeckillProcedure(long seckillId, long userPhone, String md5){ // TODO Auto-generated method stub
if(md5 == null || !md5.equals(getMD5(seckillId))) { return new SeckillExecution(seckillId,SeckillStateEnum.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); //获取result
int result = MapUtils.getInteger(map, "result",-2); if(result == 1) { SuccessKilled sk = successKilledDao.queryByIdWithSeckill(seckillId, userPhone); return new SeckillExecution(seckillId,SeckillStateEnum.SUCCESSD,sk); }else { return new SeckillExecution(seckillId, SeckillStateEnum.stateOf(result)); } } catch (Exception e) { // TODO Auto-generated catch block
logger.error(e.getMessage(),e); return new SeckillExecution(seckillId, SeckillStateEnum.INNER_ERROR); }
} }