Java高并发秒时啊API之Service层

Java高并发秒时啊API之Service层1

第一章 秒杀业务接口设计与实现

---1-1----------------------------------
1.DAO层:接口设计+SQL编写(不需要其他杂七杂八的功能)
2.代码和SQL的分离,方便Review(浏览)
3.DAO拼接等逻辑在Service完成(DAO只需负责SQL语句,其他都由Service层完成)

---1-2 秒杀Service接口设计----------------------------------

DTO设计:

Exposer.java 暴露秒杀地址DTO

package org.seckill.dto;

/*
 * 暴露秒杀地址DTO
 * 
 * 
 * */
public class Exposer {
    //是否开启秒杀
    private boolean exposed;
    //一种加密措施
    private String md5;
    
    private long seckillId;
    
    //系统当前时间(毫秒)
    private long now;
    
    private long start;
    
    private long end;
    
    public Exposer(boolean exposed,String md5,long seckillId){
        this.exposed = exposed;
        this.md5 = md5;
        this.seckillId = seckillId;
    }

    public Exposer(boolean exposed, long seckillId) {
        this.exposed = exposed;
        this.seckillId = seckillId;
    }
    public Exposer(boolean exposed, long seckillId, long now, long start, long end) {
        super();
        this.exposed = exposed;
        this.md5 = md5;
        this.seckillId = seckillId;
        this.now = now;
        this.start = start;
        this.end = end;
    }

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 封装秒杀执行结果

package org.seckill.dto;

import org.seckill.entity.SuccessKilled;

/*
 * 封装秒杀执行结果*/
public class SeckillExecution {
    private long seckillId;
    //秒杀结果状态
    private int state;
    //状态表示
    private String stateInfo;
    //秒杀成功对象
    private SuccessKilled successKilled;
    
    public SeckillExecution(long seckillId, int state, String stateInfo, SuccessKilled successKilled) {
        this.seckillId = seckillId;
        this.state = state;
        this.stateInfo = stateInfo;
        this.successKilled = successKilled;
    }
    
    public SeckillExecution(long seckillId, int state, String stateInfo) {
        super();
        this.seckillId = seckillId;
        this.state = state;
        this.stateInfo = stateInfo;
    }

    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 SuccessKilled getSuccessKilled() {
        return successKilled;
    }
    public void setSuccessKilled(SuccessKilled successKilled) {
        this.successKilled = successKilled;
    }
    public String getStateInfo() {
        return stateInfo;
    }
    public void setStateInfo(String stateInfo) {
        this.stateInfo = stateInfo;
    }
}

Service层:

 业务接口:站在"使用者角"度设计接口
 三个方面:方法定义粒度,参数,返回类型(return 类型/exception )友好

SeckillServic.java

package org.seckill.service;

import java.util.List;

import javax.swing.text.html.parser.Entity;

import org.seckill.dto.Exposer;
import org.seckill.dto.SeckillExection;
import org.seckill.entity.Seckill;
import org.seckill.exception.RepeatKillExeception;
import org.seckill.exception.SeckillCloseException;
import org.seckill.exception.SeckillException;

/*
 * 业务接口:站在"使用者角"度设计接口
 * 三个方面:方法定义粒度,参数,返回类型(return 类型/exception )友好
 * */
public interface SeckillService {
    /*
     * 查询所有秒杀记录
     * @return
     * */
    List<Seckill> getSeckillList();
    /*查询单个秒杀记录
     * @param seckillId
     * @retrun
     */
    Seckill getById(long seckillId);
    
    /*
     * 秒杀开启时输出秒杀接口地址,
     * 否则输出系统时间和秒杀 时间
     *  @param seckillId
     *  @retrun
     * */
    Exposer exportSeckillUrl(long seckillId);
    /*
     * 执行秒杀操作
     * @param seckillId
     * @param userPhone
     * @param md5
     * */
    SeckillExecution executeSeckill(long seckillId,long userPhone,String md5)
        throws SeckillException,RepeatKillExeception,SeckillCloseException;
}

Exception:

package org.seckill.exception;

/*
 * 秒杀相关业务异常
 * */
public class SeckillException extends RuntimeException{

    public SeckillException(String message, Throwable cause) {
        super(message, cause);
        // TODO Auto-generated constructor stub
    }

    public SeckillException(String message) {
        super(message);
        // TODO Auto-generated constructor stub
    }
}
package org.seckill.exception;

/*
 * 重复秒杀异常(运行期异常,spring的声明式事务处理的是runtime异常,如果不是这类异常,那么他是不会回滚的)
 * */
public class RepeatKillExeception extends SeckillException{

    public RepeatKillExeception(String message){
        super(message);
    }
    
    public RepeatKillExeception(String message,Throwable cause){
        super(message,cause);
    }
}
package org.seckill.exception;

/*
 *秒杀关闭异常
 * */
public class SeckillCloseException extends SeckillException{

