关于业绩分配,常常会出现几家公司或者几个人合作的情况,那么合作之后创造出来的业绩价值如何分配的呢?有可能是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();
}