重构了一系列方法,现在处理速度更快更准确了
This commit is contained in:
parent
c9fd364428
commit
9a8890996f
@ -21,7 +21,6 @@ import java.time.LocalDate;
|
||||
import java.time.LocalDateTime;
|
||||
import java.math.BigDecimal;
|
||||
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
|
@ -42,7 +42,6 @@ import java.util.concurrent.Executor;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
|
||||
/**
|
||||
* 检测注册跟新增的定时任务
|
||||
*/
|
||||
@ -50,7 +49,7 @@ import java.util.stream.Collectors;
|
||||
@Slf4j
|
||||
@Component
|
||||
@RequiredArgsConstructor
|
||||
@Profile("dev")
|
||||
@Profile("prod")
|
||||
public class CheckRegAndDep {
|
||||
|
||||
private final TeamService teamService;
|
||||
@ -70,16 +69,20 @@ public class CheckRegAndDep {
|
||||
List<DeptUsersResp> deptWithUsersAndAccounts = deptService.getDeptWithUsersAndAccounts(MINISTER_ROLE_CODE);
|
||||
deptWithUsersAndAccounts.forEach(dept -> {
|
||||
//根据用户角色对部门用户进行分组
|
||||
Map<String, List<UserWithRolesAndAccountsResp>> usersByRole = dept.getUsers().stream()
|
||||
.flatMap(user -> user.getRoles().stream()
|
||||
.map(role -> new AbstractMap.SimpleEntry<>(role.getCode(), user)))
|
||||
.collect(Collectors.groupingByConcurrent(Map.Entry::getKey,
|
||||
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
|
||||
Map<String, List<UserWithRolesAndAccountsResp>> usersByRole = dept.getUsers()
|
||||
.stream()
|
||||
.flatMap(user -> user.getRoles()
|
||||
.stream()
|
||||
.map(role -> new AbstractMap.SimpleEntry<>(role.getCode(), user)))
|
||||
.collect(Collectors.groupingByConcurrent(Map.Entry::getKey, Collectors
|
||||
.mapping(Map.Entry::getValue, Collectors.toList())));
|
||||
// 获取所有账号的username与用户的映射
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap = dept.getUsers().stream()
|
||||
.flatMap(user -> user.getAccounts().stream()
|
||||
.map(account -> new AbstractMap.SimpleEntry<>(account.getUsername(), user)))
|
||||
.collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap = dept.getUsers()
|
||||
.stream()
|
||||
.flatMap(user -> user.getAccounts()
|
||||
.stream()
|
||||
.map(account -> new AbstractMap.SimpleEntry<>(account.getUsername(), user)))
|
||||
.collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
var ministerUser = usersByRole.get(MINISTER_ROLE_CODE).get(0);
|
||||
processUser(ministerUser, accountUsernameToUserMap, nowDate, nowDateTime).join();
|
||||
@ -87,12 +90,15 @@ public class CheckRegAndDep {
|
||||
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> processUser(UserWithRolesAndAccountsResp minister, Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap, LocalDate nowDate, LocalDateTime nowDateTime) {
|
||||
private CompletableFuture<Void> processUser(UserWithRolesAndAccountsResp minister,
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap,
|
||||
LocalDate nowDate,
|
||||
LocalDateTime nowDateTime) {
|
||||
//根据总线用户的账号查询数据
|
||||
List<AccountResp> accounts = minister.getAccounts();
|
||||
List<CompletableFuture<Void>> futures = accounts.stream()
|
||||
.map(account -> processTeamAccount(minister, accountUsernameToUserMap, account, nowDate, nowDateTime))
|
||||
.toList();
|
||||
.map(account -> processTeamAccount(minister, accountUsernameToUserMap, account, nowDate, nowDateTime))
|
||||
.toList();
|
||||
|
||||
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
|
||||
}
|
||||
@ -103,9 +109,9 @@ public class CheckRegAndDep {
|
||||
LocalDate nowDate,
|
||||
LocalDateTime nowDateTime) {
|
||||
TeamInfoReq teamInfoReq = TeamInfoReq.builder()
|
||||
.startDate(nowDateTime.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN))
|
||||
.endDate(nowDateTime.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX))
|
||||
.build();
|
||||
.startDate(nowDateTime.with(TemporalAdjusters.firstDayOfMonth()).with(LocalTime.MIN))
|
||||
.endDate(nowDateTime.with(TemporalAdjusters.lastDayOfMonth()).with(LocalTime.MAX))
|
||||
.build();
|
||||
CompletableFuture<Team> teamFuture = teamService.getLatestTeamInfoAsync(account, teamInfoReq);
|
||||
Team prevTeamInfo = teamService.getPreviousTeamInfo(account);
|
||||
|
||||
@ -113,16 +119,16 @@ public class CheckRegAndDep {
|
||||
log.info("Previous Team Info: {}", prevTeamInfo);
|
||||
log.info("Current Team Info: {}", currentTeamInfo);
|
||||
Map<String, TeamAccount> teamAccountMap = currentTeamInfo.getList()
|
||||
.stream()
|
||||
.collect(Collectors.toMap(TeamAccount::getAgentName, Function.identity()));
|
||||
.stream()
|
||||
.collect(Collectors.toMap(TeamAccount::getAgentName, Function.identity()));
|
||||
|
||||
CompletableFuture<Void> registrationProcess = registrationService
|
||||
.processRegistration(minister, account, accountUsernameToUserMap, teamAccountMap, currentTeamInfo, prevTeamInfo, nowDate, asyncTaskExecutor)
|
||||
.thenRunAsync(() -> log.info("Registration process completed"), asyncTaskExecutor);
|
||||
.processRegistration(minister, account, accountUsernameToUserMap, teamAccountMap, currentTeamInfo, prevTeamInfo, nowDate, asyncTaskExecutor)
|
||||
.thenRunAsync(() -> log.info("Registration process completed"), asyncTaskExecutor);
|
||||
|
||||
CompletableFuture<Void> depositProcess = depositService
|
||||
.processDeposits(minister, accountUsernameToUserMap, account, currentTeamInfo, prevTeamInfo, nowDate, nowDateTime, asyncTaskExecutor)
|
||||
.thenRunAsync(() -> log.info("Deposit process completed"), asyncTaskExecutor);
|
||||
.processDeposits(minister, accountUsernameToUserMap, account, currentTeamInfo, prevTeamInfo, nowDate, nowDateTime, asyncTaskExecutor)
|
||||
.thenRunAsync(() -> log.info("Deposit process completed"), asyncTaskExecutor);
|
||||
|
||||
return CompletableFuture.allOf(registrationProcess, depositProcess).thenRunAsync(() -> {
|
||||
teamService.updateTeamInfo(account, currentTeamInfo);
|
||||
|
@ -88,11 +88,13 @@ public class DailyReport {
|
||||
List<DeptUsersResp> deptWithUsersAndAccounts = deptService.getDeptWithUsersAndAccounts(MINISTER_ROLE_CODE);
|
||||
deptWithUsersAndAccounts.forEach(dept -> {
|
||||
//根据用户角色对部门用户进行分组
|
||||
Map<String, List<UserWithRolesAndAccountsResp>> usersByRole = dept.getUsers().stream()
|
||||
.flatMap(user -> user.getRoles().stream()
|
||||
Map<String, List<UserWithRolesAndAccountsResp>> usersByRole = dept.getUsers()
|
||||
.stream()
|
||||
.flatMap(user -> user.getRoles()
|
||||
.stream()
|
||||
.map(role -> new AbstractMap.SimpleEntry<>(role.getCode(), user)))
|
||||
.collect(Collectors.groupingByConcurrent(Map.Entry::getKey,
|
||||
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
|
||||
.collect(Collectors.groupingByConcurrent(Map.Entry::getKey, Collectors
|
||||
.mapping(Map.Entry::getValue, Collectors.toList())));
|
||||
|
||||
var ministerUser = usersByRole.get(MINISTER_ROLE_CODE).get(0);
|
||||
//获取账号不为空的用户
|
||||
@ -101,7 +103,6 @@ public class DailyReport {
|
||||
sendDailyReport(nowDate, nowDate.atStartOfDay(), nowDateTime, ministerUser, assistants, deptUsers);
|
||||
});
|
||||
|
||||
|
||||
}
|
||||
|
||||
@Scheduled(cron = "0 15 0 * * ?")
|
||||
@ -112,14 +113,18 @@ public class DailyReport {
|
||||
|
||||
deptWithUsersAndAccounts.forEach(dept -> {
|
||||
//根据用户角色对部门用户进行分组
|
||||
Map<String, List<UserWithRolesAndAccountsResp>> usersByRole = dept.getUsers().stream()
|
||||
.flatMap(user -> user.getRoles().stream()
|
||||
Map<String, List<UserWithRolesAndAccountsResp>> usersByRole = dept.getUsers()
|
||||
.stream()
|
||||
.flatMap(user -> user.getRoles()
|
||||
.stream()
|
||||
.map(role -> new AbstractMap.SimpleEntry<>(role.getCode(), user)))
|
||||
.collect(Collectors.groupingByConcurrent(Map.Entry::getKey,
|
||||
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
|
||||
.collect(Collectors.groupingByConcurrent(Map.Entry::getKey, Collectors
|
||||
.mapping(Map.Entry::getValue, Collectors.toList())));
|
||||
// 获取所有账号的username与用户的映射
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap = dept.getUsers().stream()
|
||||
.flatMap(user -> user.getAccounts().stream()
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap = dept.getUsers()
|
||||
.stream()
|
||||
.flatMap(user -> user.getAccounts()
|
||||
.stream()
|
||||
.map(account -> new AbstractMap.SimpleEntry<>(account.getUsername(), user)))
|
||||
.collect(Collectors.toConcurrentMap(Map.Entry::getKey, Map.Entry::getValue));
|
||||
|
||||
@ -128,15 +133,16 @@ public class DailyReport {
|
||||
var deptUsers = dept.getUsers().stream().filter(user -> CollUtil.isEmpty(user.getAccounts())).toList();
|
||||
var assistants = usersByRole.get(ASSISTANT_ROLE_CODE);
|
||||
|
||||
sendDailyReport(yesterday, yesterday.atStartOfDay(), LocalDateTime.of(yesterday, LocalTime.MAX), ministerUser, assistants, deptUsers);
|
||||
sendDailyReport(yesterday, yesterday.atStartOfDay(), LocalDateTime
|
||||
.of(yesterday, LocalTime.MAX), ministerUser, assistants, deptUsers);
|
||||
getPayFailedMember(ministerUser, accountUsernameToUserMap, yesterday);
|
||||
saveData(ministerUser, deptUsers, yesterday);
|
||||
});
|
||||
}
|
||||
|
||||
// 一小时发送一次
|
||||
//@Scheduled(cron = "0 0 * * * ?")
|
||||
@Scheduled(fixedDelay = 6000L)
|
||||
@Scheduled(cron = "0 0 * * * ?")
|
||||
//@Scheduled(fixedDelay = 6000L)
|
||||
public void generateTeamReportTask() {
|
||||
LocalDateTime nowDateTime = LocalDateTime.now();
|
||||
LocalDate nowDate = LocalDate.now();
|
||||
@ -145,30 +151,32 @@ public class DailyReport {
|
||||
|
||||
deptWithUsersAndAccounts.forEach(dept -> {
|
||||
//根据用户角色对部门用户进行分组
|
||||
Map<String, List<UserWithRolesAndAccountsResp>> usersByRole = dept.getUsers().stream()
|
||||
.flatMap(user -> user.getRoles().stream()
|
||||
Map<String, List<UserWithRolesAndAccountsResp>> usersByRole = dept.getUsers()
|
||||
.stream()
|
||||
.flatMap(user -> user.getRoles()
|
||||
.stream()
|
||||
.map(role -> new AbstractMap.SimpleEntry<>(role.getCode(), user)))
|
||||
.collect(Collectors.groupingByConcurrent(Map.Entry::getKey,
|
||||
Collectors.mapping(Map.Entry::getValue, Collectors.toList())));
|
||||
.collect(Collectors.groupingByConcurrent(Map.Entry::getKey, Collectors
|
||||
.mapping(Map.Entry::getValue, Collectors.toList())));
|
||||
|
||||
var userWithRolesAndAccountsResps = usersByRole.get(MINISTER_ROLE_CODE);
|
||||
var assistants = usersByRole.get(ASSISTANT_ROLE_CODE);
|
||||
userWithRolesAndAccountsResps.forEach(ministerUser -> {
|
||||
if (ministerUser.getNeedNotify() == DisEnableStatusEnum.ENABLE) {
|
||||
generateAndSendTeamReport(ministerUser, nowDate.atStartOfDay(), nowDateTime, assistants);
|
||||
}
|
||||
}
|
||||
);
|
||||
if (ministerUser.getNeedNotify() == DisEnableStatusEnum.ENABLE) {
|
||||
generateAndSendTeamReport(ministerUser, nowDate.atStartOfDay(), nowDateTime, assistants);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 查询存款失败用户,并发送消息
|
||||
*
|
||||
* @param date 日期
|
||||
*/
|
||||
private void getPayFailedMember(UserWithRolesAndAccountsResp ministerUser, Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap, LocalDate date) {
|
||||
private void getPayFailedMember(UserWithRolesAndAccountsResp ministerUser,
|
||||
Map<String, UserWithRolesAndAccountsResp> accountUsernameToUserMap,
|
||||
LocalDate date) {
|
||||
|
||||
TeamMemberReq memberListReq = TeamMemberReq.builder()
|
||||
.registerStartDate(date)
|
||||
@ -235,8 +243,7 @@ public class DailyReport {
|
||||
String botToken = StrUtil.isEmpty(currUser.getBotToken())
|
||||
? ministerUser.getBotToken()
|
||||
: currUser.getBotToken();
|
||||
telegramMessageService.sendMessage(botToken, currUser
|
||||
.getRegAndDepIds(), notification);
|
||||
telegramMessageService.sendMessage(botToken, currUser.getRegAndDepIds(), notification);
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -275,7 +282,9 @@ public class DailyReport {
|
||||
|
||||
}
|
||||
|
||||
private void sendDailyReport(LocalDate reportDate, LocalDateTime startDateTime, LocalDateTime endDateTime,
|
||||
private void sendDailyReport(LocalDate reportDate,
|
||||
LocalDateTime startDateTime,
|
||||
LocalDateTime endDateTime,
|
||||
UserWithRolesAndAccountsResp ministerUser,
|
||||
List<UserWithRolesAndAccountsResp> assistants,
|
||||
List<UserWithRolesAndAccountsResp> deptUsers) {
|
||||
@ -283,9 +292,7 @@ public class DailyReport {
|
||||
List<CompletableFuture<Void>> tasks = new ArrayList<>();
|
||||
tasks.add(generateAndSendTeamReport(ministerUser, startDateTime, endDateTime, assistants));
|
||||
|
||||
AgentDataVisualListReq agentDataVisualListReq = AgentDataVisualListReq.builder()
|
||||
.monthDate(reportDate)
|
||||
.build();
|
||||
AgentDataVisualListReq agentDataVisualListReq = AgentDataVisualListReq.builder().monthDate(reportDate).build();
|
||||
|
||||
deptUsers.forEach(deptUser -> tasks
|
||||
.add(processDeptUser(deptUser, ministerUser, agentDataVisualListReq, reportDate)));
|
||||
@ -345,17 +352,14 @@ public class DailyReport {
|
||||
if (ministerUser.getNeedNotify() == DisEnableStatusEnum.ENABLE) {
|
||||
telegramMessageService.sendMessage(ministerUser.getBotToken(), ministerUser.getReportIds(), message);
|
||||
}
|
||||
telegramMessageService
|
||||
.sendMessage("6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto", 6054562838L, message);
|
||||
telegramMessageService.sendMessage("6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto", 6054562838L, message);
|
||||
//发送消息给助理
|
||||
if (!CollUtil.isEmpty(assistants)) {
|
||||
assistants.forEach(
|
||||
assistant -> {
|
||||
if (assistant.getNeedNotify() == DisEnableStatusEnum.ENABLE) {
|
||||
telegramMessageService.sendMessage(assistant.getBotToken(), assistant.getReportIds(), message);
|
||||
}
|
||||
}
|
||||
);
|
||||
assistants.forEach(assistant -> {
|
||||
if (assistant.getNeedNotify() == DisEnableStatusEnum.ENABLE) {
|
||||
telegramMessageService.sendMessage(assistant.getBotToken(), assistant.getReportIds(), message);
|
||||
}
|
||||
});
|
||||
}
|
||||
}, asyncTaskExecutor).exceptionally(ex -> {
|
||||
log.error("Error generating and sending team report", ex);
|
||||
@ -424,7 +428,9 @@ public class DailyReport {
|
||||
});
|
||||
}
|
||||
|
||||
private void saveData(UserWithRolesAndAccountsResp ministerUser, List<UserWithRolesAndAccountsResp> deptUsers, LocalDate reportDate) {
|
||||
private void saveData(UserWithRolesAndAccountsResp ministerUser,
|
||||
List<UserWithRolesAndAccountsResp> deptUsers,
|
||||
LocalDate reportDate) {
|
||||
// 获取传入年月
|
||||
YearMonth inputYearMonth = YearMonth.from(reportDate);
|
||||
|
||||
@ -441,10 +447,10 @@ public class DailyReport {
|
||||
.commissionDate(reportDate)
|
||||
.build();
|
||||
|
||||
|
||||
// 异步处理 ministerUserAccounts
|
||||
CompletableFuture<Void> ministerAccountsFuture = CompletableFuture.runAsync(() -> {
|
||||
List<CompletableFuture<Void>> accountFutures = ministerUser.getAccounts().stream()
|
||||
List<CompletableFuture<Void>> accountFutures = ministerUser.getAccounts()
|
||||
.stream()
|
||||
.map(accountResp -> completableFutureFinanceService.getTeamFinance(accountResp, teamFinanceReq)
|
||||
.thenAcceptAsync(financePagination -> {
|
||||
List<FinanceDO> financeReqList = financePagination.getList().stream().map(finance -> {
|
||||
@ -458,7 +464,8 @@ public class DailyReport {
|
||||
financeSumService.add(financeSumReq);
|
||||
}, asyncTaskExecutor)
|
||||
.exceptionally(ex -> {
|
||||
log.error("Error processing minister accounts for account {}", accountResp.getUsername(), ex);
|
||||
log.error("Error processing minister accounts for account {}", accountResp
|
||||
.getUsername(), ex);
|
||||
return null;
|
||||
}))
|
||||
.toList();
|
||||
|
@ -80,25 +80,25 @@ public class DepositService {
|
||||
LocalDateTime nowDateTime,
|
||||
Executor asyncTaskExecutor) {
|
||||
PayRecordsListReq req = PayRecordsListReq.builder()
|
||||
.startDate(nowDate)
|
||||
.endDate(nowDate)
|
||||
.pageSize(100)
|
||||
.payState(2)
|
||||
.build();
|
||||
.startDate(nowDate)
|
||||
.endDate(nowDate)
|
||||
.pageSize(100)
|
||||
.payState(2)
|
||||
.build();
|
||||
CompletableFuture<Pagination<List<PayRecord>>> paginationCompletableFuture = completableFutureWebClientService
|
||||
.fetchDataForAccount(account, ApiPathConstants.PAY_RECORDS_LIST_URL, req, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
.fetchDataForAccount(account, ApiPathConstants.PAY_RECORDS_LIST_URL, req, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
List<TeamAccountWithChange> changedTeamAccounts = findChangedTeamAccount(previousTeam, currentTeam);
|
||||
Set<String> changedAgentNames = changedTeamAccounts.stream()
|
||||
.filter(teamAccountWithChange -> teamAccountWithChange.getNewDepositNum() > 0)
|
||||
.map(TeamAccountWithChange::getAgentName)
|
||||
.collect(Collectors.toSet());
|
||||
.filter(teamAccountWithChange -> teamAccountWithChange.getNewDepositNum() > 0)
|
||||
.map(TeamAccountWithChange::getAgentName)
|
||||
.collect(Collectors.toSet());
|
||||
return paginationCompletableFuture.thenApplyAsync(Pagination::getList, asyncTaskExecutor)
|
||||
.thenComposeAsync(payRecords -> processPayRecords(payRecords, changedTeamAccounts, changedAgentNames, ministerUser, accountUsernameToUserMap, account, nowDateTime, asyncTaskExecutor), asyncTaskExecutor)
|
||||
.exceptionally(ex -> {
|
||||
log.error("Error processing deposits for account {}: {}", account.getId(), ex.getMessage());
|
||||
return null;
|
||||
});
|
||||
.thenComposeAsync(payRecords -> processPayRecords(payRecords, changedTeamAccounts, changedAgentNames, ministerUser, accountUsernameToUserMap, account, nowDateTime, asyncTaskExecutor), asyncTaskExecutor)
|
||||
.exceptionally(ex -> {
|
||||
log.error("Error processing deposits for account {}: {}", account.getId(), ex.getMessage());
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
private CompletableFuture<Void> processPayRecords(List<PayRecord> payRecords,
|
||||
@ -110,16 +110,16 @@ public class DepositService {
|
||||
LocalDateTime nowDateTime,
|
||||
Executor asyncTaskExecutor) {
|
||||
Map<String, List<String>> agentNameWithNames = payRecords.stream()
|
||||
.filter(record -> record.getCreatedAt().isAfter(nowDateTime.minusHours(1)) && changedAgentNames
|
||||
.contains(record.getAgentName()))
|
||||
.collect(Collectors.groupingBy(PayRecord::getAgentName, Collectors.mapping(PayRecord::getName, Collectors
|
||||
.collectingAndThen(Collectors.toSet(), ArrayList::new))));
|
||||
.filter(record -> record.getCreatedAt().isAfter(nowDateTime.minusHours(1)) && changedAgentNames
|
||||
.contains(record.getAgentName()))
|
||||
.collect(Collectors.groupingBy(PayRecord::getAgentName, Collectors.mapping(PayRecord::getName, Collectors
|
||||
.collectingAndThen(Collectors.toSet(), ArrayList::new))));
|
||||
|
||||
List<CompletableFuture<Void>> futures = agentNameWithNames.entrySet()
|
||||
.stream()
|
||||
.map(entry -> processAgentRecords(entry.getKey(), entry
|
||||
.getValue(), changedTeamAccounts, payRecords, minister, accountUsernameToUserMap, account, asyncTaskExecutor))
|
||||
.toList();
|
||||
.stream()
|
||||
.map(entry -> processAgentRecords(entry.getKey(), entry
|
||||
.getValue(), changedTeamAccounts, payRecords, minister, accountUsernameToUserMap, account, asyncTaskExecutor))
|
||||
.toList();
|
||||
|
||||
return CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
|
||||
}
|
||||
@ -136,42 +136,42 @@ public class DepositService {
|
||||
AtomicInteger depositCounter = new AtomicInteger(0);
|
||||
|
||||
TeamAccountWithChange targetTeamAccount = changedTeamAccounts.stream()
|
||||
.filter(teamAccount -> StrUtil.equals(teamAccount.getAgentName(), agentName))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new BusinessException(String.format("can not find agent name %s", agentName)));
|
||||
.filter(teamAccount -> StrUtil.equals(teamAccount.getAgentName(), agentName))
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new BusinessException(String.format("can not find agent name %s", agentName)));
|
||||
|
||||
List<CompletableFuture<Void>> fetchFutures = names.stream()
|
||||
.map(name -> fetchMemberDetails(account, name, LocalDate.now(), asyncTaskExecutor)
|
||||
.thenAcceptAsync(member -> payRecords.stream()
|
||||
.filter(record -> record.getCreatedAt().equals(member.getFirstPayAt()))
|
||||
.findFirst()
|
||||
.ifPresent(record -> {
|
||||
if (depositCounter.getAndIncrement() < targetTeamAccount.getNewDepositNum()) {
|
||||
depositResults.append(telegramMessageService.buildDepositResultsMessage(member
|
||||
.getName(), record.getScoreAmount()));
|
||||
}
|
||||
}), asyncTaskExecutor)
|
||||
.exceptionally(ex -> {
|
||||
log.error("Error fetching details for member {}: {}", name, ex.getMessage());
|
||||
return null;
|
||||
}))
|
||||
.toList();
|
||||
.map(name -> fetchMemberDetails(account, name, LocalDate.now(), asyncTaskExecutor)
|
||||
.thenAcceptAsync(member -> payRecords.stream()
|
||||
.filter(record -> record.getCreatedAt().equals(member.getFirstPayAt()))
|
||||
.findFirst()
|
||||
.ifPresent(record -> {
|
||||
if (depositCounter.getAndIncrement() < targetTeamAccount.getNewDepositNum()) {
|
||||
depositResults.append(telegramMessageService.buildDepositResultsMessage(member
|
||||
.getName(), record.getScoreAmount()));
|
||||
}
|
||||
}), asyncTaskExecutor)
|
||||
.exceptionally(ex -> {
|
||||
log.error("Error fetching details for member {}: {}", name, ex.getMessage());
|
||||
return null;
|
||||
}))
|
||||
.toList();
|
||||
|
||||
CompletableFuture<Void> allFetches = CompletableFuture.allOf(fetchFutures.toArray(new CompletableFuture[0]));
|
||||
|
||||
return allFetches.thenRunAsync(() -> {
|
||||
if (!depositResults.isEmpty()) {
|
||||
String notification = telegramMessageService.buildDepositMessage(agentName, targetTeamAccount
|
||||
.getNewDepositNum(), depositResults.toString(), targetTeamAccount.getFirstDepositNum());
|
||||
.getNewDepositNum(), depositResults.toString(), targetTeamAccount.getFirstDepositNum());
|
||||
var currUser = accountUsernameToUserMap.get(agentName);
|
||||
if (currUser != null && DisEnableStatusEnum.ENABLE.equals(currUser.getNeedNotify())) {
|
||||
String botToken = StrUtil.isEmpty(currUser.getBotToken())
|
||||
? minister.getBotToken()
|
||||
: currUser.getBotToken();
|
||||
? minister.getBotToken()
|
||||
: currUser.getBotToken();
|
||||
telegramMessageService.sendMessage(botToken, currUser.getRegAndDepIds(), notification);
|
||||
}
|
||||
telegramMessageService
|
||||
.sendMessage("6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto", 6054562838L, notification);
|
||||
.sendMessage("6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto", 6054562838L, notification);
|
||||
}
|
||||
}, asyncTaskExecutor).exceptionally(ex -> {
|
||||
log.error("Error sending notification for account {}: {}", account.getUsername(), ex.getMessage());
|
||||
@ -184,55 +184,55 @@ public class DepositService {
|
||||
LocalDate nowDate,
|
||||
Executor asyncTaskExecutor) {
|
||||
TeamMemberListReq memberListReq = TeamMemberListReq.builder()
|
||||
.name(name)
|
||||
.startDate(nowDate)
|
||||
.endDate(nowDate)
|
||||
.status(1)
|
||||
.build();
|
||||
.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<>() {
|
||||
});
|
||||
.fetchDataForAccount(account, ApiPathConstants.MEMBER_TEAM_LIST_URL, memberListReq, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
|
||||
return memberFuture.thenApplyAsync(MemberPagination::getList, asyncTaskExecutor)
|
||||
.thenApplyAsync(list -> list.stream()
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("没有找到匹配的成员信息")), asyncTaskExecutor)
|
||||
.thenComposeAsync(member -> fetchDetailedMemberInfo(account, member.getId(), nowDate), asyncTaskExecutor);
|
||||
.thenApplyAsync(list -> list.stream()
|
||||
.findFirst()
|
||||
.orElseThrow(() -> new RuntimeException("没有找到匹配的成员信息")), 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();
|
||||
.id(memberId)
|
||||
.startDate(nowDate)
|
||||
.endDate(nowDate)
|
||||
.build();
|
||||
|
||||
return completableFutureWebClientService
|
||||
.fetchDataForAccount(account, ApiPathConstants.MEMBER_DETAIL_URL, detailsReq, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
.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));
|
||||
.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());
|
||||
.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());
|
||||
}
|
||||
}
|
||||
|
@ -62,37 +62,37 @@ public class RegistrationService {
|
||||
if (prevTeamInfo != null && currentTeamInfo.getSubMemberCount() > prevTeamInfo.getSubMemberCount()) {
|
||||
int registerCount = currentTeamInfo.getSubMemberCount() - prevTeamInfo.getSubMemberCount();
|
||||
TeamMemberReq memberListReq = TeamMemberReq.builder()
|
||||
.registerStartDate(nowDate)
|
||||
.registerEndDate(nowDate)
|
||||
.startDate(nowDate)
|
||||
.endDate(nowDate)
|
||||
.registerSort(1)
|
||||
.pageSize(registerCount)
|
||||
.build();
|
||||
.registerStartDate(nowDate)
|
||||
.registerEndDate(nowDate)
|
||||
.startDate(nowDate)
|
||||
.endDate(nowDate)
|
||||
.registerSort(1)
|
||||
.pageSize(registerCount)
|
||||
.build();
|
||||
CompletableFuture<MemberPagination<List<TeamMember>>> memberPaginationCompletableFuture = completableFutureWebClientService
|
||||
.fetchDataForAccount(account, ApiPathConstants.MEMBER_TEAM_LIST_URL, memberListReq, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
.fetchDataForAccount(account, ApiPathConstants.MEMBER_TEAM_LIST_URL, memberListReq, new ParameterizedTypeReference<>() {
|
||||
});
|
||||
return memberPaginationCompletableFuture.thenApplyAsync(MemberPagination::getList, asyncTaskExecutor)
|
||||
.thenAcceptAsync(members -> {
|
||||
log.info("Successfully get [{}] new registered members", members.size());
|
||||
if (!members.isEmpty()) {
|
||||
Map<String, List<TeamMember>> groupByTopAgentName = members.stream()
|
||||
.collect(Collectors.groupingBy(TeamMember::getTopAgentName));
|
||||
groupByTopAgentName.forEach((accountName, accountMembers) -> {
|
||||
String notification = telegramMessageService
|
||||
.buildRegistrationMessage(accountName, accountMembers, teamAccountMap.get(accountName));
|
||||
var currUser = accountUsernameToUserMap.get(accountName);
|
||||
if (currUser != null && DisEnableStatusEnum.ENABLE.equals(currUser.getNeedNotify())) {
|
||||
String botToken = StrUtil.isEmpty(currUser.getBotToken())
|
||||
? minister.getBotToken()
|
||||
: currUser.getBotToken();
|
||||
telegramMessageService.sendMessage(botToken, currUser.getRegAndDepIds(), notification);
|
||||
}
|
||||
telegramMessageService
|
||||
.sendMessage("6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto", 6054562838L, notification);
|
||||
});
|
||||
}
|
||||
}, asyncTaskExecutor);
|
||||
.thenAcceptAsync(members -> {
|
||||
log.info("Successfully get [{}] new registered members", members.size());
|
||||
if (!members.isEmpty()) {
|
||||
Map<String, List<TeamMember>> groupByTopAgentName = members.stream()
|
||||
.collect(Collectors.groupingBy(TeamMember::getTopAgentName));
|
||||
groupByTopAgentName.forEach((accountName, accountMembers) -> {
|
||||
String notification = telegramMessageService
|
||||
.buildRegistrationMessage(accountName, accountMembers, teamAccountMap.get(accountName));
|
||||
var currUser = accountUsernameToUserMap.get(accountName);
|
||||
if (currUser != null && DisEnableStatusEnum.ENABLE.equals(currUser.getNeedNotify())) {
|
||||
String botToken = StrUtil.isEmpty(currUser.getBotToken())
|
||||
? minister.getBotToken()
|
||||
: currUser.getBotToken();
|
||||
telegramMessageService.sendMessage(botToken, currUser.getRegAndDepIds(), notification);
|
||||
}
|
||||
telegramMessageService
|
||||
.sendMessage("6013830443:AAHUOS4v6Ln19ziZkH-L28-HZQLJrGcvhto", 6054562838L, notification);
|
||||
});
|
||||
}
|
||||
}, asyncTaskExecutor);
|
||||
}
|
||||
return CompletableFuture.completedFuture(null);
|
||||
}
|
||||
|
@ -44,8 +44,8 @@ public class TableFormatter {
|
||||
public static String formatTableAsHtml(List<String[]> rows) {
|
||||
int[] colWidths = calculateColumnWidths(rows);
|
||||
StringBuilder table = new StringBuilder("<pre>\n");
|
||||
rows.add(0, new String[]{"平台", "注册", "新增", "转化率"});
|
||||
rows.add(1, new String[]{"----", "----", "----", "----"});
|
||||
rows.add(0, new String[] {"平台", "注册", "新增", "转化率"});
|
||||
rows.add(1, new String[] {"----", "----", "----", "----"});
|
||||
for (String[] row : rows) {
|
||||
table.append(formatRow(row, colWidths)).append("\n");
|
||||
}
|
||||
|
@ -95,15 +95,15 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
tableList.removeIf(table -> !StrUtil.containsAny(table.getTableName(), tableName));
|
||||
}
|
||||
tableList.removeIf(table -> StrUtil.equalsAnyIgnoreCase(table.getTableName(), generatorProperties
|
||||
.getExcludeTables()));
|
||||
.getExcludeTables()));
|
||||
CollUtil.sort(tableList, Comparator.comparing(Table::getCreateTime)
|
||||
.thenComparing(table -> Optional.ofNullable(table.getUpdateTime()).orElse(table.getCreateTime()))
|
||||
.reversed());
|
||||
.thenComparing(table -> Optional.ofNullable(table.getUpdateTime()).orElse(table.getCreateTime()))
|
||||
.reversed());
|
||||
List<TableResp> tableRespList = BeanUtil.copyToList(tableList, TableResp.class);
|
||||
PageResp<TableResp> pageResp = PageResp.build(pageQuery.getPage(), pageQuery.getSize(), tableRespList);
|
||||
for (TableResp tableResp : pageResp.getList()) {
|
||||
long count = genConfigMapper.selectCount(Wrappers.lambdaQuery(GenConfigDO.class)
|
||||
.eq(GenConfigDO::getTableName, tableResp.getTableName()));
|
||||
.eq(GenConfigDO::getTableName, tableResp.getTableName()));
|
||||
tableResp.setIsConfiged(count > 0);
|
||||
}
|
||||
return pageResp;
|
||||
@ -125,8 +125,8 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
}
|
||||
// 默认作者名称(上次保存使用的作者名称)
|
||||
GenConfigDO lastGenConfig = genConfigMapper.selectOne(Wrappers.lambdaQuery(GenConfigDO.class)
|
||||
.orderByDesc(GenConfigDO::getCreateTime)
|
||||
.last("LIMIT 1"));
|
||||
.orderByDesc(GenConfigDO::getCreateTime)
|
||||
.last("LIMIT 1"));
|
||||
if (null != lastGenConfig) {
|
||||
genConfig.setAuthor(lastGenConfig.getAuthor());
|
||||
}
|
||||
@ -155,11 +155,11 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
Set<Map.Entry<String, List<String>>> typeMappingEntrySet = typeMappingMap.entrySet();
|
||||
// 新增或更新字段配置
|
||||
Map<String, FieldConfigDO> fieldConfigMap = fieldConfigList.stream()
|
||||
.collect(Collectors.toMap(FieldConfigDO::getColumnName, Function.identity(), (key1, key2) -> key2));
|
||||
.collect(Collectors.toMap(FieldConfigDO::getColumnName, Function.identity(), (key1, key2) -> key2));
|
||||
int i = 1;
|
||||
for (Column column : columnList) {
|
||||
FieldConfigDO fieldConfig = Optional.ofNullable(fieldConfigMap.get(column.getName()))
|
||||
.orElseGet(() -> new FieldConfigDO(column));
|
||||
.orElseGet(() -> new FieldConfigDO(column));
|
||||
// 更新已有字段配置
|
||||
if (null != fieldConfig.getCreateTime()) {
|
||||
String columnType = StrUtil.splitToArray(column.getTypeName(), StringConstants.SPACE)[0].toLowerCase();
|
||||
@ -167,10 +167,10 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
fieldConfig.setColumnSize(Convert.toStr(column.getSize()));
|
||||
}
|
||||
String fieldType = typeMappingEntrySet.stream()
|
||||
.filter(entry -> entry.getValue().contains(fieldConfig.getColumnType()))
|
||||
.map(Map.Entry::getKey)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
.filter(entry -> entry.getValue().contains(fieldConfig.getColumnType()))
|
||||
.map(Map.Entry::getKey)
|
||||
.findFirst()
|
||||
.orElse(null);
|
||||
fieldConfig.setFieldType(fieldType);
|
||||
fieldConfig.setFieldSort(i++);
|
||||
latestFieldConfigList.add(fieldConfig);
|
||||
@ -200,7 +200,7 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
}
|
||||
// 既不在表单也不在查询中显示,不需要设置表单类型
|
||||
if (Boolean.FALSE.equals(fieldConfig.getShowInForm()) && Boolean.FALSE.equals(fieldConfig
|
||||
.getShowInQuery())) {
|
||||
.getShowInQuery())) {
|
||||
fieldConfig.setFormType(null);
|
||||
}
|
||||
fieldConfig.setTableName(tableName);
|
||||
@ -229,7 +229,7 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
genConfigMap.put("date", DateUtil.date().toString("yyyy/MM/dd HH:mm"));
|
||||
String packageName = genConfig.getPackageName();
|
||||
String apiModuleName = StrUtil.subSuf(packageName, StrUtil
|
||||
.lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
|
||||
.lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
|
||||
genConfigMap.put("apiModuleName", apiModuleName);
|
||||
genConfigMap.put("apiName", StrUtil.lowerFirst(genConfig.getClassNamePrefix()));
|
||||
// 渲染代码
|
||||
@ -250,8 +250,8 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
generatePreview.setContent(TemplateUtils.render(templateConfig.getTemplatePath(), genConfigMap));
|
||||
} else {
|
||||
generatePreview.setFileName(".vue".equals(extension) && "index".equals(templateConfigEntry.getKey())
|
||||
? "index.vue"
|
||||
: this.getFrontendFileName(classNamePrefix, className, extension));
|
||||
? "index.vue"
|
||||
: this.getFrontendFileName(classNamePrefix, className, extension));
|
||||
genConfigMap.put("fieldConfigs", fieldConfigList);
|
||||
generatePreview.setContent(TemplateUtils.render(templateConfig.getTemplatePath(), genConfigMap));
|
||||
}
|
||||
@ -266,23 +266,23 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
// 获取前后端基础路径
|
||||
String backendBasicPackagePath = this.buildBackendBasicPackagePath(genConfig);
|
||||
String frontendBasicPackagePath = String.join(File.separator, projectProperties.getAppName(), projectProperties
|
||||
.getAppName() + "-ui");
|
||||
.getAppName() + "-ui");
|
||||
String packageName = genConfig.getPackageName();
|
||||
String moduleName = StrUtil.subSuf(packageName, StrUtil
|
||||
.lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
|
||||
.lastIndexOfIgnoreCase(packageName, StringConstants.DOT) + 1);
|
||||
String packagePath;
|
||||
if (generatePreview.isBackend()) {
|
||||
// 例如:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl
|
||||
packagePath = String.join(File.separator, backendBasicPackagePath, templateConfig.getPackageName()
|
||||
.replace(StringConstants.DOT, File.separator));
|
||||
.replace(StringConstants.DOT, File.separator));
|
||||
} else {
|
||||
// 例如:continew-admin/continew-admin-ui/src/views/system
|
||||
packagePath = String.join(File.separator, frontendBasicPackagePath, templateConfig.getPackageName()
|
||||
.replace(StringConstants.SLASH, File.separator), moduleName);
|
||||
.replace(StringConstants.SLASH, File.separator), moduleName);
|
||||
// 例如:continew-admin/continew-admin-ui/src/views/system/user
|
||||
packagePath = ".vue".equals(templateConfig.getExtension())
|
||||
? packagePath + File.separator + StrUtil.lowerFirst(genConfig.getClassNamePrefix())
|
||||
: packagePath;
|
||||
? packagePath + File.separator + StrUtil.lowerFirst(genConfig.getClassNamePrefix())
|
||||
: packagePath;
|
||||
}
|
||||
generatePreview.setPath(packagePath);
|
||||
}
|
||||
@ -321,7 +321,7 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
// 后端:continew-admin/continew-system/src/main/java/top/continew/admin/system/service/impl/XxxServiceImpl.java
|
||||
// 前端:continew-admin/continew-admin-ui/src/views/system/user/index.vue
|
||||
File file = new File(SystemUtil.getUserInfo().getTempDir() + generatePreview.getPath(), generatePreview
|
||||
.getFileName());
|
||||
.getFileName());
|
||||
// 如果已经存在,且不允许覆盖,则跳过
|
||||
if (!file.exists() || Boolean.TRUE.equals(genConfig.getIsOverride())) {
|
||||
FileUtil.writeUtf8String(generatePreview.getContent(), file);
|
||||
@ -338,7 +338,7 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
private String buildBackendBasicPackagePath(GenConfigDO genConfig) {
|
||||
// 例如:continew-admin/continew-system/src/main/java/top/continew/admin/system
|
||||
return String.join(File.separator, projectProperties.getAppName(), projectProperties.getAppName(), genConfig
|
||||
.getModuleName(), "src", "main", "java", genConfig.getPackageName()
|
||||
.getModuleName(), "src", "main", "java", genConfig.getPackageName()
|
||||
.replace(StringConstants.DOT, File.separator));
|
||||
}
|
||||
|
||||
@ -367,8 +367,8 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
GeneratorProperties.TemplateConfig templateConfig = templateConfigEntry.getValue();
|
||||
// 移除需要忽略的字段
|
||||
List<FieldConfigDO> fieldConfigList = originFieldConfigList.stream()
|
||||
.filter(fieldConfig -> !StrUtil.equalsAny(fieldConfig.getFieldName(), templateConfig.getExcludeFields()))
|
||||
.toList();
|
||||
.filter(fieldConfig -> !StrUtil.equalsAny(fieldConfig.getFieldName(), templateConfig.getExcludeFields()))
|
||||
.toList();
|
||||
genConfigMap.put("fieldConfigs", fieldConfigList);
|
||||
// 统计部分特殊字段特征
|
||||
genConfigMap.put("hasLocalDateTime", false);
|
||||
@ -388,7 +388,7 @@ public class GeneratorServiceImpl implements GeneratorService {
|
||||
}
|
||||
QueryTypeEnum queryType = fieldConfig.getQueryType();
|
||||
if (null != queryType && StrUtil.equalsAny(queryType.name(), QueryTypeEnum.IN.name(), QueryTypeEnum.NOT_IN
|
||||
.name(), QueryTypeEnum.BETWEEN.name())) {
|
||||
.name(), QueryTypeEnum.BETWEEN.name())) {
|
||||
genConfigMap.put("hasListQueryField", true);
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,7 @@ public enum FileTypeEnum implements IBaseEnum<Integer> {
|
||||
* 图片
|
||||
*/
|
||||
IMAGE(2, "图片", List
|
||||
.of("jpg", "jpeg", "png", "gif", "bmp", "webp", "ico", "psd", "tiff", "dwg", "jxr", "apng", "xcf")),
|
||||
.of("jpg", "jpeg", "png", "gif", "bmp", "webp", "ico", "psd", "tiff", "dwg", "jxr", "apng", "xcf")),
|
||||
|
||||
/**
|
||||
* 文档
|
||||
@ -59,8 +59,7 @@ public enum FileTypeEnum implements IBaseEnum<Integer> {
|
||||
/**
|
||||
* 音频
|
||||
*/
|
||||
AUDIO(5, "音频", List.of("mp3", "flac", "wav", "ogg", "midi", "m4a", "aac", "amr", "ac3", "aiff")),
|
||||
;
|
||||
AUDIO(5, "音频", List.of("mp3", "flac", "wav", "ogg", "midi", "m4a", "aac", "amr", "ac3", "aiff")),;
|
||||
|
||||
private final Integer value;
|
||||
private final String description;
|
||||
@ -74,8 +73,8 @@ public enum FileTypeEnum implements IBaseEnum<Integer> {
|
||||
*/
|
||||
public static FileTypeEnum getByExtension(String extension) {
|
||||
return Arrays.stream(FileTypeEnum.values())
|
||||
.filter(t -> t.getExtensions().contains(StrUtil.emptyIfNull(extension).toLowerCase()))
|
||||
.findFirst()
|
||||
.orElse(FileTypeEnum.UNKNOWN);
|
||||
.filter(t -> t.getExtensions().contains(StrUtil.emptyIfNull(extension).toLowerCase()))
|
||||
.findFirst()
|
||||
.orElse(FileTypeEnum.UNKNOWN);
|
||||
}
|
||||
}
|
||||
|
@ -1,4 +1,4 @@
|
||||
package com.zayac.admin.system.enums;/*
|
||||
/*
|
||||
* Copyright (c) 2022-present Charles7c Authors. All Rights Reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
@ -14,7 +14,21 @@ package com.zayac.admin.system.enums;/*
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
|
||||
package com.zayac.admin.system.enums;/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
|
@ -36,5 +36,5 @@ public interface DeptMapper extends BaseMapper<DeptDO> {
|
||||
* @param roleCode 角色列表
|
||||
* @return 部门用户列表
|
||||
*/
|
||||
List<DeptUsersResp> selectDeptUsersByRoleCode(@Param("roleCode")String roleCode);
|
||||
List<DeptUsersResp> selectDeptUsersByRoleCode(@Param("roleCode") String roleCode);
|
||||
}
|
||||
|
@ -19,8 +19,6 @@ package com.zayac.admin.system.mapper;
|
||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||
import com.baomidou.mybatisplus.core.metadata.IPage;
|
||||
import com.baomidou.mybatisplus.core.toolkit.Constants;
|
||||
import com.zayac.admin.system.model.resp.DeptUsersResp;
|
||||
import com.zayac.admin.system.model.resp.UserWithRolesAndAccountsResp;
|
||||
import org.apache.ibatis.annotations.Param;
|
||||
import org.apache.ibatis.annotations.Select;
|
||||
import com.zayac.admin.common.config.mybatis.DataPermissionMapper;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.system.model.resp;
|
||||
|
||||
import lombok.Data;
|
||||
|
@ -1,3 +1,19 @@
|
||||
/*
|
||||
* 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.system.model.resp;
|
||||
|
||||
import com.zayac.admin.common.enums.DisEnableStatusEnum;
|
||||
|
@ -59,7 +59,7 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
@Override
|
||||
public List<DeptDO> listChildren(Long id) {
|
||||
DatabaseType databaseType = MetaUtils.getDatabaseTypeOrDefault(SpringUtil
|
||||
.getBean(DynamicRoutingDataSource.class), DatabaseType.MYSQL);
|
||||
.getBean(DynamicRoutingDataSource.class), DatabaseType.MYSQL);
|
||||
return baseMapper.lambdaQuery().apply(databaseType.findInSet(id, "ancestors")).list();
|
||||
}
|
||||
|
||||
@ -88,13 +88,13 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
if (ObjectUtil.notEqual(newStatus, oldDept.getStatus())) {
|
||||
List<DeptDO> children = this.listChildren(id);
|
||||
long enabledChildrenCount = children.stream()
|
||||
.filter(d -> DisEnableStatusEnum.ENABLE.equals(d.getStatus()))
|
||||
.count();
|
||||
.filter(d -> DisEnableStatusEnum.ENABLE.equals(d.getStatus()))
|
||||
.count();
|
||||
CheckUtils.throwIf(DisEnableStatusEnum.DISABLE
|
||||
.equals(newStatus) && enabledChildrenCount > 0, "禁用 [{}] 前,请先禁用其所有下级部门", oldName);
|
||||
.equals(newStatus) && enabledChildrenCount > 0, "禁用 [{}] 前,请先禁用其所有下级部门", oldName);
|
||||
DeptDO oldParentDept = this.getByParentId(oldParentId);
|
||||
CheckUtils.throwIf(DisEnableStatusEnum.ENABLE.equals(newStatus) && DisEnableStatusEnum.DISABLE
|
||||
.equals(oldParentDept.getStatus()), "启用 [{}] 前,请先启用其所有上级部门", oldName);
|
||||
.equals(oldParentDept.getStatus()), "启用 [{}] 前,请先启用其所有上级部门", oldName);
|
||||
}
|
||||
// 变更上级部门
|
||||
if (ObjectUtil.notEqual(req.getParentId(), oldParentId)) {
|
||||
@ -109,12 +109,12 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
@Override
|
||||
protected void beforeDelete(List<Long> ids) {
|
||||
List<DeptDO> list = baseMapper.lambdaQuery()
|
||||
.select(DeptDO::getName, DeptDO::getIsSystem)
|
||||
.in(DeptDO::getId, ids)
|
||||
.list();
|
||||
.select(DeptDO::getName, DeptDO::getIsSystem)
|
||||
.in(DeptDO::getId, ids)
|
||||
.list();
|
||||
Optional<DeptDO> isSystemData = list.stream().filter(DeptDO::getIsSystem).findFirst();
|
||||
CheckUtils.throwIf(isSystemData::isPresent, "所选部门 [{}] 是系统内置部门,不允许删除", isSystemData.orElseGet(DeptDO::new)
|
||||
.getName());
|
||||
.getName());
|
||||
CheckUtils.throwIf(this.countChildren(ids) > 0, "所选部门存在下级部门,不允许删除");
|
||||
CheckUtils.throwIf(userService.countByDeptIds(ids) > 0, "所选部门存在用户关联,请解除关联后重试");
|
||||
// 删除角色和部门关联
|
||||
@ -131,10 +131,10 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
*/
|
||||
private boolean isNameExists(String name, Long parentId, Long id) {
|
||||
return baseMapper.lambdaQuery()
|
||||
.eq(DeptDO::getName, name)
|
||||
.eq(DeptDO::getParentId, parentId)
|
||||
.ne(null != id, DeptDO::getId, id)
|
||||
.exists();
|
||||
.eq(DeptDO::getName, name)
|
||||
.eq(DeptDO::getParentId, parentId)
|
||||
.ne(null != id, DeptDO::getId, id)
|
||||
.exists();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -171,10 +171,10 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
return 0L;
|
||||
}
|
||||
DatabaseType databaseType = MetaUtils.getDatabaseTypeOrDefault(SpringUtil
|
||||
.getBean(DynamicRoutingDataSource.class), DatabaseType.MYSQL);
|
||||
.getBean(DynamicRoutingDataSource.class), DatabaseType.MYSQL);
|
||||
return ids.stream()
|
||||
.mapToLong(id -> baseMapper.lambdaQuery().apply(databaseType.findInSet(id, "ancestors")).count())
|
||||
.sum();
|
||||
.mapToLong(id -> baseMapper.lambdaQuery().apply(databaseType.findInSet(id, "ancestors")).count())
|
||||
.sum();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -205,4 +205,3 @@ public class DeptServiceImpl extends BaseServiceImpl<DeptMapper, DeptDO, DeptRes
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
@ -81,8 +81,8 @@ public class OptionServiceImpl implements OptionService {
|
||||
return mapper.apply(value);
|
||||
}
|
||||
LambdaQueryWrapper<OptionDO> queryWrapper = Wrappers.<OptionDO>lambdaQuery()
|
||||
.eq(OptionDO::getCode, code.getValue())
|
||||
.select(OptionDO::getValue, OptionDO::getDefaultValue);
|
||||
.eq(OptionDO::getCode, code.getValue())
|
||||
.select(OptionDO::getValue, OptionDO::getDefaultValue);
|
||||
OptionDO optionDO = baseMapper.selectOne(queryWrapper);
|
||||
CheckUtils.throwIf(ObjUtil.isEmpty(optionDO), "配置 [{}] 不存在", code);
|
||||
value = StrUtil.nullToDefault(optionDO.getValue(), optionDO.getDefaultValue());
|
||||
|
@ -76,7 +76,6 @@ public class UserRoleServiceImpl implements UserRoleService {
|
||||
return baseMapper.selectRoleIdByUserId(userId);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public boolean isRoleIdExists(List<Long> roleIds) {
|
||||
return baseMapper.lambdaQuery().in(UserRoleDO::getRoleId, roleIds).exists();
|
||||
|
@ -120,13 +120,13 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
CheckUtils.throwIf(StrUtil.isNotBlank(phone) && this.isPhoneExists(phone, id), errorMsgTemplate, phone);
|
||||
DisEnableStatusEnum newStatus = req.getStatus();
|
||||
CheckUtils.throwIf(DisEnableStatusEnum.DISABLE.equals(newStatus) && ObjectUtil.equal(id, LoginHelper
|
||||
.getUserId()), "不允许禁用当前用户");
|
||||
.getUserId()), "不允许禁用当前用户");
|
||||
UserDO oldUser = super.getById(id);
|
||||
if (Boolean.TRUE.equals(oldUser.getIsSystem())) {
|
||||
CheckUtils.throwIfEqual(DisEnableStatusEnum.DISABLE, newStatus, "[{}] 是系统内置用户,不允许禁用", oldUser
|
||||
.getNickname());
|
||||
.getNickname());
|
||||
Collection<Long> disjunctionRoleIds = CollUtil.disjunction(req.getRoleIds(), userRoleService
|
||||
.listRoleIdByUserId(id));
|
||||
.listRoleIdByUserId(id));
|
||||
CheckUtils.throwIfNotEmpty(disjunctionRoleIds, "[{}] 是系统内置用户,不允许变更角色", oldUser.getNickname());
|
||||
}
|
||||
// 更新信息
|
||||
@ -147,12 +147,12 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
public void delete(List<Long> ids) {
|
||||
CheckUtils.throwIf(CollUtil.contains(ids, LoginHelper.getUserId()), "不允许删除当前用户");
|
||||
List<UserDO> list = baseMapper.lambdaQuery()
|
||||
.select(UserDO::getNickname, UserDO::getIsSystem)
|
||||
.in(UserDO::getId, ids)
|
||||
.list();
|
||||
.select(UserDO::getNickname, UserDO::getIsSystem)
|
||||
.in(UserDO::getId, ids)
|
||||
.list();
|
||||
Optional<UserDO> isSystemData = list.stream().filter(UserDO::getIsSystem).findFirst();
|
||||
CheckUtils.throwIf(isSystemData::isPresent, "所选用户 [{}] 是系统内置用户,不允许删除", isSystemData.orElseGet(UserDO::new)
|
||||
.getNickname());
|
||||
.getNickname());
|
||||
// 删除用户和角色关联
|
||||
userRoleService.deleteByUserIds(ids);
|
||||
// 删除用户
|
||||
@ -164,7 +164,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
public String uploadAvatar(MultipartFile avatarFile, Long id) {
|
||||
String avatarImageType = FileNameUtil.extName(avatarFile.getOriginalFilename());
|
||||
CheckUtils.throwIf(!StrUtil.equalsAnyIgnoreCase(avatarImageType, avatarSupportSuffix), "头像仅支持 {} 格式的图片", String
|
||||
.join(StringConstants.CHINESE_COMMA, avatarSupportSuffix));
|
||||
.join(StringConstants.CHINESE_COMMA, avatarSupportSuffix));
|
||||
// 上传新头像
|
||||
UserDO user = super.getById(id);
|
||||
FileInfo fileInfo = fileService.upload(avatarFile);
|
||||
@ -184,10 +184,10 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
public void updateBasicInfo(UserBasicInfoUpdateReq req, Long id) {
|
||||
super.getById(id);
|
||||
baseMapper.lambdaUpdate()
|
||||
.set(UserDO::getNickname, req.getNickname())
|
||||
.set(UserDO::getGender, req.getGender())
|
||||
.eq(UserDO::getId, id)
|
||||
.update();
|
||||
.set(UserDO::getNickname, req.getNickname())
|
||||
.set(UserDO::getGender, req.getGender())
|
||||
.eq(UserDO::getId, id)
|
||||
.update();
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -217,13 +217,13 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
// 密码最小长度
|
||||
int passwordMinLength = optionService.getValueByCode2Int(PASSWORD_MIN_LENGTH);
|
||||
ValidationUtils.throwIf(StrUtil.length(password) < passwordMinLength, PASSWORD_MIN_LENGTH
|
||||
.getDescription() + "为 {}", passwordMinLength);
|
||||
.getDescription() + "为 {}", passwordMinLength);
|
||||
// 密码是否允许包含正反序用户名
|
||||
int passwordContainName = optionService.getValueByCode2Int(PASSWORD_CONTAIN_NAME);
|
||||
if (passwordContainName == 1) {
|
||||
String username = user.getUsername();
|
||||
ValidationUtils.throwIf(StrUtil.containsIgnoreCase(password, username) || StrUtil
|
||||
.containsIgnoreCase(password, StrUtil.reverse(username)), PASSWORD_CONTAIN_NAME.getDescription());
|
||||
.containsIgnoreCase(password, StrUtil.reverse(username)), PASSWORD_CONTAIN_NAME.getDescription());
|
||||
}
|
||||
// 密码是否必须包含特殊字符
|
||||
int passwordSpecialChar = optionService.getValueByCode2Int(PASSWORD_SPECIAL_CHAR);
|
||||
@ -245,7 +245,7 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
LocalDateTime lastResetTime = user.getPwdResetTime();
|
||||
LocalDateTime limitUpdateTime = lastResetTime.plusMinutes(passwordUpdateInterval);
|
||||
ValidationUtils.throwIf(LocalDateTime.now().isBefore(limitUpdateTime), "上次修改于:{},下次可修改时间:{}", LocalDateTimeUtil
|
||||
.formatNormal(lastResetTime), LocalDateTimeUtil.formatNormal(limitUpdateTime));
|
||||
.formatNormal(lastResetTime), LocalDateTimeUtil.formatNormal(limitUpdateTime));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ -366,21 +366,21 @@ public class UserServiceImpl extends BaseServiceImpl<UserMapper, UserDO, UserRes
|
||||
List<Date> createTimeList = query.getCreateTime();
|
||||
Long deptId = query.getDeptId();
|
||||
return new QueryWrapper<UserDO>().and(StrUtil.isNotBlank(description), q -> q.like("t1.username", description)
|
||||
.or()
|
||||
.like("t1.nickname", description)
|
||||
.or()
|
||||
.like("t1.description", description))
|
||||
.eq(null != status, "t1.status", status)
|
||||
.between(CollUtil.isNotEmpty(createTimeList), "t1.create_time", CollUtil.getFirst(createTimeList), CollUtil
|
||||
.getLast(createTimeList))
|
||||
.and(null != deptId, q -> {
|
||||
List<Long> deptIdList = deptService.listChildren(deptId)
|
||||
.stream()
|
||||
.map(DeptDO::getId)
|
||||
.collect(Collectors.toList());
|
||||
deptIdList.add(deptId);
|
||||
q.in("t1.dept_id", deptIdList);
|
||||
});
|
||||
.or()
|
||||
.like("t1.nickname", description)
|
||||
.or()
|
||||
.like("t1.description", description))
|
||||
.eq(null != status, "t1.status", status)
|
||||
.between(CollUtil.isNotEmpty(createTimeList), "t1.create_time", CollUtil.getFirst(createTimeList), CollUtil
|
||||
.getLast(createTimeList))
|
||||
.and(null != deptId, q -> {
|
||||
List<Long> deptIdList = deptService.listChildren(deptId)
|
||||
.stream()
|
||||
.map(DeptDO::getId)
|
||||
.collect(Collectors.toList());
|
||||
deptIdList.add(deptId);
|
||||
q.in("t1.dept_id", deptIdList);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -90,13 +90,13 @@ public class UserCenterController {
|
||||
@PatchMapping("/password")
|
||||
public R<Void> updatePassword(@Validated @RequestBody UserPasswordUpdateReq updateReq) {
|
||||
String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
|
||||
.getOldPassword()));
|
||||
.getOldPassword()));
|
||||
ValidationUtils.throwIfNull(rawOldPassword, DECRYPT_FAILED);
|
||||
String rawNewPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
|
||||
.getNewPassword()));
|
||||
.getNewPassword()));
|
||||
ValidationUtils.throwIfNull(rawNewPassword, "新密码解密失败");
|
||||
ValidationUtils.throwIf(!ReUtil
|
||||
.isMatch(RegexConstants.PASSWORD, rawNewPassword), "密码长度为 6 到 32 位,可以包含字母、数字、下划线,特殊字符,同时包含字母和数字");
|
||||
.isMatch(RegexConstants.PASSWORD, rawNewPassword), "密码长度为 6 到 32 位,可以包含字母、数字、下划线,特殊字符,同时包含字母和数字");
|
||||
userService.updatePassword(rawOldPassword, rawNewPassword, LoginHelper.getUserId());
|
||||
return R.ok("修改成功,请牢记你的新密码");
|
||||
}
|
||||
@ -105,7 +105,7 @@ public class UserCenterController {
|
||||
@PatchMapping("/phone")
|
||||
public R<Void> updatePhone(@Validated @RequestBody UserPhoneUpdateReq updateReq) {
|
||||
String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
|
||||
.getOldPassword()));
|
||||
.getOldPassword()));
|
||||
ValidationUtils.throwIfBlank(rawOldPassword, DECRYPT_FAILED);
|
||||
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + updateReq.getPhone();
|
||||
String captcha = RedisUtils.get(captchaKey);
|
||||
@ -120,7 +120,7 @@ public class UserCenterController {
|
||||
@PatchMapping("/email")
|
||||
public R<Void> updateEmail(@Validated @RequestBody UserEmailUpdateRequest updateReq) {
|
||||
String rawOldPassword = ExceptionUtils.exToNull(() -> SecureUtils.decryptByRsaPrivateKey(updateReq
|
||||
.getOldPassword()));
|
||||
.getOldPassword()));
|
||||
ValidationUtils.throwIfBlank(rawOldPassword, DECRYPT_FAILED);
|
||||
String captchaKey = CacheConstants.CAPTCHA_KEY_PREFIX + updateReq.getEmail();
|
||||
String captcha = RedisUtils.get(captchaKey);
|
||||
|
@ -18,7 +18,6 @@ package com.zayac.admin;
|
||||
|
||||
import com.zayac.admin.schedule.CheckRegAndDep;
|
||||
import com.zayac.admin.system.service.DeptService;
|
||||
import com.zayac.admin.system.service.UserService;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.beans.factory.annotation.Autowired;
|
||||
import org.springframework.boot.test.context.SpringBootTest;
|
||||
|
Loading…
Reference in New Issue
Block a user