com.cai310.lottery.service.lottery.keno.impl.KenoServiceImpl.java Source code

Java tutorial

Introduction

Here is the source code for com.cai310.lottery.service.lottery.keno.impl.KenoServiceImpl.java

Source

package com.cai310.lottery.service.lottery.keno.impl;

import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.annotation.Resource;

import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.time.DateUtils;
import org.hibernate.Criteria;
import org.hibernate.criterion.DetachedCriteria;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Projections;
import org.hibernate.criterion.Restrictions;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

import com.cai310.event.UserNewestLogSupport;
import com.cai310.lottery.Constant;
import com.cai310.lottery.common.AgentAnalyseState;
import com.cai310.lottery.common.AgentDetailType;
import com.cai310.lottery.common.AgentLotteryType;
import com.cai310.lottery.common.BaodiState;
import com.cai310.lottery.common.ChaseState;
import com.cai310.lottery.common.DefaultPrizeSendType;
import com.cai310.lottery.common.FundDetailType;
import com.cai310.lottery.common.FundMode;
import com.cai310.lottery.common.Lottery;
import com.cai310.lottery.common.PlatformInfo;
import com.cai310.lottery.common.PrepaymentState;
import com.cai310.lottery.common.PrepaymentType;
import com.cai310.lottery.common.PrizeSendType;
import com.cai310.lottery.common.SchemePrintState;
import com.cai310.lottery.common.SchemeState;
import com.cai310.lottery.common.ScoreDetailType;
import com.cai310.lottery.common.SecretType;
import com.cai310.lottery.common.ShareType;
import com.cai310.lottery.common.SubscriptionLicenseType;
import com.cai310.lottery.common.SubscriptionState;
import com.cai310.lottery.common.SubscriptionWay;
import com.cai310.lottery.common.TicketSchemeState;
import com.cai310.lottery.common.TransactionState;
import com.cai310.lottery.common.TransactionType;
import com.cai310.lottery.common.UserWay;
import com.cai310.lottery.common.WinningUpdateStatus;
import com.cai310.lottery.common.keno.IssueState;
import com.cai310.lottery.dao.lottery.BaodiDao;
import com.cai310.lottery.dao.lottery.PrintInterfaceDao;
import com.cai310.lottery.dao.lottery.SubscriptionDao;
import com.cai310.lottery.dao.lottery.keno.IssueDataDao;
import com.cai310.lottery.dao.lottery.keno.KenoSysConfigDao;
import com.cai310.lottery.dao.lottery.keno.SchemeDao;
import com.cai310.lottery.dao.user.AccountDao;
import com.cai310.lottery.dao.user.FundDetailDao;
import com.cai310.lottery.dao.user.IntegralDao;
import com.cai310.lottery.dao.user.PrepaymentDao;
import com.cai310.lottery.dao.user.TransactionDao;
import com.cai310.lottery.dao.user.UserDao;
import com.cai310.lottery.dao.user.agent.AgentFundDetailDao;
import com.cai310.lottery.dao.user.agent.AgentRebateDao;
import com.cai310.lottery.dao.user.agent.AgentRelationDao;
import com.cai310.lottery.dto.lottery.SubscribeDTO;
import com.cai310.lottery.dto.user.AgentRelationRebate;
import com.cai310.lottery.entity.lottery.Baodi;
import com.cai310.lottery.entity.lottery.ChasePlan;
import com.cai310.lottery.entity.lottery.Period;
import com.cai310.lottery.entity.lottery.PeriodSalesId;
import com.cai310.lottery.entity.lottery.SaleAnalyse;
import com.cai310.lottery.entity.lottery.Scheme;
import com.cai310.lottery.entity.lottery.Subscription;
import com.cai310.lottery.entity.lottery.TradeSuccessScheme;
import com.cai310.lottery.entity.lottery.keno.KenoPeriod;
import com.cai310.lottery.entity.lottery.keno.KenoScheme;
import com.cai310.lottery.entity.lottery.keno.KenoSysConfig;
import com.cai310.lottery.entity.lottery.keno.ahkuai3.AhKuai3Scheme;
import com.cai310.lottery.entity.lottery.keno.el11to5.El11to5Scheme;
import com.cai310.lottery.entity.lottery.keno.gdel11to5.GdEl11to5Scheme;
import com.cai310.lottery.entity.lottery.keno.sdel11to5.SdEl11to5Scheme;
import com.cai310.lottery.entity.lottery.keno.ssc.SscScheme;
import com.cai310.lottery.entity.lottery.keno.xjel11to5.XjEl11to5Scheme;
import com.cai310.lottery.entity.security.AdminUser;
import com.cai310.lottery.entity.ticket.TicketDatail;
import com.cai310.lottery.entity.ticket.TicketPlatformInfo;
import com.cai310.lottery.entity.ticket.TicketThen;
import com.cai310.lottery.entity.user.Account;
import com.cai310.lottery.entity.user.BankInfo;
import com.cai310.lottery.entity.user.FundDetail;
import com.cai310.lottery.entity.user.Integral;
import com.cai310.lottery.entity.user.Prepayment;
import com.cai310.lottery.entity.user.Transaction;
import com.cai310.lottery.entity.user.User;
import com.cai310.lottery.entity.user.UserInfo;
import com.cai310.lottery.entity.user.agent.AgentFundDetail;
import com.cai310.lottery.entity.user.agent.AgentRebate;
import com.cai310.lottery.entity.user.agent.AgentRelation;
import com.cai310.lottery.exception.DataException;
import com.cai310.lottery.prizeutils.template.VariableString;
import com.cai310.lottery.service.QueryService;
import com.cai310.lottery.service.ServiceException;
import com.cai310.lottery.service.lottery.ChasePlanEntityManager;
import com.cai310.lottery.service.lottery.SaleAnalyseEntityManager;
import com.cai310.lottery.service.lottery.SchemeEntityManager;
import com.cai310.lottery.service.lottery.impl.TradeSuccessSchemeEntityManagerImpl;
import com.cai310.lottery.service.lottery.keno.KenoManager;
import com.cai310.lottery.service.lottery.keno.KenoPlayer;
import com.cai310.lottery.service.lottery.keno.KenoService;
import com.cai310.lottery.service.ticket.TicketThenEntityManager;
import com.cai310.lottery.service.user.AgentEntityManager;
import com.cai310.lottery.service.user.UserEntityManager;
import com.cai310.lottery.utils.BigDecimalUtil;
import com.cai310.lottery.web.controller.lottery.SubscribeForm;
import com.cai310.lottery.web.controller.lottery.keno.KenoSchemeDTO;
import com.cai310.orm.Pagination;
import com.cai310.orm.XDetachedCriteria;
import com.cai310.orm.hibernate.CriteriaExecuteCallBack;
import com.cai310.utils.DateUtil;
import com.cai310.utils.NumUtils;
import com.cai310.utils.ReflectionUtils;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

@Transactional
public abstract class KenoServiceImpl<I extends KenoPeriod, S extends KenoScheme> implements KenoService<I, S> {
    @Autowired
    private List<SchemeEntityManager> schemeEntityManagerList;

    private SchemeEntityManager getSchemeEntityManager(Lottery lotteryType) {
        for (SchemeEntityManager manager : schemeEntityManagerList) {
            if (manager.getLottery().equals(lotteryType))
                return manager;
        }
        return null;
    }

    @Autowired
    protected AgentFundDetailDao agentFundDetailDao;
    @Autowired
    protected AgentRebateDao agentRebateDao;
    @Autowired
    protected AgentRelationDao agentRelationDao;
    @Autowired
    protected UserDao userDao;
    @Autowired
    protected AccountDao accountDao;
    @Autowired
    protected TransactionDao transactionDao;
    @Autowired
    protected PrepaymentDao prepaymentDao;
    @Autowired
    protected FundDetailDao fundDetailDao;
    @Autowired
    protected BaodiDao baodiDao;
    @Autowired
    protected IntegralDao integralDao;
    @Resource
    private QueryService queryService;
    protected KenoManager<I, S> kenoManager;
    protected KenoPlayer<I, S> kenoPlayer;
    @Autowired
    protected UserEntityManager userEntityManager;

    public void setKenoPlayer(KenoPlayer<I, S> kenoPlayer) {
        this.kenoPlayer = kenoPlayer;
    }

    @Autowired
    protected TradeSuccessSchemeEntityManagerImpl successSchemeManager;
    protected SubscribeForm subscribeForm;
    //   protected NumberSchemeEntityManager<KenoScheme> getSchemeEntityManager() {
    //      return schemeManager;
    //   }

    SdEl11to5Scheme sdEl11to5Scheme = null;
    SscScheme sscScheme = null;
    GdEl11to5Scheme gdEl11to5Scheme = null;
    El11to5Scheme el11to5Scheme = null;
    AhKuai3Scheme kuaiScheme = null;
    XjEl11to5Scheme xjEl11to5Scheme = null;
    String playName = null;
    String simpleName = null;
    @Autowired
    protected TicketThenEntityManager ticketThenEntityManager;
    long time = System.currentTimeMillis();

    public void xiaolu(String word) {
        System.out.println("----------" + word + "------------" + (System.currentTimeMillis() - time));
        time = System.currentTimeMillis();
    }

    public void updatePeriodPrize(I issueData, KenoPlayer kenoPlayer) throws DataException {
        issueData = this.findIssueDataById(issueData.getId());
        String results = issueData.getResults();
        // ?
        List<S> schemeList = null;
        schemeList = this.getSchemeByIssueNumber(issueData.getPeriodNumber().trim());
        if (schemeList != null && !schemeList.isEmpty()) {
            for (S scheme : schemeList) {
                xiaolu("-?" + scheme.getId());
                if (!scheme.isWon()) {
                    kenoPlayer.calculatePrice(scheme, results);
                    //               ZoomType zoomType = ((KenoScheme)scheme).getZoomType();
                    //               WonType wonType = ((KenoScheme)scheme).getWonType();
                    if (scheme.isWon()) {
                        BigDecimal prizeAfterTax = scheme.getPrizeAfterTax();
                        scheme.setPrize(scheme.getPrize());
                        scheme.setPrizeAfterTax(scheme.getPrizeAfterTax());
                        scheme.setPrizeDetail(
                                scheme.getPrizeDetail() + "?" + scheme.getPrizeAfterTax());
                        scheme.setWinningUpdateStatus(WinningUpdateStatus.WINNING_UPDATED);
                    } else {
                        scheme.setWinningUpdateStatus(WinningUpdateStatus.WINNING_UPDATED);
                    }
                    this.saveScheme(scheme);
                    this.saveTradeSuccessScheme(scheme);
                }
                xiaolu("-?" + scheme.getId());
            }
        }

        logger.info("==========?????==========");
        //  ????
        // ?
        // ?
        List<ChasePlan> chasePlanList = getOutNumStopChasePlan(true, issueData);
        if (chasePlanList != null && !chasePlanList.isEmpty()) {
            for (ChasePlan chasePlan : chasePlanList) {
                // ???  ?
                if (chasePlan.isOutNumStop() && chasePlan.getChasedSize() < 0) {
                    boolean isWon = kenoPlayer.calculatePrice(chasePlan.getMode(), chasePlan.getContent(),
                            chasePlan.getNextMultiple(), chasePlan.getPlayType(), results);
                    if (isWon) {
                        logger.info("==========????ID======?====" + chasePlan.getId());
                        synchronized (Constant.STOPCHASE) {
                            this.stopChasePlan(chasePlan.getId(), "??");
                        }
                    }
                }
            }
        }

        logger.info("==========????==========");
        this.doChasePlan(issueData.getId(), true);

        // 
        this.updateIssueState(issueData.getId(), IssueState.ISSUE_SATE_ACCOUNT_PRIZE);
        logger.info("==========[" + issueData.getPeriodNumber() + "]??==========");
    }

    /**
     * ??
     * @param scheme
     */
    private void saveTradeSuccessScheme(S scheme) {
        Long successSchemeId = null;
        TradeSuccessScheme successScheme = successSchemeManager.getScheme(getLottery(), scheme.getId());
        if (successScheme == null) {
            successScheme = new TradeSuccessScheme();
        } else {
            successSchemeId = successScheme.getId();
        }
        successScheme.setLotteryType(getLottery());
        successScheme.setSchemeId(scheme.getId());
        try {
            BeanUtils.copyProperties(successScheme, scheme);
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        } catch (InvocationTargetException e) {
            e.printStackTrace();
        }
        successScheme.setId(successSchemeId);
        if (scheme.getPrize() != null) {
            successScheme.setSchemePrize(scheme.getPrize());
        }
        if (scheme.getPrizeAfterTax() != null) {
            successScheme.setSchemePrizeAfterTax(scheme.getPrizeAfterTax());
        }
        successSchemeManager.saveScheme(successScheme);
    }

    public void sendPrize(I issueData) {
        // ?
        issueData = this.findIssueDataById(issueData.getId());
        List<S> schemeList = this.getSchemeByIssueNumber(issueData.getPeriodNumber().trim());
        if (schemeList != null && !schemeList.isEmpty()) {
            for (S scheme : schemeList) {
                if (scheme.isWon() && !scheme.isPrizeSended()) {
                    // ?
                    this.sendUserPrice(scheme);
                }
            }
        }
        // 
        this.updateIssueState(issueData.getId(), IssueState.ISSUE_SATE_SEND_PRIZE);
        logger.info("==========[" + issueData.getPeriodNumber() + "]??==========");

    }