    public SeckillCloseException(String message, Throwable cause) {
        super(message, cause);
        // TODO Auto-generated constructor stub
    }

    public SeckillCloseException(String message) {
        super(message);
        // TODO Auto-generated constructor stub
    }
}

---1-3  1-4秒杀Service接口实现1----------------------------------

SeckillServiceImpl.java

package org.seckill.service.impl;

import java.util.Date;
import java.util.List;

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.SeckillStatEnum;
import org.seckill.exception.RepeatKillExeception;
import org.seckill.exception.SeckillCloseException;
import org.seckill.exception.SeckillException;
import org.seckill.service.SeckillService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.util.DigestUtils;

public class SeckillServiceImpl implements SeckillService {
    private Logger logger = LoggerFactory.getLogger(this.getClass());
    
    private SeckillDao seckillDao;
    
    private SuccessKilledDao successKilledDao;
    
    //md5盐值字符串,用于混淆MD5
    private final String slat = "sldijfldkjfpaojj@#(#$sldfj`123";
    @Override
    public List<Seckill> getSeckillList() {
        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) {
        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);//TODO
        return new Exposer(true,md5,seckillId);
    }

    private String getMD5(long seckillId){
        String base = seckillId+"/"+slat;
        String md5 = DigestUtils.md5DigestAsHex(base.getBytes());
        return md5;
    }
    @Override
    public SeckillExecution executeSeckill(long seckillId, long userPhone, String md5)
            throws SeckillException, RepeatKillExeception, SeckillCloseException {
        if(md5==null|| !md5.equals(getMD5(seckillId))){
            throw new SeckillException("seckill data rewrite");
        }
        //执行秒杀逻辑:减库存 + 记录购买行为
        Date nowTime = new Date();
        try{

            int updateCount = seckillDao.reduceNumber(seckillId, nowTime);
            if(updateCount <=0){
                //没有更新记录
                throw new SeckillCloseException("seckill is closed");
            } else {
                //记录购买行为
                int insertCount= successKilledDao.insertSuccessKilled(seckillId, userPhone);
                //唯一:insert ignore 
                if(insertCount <=0){
                    //重复秒杀
                    throw new RepeatKillExeception("seckill repeated");
                }else {
                    //秒杀成功
                    SuccessKilled successKilled = successKilledDao.queryByIdWithSeckill(seckillId, userPhone);
                    //return new SeckillExecution(seckillId,1,"秒杀成功",successKilled);
                    return new  SeckillExecution(seckillId,SeckillStatEnum.SUCCESS);
                }
            }
            
        } catch (SeckillCloseException e1) {
            throw e1;
        } catch (RepeatKillExeception e2) {
            throw e2;
        }catch (Exception e){
            logger.error(e.getMessage(),e);
            //所有编译期异常 转化为运行期异常
            //spring事务会做roll back
            throw new SeckillException("seckill inner error : "+e.getMessage());
        }

        return null;
    }

}

SeckillExecution.java

package org.seckill.dto;

import org.seckill.entity.SuccessKilled;
import org.seckill.enums.SeckillStatEnum;

/*
 * 封装秒杀执行结果*/
public class SeckillExecution {
    private long seckillId;
    /*此处不做枚举类型的原因:会把这个对象转换为json,通过ajax返回给前端浏览器。
     * 默认的json的类库在转换枚举时有问题。
     * (可以做一个把枚举类型转换为json的transfer,不过比较复杂,此处不使用此方法。
    */
    //秒杀结果状态
    private int state;
    //状态表示
    private String stateInfo;
    //秒杀成功对象
    private SuccessKilled successKilled;

    public SeckillExecution(long seckillId, SeckillStatEnum statEnum) {
        super();
        this.seckillId = seckillId;
        this.state = statEnum.getState();
        this.stateInfo = statEnum.getStateInfo();
    }

    
    //public SeckillExecution(long seckillId, int state, String stateInfo, SuccessKilled successKilled) {
    public SeckillExecution(long seckillId, SeckillStatEnum statEnum, SuccessKilled successKilled) {
        super();
        this.seckillId = seckillId;
        this.state = statEnum.getState();
        this.stateInfo = statEnum.getStateInfo();
        this.successKilled = successKilled;
    }

    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 SuccessKilled getSuccessKilled() {
        return successKilled;
    }
    public void setSuccessKilled(SuccessKilled successKilled) {
        this.successKilled = successKilled;
    }
    public String getStateInfo() {
        return stateInfo;
    }
    public void setStateInfo(String stateInfo) {
        this.stateInfo = stateInfo;
    }
}

SeckillStatEnum.enum

数据字典放入枚举中。

package org.seckill.enums;

//使用枚举表示常量数据
public enum SeckillStatEnum {
    SUCCESS(1,"秒杀成功"),
    END(1,"秒杀结束"),
    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;
    }
}

---1-1----------------------------------

---1-1----------------------------------

原文地址:https://www.cnblogs.com/charles999/p/7122286.html