首页 诗词 字典 板报 句子 名言 友答 励志 学校 网站地图
当前位置: 首页 > 教程频道 > 开发语言 > 编程 >

在线交易平台-配额管理模块的设计

2012-10-28 
在线交易平台---配额管理模块的设计??? 1、 配额管理模块设计的思路:????????? a、引入账户模型。通过引入账

在线交易平台---配额管理模块的设计

??? 1、 配额管理模块设计的思路:

????????? a、引入账户模型。通过引入账户模型,可以通过控制配额账户的活动,我们可以允许透支,可以规定额度,也可以冻结其账户。另外系统可以追溯配额的使用情况,来龙去脉。

????????? b、引入生产者和消费者模型。配额的产生和配额的使用是两个不同的活动。我把前一种活动产生的配额叫生产配额,后一种叫消费配额。

????????? 生产配额是有期限的,过期的生产配额是无法使用的,同时生产配额必须记录它已经使用了多少配额,这通过持有消费配额的集合 来达到目的。

???????? 与此相反,消费配额没有有效期,但是它必须记录配额在什么时候被使用。

???????? 通过这种方式,可以计算某个消费配额的剩余额度,也容易地作废某些过期配额。

?

?

??? 2、集体实现

?? ? ?? 首先是 QuotaEntery(配额条目),做为配额增加、减少、使用、调整、作废等情况的。

/** * 配额条目 */public class QuotaEntry {    /** 项目类型*/    protected QuotaEntryType entryType;    /** 额度,可正,可负 */    protected int amount;    /** 创建时间*/    private Date createTime;    protected QuotaAccount account;    public QuotaEntryType getEntryType() {        return entryType;    }    public void setEntryType(QuotaEntryType entryType) {        this.entryType = entryType;    }    public int getAmount() {        return amount;    }    public void setAmount(int amount) {        this.amount = amount;    }    public QuotaAccount getAccount() {        return account;    }    public void setAccount(QuotaAccount account) {        this.account = account;    }    public Date getCreateTime() {        return createTime;    }    public void setCreateTime(Date createTime) {        this.createTime = createTime;    }}

?

??? 其次是ConsumeQuotaEntry(消费者)和ProduceQuotaEntry(生产者)。他们继承QuotaEntry,不同的是ProdcuceQuotaEntry记录配额的产生,ConsumeQuotaEntry则是记录配额的减少,

?

/** * 生产配额 条目 */public class ProduceQuotaEntry extends QuotaEntry implements Comparable<ProduceQuotaEntry>{    /**     * 有效期     */    public static int DEFALUT_LIFE_YEAR = 2;    /**     * 配额的有效期     */    private Date beginTime;    private Date endTime;    private List<ConsumeQuotaEntry> consumeEntries = new ArrayList<ConsumeQuotaEntry>(10);    public Date getBeginTime() {        return beginTime;    }    public void setBeginTime(Date beginTime) {        this.beginTime = beginTime;    }    public Date getEndTime() {        return endTime;    }    public void setEndTime(Date endTime) {        this.endTime = endTime;    }    /**     * 是否是可用的配额条目     */    public boolean isUsable() {        Date current = new Date();        return  current.getTime() >= beginTime.getTime() && current.getTime() <= endTime.getTime() &&                 caclRemainQuota() > 0;    }    /**     * 是否是过期的配额     * @return     */    public boolean isOverdue(){        return new Date().getTime() > endTime.getTime();    }    /**     * 计算当前条目剩下可用的配额     * @return     */    public int caclRemainQuota(){       int used = 0;       for(ConsumeQuotaEntry entry:consumeEntries){             used += entry.getAmount();       }       return amount + used;    }    public int compareTo(ProduceQuotaEntry o) {        if(this.getEndTime().getTime() > o.getEndTime().getTime()){            return 1;        }        if(this.getEndTime().getTime() < o.getEndTime().getTime()){            return -1;        }        return 0;    }    public void addConsumeEntry(ConsumeQuotaEntry consume) {        consumeEntries.add(consume);        consume.setProduce(this);    }}