    @Override
    public List<ChasePlan> getOutNumStopChasePlan(boolean outNumStop, I issueData) {
        DetachedCriteria criteria = DetachedCriteria.forClass(ChasePlan.class);
        if (outNumStop) {
            criteria.add(Restrictions.eq("outNumStop", outNumStop));
        }
        criteria.add(Restrictions.eq("lotteryType", this.getLottery()));
        // criteria.add(Restrictions.ge("curPeriodId",
        // Long.valueOf(issueData.getPeriodNumber())));
        criteria.add(Restrictions.eq("state", ChaseState.RUNNING));
        List<ChasePlan> list = queryService.findByDetachedCriteria(criteria);
        return list;
    }

    /**
     * ?
     */
    @Override
    public void doChasePlan(long curPeriodId, Boolean iswonStop) {
        DetachedCriteria criteria = DetachedCriteria.forClass(ChasePlan.class);
        criteria.setProjection(Projections.property("id"));
        if (iswonStop != null)
            criteria.add(Restrictions.eq("wonStop", iswonStop));
        criteria.add(Restrictions.eq("lotteryType", getLottery()));
        criteria.add(Restrictions.eq("curPeriodId", curPeriodId));
        criteria.add(Restrictions.eq("state", ChaseState.RUNNING));
        List<Long> list = queryService.findByDetachedCriteria(criteria);
        if (list != null && !list.isEmpty()) {
            for (Long chasePlanId : list) {
                this.handChasePlan(chasePlanId);
            }
        }
    }

    // protected ChasePlanDao<C> chasePlanDao;
    protected IssueDataDao<I> issueDataDao;
    protected SchemeDao<S> schemeDao;
    protected KenoSysConfigDao kenoSysConfigDao;
    @Autowired
    protected ChasePlanEntityManager chasePlanEntityManager;

    @Autowired
    protected AgentEntityManager agentEntityManager;

    @Autowired
    protected UserEntityManager userManager;
    @Autowired
    protected SaleAnalyseEntityManager saleAnalyseEntityManager;
    @Autowired
    protected PrintInterfaceDao printInterfaceDao;

    @Autowired
    protected SubscriptionDao subscriptionDao;

    protected Logger logger = LoggerFactory.getLogger(getClass());

    public abstract Lottery getLottery();

    /**
     * ??
     * 
     * @param scheme
     */
    protected abstract void pushToPrintInterface(S scheme, I issueData);

    protected abstract ChasePlan setChasePlanPlayType(ChasePlan chasePlan, KenoSchemeDTO schemeDTO);

    /**
     * ????
     * 
     * @param id
     *            
     * @return ???
     */
    @Transactional(readOnly = true)
    public KenoSysConfig getSysConfigByKey(String id) {
        return kenoSysConfigDao.get(id);
    }

    /**
     * ???
     * 
     * @param entity
     *            ???
     */
    @Transactional(readOnly = true)
    public void getSysConfigByKey(KenoSysConfig entity) {
        kenoSysConfigDao.save(entity);
    }

    /**
     * ?????
     * 
     * @param issueNumber
     *            ??
     */
    @Transactional(readOnly = true)
    public I findByIssueNumber(String issueNumber) {
        return issueDataDao.findDataByNumber(issueNumber);
    }

    /**
     * ???
     * 
     * @param dateNow
     *            ?
     * @param beforeTime
     *            ?????
     * @return ??
     */
    @Transactional(readOnly = true)
    public I getCurrIssueData(Date dateNow, int beforeTime) {
        return issueDataDao.findCurrentData(dateNow, beforeTime);
    }

    /**
     * ????
     * 
     * @return ???
     */
    @Transactional(readOnly = true)
    public I getLastIssue() {
        return issueDataDao.findLastIssue();
    }

    /**
     * ????
     * 
     * @return ???
     */
    @Transactional(readOnly = true)
    public I getLastResultIssueData() {
        return issueDataDao.findLastResultIssueData();
    }

    /**
     * ???
     * 
     * @param dateNow
     *            ?
     * @return ??
     */
    @Transactional(readOnly = true)
    public List<I> getCanSendPrize(Date dateNow) {
        return issueDataDao.findIssueDataList(dateNow, IssueState.ISSUE_SATE_ACCOUNT_PRIZE);
    }

    /**
     * ?
     * 
     * @param dateNow
     *            ?
     * @param beforeTime
     *            ?????
     */
    public void openCurrentIssue(Date dateNow, int beforeTime) {
        I issueData = issueDataDao.findCurrentData(dateNow, beforeTime);
        if (issueData != null) {
            if (issueData.getState().getStateValue() < IssueState.ISSUE_SATE_CURRENT.getStateValue()) {
                issueData.setState(IssueState.ISSUE_SATE_CURRENT);
                issueDataDao.save(issueData);
            }
        }
    }

    @Transactional(readOnly = true)
    public I findCurrentData(Date dateNow, int beforeTime) {
        return issueDataDao.findCurrentData(dateNow, beforeTime);
    }

    /**
     * ???
     * 
     * @param issueData
     *            ????
     */
    @Transactional
    public I saveIssueData(I issueData) {
        I newIssueData = issueDataDao.save(issueData);
        return newIssueData;
    }

    /**
     * ???
     * 
     * @param dateNow
     *            ?
     * @return ??
     */
    @Transactional(readOnly = true)
    public List<I> getCanOpenPrize(Date dateNow) {
        return issueDataDao.findIssueDataList(dateNow, IssueState.ISSUE_SATE_RESULT);
    }

    @Transactional(readOnly = true)
    public List<I> getCanOpenResults() {
        DetachedCriteria criteria = DetachedCriteria.forClass(issueDataDao.getEntityClass());
        criteria.add(Restrictions.lt("endedTime", new Date()));
        criteria.add(Restrictions.gt("endedTime", DateUtils.addDays(new Date(), -1)));
        criteria.add(Restrictions.eq("state", IssueState.ISSUE_SATE_END));
        criteria.addOrder(Order.desc("id"));
        return issueDataDao.findByDetachedCriteria(criteria, true);
    }

    public void endAll(I issueData) {
        // ?
        issueData = this.findIssueDataById(issueData.getId());
        this.updateIssueState(issueData.getId(), IssueState.ISSUE_SATE_FINISH);
    }

    /**
     * ??
     * 
     * @param issueId
     *            ?ID
     * @param state
     *            ??
     */
    public void updateIssueState(Long issueId, IssueState state) {
        issueDataDao.updateIssueState(issueId, state);
    }

    public List<I> findKenoPeriodByCriteria(DetachedCriteria criteria, Integer num) {
        if (null == num) {
            return issueDataDao.findByDetachedCriteria(criteria);
        }
        return issueDataDao.findByDetachedCriteria(criteria, 0, num);
    }

    /**
     * ???
     * 
     * @param issueNumber
     *            ?
     * @return 
     */
    @Transactional(readOnly = true)
    public List<S> getSchemeByIssueNumber(String issueNumber) {
        return schemeDao.findByIssueNumber(issueNumber);
    }

    /**
     * ?
     * 
     * @param periodId
     *            ?
     * @return 
     */
    @Transactional(readOnly = true)
    public List<S> getUnAgentAnalyseScheme(Long periodId) {
        return schemeDao.findUnAnalyes(periodId);
    }

    /**
     * ?
     * 
     * @param periodId
     *            ?
     * @return 
     */
    @Transactional(readOnly = true)
    public List<S> findUnSendPrize(Long periodId) {
        return schemeDao.findUnSendPrize(periodId);
    }

    /**
     * ???
     * 
     * @param issueNumber
     *            ?
     * @return 
     */
    @Transactional(readOnly = true)
    public List<S> getPrintFailSchemeByIssueNumber(String issueNumber) {
        return schemeDao.findPrintFailByIssueNumber(issueNumber);
    }

    /**
     * ?
     * 
     * @param scheme
     *            ?
     * @return 
     */
    public void saveScheme(S scheme) {
        schemeDao.save(scheme);
    }

    public S saveSchemeReturn(S scheme) {
        return schemeDao.save(scheme);
    }

    /**
     * ???
     * 
     * @param scheme
     *            
     */
    public void sendUserPrice(S scheme) {

        //      KenoScheme kenoScheme = null;

        Float bouns = 0.0f;
        Float scoreRate = 0.0f;
        scheme.setPrizeSended(true);
        // zhuhui motify by 2011-05-03  ?
        scheme.setPrizeSendTime(new Date());
        // xxxc motify by 2012-09-03  ??
        scheme.setWinningUpdateStatus(WinningUpdateStatus.PRICE_UPDATED);
        this.schemeDao.save(scheme);

        if (Lottery.isKeno(scheme.getLotteryType())) {//?
            System.out.println(scheme.getLotteryType() + "?");//?
            if (Lottery.SSC.equals(scheme.getLotteryType())) {
                sscScheme = (SscScheme) scheme;
                playName = sscScheme.getBetType().getTypeName();
                simpleName = Lottery.SSC.getSchemeNumberPrefix();
            } else if (Lottery.SDEL11TO5.equals(scheme.getLotteryType())) {
                sdEl11to5Scheme = (SdEl11to5Scheme) scheme;
                playName = sdEl11to5Scheme.getBetType().getTypeName();
                simpleName = Lottery.SDEL11TO5.getSchemeNumberPrefix();
            } else if (Lottery.GDEL11TO5.equals(scheme.getLotteryType())) {
                gdEl11to5Scheme = (GdEl11to5Scheme) scheme;
                playName = gdEl11to5Scheme.getBetType().getTypeName();
                simpleName = Lottery.GDEL11TO5.getSchemeNumberPrefix();
            } else if (Lottery.EL11TO5.equals(scheme.getLotteryType())) {
                el11to5Scheme = (El11to5Scheme) scheme;
                playName = el11to5Scheme.getBetType().getTypeName();
                simpleName = Lottery.EL11TO5.getSchemeNumberPrefix();
            } else if (Lottery.AHKUAI3.equals(scheme.getLotteryType())) {
                kuaiScheme = (AhKuai3Scheme) scheme;
                playName = kuaiScheme.getBetType().getTypeName();
                simpleName = Lottery.AHKUAI3.getSchemeNumberPrefix();
            } else if (Lottery.XJEL11TO5.equals(scheme.getLotteryType())) {
                xjEl11to5Scheme = (XjEl11to5Scheme) scheme;
                playName = xjEl11to5Scheme.getBetType().getTypeName();
                simpleName = Lottery.XJEL11TO5.getSchemeNumberPrefix();
            }
            DetachedCriteria criteria = DetachedCriteria.forClass(Integral.class, "m");
            criteria.add(Restrictions.eq("m.parent", simpleName));
            criteria.add(Restrictions.eq("m.playName", playName));
            List<Integral> list = integralDao.findByDetachedCriteria(criteria);
            bouns = list.get(0).getBouns();
            scoreRate = list.get(0).getScoreRate();
        }

        PrizeSendType prizeSendType = new DefaultPrizeSendType(scheme);// ?
        StringBuffer memosb = new StringBuffer("");
        if (scheme.getShareType() == ShareType.TOGETHER) {
            memosb.append("??");
        } else if (scheme.isChasePlanScheme()) {
            memosb.append("?");
        } else {
            memosb.append("");
        }
        memosb.append("[" + scheme.getSchemeNumber() + "]");
        memosb.append("?{prize}");
        VariableString varReturnMemo = new VariableString(memosb.toString(), null);// ????

        // 
        List<Subscription> subscriptionList = findSubscriptionsOfScheme(scheme.getId(), SubscriptionState.NORMAL);// /?
        BigDecimal actualTotalReturn = BigDecimal.ZERO;// ??
        BigDecimal joinPrize;
        //?
        BigDecimal schemeCost = BigDecimalUtil.valueOf(scheme.getSchemeCost());
        for (Subscription subscription : subscriptionList) {
            joinPrize = BigDecimalUtil.divide(
                    BigDecimalUtil.multiply(subscription.getCost(), prizeSendType.getTotalReturn()), schemeCost);
            subscription.setBonusSended(true);
            subscription.setBonus(joinPrize);// ?
            subscription = subscriptionDao.save(subscription);// 

            varReturnMemo.setVar("prize", Constant.numFmt.format(joinPrize));// ???

            // ?
            userManager.oprUserMoney(userManager.getUser(subscription.getUserId()), joinPrize,
                    varReturnMemo.toString(), FundMode.IN, FundDetailType.SCHEME_BONUS, null);
            ///
            agentEntityManager.recordAgent(userManager.getUser(subscription.getUserId()), scheme.getLotteryType(),
                    AgentDetailType.PRIZE, new Date(), joinPrize, null);
            if (joinPrize.compareTo(BigDecimal.valueOf(bouns)) > 0) {

            } else {
                //
                //            String remark = varReturnMemo.toString()+"?"+scoreRate+"?"+joinPrize.multiply(BigDecimal.valueOf(scoreRate))+"";
                String remark = varReturnMemo.toString().concat("?").concat(scoreRate.toString())
                        .concat("?").concat(joinPrize.multiply(BigDecimal.valueOf(scoreRate)).toString())
                        .concat("");
                userManager.oprUserScore(userManager.getUser(subscription.getUserId()),
                        joinPrize.multiply(BigDecimal.valueOf(scoreRate)), remark, FundMode.IN,
                        ScoreDetailType.WINNING, null);
            }
            actualTotalReturn = actualTotalReturn.add(joinPrize);// ??
        }
        // ?
        BigDecimal remainPrize = prizeSendType.getTotalReturn().subtract(actualTotalReturn);
        // ???1?
        if (remainPrize.doubleValue() < 0) {
            throw new ServiceException("???");
        } else if (remainPrize.doubleValue() > 0) {
            // ???
            if (remainPrize.compareTo(BigDecimal.valueOf(0.01)) > 0) {
                varReturnMemo.setVar("prize", Constant.numFmt.format(remainPrize));
                userManager.oprUserMoney(userManager.getUser(scheme.getSponsorId()), remainPrize,
                        varReturnMemo.toString(), FundMode.IN, FundDetailType.SCHEME_BONUS, null);
                ///
                agentEntityManager.recordAgent(userManager.getUser(scheme.getSponsorId()), scheme.getLotteryType(),
                        AgentDetailType.PRIZE, new Date(), remainPrize, null);
                if (remainPrize.compareTo(BigDecimal.valueOf(bouns)) > 0) {

                } else {
                    //
                    String s = null;
                    String remark = varReturnMemo.toString().concat("?").concat(scoreRate.toString())
                            .concat("?")
                            .concat(remainPrize.multiply(BigDecimal.valueOf(scoreRate)).toString()).concat("");
                    userManager.oprUserScore(userManager.getUser(scheme.getSponsorId()),
                            remainPrize.multiply(BigDecimal.valueOf(scoreRate)), remark, FundMode.IN,
                            ScoreDetailType.WINNING, null);
                }
            }
        }

        if (prizeSendType.getOrganigerDeduct().doubleValue() > 0) {// ????
            userManager.oprUserMoney(userManager.getUser(scheme.getSponsorId()), prizeSendType.getOrganigerDeduct(),
                    "?[" + scheme.getSchemeNumber() + "]???", FundMode.IN,
                    FundDetailType.SCHEME_COMMISSION, null);
        }
        UserNewestLogSupport.won(scheme);
        ticketThenEntityManager.synchronizationWonTicket(scheme);
        //      User user = userManager.getUser(scheme.getSponsorId());
        //      //?
        //      scheme.getSchemeCost();
        //      // cyy-c 2011-04-13 
        //      // 
        //      List<Subscription> subscriptionList = findSubscriptionsOfScheme(scheme.getId(), SubscriptionState.NORMAL);// /?
        //      if (null != subscriptionList && !subscriptionList.isEmpty()) {// ?
        //         Subscription subscription = subscriptionList.get(0);// ?
        //         subscription.setBonus(scheme.getPrizeAfterTax());// ?
        //         subscriptionDao.save(subscription);// ?
        //         userManager.oprUserMoney(user, scheme.getPrizeAfterTax(), this.getLottery()
        //               .getLotteryName() + ":?:[" + scheme.getSchemeNumber() + "]", FundMode.IN,
        //               FundDetailType.SCHEME_BONUS, null);// ?
        //         ///
        //         agentEntityManager.recordAgent(user, this.getLottery(), AgentDetailType.PRIZE,new Date(), scheme.getPrizeAfterTax(),null);
        //      }
    }

