比例分成算法--java

关于业绩分配,常常会出现几家公司或者几个人合作的情况,那么合作之后创造出来的业绩价值如何分配的呢?有可能是1:1或者3:7.或者是3:3:4等等。那么我们针对这种情况就设计出来了一套比例分成系统。

代码如下所示:

主代码:

package com.handler.mark.cardperformance;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.dataplatform.dto.base.IBaseOrgDTO;
import com.dataplatform.enums.DimensionChannelGroupEnum;
import com.dataplatform.model.ApplyDetailInfo;
import com.dataplatform.util.MarkByProportionUtil;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.ShardedJedis;
import redis.clients.jedis.ShardedJedisPool;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;

/**
 * Created by on 2018/7/20.
 */

/**
 * 比例分成算法
 * @author 
 * @date 2018-07-26
 * @version 1.0
 *
 */
public class WeightRoundRobinBo {

    private static final int DEF_CURRENT_INDEX = -1;
    private static final int DEF_CURRENT_WEIGHT = 0;

    private static final String KEY_CURRENT_INDEX = "currentIndex";
    private static final String KEY_CURRENT_WEIGHT = "currentWeight";

    private Logger LOG = LoggerFactory.getLogger(WeightRoundRobinBo.class);

    private ShardedJedisPool shardedJedisPool;

    @SuppressWarnings("unused")
	private WeightRoundRobinBo() {
    }

    /**
     * 自定义构造方法
     * @param orgListOfChannel 渠道所对应机构列表
     * @param currentEnum
     * @param applyDetailInfo    当前进件信息
     * @param shardedJedisPool
     */
    public WeightRoundRobinBo(List<? extends IBaseOrgDTO> orgListOfChannel, DimensionChannelGroupEnum currentEnum, ApplyDetailInfo applyDetailInfo, ShardedJedisPool shardedJedisPool) {
        this.orgListOfChannel = orgListOfChannel;
        this.shardedJedisPool = shardedJedisPool;
        try(ShardedJedis jedis = shardedJedisPool.getResource()){


            /** 持卡用户进件与新用户进件分开打标*/
            this.redisKey = MarkByProportionUtil.getRedisKeyOfCurrentWight(
                    currentEnum.getDimensionCode(),
                    currentEnum.getChannelGroupCode(),
                    applyDetailInfo.getSource(),
                    applyDetailInfo.getIsCardHolder()
            );

            if(StringUtils.isBlank(redisKey)){
                throw new RuntimeException("illegal parameters: redisKey is null");
            }

            JSONObject json = JSON.parseObject(jedis.get(this.redisKey));

            String currentIndexStr = json == null ? null : json.getString(KEY_CURRENT_INDEX);
            String currentWeightStr = json == null ? null : json.getString(KEY_CURRENT_WEIGHT);

            this.currentIndex = StringUtils.isNumeric(currentIndexStr) ? Integer.parseInt(currentIndexStr) : DEF_CURRENT_INDEX;
            this.currentWeight = StringUtils.isNumeric(currentWeightStr) ? Integer.parseInt(currentWeightStr) : DEF_CURRENT_WEIGHT;
            this.orgCount = orgListOfChannel.size();
            this.maxWeight = greatestWeight();
            this.gcdWeight = greatestCommonDivisor();


            this.initStatus = true;

        }catch (Exception e){
            LOG.error("WeightRoundRobin init error: ", e);
        }
    }

    /**
     * 上次选择机构
     */
    private int currentIndex = DEF_CURRENT_INDEX;
    /**
     * 当前调度的权值
     */
    private int currentWeight = DEF_CURRENT_WEIGHT;
    /**
     * 最大权重
     */
    private int maxWeight;
    /**
     * 权重的最大公约数
     */
    private int gcdWeight;
    /**
     * 机构数量
     */
    private int orgCount;

    private List<? extends IBaseOrgDTO> orgListOfChannel = new ArrayList<>();

    /**
     * redis上保存当前index和weight的key
     */
    private String redisKey;

    /**
     * 是否初始化
     */
    private boolean initStatus = false;

    /**
     * 获取两个数的最大公约数
     */
    private int greaterCommonDivisor(int a, int b) {
        BigInteger aBig = new BigInteger(String.valueOf(a));
        BigInteger bBig = new BigInteger(String.valueOf(b));
        return aBig.gcd(bBig).intValue();
    }

    /**
     * 遍历获取所有机构比例的最大公约数
     */
    private int greatestCommonDivisor() {
        int divisor = 0;
        for (int index = 0, len = orgListOfChannel.size(); index < len - 1; index++) {
            if (index == 0) {
                divisor = greaterCommonDivisor(
                        orgListOfChannel.get(index).getProportion(), orgListOfChannel.get(index + 1).getProportion());
            } else {
                divisor = greaterCommonDivisor(divisor, orgListOfChannel.get(index).getProportion());
            }
        }
        return divisor;
    }

