zayac-admin/zayac-admin-agent/src/main/java/com/zayac/admin/service/DepositService.java

246 lines
13 KiB
Java

/*
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.zayac.admin.service;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.StrUtil;
import com.zayac.admin.common.enums.DisEnableStatusEnum;
import com.zayac.admin.constant.ApiPathConstants;
import com.zayac.admin.req.MemberDetailsReq;
import com.zayac.admin.req.PayRecordsListReq;
import com.zayac.admin.req.team.TeamMemberListReq;
import com.zayac.admin.resp.Member;
import com.zayac.admin.resp.MemberPagination;
import com.zayac.admin.resp.Pagination;
import com.zayac.admin.resp.PayRecord;
import com.zayac.admin.resp.team.Team;
import com.zayac.admin.resp.team.TeamAccount;
import com.zayac.admin.resp.team.TeamAccountWithChange;
import com.zayac.admin.system.model.resp.AccountResp;
import com.zayac.admin.system.model.resp.UserWithRolesAndAccountsResp;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.ParameterizedTypeReference;
import org.springframework.stereotype.Service;
import top.continew.starter.core.exception.BusinessException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
@Slf4j
@Service
@RequiredArgsConstructor
public class DepositService {
private final CompletableFutureWebClientService completableFutureWebClientService;
private final TelegramMessageService telegramMessageService;
private static final String BOT_TOKEN = "6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto";
private static final Long TELEGRAM_CHAT_ID = 6054562838L;
public CompletableFuture<Void> processDeposits(UserWithRolesAndAccountsResp ministerUser,
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap,
AccountResp account,
Team currentTeam,
Team previousTeam,
LocalDate nowDate,
LocalDateTime nowDateTime,
Executor asyncTaskExecutor) {
if (previousTeam == null || currentTeam.getFirstDepositNum() <= previousTeam.getFirstDepositNum()) {
return CompletableFuture.completedFuture(null);
}
return processDepositRecords(ministerUser, accountUsernameToUserMap, account, currentTeam, previousTeam, nowDate, nowDateTime, asyncTaskExecutor);
}
private CompletableFuture<Void> processDepositRecords(UserWithRolesAndAccountsResp ministerUser,
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap,
AccountResp account,
Team currentTeam,
Team previousTeam,
LocalDate nowDate,
LocalDateTime nowDateTime,
Executor asyncTaskExecutor) {
List<TeamAccountWithChange> hasNewDepositAccounts = findChangedTeamAccount(previousTeam, currentTeam).stream()
.filter(teamAccountWithChange -> teamAccountWithChange.getNewDepositNum() > 0)
.toList();
List<CompletableFuture<Void>> allTasks = hasNewDepositAccounts.stream()
.map(accountWithChange -> processAccountChanges(accountWithChange, ministerUser, accountUsernameToUserMap, account, nowDate, nowDateTime, asyncTaskExecutor))
.toList();
return CompletableFuture.allOf(allTasks.toArray(new CompletableFuture[0]));
}
private CompletableFuture<Void> processAccountChanges(TeamAccountWithChange accountWithChange,
UserWithRolesAndAccountsResp ministerUser,
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap,
AccountResp account,
LocalDate nowDate,
LocalDateTime nowDateTime,
Executor asyncTaskExecutor) {
PayRecordsListReq req = createPayRecordsListReq(accountWithChange.getAgentName(), nowDate);
CompletableFuture<Pagination<List<PayRecord>>> paginationCompletableFuture = completableFutureWebClientService
.fetchDataForAccount(account, ApiPathConstants.PAY_RECORDS_LIST_URL, req, new ParameterizedTypeReference<>() {
});
StringBuilder depositResults = new StringBuilder();
AtomicInteger depositCounter = new AtomicInteger(0);
return paginationCompletableFuture.thenApply(Pagination::getList)
.thenComposeAsync(payRecords -> processPayRecords(payRecords, accountWithChange, account, nowDate, nowDateTime, depositResults, depositCounter, asyncTaskExecutor), asyncTaskExecutor)
.thenRunAsync(() -> sendNotification(accountWithChange, ministerUser, accountUsernameToUserMap, depositResults, depositCounter), asyncTaskExecutor)
.exceptionally(ex -> {
log.error("Error processing account changes for agent {}: {}", accountWithChange.getAgentName(), ex
.getMessage());
return null;
});
}
private PayRecordsListReq createPayRecordsListReq(String agentName, LocalDate nowDate) {
return PayRecordsListReq.builder()
.startDate(nowDate)
.endDate(nowDate)
.pageSize(100)
.payState(2)
.agentName(agentName)
.build();
}
private CompletableFuture<Void> processPayRecords(List<PayRecord> payRecords,
TeamAccountWithChange accountWithChange,
AccountResp account,
LocalDate nowDate,
LocalDateTime nowDateTime,
StringBuilder depositResults,
AtomicInteger depositCounter,
Executor asyncTaskExecutor) {
Map<String, PayRecord> earliestPayRecords = payRecords.stream()
.filter(record -> record.getCreatedAt().isAfter(nowDateTime.minusHours(1)))
.collect(Collectors.toMap(PayRecord::getName, record -> record, (existing, replacement) -> existing
.getCreatedAt()
.isBefore(replacement.getCreatedAt()) ? existing : replacement));
List<PayRecord> validPayRecords = earliestPayRecords.values().stream().toList();
List<CompletableFuture<Void>> fetchMemberFutures = validPayRecords.stream()
.map(payRecord -> fetchMemberDetails(account, payRecord.getName(), nowDate, asyncTaskExecutor)
.thenAcceptAsync(member -> processMemberDetails(member, payRecord, accountWithChange, depositResults, depositCounter), asyncTaskExecutor)
.exceptionally(ex -> {
log.error("Error fetching details for member {}: {}", payRecord.getName(), ex.getMessage());
return null;
}))
.toList();
return CompletableFuture.allOf(fetchMemberFutures.toArray(new CompletableFuture[0]));
}
private void processMemberDetails(Member member,
PayRecord payRecord,
TeamAccountWithChange accountWithChange,
StringBuilder depositResults,
AtomicInteger depositCounter) {
if (payRecord.getCreatedAt().equals(member.getFirstPayAt()) && depositCounter
.getAndIncrement() < accountWithChange.getNewDepositNum()) {
depositResults.append(telegramMessageService.buildDepositResultsMessage(member.getName(), payRecord
.getScoreAmount()));
}
}
private void sendNotification(TeamAccountWithChange accountWithChange,
UserWithRolesAndAccountsResp ministerUser,
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap,
StringBuilder depositResults,
AtomicInteger depositCounter) {
if (depositCounter.get() > 0) {
String notification = telegramMessageService.buildDepositMessage(accountWithChange
.getAgentName(), accountWithChange.getNewDepositNum(), depositResults.toString(), accountWithChange
.getFirstDepositNum());
var currUser = accountUsernameToUserMap.get(accountWithChange.getAgentName());
if (currUser != null && DisEnableStatusEnum.ENABLE.equals(currUser.getNeedNotify())) {
String botToken = StrUtil.isEmpty(currUser.getBotToken())
? ministerUser.getBotToken()
: currUser.getBotToken();
telegramMessageService.sendMessage(botToken, currUser.getRegAndDepIds(), notification);
}
telegramMessageService.sendMessage(BOT_TOKEN, TELEGRAM_CHAT_ID, notification);
}
}
private CompletableFuture<Member> fetchMemberDetails(AccountResp account,
String name,
LocalDate nowDate,
Executor asyncTaskExecutor) {
TeamMemberListReq memberListReq = TeamMemberListReq.builder()
.name(name)
.startDate(nowDate)
.endDate(nowDate)
.status(1)
.build();
CompletableFuture<MemberPagination<List<Member>>> memberFuture = completableFutureWebClientService
.fetchDataForAccount(account, ApiPathConstants.MEMBER_TEAM_LIST_URL, memberListReq, new ParameterizedTypeReference<>() {
});
return memberFuture.thenApplyAsync(MemberPagination::getList, asyncTaskExecutor)
.thenApplyAsync(list -> list.stream()
.findFirst()
.orElseThrow(() -> new BusinessException("没有找到匹配的成员信息")), asyncTaskExecutor)
.thenComposeAsync(member -> fetchDetailedMemberInfo(account, member.getId(), nowDate), asyncTaskExecutor);
}
private CompletableFuture<Member> fetchDetailedMemberInfo(AccountResp account, Long memberId, LocalDate nowDate) {
MemberDetailsReq detailsReq = MemberDetailsReq.builder()
.id(memberId)
.startDate(nowDate)
.endDate(nowDate)
.build();
return completableFutureWebClientService
.fetchDataForAccount(account, ApiPathConstants.MEMBER_DETAIL_URL, detailsReq, new ParameterizedTypeReference<>() {
});
}
private List<TeamAccountWithChange> findChangedTeamAccount(Team prevTeam, Team currTeam) {
Map<Long, TeamAccount> team2AccountMap = currTeam.getList()
.stream()
.collect(Collectors.toMap(TeamAccount::getId, account -> account));
return prevTeam.getList()
.stream()
.filter(account1 -> team2AccountMap.containsKey(account1.getId()))
.map(account1 -> {
TeamAccount account2 = team2AccountMap.get(account1.getId());
TeamAccountWithChange changedAccount = new TeamAccountWithChange();
BeanUtil.copyProperties(account2, changedAccount);
if (account1.getFirstDepositNum() != account2.getFirstDepositNum()) {
changedAccount.setNewDepositNum(account2.getFirstDepositNum() - account1.getFirstDepositNum());
}
if (account1.getSubMemberNum() != account2.getSubMemberNum()) {
changedAccount.setNewRegisterNum(account2.getSubMemberNum() - account1.getSubMemberNum());
}
return changedAccount;
})
.collect(Collectors.toList());
}
}