    /**
     * ???
     * 
     * @param dateNow
     *            ?
     * @return ??
     */
    @Transactional(readOnly = true)
    public List<I> getCanCloseIssue(Date dateNow) {
        return issueDataDao.findIssueDataList(dateNow, IssueState.ISSUE_SATE_SEND_PRIZE);
    }

    /**
     * 
     * 
     * @param scheme
     *            ?
     * @param user
     *            
     * @throws DataException
     *             ?
     */
    public void userBet(S scheme, User user) throws DataException {
        BigDecimal cost = new BigDecimal(scheme.getSchemeCost());
        userManager.oprUserMoney(user, cost, scheme.getLotteryType().getLotteryName() + "", FundMode.OUT,
                FundDetailType.SUBSCRIPTION, null);
        // ?
        schemeDao.save(scheme);
    }

    /**
     * ??
     * 
     * @param chasePlan
     *            ?
     */
    public void userChasePlan(ChasePlan chasePlan) {
        chasePlanEntityManager.saveChasePlan(chasePlan);
    }

    /**
     * ?
     * 
     * @param criteria
     * @param pagination
     * @return
     */
    @Transactional(readOnly = true)
    public Pagination findByCriteriaAndPagination(XDetachedCriteria criteria, Pagination pagination) {
        return issueDataDao.findByCriteriaAndPagination(criteria, pagination);
    }

    @Transactional(readOnly = true)
    public I findIssueDataById(Long id) {
        return issueDataDao.get(id);
    }

    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public I loadPeriod(String periodNumber) {
        List<I> list = issueDataDao.find(Restrictions.eq("periodNumber", periodNumber));
        if (list != null && !list.isEmpty()) {
            return list.get(0);
        }
        return null;
    }

    @Transactional(readOnly = true)
    public Class<I> getIssueDataClass() {
        return issueDataDao.getEntityClass();
    }

    @Transactional(readOnly = true)
    public Class<S> getSchemeClass() {
        return schemeDao.getEntityClass();
    }

    /**
     * ?ID??
     */
    @Transactional(readOnly = true)
    public ChasePlan findChasePlanById(Long id) {
        return chasePlanEntityManager.getChasePlan(id);
    }

