z
增加灵仙游戏,修复房间排序,数据大屏增加
This commit is contained in:
parent
19675dfe19
commit
ff9b790de5
@ -107,6 +107,14 @@ lucky:
|
|||||||
app_id: ${LIKEI_GAME_BAISHUN_APP_ID}
|
app_id: ${LIKEI_GAME_BAISHUN_APP_ID}
|
||||||
app_channel: ${LIKEI_GAME_BAISHUN_APP_CHANNEL}
|
app_channel: ${LIKEI_GAME_BAISHUN_APP_CHANNEL}
|
||||||
|
|
||||||
|
invite:
|
||||||
|
campaign:
|
||||||
|
go:
|
||||||
|
enabled: ${INVITE_CAMPAIGN_GO_ENABLED:true}
|
||||||
|
base-url: ${INVITE_CAMPAIGN_GO_URL:}
|
||||||
|
internal-token: ${GAME_INTERNAL_CALLBACK_SECRET:}
|
||||||
|
timeout-millis: ${INVITE_CAMPAIGN_GO_TIMEOUT_MILLIS:10000}
|
||||||
|
|
||||||
activity:
|
activity:
|
||||||
ranking:
|
ranking:
|
||||||
reward:
|
reward:
|
||||||
|
|||||||
@ -29,6 +29,11 @@ public class LoginLoggerEvent implements Serializable {
|
|||||||
@JsonSerialize(using = ToStringSerializer.class)
|
@JsonSerialize(using = ToStringSerializer.class)
|
||||||
private String invitePeople;
|
private String invitePeople;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邀请活动邀请码(注册时才有).
|
||||||
|
*/
|
||||||
|
private String invitationCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 0.注册登录,1.登陆.
|
* 0.注册登录,1.登陆.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -25,6 +25,11 @@ public class CreateAccountCmd extends AppExtCommand {
|
|||||||
*/
|
*/
|
||||||
private String invitePeople;
|
private String invitePeople;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 邀请活动邀请码.
|
||||||
|
*/
|
||||||
|
private String invitationCode;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 注册类型:MOBILE、FACEBOOK、GOOGLE、APPLE、Snapchat.
|
* 注册类型:MOBILE、FACEBOOK、GOOGLE、APPLE、Snapchat.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -5,6 +5,7 @@ import com.red.circle.console.app.dto.clientobject.datav.CountryDashboardMetricC
|
|||||||
import com.red.circle.console.app.dto.cmd.datav.CountryDashboardQryCmd;
|
import com.red.circle.console.app.dto.cmd.datav.CountryDashboardQryCmd;
|
||||||
import com.red.circle.console.infra.database.rds.dao.datav.CountryDashboardDAO;
|
import com.red.circle.console.infra.database.rds.dao.datav.CountryDashboardDAO;
|
||||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardMetricDTO;
|
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardMetricDTO;
|
||||||
|
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardRetentionUserDTO;
|
||||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardUserCountryDTO;
|
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardUserCountryDTO;
|
||||||
import java.math.BigDecimal;
|
import java.math.BigDecimal;
|
||||||
import java.math.RoundingMode;
|
import java.math.RoundingMode;
|
||||||
@ -19,11 +20,13 @@ import java.util.ArrayList;
|
|||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
import java.util.HashSet;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Objects;
|
import java.util.Objects;
|
||||||
|
import java.util.Set;
|
||||||
import java.util.function.BiConsumer;
|
import java.util.function.BiConsumer;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
@ -32,6 +35,7 @@ import org.springframework.data.mongodb.core.MongoTemplate;
|
|||||||
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
import org.springframework.data.mongodb.core.aggregation.Aggregation;
|
||||||
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
|
import org.springframework.data.mongodb.core.aggregation.AggregationOperation;
|
||||||
import org.springframework.data.mongodb.core.query.Criteria;
|
import org.springframework.data.mongodb.core.query.Criteria;
|
||||||
|
import org.springframework.data.mongodb.core.query.Query;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -42,6 +46,7 @@ import org.springframework.stereotype.Service;
|
|||||||
public class CountryDashboardServiceImpl implements CountryDashboardService {
|
public class CountryDashboardServiceImpl implements CountryDashboardService {
|
||||||
|
|
||||||
private static final String COLLECTION_GIFT_GIVE_RUNNING_WATER = "gift_give_running_water";
|
private static final String COLLECTION_GIFT_GIVE_RUNNING_WATER = "gift_give_running_water";
|
||||||
|
private static final String COLLECTION_USER_DAILY_ACTIVE_LOG = "user_daily_active_log";
|
||||||
private static final String PERIOD_ALL = "ALL";
|
private static final String PERIOD_ALL = "ALL";
|
||||||
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
private static final DateTimeFormatter DATE_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd");
|
||||||
private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
|
private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM");
|
||||||
@ -79,11 +84,16 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
|||||||
mergeSql(rows, countryDashboardDAO.listLuckyBoxGame(
|
mergeSql(rows, countryDashboardDAO.listLuckyBoxGame(
|
||||||
condition.periodType, condition.startTime, condition.endTime,
|
condition.periodType, condition.startTime, condition.endTime,
|
||||||
condition.countryKeyword, condition.sysOrigin));
|
condition.countryKeyword, condition.sysOrigin));
|
||||||
|
mergeSql(rows, countryDashboardDAO.listGameUser(
|
||||||
|
condition.periodType, condition.startTime, condition.endTime,
|
||||||
|
condition.countryKeyword, condition.sysOrigin));
|
||||||
|
|
||||||
mergeMongo(rows, condition, listGiftConsume(condition),
|
mergeMongo(rows, condition, listGiftConsume(condition),
|
||||||
(row, amount) -> row.setGiftConsume(add(row.getGiftConsume(), amount)));
|
(row, amount) -> row.setGiftConsume(add(row.getGiftConsume(), amount)));
|
||||||
mergeMongo(rows, condition, listLuckyGiftAnchorShare(condition),
|
mergeMongo(rows, condition, listLuckyGiftAnchorShare(condition),
|
||||||
(row, amount) -> row.setLuckyGiftAnchorShare(add(row.getLuckyGiftAnchorShare(), amount)));
|
(row, amount) -> row.setLuckyGiftAnchorShare(add(row.getLuckyGiftAnchorShare(), amount)));
|
||||||
|
mergeDailyActive(rows, condition, listDailyActive(condition));
|
||||||
|
mergeRetention(rows, condition);
|
||||||
|
|
||||||
List<CountryDashboardMetricCO> records = rows.values().stream()
|
List<CountryDashboardMetricCO> records = rows.values().stream()
|
||||||
.peek(this::calculateFormulaFields)
|
.peek(this::calculateFormulaFields)
|
||||||
@ -118,8 +128,10 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
|||||||
row.setDealerRecharge(add(row.getDealerRecharge(), metric.getDealerRecharge()));
|
row.setDealerRecharge(add(row.getDealerRecharge(), metric.getDealerRecharge()));
|
||||||
row.setSalaryExchange(add(row.getSalaryExchange(), metric.getSalaryExchange()));
|
row.setSalaryExchange(add(row.getSalaryExchange(), metric.getSalaryExchange()));
|
||||||
row.setLuckyGiftTotalFlow(add(row.getLuckyGiftTotalFlow(), metric.getLuckyGiftTotalFlow()));
|
row.setLuckyGiftTotalFlow(add(row.getLuckyGiftTotalFlow(), metric.getLuckyGiftTotalFlow()));
|
||||||
|
row.setLuckyGiftUser(row.getLuckyGiftUser() + safeLong(metric.getLuckyGiftUser()));
|
||||||
row.setLuckyGiftPayout(add(row.getLuckyGiftPayout(), metric.getLuckyGiftPayout()));
|
row.setLuckyGiftPayout(add(row.getLuckyGiftPayout(), metric.getLuckyGiftPayout()));
|
||||||
row.setGameTotalFlow(add(row.getGameTotalFlow(), metric.getGameTotalFlow()));
|
row.setGameTotalFlow(add(row.getGameTotalFlow(), metric.getGameTotalFlow()));
|
||||||
|
row.setGameUser(row.getGameUser() + safeLong(metric.getGameUser()));
|
||||||
row.setGamePayout(add(row.getGamePayout(), metric.getGamePayout()));
|
row.setGamePayout(add(row.getGamePayout(), metric.getGamePayout()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -145,6 +157,66 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void mergeDailyActive(Map<String, CountryDashboardMetricCO> rows, QueryCondition condition,
|
||||||
|
List<MongoUserDayMetric> metrics) {
|
||||||
|
if (metrics == null || metrics.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<Long, CountryDashboardUserCountryDTO> countryMap = mapUserCountries(
|
||||||
|
metrics.stream().map(MongoUserDayMetric::userId).filter(Objects::nonNull).distinct().toList());
|
||||||
|
Set<String> counted = new HashSet<>();
|
||||||
|
|
||||||
|
for (MongoUserDayMetric metric : metrics) {
|
||||||
|
CountryDashboardUserCountryDTO country = countryMap.get(metric.userId());
|
||||||
|
if (country == null || !matchesCountry(country, condition.countryKeyword)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PeriodBucket bucket = condition.toPeriodBucket(metric.day());
|
||||||
|
String dedupeKey = country.getCountryCode() + "|" + bucket.key() + "|" + metric.userId();
|
||||||
|
if (!counted.add(dedupeKey)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
CountryDashboardMetricCO row = getRow(rows, country.getCountryCode(), country.getCountryName(),
|
||||||
|
bucket.key(), bucket.name());
|
||||||
|
row.setDailyActiveUser(row.getDailyActiveUser() + 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mergeRetention(Map<String, CountryDashboardMetricCO> rows, QueryCondition condition) {
|
||||||
|
List<CountryDashboardRetentionUserDTO> cohorts = countryDashboardDAO.listNewUserCohorts(
|
||||||
|
condition.periodType, condition.startTime, condition.endTime,
|
||||||
|
condition.countryKeyword, condition.sysOrigin);
|
||||||
|
if (cohorts == null || cohorts.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
LocalDate today = LocalDate.now();
|
||||||
|
Set<Long> userIds = new HashSet<>();
|
||||||
|
Set<String> activeDates = new HashSet<>();
|
||||||
|
for (CountryDashboardRetentionUserDTO cohort : cohorts) {
|
||||||
|
if (cohort.getUserId() == null || !hasText(cohort.getRegisterDate())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
userIds.add(cohort.getUserId());
|
||||||
|
collectRetentionActiveDate(activeDates, cohort.getRegisterDate(), 1, today);
|
||||||
|
collectRetentionActiveDate(activeDates, cohort.getRegisterDate(), 7, today);
|
||||||
|
collectRetentionActiveDate(activeDates, cohort.getRegisterDate(), 30, today);
|
||||||
|
}
|
||||||
|
|
||||||
|
Set<String> activeUserDates = listActiveUserDateSet(userIds, activeDates, condition.sysOrigin);
|
||||||
|
for (CountryDashboardRetentionUserDTO cohort : cohorts) {
|
||||||
|
if (cohort.getUserId() == null || !hasText(cohort.getRegisterDate())) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
CountryDashboardMetricCO row = getRow(rows, cohort.getCountryCode(), cohort.getCountryName(),
|
||||||
|
cohort.getPeriodKey(), cohort.getPeriodName());
|
||||||
|
mergeRetentionDay(row, cohort.getUserId(), cohort.getRegisterDate(), 1, today, activeUserDates);
|
||||||
|
mergeRetentionDay(row, cohort.getUserId(), cohort.getRegisterDate(), 7, today, activeUserDates);
|
||||||
|
mergeRetentionDay(row, cohort.getUserId(), cohort.getRegisterDate(), 30, today, activeUserDates);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<MongoMetric> listGiftConsume(QueryCondition condition) {
|
private List<MongoMetric> listGiftConsume(QueryCondition condition) {
|
||||||
List<Criteria> criteria = baseGiftCriteria(condition);
|
List<Criteria> criteria = baseGiftCriteria(condition);
|
||||||
criteria.add(Criteria.where("luckyGift").ne(Boolean.TRUE));
|
criteria.add(Criteria.where("luckyGift").ne(Boolean.TRUE));
|
||||||
@ -163,6 +235,23 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
|||||||
return aggregateGiftAmount(criteria, "$acceptUsers.targetAmount", true);
|
return aggregateGiftAmount(criteria, "$acceptUsers.targetAmount", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<MongoUserDayMetric> listDailyActive(QueryCondition condition) {
|
||||||
|
List<Criteria> criteria = baseActiveCriteria(condition);
|
||||||
|
List<AggregationOperation> operations = new ArrayList<>();
|
||||||
|
if (!criteria.isEmpty()) {
|
||||||
|
operations.add(Aggregation.match(new Criteria().andOperator(criteria.toArray(new Criteria[0]))));
|
||||||
|
}
|
||||||
|
operations.add(Aggregation.group("userId", "activeDate"));
|
||||||
|
|
||||||
|
return mongoTemplate.aggregate(Aggregation.newAggregation(operations),
|
||||||
|
COLLECTION_USER_DAILY_ACTIVE_LOG, Document.class)
|
||||||
|
.getMappedResults()
|
||||||
|
.stream()
|
||||||
|
.map(this::toMongoUserDayMetric)
|
||||||
|
.filter(Objects::nonNull)
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
private List<Criteria> baseGiftCriteria(QueryCondition condition) {
|
private List<Criteria> baseGiftCriteria(QueryCondition condition) {
|
||||||
List<Criteria> criteria = new ArrayList<>();
|
List<Criteria> criteria = new ArrayList<>();
|
||||||
criteria.add(Criteria.where("refunded").ne(Boolean.TRUE));
|
criteria.add(Criteria.where("refunded").ne(Boolean.TRUE));
|
||||||
@ -179,6 +268,27 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
|||||||
return criteria;
|
return criteria;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<Criteria> baseActiveCriteria(QueryCondition condition) {
|
||||||
|
List<Criteria> criteria = new ArrayList<>();
|
||||||
|
Criteria activeDate = Criteria.where("activeDate");
|
||||||
|
boolean hasActiveDateCriteria = false;
|
||||||
|
if (hasText(condition.startDate)) {
|
||||||
|
activeDate = activeDate.gte(condition.startDate);
|
||||||
|
hasActiveDateCriteria = true;
|
||||||
|
}
|
||||||
|
if (hasText(condition.endDate)) {
|
||||||
|
activeDate = activeDate.lte(condition.endDate);
|
||||||
|
hasActiveDateCriteria = true;
|
||||||
|
}
|
||||||
|
if (hasActiveDateCriteria) {
|
||||||
|
criteria.add(activeDate);
|
||||||
|
}
|
||||||
|
if (hasText(condition.sysOrigin)) {
|
||||||
|
criteria.add(Criteria.where("sysOrigin").is(condition.sysOrigin));
|
||||||
|
}
|
||||||
|
return criteria;
|
||||||
|
}
|
||||||
|
|
||||||
private List<MongoMetric> aggregateGiftAmount(List<Criteria> criteria, String amountPath,
|
private List<MongoMetric> aggregateGiftAmount(List<Criteria> criteria, String amountPath,
|
||||||
boolean unwindAcceptUsers) {
|
boolean unwindAcceptUsers) {
|
||||||
List<AggregationOperation> operations = new ArrayList<>();
|
List<AggregationOperation> operations = new ArrayList<>();
|
||||||
@ -220,6 +330,79 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
|||||||
return new MongoMetric(userId, day, amount);
|
return new MongoMetric(userId, day, amount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private MongoUserDayMetric toMongoUserDayMetric(Document document) {
|
||||||
|
Object id = document.get("_id");
|
||||||
|
if (!(id instanceof Document idDoc)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
Long userId = toLong(idDoc.get("userId"));
|
||||||
|
String day = Objects.toString(idDoc.get("activeDate"), null);
|
||||||
|
if (userId == null || !hasText(day)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return new MongoUserDayMetric(userId, day);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void collectRetentionActiveDate(Set<String> activeDates, String registerDate,
|
||||||
|
int retentionDays, LocalDate today) {
|
||||||
|
LocalDate targetDate = LocalDate.parse(registerDate, DATE_FORMATTER).plusDays(retentionDays);
|
||||||
|
if (!targetDate.isAfter(today)) {
|
||||||
|
activeDates.add(DATE_FORMATTER.format(targetDate));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void mergeRetentionDay(CountryDashboardMetricCO row, Long userId, String registerDate,
|
||||||
|
int retentionDays, LocalDate today, Set<String> activeUserDates) {
|
||||||
|
LocalDate targetDate = LocalDate.parse(registerDate, DATE_FORMATTER).plusDays(retentionDays);
|
||||||
|
if (targetDate.isAfter(today)) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
String activeKey = userId + "|" + DATE_FORMATTER.format(targetDate);
|
||||||
|
boolean retained = activeUserDates.contains(activeKey);
|
||||||
|
if (retentionDays == 1) {
|
||||||
|
row.setD1RetentionBaseUser(row.getD1RetentionBaseUser() + 1);
|
||||||
|
row.setD1RetentionUser(row.getD1RetentionUser() + (retained ? 1 : 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (retentionDays == 7) {
|
||||||
|
row.setD7RetentionBaseUser(row.getD7RetentionBaseUser() + 1);
|
||||||
|
row.setD7RetentionUser(row.getD7RetentionUser() + (retained ? 1 : 0));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
row.setD30RetentionBaseUser(row.getD30RetentionBaseUser() + 1);
|
||||||
|
row.setD30RetentionUser(row.getD30RetentionUser() + (retained ? 1 : 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
private Set<String> listActiveUserDateSet(Set<Long> userIds, Set<String> activeDates,
|
||||||
|
String sysOrigin) {
|
||||||
|
Set<String> result = new HashSet<>();
|
||||||
|
if (userIds == null || userIds.isEmpty() || activeDates == null || activeDates.isEmpty()) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<Long> ids = new ArrayList<>(userIds);
|
||||||
|
for (int start = 0; start < ids.size(); start += USER_COUNTRY_BATCH_SIZE) {
|
||||||
|
int end = Math.min(start + USER_COUNTRY_BATCH_SIZE, ids.size());
|
||||||
|
List<Criteria> criteria = new ArrayList<>();
|
||||||
|
criteria.add(Criteria.where("userId").in(ids.subList(start, end)));
|
||||||
|
criteria.add(Criteria.where("activeDate").in(activeDates));
|
||||||
|
if (hasText(sysOrigin)) {
|
||||||
|
criteria.add(Criteria.where("sysOrigin").is(sysOrigin));
|
||||||
|
}
|
||||||
|
Query query = Query.query(new Criteria().andOperator(criteria.toArray(new Criteria[0])));
|
||||||
|
query.fields().include("userId").include("activeDate");
|
||||||
|
List<Document> documents = mongoTemplate.find(query, Document.class, COLLECTION_USER_DAILY_ACTIVE_LOG);
|
||||||
|
for (Document document : documents) {
|
||||||
|
Long userId = toLong(document.get("userId"));
|
||||||
|
String activeDate = Objects.toString(document.get("activeDate"), null);
|
||||||
|
if (userId != null && hasText(activeDate)) {
|
||||||
|
result.add(userId + "|" + activeDate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
private Map<Long, CountryDashboardUserCountryDTO> mapUserCountries(Collection<Long> userIds) {
|
private Map<Long, CountryDashboardUserCountryDTO> mapUserCountries(Collection<Long> userIds) {
|
||||||
Map<Long, CountryDashboardUserCountryDTO> result = new HashMap<>();
|
Map<Long, CountryDashboardUserCountryDTO> result = new HashMap<>();
|
||||||
if (userIds == null || userIds.isEmpty()) {
|
if (userIds == null || userIds.isEmpty()) {
|
||||||
@ -268,8 +451,15 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
|||||||
row.setLuckyGiftProfit(safe(row.getLuckyGiftTotalFlow())
|
row.setLuckyGiftProfit(safe(row.getLuckyGiftTotalFlow())
|
||||||
.subtract(safe(row.getLuckyGiftPayout()))
|
.subtract(safe(row.getLuckyGiftPayout()))
|
||||||
.subtract(safe(row.getLuckyGiftAnchorShare())));
|
.subtract(safe(row.getLuckyGiftAnchorShare())));
|
||||||
|
row.setLuckyGiftUserRate(percent(row.getLuckyGiftUser(), row.getDailyActiveUser()));
|
||||||
|
row.setLuckyGiftPayoutRate(percent(row.getLuckyGiftPayout(), row.getLuckyGiftTotalFlow()));
|
||||||
row.setLuckyGiftProfitRate(percent(row.getLuckyGiftProfit(), row.getLuckyGiftTotalFlow()));
|
row.setLuckyGiftProfitRate(percent(row.getLuckyGiftProfit(), row.getLuckyGiftTotalFlow()));
|
||||||
row.setGameProfit(safe(row.getGameTotalFlow()).subtract(safe(row.getGamePayout())));
|
row.setGameProfit(safe(row.getGameTotalFlow()).subtract(safe(row.getGamePayout())));
|
||||||
|
row.setGamePayoutRate(percent(row.getGamePayout(), row.getGameTotalFlow()));
|
||||||
|
row.setGameProfitRate(percent(row.getGameProfit(), row.getGameTotalFlow()));
|
||||||
|
row.setD1RetentionRate(percent(row.getD1RetentionUser(), row.getD1RetentionBaseUser()));
|
||||||
|
row.setD7RetentionRate(percent(row.getD7RetentionUser(), row.getD7RetentionBaseUser()));
|
||||||
|
row.setD30RetentionRate(percent(row.getD30RetentionUser(), row.getD30RetentionBaseUser()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private CountryDashboardMetricCO buildTotal(List<CountryDashboardMetricCO> records,
|
private CountryDashboardMetricCO buildTotal(List<CountryDashboardMetricCO> records,
|
||||||
@ -281,16 +471,25 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
|||||||
.setPeriodName("合计");
|
.setPeriodName("合计");
|
||||||
for (CountryDashboardMetricCO row : records) {
|
for (CountryDashboardMetricCO row : records) {
|
||||||
total.setCountryNewUser(total.getCountryNewUser() + safeLong(row.getCountryNewUser()));
|
total.setCountryNewUser(total.getCountryNewUser() + safeLong(row.getCountryNewUser()));
|
||||||
|
total.setDailyActiveUser(total.getDailyActiveUser() + safeLong(row.getDailyActiveUser()));
|
||||||
total.setNewUserRecharge(add(total.getNewUserRecharge(), row.getNewUserRecharge()));
|
total.setNewUserRecharge(add(total.getNewUserRecharge(), row.getNewUserRecharge()));
|
||||||
total.setOfficialRecharge(add(total.getOfficialRecharge(), row.getOfficialRecharge()));
|
total.setOfficialRecharge(add(total.getOfficialRecharge(), row.getOfficialRecharge()));
|
||||||
total.setDealerRecharge(add(total.getDealerRecharge(), row.getDealerRecharge()));
|
total.setDealerRecharge(add(total.getDealerRecharge(), row.getDealerRecharge()));
|
||||||
total.setSalaryExchange(add(total.getSalaryExchange(), row.getSalaryExchange()));
|
total.setSalaryExchange(add(total.getSalaryExchange(), row.getSalaryExchange()));
|
||||||
total.setGiftConsume(add(total.getGiftConsume(), row.getGiftConsume()));
|
total.setGiftConsume(add(total.getGiftConsume(), row.getGiftConsume()));
|
||||||
total.setLuckyGiftTotalFlow(add(total.getLuckyGiftTotalFlow(), row.getLuckyGiftTotalFlow()));
|
total.setLuckyGiftTotalFlow(add(total.getLuckyGiftTotalFlow(), row.getLuckyGiftTotalFlow()));
|
||||||
|
total.setLuckyGiftUser(total.getLuckyGiftUser() + safeLong(row.getLuckyGiftUser()));
|
||||||
total.setLuckyGiftPayout(add(total.getLuckyGiftPayout(), row.getLuckyGiftPayout()));
|
total.setLuckyGiftPayout(add(total.getLuckyGiftPayout(), row.getLuckyGiftPayout()));
|
||||||
total.setLuckyGiftAnchorShare(add(total.getLuckyGiftAnchorShare(), row.getLuckyGiftAnchorShare()));
|
total.setLuckyGiftAnchorShare(add(total.getLuckyGiftAnchorShare(), row.getLuckyGiftAnchorShare()));
|
||||||
total.setGameTotalFlow(add(total.getGameTotalFlow(), row.getGameTotalFlow()));
|
total.setGameTotalFlow(add(total.getGameTotalFlow(), row.getGameTotalFlow()));
|
||||||
|
total.setGameUser(total.getGameUser() + safeLong(row.getGameUser()));
|
||||||
total.setGamePayout(add(total.getGamePayout(), row.getGamePayout()));
|
total.setGamePayout(add(total.getGamePayout(), row.getGamePayout()));
|
||||||
|
total.setD1RetentionUser(total.getD1RetentionUser() + safeLong(row.getD1RetentionUser()));
|
||||||
|
total.setD1RetentionBaseUser(total.getD1RetentionBaseUser() + safeLong(row.getD1RetentionBaseUser()));
|
||||||
|
total.setD7RetentionUser(total.getD7RetentionUser() + safeLong(row.getD7RetentionUser()));
|
||||||
|
total.setD7RetentionBaseUser(total.getD7RetentionBaseUser() + safeLong(row.getD7RetentionBaseUser()));
|
||||||
|
total.setD30RetentionUser(total.getD30RetentionUser() + safeLong(row.getD30RetentionUser()));
|
||||||
|
total.setD30RetentionBaseUser(total.getD30RetentionBaseUser() + safeLong(row.getD30RetentionBaseUser()));
|
||||||
}
|
}
|
||||||
calculateFormulaFields(total);
|
calculateFormulaFields(total);
|
||||||
return total;
|
return total;
|
||||||
@ -314,6 +513,10 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
|||||||
.divide(safeDenominator, 4, RoundingMode.HALF_UP);
|
.divide(safeDenominator, 4, RoundingMode.HALF_UP);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static BigDecimal percent(Long numerator, Long denominator) {
|
||||||
|
return percent(BigDecimal.valueOf(safeLong(numerator)), BigDecimal.valueOf(safeLong(denominator)));
|
||||||
|
}
|
||||||
|
|
||||||
private static long safeLong(Long value) {
|
private static long safeLong(Long value) {
|
||||||
return value == null ? 0L : value;
|
return value == null ? 0L : value;
|
||||||
}
|
}
|
||||||
@ -351,6 +554,9 @@ public class CountryDashboardServiceImpl implements CountryDashboardService {
|
|||||||
private record MongoMetric(Long userId, String day, BigDecimal amount) {
|
private record MongoMetric(Long userId, String day, BigDecimal amount) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private record MongoUserDayMetric(Long userId, String day) {
|
||||||
|
}
|
||||||
|
|
||||||
private record PeriodBucket(String key, String name) {
|
private record PeriodBucket(String key, String name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -26,6 +26,8 @@ public class CountryDashboardMetricCO implements Serializable {
|
|||||||
|
|
||||||
private Long countryNewUser = 0L;
|
private Long countryNewUser = 0L;
|
||||||
|
|
||||||
|
private Long dailyActiveUser = 0L;
|
||||||
|
|
||||||
private BigDecimal newUserRecharge = BigDecimal.ZERO;
|
private BigDecimal newUserRecharge = BigDecimal.ZERO;
|
||||||
|
|
||||||
private BigDecimal dealerRecharge = BigDecimal.ZERO;
|
private BigDecimal dealerRecharge = BigDecimal.ZERO;
|
||||||
@ -38,8 +40,14 @@ public class CountryDashboardMetricCO implements Serializable {
|
|||||||
|
|
||||||
private BigDecimal luckyGiftTotalFlow = BigDecimal.ZERO;
|
private BigDecimal luckyGiftTotalFlow = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
private Long luckyGiftUser = 0L;
|
||||||
|
|
||||||
|
private BigDecimal luckyGiftUserRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
private BigDecimal luckyGiftPayout = BigDecimal.ZERO;
|
private BigDecimal luckyGiftPayout = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
private BigDecimal luckyGiftPayoutRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
private BigDecimal luckyGiftAnchorShare = BigDecimal.ZERO;
|
private BigDecimal luckyGiftAnchorShare = BigDecimal.ZERO;
|
||||||
|
|
||||||
private BigDecimal luckyGiftProfit = BigDecimal.ZERO;
|
private BigDecimal luckyGiftProfit = BigDecimal.ZERO;
|
||||||
@ -48,10 +56,34 @@ public class CountryDashboardMetricCO implements Serializable {
|
|||||||
|
|
||||||
private BigDecimal gameTotalFlow = BigDecimal.ZERO;
|
private BigDecimal gameTotalFlow = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
private Long gameUser = 0L;
|
||||||
|
|
||||||
private BigDecimal gamePayout = BigDecimal.ZERO;
|
private BigDecimal gamePayout = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
private BigDecimal gamePayoutRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
private BigDecimal gameProfit = BigDecimal.ZERO;
|
private BigDecimal gameProfit = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
private BigDecimal gameProfitRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
private Long d1RetentionUser = 0L;
|
||||||
|
|
||||||
|
private Long d1RetentionBaseUser = 0L;
|
||||||
|
|
||||||
|
private BigDecimal d1RetentionRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
private Long d7RetentionUser = 0L;
|
||||||
|
|
||||||
|
private Long d7RetentionBaseUser = 0L;
|
||||||
|
|
||||||
|
private BigDecimal d7RetentionRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
|
private Long d30RetentionUser = 0L;
|
||||||
|
|
||||||
|
private Long d30RetentionBaseUser = 0L;
|
||||||
|
|
||||||
|
private BigDecimal d30RetentionRate = BigDecimal.ZERO;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 官方直充总额,服务端用于计算总充值.
|
* 官方直充总额,服务端用于计算总充值.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
package com.red.circle.console.infra.database.rds.dao.datav;
|
package com.red.circle.console.infra.database.rds.dao.datav;
|
||||||
|
|
||||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardMetricDTO;
|
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardMetricDTO;
|
||||||
|
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardRetentionUserDTO;
|
||||||
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardUserCountryDTO;
|
import com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardUserCountryDTO;
|
||||||
import com.red.circle.console.infra.database.rds.entity.app.IUserBaseInfo;
|
import com.red.circle.console.infra.database.rds.entity.app.IUserBaseInfo;
|
||||||
import com.red.circle.framework.mybatis.dao.BaseDAO;
|
import com.red.circle.framework.mybatis.dao.BaseDAO;
|
||||||
@ -70,5 +71,19 @@ public interface CountryDashboardDAO extends BaseDAO<IUserBaseInfo> {
|
|||||||
@Param("countryKeyword") String countryKeyword,
|
@Param("countryKeyword") String countryKeyword,
|
||||||
@Param("sysOrigin") String sysOrigin);
|
@Param("sysOrigin") String sysOrigin);
|
||||||
|
|
||||||
|
List<CountryDashboardMetricDTO> listGameUser(
|
||||||
|
@Param("periodType") String periodType,
|
||||||
|
@Param("startTime") LocalDateTime startTime,
|
||||||
|
@Param("endTime") LocalDateTime endTime,
|
||||||
|
@Param("countryKeyword") String countryKeyword,
|
||||||
|
@Param("sysOrigin") String sysOrigin);
|
||||||
|
|
||||||
|
List<CountryDashboardRetentionUserDTO> listNewUserCohorts(
|
||||||
|
@Param("periodType") String periodType,
|
||||||
|
@Param("startTime") LocalDateTime startTime,
|
||||||
|
@Param("endTime") LocalDateTime endTime,
|
||||||
|
@Param("countryKeyword") String countryKeyword,
|
||||||
|
@Param("sysOrigin") String sysOrigin);
|
||||||
|
|
||||||
List<CountryDashboardUserCountryDTO> listUserCountryByIds(@Param("userIds") Collection<Long> userIds);
|
List<CountryDashboardUserCountryDTO> listUserCountryByIds(@Param("userIds") Collection<Long> userIds);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -31,9 +31,13 @@ public class CountryDashboardMetricDTO {
|
|||||||
|
|
||||||
private BigDecimal luckyGiftTotalFlow;
|
private BigDecimal luckyGiftTotalFlow;
|
||||||
|
|
||||||
|
private Long luckyGiftUser;
|
||||||
|
|
||||||
private BigDecimal luckyGiftPayout;
|
private BigDecimal luckyGiftPayout;
|
||||||
|
|
||||||
private BigDecimal gameTotalFlow;
|
private BigDecimal gameTotalFlow;
|
||||||
|
|
||||||
|
private Long gameUser;
|
||||||
|
|
||||||
private BigDecimal gamePayout;
|
private BigDecimal gamePayout;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,24 @@
|
|||||||
|
package com.red.circle.console.infra.database.rds.dto.datav;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 国家数据大屏新增用户留存队列.
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public class CountryDashboardRetentionUserDTO {
|
||||||
|
|
||||||
|
private Long userId;
|
||||||
|
|
||||||
|
private String countryCode;
|
||||||
|
|
||||||
|
private String countryName;
|
||||||
|
|
||||||
|
private String periodKey;
|
||||||
|
|
||||||
|
private String periodName;
|
||||||
|
|
||||||
|
private String registerDate;
|
||||||
|
}
|
||||||
@ -117,6 +117,43 @@
|
|||||||
FROM likei_wallet.wallet_gold_asset_record_12
|
FROM likei_wallet.wallet_gold_asset_record_12
|
||||||
</sql>
|
</sql>
|
||||||
|
|
||||||
|
<sql id="WalletGameEventWhere">
|
||||||
|
(
|
||||||
|
t.event_type IN (
|
||||||
|
'GAME_SLOT_MACHINE',
|
||||||
|
'GAME_BARBECUE',
|
||||||
|
'TURNTABLE_GAME',
|
||||||
|
'LUDO_GAME',
|
||||||
|
'GAME_BURST_CRYSTAL',
|
||||||
|
'EGG',
|
||||||
|
'FRUIT_GAME',
|
||||||
|
'DOUBLE_LAYER_FRUIT',
|
||||||
|
'DOUBLE_LAYER_BARBECUE',
|
||||||
|
'GAME_FRUIT_BET',
|
||||||
|
'GAME_FRUIT_AWARD',
|
||||||
|
'GAME_BACK',
|
||||||
|
'GAME_WINNING',
|
||||||
|
'LUDO_VICTORY',
|
||||||
|
'LUDO_REFUND',
|
||||||
|
'GAME_LUDO_REFUND',
|
||||||
|
'USD_GAME_WIN',
|
||||||
|
'USD_GAME_REFUND',
|
||||||
|
'USD_GAME_REFUND_DRAW',
|
||||||
|
'TEEN_PATTI',
|
||||||
|
'BET_TEEN_PATTI',
|
||||||
|
'LOTTERY_NUMBER_BET',
|
||||||
|
'LOTTERY_REWARD'
|
||||||
|
)
|
||||||
|
OR t.event_type LIKE 'BAISHUN_GAME%'
|
||||||
|
OR t.event_type LIKE 'YOMI_GAME%'
|
||||||
|
OR t.event_type LIKE 'HKYS_GAME%'
|
||||||
|
OR t.event_type LIKE 'HOT_GAME%'
|
||||||
|
)
|
||||||
|
AND t.event_type NOT IN ('LUCKY_GIFT', 'LUCKY_GIFT_GOLD_REWARD', 'LUCKY_BOX_GAME', 'NEW_LUCKY_BOX_GAME', 'GAME_RAKE')
|
||||||
|
AND t.event_type NOT LIKE 'GIVE_GIFT%'
|
||||||
|
AND t.event_type NOT LIKE 'ACCEPT_GIFT%'
|
||||||
|
</sql>
|
||||||
|
|
||||||
<select id="listNewUsers"
|
<select id="listNewUsers"
|
||||||
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardMetricDTO">
|
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardMetricDTO">
|
||||||
SELECT
|
SELECT
|
||||||
@ -214,6 +251,7 @@
|
|||||||
<include refid="CountryColumns"/>,
|
<include refid="CountryColumns"/>,
|
||||||
<include refid="PeriodColumnsT"/>,
|
<include refid="PeriodColumnsT"/>,
|
||||||
IFNULL(SUM(t.pay_amount), 0) AS luckyGiftTotalFlow,
|
IFNULL(SUM(t.pay_amount), 0) AS luckyGiftTotalFlow,
|
||||||
|
COUNT(DISTINCT t.user_id) AS luckyGiftUser,
|
||||||
IFNULL(SUM(t.award_amount), 0) AS luckyGiftPayout
|
IFNULL(SUM(t.award_amount), 0) AS luckyGiftPayout
|
||||||
FROM game_lucky_gift_count t
|
FROM game_lucky_gift_count t
|
||||||
INNER JOIN user_base_info u ON u.id = t.user_id
|
INNER JOIN user_base_info u ON u.id = t.user_id
|
||||||
@ -235,40 +273,7 @@
|
|||||||
<include refid="WalletGoldAssetRecordSource"/>
|
<include refid="WalletGoldAssetRecordSource"/>
|
||||||
) t
|
) t
|
||||||
INNER JOIN user_base_info u ON u.id = t.user_id
|
INNER JOIN user_base_info u ON u.id = t.user_id
|
||||||
WHERE (
|
WHERE <include refid="WalletGameEventWhere"/>
|
||||||
t.event_type IN (
|
|
||||||
'GAME_SLOT_MACHINE',
|
|
||||||
'GAME_BARBECUE',
|
|
||||||
'TURNTABLE_GAME',
|
|
||||||
'LUDO_GAME',
|
|
||||||
'GAME_BURST_CRYSTAL',
|
|
||||||
'EGG',
|
|
||||||
'FRUIT_GAME',
|
|
||||||
'DOUBLE_LAYER_FRUIT',
|
|
||||||
'DOUBLE_LAYER_BARBECUE',
|
|
||||||
'GAME_FRUIT_BET',
|
|
||||||
'GAME_FRUIT_AWARD',
|
|
||||||
'GAME_BACK',
|
|
||||||
'GAME_WINNING',
|
|
||||||
'LUDO_VICTORY',
|
|
||||||
'LUDO_REFUND',
|
|
||||||
'GAME_LUDO_REFUND',
|
|
||||||
'USD_GAME_WIN',
|
|
||||||
'USD_GAME_REFUND',
|
|
||||||
'USD_GAME_REFUND_DRAW',
|
|
||||||
'TEEN_PATTI',
|
|
||||||
'BET_TEEN_PATTI',
|
|
||||||
'LOTTERY_NUMBER_BET',
|
|
||||||
'LOTTERY_REWARD'
|
|
||||||
)
|
|
||||||
OR t.event_type LIKE 'BAISHUN_GAME%'
|
|
||||||
OR t.event_type LIKE 'YOMI_GAME%'
|
|
||||||
OR t.event_type LIKE 'HKYS_GAME%'
|
|
||||||
OR t.event_type LIKE 'HOT_GAME%'
|
|
||||||
)
|
|
||||||
AND t.event_type NOT IN ('LUCKY_GIFT', 'LUCKY_GIFT_GOLD_REWARD', 'LUCKY_BOX_GAME', 'NEW_LUCKY_BOX_GAME', 'GAME_RAKE')
|
|
||||||
AND t.event_type NOT LIKE 'GIVE_GIFT%'
|
|
||||||
AND t.event_type NOT LIKE 'ACCEPT_GIFT%'
|
|
||||||
<include refid="UserWhere"/>
|
<include refid="UserWhere"/>
|
||||||
<include refid="SourceTimeWhere"/>
|
<include refid="SourceTimeWhere"/>
|
||||||
<include refid="SourceSysOriginWhere"/>
|
<include refid="SourceSysOriginWhere"/>
|
||||||
@ -291,6 +296,43 @@
|
|||||||
GROUP BY countryCode, countryName, periodKey, periodName
|
GROUP BY countryCode, countryName, periodKey, periodName
|
||||||
</select>
|
</select>
|
||||||
|
|
||||||
|
<select id="listGameUser"
|
||||||
|
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardMetricDTO">
|
||||||
|
SELECT
|
||||||
|
<include refid="CountryColumns"/>,
|
||||||
|
<include refid="PeriodColumnsT"/>,
|
||||||
|
COUNT(DISTINCT t.user_id) AS gameUser
|
||||||
|
FROM (
|
||||||
|
SELECT t.user_id, t.sys_origin, t.create_time
|
||||||
|
FROM (
|
||||||
|
<include refid="WalletGoldAssetRecordSource"/>
|
||||||
|
) t
|
||||||
|
WHERE <include refid="WalletGameEventWhere"/>
|
||||||
|
UNION ALL
|
||||||
|
SELECT user_id, sys_origin, create_time
|
||||||
|
FROM game_lucky_box_count
|
||||||
|
) t
|
||||||
|
INNER JOIN user_base_info u ON u.id = t.user_id
|
||||||
|
WHERE 1 = 1
|
||||||
|
<include refid="UserWhere"/>
|
||||||
|
<include refid="SourceTimeWhere"/>
|
||||||
|
<include refid="SourceSysOriginWhere"/>
|
||||||
|
GROUP BY countryCode, countryName, periodKey, periodName
|
||||||
|
</select>
|
||||||
|
|
||||||
|
<select id="listNewUserCohorts"
|
||||||
|
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardRetentionUserDTO">
|
||||||
|
SELECT
|
||||||
|
u.id AS userId,
|
||||||
|
<include refid="CountryColumns"/>,
|
||||||
|
<include refid="PeriodColumnsU"/>,
|
||||||
|
DATE_FORMAT(u.create_time, '%Y-%m-%d') AS registerDate
|
||||||
|
FROM user_base_info u
|
||||||
|
WHERE 1 = 1
|
||||||
|
<include refid="UserWhere"/>
|
||||||
|
<include refid="UserTimeWhere"/>
|
||||||
|
</select>
|
||||||
|
|
||||||
<select id="listUserCountryByIds"
|
<select id="listUserCountryByIds"
|
||||||
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardUserCountryDTO">
|
resultType="com.red.circle.console.infra.database.rds.dto.datav.CountryDashboardUserCountryDTO">
|
||||||
SELECT
|
SELECT
|
||||||
|
|||||||
@ -68,8 +68,9 @@ public class RoomVoiceDiscoverQryExe {
|
|||||||
private final GameLudoService gameLudoService;
|
private final GameLudoService gameLudoService;
|
||||||
private final UserProfileGateway userProfileGateway;
|
private final UserProfileGateway userProfileGateway;
|
||||||
private final CountryCodeAliasSupport countryCodeAliasSupport;
|
private final CountryCodeAliasSupport countryCodeAliasSupport;
|
||||||
|
|
||||||
private static final String EMPTY_ROOM_CACHE_KEY = "empty_room_cache:*";
|
private static final String EMPTY_ROOM_CACHE_KEY = "empty_room_cache:*";
|
||||||
|
private static final String MEMBER_QUANTITY_EXT_KEY = "memberQuantity";
|
||||||
private static final int DISCOVERY_LIMIT = 50;
|
private static final int DISCOVERY_LIMIT = 50;
|
||||||
|
|
||||||
public List<RoomVoiceProfileCO> execute(AppExtCommand cmd) {
|
public List<RoomVoiceProfileCO> execute(AppExtCommand cmd) {
|
||||||
@ -147,6 +148,7 @@ public class RoomVoiceDiscoverQryExe {
|
|||||||
roomList = mergeRoomList(roomList, roomVoiceProfileCommon.listRoomVoiceProfilesV2(fallbackRoomManagers),
|
roomList = mergeRoomList(roomList, roomVoiceProfileCommon.listRoomVoiceProfilesV2(fallbackRoomManagers),
|
||||||
DISCOVERY_LIMIT);
|
DISCOVERY_LIMIT);
|
||||||
}
|
}
|
||||||
|
roomList = sortByMemberQuantityDesc(roomList);
|
||||||
|
|
||||||
if (CollectionUtils.isEmpty(roomList)) {
|
if (CollectionUtils.isEmpty(roomList)) {
|
||||||
return CollectionUtils.newArrayList();
|
return CollectionUtils.newArrayList();
|
||||||
@ -168,6 +170,33 @@ public class RoomVoiceDiscoverQryExe {
|
|||||||
return roomList;
|
return roomList;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private List<RoomVoiceProfileCO> sortByMemberQuantityDesc(List<RoomVoiceProfileCO> roomList) {
|
||||||
|
if (CollectionUtils.isEmpty(roomList)) {
|
||||||
|
return CollectionUtils.newArrayList();
|
||||||
|
}
|
||||||
|
return roomList.stream()
|
||||||
|
.sorted(Comparator.comparingInt(this::roomMemberQuantity).reversed())
|
||||||
|
.toList();
|
||||||
|
}
|
||||||
|
|
||||||
|
private int roomMemberQuantity(RoomVoiceProfileCO room) {
|
||||||
|
if (Objects.isNull(room) || Objects.isNull(room.getExtValues())) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
Object value = room.getExtValues().get(MEMBER_QUANTITY_EXT_KEY);
|
||||||
|
if (value instanceof Number number) {
|
||||||
|
return Math.max(number.intValue(), 0);
|
||||||
|
}
|
||||||
|
if (value instanceof String text) {
|
||||||
|
try {
|
||||||
|
return Math.max(Integer.parseInt(text), 0);
|
||||||
|
} catch (NumberFormatException ignored) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
private String discoveryCacheKey(String sysOrigin, String region, boolean allRegionWhiteListUser) {
|
private String discoveryCacheKey(String sysOrigin, String region, boolean allRegionWhiteListUser) {
|
||||||
return allRegionWhiteListUser ? sysOrigin + region + ":ALL_REGION" : sysOrigin + region;
|
return allRegionWhiteListUser ? sysOrigin + region + ":ALL_REGION" : sysOrigin + region;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -146,8 +146,11 @@ public class GameListConfigQryExe {
|
|||||||
|
|
||||||
private List<GameListConfigCO> getGameListConfigs(GameListQryCmd cmd) {
|
private List<GameListConfigCO> getGameListConfigs(GameListQryCmd cmd) {
|
||||||
|
|
||||||
String activeBaishunProfile = gameListConfigService.getActiveBaishunProfile(cmd.requireReqSysOrigin());
|
String reqSysOrigin = cmd.requireReqSysOrigin();
|
||||||
String cacheCategory = String.valueOf(cmd.getCategory()) + ":BAISHUN:" + activeBaishunProfile;
|
String activeBaishunProfile = gameListConfigService.getActiveBaishunProfile(reqSysOrigin);
|
||||||
|
String activeLingxianProfile = gameListConfigService.getActiveLingxianProfile(reqSysOrigin);
|
||||||
|
String cacheCategory = String.valueOf(cmd.getCategory()) + ":BAISHUN:" + activeBaishunProfile
|
||||||
|
+ ":LINGXIAN:" + activeLingxianProfile;
|
||||||
String data = gameListCacheService.listCache(cmd.requireReqSysOrigin(), cacheCategory,
|
String data = gameListCacheService.listCache(cmd.requireReqSysOrigin(), cacheCategory,
|
||||||
sysOrigin -> JacksonUtils.toJson(sysConfigAppConvertor.toListGameListConfigCO(
|
sysOrigin -> JacksonUtils.toJson(sysConfigAppConvertor.toListGameListConfigCO(
|
||||||
gameListConfigService.listShowcaseConfig(cmd.requireReqSysOrigin(), cmd.getCategory(), cmd.getRoomSessionId()))), cmd.getReqAppIntel().getVersion());
|
gameListConfigService.listShowcaseConfig(cmd.requireReqSysOrigin(), cmd.getCategory(), cmd.getRoomSessionId()))), cmd.getReqAppIntel().getVersion());
|
||||||
|
|||||||
@ -0,0 +1,92 @@
|
|||||||
|
package com.red.circle.other.app.common.invite;
|
||||||
|
|
||||||
|
import cn.hutool.http.HttpResponse;
|
||||||
|
import cn.hutool.http.HttpUtil;
|
||||||
|
import com.alibaba.fastjson.JSON;
|
||||||
|
import com.alibaba.fastjson.JSONObject;
|
||||||
|
import com.red.circle.other.infra.config.InviteCampaignGoConfig;
|
||||||
|
import java.util.Objects;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.RequiredArgsConstructor;
|
||||||
|
import lombok.experimental.Accessors;
|
||||||
|
import lombok.extern.slf4j.Slf4j;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Slf4j
|
||||||
|
@Component
|
||||||
|
@RequiredArgsConstructor
|
||||||
|
public class InviteCampaignGoClient {
|
||||||
|
|
||||||
|
private final InviteCampaignGoConfig inviteCampaignGoConfig;
|
||||||
|
|
||||||
|
public RegisterBindCodeResponse bindRegisterCode(RegisterBindCodeRequest request) {
|
||||||
|
if (!inviteCampaignGoConfig.isEnabled()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
String baseUrl = Objects.toString(inviteCampaignGoConfig.getBaseUrl(), "").trim();
|
||||||
|
if (baseUrl.isEmpty()) {
|
||||||
|
log.warn("Invite campaign go baseUrl is blank, skip register bind. userId={}",
|
||||||
|
request.getInviteeUserId());
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
HttpResponse response = HttpUtil.createPost(trimRightSlash(baseUrl)
|
||||||
|
+ "/internal/invite-campaign/register-bind-code")
|
||||||
|
.header("Content-Type", "application/json")
|
||||||
|
.header("Accept", "application/json")
|
||||||
|
.header("X-Internal-Token", Objects.toString(inviteCampaignGoConfig.getInternalToken(), ""))
|
||||||
|
.timeout(Objects.requireNonNullElse(inviteCampaignGoConfig.getTimeoutMillis(), 10000))
|
||||||
|
.body(JSON.toJSONString(request))
|
||||||
|
.execute();
|
||||||
|
|
||||||
|
String responseBody = response.body();
|
||||||
|
if (response.getStatus() < 200 || response.getStatus() >= 300) {
|
||||||
|
throw new IllegalStateException("Invite campaign go http error, status=" + response.getStatus()
|
||||||
|
+ ", body=" + responseBody);
|
||||||
|
}
|
||||||
|
if (responseBody == null || responseBody.trim().isEmpty()) {
|
||||||
|
throw new IllegalStateException("Invite campaign go response is empty");
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject root = JSON.parseObject(responseBody);
|
||||||
|
if (root == null) {
|
||||||
|
throw new IllegalStateException("Invite campaign go response is invalid");
|
||||||
|
}
|
||||||
|
if (root.containsKey("status") && !root.getBooleanValue("status")) {
|
||||||
|
throw new IllegalStateException("Invite campaign go business error: " + root.getString("message"));
|
||||||
|
}
|
||||||
|
|
||||||
|
JSONObject body = root.getJSONObject("body");
|
||||||
|
return Objects.nonNull(body)
|
||||||
|
? body.toJavaObject(RegisterBindCodeResponse.class)
|
||||||
|
: root.toJavaObject(RegisterBindCodeResponse.class);
|
||||||
|
}
|
||||||
|
|
||||||
|
private String trimRightSlash(String value) {
|
||||||
|
while (value.endsWith("/")) {
|
||||||
|
value = value.substring(0, value.length() - 1);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public static class RegisterBindCodeRequest {
|
||||||
|
private String inviteCode;
|
||||||
|
private String sysOrigin;
|
||||||
|
private Long inviteeUserId;
|
||||||
|
private String clientIp;
|
||||||
|
private String deviceFingerprint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Accessors(chain = true)
|
||||||
|
public static class RegisterBindCodeResponse {
|
||||||
|
private Long relationId;
|
||||||
|
private Boolean eligible;
|
||||||
|
private String blockReason;
|
||||||
|
private Long inviterUserId;
|
||||||
|
private String inviteCode;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -36,6 +36,7 @@ public interface UserProfileAppConvertor {
|
|||||||
default LoginLoggerEvent toLoginLoggerEventRegister(CreateAccountCmd cmd) {
|
default LoginLoggerEvent toLoginLoggerEventRegister(CreateAccountCmd cmd) {
|
||||||
LoginLoggerEvent event = toLoginLoggerEvent(cmd);
|
LoginLoggerEvent event = toLoginLoggerEvent(cmd);
|
||||||
event.setInvitePeople(cmd.getInvitePeople());
|
event.setInvitePeople(cmd.getInvitePeople());
|
||||||
|
event.setInvitationCode(cmd.getInvitationCode());
|
||||||
event.setEventType(0);
|
event.setEventType(0);
|
||||||
event.setAuthType(cmd.getType());
|
event.setAuthType(cmd.getType());
|
||||||
return event;
|
return event;
|
||||||
|
|||||||
@ -18,6 +18,7 @@ import com.red.circle.mq.business.model.event.approval.CensorProfileEvent;
|
|||||||
import com.red.circle.mq.business.model.event.user.LoginLoggerEvent;
|
import com.red.circle.mq.business.model.event.user.LoginLoggerEvent;
|
||||||
import com.red.circle.mq.rocket.business.producer.CensorMqMessage;
|
import com.red.circle.mq.rocket.business.producer.CensorMqMessage;
|
||||||
import com.red.circle.mq.rocket.business.streams.LoginRegisterLoggerSink;
|
import com.red.circle.mq.rocket.business.streams.LoginRegisterLoggerSink;
|
||||||
|
import com.red.circle.other.app.common.invite.InviteCampaignGoClient;
|
||||||
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
import com.red.circle.other.domain.gateway.user.UserProfileGateway;
|
||||||
import com.red.circle.other.domain.gateway.user.ability.RegisterDeviceGateway;
|
import com.red.circle.other.domain.gateway.user.ability.RegisterDeviceGateway;
|
||||||
import com.red.circle.other.domain.model.user.UserConstant;
|
import com.red.circle.other.domain.model.user.UserConstant;
|
||||||
@ -71,6 +72,7 @@ public class LoginLoggerListener implements MessageListener {
|
|||||||
private final RegisterDeviceGateway registerDeviceGateway;
|
private final RegisterDeviceGateway registerDeviceGateway;
|
||||||
private final EnumConfigCacheService enumConfigCacheService;
|
private final EnumConfigCacheService enumConfigCacheService;
|
||||||
private final InviteUserSummaryService inviteUserSummaryService;
|
private final InviteUserSummaryService inviteUserSummaryService;
|
||||||
|
private final InviteCampaignGoClient inviteCampaignGoClient;
|
||||||
private final LatestMobileDeviceService latestMobileDeviceService;
|
private final LatestMobileDeviceService latestMobileDeviceService;
|
||||||
private final InviteUserRegisterService inviteUserRegisterService;
|
private final InviteUserRegisterService inviteUserRegisterService;
|
||||||
private final InviteActivityCountService inviteActivityCountService;
|
private final InviteActivityCountService inviteActivityCountService;
|
||||||
@ -149,6 +151,7 @@ public class LoginLoggerListener implements MessageListener {
|
|||||||
);
|
);
|
||||||
approvalProfile(userProfile);
|
approvalProfile(userProfile);
|
||||||
processLogin(event);
|
processLogin(event);
|
||||||
|
handleInviteCampaignCode(event);
|
||||||
handleInvitePeople(event);
|
handleInvitePeople(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -171,6 +174,34 @@ public class LoginLoggerListener implements MessageListener {
|
|||||||
? ApprovalStatusEnum.NONE : ApprovalStatusEnum.PENDING);
|
? ApprovalStatusEnum.NONE : ApprovalStatusEnum.PENDING);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleInviteCampaignCode(LoginLoggerEvent event) {
|
||||||
|
String invitationCode = Objects.toString(event.getInvitationCode(), "").trim();
|
||||||
|
if (invitationCode.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Objects.isNull(event.getUserId()) || Objects.toString(event.getSysOrigin(), "").trim()
|
||||||
|
.isEmpty()) {
|
||||||
|
log.warn("invite campaign register bind skipped, missing userId or sysOrigin. event={}",
|
||||||
|
JacksonUtils.toJson(event));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
InviteCampaignGoClient.RegisterBindCodeResponse response = inviteCampaignGoClient.bindRegisterCode(
|
||||||
|
new InviteCampaignGoClient.RegisterBindCodeRequest()
|
||||||
|
.setInviteCode(invitationCode)
|
||||||
|
.setSysOrigin(event.getSysOrigin())
|
||||||
|
.setInviteeUserId(event.getUserId())
|
||||||
|
.setClientIp(Objects.toString(event.getIp(), ""))
|
||||||
|
.setDeviceFingerprint(Objects.toString(event.getImei(), "")));
|
||||||
|
log.info("invite campaign register bind result, userId={}, inviteCode={}, response={}",
|
||||||
|
event.getUserId(), invitationCode, JacksonUtils.toJson(response));
|
||||||
|
} catch (Exception ex) {
|
||||||
|
log.warn("invite campaign register bind failed, userId={}, inviteCode={}",
|
||||||
|
event.getUserId(), invitationCode, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void handleInvitePeople(LoginLoggerEvent event) {
|
private void handleInvitePeople(LoginLoggerEvent event) {
|
||||||
log.warn("event {}", event);
|
log.warn("event {}", event);
|
||||||
if (Objects.isNull(event.getInvitePeople())) {
|
if (Objects.isNull(event.getInvitePeople())) {
|
||||||
|
|||||||
@ -86,8 +86,10 @@ class RoomVoiceDiscoverQryExeTest {
|
|||||||
fallbackRoomManager.setId(102L);
|
fallbackRoomManager.setId(102L);
|
||||||
fallbackRoomManager.setUserId(202L);
|
fallbackRoomManager.setUserId(202L);
|
||||||
|
|
||||||
RoomVoiceProfileCO activeRoomCo = new RoomVoiceProfileCO().setId(101L).setUserId(201L);
|
RoomVoiceProfileCO activeRoomCo = new RoomVoiceProfileCO().setId(101L).setUserId(201L)
|
||||||
RoomVoiceProfileCO fallbackRoomCo = new RoomVoiceProfileCO().setId(102L).setUserId(202L);
|
.putRoomMemberQuantity(0);
|
||||||
|
RoomVoiceProfileCO fallbackRoomCo = new RoomVoiceProfileCO().setId(102L).setUserId(202L)
|
||||||
|
.putRoomMemberQuantity(2);
|
||||||
|
|
||||||
when(roomRegionFilterCommon.requireRegionConfig(1L)).thenReturn(currentRegion);
|
when(roomRegionFilterCommon.requireRegionConfig(1L)).thenReturn(currentRegion);
|
||||||
when(userProfileGateway.getByUserId(1L)).thenReturn(userProfile);
|
when(userProfileGateway.getByUserId(1L)).thenReturn(userProfile);
|
||||||
@ -112,12 +114,12 @@ class RoomVoiceDiscoverQryExeTest {
|
|||||||
when(userSVipGateway.checkSVipIdentity(anySet())).thenReturn(
|
when(userSVipGateway.checkSVipIdentity(anySet())).thenReturn(
|
||||||
Map.of(201L, SVIPLevelEnum.NONE, 202L, SVIPLevelEnum.NONE)
|
Map.of(201L, SVIPLevelEnum.NONE, 202L, SVIPLevelEnum.NONE)
|
||||||
);
|
);
|
||||||
when(rocketStatusCacheService.batchGetRocketStatus(List.of(101L, 102L))).thenReturn(Map.of());
|
when(rocketStatusCacheService.batchGetRocketStatus(List.of(102L, 101L))).thenReturn(Map.of());
|
||||||
when(gameLudoService.listGameCoverByRoomIds(List.of(101L, 102L))).thenReturn(Map.of());
|
when(gameLudoService.listGameCoverByRoomIds(List.of(102L, 101L))).thenReturn(Map.of());
|
||||||
|
|
||||||
List<RoomVoiceProfileCO> roomList = exe.execute(cmd);
|
List<RoomVoiceProfileCO> roomList = exe.execute(cmd);
|
||||||
|
|
||||||
assertEquals(List.of(activeRoomCo, fallbackRoomCo), roomList);
|
assertEquals(List.of(fallbackRoomCo, activeRoomCo), roomList);
|
||||||
verify(activeVoiceRoomService).listByRegion("YOLO", "AE", 50);
|
verify(activeVoiceRoomService).listByRegion("YOLO", "AE", 50);
|
||||||
verify(activeVoiceRoomService, never()).listDiscover("YOLO", true, "AE", null, 50);
|
verify(activeVoiceRoomService, never()).listDiscover("YOLO", true, "AE", null, 50);
|
||||||
}
|
}
|
||||||
@ -162,7 +164,8 @@ class RoomVoiceDiscoverQryExeTest {
|
|||||||
cmd.setReqSysOrigin(ReqSysOrigin.of("YOLO", "YOLO"));
|
cmd.setReqSysOrigin(ReqSysOrigin.of("YOLO", "YOLO"));
|
||||||
|
|
||||||
RegionConfig currentRegion = new RegionConfig().setRegionCode("AE").setCountryCodes("AE,SA");
|
RegionConfig currentRegion = new RegionConfig().setRegionCode("AE").setCountryCodes("AE,SA");
|
||||||
UserProfile userProfile = new UserProfile().setCountryCode("AE");
|
UserProfile userProfile = new UserProfile();
|
||||||
|
userProfile.setCountryCode("AE");
|
||||||
ActiveVoiceRoom activeVoiceRoom = new ActiveVoiceRoom().setId(101L).setUserId(201L);
|
ActiveVoiceRoom activeVoiceRoom = new ActiveVoiceRoom().setId(101L).setUserId(201L);
|
||||||
RoomProfileManager fallbackRoomManager = new RoomProfileManager();
|
RoomProfileManager fallbackRoomManager = new RoomProfileManager();
|
||||||
fallbackRoomManager.setId(102L);
|
fallbackRoomManager.setId(102L);
|
||||||
|
|||||||
@ -0,0 +1,21 @@
|
|||||||
|
package com.red.circle.other.infra.config;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||||
|
import org.springframework.cloud.context.config.annotation.RefreshScope;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
@Component
|
||||||
|
@RefreshScope
|
||||||
|
@ConfigurationProperties(prefix = "invite.campaign.go")
|
||||||
|
public class InviteCampaignGoConfig {
|
||||||
|
|
||||||
|
private boolean enabled = true;
|
||||||
|
|
||||||
|
private String baseUrl;
|
||||||
|
|
||||||
|
private String internalToken;
|
||||||
|
|
||||||
|
private Integer timeoutMillis = 10000;
|
||||||
|
}
|
||||||
@ -68,6 +68,7 @@ public interface UserProfileInfraConvertor {
|
|||||||
default LoginLoggerEvent toLoginLoggerEventRegister(CreateAccountCmd cmd) {
|
default LoginLoggerEvent toLoginLoggerEventRegister(CreateAccountCmd cmd) {
|
||||||
LoginLoggerEvent event = toLoginLoggerEvent(cmd);
|
LoginLoggerEvent event = toLoginLoggerEvent(cmd);
|
||||||
event.setInvitePeople(cmd.getInvitePeople());
|
event.setInvitePeople(cmd.getInvitePeople());
|
||||||
|
event.setInvitationCode(cmd.getInvitationCode());
|
||||||
event.setEventType(0);
|
event.setEventType(0);
|
||||||
event.setAuthType(cmd.getType());
|
event.setAuthType(cmd.getType());
|
||||||
return event;
|
return event;
|
||||||
|
|||||||
@ -124,11 +124,11 @@ public class ActiveVoiceRoomServiceImpl implements ActiveVoiceRoomService {
|
|||||||
|
|
||||||
Query query = Query.query(criteria);
|
Query query = Query.query(criteria);
|
||||||
|
|
||||||
if (Objects.nonNull(qryCmd.getOrderByStrategy())) {
|
if (Objects.nonNull(qryCmd.getOrderByStrategy())) {
|
||||||
query.with(
|
query.with(
|
||||||
Sort.by(Stream.of("fixedWeights", "weights", "onlineQuantity").map(Order::desc)
|
Sort.by(Stream.of("onlineQuantity", "fixedWeights", "weights").map(Order::desc)
|
||||||
.toArray(Order[]::new)));
|
.toArray(Order[]::new)));
|
||||||
}
|
}
|
||||||
|
|
||||||
return mongoTemplate.find(query.limit(qryCmd.getLimit()), ActiveVoiceRoom.class);
|
return mongoTemplate.find(query.limit(qryCmd.getLimit()), ActiveVoiceRoom.class);
|
||||||
}
|
}
|
||||||
@ -172,13 +172,13 @@ public class ActiveVoiceRoomServiceImpl implements ActiveVoiceRoomService {
|
|||||||
).as("fixedWeightsScore")
|
).as("fixedWeightsScore")
|
||||||
.and("weights").as("weights"),
|
.and("weights").as("weights"),
|
||||||
|
|
||||||
// 3. 排序逻辑
|
// 3. 排序逻辑
|
||||||
Aggregation.sort(Sort.by(
|
Aggregation.sort(Sort.by(
|
||||||
Sort.Order.desc("regionMatchScore"),
|
Sort.Order.desc("onlineQuantity"),
|
||||||
Sort.Order.desc("fixedWeightsScore"), // 如果区域不匹配,fixedWeights 为 0,不影响排序
|
Sort.Order.desc("regionMatchScore"),
|
||||||
Sort.Order.desc("weights"),
|
Sort.Order.desc("fixedWeightsScore"), // 如果区域不匹配,fixedWeights 为 0,不影响排序
|
||||||
Sort.Order.desc("onlineQuantity")
|
Sort.Order.desc("weights")
|
||||||
)),
|
)),
|
||||||
|
|
||||||
// 4. 分页
|
// 4. 分页
|
||||||
Aggregation.skip(0),
|
Aggregation.skip(0),
|
||||||
@ -196,9 +196,9 @@ public class ActiveVoiceRoomServiceImpl implements ActiveVoiceRoomService {
|
|||||||
Criteria.where("sysOrigin").is(sysOrigin)
|
Criteria.where("sysOrigin").is(sysOrigin)
|
||||||
.and("region").is(region)
|
.and("region").is(region)
|
||||||
).with(Sort.by(
|
).with(Sort.by(
|
||||||
|
Sort.Order.desc("onlineQuantity"),
|
||||||
Sort.Order.desc("fixedWeights"),
|
Sort.Order.desc("fixedWeights"),
|
||||||
Sort.Order.desc("weights"),
|
Sort.Order.desc("weights")
|
||||||
Sort.Order.desc("onlineQuantity")
|
|
||||||
));
|
));
|
||||||
|
|
||||||
if (Objects.nonNull(limit)) {
|
if (Objects.nonNull(limit)) {
|
||||||
|
|||||||
@ -322,8 +322,7 @@ public class RoomProfileManagerServiceImpl implements RoomProfileManagerService
|
|||||||
|
|
||||||
Query query = Query.query(criteria)
|
Query query = Query.query(criteria)
|
||||||
.with(Sort.by(
|
.with(Sort.by(
|
||||||
Sort.Order.desc("activeTime"),
|
Sort.Order.desc("counter.memberCount"),
|
||||||
Sort.Order.desc("updateTime"),
|
|
||||||
Sort.Order.desc("createTime"),
|
Sort.Order.desc("createTime"),
|
||||||
Sort.Order.desc("id")
|
Sort.Order.desc("id")
|
||||||
));
|
));
|
||||||
|
|||||||
@ -41,6 +41,11 @@ public interface GameListConfigService extends BaseService<GameListConfig> {
|
|||||||
* 获取百顺当前启用的配置档案.
|
* 获取百顺当前启用的配置档案.
|
||||||
*/
|
*/
|
||||||
String getActiveBaishunProfile(String sysOrigin);
|
String getActiveBaishunProfile(String sysOrigin);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取灵仙当前启用的配置档案.
|
||||||
|
*/
|
||||||
|
String getActiveLingxianProfile(String sysOrigin);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询游戏信息
|
* 查询游戏信息
|
||||||
|
|||||||
@ -16,6 +16,7 @@ import com.red.circle.tool.core.text.StringUtils;
|
|||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
import org.springframework.jdbc.core.JdbcTemplate;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
@ -30,7 +31,9 @@ public class GameListConfigServiceImpl extends
|
|||||||
BaseServiceImpl<GameListConfigDAO, GameListConfig> implements GameListConfigService {
|
BaseServiceImpl<GameListConfigDAO, GameListConfig> implements GameListConfigService {
|
||||||
|
|
||||||
private static final String BAISHUN_GAME_ORIGIN = "BAISHUN";
|
private static final String BAISHUN_GAME_ORIGIN = "BAISHUN";
|
||||||
|
private static final String LINGXIAN_GAME_ORIGIN = "LINGXIAN";
|
||||||
private static final String BAISHUN_PROFILE_PROD = "PROD";
|
private static final String BAISHUN_PROFILE_PROD = "PROD";
|
||||||
|
private static final String LINGXIAN_PROFILE_PROD = "PROD";
|
||||||
private static final String BAISHUN_ACTIVE_PROFILE_EXISTS_SQL = """
|
private static final String BAISHUN_ACTIVE_PROFILE_EXISTS_SQL = """
|
||||||
EXISTS (
|
EXISTS (
|
||||||
SELECT 1
|
SELECT 1
|
||||||
@ -56,11 +59,43 @@ public class GameListConfigServiceImpl extends
|
|||||||
OR %s
|
OR %s
|
||||||
)
|
)
|
||||||
""".formatted(BAISHUN_ACTIVE_PROFILE_EXISTS_SQL);
|
""".formatted(BAISHUN_ACTIVE_PROFILE_EXISTS_SQL);
|
||||||
|
private static final String LINGXIAN_ACTIVE_PROFILE_EXISTS_SQL = """
|
||||||
|
EXISTS (
|
||||||
|
SELECT 1
|
||||||
|
FROM sys_game_list_vendor_ext ext
|
||||||
|
WHERE ext.game_list_config_id = sys_game_list_config.id
|
||||||
|
AND ext.sys_origin = sys_game_list_config.sys_origin
|
||||||
|
AND ext.vendor_type = 'LINGXIAN'
|
||||||
|
AND ext.enabled = 1
|
||||||
|
AND ext.profile = COALESCE((
|
||||||
|
SELECT provider.profile
|
||||||
|
FROM lingxian_provider_config provider
|
||||||
|
WHERE provider.sys_origin = sys_game_list_config.sys_origin
|
||||||
|
AND provider.active = 1
|
||||||
|
ORDER BY provider.update_time DESC
|
||||||
|
LIMIT 1
|
||||||
|
), 'PROD')
|
||||||
|
)
|
||||||
|
""";
|
||||||
|
private static final String ACTIVE_PROFILE_FILTER_SQL = """
|
||||||
|
(
|
||||||
|
game_origin IS NULL
|
||||||
|
OR (
|
||||||
|
game_origin <> 'BAISHUN'
|
||||||
|
AND game_origin <> 'LINGXIAN'
|
||||||
|
)
|
||||||
|
OR %s
|
||||||
|
OR %s
|
||||||
|
)
|
||||||
|
""".formatted(BAISHUN_ACTIVE_PROFILE_EXISTS_SQL, LINGXIAN_ACTIVE_PROFILE_EXISTS_SQL);
|
||||||
|
|
||||||
private final BaishunProviderConfigService baishunProviderConfigService;
|
private final BaishunProviderConfigService baishunProviderConfigService;
|
||||||
|
private final JdbcTemplate jdbcTemplate;
|
||||||
|
|
||||||
public GameListConfigServiceImpl(BaishunProviderConfigService baishunProviderConfigService) {
|
public GameListConfigServiceImpl(BaishunProviderConfigService baishunProviderConfigService,
|
||||||
|
JdbcTemplate jdbcTemplate) {
|
||||||
this.baishunProviderConfigService = baishunProviderConfigService;
|
this.baishunProviderConfigService = baishunProviderConfigService;
|
||||||
|
this.jdbcTemplate = jdbcTemplate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -70,7 +105,7 @@ public class GameListConfigServiceImpl extends
|
|||||||
.eq(StringUtils.isNotBlank(category), GameListConfig::getCategory, category)
|
.eq(StringUtils.isNotBlank(category), GameListConfig::getCategory, category)
|
||||||
.ne(StringUtils.isBlank(roomId), GameListConfig::getCategory, "HIDDEN")
|
.ne(StringUtils.isBlank(roomId), GameListConfig::getCategory, "HIDDEN")
|
||||||
.eq(GameListConfig::getShowcase, Boolean.TRUE)
|
.eq(GameListConfig::getShowcase, Boolean.TRUE)
|
||||||
.apply(BAISHUN_ACTIVE_PROFILE_FILTER_SQL)
|
.apply(ACTIVE_PROFILE_FILTER_SQL)
|
||||||
.orderByDesc(TimestampBaseEntity::getCreateTime, GameListConfig::getSort)
|
.orderByDesc(TimestampBaseEntity::getCreateTime, GameListConfig::getSort)
|
||||||
.list();
|
.list();
|
||||||
}
|
}
|
||||||
@ -94,6 +129,7 @@ public class GameListConfigServiceImpl extends
|
|||||||
.eq(GameListConfig::getSysOrigin, sysOrigin)
|
.eq(GameListConfig::getSysOrigin, sysOrigin)
|
||||||
.eq(GameListConfig::getShowcase, Boolean.TRUE)
|
.eq(GameListConfig::getShowcase, Boolean.TRUE)
|
||||||
.apply(isBaishunGameOrigin(gameOrigin), BAISHUN_ACTIVE_PROFILE_EXISTS_SQL)
|
.apply(isBaishunGameOrigin(gameOrigin), BAISHUN_ACTIVE_PROFILE_EXISTS_SQL)
|
||||||
|
.apply(isLingxianGameOrigin(gameOrigin), LINGXIAN_ACTIVE_PROFILE_EXISTS_SQL)
|
||||||
.list();
|
.list();
|
||||||
if (CollectionUtils.isEmpty(gameListConfigs)) {
|
if (CollectionUtils.isEmpty(gameListConfigs)) {
|
||||||
return "";
|
return "";
|
||||||
@ -140,7 +176,7 @@ public class GameListConfigServiceImpl extends
|
|||||||
query.getGameOrigin())
|
query.getGameOrigin())
|
||||||
.like(StringUtils.isNotBlank(query.getRegion()), GameListConfig::getRegions,
|
.like(StringUtils.isNotBlank(query.getRegion()), GameListConfig::getRegions,
|
||||||
query.getRegion())
|
query.getRegion())
|
||||||
.apply(BAISHUN_ACTIVE_PROFILE_FILTER_SQL)
|
.apply(ACTIVE_PROFILE_FILTER_SQL)
|
||||||
.orderByDesc(GameListConfig::getSort)
|
.orderByDesc(GameListConfig::getSort)
|
||||||
.page(query.getPageQuery());
|
.page(query.getPageQuery());
|
||||||
}
|
}
|
||||||
@ -157,12 +193,32 @@ public class GameListConfigServiceImpl extends
|
|||||||
return normalizeBaishunProfile(config.getProfile());
|
return normalizeBaishunProfile(config.getProfile());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getActiveLingxianProfile(String sysOrigin) {
|
||||||
|
if (StringUtils.isBlank(sysOrigin)) {
|
||||||
|
return LINGXIAN_PROFILE_PROD;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
String profile = jdbcTemplate.queryForObject("""
|
||||||
|
SELECT profile
|
||||||
|
FROM lingxian_provider_config
|
||||||
|
WHERE sys_origin = ?
|
||||||
|
AND active = 1
|
||||||
|
ORDER BY update_time DESC
|
||||||
|
LIMIT 1
|
||||||
|
""", String.class, sysOrigin);
|
||||||
|
return normalizeProfile(profile, LINGXIAN_PROFILE_PROD);
|
||||||
|
} catch (Exception e) {
|
||||||
|
return LINGXIAN_PROFILE_PROD;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public GameListConfig getByGameCode(String gameCode, String sysOrigin) {
|
public GameListConfig getByGameCode(String gameCode, String sysOrigin) {
|
||||||
return query()
|
return query()
|
||||||
.eq(GameListConfig::getGameCode, gameCode)
|
.eq(GameListConfig::getGameCode, gameCode)
|
||||||
.eq(GameListConfig::getSysOrigin, sysOrigin)
|
.eq(GameListConfig::getSysOrigin, sysOrigin)
|
||||||
.apply(BAISHUN_ACTIVE_PROFILE_FILTER_SQL)
|
.apply(ACTIVE_PROFILE_FILTER_SQL)
|
||||||
.last(PageConstant.LIMIT_ONE)
|
.last(PageConstant.LIMIT_ONE)
|
||||||
.getOne();
|
.getOne();
|
||||||
}
|
}
|
||||||
@ -177,6 +233,7 @@ public class GameListConfigServiceImpl extends
|
|||||||
.eq(GameListConfig::getGameOrigin, gameOrigin)
|
.eq(GameListConfig::getGameOrigin, gameOrigin)
|
||||||
.eq(GameListConfig::getSysOrigin, sysOrigin)
|
.eq(GameListConfig::getSysOrigin, sysOrigin)
|
||||||
.apply(isBaishunGameOrigin(gameOrigin), BAISHUN_ACTIVE_PROFILE_EXISTS_SQL)
|
.apply(isBaishunGameOrigin(gameOrigin), BAISHUN_ACTIVE_PROFILE_EXISTS_SQL)
|
||||||
|
.apply(isLingxianGameOrigin(gameOrigin), LINGXIAN_ACTIVE_PROFILE_EXISTS_SQL)
|
||||||
.last(PageConstant.LIMIT_ONE)
|
.last(PageConstant.LIMIT_ONE)
|
||||||
.getOne();
|
.getOne();
|
||||||
}
|
}
|
||||||
@ -196,6 +253,11 @@ public class GameListConfigServiceImpl extends
|
|||||||
&& BAISHUN_GAME_ORIGIN.equalsIgnoreCase(gameOrigin.trim());
|
&& BAISHUN_GAME_ORIGIN.equalsIgnoreCase(gameOrigin.trim());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private boolean isLingxianGameOrigin(String gameOrigin) {
|
||||||
|
return StringUtils.isNotBlank(gameOrigin)
|
||||||
|
&& LINGXIAN_GAME_ORIGIN.equalsIgnoreCase(gameOrigin.trim());
|
||||||
|
}
|
||||||
|
|
||||||
private String normalizeBaishunProfile(String profile) {
|
private String normalizeBaishunProfile(String profile) {
|
||||||
if (StringUtils.isBlank(profile)) {
|
if (StringUtils.isBlank(profile)) {
|
||||||
return BAISHUN_PROFILE_PROD;
|
return BAISHUN_PROFILE_PROD;
|
||||||
@ -207,4 +269,16 @@ public class GameListConfigServiceImpl extends
|
|||||||
default -> value;
|
default -> value;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String normalizeProfile(String profile, String defaultProfile) {
|
||||||
|
if (StringUtils.isBlank(profile)) {
|
||||||
|
return defaultProfile;
|
||||||
|
}
|
||||||
|
String value = profile.trim().toUpperCase(Locale.ROOT);
|
||||||
|
return switch (value) {
|
||||||
|
case "PRODUCT", "PRODUCTION", "ONLINE", "OFFICIAL", "FORMAL" -> defaultProfile;
|
||||||
|
case "TESTING", "SANDBOX", "DEV" -> "TEST";
|
||||||
|
default -> value;
|
||||||
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,19 +1,19 @@
|
|||||||
package com.red.circle.other.infra.database.mongo.service.live.impl;
|
package com.red.circle.other.infra.database.mongo.service.live.impl;
|
||||||
|
|
||||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||||
import static org.mockito.ArgumentMatchers.any;
|
import static org.junit.jupiter.api.Assertions.assertIterableEquals;
|
||||||
import static org.mockito.ArgumentMatchers.eq;
|
import static org.mockito.ArgumentMatchers.eq;
|
||||||
import static org.mockito.Mockito.mock;
|
import static org.mockito.Mockito.mock;
|
||||||
import static org.mockito.Mockito.when;
|
import static org.mockito.Mockito.when;
|
||||||
|
|
||||||
import com.red.circle.other.infra.common.sys.CountryCodeAliasSupport;
|
import com.red.circle.other.infra.common.sys.CountryCodeAliasSupport;
|
||||||
import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom;
|
import com.red.circle.other.infra.database.mongo.entity.live.ActiveVoiceRoom;
|
||||||
|
import com.red.circle.other.inner.model.cmd.live.AppActiveVoiceRoomQryCmd;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import org.bson.Document;
|
import org.bson.Document;
|
||||||
import org.junit.jupiter.api.BeforeEach;
|
import org.junit.jupiter.api.BeforeEach;
|
||||||
import org.junit.jupiter.api.Test;
|
import org.junit.jupiter.api.Test;
|
||||||
import org.mockito.ArgumentCaptor;
|
import org.mockito.ArgumentCaptor;
|
||||||
import org.springframework.data.domain.Sort;
|
|
||||||
import org.springframework.data.mongodb.core.MongoTemplate;
|
import org.springframework.data.mongodb.core.MongoTemplate;
|
||||||
import org.springframework.data.mongodb.core.query.Query;
|
import org.springframework.data.mongodb.core.query.Query;
|
||||||
|
|
||||||
@ -43,8 +43,30 @@ class ActiveVoiceRoomServiceImplTest {
|
|||||||
assertEquals(50, query.getLimit());
|
assertEquals(50, query.getLimit());
|
||||||
|
|
||||||
Document sortObject = query.getSortObject();
|
Document sortObject = query.getSortObject();
|
||||||
|
assertIterableEquals(List.of("onlineQuantity", "fixedWeights", "weights"),
|
||||||
|
sortObject.keySet());
|
||||||
|
assertEquals(-1, sortObject.getInteger("onlineQuantity"));
|
||||||
assertEquals(-1, sortObject.getInteger("fixedWeights"));
|
assertEquals(-1, sortObject.getInteger("fixedWeights"));
|
||||||
assertEquals(-1, sortObject.getInteger("weights"));
|
assertEquals(-1, sortObject.getInteger("weights"));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
void listOps_shouldUseOnlineQuantityFirstForDiscoverySort() {
|
||||||
|
ArgumentCaptor<Query> queryCaptor = ArgumentCaptor.forClass(Query.class);
|
||||||
|
when(mongoTemplate.find(queryCaptor.capture(), eq(ActiveVoiceRoom.class))).thenReturn(List.of());
|
||||||
|
|
||||||
|
AppActiveVoiceRoomQryCmd cmd = new AppActiveVoiceRoomQryCmd();
|
||||||
|
cmd.setSysOrigin("YOLO");
|
||||||
|
cmd.setLimit(50);
|
||||||
|
cmd.setOrderByStrategy(AppActiveVoiceRoomQryCmd.OrderByStrategy.APP_DISCOVERY);
|
||||||
|
|
||||||
|
service.listOps(cmd);
|
||||||
|
|
||||||
|
Document sortObject = queryCaptor.getValue().getSortObject();
|
||||||
|
assertIterableEquals(List.of("onlineQuantity", "fixedWeights", "weights"),
|
||||||
|
sortObject.keySet());
|
||||||
assertEquals(-1, sortObject.getInteger("onlineQuantity"));
|
assertEquals(-1, sortObject.getInteger("onlineQuantity"));
|
||||||
|
assertEquals(-1, sortObject.getInteger("fixedWeights"));
|
||||||
|
assertEquals(-1, sortObject.getInteger("weights"));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -105,7 +105,7 @@ class RoomProfileManagerServiceImplTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void listSelectiveLimitByCountryCodes_shouldQueryIncludedCountriesWithRecentSort() {
|
void listSelectiveLimitByCountryCodes_shouldQueryIncludedCountriesWithMemberCountSort() {
|
||||||
ArgumentCaptor<Query> queryCaptor = ArgumentCaptor.forClass(Query.class);
|
ArgumentCaptor<Query> queryCaptor = ArgumentCaptor.forClass(Query.class);
|
||||||
when(mongoTemplate.find(queryCaptor.capture(), eq(RoomProfileManager.class))).thenReturn(
|
when(mongoTemplate.find(queryCaptor.capture(), eq(RoomProfileManager.class))).thenReturn(
|
||||||
List.of()
|
List.of()
|
||||||
@ -126,8 +126,7 @@ class RoomProfileManagerServiceImplTest {
|
|||||||
assertEquals(50, query.getLimit());
|
assertEquals(50, query.getLimit());
|
||||||
|
|
||||||
Document sortObject = query.getSortObject();
|
Document sortObject = query.getSortObject();
|
||||||
assertEquals(-1, sortObject.getInteger("activeTime"));
|
assertEquals(-1, sortObject.getInteger("counter.memberCount"));
|
||||||
assertEquals(-1, sortObject.getInteger("updateTime"));
|
|
||||||
assertEquals(-1, sortObject.getInteger("createTime"));
|
assertEquals(-1, sortObject.getInteger("createTime"));
|
||||||
assertEquals(-1, sortObject.getInteger("id"));
|
assertEquals(-1, sortObject.getInteger("id"));
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user