?

/** * 消费配额 */public class ConsumeQuotaEntry extends QuotaEntry {    /**减少配额 所依据的 消费配额额 */    private ProduceQuotaEntry produce;    public ProduceQuotaEntry getProduce() {        return produce;    }    public void setProduce(ProduceQuotaEntry produce) {        this.produce = produce;    }}

?

??? QuotaEntryType,枚举类,记录 导致配额增加,减少的业务种类

public enum QuotaEntryType {    TransferFrom, //转入    TransferTo,   //转出    Deduct,       //扣减    Restore,      //归还    Init,             //初始    Decrease,     //减少    Increase,      //增加    Repeal;         //作废}

?

?? QuotaEntryFactory,配额条目工厂,屏蔽创建配额条目的细节

?

public class QuotaEntryFactory {     /**     * 返回 过期作废的配额条目     * @param amount     * @return     */    public static ConsumeQuotaEntry getRepealEntry(int amount) throws QuotaException {       return getReducedEntry(amount,QuotaEntryType.Repeal);    }    /**     * 返回 转出的 配额     * @param amount     * @return     * @throws QuotaException     */    public static ConsumeQuotaEntry getTransferToEntry(int amount) throws QuotaException {       return getReducedEntry(amount,QuotaEntryType.TransferTo);    }    /**     * 返回 扣减的配额     * @param amount     * @return     * @throws QuotaException     */    public static ConsumeQuotaEntry getDeductEntry(int amount) throws QuotaException {        return getReducedEntry(amount,QuotaEntryType.Deduct);    }    /**     * 返回 减少的配额     * @param amount     * @return     * @throws QuotaException     */    public static ConsumeQuotaEntry getDecreaseEntry(int amount) throws QuotaException {        return getReducedEntry(amount,QuotaEntryType.Decrease);    }    /**     * 返回 归还的配额     * @param consume     * @return     * @throws QuotaException     */    public static ConsumeQuotaEntry getRestoreEntry(ConsumeQuotaEntry consume) throws QuotaException {        ConsumeQuotaEntry newConsume = new ConsumeQuotaEntry();        newConsume.setEntryType(QuotaEntryType.Restore);        newConsume.setAmount(-consume.getAmount());        newConsume.setCreateTime(new Date());        return newConsume;    }    public static ConsumeQuotaEntry getReducedEntry(int amount,QuotaEntryType type) throws QuotaException {        check(amount);        ConsumeQuotaEntry consume = new ConsumeQuotaEntry();        consume.setEntryType(type);        consume.setCreateTime(new Date());        consume.setAmount(-amount);        return consume;   }    public static ProduceQuotaEntry getInitEntry(int amount) throws QuotaException {        check(amount);        return getRaisedEntry(amount,QuotaEntryType.Init);    }    public static ProduceQuotaEntry getIncreaseQuotaEntry(int amount) throws QuotaException {        check(amount);        return getRaisedEntry(amount,QuotaEntryType.Increase);    }    public static ProduceQuotaEntry getTransferFromEntry(int amount) throws QuotaException {           check(amount);           return getRaisedEntry(amount,QuotaEntryType.TransferFrom);    }    public static ProduceQuotaEntry getRaisedEntry(int amount,QuotaEntryType type){           ProduceQuotaEntry entry = new ProduceQuotaEntry();           entry.setEntryType(type);           Calendar c = Calendar.getInstance();           entry.setBeginTime(c.getTime());           c.add(Calendar.YEAR, ProduceQuotaEntry.DEFALUT_LIFE_YEAR);           entry.setEndTime(c.getTime());           entry.setAmount(amount);           entry.setCreateTime(new Date());           return entry;    }     private static void check(int amount) throws QuotaException {        if (amount < 0) throw new QuotaException(QuotaMsg.QuotaMustBeMoreThanZero.getName());    }}

?

? ? QuotaException(配额异常消息类)

?

public class QuotaException extends Exception {    public QuotaException(String s) {        super(s);    }}

?

?? QuotaMsg(各种异常消息)

?

public enum QuotaMsg {    AccountHasBeenFrozen {        public String getName() {            return "账户已被冻结";        }    },    AccountCantBeOverDraft {        public String getName() {            return "账户不可透支";        }    },    ItHasBeyondLimitQuota {        public String getName() {            return "已经超过透支额度";        }    },    QuotaMustBeMoreThanZero {        public String getName() {            return "配额必须大于0";        }    },    AnAccountOnlyHasAnInitEntry {        public String getName() {            return "一个账户只有一条Init记录";        }    };    public abstract String getName();}

?

??? QuotaAccount,业务逻辑发生的地方

?

/** * 配额账户.. */public class QuotaAccount {    /** 账户名称 */    private String name;    /** 是否冻结 */    private boolean frozen;    /** 是否可以透支 */    private boolean overdrawn;    /** 透支额度 */    private Long limit;    /** 配额条目 */    private List<QuotaEntry> quotaEntries = new ArrayList<QuotaEntry>(16);     /**     * 购买商品后,扣减配额     * @param amount 额度     */    public List<ConsumeQuotaEntry> deduct(int amount) throws QuotaException {        checkAccount(amount);        //可用的生产配额        List<ProduceQuotaEntry> avaiEntries = findAvaiEntry();        //生成的消费配额        List<ConsumeQuotaEntry> consumeList = reduceQuota(avaiEntries,amount,QuotaEntryType.Deduct);        return consumeList;    }    /**     * 计算剩余配额     * @return     */    public int caclOddQuota(){        int count = 0;        for(QuotaEntry entry:quotaEntries){            count += entry.getAmount();        }        return count;    }    /**     * 归还配额     * @param consume 消费配额     * @throws QuotaException     */    private void restore(ConsumeQuotaEntry consume) throws QuotaException {        //1、找到对应的生产积分条目        ProduceQuotaEntry produce = consume.getProduce();        //2、生成一条 归还条目        ConsumeQuotaEntry newConsume = QuotaEntryFactory.getRestoreEntry(consume);        //3、把积分写到指定生产配额下        produce.addConsumeEntry(newConsume);        //4、添加到账户下        addQuotaEntry(newConsume);    }    /**     * 归还配额     * @param consumeList 消费条目列表     * @throws QuotaException     */    public void restore(List<ConsumeQuotaEntry> consumeList) throws QuotaException {        for(ConsumeQuotaEntry consume:consumeList){            restore(consume);        }    }    /**     * 作废生产配额     * @param produce 生产配额     * @return     * @throws QuotaException     */    public ConsumeQuotaEntry repeal(ProduceQuotaEntry produce) throws QuotaException {        int remain = produce.caclRemainQuota();        ConsumeQuotaEntry consume = QuotaEntryFactory.getRepealEntry(remain);        produce.addConsumeEntry(consume);        addQuotaEntry(consume);        return consume;    }    /**     * 作废所有过期的配额     * @return     */    public void repeal() throws QuotaException {        for(QuotaEntry entry:quotaEntries) {            if(ProduceQuotaEntry.class.isInstance(entry)){                ProduceQuotaEntry produce = (ProduceQuotaEntry)entry;                if(produce.isOverdue() && produce.caclRemainQuota() > 0){                    repeal(produce);                }            }        }    }    /**     * 把 一定数量的配额 转移到 另一个账户上     *     * @param account 目标账户     * @param amount  额度     * @throws QuotaException     */    public void transferTo(QuotaAccount account, int amount) throws QuotaException {        checkAccount(amount);        List<ProduceQuotaEntry> avaiEntries = findAvaiEntry();        List<ConsumeQuotaEntry> consumeList= reduceQuota(avaiEntries,amount,QuotaEntryType.TransferTo);        account.transferFrom(consumeList);    }    /**     * 接收 从某个账户转移过来的额度     *     */    public void transferFrom(List<ConsumeQuotaEntry>  consumeList) throws QuotaException {        int amount =  0;        for(ConsumeQuotaEntry consume:consumeList){           amount += consume.getAmount();        }        amount = Math.abs(amount);        transferFrom(amount);    }    private void transferFrom(int amount) throws QuotaException {        ProduceQuotaEntry produce = QuotaEntryFactory.getTransferFromEntry(amount);        addQuotaEntry(produce);    }    /**     * 增加 配额     *     * @param amount     */    public ProduceQuotaEntry increaseQuota(int amount) throws QuotaException {        ProduceQuotaEntry produce =  QuotaEntryFactory.getIncreaseQuotaEntry(amount);        addQuotaEntry(produce);        return produce;    }    /**     * 减少 配额     * produce 生产配额     * @param amount     */    public void decreaseQuota(ProduceQuotaEntry produce,int amount) throws QuotaException {        checkAccount(amount);        if(produce.caclRemainQuota() <amount) throw new QuotaException("生产配额少于消费配额");        ConsumeQuotaEntry consume = QuotaEntryFactory.getDeductEntry(amount);        addQuotaEntry(consume);    }    /**     * 找到可用的配额账目     * @return     */    private List<ProduceQuotaEntry> findAvaiEntry(){        List<ProduceQuotaEntry> avaiEntries = new ArrayList<ProduceQuotaEntry>(10);        for(QuotaEntry entry:quotaEntries){            if(!ProduceQuotaEntry.class.isInstance(entry)) break;            ProduceQuotaEntry produce = (ProduceQuotaEntry) entry;            if(produce.caclRemainQuota() > 0 && produce.isUsable()){                avaiEntries.add(produce);            }        }        Collections.sort(avaiEntries);        return avaiEntries;    }    /**     * 创建初始配额账户     *     * @param amount     */    public void init(int amount) throws QuotaException {        if (hasInitEntry()) throw new QuotaException(QuotaMsg.AnAccountOnlyHasAnInitEntry.getName());       addQuotaEntry(QuotaEntryFactory.getInitEntry(amount));    }    public boolean hasInitEntry() {        boolean has = false;        for (QuotaEntry entry : quotaEntries) {            if (entry.getEntryType().equals(QuotaEntryType.Init)) {                has = true;                break;            }        }        return has;    }    /**     * 从一群 可用的而生产配额中,减去 配额     * @param avaiEntries     * @param amount     * @return  扣减记录     * @throws QuotaException     */    private List<ConsumeQuotaEntry> reduceQuota(List<ProduceQuotaEntry> avaiEntries,int amount,QuotaEntryType type) throws QuotaException {        List<ConsumeQuotaEntry> consumeList= new ArrayList<ConsumeQuotaEntry>(4);        int remain =amount;        int toReduce = amount;        Iterator<ProduceQuotaEntry> iterator = avaiEntries.iterator();        while(iterator.hasNext()){            ProduceQuotaEntry produce = iterator.next();            remain -=  produce.caclRemainQuota();            ConsumeQuotaEntry consume= null;            //如果还没有扣完,而且还有配额条目            if(remain > 0 &&  iterator.hasNext()){                 consume = QuotaEntryFactory.getReducedEntry(produce.getAmount(),type);                 toReduce = amount - produce.getAmount();            //如果没有扣完,也没有其他配额条目,            }else if(remain > 0 && !iterator.hasNext()){                 consume = QuotaEntryFactory.getDeductEntry(toReduce);            }else if(remain <= 0){                 consume = QuotaEntryFactory.getDeductEntry(toReduce);            }            produce.addConsumeEntry(consume);            addQuotaEntry(consume);            consumeList.add(consume);        }        return consumeList;    }    /**     * 检验配额是是否足够     * @param amount     * @throws QuotaException     */    private void checkAccount(int amount) throws QuotaException {        if (isFrozen()) throw new QuotaException(QuotaMsg.AccountHasBeenFrozen.getName());        if (!isOverdrawn() && caclOddQuota() < amount)            throw new QuotaException(QuotaMsg.AccountCantBeOverDraft.getName());        if (isOverdrawn() && (caclOddQuota() + limit < amount))            throw new QuotaException(QuotaMsg.ItHasBeyondLimitQuota.getName());    }    /**     * 添加配额条目     * @param quotaEntry     */    private void addQuotaEntry(QuotaEntry quotaEntry){        quotaEntries.add(quotaEntry);        quotaEntry.setAccount(this);    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    public boolean isFrozen() {        return frozen;    }    public void setFrozen(boolean frozen) {        this.frozen = frozen;    }    public boolean isOverdrawn() {        return overdrawn;    }    public void setOverdrawn(boolean overdrawn) {        this.overdrawn = overdrawn;    }    public Long getLimit() {        return limit;    }    public void setLimit(Long limit) {        this.limit = limit;    }    public List<QuotaEntry> getQuotaEntries() {        return quotaEntries;    }    public void setQuotaEntries(List<QuotaEntry> quotaEntries) {        this.quotaEntries = quotaEntries;    }}

??? 测试类

?

public class QuotaAccountTest {    private QuotaAccount account;    private QuotaAccount accountTwo;    @Before    public void setUp() throws QuotaException, ParseException {        account = new QuotaAccount();        account.increaseQuota(34);        account.setFrozen(false);        account.setLimit(10L);        account.setOverdrawn(true);        account.setName("小明的账户");        accountTwo = new QuotaAccount();        accountTwo.increaseQuota(34);        accountTwo.setFrozen(false);        accountTwo.setLimit(10L);        accountTwo.setOverdrawn(true);        accountTwo.setName("小明的账户2");    }    @After    public void tearDown(){        account =null;        accountTwo = null;    }    @Test    public void caclRemainQuota(){        assertEquals(34, account.caclOddQuota());    }    @Test    public void caclRemainQuotaAfterDeducting4Quotas() throws QuotaException {        account.deduct(4);        assertEquals(30,account.caclOddQuota());    }    @Test    public void caclRemainQuotaAfterDeducting44Quotas() throws QuotaException {        account.deduct(44);        assertEquals(-10,account.caclOddQuota());    }    @Test(expected = QuotaException.class)    public void caclRemainQuotaAfterDeductingAndAccountItIsNotOverdrawn() throws QuotaException {        account.setOverdrawn(false);        account.deduct(44);    }    @Test    public void caclRemainQuotaAfterRestoreQuota() throws QuotaException {        List<ConsumeQuotaEntry> consumes =account.deduct(44);        assertEquals(-10,account.caclOddQuota());        account.restore(consumes);        assertEquals(34,account.caclOddQuota());    }       @Test    public void caclRemainQuotaAferIncreasingQuotaAndDecutingQuota() throws QuotaException {        account.increaseQuota(20);        List<ConsumeQuotaEntry> consumes =account.deduct(44);        assertEquals(10,account.caclOddQuota());    }    @Test    public void transferSmallQuotas() throws QuotaException {        account.transferTo(accountTwo,12);        assertEquals(22,account.caclOddQuota());        assertEquals(46,accountTwo.caclOddQuota());    }    @Test    public void transferMiddleQuotas() throws QuotaException {        account.transferTo(accountTwo,34);        assertEquals(0,account.caclOddQuota());        assertEquals(68,accountTwo.caclOddQuota());    }      @Test    public void transferLargeQuotas() throws QuotaException {        account.transferTo(accountTwo,44);        assertEquals(-10,account.caclOddQuota());        assertEquals(78,accountTwo.caclOddQuota());    }       @Test    public void transferLargeLargeQuota() throws QuotaException {        account.increaseQuota(30);        account.transferTo(accountTwo,44);        assertEquals(20,account.caclOddQuota());        assertEquals(78,accountTwo.caclOddQuota());    }    @Test    public void testRepealEntry() throws QuotaException {        ProduceQuotaEntry produce = new ProduceQuotaEntry();        produce.setAmount(34);        ConsumeQuotaEntry consume =account.repeal(produce);        assertEquals(0, produce.getAmount() + consume.getAmount());    }}

?

?

热点排行