    /**
     * new
     * 
     * @return 
     */
    @Transactional(readOnly = true)
    public S newInstance(KenoSchemeDTO schemeDTO) {
        S scheme;
        try {
            scheme = schemeDao.getEntityClass().newInstance();
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
        scheme.setContent(schemeDTO.getContent());
        scheme.setSchemeCost(schemeDTO.getSchemeCost());
        scheme.setUnits(schemeDTO.getUnits());
        scheme.setMultiple(schemeDTO.getMultiple());
        scheme.setPeriodId(schemeDTO.getPeriodId());
        scheme.setCreateTime(new Date());
        scheme.setPrizeSended(false);
        scheme.setWon(false);
        scheme.setSponsorId(schemeDTO.getSponsorId());
        scheme.setMode(schemeDTO.getMode());
        scheme.setSchemePrintState(SchemePrintState.UNPRINT);
        scheme.setShareType(schemeDTO.getShareType());
        scheme.setSecretType(schemeDTO.getSecretType());
        scheme.setSubscriptionLicenseType(schemeDTO.getSubscriptionLicenseType());
        scheme.setSubscriptionPassword(schemeDTO.getSubscriptionPassword());
        scheme.setMinSubscriptionCost(schemeDTO.getMinSubscriptionCost());
        scheme.setDescription(schemeDTO.getDescription());
        scheme.setCommissionRate(schemeDTO.getCommissionRate());
        if (StringUtils.isNotBlank(schemeDTO.getContent())) {
            try {
                scheme.uploadContent(schemeDTO.getContent());
            } catch (DataException e) {
                throw new ServiceException(e.getMessage());
            }
        }

        scheme.setState(SchemeState.UNFULL);
        scheme.setWinningUpdateStatus(WinningUpdateStatus.NONE);

        return scheme;
    }

    @Transactional(readOnly = true)
    public S newInstance(ChasePlan chasePlan) {
        S scheme;
        try {
            scheme = schemeDao.getEntityClass().newInstance();
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }
        scheme.setContent(chasePlan.getContent());
        scheme.setUnits(chasePlan.getUnits());

        scheme.setSponsorId(chasePlan.getUserId());
        scheme.setSponsorName(chasePlan.getUserName());

        scheme.setChaseId(chasePlan.getId());

        scheme.setMode(chasePlan.getMode());
        scheme.setSchemePrintState(SchemePrintState.UNPRINT);
        scheme.setShareType(ShareType.SELF);
        scheme.setSecretType(SecretType.FULL_SECRET);
        scheme.setSubscriptionLicenseType(SubscriptionLicenseType.PUBLIC_LICENSE);
        if (StringUtils.isNotBlank(chasePlan.getContent())) {
            try {
                scheme.uploadContent(chasePlan.getContent());
            } catch (DataException e) {
                throw new ServiceException(e.getMessage());
            }
        }
        scheme.setId(null);
        return scheme;
    }

    /**
     * ?????
     * 
     * @param user
     *            
     * @param schemeDTO
     *            ??
     */
    protected void checkTicketPlatformInfo(TicketPlatformInfo ticketPlatformInfo, KenoSchemeDTO schemeDTO) {
        if (ticketPlatformInfo.isLocked())
            throw new ServiceException(
                    "???,??.,??.");

        BigDecimal totalCost = BigDecimal.ZERO;
        if (schemeDTO.getSponsorSubscriptionCost() != null
                && schemeDTO.getSponsorSubscriptionCost().doubleValue() > 0)
            totalCost = totalCost.add(schemeDTO.getSponsorSubscriptionCost());
        if (schemeDTO.getSponsorBaodiCost() != null && schemeDTO.getSponsorBaodiCost().doubleValue() > 0)
            totalCost = totalCost.add(schemeDTO.getSponsorBaodiCost());
        if (ticketPlatformInfo == null) {
            throw new ServiceException("?[#" + ticketPlatformInfo.getId() + "]?.");
        }
        BigDecimal allAccountBalance = ticketPlatformInfo.getRemainMoney();

        BigDecimal remainMoney = (allAccountBalance != null) ? allAccountBalance : BigDecimal.ZERO;
        if (remainMoney.doubleValue() < totalCost.doubleValue())
            throw new ServiceException("?,??["
                    + Constant.MONEY_FORMAT.format(totalCost) + "],??["
                    + Constant.MONEY_FORMAT.format(remainMoney) + "],?.");
    }

    public S createTicketScheme(KenoSchemeDTO schemeDTO) {
        I issueData = issueDataDao.get(schemeDTO.getPeriodId());
        checkConformPeriodInitConfig(issueData, schemeDTO);

        TicketPlatformInfo ticketPlatformInfo = ticketThenEntityManager
                .getTicketPlatformInfo(schemeDTO.getSponsorId());
        checkTicketPlatformInfo(ticketPlatformInfo, schemeDTO);// ,???

        S scheme = null;
        try {
            scheme = schemeDao.getEntityClass().newInstance();
        } catch (InstantiationException e) {
            throw new ServiceException(e.getMessage());
        } catch (IllegalAccessException e) {
            throw new ServiceException(e.getMessage());
        }
        scheme = newInstance(schemeDTO);

        scheme.setPeriodNumber(issueData.getPeriodNumber());
        StringBuilder sb = new StringBuilder(50);
        sb.append("?").append(scheme.getLotteryType().getLotteryName()).append("")
                .append(scheme.getPeriodNumber()).append("").append(scheme.getMode().getModeName())
                .append(scheme.getShareType().getShareName()).append(".");
        Transaction tran = userManager.createTransaction(TransactionType.SCHEME, sb.toString());
        scheme.setTransactionId(tran.getId());
        scheme.setSchemePrintState(SchemePrintState.UNPRINT);
        // /
        scheme.setOrderId(schemeDTO.getOrderId());
        if (null != schemeDTO.getOfficialEndTime()) {
            scheme.setCommitTime(schemeDTO.getOfficialEndTime());
        }
        // ??
        if (null != schemeDTO.getPlatform()) {
            scheme.setPlatform(schemeDTO.getPlatform());
        }
        scheme = doTicket(scheme, schemeDTO, ticketPlatformInfo, issueData);

        return scheme;
    }

    /**
     * ??
     * 
     * @param scheme
     *            
     * @param schemeDTO
     *            ??
     * @param user
     *            ?
     */
    protected S doTicket(S scheme, KenoSchemeDTO schemeDTO, TicketPlatformInfo ticketPlatformInfo, I issueData) {
        BigDecimal subscriptionCost = BigDecimalUtil.valueOf(scheme.getSchemeCost());
        try {
            scheme.subscription(subscriptionCost);
        } catch (DataException e) {
            throw new ServiceException(e.getMessage(), e);
        }

        //Prepayment prepayment = userManager.createPrepayment(scheme
        //      .getTransactionId(), user.getId(), cost,
        //      PrepaymentType.SUBSCRIPTION, FundDetailType.SUBSCRIPTION, sb
        //            .toString(), PlatformInfo.TICKET);

        // ?
        ticketPlatformInfo = ticketThenEntityManager.getTicketPlatformInfo(ticketPlatformInfo.getId());
        User user = null;
        if (null == ticketPlatformInfo.getUserId()) {
            user = new User();
            user.setUserName(ticketPlatformInfo.getPlatformName());
            user.setPassword(ticketPlatformInfo.getPassword());
            user.setUserWay(UserWay.WEB);
            user.setMid(0L);
            user.setLocked(User.NO_LOCK_STATUS);
            user.setRemainMoney(BigDecimal.ZERO);
            user.setAgentId(Constant.SITE_BAODI_USER_ID);
            UserInfo info = new UserInfo();
            BankInfo bank = new BankInfo();
            user.setBank(bank);
            user.setInfo(info);
            user = userManager.saveUser(user, info, bank);
            ticketPlatformInfo.setUserId(user.getId());
        } else {
            user = userManager.getUser(ticketPlatformInfo.getUserId());
        }

        scheme.setSponsorId(user.getUserId());
        scheme.setSponsorName(user.getUserName());
        scheme = schemeDao.save(scheme);

        StringBuilder sb = new StringBuilder(50);
        sb.append("?[").append(scheme.getSchemeNumber()).append("].");
        try {
            ticketPlatformInfo.subtractMoney(subscriptionCost);
            ///
        } catch (DataException e) {
            throw new ServiceException(e.getMessage());
        }
        ticketPlatformInfo = ticketThenEntityManager.saveTicketPlatformInfo(ticketPlatformInfo);

        Transaction tran = transactionDao.get(scheme.getTransactionId());
        if (tran.getState() != TransactionState.UNDER_WAY)
            throw new ServiceException("??[" + tran.getState().getStateName()
                    + "],??.");

        // 
        FundDetail fundDetail = new FundDetail();
        fundDetail.setMode(FundMode.OUT);
        fundDetail.setType(FundDetailType.SUBSCRIPTION);
        fundDetail.setMoney(subscriptionCost);
        fundDetail.setRemark(sb.toString());
        fundDetail.setResultMoney(ticketPlatformInfo.getRemainMoney());
        fundDetail.setUserId(user.getId());
        fundDetail.setUserName(user.getUserName());
        fundDetail.setPlatform(PlatformInfo.TICKET);
        fundDetail = fundDetailDao.save(fundDetail);

        // 
        Prepayment prepayment = new Prepayment();
        prepayment.setType(PrepaymentType.SUBSCRIPTION);
        prepayment.setState(PrepaymentState.AWAIT);
        prepayment.setTransactionId(tran.getId());
        prepayment.setUserId(user.getId());
        prepayment.setAmount(subscriptionCost);
        prepayment.setCreateFundDetailId(fundDetail.getId());
        prepayment.setLotteryType(scheme.getLotteryType());
        prepayment = prepaymentDao.save(prepayment);
        //
        Subscription subscription = new Subscription();
        subscription.setUserId(user.getId());
        subscription.setUserName(user.getUserName());
        subscription.setCost(subscriptionCost);
        subscription.setState(SubscriptionState.NORMAL);
        subscription.setWay(SubscriptionWay.INITIATE);
        subscription.setLotteryType(scheme.getLotteryType());
        subscription.setSchemeId(scheme.getId());
        subscription.setPrepaymentId(prepayment.getId());
        subscription.setPlatform(PlatformInfo.TICKET);
        subscription = subscriptionDao.save(subscription);
        ///
        TicketThen ticketThen = new TicketThen();
        ticketThen.setOutOrderNumber(schemeDTO.getOutOrderNumber());
        ticketThen.setOrderId(scheme.getOrderId());
        ticketThen.setOfficialEndTime(issueData.getEndedTime());
        ticketThen.setPlatformInfoId(ticketPlatformInfo.getId());
        ticketThen.setUserId(user.getId());
        ticketThen.setLotteryType(this.getLottery());
        ticketThen.setPeriodNumber(scheme.getPeriodNumber());
        ticketThen.setSchemeNumber(scheme.getSchemeNumber());
        ticketThen.setUnits(scheme.getUnits());
        ticketThen.setMultiple(scheme.getMultiple());
        ticketThen.setSchemeCost(scheme.getSchemeCost());
        ticketThen.setState(TicketSchemeState.FULL);
        ticketThen.setMode(scheme.getMode());
        ticketThen.setPlayType(getPlayType(schemeDTO));
        ticketThen = this.ticketThenEntityManager.saveTicketThen(ticketThen);
        TicketDatail ticketDatail = new TicketDatail();
        ticketDatail.setTicketId(ticketThen.getId());
        ticketDatail.setContent(scheme.getContentText());
        this.ticketThenEntityManager.saveTicketDatail(ticketDatail);
        ///
        ticketOther(scheme);
        return scheme;
    }

    /// ?
    public Byte getPlayType(KenoSchemeDTO schemeDTO) {
        return Byte.valueOf("0");
    }

    //? 
    public void ticketOther(Scheme scheme) {

    }

    /**
     * ?
     * 
     * @param dto
     * @return
     */
    public S createScheme(KenoSchemeDTO schemeDTO) {
        I issueData = issueDataDao.get(schemeDTO.getPeriodId());
        checkConformPeriodInitConfig(issueData, schemeDTO);

        User user = userManager.getUser(schemeDTO.getSponsorId());
        checkUser(user, schemeDTO);

        S scheme = null;
        try {
            scheme = schemeDao.getEntityClass().newInstance();
        } catch (InstantiationException e) {
            throw new ServiceException(e.getMessage());
        } catch (IllegalAccessException e) {
            throw new ServiceException(e.getMessage());
        }
        scheme = newInstance(schemeDTO);
        //??
        if (null != schemeDTO.getPlatform()) {
            scheme.setPlatform(schemeDTO.getPlatform());
        }
        Date commitTime = DateUtils.addMinutes(issueData.getEndedTime(), -kenoPlayer.getBeforeTime());
        commitTime = DateUtils.addSeconds(commitTime, -kenoPlayer.getBeforeSecondsTime());
        scheme.setCommitTime(commitTime);

        scheme.setPeriodNumber(issueData.getPeriodNumber());
        scheme.setSponsorName(user.getUserName());
        StringBuilder sb = new StringBuilder(50);
        sb.append("?").append(scheme.getLotteryType().getLotteryName()).append("")
                .append(scheme.getPeriodNumber()).append("").append(scheme.getMode().getModeName())
                .append(scheme.getShareType().getShareName()).append(".");
        Transaction tran = userManager.createTransaction(TransactionType.SCHEME, sb.toString());
        scheme.setTransactionId(tran.getId());
        // ??
        if (schemeDTO.isChase()) {
            Lottery lotteryType = scheme.getLotteryType();
            ChasePlan chasePlan = new ChasePlan();
            chasePlan.setPlatform(schemeDTO.getPlatform());
            chasePlan.setLotteryType(lotteryType);
            chasePlan.setState(ChaseState.RUNNING);
            chasePlan.setUserId(user.getId());
            chasePlan.setUserName(user.getUserName());
            chasePlan.setTotalCost(schemeDTO.getTotalCostOfChase());
            chasePlan.setRandom(schemeDTO.isRandomOfChase());
            chasePlan.setRandomUnits(schemeDTO.getRandomUnitsOfChase());
            chasePlan.setHasDan(false);
            chasePlan.setWonStop(schemeDTO.isWonStopOfChase());
            chasePlan.setPrizeForWonStop(schemeDTO.getPrizeForWonStopOfChase());
            chasePlan.setOutNumStop(schemeDTO.isOutNumStop());
            chasePlan = this.setChasePlanPlayType(chasePlan, schemeDTO);
            chasePlan.setUnits(schemeDTO.getUnits());
            chasePlan.setMode(schemeDTO.getMode());
            chasePlan.setContent(schemeDTO.getContent());
            chasePlan.setSchemeCost(schemeDTO.getSchemeCost());
            chasePlan.setCapacityProfit(schemeDTO.getCapacityProfit());

            try {
                chasePlan.setMultiples(schemeDTO.getMultiplesOfChase());
            } catch (DataException e) {
                throw new ServiceException(e.getMessage());
            }
            StringBuilder sb1 = new StringBuilder(20);
            sb1.append("??").append(lotteryType.getLotteryName()).append("?.")
                    .append("?")
                    .append(BigDecimalUtil.valueOf(chasePlan.getTotalCost()) + "   ").append(" ");

            tran = userManager.createTransaction(TransactionType.CHASE, sb1.toString());
            chasePlan.setTransactionId(tran.getId());
            Prepayment chasePrepayment = userManager.createKenoPrepayment(chasePlan.getTransactionId(),
                    user.getId(), BigDecimal.valueOf(chasePlan.getTotalCost()), PrepaymentType.CHASE,
                    FundDetailType.CHASE, sb1.toString(), schemeDTO.getPlatform(), this.getLottery());

            // ? chasePrepayment.getId()
            chasePlan.setPrepaymentId(chasePrepayment.getId());

            if (schemeDTO.getStartChasePeriodId() != null) {
                if (scheme.getPeriodId().longValue() != schemeDTO.getStartChasePeriodId().longValue()) {
                    I period = this.findIssueDataById(schemeDTO.getStartChasePeriodId());
                    chasePlan.setCurPeriodId(period.getPrevPreriodId());
                    chasePlan.setChasedCost(0);
                    chasePlan = chasePlanEntityManager.saveChasePlan(chasePlan);
                    scheme = null;
                    return null;
                }
            }

            try {
                Integer nextMultiple = chasePlan.getNextMultiple();// ?
                if (nextMultiple == null || nextMultiple.intValue() <= 0) {
                    throw new DataException("???");
                }
                scheme.setMultiple(nextMultiple);
                scheme.setSchemeCost(scheme.getUnits() * nextMultiple * 2);

            } catch (DataException e) {
                throw new ServiceException(e.getMessage(), e);
            }

            try {
                chasePlan.chase(scheme.getPeriodId(), scheme.getSchemeCost());
            } catch (DataException e) {
                throw new ServiceException(e.getMessage());
            }

            chasePlan = chasePlanEntityManager.saveChasePlan(chasePlan);

            // 

            scheme.setChaseId(chasePlan.getId());
            BigDecimal subscriptionCost = BigDecimal.valueOf(scheme.getSchemeCost());
            try {
                scheme.subscription(subscriptionCost);
            } catch (DataException e) {
                throw new ServiceException(e.getMessage(), e);
            }
            scheme = schemeDao.save(scheme);
            sb.setLength(0);
            sb.append("?").append(scheme.getLotteryType().getLotteryName()).append("")
                    .append(scheme.getPeriodNumber()).append("").append("[")
                    .append(scheme.getSchemeNumber())
                    .append("],??(?). ");
            Prepayment subscriptionPrepayment = userManager.transferKenoPrepayment(scheme.getTransactionId(),
                    chasePlan.getPrepaymentId(), subscriptionCost, PrepaymentType.SUBSCRIPTION,
                    FundDetailType.SUBSCRIPTION, sb.toString(), schemeDTO.getPlatform(), this.getLottery());
            saveSubscription(scheme, subscriptionPrepayment.getId(), user, subscriptionCost,
                    SubscriptionWay.INITIATE, schemeDTO.getPlatform());
            return scheme;
        } else {

            switch (scheme.getShareType()) {
            case TOGETHER:
                scheme = doTogether(scheme, schemeDTO, user);
                break;
            case SELF:
                scheme = doSelf(scheme, schemeDTO, user);
                break;
            default:
                throw new ServiceException(": " + scheme.getShareType());
            }

            //         BigDecimal subscriptionCost = BigDecimal.valueOf(scheme.getSchemeCost());
            //         try {
            //            scheme.subscription(subscriptionCost);
            //         } catch (DataException e) {
            //            throw new ServiceException(e.getMessage(), e);
            //         }
            //         scheme = schemeDao.save(scheme);

            //         createSubscription(scheme, user, subscriptionCost, SubscriptionWay.INITIATE,schemeDTO.getPlatform(),schemeDTO.getZoomType());

            return scheme;
        }
    }

    private S doSelf(S scheme, KenoSchemeDTO schemeDTO, User user) {
        BigDecimal subscriptionCost = BigDecimalUtil.valueOf(scheme.getSchemeCost());
        try {
            scheme.subscription(subscriptionCost);
        } catch (DataException e) {
            throw new ServiceException(e.getMessage(), e);
        }
        scheme = schemeDao.save(scheme);

        createSubscription(scheme, user, subscriptionCost, SubscriptionWay.INITIATE, schemeDTO.getPlatform());

        return scheme;
    }

    /**
     * ???
     * 
     * @param scheme
     *            
     * @param schemeDTO
     *            ??
     * @param user
     *            ?
     * @return 
     */
    protected S doTogether(S scheme, KenoSchemeDTO schemeDTO, User user) {
        if (schemeDTO.getSponsorSubscriptionCost() == null
                || schemeDTO.getSponsorSubscriptionCost().doubleValue() <= 0)
            throw new ServiceException("????0.");

        double sponsorMinSubscriptionCost = scheme.getSchemeCost() * Constant.SPONSOR_MIN_SUBSCRIPTION_PERCENT;
        if (schemeDTO.getSponsorSubscriptionCost().doubleValue() < sponsorMinSubscriptionCost)
            throw new ServiceException(
                    "??" + (Constant.SPONSOR_MIN_SUBSCRIPTION_PERCENT * 100)
                            + "%,?" + Constant.MONEY_FORMAT.format(sponsorMinSubscriptionCost) + ".");

        boolean hasBaodi = schemeDTO.getSponsorBaodiCost() != null
                && schemeDTO.getSponsorBaodiCost().doubleValue() > 0;
        try {
            scheme.subscription(schemeDTO.getSponsorSubscriptionCost());
            if (hasBaodi) {
                scheme.baodi(schemeDTO.getSponsorBaodiCost());
            }
        } catch (DataException e) {
            throw new ServiceException(e.getMessage(), e);
        }
        scheme = schemeDao.save(scheme);// ?
        createSubscription(scheme, user, schemeDTO.getSponsorSubscriptionCost(), SubscriptionWay.INITIATE,
                schemeDTO.getPlatform());
        if (hasBaodi) {
            createBaodi(scheme, user, schemeDTO.getSponsorBaodiCost(), schemeDTO.getPlatform());
        }
        return scheme;
    }

    private Baodi createBaodi(S scheme, User user, BigDecimal sponsorBaodiCost, PlatformInfo platform) {
        StringBuilder sb = new StringBuilder(50);
        sb.append("?[").append(scheme.getSchemeNumber()).append("].");
        Prepayment prepayment = userManager.createPrepayment(scheme.getTransactionId(), user.getId(),
                sponsorBaodiCost, PrepaymentType.BAODI, FundDetailType.BAODI, sb.toString(), platform,
                scheme.getLotteryType());

        Baodi baodi = new Baodi();
        baodi.setSchemeId(scheme.getId());
        baodi.setPrepaymentId(prepayment.getId());
        baodi.setUserId(user.getId());
        baodi.setUserName(user.getUserName());
        baodi.setCost(sponsorBaodiCost);
        baodi.setState(BaodiState.NORMAL);
        baodi.setLotteryType(scheme.getLotteryType());
        baodi.setPlatform(platform);
        //      return getSchemeEntityManager().saveBaodi(baodi);
        return baodiDao.save(baodi);

    }

    /**
     * 
     * 
     * @param scheme
     *             
     * @param user
     *            
     * @param cost
     *            ?
     * @param way
     *            ?
     * @return 
     */
    protected Subscription createSubscription(S scheme, User user, BigDecimal cost, SubscriptionWay way,
            PlatformInfo platform) {
        StringBuilder sb = new StringBuilder(50);
        sb.append("[").append(scheme.getSchemeNumber()).append("].");
        Prepayment prepayment = userManager.createKenoPrepayment(scheme.getTransactionId(), user.getId(), cost,
                PrepaymentType.SUBSCRIPTION, FundDetailType.SUBSCRIPTION, sb.toString(), platform,
                this.getLottery());

        return saveSubscription(scheme, prepayment.getId(), user, cost, way, platform);
    }

    /**
     * 
     * 
     * @param scheme
     *            
     * @param prepaymentId
     *            ID
     * @param user
     *            
     * @param cost
     *             ?
     * @param way
     *            ?
     * @return 
     */
    protected Subscription saveSubscription(S scheme, Long prepaymentId, User user, BigDecimal cost,
            SubscriptionWay way, PlatformInfo platform) {
        Subscription subscription = new Subscription();
        subscription.setUserId(user.getId());
        subscription.setUserName(user.getUserName());
        subscription.setCost(cost);
        subscription.setState(SubscriptionState.NORMAL);
        subscription.setWay(way);
        subscription.setLotteryType(scheme.getLotteryType());
        subscription.setSchemeId(scheme.getId());
        subscription.setPrepaymentId(prepaymentId);
        subscription.setPlatform(platform);
        return subscriptionDao.save(subscription);
    }

    /**
     * ?????
     * 
     * @param user
     *            
     * @param schemeDTO
     *            ??
     */
    @Transactional(readOnly = true)
    protected void checkUser(User user, KenoSchemeDTO schemeDTO) {
        if (user.isLocked())
            throw new ServiceException(
                    "???,??.,??.");
        BigDecimal totalCost = BigDecimal.ZERO;
        if (schemeDTO.isChase()) {
            totalCost = BigDecimal.valueOf(schemeDTO.getTotalCostOfChase());
            schemeDTO.getSponsorSubscriptionCost();
        } else if (schemeDTO.getSponsorSubscriptionCost() != null) {
            if (schemeDTO.getSponsorSubscriptionCost() != null
                    && schemeDTO.getSponsorSubscriptionCost().doubleValue() > 0)
                totalCost = totalCost.add(schemeDTO.getSponsorSubscriptionCost());
            if (schemeDTO.getSponsorBaodiCost() != null && schemeDTO.getSponsorBaodiCost().doubleValue() > 0)
                totalCost = totalCost.add(schemeDTO.getSponsorBaodiCost());
        } else {
            totalCost = BigDecimal.valueOf(schemeDTO.getSchemeCost());
        }
        BigDecimal allAccountBalance = userManager.getUser(user.getId()).getRemainMoney();

        BigDecimal remainMoney = (allAccountBalance != null) ? allAccountBalance : BigDecimal.ZERO;
        if (remainMoney.doubleValue() < totalCost.doubleValue())
            throw new ServiceException("?,??["
                    + Constant.MONEY_FORMAT.format(totalCost) + "],??["
                    + Constant.MONEY_FORMAT.format(remainMoney) + "],?.");
    }

    @Transactional(readOnly = true)
    protected void checkConformPeriodInitConfig(I issueData, KenoSchemeDTO schemeDTO) {
        if (null == issueData) {
            throw new ServiceException("?");
        }
        if (isPause()) {
            throw new ServiceException("?,??");
        }
        if (issueData.isSaleEnded()) {
            throw new ServiceException("?," + issueData.getPeriodNumber() + "??");
        }
        Long i = (new Date()).getTime() - Long.valueOf(schemeDTO.getBeforeTime() * 1000 * 60);
        if (issueData.getEndedTime().getTime() < (new Date()).getTime()
                - Long.valueOf(schemeDTO.getBeforeTime() * 1000 * 60)) {
            throw new ServiceException("?," + issueData.getPeriodNumber() + "??");
        }
    }

    @Transactional(readOnly = true)
    public void setUserManager(UserEntityManager userManager) {
        this.userManager = userManager;
    }

    @Transactional(readOnly = true)
    public I getNextPeriod(long periodId) {
        return this.issueDataDao.findNextIssueData(periodId);
    }

    /**
     * 
     * 
     * @param criteria
     * @return
     */
    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true)
    public List<S> findByCriteria(XDetachedCriteria criteria) {
        return schemeDao.findByDetachedCriteria(criteria);
    }