    /**
     * 遍历获取最大的机构占比(权重)
     */
    private int greatestWeight() {
        int weight = 0;
        for (IBaseOrgDTO orgObj : orgListOfChannel) {
            if (weight < orgObj.getProportion()) {
                weight = orgObj.getProportion();
            }
        }
        return weight;
    }

    /**
     * 根据权重轮询获取当前org
     *
     * @gcdWeight 权重的最大公约数
     * @currentIndex 用于记录当前轮询链表的位置
     * @currentWeight 用于记录当前权重值
     * @orgListOfChannel 用于当前进件的渠道所对应的分成机构列表
     *
     *
     * 初始从最大的权重(占比)进行分配 currentWeight = maxWeight
     *
     * 非初始情况,根据redis缓存中保存的位置currentIndex和currentWeight继续分配
     *
     * 遍历机构列表,每次遍历输出大于等于currentWeight的机构对象
     *
     * 每次遍历完,将currentWeight减去 所有机构权重值的最大公约数,再次遍历输出
     *
     * 直到currentWeight小于等于零,即完成了一遍权重轮询,每一个权重值都输出了对应比例的次数
     *
     *
     */
	private IBaseOrgDTO getOrgInfo() {
        while (true) {
            currentIndex = (currentIndex + 1) % orgCount;
            if (currentIndex == 0) {
                currentWeight = currentWeight - gcdWeight;
                if (currentWeight <= 0) {
                    currentWeight = maxWeight;
                    if (currentWeight == 0) {
                        return null;
                    }
                }
            }
            if (orgListOfChannel.get(currentIndex).getProportion() >= currentWeight) {
                return orgListOfChannel.get(currentIndex);
            }
        }
    }

    /**
     * 输出org后,将currentIndex和currentWeight更新到redis上
     */
    private void updateRedis(){
        JSONObject json = new JSONObject();
        json.put(KEY_CURRENT_INDEX, currentIndex);
        json.put(KEY_CURRENT_WEIGHT, currentWeight);

        try(ShardedJedis jedis = shardedJedisPool.getResource()){
            jedis.set(this.redisKey, json.toJSONString());
        }catch (Exception e){
            LOG.error("updateRedis error: ", e);
        }

    }

    public IBaseOrgDTO execute(){
        IBaseOrgDTO org = null;
        if(initStatus) {
        	org = getOrgInfo();
            updateRedis();
        }
        return org;
    }

}
package com.cmbc.util;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Created by on 2018/8/13.
 */
public class MarkByProportionUtil {

    private static Logger LOG = LoggerFactory.getLogger(MarkByProportionUtil.class);

    /**
     * 比例打标redis key前缀
     */
    public static final String REDIS_KEY_PREFIX = "WSDP_index&weight";

    /**
     * 比例打标redis key分隔符
     */
    public static final String REDIS_KEY_SEPARATOR = ":";

    public static String getRedisKeyOfCurrentWight(String dimensionCode, String channelGroupCode, String channel, int isCardHolder) {

        if (StringUtils.isAnyBlank(dimensionCode, channelGroupCode, channel)) {
            LOG.error("illegal parameters:  dimensionCode:{},  channelGroupCode:{},  channel:{}", dimensionCode, channelGroupCode, channel);
        }

        /** 固定前缀 + 维度code + 渠道组code + 渠道代码 + 是否持卡人 */
        String key = new StringBuffer()
                .append(REDIS_KEY_PREFIX)
                .append(REDIS_KEY_SEPARATOR)
                .append(dimensionCode)
                .append(REDIS_KEY_SEPARATOR)
                .append(channelGroupCode)
                .append(REDIS_KEY_SEPARATOR)
                .append(channel)
                .append("_")
                .append(isCardHolder)
                .toString();

        return key;
    }
}
package com.cmbc.dto.base;

/**
 * Created by on 2018/7/22.
 */
public interface  IBaseOrgDTO {

	/**
	 * 获取机构id
	 * @return
	 */
    public abstract Integer getOrgId();

    /**
     * 获取比例
     * @return
     */
    public abstract Integer getProportion();

    /**
     * 获取机构类型
     * @return
     */
    public abstract String getOrgType();
}

调用方法:

/** 按比例分成 */
WeightRoundRobinBo wrr = new WeightRoundRobinBo(orgList, rule, applyDetailInfo,
      cacheService.getShardedJedisPool());

org = wrr.execute();
if (null == org) {

   LOG.error(ErrorEnums.ERR_10013.getMsg());
   return ErrorEnums.ERR_10013.getMsg();

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值