一、背景
日常做需求开发过程中,有以下业务场景:将一批案件分配给一批客户,每个案件具有不同的金额,每个客户有分配比例,需要实现在案件数量按照比例分配给客户的情况下,还要尽量保持客户分配到案件的总金额也能按照分配比例分布。
二、分析
如果案件分配单纯考虑数量或者金额,分配逻辑还是比较简单的。但是既要考虑数量,又要考虑金额,这涉及到两个变量,而且一般案件金额分布并不均匀,直接分配往往达不到期望的效果。
在这里,采用每个客户依次分配方法,一个客户分配到按比例算出的案件数量后,再进行下一个客户的案件分配。为了保持最终金额尽量保持比例分布,引入单次分配案件期望金额(expectedCaseAmount)这一概念,用来寻找每次给客户分配案件过程中最符合条件的案件。
具体流程如下:
- 分案需要提供待分配案件列表(caseList)和参与分案的用户列表(userList)。
- 计算每个用户期望分配到的案件总数(expectedTotalCaseNum)和期望分配到的金额总和
(expectedTotalCaseAmount)。 - 对于每个客户分案时,计算单次分配案件期望金额(expectedCaseAmount)。
- 在每个案件分案时,取所有案件中案件金额与单次分配案件期望金额最接近的案件分给客户。
- 对于客户每分配到一个案件后,用户期望分配到金额总和需要减掉该案件金额,同时期望分配到的案件总数需要减1,操作完成后重新执行步骤3,计算单次分配案件期望金额。然后根据最新计算的单次分配案件期望金额继续进行步骤4,直到客户分配案件数达到其一开始期望分配的案件数(由于每次分完案期望分配到的案件总数会减1,所以最终单个客户案件分配完成标志为期望分配到的案件总数值为0)。
- 由于每次分案过程中,分配给客户的案件金额与期望的金额接近,所以最终总的分案金额也会与分配比例吻合。
三、代码实现
新建用户类(User)和案件类(Case):
@Data
public class User {
/**
* 用户名
*/
private String username;
/**
* 分案比例
*/
private int rate;
}
@Data
public class Case {
/**
* 案件id
*/
private String caseId;
/**
* 案件金额
*/
private BigDecimal caseAmount;
/**
* 案件归属人
*/
private String username;
}
分案逻辑代码:
import java.math.BigDecimal;
import java.util.*;
public class CaseAllocation {
/**
* 案件分配
* @param userList 用户集合
* @param caseList 案件集合
* @return
*/
public List allocateTotCase(List<User> userList, List<Case> caseList) {
//记录分配完成案件
List<Case> completedCaseList = new ArrayList<>();
//记录每个用户期望分配的总案件金额
Map<String, BigDecimal> expectedAmountMap = new HashMap<>();
//记录每个用户期望分配的总案件数
Map<String, Integer> expectedNumMap = new HashMap<>();
//全部案件总金额
BigDecimal totCaseAmount = new BigDecimal(0);
//全部用户分配比例之和(每个用户实际分案比例为