    /**
     * ??
     * 
     * @param issueData
     */
    public void updateResults(I issueData) {
        I oldIssueData = issueDataDao.get(issueData.getId());
        if (oldIssueData != null) {
            if (!StringUtils.isBlank(issueData.getResults())) {
                // if(!oldIssueData.isDrawed()){
                // oldIssueData.setDrawed(true);
                // }
                if (oldIssueData.getState().getStateValue() < IssueState.ISSUE_SATE_RESULT.getStateValue()) {
                    oldIssueData.setState(IssueState.ISSUE_SATE_RESULT);
                }
                // oldIssueData.addAfterSaleFlags(AfterSaleFlag.RESULT_UPDATED.getFlagValue());
                oldIssueData.setResults(issueData.getResults());
            }
            issueDataDao.save(oldIssueData);
        }
    }

    /**
     * ?/?
     */
    public void pauseOrStartLottery() {
        KenoSysConfig config = kenoSysConfigDao.get(this.getLottery().getKey() + KenoSysConfig.PAUSE);
        if (config != null) {
            String value = config.getKeyValue();
            if (!StringUtils.isBlank(value) && "true".equals(value.trim())) {
                config.setKeyValue("false");
            } else {
                config.setKeyValue("true");
            }
            kenoSysConfigDao.save(config);
        }
    }

    /**
     * ?/??
     * 
     * @return
     */
    public String findPauseOrStart() {
        return isPause() ? "true" : "false";
    }

    @Transactional(readOnly = true)
    protected boolean isPause() {
        KenoSysConfig config = kenoSysConfigDao.get(this.getLottery().getKey() + KenoSysConfig.PAUSE);
        if (config != null) {
            String value = config.getKeyValue();
            if (!StringUtils.isBlank(value) && "true".equals(value.trim())) {
                return true;
            }
        }
        return false;
    }

    @Resource(name = "kenoSysConfigDao")
    public void setKenoSysConfigDao(KenoSysConfigDao kenoSysConfigDao) {
        this.kenoSysConfigDao = kenoSysConfigDao;
    }

    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public S cloneNewScheme(S oldScheme) {
        this.schemeDao.getSession().evict(oldScheme);
        oldScheme.setSubscriptions(null);
        S newScheme = null;
        try {
            newScheme = schemeDao.getEntityClass().newInstance();
            try {
                try {
                    PropertyUtils.copyProperties(newScheme, oldScheme);
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                }
            } catch (InvocationTargetException e) {
                throw new ServiceException(e.getMessage());
            }
        } catch (InstantiationException e) {
            throw new ServiceException(e.getMessage());
        } catch (IllegalAccessException e) {
            throw new ServiceException(e.getMessage());
        }

        newScheme.setId(null);
        oldScheme = schemeDao.get(oldScheme.getId());
        return newScheme;
    }

    @Transactional(readOnly = true)
    public S getLastChaseScheme(long chaseId) {
        DetachedCriteria detachedCriteria = DetachedCriteria.forClass(this.schemeDao.getEntityClass());
        detachedCriteria.add(Restrictions.eq("chaseId", chaseId));
        detachedCriteria.addOrder(Order.desc("id"));
        List<S> schemeList = this.schemeDao.findByDetachedCriteria(detachedCriteria, 0, 1, true);
        if (schemeList != null && schemeList.size() > 0) {
            return schemeList.get(0);
        }
        return null;
    }

    public void handChasePlan(long chasePlanId) {
        ChasePlan chasePlan = this.chasePlanEntityManager.getChasePlan(chasePlanId);
        S lastScheme = this.getLastChaseScheme(chasePlan.getId());
        if (null == lastScheme) {
            lastScheme = this.newInstance(chasePlan);
        }
        boolean canChase = true;
        if (chasePlan.isWonStop() && lastScheme.isWon() && lastScheme.getState() == SchemeState.SUCCESS) {
            if (chasePlan.getPrizeForWonStop() != null && chasePlan.getPrizeForWonStop() >= 0) {
                if (lastScheme.getPrize().intValue() >= chasePlan.getPrizeForWonStop()) {
                    canChase = false;
                }
            }
        }
        if (!canChase) {
            this.stopChasePlan(chasePlan.getId(), "");// ???
        } else {
            // ???????
            I nexPeriod = this.getNextPeriod(chasePlan.getCurPeriodId());
            if (nexPeriod.isSaleEnded()) {
                this.stopChasePlan(chasePlan.getId(), "");
            } else {
                Integer multiple = chasePlan.getNextMultiple();
                if (multiple == null || multiple <= 0) {
                    try {
                        chasePlan.skip(nexPeriod.getId());
                    } catch (DataException e) {
                        throw new ServiceException(e.getMessage(), e);
                    }
                    this.userChasePlan(chasePlan);
                    return;
                }
                // 
                Transaction tran = userManager.createTransaction(TransactionType.SCHEME, "??");
                // ??
                S newScheme = null;
                if (null != lastScheme.getId()) {
                    newScheme = this.cloneNewScheme(lastScheme);
                } else {
                    newScheme = lastScheme;
                }
                try {
                    newScheme.reset(nexPeriod, chasePlan, tran.getId());// ?
                } catch (DataException e) {
                    throw new ServiceException(e.getMessage(), e);
                }
                newScheme.setId(null);// 
                newScheme = schemeDao.save(newScheme);

                // ????
                StringBuffer memo = new StringBuffer(50);
                memo.append("??[").append(newScheme.getSchemeNumber()).append(
                        "]?(?) ");
                Prepayment prepayment = userManager.transferKenoPrepayment(tran.getId(),
                        chasePlan.getPrepaymentId(), BigDecimalUtil.valueOf(newScheme.getSchemeCost()),
                        PrepaymentType.CHASE, FundDetailType.CHASE, memo.toString(), chasePlan.getPlatform(),
                        this.getLottery());
                // ??
                List<Subscription> joinList = findSubscriptionsOfScheme(lastScheme.getId(), null);

                if (null == joinList || joinList.isEmpty() || joinList.size() == 0) {
                    Subscription subscription = new Subscription();
                    subscription.setUserId(chasePlan.getUserId());
                    subscription.setUserName(chasePlan.getUserName());
                    subscription.setCost(BigDecimalUtil.valueOf(newScheme.getSchemeCost()));
                    subscription.setState(SubscriptionState.NORMAL);
                    subscription.setWay(SubscriptionWay.NORMAL);
                    subscription.setLotteryType(newScheme.getLotteryType());
                    subscription.setSchemeId(newScheme.getId());
                    subscription.setPrepaymentId(prepayment.getId());
                    subscriptionDao.save(subscription);
                } else {
                    Subscription oldSubscrt = joinList.get(0);
                    Subscription newSubscrt = cloneNewSubscription(oldSubscrt);
                    newSubscrt.reset(newScheme, prepayment.getId());
                    newSubscrt = subscriptionDao.save(newSubscrt);
                }

                // ??
                try {
                    chasePlan.chase(newScheme.getPeriodId(), newScheme.getSchemeCost().intValue());
                } catch (DataException e) {
                    throw new ServiceException(e.getMessage(), e);
                }
                this.userChasePlan(chasePlan);
            }
        }
    }

    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public List<Subscription> findSubscriptionsOfScheme(final long schemeId, final SubscriptionState state) {
        final S scheme = getScheme(schemeId);
        DetachedCriteria criteria = DetachedCriteria.forClass(Subscription.class);
        criteria.add(Restrictions.eq("schemeId", schemeId));
        criteria.add(Restrictions.eq("lotteryType", scheme.getLotteryType()));
        if (state != null) {
            criteria.add(Restrictions.eq("state", state));
        }
        return subscriptionDao.findByDetachedCriteria(criteria);
    }

    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public Subscription cloneNewSubscription(Subscription oldSubscription) {
        subscriptionDao.getSession().evict(oldSubscription);
        Subscription newSubscrt = new Subscription();
        try {
            try {
                PropertyUtils.copyProperties(newSubscrt, oldSubscription);
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            } catch (NoSuchMethodException e) {
                e.printStackTrace();
            }
        } catch (IllegalAccessException e) {
            throw new ServiceException(e.getMessage());
        }
        newSubscrt.setId(null);
        return newSubscrt;
    }

