| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731 |
- package com.qingjian.module.person.service;
- import cn.hutool.core.collection.CollUtil;
- import cn.hutool.core.util.ReUtil;
- import cn.hutool.core.util.StrUtil;
- import cn.hutool.json.JSONObject;
- import cn.hutool.json.JSONUtil;
- import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
- import com.baomidou.mybatisplus.core.toolkit.Wrappers;
- import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
- import com.qingjian.common.cache.CaseDataCache;
- import com.qingjian.common.exception.ServerException;
- import com.qingjian.common.model.person.dto.*;
- import com.qingjian.common.model.person.entity.Intimacy;
- import com.qingjian.common.model.person.entity.PersonBasicInfo;
- import com.qingjian.common.model.person.entity.PersonGroup;
- import com.qingjian.common.model.person.entity.PersonGroupRel;
- import com.qingjian.common.model.track.dto.TravelDataDTO;
- import com.qingjian.common.model.track.dto.TravelPairDTO;
- import com.qingjian.common.model.track.dto.TravelTimelineDTO;
- import com.qingjian.common.model.trans.entity.AcctOpenInfo;
- import com.qingjian.common.model.trans.entity.TransRecord;
- import com.qingjian.module.govern.serivce.GovernConfService;
- import com.qingjian.module.person.mapper.IntimacyMapper;
- import com.qingjian.module.person.mapper.PersonBasicInfoMapper;
- import com.qingjian.module.person.mapper.PersonGroupMapper;
- import com.qingjian.module.person.mapper.PersonGroupRelMapper;
- import com.qingjian.module.plat.service.CacheManageService;
- import com.qingjian.module.track.mapper.TrackTogetherTravelMapper;
- import com.qingjian.module.trans.mapper.AcctOpenInfoMapper;
- import com.qingjian.module.trans.mapper.TransRecordMapper;
- import lombok.extern.slf4j.Slf4j;
- import org.noear.solon.annotation.Inject;
- import org.noear.solon.annotation.Managed;
- import java.math.BigDecimal;
- import java.time.LocalDate;
- import java.time.format.DateTimeFormatter;
- import java.util.*;
- import java.util.concurrent.atomic.AtomicInteger;
- import java.util.function.Function;
- import java.util.stream.Collectors;
- import java.util.stream.IntStream;
- @Slf4j
- @Managed
- public class IntimacyService extends ServiceImpl<IntimacyMapper, Intimacy> {
- @Inject
- private IntimacyMapper intimacyMapper;
- @Inject
- private TransRecordMapper transRecordMapper;
- @Inject
- private PersonGroupMapper personGroupMapper;
- @Inject
- private PersonGroupRelMapper personGroupRelMapper;
- @Inject
- private AcctOpenInfoMapper acctOpenInfoMapper;
- @Inject
- private GovernConfService governConfService;
- @Inject
- private PersonBasicInfoMapper personBasicInfoMapper;
- @Inject
- private CacheManageService cacheManageService;
- @Inject
- private TrackTogetherTravelMapper trackTogetherTravelMapper;
- public List<Intimacy> dataList() {
- return this.list();
- }
- public void update(List<Intimacy> intimacyList) {
- if (CollUtil.isEmpty(intimacyList)) {
- return;
- }
- Integer totalWeight = intimacyList.stream().map(Intimacy::getWeight).reduce(0, Integer::sum);
- if (totalWeight > 100) {
- throw new ServerException("权重之和不能大于 100!");
- }
- intimacyMapper.updateById(intimacyList);
- cacheManageService.removeByPrefix("dp_intimacySummary_");
- }
- // ==================== 亲密度汇总 ====================
- public List<IntimacyDTO> intimacySummary(String personName) {
- List<IntimacyDTO> allBehaviors = new ArrayList<>();
- allBehaviors.addAll(bigTrans(personName));
- allBehaviors.addAll(specialDayTrans(personName));
- allBehaviors.addAll(samePhoneBinding(personName));
- allBehaviors.addAll(keepCall(personName));
- allBehaviors.addAll(nightCall(personName));
- allBehaviors.addAll(specialDayCall(personName));
- allBehaviors.addAll(keepTrans(personName));
- allBehaviors.addAll(travelTogether(personName));
- Map<String, List<IntimacyDTO>> groupedByOther = allBehaviors.stream()
- .filter(i -> StrUtil.isNotEmpty(i.getOtherName()))
- .collect(Collectors.groupingBy(IntimacyDTO::getOtherName));
- List<IntimacyDTO> results = new ArrayList<>();
- groupedByOther.forEach((otherName, behaviors) -> {
- if (CollUtil.isEmpty(behaviors)) {
- return;
- }
- int totalIntimacy = behaviors.stream().map(IntimacyDTO::getIntimacy).reduce(0, Integer::sum);
- List<String> behaviorLabels = behaviors.stream().map(IntimacyDTO::getIntimacyBehavior).distinct().collect(Collectors.toList());
- JSONObject detail = mergeDetail(behaviors);
- IntimacyDTO result = new IntimacyDTO();
- result.setOtherName(otherName);
- result.setIntimacy(totalIntimacy / 100);
- result.setIntimacyBehavior(StrUtil.join(",", behaviorLabels));
- result.setDetail(detail);
- results.add(result);
- });
- return results.stream()
- .filter(i -> i.getIntimacy() != 0)
- .sorted(Comparator.comparingInt(IntimacyDTO::getIntimacy).reversed())
- .toList();
- }
- private JSONObject mergeDetail(List<IntimacyDTO> behaviors) {
- return behaviors.stream()
- .map(IntimacyDTO::getDetail)
- .flatMap(d -> d.entrySet().stream())
- .collect(
- JSONObject::new,
- (jsonObj, entry) -> jsonObj.set(
- entry.getKey(),
- jsonObj.getInt(entry.getKey(), 0) + (Integer) entry.getValue()
- ),
- JSONObject::putAll
- );
- }
- // ==================== 大额转账 ====================
- public List<IntimacyDTO> bigTrans(String personName) {
- Intimacy config = getAndCheckIntimacy("大额转账");
- if (config == null) {
- return List.of();
- }
- int weight = config.getWeight();
- JSONObject scoreConfig = parseScores(config);
- Set<String> personCards = CaseDataCache.getPerCards(personName);
- if (CollUtil.isEmpty(personCards)) {
- return List.of();
- }
- List<TransRecord> records = transRecordMapper.selectListIntimacy(personCards, governConfService.getTransTableName());
- Map<String, List<TransRecord>> groupedByOther = records.stream()
- .collect(Collectors.groupingBy(record -> resolveOtherName(record.getOtherName(), record.getOtherCardNo())));
- List<IntimacyDTO> results = new ArrayList<>();
- groupedByOther.forEach((otherName, transRecords) -> {
- JSONObject detail = new JSONObject();
- int above50kCount = 0;
- int between10kAnd50kCount = 0;
- for (TransRecord record : transRecords) {
- if (record.getTransAmount().compareTo(new BigDecimal(50000)) >= 0) {
- above50kCount++;
- } else if (record.getTransAmount().compareTo(new BigDecimal(10000)) >= 0
- && record.getTransAmount().compareTo(new BigDecimal(50000)) < 0) {
- between10kAnd50kCount++;
- }
- }
- int score;
- if (above50kCount > 0) {
- score = weight * scoreConfig.getInt("大于等于5万");
- detail.set("大额交易次数", above50kCount + between10kAnd50kCount);
- } else if (between10kAnd50kCount > 0) {
- score = weight * scoreConfig.getInt("1万到5万");
- detail.set("大额交易次数", between10kAnd50kCount);
- } else {
- return;
- }
- results.add(buildIntimacyDTO(otherName, score, "大额转账", weight, detail));
- });
- return results;
- }
- // ==================== 特殊日期特殊转账 ====================
- public List<IntimacyDTO> specialDayTrans(String personName) {
- Intimacy config = getAndCheckIntimacy("特殊日期特殊转账");
- if (config == null) {
- return List.of();
- }
- int weight = config.getWeight();
- JSONObject scoreConfig = parseScores(config);
- Set<String> personCards = CaseDataCache.getPerCards(personName);
- if (CollUtil.isEmpty(personCards)) {
- return List.of();
- }
- List<SpecialDmDTO> records = intimacyMapper.specialDm(personName, personCards, governConfService.getTransTableName());
- Map<Integer, List<SpecialDmDTO>> countGroups = records.stream().collect(Collectors.groupingBy(SpecialDmDTO::getCount));
- List<List<SpecialDmDTO>> topGroups = countGroups.entrySet().stream()
- .sorted(Map.Entry.<Integer, List<SpecialDmDTO>>comparingByKey().reversed())
- .map(Map.Entry::getValue)
- .limit(20)
- .toList();
- return IntStream.range(0, topGroups.size())
- .mapToObj(i -> {
- int scoreTier = i < 5 ? 0 : i < 10 ? 1 : 2;
- int baseScore = scoreTier == 0 ? scoreConfig.getInt("交易次数top1~5")
- : scoreTier == 1 ? scoreConfig.getInt("交易次数top6~10")
- : scoreConfig.getInt("交易次数top11~top20");
- return topGroups.get(i).stream().map(record -> buildIntimacyDTO(
- record.getOtherName(),
- weight * baseScore,
- "特殊日期特殊转账",
- weight,
- buildDetail("特殊日期特殊转账", record.getCount())
- ));
- })
- .flatMap(Function.identity())
- .collect(Collectors.toList());
- }
- // ==================== 异卡同号 ====================
- public List<IntimacyDTO> samePhoneBinding(String personName) {
- Intimacy config = getAndCheckIntimacy("异卡同号");
- if (config == null) {
- return List.of();
- }
- int weight = config.getWeight();
- JSONObject scoreConfig = parseScores(config);
- PersonGroupRel groupRel = personGroupRelMapper.selectOne(Wrappers.lambdaQuery(PersonGroupRel.class)
- .eq(PersonGroupRel::getPersonName, personName));
- if (groupRel == null) {
- return List.of();
- }
- PersonGroup group = personGroupMapper.selectWithPersonsById(groupRel.getGroupId());
- String mainPersonName = null;
- List<PersonGroupRel> personList = group.getPersonList();
- Map<String, String> cardToPerson = new HashMap<>();
- for (PersonGroupRel rel : personList) {
- if (StrUtil.equals("主体", rel.getMainDesc())) {
- mainPersonName = rel.getPersonName();
- } else {
- Set<String> cards = CaseDataCache.getPerCards(rel.getPersonName());
- for (String card : cards) {
- cardToPerson.put(card, rel.getPersonName());
- }
- }
- }
- if (!StrUtil.equals(personName, mainPersonName)) {
- return List.of();
- }
- Set<String> mainPersonPhones = CaseDataCache.getPerPhones(mainPersonName);
- List<AcctOpenInfo> otherAccounts = acctOpenInfoMapper.selectList(new LambdaQueryWrapper<AcctOpenInfo>()
- .ne(AcctOpenInfo::getPersonName, mainPersonName)
- .isNotNull(AcctOpenInfo::getPersonName)
- .isNotNull(AcctOpenInfo::getPhone));
- List<AcctOpenInfo> matchedAccounts = otherAccounts.stream()
- .filter(account -> CollUtil.contains(mainPersonPhones, account.getPhone()))
- .filter(account -> cardToPerson.containsKey(account.getPersonCardNo()))
- .collect(Collectors.toList());
- List<IntimacyDTO> results = new ArrayList<>();
- if (CollUtil.isNotEmpty(matchedAccounts)) {
- Map<String, List<AcctOpenInfo>> groupedByName = matchedAccounts.stream()
- .filter(account -> !StrUtil.isBlank(account.getPersonName()))
- .collect(Collectors.groupingBy(AcctOpenInfo::getPersonName));
- groupedByName.forEach((ownerName, accounts) -> {
- IntimacyDTO result = new IntimacyDTO();
- result.setOtherName(ownerName);
- result.setIntimacyBehavior("异卡同号");
- List<String> matchedCards = accounts.stream().map(AcctOpenInfo::getPersonCardNo).distinct().collect(Collectors.toList());
- if (CollUtil.size(matchedCards) >= 2) {
- result.setIntimacy(weight * scoreConfig.getInt("绑定号码关联银行卡数量大于等于2张"));
- } else if (CollUtil.size(matchedCards) == 1) {
- result.setIntimacy(weight * scoreConfig.getInt("绑定号码关联银行卡数量等于1张"));
- } else {
- return;
- }
- JSONObject detail = new JSONObject();
- detail.set("异卡同号", CollUtil.size(matchedCards));
- result.setDetail(detail);
- results.add(result);
- });
- }
- return results;
- }
- // ==================== 频繁联系 ====================
- public List<IntimacyDTO> keepCall(String personName) {
- Intimacy config = intimacyMapper.selectOne(Wrappers.<Intimacy>lambdaQuery()
- .eq(Intimacy::getTags, "频繁联系").last("limit 1"));
- if (config == null) {
- return List.of();
- }
- JSONObject scoreConfig = parseScores(config);
- List<IntimacyDTO> results = new ArrayList<>();
- List<CallListDTO> callRecords = fetchCallList(personName);
- Map<Integer, List<CallListDTO>> countGroups = callRecords.stream()
- .collect(Collectors.groupingBy(CallListDTO::getCount));
- AtomicInteger index = new AtomicInteger(0);
- countGroups.forEach((count, records) -> {
- if (index.incrementAndGet() < 6) {
- records.forEach(record -> {
- int score = scoreConfig.getInt("联系次数top前5");
- results.add(buildIntimacyDTO(record.getOtherName(), config.getWeight() * score, "联系次数",
- config.getWeight(), buildDetail("联系次数", record.getCount())));
- });
- } else if (index.incrementAndGet() < 11) {
- records.forEach(record -> {
- int score = scoreConfig.getInt("联系次数top6~10");
- results.add(buildIntimacyDTO(record.getOtherName(), config.getWeight() * score, "联系次数",
- config.getWeight(), buildDetail("联系次数", record.getCount())));
- });
- } else if (index.incrementAndGet() < 21) {
- records.forEach(record -> {
- int score = scoreConfig.getInt("联系次数top11~20");
- results.add(buildIntimacyDTO(record.getOtherName(), config.getWeight() * score, "联系次数",
- config.getWeight(), buildDetail("联系次数", record.getCount())));
- });
- }
- });
- filterEmptyDetail(results);
- return results;
- }
- private List<CallListDTO> fetchCallList(String personName) {
- String callTableName = governConfService.getCallTableName();
- List<CallListDTO> records = intimacyMapper.selectCallAllListByName(personName, callTableName);
- return records.stream()
- .filter(a -> !ReUtil.isMatch("^(12|95|96|106)", a.getOtherPhone()))
- .collect(Collectors.toList());
- }
- // ==================== 特殊日期联系 ====================
- public List<IntimacyDTO> specialDayCall(String personName) {
- Intimacy config = getAndCheckIntimacy("特殊日期联系");
- if (config == null) {
- return List.of();
- }
- JSONObject scoreConfig = parseScores(config);
- Set<String> personPhones = CaseDataCache.getPerPhones(personName);
- if (CollUtil.isEmpty(personPhones)) {
- return List.of();
- }
- String callTableName = governConfService.getCallTableName();
- List<CallVoiceDTO> records = intimacyMapper.specialCallList(personPhones, callTableName);
- AtomicInteger index = new AtomicInteger(0);
- List<IntimacyDTO> results = records.stream()
- .sorted(Comparator.comparing(CallVoiceDTO::getVcCount, Comparator.reverseOrder()))
- .limit(20).map(record -> {
- int i = index.incrementAndGet();
- int weightedScore = calcSpecialDayScore(i, scoreConfig, config.getWeight());
- return buildIntimacyDTO(record.getOtherName(), weightedScore, "特殊日期通话",
- config.getWeight(), buildDetail("特殊日期通话次数", record.getVcCount()));
- }).collect(Collectors.toList());
- filterEmptyDetail(results);
- return results;
- }
- private int calcSpecialDayScore(int rank, JSONObject scoreConfig, int weight) {
- int score;
- if (rank < 6) {
- score = scoreConfig.getInt("联系次数top1~5", 0);
- } else if (rank < 11) {
- score = scoreConfig.getInt("联系次数top6~10", 0);
- } else if (rank < 21) {
- score = scoreConfig.getInt("联系次数top11~20", 0);
- } else {
- score = scoreConfig.getInt("联系次数在top20以后", 0);
- }
- return weight * score;
- }
- // ==================== 频繁交易 ====================
- public List<IntimacyDTO> keepTrans(String personName) {
- Set<String> personCards = CaseDataCache.getPerCards(personName);
- if (CollUtil.isEmpty(personCards)) {
- return List.of();
- }
- List<DealSummaryVO> summaries = intimacyMapper.dealCountSummary(personCards, governConfService.getTransTableName());
- if (CollUtil.isEmpty(summaries)) {
- return List.of();
- }
- Integer weight = null;
- Integer score1to5 = null;
- Integer score6to10 = null;
- Integer score11to20 = null;
- Integer score21plus = null;
- try {
- Intimacy config = intimacyMapper.selectOne(Wrappers.<Intimacy>lambdaQuery()
- .eq(Intimacy::getTags, "频繁交易").last("limit 1"));
- weight = config.getWeight();
- JSONObject scoreConfig = parseScores(config);
- score1to5 = scoreConfig.getInt("交易次数top1~5");
- score6to10 = scoreConfig.getInt("交易次数top6~10");
- score11to20 = scoreConfig.getInt("交易次数top11~top20");
- score21plus = scoreConfig.getInt("交易次数大于top20");
- } catch (Exception e) {
- log.error("亲密度 - 频繁交易配置信息获取", e);
- }
- int finalWeight = weight == null ? 15 : weight;
- int finalScore1to5 = score1to5 == null ? 100 : score1to5;
- int finalScore6to10 = score6to10 == null ? 60 : score6to10;
- int finalScore11to20 = score11to20 == null ? 40 : score11to20;
- int finalScore21plus = score21plus == null ? 0 : score21plus;
- Map<Integer, List<DealSummaryVO>> countGroups = summaries.stream()
- .collect(Collectors.groupingBy(DealSummaryVO::getDealCount, LinkedHashMap::new, Collectors.toList()));
- List<List<DealSummaryVO>> groupedSummaries = new ArrayList<>(countGroups.values());
- List<IntimacyDTO> results = new ArrayList<>();
- for (int i = 0; i < groupedSummaries.size(); i++) {
- List<DealSummaryVO> sameCountSummaries = groupedSummaries.get(i);
- for (DealSummaryVO summary : sameCountSummaries) {
- IntimacyDTO result = new IntimacyDTO();
- String name = summary.getOtherName();
- if (StrUtil.isNotEmpty(name)) {
- result.setOtherName(name);
- int score = i < 5 ? finalScore1to5
- : i < 10 ? finalScore6to10
- : i < 20 ? finalScore11to20 : finalScore21plus;
- result.setIntimacy(score * finalWeight);
- result.setIntimacyBehavior(buildBehaviorLabel("频繁交易", finalWeight));
- result.setDetail(buildDetail("交易次数", summary.getDealCount()));
- results.add(result);
- }
- }
- }
- return results.stream().filter(e -> e.getIntimacy() > 0).collect(Collectors.toList());
- }
- // ==================== 同交通工具出行 ====================
- public List<IntimacyDTO> travelTogether(String personName) {
- List<TravelDataDTO> serviceRecords = new ArrayList<>();
- Map<String, List<TravelDataDTO>> tripMatchMap = new HashMap<>();
- PersonBasicInfo personBasicInfo = personBasicInfoMapper.selectOne(Wrappers.<PersonBasicInfo>lambdaQuery()
- .eq(PersonBasicInfo::getPersonName, personName), false);
- if (personBasicInfo != null && StrUtil.isNotEmpty(personBasicInfo.getPersonCertNo())) {
- List<TravelDataDTO> flightTrainRecords = intimacyMapper.travelsTrainFlight(personName, personBasicInfo.getPersonCertNo());
- if (CollUtil.isNotEmpty(flightTrainRecords)) {
- List<TravelDataDTO> mappedRecords = flightTrainRecords.stream().map(record -> {
- TravelDataDTO dto = new TravelDataDTO();
- dto.setId(record.getId());
- dto.setPersonName(personName);
- dto.setCompanionName(record.getCompanionName());
- dto.setCount(1);
- return dto;
- }).toList();
- serviceRecords.addAll(mappedRecords);
- }
- }
- List<PersonBasicInfo> allPersons = personBasicInfoMapper.selectList(Wrappers.emptyWrapper());
- List<TravelDataDTO> travelRecords = trackTogetherTravelMapper.travels(null, null, personName);
- Map<String, List<TravelDataDTO>> timeTripGroup = travelRecords.stream().collect(Collectors.groupingBy(item ->
- String.format("%s_%s",
- StrUtil.isBlank(item.getTripDateTime()) ? "" : item.getTripDateTime().trim(),
- StrUtil.isBlank(item.getTripNo()) ? "" : item.getTripNo().trim())));
- for (PersonBasicInfo otherPerson : allPersons) {
- if (Objects.equals(otherPerson.getPersonName(), personName)) {
- continue;
- }
- List<TravelDataDTO> otherTravelRecords = trackTogetherTravelMapper.travels(null, null, otherPerson.getPersonName());
- if (CollUtil.isEmpty(otherTravelRecords)) {
- continue;
- }
- otherTravelRecords.forEach(record -> {
- List<TravelDataDTO> matchedRecords = timeTripGroup.get(String.format("%s_%s",
- StrUtil.isBlank(record.getTripDateTime()) ? "" : record.getTripDateTime().trim(),
- StrUtil.isBlank(record.getTripNo()) ? "" : record.getTripNo().trim()));
- if (CollUtil.isEmpty(matchedRecords)) {
- return;
- }
- AtomicInteger matchCount = new AtomicInteger();
- TravelDataDTO dto = new TravelDataDTO();
- matchCount.set(matchedRecords.size());
- dto.setId(record.getId());
- dto.setPersonName(personName);
- dto.setCompanionName(otherPerson.getPersonName());
- dto.setCount(matchCount.get());
- List<TravelDataDTO> matchedGroup = new ArrayList<>(matchedRecords);
- matchedGroup.add(record);
- tripMatchMap.put(String.format("%s_%s",
- StrUtil.isBlank(record.getTripDateTime()) ? "" : record.getTripDateTime().trim(),
- StrUtil.isBlank(record.getTripNo()) ? "" : record.getTripNo().trim()), matchedGroup);
- });
- }
- List<TravelTimelineDTO> timelines = buildTravelTimeline(tripMatchMap, serviceRecords);
- Integer weight = null;
- Integer score10Plus = null;
- Integer score5to9 = null;
- Integer score1to4 = null;
- try {
- Intimacy config = intimacyMapper.selectOne(Wrappers.<Intimacy>lambdaQuery()
- .eq(Intimacy::getTags, "同交通工具出行").last("limit 1"));
- weight = config.getWeight();
- JSONObject scoreConfig = parseScores(config);
- score10Plus = scoreConfig.getInt("同行次数大于等于10次");
- score5to9 = scoreConfig.getInt("同行次数5~9次");
- score1to4 = scoreConfig.getInt("同行次数1~4次");
- } catch (Exception e) {
- log.error("亲密度 - 同交通工具出行配置信息获取", e);
- }
- int finalScore10Plus = score10Plus == null ? 100 : score10Plus;
- int finalScore5to9 = score5to9 == null ? 80 : score5to9;
- int finalScore1to4 = score1to4 == null ? 70 : score1to4;
- int finalWeight = weight == null ? 5 : weight;
- List<IntimacyDTO> results = timelines.stream().map(timeline -> {
- IntimacyDTO result = new IntimacyDTO();
- if (timeline.getPersonName().equals(personName)) {
- result.setOtherName(timeline.getCompanionName());
- } else {
- result.setOtherName(timeline.getPersonName());
- }
- int score = timeline.getCount() >= 10 ? finalScore10Plus
- : timeline.getCount() >= 5 ? finalScore5to9
- : timeline.getCount() >= 1 ? finalScore1to4 : 0;
- result.setIntimacy(score * finalWeight);
- result.setIntimacyBehavior(buildBehaviorLabel("同交通工具出行", finalWeight));
- result.setDetail(buildDetail("同行次数", timeline.getCount()));
- return result;
- }).toList();
- return results.stream().filter(e -> e.getIntimacy() > 0).collect(Collectors.toList());
- }
- private List<TravelTimelineDTO> buildTravelTimeline(Map<String, List<TravelDataDTO>> tripMatchMap,
- List<TravelDataDTO> serviceRecords) {
- List<TravelTimelineDTO> results = new ArrayList<>();
- tripMatchMap.forEach((key, matchedGroup) -> {
- if (CollUtil.isEmpty(matchedGroup) || matchedGroup.size() < 2) {
- return;
- }
- TravelTimelineDTO timelineDTO = new TravelTimelineDTO();
- TravelDataDTO first = matchedGroup.get(0);
- TravelDataDTO second = matchedGroup.get(1);
- timelineDTO.setPersonName(first.getPersonName());
- timelineDTO.setPersonName(second.getPersonName());
- timelineDTO.setCount(1);
- timelineDTO.setIdList(matchedGroup.stream().map(TravelDataDTO::getId).collect(Collectors.toList()));
- results.add(timelineDTO);
- });
- Map<TravelPairDTO, List<TravelDataDTO>> pairGroup = serviceRecords.stream()
- .collect(Collectors.groupingBy(item -> TravelPairDTO.create(item.getPersonName(), item.getCompanionName())));
- pairGroup.forEach((pair, pairRecords) -> {
- if (CollUtil.isEmpty(pairRecords)) {
- return;
- }
- TravelTimelineDTO timelineDTO = new TravelTimelineDTO();
- timelineDTO.setPersonName(pair.getPersonName());
- timelineDTO.setCompanionName(pair.getCompanionName());
- timelineDTO.setCount(pairRecords.size());
- timelineDTO.setIdList(pairRecords.stream().map(TravelDataDTO::getId).collect(Collectors.toList()));
- results.add(timelineDTO);
- });
- return aggregateTravelTimeline(results);
- }
- private List<TravelTimelineDTO> aggregateTravelTimeline(List<TravelTimelineDTO> timelines) {
- List<TravelTimelineDTO> results = new ArrayList<>();
- Map<String, List<TravelTimelineDTO>> pairGroup = timelines.stream()
- .collect(Collectors.groupingBy(t -> t.getPersonName() + "-" + t.getCompanionName()));
- pairGroup.forEach((key, items) -> {
- List<String> split = StrUtil.split(key, "-");
- TravelTimelineDTO merged = new TravelTimelineDTO();
- merged.setPersonName(split.get(0));
- merged.setCompanionName(split.get(1));
- Integer totalCount = items.stream().map(TravelTimelineDTO::getCount).reduce(0, Integer::sum);
- List<String> ids = items.stream().flatMap(t -> t.getIdList().stream()).collect(Collectors.toList());
- merged.setCount(totalCount);
- merged.setIdList(ids);
- results.add(merged);
- });
- return results;
- }
- // ==================== 夜间通话 ====================
- public List<IntimacyDTO> nightCall(String personName) {
- Intimacy config = getAndCheckIntimacy("夜间通话");
- if (config == null) {
- return List.of();
- }
- JSONObject scoreConfig = parseScores(config);
- List<NightVoiceDTO> nightCallRecords = new ArrayList<>();
- buildNightCallRecords(personName, nightCallRecords);
- if (CollUtil.isEmpty(nightCallRecords)) {
- return List.of();
- }
- List<IntimacyDTO> results = new ArrayList<>();
- NightVoiceDTO nightCall = nightCallRecords.getFirst();
- List<NightVoiceTop5DTO> topRecords = nightCall.getTop5List();
- Map<Integer, List<NightVoiceTop5DTO>> countGroups = topRecords.stream()
- .collect(Collectors.groupingBy(NightVoiceTop5DTO::getCmnCount));
- AtomicInteger index = new AtomicInteger(0);
- countGroups.forEach((count, records) -> {
- if (index.incrementAndGet() < 6) {
- records.forEach(record -> {
- int score = scoreConfig.getInt("夜间通话次数top前5");
- results.add(buildIntimacyDTO(record.getOtherPhone(), config.getWeight() * score, "夜间通话次数",
- config.getWeight(), buildDetail("夜间通话次数", record.getCmnCount())));
- });
- } else if (index.incrementAndGet() < 11) {
- records.forEach(record -> {
- int score = scoreConfig.getInt("夜间通话次数top6~10");
- results.add(buildIntimacyDTO(record.getOtherPhone(), config.getWeight() * score, "夜间通话次数",
- config.getWeight(), buildDetail("夜间通话次数", record.getCmnCount())));
- });
- } else if (index.incrementAndGet() < 21) {
- records.forEach(record -> {
- int score = scoreConfig.getInt("夜间通话次数top11~20");
- results.add(buildIntimacyDTO(record.getOtherPhone(), config.getWeight() * score, "夜间通话次数",
- config.getWeight(), buildDetail("夜间通话次数", record.getCmnCount())));
- });
- }
- });
- filterEmptyDetail(results);
- return results;
- }
- // ==================== 工具方法 ====================
- private Intimacy getAndCheckIntimacy(String tags) {
- return intimacyMapper.selectOne(new LambdaQueryWrapper<Intimacy>()
- .eq(Intimacy::getTags, tags));
- }
- private JSONObject parseScores(Intimacy intimacy) {
- return JSONUtil.parseObj(intimacy.getScores());
- }
- private String resolveOtherName(String otherName, String fallback) {
- return StrUtil.emptyToDefault(otherName, fallback);
- }
- private String buildBehaviorLabel(String behavior, int weight) {
- return behavior + "-" + "(权重" + weight + "%)";
- }
- private JSONObject buildDetail(String key, Object value) {
- JSONObject detail = new JSONObject();
- detail.set(key, value);
- return detail;
- }
- private IntimacyDTO buildIntimacyDTO(String otherName, int intimacy, String behavior, int weight, JSONObject detail) {
- IntimacyDTO dto = new IntimacyDTO();
- dto.setOtherName(otherName);
- dto.setIntimacy(intimacy);
- dto.setIntimacyBehavior(buildBehaviorLabel(behavior, weight));
- dto.setDetail(detail);
- return dto;
- }
- private void filterEmptyDetail(List<IntimacyDTO> dtos) {
- for (IntimacyDTO dto : dtos) {
- JSONObject entries = new JSONObject();
- dto.getDetail().forEach((k, v) -> {
- if (!v.equals(0)) {
- entries.set(k, v);
- }
- });
- dto.setDetail(entries);
- }
- dtos.removeIf(a -> a.getDetail().isEmpty());
- }
- private void buildNightCallRecords(String personName, List<NightVoiceDTO> nightCallList) {
- String callTableName = governConfService.getCallTableName();
- List<NightVoiceTempDTO> rawRecords = intimacyMapper.selectAllDataByName(personName, callTableName);
- Map<String, List<NightVoiceTempDTO>> groupedByName = rawRecords.stream()
- .filter(a -> !ReUtil.isMatch("^(12|95|96|106)", a.getOtherName()))
- .collect(Collectors.groupingBy(NightVoiceTempDTO::getOtherName));
- for (Map.Entry<String, List<NightVoiceTempDTO>> nameEntry : groupedByName.entrySet()) {
- NightVoiceDTO nightCall = new NightVoiceDTO();
- List<NightVoiceTop5DTO> topRecords = new ArrayList<>();
- List<NightVoiceTempDTO> nameRecords = nameEntry.getValue();
- Map<String, List<NightVoiceTempDTO>> groupedByPhone = nameRecords.stream()
- .collect(Collectors.groupingBy(NightVoiceTempDTO::getOtherName));
- for (Map.Entry<String, List<NightVoiceTempDTO>> phoneEntry : groupedByPhone.entrySet()) {
- NightVoiceTop5DTO topRecord = new NightVoiceTop5DTO();
- String phone = phoneEntry.getKey();
- List<NightVoiceTempDTO> phoneRecords = phoneEntry.getValue();
- List<String> yearMonths = phoneRecords.stream().map(record -> {
- LocalDate callDate = record.getCallDate();
- return callDate.format(DateTimeFormatter.ofPattern("yyyy-MM"));
- }).distinct().sorted().toList();
- topRecord.setOtherPhone(phone);
- topRecord.setCmnCount(phoneRecords.size());
- topRecord.setTotalMonth(yearMonths.size());
- topRecords.add(topRecord);
- }
- topRecords = topRecords.stream()
- .filter(record -> record.getTotalMonth() >= (Long) 2L)
- .sorted(Comparator.comparing(NightVoiceTop5DTO::getCmnCount, Comparator.reverseOrder()))
- .limit(20)
- .collect(Collectors.toList());
- nightCall.setTop5List(topRecords);
- nightCallList.add(nightCall);
- }
- }
- }
|