[DAO编写之后的总结]
DAO层 --> 接口设计 + SQL编写
DAO拼接等逻辑 --> 统一在Service层完成
[Service层的接口设计]
1.接口设计原则:站在'使用者'的角度设计接口
2.方法定义粒度:从'使用者'的行为角度来思考-- 减库存+插入购买明细 --> 执行秒杀
3.参数:越简练越直接
4.返回类型:return 类型/Exception 返回类型要友好,使用DTO
package org.azcode.service; import org.azcode.dto.Exposer; import org.azcode.dto.SeckillExecution; import org.azcode.entity.Seckill; import org.azcode.exception.RepeatKillException; import org.azcode.exception.SeckillCloseException; import org.azcode.exception.SeckillException; import java.util.List; /** * Created by azcod on 2017/4/15. */ public interface SeckillService { /** * 查询所有的秒杀记录 * @return */ List<Seckill> getSeckillList(); /** * 根据id查询单个秒杀记录 * @param seckillId * @return */ Seckill getById(long seckillId); /** * 秒杀开启时输出秒杀接口地址, * 否则输出系统时间和秒杀时间 * @param seckillId * @return Exposer */ Exposer exportSeckillUrl(long seckillId); /** * 执行秒杀操作 * @param seckillId * @param userPhone * @param md5 * @return SeckillExecution */ SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillException,RepeatKillException,SeckillCloseException; }
[Service接口实现编码]
1.防止数据篡改的措施,使用MD5给数据加密
2.具体业务逻辑的编码,对异常的处理
3.异常的多重catch,将编译期异常转换为运行期异常
4.使用枚举表述常量数据字典
package org.azcode.service.impl; import org.azcode.dao.SeckillDao; import org.azcode.dao.SuccessKilledDao; import org.azcode.dto.Exposer; import org.azcode.dto.SeckillExecution; import org.azcode.entity.Seckill; import org.azcode.entity.SuccessKilled; import org.azcode.enums.SeckillStateEnum; import org.azcode.exception.RepeatKillException; import org.azcode.exception.SeckillCloseException; import org.azcode.exception.SeckillException; import org.azcode.service.SeckillService; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.util.DigestUtils; import java.util.Date; import java.util.List; /** * Created by azcod on 2017/4/15. */ public class SeckillServiceImpl implements SeckillService { private Logger logger = LoggerFactory.getLogger(this.getClass()); private SeckillDao seckillDao; private SuccessKilledDao successKilledDao; //MD5盐值字符串,用于混淆MD5 private final String slat = "r#^*T*(&F)HHU!&@T#456465"; public List<Seckill> getSeckillList() { return seckillDao.queryAll(0, 4); } public Seckill getById(long seckillId) { return seckillDao.queryById(seckillId); } public Exposer exportSeckillUrl(long seckillId) { Seckill seckill = seckillDao.queryById(seckillId); //step1:判断秒杀产品是否存在 if (seckill == null) { return new Exposer(false, seckillId); } //step2:判断秒杀是否开启 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()); } //MD5:一种转换特定字符串的过程,不可逆 String md5 = getMD5(seckillId); return new Exposer(true, md5, seckillId); } public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillException, RepeatKillException, SeckillCloseException { //step1:校验是否篡改秒杀数据 if (md5 == null || !md5.equals(getMD5(seckillId))) { throw new SeckillException("seckill data rewrite"); } //step2:秒杀逻辑-减库存+插入购买明细 Date nowTime = new Date(); // 异常捕获的IDEA快捷键:包选代码块后->ctrl+alt+t try { //减库存 int updateCount = seckillDao.reduceNumber(seckillId, nowTime); if (updateCount <= 0) { //秒杀结束 throw new SeckillCloseException("seckill is closed"); } else { //插入购买明细 int insertCount = successKilledDao.insertSuccessKilled(seckillId, userPhone); if (insertCount <= 0) { //重复秒杀 throw new RepeatKillException("seckill repeated"); } else { //秒杀成功 SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone); return new SeckillExecution(seckillId, SeckillStateEnum.SUCCESS, successKilled); } } } catch (SeckillCloseException e1) { throw e1; } catch (RepeatKillException e2) { throw e2; } catch (Exception e) { logger.error(e.getMessage(), e); //所有编译期异常转换为运行期异常,Spring声明式事务只支持运行期异常的回滚 //转换的目的是为了出现编译期异常时,rollback回滚 throw new SeckillException("seckill inner error" + e.getMessage()); } } /** * 生成MD5的通用方法 * * @param seckillId * @return */ private String getMD5(long seckillId) { String base = seckillId + slat; String md5 = DigestUtils.md5DigestAsHex(base.getBytes()); return md5; } }
package org.azcode.enums; /** * 使用枚举表述常量数据字典 * Created by azcod on 2017/4/15. */ public enum SeckillStateEnum { SUCCESS(1, "秒杀成功"), END(0, "秒杀结束"), REPEAT_KILL(-1, "重复秒杀"), INNER_ERROR(-2, "系统异常"), DATA_REWRITE(-3, "数据篡改"); private int state; private String stateInfo; SeckillStateEnum(int state, String stateInfo) { this.state = state; this.stateInfo = stateInfo; } public int getState() { return state; } public String getStateInfo() { return stateInfo; } public static SeckillStateEnum stateOf(int index) { for (SeckillStateEnum stateEnum : values()) { if (stateEnum.getState() == index) { return stateEnum; } } return null; } }