    public void stopChasePlan(long chasePlanId, String memo) {
        ChasePlan chasePlan = this.chasePlanEntityManager.getChasePlan(chasePlanId);
        if (chasePlan == null)
            throw new ServiceException("??.");
        try {
            chasePlan.stop();
        } catch (DataException e) {
        }
        String multiples[] = chasePlan.getMultiples().split(",");
        StringBuffer sb_ = new StringBuffer();
        for (String multiple : multiples) {
            if ("1".equals(multiple)) {
                multiple = "0";
            }
            sb_.append(multiple).append(",");
            ;
        }
        sb_.delete(sb_.length() - 1, sb_.length());
        chasePlan.setMultiples(sb_.toString());

        chasePlan = chasePlanEntityManager.saveChasePlan(chasePlan);

        int remainMoney = chasePlan.getTotalCost() - chasePlan.getChasedCost();

        StringBuilder sb = new StringBuilder(50);
        if (StringUtils.isNotBlank(memo)) {
            sb.append(memo + ",");
        }
        sb.append("??[").append(chasePlan.getId()).append("]?.");
        User user = userManager.getUser(chasePlan.getUserId());
        if (remainMoney > 0) {
            userManager.cancelPrepayment(chasePlan.getPrepaymentId(), FundDetailType.STOP_CHASE, sb.toString());
        }
    }

    @Transactional(readOnly = true)
    public List<I> findCanCloseIssue(Date time) {
        return this.issueDataDao.findCanCloseIssue(time);
    }

