com.github.dennisit.vplus.data.utils.grain.GrainUtils Maven / Gradle / Ivy
package com.github.dennisit.vplus.data.utils.grain;
import com.google.common.base.Function;
import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;
import org.apache.commons.collections.CollectionUtils;
import org.springframework.util.Assert;
import javax.annotation.Nullable;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicLong;
public class GrainUtils {
/**
* 检测目标集合元素是否够抵扣
*
* @param coll 抵扣的目标集合
* @param sum 抵扣总数
* @return 目标集合中的数据是否够抵扣
*/
public static boolean deductible(List coll, long sum) {
Assert.isTrue(sum > 0 && CollectionUtils.isNotEmpty(coll), "参数不合规(抵扣集合为空|抵扣总数<0)");
boolean deductible = false;
AtomicLong count = new AtomicLong(0);
for (GrainEntry.GrainBatch triple : coll) {
if (count.addAndGet(triple.getNum()) >= sum) {
deductible = true;
break;
}
}
return deductible;
}
/**
* 从原始批次中追加抠币目标批次数
*
* @param coll 目标扣除集合, 结构是因为需求要求扣除按顺序批次顺序
* @param sum 目标总数
* @return 抵扣逻辑
*/
public static GrainEntry.DiscountEntry discount(List coll, long sum) {
Assert.isTrue(deductible(coll, sum), "目标集合不够抵扣");
AtomicLong total = new AtomicLong(sum);
GrainEntry.GrainBatch tail = null;
List used = Lists.newArrayList();
for (GrainEntry.GrainBatch triple : coll) {
if (total.get() - triple.getNum() >= 0) { // 抵扣全部(只能>=)
total.addAndGet(0 - triple.getNum());
used.add(triple);
if (total.get() == 0) {
tail = new GrainEntry.GrainBatch(triple.getId(), 0, triple.getCreateTime());
break;
}
} else if (total.get() > 0) { // 抵扣部分(只能>)
long occupy = total.get(); // 尾数占用
total.addAndGet(0 - occupy);
tail = new GrainEntry.GrainBatch(triple.getId(), triple.getNum() - occupy, triple.getCreateTime());
used.add(new GrainEntry.GrainBatch(triple.getId(), occupy, triple.getCreateTime()));
}
}
return new GrainEntry.DiscountEntry(tail, used);
}
/**
* 退还币算法抽象, 这里有一些业务前提条件: 退还的所有数据,存放到优先使用批次集合中去
*
* @param referList 退换参照对象[对应的入账流水] 升序序列 (referList是priorityPair和usedPair的数据KEY的集合总集)
* @param priorityPair 优先使用对象[对应的历史退币优先批次]
* @param usedPair 待退还对象[对应的本次要退还的批次]
* @return 要求返回结果需要按顺序进行入库抵消
*/
public static GrainEntry.RefundEntry refund(List referList, Map priorityPair, Map usedPair) {
// 退还记录总数
AtomicLong refundTotal = new AtomicLong(0);
// 退还更新后优先使用批次集合
List priorityList = Lists.newCopyOnWriteArrayList();
// 有效的真实有效的数据集合
List refundedList = Lists.newCopyOnWriteArrayList();
// 退还处理逻辑
for (GrainEntry.GrainBatch triple : referList) {
// 数据已经过期的不在处理
if (triple.getCreateTime().getTime() <= System.currentTimeMillis()) {
continue;
}
// 如果待退还的批次集包含在参照集中
if (usedPair.containsKey(triple.getId())) {
long refundNum = usedPair.get(triple.getId());
// 退还优先批次处理
if (priorityPair.containsKey(triple.getId())) {
// merge 如果退还的批次ID包含在优先批次集合中, 则将退还批次和优先批次上的数量聚合
long sum = refundNum + priorityPair.get(triple.getId());
long val = sum >= triple.getNum() ? triple.getNum() : sum;
priorityList.add(new GrainEntry.GrainBatch(triple.getId(), val, triple.getCreateTime()));
} else {
// append 如果退还的批次ID不包含在优先批次集合中, 则追加到优先使用批次
long val = refundNum >= triple.getNum() ? triple.getNum() : refundNum;
priorityList.add(new GrainEntry.GrainBatch(triple.getId(), val, triple.getCreateTime()));
}
// 追加退还集合
refundedList.add(new GrainEntry.GrainBatch(triple.getId(), refundNum, triple.getCreateTime()));
// 追加退还总数
refundTotal.addAndGet(refundNum);
}
// 退币批次不存在于优先批次的数据继续追加上去
if (priorityPair.containsKey(triple.getId()) && !usedPair.containsKey(triple.getId())) {
priorityList.add(new GrainEntry.GrainBatch(triple.getId(), priorityPair.get(triple.getId()), triple.getCreateTime()));
}
}
return new GrainEntry.RefundEntry(refundedList, sortGrainBatch(priorityList), refundTotal.get());
}
/**
* 按照批次编号升序排列优先使用批次信息
*
* @param coll 目标集合
* @return 升序后的集合
*/
public static List sortGrainBatch(List coll) {
if (CollectionUtils.isEmpty(coll)) {
return Lists.newArrayList();
}
Ordering batchIdAsc = Ordering.natural().nullsLast().onResultOf(new Function() {
@Nullable
@Override
public Long apply(@Nullable GrainEntry.GrainBatch input) {
return input.getId();
}
});
Collections.sort(coll, batchIdAsc);
return coll;
}
}