基于Spring+SpringMVC+Mybatis的秒杀系统之Service层(2)

来源:互联网 发布:怎么注销淘宝卖家账户 编辑:程序博客网 时间:2024/05/29 12:29

接上一节(1)
spring/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/beanshttp://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">    <!-- 扫描servie包下所有使用注解的类型 -->    <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>

org.seckill.enums.SeckillStateEnum:

package org.seckill.enums;/** * 使用枚举表述常量数据字段 * Created by tianjun on 2016/8/4. */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.stateInfo = stateInfo;        this.state = state;    }    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  static SeckillStateEnum stateOf(int index){        for (SeckillStateEnum state : values()){            if(state.getState() == index ){                return state;            }        }        return null;    }}

org.seckill.exception.SeckillExpection:

package org.seckill.exception;/** * 秒杀相关业务异常 * Created by tianjun on 2016/8/4. */public class SeckillExpection extends RuntimeException {    public SeckillExpection(String message) {        super(message);    }    public SeckillExpection(String message, Throwable cause) {        super(message, cause);    }}

org.seckill.exception.RepeatKillException:

package org.seckill.exception;/** * 重复秒杀异常(运行期异常) * Created by tianjun on 2016/8/4. */public class RepeatKillException extends SeckillExpection {    public RepeatKillException(String message) {        super(message);    }    public RepeatKillException(String message, Throwable cause) {        super(message, cause);    }}

org.seckill.exception.SeckillCloseException:

package org.seckill.exception;/** * 秒杀关闭异常 * Created by tianjun on 2016/8/4. */public class SeckillCloseException extends SeckillExpection {    public SeckillCloseException(String message) {        super(message);    }    public SeckillCloseException(String message, Throwable cause) {        super(message, cause);    }}

org.seckill.service.SeckillService:

package org.seckill.service;import org.seckill.dto.Exposer;import org.seckill.dto.SeckillExecution;import org.seckill.entity.Seckill;import org.seckill.exception.RepeatKillException;import org.seckill.exception.SeckillCloseException;import org.seckill.exception.SeckillExpection;import java.util.List;/** * 业务接口:站在:“使用者”的角度设计接口 * 三个方面:方法的粒度,参数,返回类型(return 类型/异常) * Created by tianjun on 2016/8/4. */public interface SeckillService {    /**     * 查询所有秒杀记录     * @return     */    List<Seckill> getSeckillList();    /**     * 查询单个秒杀记录     * @param seckillId     * @return     */    Seckill getById(long seckillId);    /**     * 秒杀开启时输出秒杀接口地址,否则输出系统时间和秒杀时间     * @param seckillId     */    Exposer exportSeckillUrl(long seckillId);    /**     * 执行秒杀操作     * @param seckillId     * @param userPhone     * @param md5     */    SeckillExecution executeSeckill(long seckillId,long userPhone,String md5)        throws SeckillExpection,RepeatKillException,SeckillCloseException;}

org.seckill.service.impl.SeckillServiceImpl:

package org.seckill.service.impl;import org.seckill.dao.SeckillDao;import org.seckill.dao.SuccessKilledDao;import org.seckill.dto.Exposer;import org.seckill.dto.SeckillExecution;import org.seckill.entity.Seckill;import org.seckill.entity.SuccessKilled;import org.seckill.enums.SeckillStateEnum;import org.seckill.exception.RepeatKillException;import org.seckill.exception.SeckillCloseException;import org.seckill.exception.SeckillExpection;import org.seckill.service.SeckillService;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 java.util.Date;import java.util.List;/** * * Created by tianjun on 2016/8/4. */@Service("seckillService")public class SeckillServiceImpl implements SeckillService {    private Logger logger = LoggerFactory.getLogger(this.getClass());    @Autowired    private SeckillDao seckillDao;    @Autowired    private SuccessKilledDao successKilledDao;    //md5盐值,用来混淆md5    private final String slat = "123*&*^%%^^(-9lsdjfsjflsyiyiiuiuiu";    @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 = seckillDao.queryById(seckillId);        if(seckill == null){            return  new Exposer(false,seckillId);        }        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 + "/" + slat;        String md5 = DigestUtils.md5DigestAsHex(base.getBytes());        return md5;    }    @Override    @Transactional    /**     * 使用事务控制事务方法的有点:     * 1:开发团队达成一致约定,明确标注事务方法的编程格式。     * 2:保证事务方法的而执行时间尽可能短,不要穿插其他的网络操作,RPC/HTTP请求或者剥离到事务方法外部。     * 3: 不是所有的方法都需要事务,如只有一条修改操作,只读操作不需要事务控制。     */    public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5) throws SeckillExpection, RepeatKillException, SeckillCloseException {        if(md5 == null || !md5.equals(getMD5(seckillId))){            throw new SeckillExpection("seckill data rewrite");        }        //执行秒杀逻辑 + 记录购买行为        Date nowTime = new Date();        try {            int updateCount = seckillDao.reduceNumber(seckillId,nowTime);            if(updateCount<=0){                //没有更新到记录                throw new SeckillCloseException("seckill is close");            } else {              //记录购买行为                int insertCount = successKilledDao.insertSuccessKilled(seckillId,userPhone);                //唯一:seckillId,userPhone                if(insertCount<=0){                    //重复秒杀                    throw new RepeatKillException("seckill repeat");                }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事务回滚只对运行期异常起作用)            throw new SeckillExpection("seckill inner error:"+e.getMessage());        }    }}

org.seckill.service.impl.SeckillServiceImplTest:

package org.seckill.service.impl;import org.junit.Test;import org.junit.runner.RunWith;import org.seckill.dto.Exposer;import org.seckill.dto.SeckillExecution;import org.seckill.entity.Seckill;import org.seckill.exception.RepeatKillException;import org.seckill.exception.SeckillCloseException;import org.seckill.exception.SeckillExpection;import org.seckill.service.SeckillService;import org.slf4j.Logger;import org.slf4j.LoggerFactory;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.test.context.ContextConfiguration;import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;import java.util.List;import static org.junit.Assert.*;/** * 测试类 * Created by tianjun on 2016/8/4. */@RunWith(SpringJUnit4ClassRunner.class)@ContextConfiguration({        "classpath:spring/spring-dao.xml",        "classpath:spring/spring-service.xml"})public class SeckillServiceImplTest {    private final Logger logger = LoggerFactory.getLogger(this.getClass());    @Autowired    private SeckillService seckillService;    @Test    public void testGetSeckillList() throws Exception {        List<Seckill> list = seckillService.getSeckillList();        logger.info("list={}",list);    }    @Test    public void testGetById() throws Exception {        long id = 1000L;        Seckill seckill = seckillService.getById(id);        logger.info("seckill={}",seckill);    }    //测试代码完整逻辑,注意可重复执行    @Test    public void testSeckillLogic() throws Exception {        long id = 1001L;        Exposer exposer = seckillService.exportSeckillUrl(id);        if(exposer.isExposed()){            logger.info("exposer={}",exposer);            long phone = 18867102345L;            String md5 = exposer.getMd5();            try {                SeckillExecution seckillExecution = seckillService.executeSeckill(id, phone, md5);                logger.info("result={}",seckillExecution);            } catch (RepeatKillException e) {                logger.error(e.getMessage());            } catch (SeckillCloseException e) {                logger.error(e.getMessage());            }        }else {            //秒杀未开启            logger.warn("exposer={}",exposer);        }    }}

综上,测试通过,即Service层完成,进入controller层。

0 0
原创粉丝点击