    /**
     * 
     * 
     * @param periodId
     * @return
     */
    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true)
    public Integer calAllSchemeCount(Long periodId) {
        Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 1);
        DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
        criteria.add(Restrictions.eq("periodId", periodId));
        criteria.setProjection(Projections.rowCount());
        List<Integer> resultList = schemeDao.findByDetachedCriteria(criteria);
        if (resultList != null && resultList.size() > 0) {
            return resultList.get(0);
        } else {
            return 0;
        }
    }

    /**
     * ?
     * 
     * @param periodId
     * @return
     */
    @Transactional(readOnly = true)
    public Integer calAllSaleCount(Long periodId) {
        Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 1);
        DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
        criteria.add(Restrictions.eq("periodId", periodId));
        criteria.add(Restrictions.eq("state", SchemeState.SUCCESS));
        criteria.setProjection(Projections.sum("schemeCost"));
        List<Integer> resultList = schemeDao.findByDetachedCriteria(criteria);
        if (null != resultList && !resultList.isEmpty()) {
            Integer count = resultList.get(0);
            if (null != count) {
                return count.intValue();
            } else {
                return 0;
            }
        } else {
            return 0;
        }
    }

    /**
     * 
     * 
     * @param periodId
     * @return
     */
    @Transactional(readOnly = true)
    public Integer calAllPrizeCount(Long periodId) {
        Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 1);
        DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
        criteria.add(Restrictions.eq("periodId", periodId));
        criteria.add(Restrictions.eq("won", true));
        criteria.setProjection(Projections.rowCount());
        List<Integer> resultList = schemeDao.findByDetachedCriteria(criteria);
        if (resultList != null && resultList.size() > 0) {
            return resultList.get(0);
        } else {
            return 0;
        }
    }

    /**
     * ?
     * 
     * @param periodId
     * @return
     */
    @Transactional(readOnly = true)
    public Integer calSuccessSchemeCount(Long periodId) {
        Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 1);
        DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
        criteria.add(Restrictions.eq("periodId", periodId));
        criteria.add(Restrictions.eq("state", SchemeState.SUCCESS));
        criteria.setProjection(Projections.rowCount());
        List<Integer> resultList = schemeDao.findByDetachedCriteria(criteria);
        if (resultList != null && resultList.size() > 0) {
            return resultList.get(0);
        } else {
            return 0;
        }
    }

    /**
     * ??
     * 
     * @param dateNow
     *            
     * @param beforeTime
     *            ???
     * @param size
     *            ?
     * @return ???
     */
    @Transactional(readOnly = true)
    public List<I> getCanChaseIssue(Date dateNow, Integer beforeTime, Integer size) {
        if (null == dateNow)
            return null;
        if (null == beforeTime)
            return null;
        if (null == size)
            return null;
        Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 0);
        DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
        criteria.add(Restrictions.not(Restrictions.eq("state", IssueState.ISSUE_SATE_FINISH)));
        criteria.add(Restrictions.gt("endedTime", DateUtils.addMinutes(dateNow, beforeTime)));
        criteria.addOrder(Order.asc("endedTime"));
        return issueDataDao.findByDetachedCriteria(criteria, 0, size, true);
    }

    /**
     * ??
     * 
     * @param dateNow
     *            
     * @param beforeTime
     *            ???
     * @return ???
     */
    @Transactional(readOnly = true)
    public Integer getCanChaseIssueNum(Date dateNow, Integer beforeTime) {
        if (null == dateNow)
            return null;
        if (null == beforeTime)
            return null;
        Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 0);
        DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
        criteria.add(Restrictions.not(Restrictions.eq("state", IssueState.ISSUE_SATE_FINISH)));
        criteria.add(Restrictions.gt("endedTime", DateUtils.addMinutes(dateNow, beforeTime)));
        criteria.addOrder(Order.asc("endedTime"));
        criteria.setProjection(Projections.rowCount());
        List<Integer> resultList = schemeDao.findByDetachedCriteria(criteria, true);
        if (resultList != null && resultList.size() > 0) {
            return resultList.get(0);
        } else {
            return 0;
        }
    }

    /**
     * ?
     * 
     * @param dateStar
     *            
     * @param dateEnd
     *            ?
     * @param dateMin
     *            ??action
     * @return ???
     */
    public void oprIssueTime(Date dateStar, Date dateEnd, Integer dateMin) {
        if (null == dateStar)
            throw new ServiceException("");
        if (null == dateEnd)
            throw new ServiceException("?");
        if (null == dateMin)
            throw new ServiceException("?.");
        Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 0);
        DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
        criteria.add(Restrictions.ge("endedTime", dateStar));
        criteria.add(Restrictions.le("endedTime", dateEnd));
        criteria.addOrder(Order.asc("endedTime"));
        List<I> resultList = schemeDao.findByDetachedCriteria(criteria, true);
        if (resultList != null && resultList.size() > 0) {
            for (I issue : resultList) {
                issue.setStartTime(DateUtils.addMinutes(issue.getStartTime(), dateMin));
                issue.setEndedTime(DateUtils.addMinutes(issue.getEndedTime(), dateMin));
                issue.setPrizeTime(DateUtils.addMinutes(issue.getPrizeTime(), dateMin));
            }
        } else {
            if (null == dateMin)
                throw new ServiceException("?.");
        }
    }

    /**
     * ?
     */
    public S cancelScheme(Long id, AdminUser adminUser) {
        if (null == id)
            throw new ServiceException("ID.");
        S s = schemeDao.get(id);
        if (null == s)
            throw new ServiceException("?.");
        if (null == s.getSponsorId())
            throw new ServiceException("??.");
        if (null == s.getSchemeCost())
            throw new ServiceException("??.");
        if (null == s.getPeriodId())
            throw new ServiceException("??.");
        Long issueId = s.getPeriodId();
        I issue = issueDataDao.get(issueId);
        if (null == issue)
            throw new ServiceException("??.");
        if (null == issue.getState())
            throw new ServiceException("??.");
        if (SchemeState.CANCEL.equals(s.getState()))
            throw new ServiceException("????.");
        if (issue.isDrawed()) {
            // ????
            throw new ServiceException("????.");
        }
        s.setState(SchemeState.CANCEL);
        schemeDao.save(s);
        // cyy-c 2011-04-13 
        // 
        List<Subscription> subscriptionList = findSubscriptionsOfScheme(s.getId(), SubscriptionState.NORMAL);// /?
        if (null != subscriptionList && !subscriptionList.isEmpty()) {
            // ?
            //xxxx-c 2014-09-23 ??
            for (Subscription subscription : subscriptionList) {
                subscription.setState(SubscriptionState.CANCEL);
                subscriptionDao.save(subscription);// ?
            }
        }
        StringBuilder cause = new StringBuilder(50);
        cause.append(adminUser == null ? "" : "?").append("[").append(s.getSchemeNumber())
                .append("].");
        userManager.cancelTransaction(s.getTransactionId(), FundDetailType.CANCEL_SCHEME, cause.toString());
        ticketThenEntityManager.synchronizationFailTicket(s);
        return s;
    }

    /**
     * ?
     */
    public S commitTransactionScheme(Long id, AdminUser adminUser) {
        if (null == id)
            throw new ServiceException("ID.");
        S s = schemeDao.get(id);
        userManager.commitTransaction(s.getTransactionId());
        return s;
    }

    // ???
    public void commitOrCancelTransaction(Long schemeId) {
        S scheme = schemeDao.get(schemeId);
        if (null == scheme)
            throw new ServiceException(".");

        System.out.println(scheme.isSendToPrint() + "~~~~" + scheme.getSchemePrintState());
        if (scheme.isSendToPrint()
                && (scheme.getState().equals(com.cai310.lottery.common.SchemeState.UNFULL)
                        || scheme.getState().equals(com.cai310.lottery.common.SchemeState.FULL))
                && SchemePrintState.SUCCESS.equals(scheme.getSchemePrintState())) {// ?
            //?&??
            if (scheme.isHasBaodi()) {// ?...
                List<Baodi> baodiList = this.findNormalBaodi(scheme.getId());
                if (baodiList != null && !baodiList.isEmpty()) {
                    for (Baodi baodi : baodiList) {
                        if (scheme.canUseBaodi()) {
                            this.baodiTransferSubscription(baodi, scheme);// ?
                            System.out.println("?");
                        } else {
                            this.cancelBaodi(baodi, scheme, false);// ?
                        }
                    }
                } else {
                    throw new ServiceException("????");
                }
            }
            if (scheme.isCanSubscribe()) {// ?...
                System.out.println("??");
                User userInfo = userManager.getUser(Constant.SITE_BAODI_USER_ID);// ?
                BigDecimal cost = scheme.getRemainingCost();
                try {
                    scheme.subscription(cost);
                } catch (DataException e) {
                    throw new ServiceException(e.getMessage(), e);
                }
                //???web
                PlatformInfo platformInfo = PlatformInfo.WEB;
                this.createSubscription(scheme, userInfo, cost, SubscriptionWay.SITE_BAODI, platformInfo);
            }
            // ?
            if (scheme.canCommitTransaction()) {
                scheme.commitTransaction();// ?
                scheme = schemeDao.save(scheme);// 
                //            // ??
                //            userManager.commitTransaction(scheme.getTransactionId());
                this.commitTransactionScheme(schemeId, null);
            } else {
                throw new ServiceException("??");
            }
        } else {// 
            if (scheme.getState().equals(com.cai310.lottery.common.SchemeState.CANCEL)
                    || scheme.getState().equals(com.cai310.lottery.common.SchemeState.REFUNDMENT)) {
            } else {
                this.cancelScheme(scheme.getId(), null);
            }
        }
    }

    public S forcePrint(Long schemeId) {
        S scheme = schemeDao.get(schemeId);
        if (scheme == null)
            throw new ServiceException("?.");
        scheme.setSendToPrint(true);// ?
        scheme.setSchemePrintState(SchemePrintState.SUCCESS);// 
        scheme = schemeDao.save(scheme);
        return scheme;
    }

    public S agentAnalyse(Long schemeId) {
        S scheme = schemeDao.get(schemeId);
        if (scheme == null)
            throw new ServiceException("?.");
        scheme.setAgentAnalyseState(AgentAnalyseState.UPDATED);
        scheme = schemeDao.save(scheme);
        return scheme;
    }

    /**
     * ?
     * 
     * @param scheme
     */
    @Transactional(readOnly = true)
    public S getScheme(Long id) {
        return schemeDao.get(id);
    }

    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public BigDecimal countSubscribedCost(Long schemeId, final Long userId) {
        final S scheme = getScheme(schemeId);
        return (BigDecimal) subscriptionDao.execute(new CriteriaExecuteCallBack() {
            public Object execute(Criteria criteria) {
                criteria.setProjection(Projections.sum("cost"));
                criteria.add(Restrictions.eq("schemeId", scheme.getId()));
                criteria.add(Restrictions.eq("userId", userId));
                criteria.add(Restrictions.eq("state", SubscriptionState.NORMAL));
                criteria.add(Restrictions.eq("lotteryType", scheme.getLotteryType()));
                return criteria.uniqueResult();
            }
        });
    }

    /**
     * 
     * 
     * @param issueData
     * @throws DataException
     */
    public void saleAnalye(I issueData) throws DataException {
        Class<S> clazz = ReflectionUtils.getSuperClassGenricType(getClass(), 1);
        DetachedCriteria criteria = DetachedCriteria.forClass(clazz);
        criteria.setProjection(Projections.property("id"));
        criteria.add(Restrictions.eq("periodId", issueData.getId()));
        List<Long> schemeIdList = schemeDao.findByDetachedCriteria(criteria);
        SaleAnalyse saleAnalyse = null;
        Integer playType = null;
        Map<String, SaleAnalyse> saleAnalyeMap = new HashMap<String, SaleAnalyse>();
        S s;
        SdEl11to5Scheme sdEl11to5Scheme = null;
        SscScheme sscScheme = null;
        AhKuai3Scheme ahKuai3Scheme = null;
        XjEl11to5Scheme xjEl11to5Scheme = null;
        Set<Long> updateSchemeSet = Sets.newHashSet();
        for (Long id : schemeIdList) {
            // ///keyplayType key=userId_ key=userId_0userId_1
            s = getScheme(id);
            saleAnalyse = saleAnalyeMap.get(s.getSponsorId() + "_" + (null == playType ? "" : playType));
            if (null == saleAnalyse) {
                saleAnalyse = new SaleAnalyse();
            }
            updateSchemeSet.add(s.getId());
            if (Lottery.SDEL11TO5.equals(s.getLotteryType())) {
                sdEl11to5Scheme = (SdEl11to5Scheme) s;
                playType = sdEl11to5Scheme.getBetType().ordinal();
            } else if (Lottery.SSC.equals(s.getLotteryType())) {
                sscScheme = (SscScheme) s;
                playType = sscScheme.getBetType().ordinal();
            } else if (Lottery.AHKUAI3.equals(s.getLotteryPlayType())) {
                ahKuai3Scheme = (AhKuai3Scheme) s;
                playType = ahKuai3Scheme.getBetType().ordinal();
            } else if (Lottery.XJEL11TO5.equals(s.getLotteryPlayType())) {
                xjEl11to5Scheme = (XjEl11to5Scheme) s;
                playType = xjEl11to5Scheme.getBetType().ordinal();
            }
            // //
            saleAnalyse.setPeriodId(issueData.getId());
            saleAnalyse.setLottery(s.getLotteryType());
            saleAnalyse.setPeriodNumber(issueData.getPeriodNumber());
            saleAnalyse.setPlayType(playType);
            saleAnalyse.setUserId(s.getSponsorId());
            saleAnalyse.setUserName(s.getSponsorName());
            saleAnalyse.setEndedTime(issueData.getEndedTime());
            saleAnalyse.setYearNum(DateUtil.getYear(issueData.getEndedTime()));
            saleAnalyse.setQuarterNum(DateUtil.getQuarter(issueData.getEndedTime()));
            saleAnalyse.setMonthNum(DateUtil.getMonth(issueData.getEndedTime()));
            saleAnalyse.setWeekNum(DateUtil.getWeek(issueData.getEndedTime()));
            saleAnalyse.setSendCurrency(Boolean.FALSE);

            saleAnalyse.addSchemeCount(1);
            saleAnalyse.addSchemeCost(s.getSchemeCost().intValue());
            saleAnalyse.addSchemeWonPrize(s.getPrize());
            saleAnalyse.addSubscriptionCount(1);
            saleAnalyse.addSubscriptionCost(new BigDecimal(s.getSchemeCost()));
            saleAnalyse.addSubscriptionWonPrize(s.getPrize());
            if (SchemeState.SUCCESS.equals(s.getState())) {
                // ?
                saleAnalyse.addSchemeSuccessCount(1);
                saleAnalyse.addSchemeSuccessCost(s.getSchemeCost().intValue());
                saleAnalyse.addSchemeSuccessWonPrize(s.getPrize());

                saleAnalyse.addSubscriptionSuccessCount(1);
                saleAnalyse.addSubscriptionSuccessCost(new BigDecimal(s.getSchemeCost()));
                saleAnalyse.addSubscriptionSuccessWonPrize(s.getPrize());

            } else if (SchemeState.CANCEL.equals(s.getState())) {
                // 
                saleAnalyse.addSchemeCancelCount(1);
                saleAnalyse.addSchemeCancelCost(s.getSchemeCost().intValue());
                saleAnalyse.addSchemeCancelWonPrize(s.getPrize());

                saleAnalyse.addSubscriptionCancelCount(1);
                saleAnalyse.addSubscriptionCancelCost(new BigDecimal(s.getSchemeCost()));
                saleAnalyse.addSubscriptionCancelWonPrize(s.getPrize());
            } else {
                // ?
                throw new DataException("?");
            }
            saleAnalyeMap.put(s.getSponsorId() + "_" + (null == playType ? "" : playType), saleAnalyse);
        }
        Set<String> keySet = saleAnalyeMap.keySet();
        List<SaleAnalyse> updateSaleAnalyse = Lists.newArrayList();
        for (String key : keySet) {
            updateSaleAnalyse.add(saleAnalyeMap.get(key));
        }
        saleAnalyseEntityManager.updateSaleAnalyse(updateSaleAnalyse, updateSchemeSet, issueData.getLotteryType());
    }

    // TODO
    @Override
    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public List moneyAnalye(String startDate, String endDate) {
        String tableName = "Lotu_Sd_El11to5_Scheme";
        if (Lottery.EL11TO5.equals(this.getLottery())) {
            tableName = "lotu_el11to5_scheme";
        } else if (Lottery.QYH.equals(this.getLottery())) {
            tableName = "lotu_qyh_scheme";
        }
        StringBuffer sql = new StringBuffer();
        sql.append("select sum(t.prize) from " + tableName
                + " t where 1=1 and t.won = 1 and t.state = 2 and t.sendtoprint = '1' and t.prizesended = '1' ");
        StringBuffer ticketSql = new StringBuffer();
        ticketSql.append("select sum(g.schemecost) from lotu_ticketgz g where 1=1 ");
        if (StringUtils.isNotBlank(startDate)) {
            sql.append("and t.prizesendtime >= to_date('" + startDate + "','yyyy-mm-dd hh24:mi:ss') ");
            ticketSql.append("and g.createtime >= to_date('" + startDate + "','yyyy-mm-dd hh24:mi:ss') ");
        }
        if (StringUtils.isNotBlank(endDate)) {
            sql.append("and t.prizesendtime <= to_date('" + endDate + "','yyyy-mm-dd hh24:mi:ss') ");
            ticketSql.append("and g.createtime <= to_date('" + endDate + "','yyyy-mm-dd hh24:mi:ss') ");
        }
        sql.append(" and t.won = 1 ");
        ticketSql.append("and g.ticketfinsh = 0 and g.lotterytype=" + this.getLottery().ordinal());
        sql.append("union " + ticketSql.toString());
        System.out.println("=============" + sql.toString());
        return issueDataDao.getSession().createSQLQuery(sql.toString()).list();
    }

    public void cancelSchemeBySponsor(Long schemeId) {
        S scheme = schemeDao.get(schemeId);
        //      T scheme = getSchemeEntityManager().getScheme(schemeId);
        if (scheme == null)
            throw new ServiceException("?.");

        if (isSaleEnded(scheme))
            throw new ServiceException(".");

        try {
            scheme.cancel(false);
        } catch (DataException e) {
            throw new ServiceException(e.getMessage());
        }

        scheme = schemeDao.save(scheme);

        StringBuilder cause = new StringBuilder(50);
        cause.append("?[").append(scheme.getSchemeNumber()).append("].");
        userManager.cancelTransaction(scheme.getTransactionId(), FundDetailType.CANCEL_SCHEME, cause.toString());
    }

    protected boolean isSaleEnded(S scheme) {
        I period = issueDataDao.findDataByNumber(scheme.getPeriodNumber());
        //      if (period.isSaleEnded()) {
        //         PeriodSalesId id = new PeriodSalesId(period.getId(), scheme.getMode());
        //         PeriodSales periodSales = periodManager.getPeriodSales(id);
        //         try {
        //            periodSales.checkIsCanCancelScheme();
        //         } catch (DataException e) {
        //            return true;
        //         }
        //         return false;
        //      } else {
        //         return true;
        //      }
        return period.isSaleEnded();
    }

    @Transactional(propagation = Propagation.NESTED)
    public S subscribe(SubscribeDTO dto) {
        //      S scheme = getSchemeEntityManager().getScheme(dto.getSchemeId());
        S scheme = schemeDao.get(dto.getSchemeId());
        if ((new Date()).compareTo(scheme.getCommitTime()) > 0) {
            throw new ServiceException("?");
        }
        if (scheme == null)
            throw new ServiceException("?.");

        if (dto.getWay() != SubscriptionWay.AUTOFOLLOW && !scheme.isAutoFollowCompleted())
            throw new ServiceException("??,??.");

        checkConformPeriodSubscriptionConfig(scheme);
        if (!dto.getUserId().equals(scheme.getSponsorId())) {
            switch (scheme.getSubscriptionLicenseType()) {
            case PUBLIC_LICENSE:
                break;
            case PASSWORD_LICENSE:
                if (StringUtils.isBlank(dto.getPassword()))
                    throw new ServiceException("?,???.");
                else if (!scheme.getSubscriptionPassword().equals(dto.getPassword().trim()))
                    throw new ServiceException("??.");
                break;
            }
        }

        boolean isSubscription = dto.getSubscriptionCost() != null && dto.getSubscriptionCost().doubleValue() > 0;
        boolean isBaodi = dto.getBaodiCost() != null && dto.getBaodiCost().doubleValue() > 0;
        if (!isSubscription && !isBaodi)
            throw new ServiceException("??.");

        BigDecimal totalCost = BigDecimal.ZERO;
        if (isSubscription)
            totalCost = totalCost.add(dto.getSubscriptionCost());
        if (isBaodi)
            totalCost = totalCost.add(dto.getBaodiCost());

        User user = userManager.getUser(dto.getUserId());

        if (user == null) {
            throw new ServiceException("?[#" + dto.getUserId() + "]?.");
        }
        BigDecimal allAccountBalance = user.getRemainMoney();

        if (allAccountBalance != null) {
            BigDecimal remainMoney = (allAccountBalance != null) ? allAccountBalance : BigDecimal.ZERO;
            if (remainMoney.doubleValue() < totalCost.doubleValue()) {
                throw new ServiceException("?,??["
                        + Constant.MONEY_FORMAT.format(totalCost) + "],??["
                        + Constant.MONEY_FORMAT.format(remainMoney) + "],?.");
            }
        }

        try {
            if (isSubscription) {
                scheme.subscription(dto.getSubscriptionCost());
                System.out.println("???:" + scheme.getSubscribedCost());
                System.out.println("3:::" + scheme.getState());
            }
            if (isBaodi) {
                scheme.baodi(dto.getBaodiCost());
                System.out.println("???:" + scheme.getBaodiCost());
                System.out.println("4:::" + scheme.getState());
            }
        } catch (DataException e) {
            throw new ServiceException("??" + e.getMessage(), e);
        }
        //      scheme = getSchemeEntityManager().saveScheme(scheme);
        System.out.println("scheme?:::" + scheme.getState());
        scheme = schemeDao.save(scheme);
        System.out.println("scheme?:::" + scheme.getState());
        //???web
        PlatformInfo platformInfo = PlatformInfo.WEB;
        if (isSubscription) {
            Subscription subscription = createSubscription(scheme, user, dto.getSubscriptionCost(), dto.getWay(),
                    platformInfo);
            if (scheme.getSchemePrintState().equals(SchemePrintState.SUCCESS)) {
                //?
                //            List<Subscription> subscriptionList = getSchemeEntityManager(scheme.getLotteryType()).findSubscriptionsOfBaoDiScheme(scheme.getId(),dto.getUserId(),SubscriptionState.NORMAL);
                //            for (Subscription st : subscriptionList) {
                User user_ = this.getUser(subscription.getUserId());
                recordAgent(user_, subscription.getLotteryType(), AgentDetailType.BUY, subscription.getCreateTime(),
                        subscription.getCost(), scheme.getId());
                //            }
            }
            System.out.println("createSubscription:::" + scheme.getState());
        }
        if (isBaodi) {
            createBaodi(scheme, user, dto.getBaodiCost(), platformInfo);
        }

        if (scheme.getState() == SchemeState.FULL && scheme.isHasBaodi()) {

            List<Baodi> baodiList = findNormalBaodi(scheme.getId());
            if (baodiList != null && !baodiList.isEmpty()) {
                for (Baodi baodi : baodiList) {
                    this.cancelBaodi(baodi, scheme, false);// ?
                }
            }
        }

        return scheme;
    }

    /**
     * ???
     * 
     * @param period
     *            
     * @param dto
     *            ??
     */
    protected void checkConformPeriodSubscriptionConfig(S scheme) {

        //      I period = kenoManager.getKenoPeriod(scheme.getPeriodId());
        I period = issueDataDao.get(scheme.getPeriodId());
        try {

            period.checkIsCanSubscriptionOrBaodi();
            PeriodSalesId id = new PeriodSalesId(period.getId(), scheme.getMode());
            //         PeriodSales periodSales = periodManager.getPeriodSales(id);
            //         periodSales.checkIsCanSubscriptionOrBaodi();
        } catch (DataException e) {
            throw new ServiceException(e.getMessage());
        }
    }

    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public List<Baodi> findNormalBaodi(final Long schemeId) {
        final S scheme = getScheme(schemeId);
        return (List<Baodi>) baodiDao.execute(new CriteriaExecuteCallBack() {
            public Object execute(Criteria criteria) {
                criteria.add(Restrictions.eq("schemeId", schemeId));
                criteria.add(Restrictions.eq("lotteryType", scheme.getLotteryType()));
                criteria.add(Restrictions.eq("state", BaodiState.NORMAL));
                return criteria.list();
            }
        });
    }

    protected void cancelBaodi(Baodi baodi, S scheme, boolean checkSpare) {
        try {
            baodi.cancel();
            scheme.cancelBaodi(baodi.getCost(), checkSpare);
        } catch (DataException e) {
            throw new ServiceException(e.getMessage());
        }
        baodi = baodiDao.save(baodi);
        scheme = this.saveSchemeReturn(scheme);

        StringBuilder cause = new StringBuilder(50);
        cause.append("[").append(scheme.getSchemeNumber()).append("]?[")
                .append(baodi.getId()).append("]?.");
        userManager.cancelPrepayment(baodi.getPrepaymentId(), FundDetailType.CANCEL_BAODI, cause.toString());
    }

    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public Baodi getBaodi(Long id) {
        return baodiDao.get(id);
    }

    public Subscription baodiTransferSubscription(Long baodiId) {
        //      Baodi baodi = getSchemeEntityManager().getBaodi(baodiId);
        Baodi baodi = baodiDao.get(baodiId);
        S scheme = schemeDao.get(baodi.getSchemeId());

        checkConformPeriodSubscriptionConfig(scheme);

        Subscription subscription = baodiTransferSubscription(baodi, scheme);
        scheme = schemeDao.save(scheme);
        return subscription;
    }

    protected Subscription baodiTransferSubscription(Baodi baodi, S scheme) {
        BigDecimal subscriptionCost;
        BigDecimal remainBaodiCost;
        try {
            subscriptionCost = scheme.baodiTransferSubscription(baodi.getCost());
            remainBaodiCost = baodi.useBaodi(subscriptionCost);
            if (remainBaodiCost.doubleValue() > 0) {
                scheme.cancelBaodi(remainBaodiCost, false);
            }
        } catch (DataException e) {
            throw new ServiceException(e.getMessage());
        }
        //      baodi = getSchemeEntityManager().saveBaodi(baodi);
        baodi = baodiDao.save(baodi);
        User user = userManager.getUser(baodi.getUserId());

        StringBuilder sb = new StringBuilder(50);
        sb.append("?").append(baodi.getId()).append("?,???")
                .append(Constant.MONEY_FORMAT.format(subscriptionCost));
        Prepayment prepayment = userManager.transferPrepayment(scheme.getTransactionId(), baodi.getPrepaymentId(),
                subscriptionCost, PrepaymentType.SUBSCRIPTION, FundDetailType.SUBSCRIPTION, sb.toString(),
                baodi.getPlatform(), this.getLottery());

        Subscription subscription = saveSubscription(scheme, prepayment.getId(), user, subscriptionCost,
                SubscriptionWay.TRANSFORM_BAODI, baodi.getPlatform());

        //?
        List<Subscription> subscriptionList = getSchemeEntityManager(baodi.getLotteryType())
                .findSubscriptionsOfBaoDiScheme(scheme.getId(), baodi.getUserId(), SubscriptionState.NORMAL);
        for (Subscription st : subscriptionList) {
            User user_ = this.getUser(st.getUserId());
            recordAgent(user_, st.getLotteryType(), AgentDetailType.BUY, st.getCreateTime(), st.getCost(),
                    scheme.getId());
        }

        if (remainBaodiCost.doubleValue() > 0) {
            userManager.cancelPrepayment(baodi.getPrepaymentId(), FundDetailType.CANCEL_BAODI,
                    "?,?.");
        }

        return subscription;
    }

    public User getUser(Long id) {
        return userDao.get(id);
    }

    public void recordAgent(User user, Lottery lottery, AgentDetailType agentDetailType, Date time,
            BigDecimal money, Long schemeId) {
        if (!user.getId().equals(Constant.SITE_BAODI_USER_ID)) {
            //???
            if (AgentDetailType.BUY.equals(agentDetailType)) {
                ///?
                LinkedHashMap<User, AgentRelationRebate> agentRebateMap = this.findAgentUser(user, lottery);
                if (null != agentRebateMap && !agentRebateMap.isEmpty()) {
                    try {
                        this.saveAgentFundDetail(user, money, AgentDetailType.BUY,
                                AgentLotteryType.getAgentLotteryType(lottery), null, schemeId, lottery);
                        sendAgentRebate(agentRebateMap, money, schemeId, lottery);
                    } catch (DataException e) {
                        throw new ServiceException(e.getMessage());
                    }
                }
            } else {
                this.saveAgentFundDetail(user, money, agentDetailType, null, null, null, null);
            }
        }
    }

    public void sendAgentRebate(LinkedHashMap<User, AgentRelationRebate> agentRebateMap, BigDecimal amount,
            Long schemeId, Lottery lottery) throws DataException {
        Set<User> agentRebateKey = agentRebateMap.keySet();
        if (agentRebateKey.size() > 0) {
            //
            User buy_user = null;
            LinkedList<User> userList = Lists.newLinkedList();
            for (User user : agentRebateKey) {
                userList.add(user);
                buy_user = user;
            }
            User userUp = null;
            AgentRelationRebate agentRebateUp = null;
            User userDown = null;
            AgentRelationRebate agentRebateDown = null;
            Double rebatetemp = null;
            BigDecimal rebateAmount = null;
            for (int i = 0; i < userList.size(); i++) {
                if (i == userList.size() - 1) {
                    //?
                    userUp = userList.get(i);///
                    agentRebateUp = agentRebateMap.get(userUp);
                    rebatetemp = agentRebateUp.getRebate();///
                    rebateAmount = amount.multiply(BigDecimal.valueOf(rebatetemp)).divide(BigDecimal.valueOf(100));///
                    if (null != rebateAmount.abs() && rebateAmount.abs().doubleValue() > 0) {
                        this.saveAgentFundDetail(userUp, rebateAmount, AgentDetailType.REBATE,
                                AgentLotteryType.getAgentLotteryType(lottery), rebatetemp, schemeId, lottery);
                        userEntityManager.oprUserMoney(userUp, rebateAmount.abs(),
                                "{" + buy_user.getUserName() + "}?{" + amount
                                        + "},{" + NumUtils.format(rebatetemp, 4, 4)
                                        + "},{" + NumUtils.format(rebateAmount.abs(), 4, 4) + "}",
                                FundMode.IN, FundDetailType.REBATE, null);
                    }
                } else {
                    userUp = userList.get(i);///
                    agentRebateUp = agentRebateMap.get(userUp);
                    userDown = userList.get(i + 1);//??????
                    agentRebateDown = agentRebateMap.get(userDown);

                    rebatetemp = agentRebateUp.getRebate() - agentRebateDown.getRebate();///
                    rebateAmount = amount.multiply(BigDecimal.valueOf(rebatetemp)).divide(BigDecimal.valueOf(100));///
                    //
                    if (null != rebateAmount.abs() && rebateAmount.abs().doubleValue() > 0) {
                        this.saveAgentFundDetail(userUp, rebateAmount, AgentDetailType.REBATE,
                                AgentLotteryType.getAgentLotteryType(lottery), rebatetemp, schemeId, lottery);
                        userEntityManager.oprUserMoney(userUp, rebateAmount.abs(),
                                "{" + buy_user.getUserName() + "}?{" + amount
                                        + "},{" + agentRebateUp.getRebate() + "-"
                                        + agentRebateDown.getRebate() + "},{"
                                        + NumUtils.format(rebateAmount.abs(), 4, 4) + "}",
                                FundMode.IN, FundDetailType.REBATE, null);

                    }
                }

            }
        }
    }

    public AgentFundDetail saveAgentFundDetail(User user, BigDecimal money, AgentDetailType detailType,
            AgentLotteryType lotteryType, Double rebate, Long schemeId, Lottery lottery) {
        if (detailType.equals(AgentDetailType.BUY)) {
            XDetachedCriteria criteria = new XDetachedCriteria(AgentFundDetail.class, "a");
            criteria.add(Restrictions.eq("a.schemeId", schemeId));
            criteria.add(Restrictions.eq("a.lottery", lottery));
            criteria.add(Restrictions.eq("a.detailType", detailType));
            criteria.add(Restrictions.eq("a.userId", user.getId()));
            List<AgentFundDetail> agentFundDetailList = this.agentFundDetailDao.findByDetachedCriteria(criteria);
            if (agentFundDetailList != null && !agentFundDetailList.isEmpty()) {
                AgentFundDetail agentFundDetail = agentFundDetailList.get(0);
                BigDecimal money_ = agentFundDetail.getMoney();
                money_ = money_.add(money);
                agentFundDetail.setMoney(money_);
                return this.agentFundDetailDao.save(agentFundDetail);
            } else {
                AgentFundDetail agentFundDetail = new AgentFundDetail();
                agentFundDetail.setUserId(user.getId());
                agentFundDetail.setUserName(user.getUserName());
                agentFundDetail.setMoney(money);
                agentFundDetail.setDetailType(detailType);
                Account account = findAccount(user.getId());
                if (null == account) {
                    //?
                    account = new Account(user);
                    account = this.accountDao.save(account);
                }
                agentFundDetail.setAccountId(account.getId());
                agentFundDetail.setLotteryType(lotteryType);
                agentFundDetail.setRebate(rebate);
                agentFundDetail.setSchemeId(schemeId);
                agentFundDetail.setLottery(lottery);
                return this.agentFundDetailDao.save(agentFundDetail);
            }
        } else {
            XDetachedCriteria criteria = new XDetachedCriteria(AgentFundDetail.class, "a");
            criteria.add(Restrictions.eq("a.schemeId", schemeId));
            criteria.add(Restrictions.eq("a.lottery", lottery));
            criteria.add(Restrictions.eq("a.lotteryType", lotteryType));
            criteria.add(Restrictions.eq("a.detailType", detailType));
            criteria.add(Restrictions.eq("a.userId", user.getId()));
            List<AgentFundDetail> agentFundDetailList = this.agentFundDetailDao.findByDetachedCriteria(criteria);
            if (agentFundDetailList != null || !agentFundDetailList.isEmpty()) {
                AgentFundDetail agentFundDetail = agentFundDetailList.get(0);
                BigDecimal money_ = agentFundDetail.getMoney();
                money_ = money_.add(money);
                agentFundDetail.setMoney(money_);
                return this.agentFundDetailDao.save(agentFundDetail);
            } else {
                AgentFundDetail agentFundDetail = new AgentFundDetail();
                agentFundDetail.setUserId(user.getId());
                agentFundDetail.setUserName(user.getUserName());
                agentFundDetail.setMoney(money);
                agentFundDetail.setDetailType(detailType);
                Account account = findAccount(user.getId());
                if (null == account) {
                    //?
                    account = new Account(user);
                    account = this.accountDao.save(account);
                }
                agentFundDetail.setAccountId(account.getId());
                agentFundDetail.setLotteryType(lotteryType);
                agentFundDetail.setRebate(rebate);
                agentFundDetail.setSchemeId(schemeId);
                agentFundDetail.setLottery(lottery);
                return this.agentFundDetailDao.save(agentFundDetail);
            }
        }
    }

    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public Account findAccount(final Long userId) {
        return this.accountDao.findUniqueBy("userId", userId);
    }

    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public LinkedHashMap<User, AgentRelationRebate> findAgentUser(User user, Lottery lottery) {
        if (null == user)
            throw new ServiceException("");
        User userTemp = null;
        LinkedHashMap<User, AgentRelationRebate> agentRelationRebateMap = Maps.newLinkedHashMap();
        List<AgentRelation> userAgentList = this.findUserAgentRelation(user.getId());
        if (null != userAgentList) {
            for (AgentRelation agentRelation : userAgentList) {
                userTemp = this.getUser(agentRelation.getGroupId());
                if (null != userTemp) {
                    AgentRelationRebate agentRelationRebate = new AgentRelationRebate();
                    if (null != lottery) {
                        agentRelationRebate.setAgentLotteryType(AgentLotteryType.getAgentLotteryType(lottery));
                        if (userTemp.getId().equals(Constant.SITE_BAODI_USER_ID)) {
                            agentRelationRebate.setRebate(Constant.MAXREBATE);
                        } else {
                            agentRelationRebate.setRebate(this.findAgentRebate(userTemp.getId(),
                                    AgentLotteryType.getAgentLotteryType(lottery)).getRebate());
                        }
                    }
                    agentRelationRebate.setUserId(userTemp.getId());
                    agentRelationRebate.setUserName(userTemp.getUserName());
                    agentRelation.setRealName(null == userTemp.getInfo() ? null : userTemp.getInfo().getRealName());
                    agentRelation.setPos(agentRelation.getPos());
                    agentRelationRebateMap.put(userTemp, agentRelationRebate);
                }
            }
        }
        return agentRelationRebateMap;
    }

    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public List<AgentRelation> findUserAgentRelation(final Long userId) {
        return (List<AgentRelation>) agentRelationDao.execute(new CriteriaExecuteCallBack() {
            public Object execute(Criteria criteria) {
                criteria.add(Restrictions.eq("userId", userId));
                criteria.addOrder(Order.asc("pos"));
                return criteria.list();
            }
        });
    }

    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public AgentRebate findAgentRebate(final Long userId, final AgentLotteryType agentLotteryType) {
        return (AgentRebate) agentRebateDao.execute(new CriteriaExecuteCallBack() {
            public Object execute(Criteria criteria) {
                criteria.add(Restrictions.eq("userId", userId));
                criteria.add(Restrictions.eq("agentLotteryType", agentLotteryType));
                List list = criteria.list();
                if (null != list && !list.isEmpty())
                    return list.get(0);
                return null;
            }
        });
    }

    @Transactional(readOnly = true, propagation = Propagation.SUPPORTS)
    public S getSchemeById(Long id) {
        return this.getScheme(id);
    }
}