diff --git a/hiver-admin/test-output/test-report.html b/hiver-admin/test-output/test-report.html index e979ce1a..29a18dc8 100644 --- a/hiver-admin/test-output/test-report.html +++ b/hiver-admin/test-output/test-report.html @@ -35,7 +35,7 @@ Hiver
  • -六月 09, 2026 18:01:00 +六月 10, 2026 17:44:25
  • @@ -84,7 +84,7 @@

    passTest

    -

    18:01:01 下午 / 0.021 secs

    +

    17:44:25 下午 / 0.018 secs

    @@ -92,9 +92,9 @@
    #test-id=1
    passTest
    -06.09.2026 18:01:01 -06.09.2026 18:01:01 -0.021 secs +06.10.2026 17:44:25 +06.10.2026 17:44:25 +0.018 secs
    @@ -104,7 +104,7 @@ Pass - 18:01:01 + 17:44:25 Test passed @@ -128,13 +128,13 @@

    Started

    -

    六月 09, 2026 18:01:00

    +

    六月 10, 2026 17:44:25

    Ended

    -

    六月 09, 2026 18:01:01

    +

    六月 10, 2026 17:44:25

    diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/constant/IeRedisKey.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/constant/IeRedisKey.java index ae389db0..cd534746 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/constant/IeRedisKey.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/constant/IeRedisKey.java @@ -16,6 +16,9 @@ public final class IeRedisKey { /** 鍖归厤姹狅細ZSET, key=ie:match:pool:{mode}:{mood}, member=userId, score=matchScore */ public static final String MATCH_POOL = PREFIX + "match:pool:"; + /** 鏍″尯鍖归厤姹狅細ZSET, key=ie:match:pool:region:{regionId}:{mode}:{mood}, member=userId */ + public static final String REGION_MATCH_POOL = PREFIX + "match:pool:region:"; + /** 鐢ㄦ埛鎵鍦ㄥ尮閰嶆睜绱㈠紩锛歋TRING, key=ie:match:user-pool:{userId}, value=poolKey */ public static final String USER_MATCH_POOL = PREFIX + "match:user-pool:"; @@ -61,4 +64,8 @@ public final class IeRedisKey { public static String matchPool(String mode, String mood) { return MATCH_POOL + mode + ":" + mood; } + + public static String regionMatchPool(String regionId, String mode, String mood) { + return REGION_MATCH_POOL + regionId + ":" + mode + ":" + mood; + } } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/dto/IeMatchStartDTO.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/dto/IeMatchStartDTO.java index ba9eab0c..4a72b61e 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/dto/IeMatchStartDTO.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/dto/IeMatchStartDTO.java @@ -9,8 +9,11 @@ public class IeMatchStartDTO { private String mode; private String targetMode; private String targetGender; + private String matchScope; private String mood; private Double longitude; private Double latitude; + private String regionId; + private String regionName; private List interestTags; } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/dto/IeProfileDTO.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/dto/IeProfileDTO.java index 1c80676e..6faf9297 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/dto/IeProfileDTO.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/dto/IeProfileDTO.java @@ -16,4 +16,6 @@ public class IeProfileDTO { private String currentMode; private String targetModePreference; private String targetGenderPreference; + private String regionId; + private String regionName; } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/dto/IeStatusDTO.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/dto/IeStatusDTO.java index a724a035..99639a04 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/dto/IeStatusDTO.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/dto/IeStatusDTO.java @@ -11,5 +11,7 @@ public class IeStatusDTO { private String statusText; private Double longitude; private Double latitude; + private String regionId; + private String regionName; private List interestTags; } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeMatchAttempt.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeMatchAttempt.java index 66672036..e2f55be0 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeMatchAttempt.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeMatchAttempt.java @@ -16,6 +16,8 @@ public class IeMatchAttempt extends IeBaseEntity { private Integer status; private String anonymousName; private String avatarText; + private String targetRegionId; + private String targetRegionName; private String stateText; private String quoteText; private String failReason; diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeUserProfile.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeUserProfile.java index 7d673ab8..4db57db2 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeUserProfile.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeUserProfile.java @@ -22,6 +22,8 @@ public class IeUserProfile extends IeBaseEntity { private String recentPreference; private String targetModePreference; private String targetGenderPreference; + private String regionId; + private String regionName; private Integer defaultRoomMinutes; private Integer dailyQuota; private Integer usedQuota; diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeUserStatus.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeUserStatus.java index f33662ac..d85908a1 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeUserStatus.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/entity/IeUserStatus.java @@ -14,6 +14,8 @@ public class IeUserStatus extends IeBaseEntity { private String mode; private String mood; private String statusText; + private String regionId; + private String regionName; private Integer onlineStatus; private Date lastActiveTime; } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/IeRedisService.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/IeRedisService.java index 92406287..29a01f2c 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/IeRedisService.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/IeRedisService.java @@ -17,7 +17,7 @@ public interface IeRedisService { void unlockMatchUser(Long userId); - List candidates(String mode, String mood, int limit); + List candidates(String mode, String mood, String regionId, boolean regionScope, int limit); boolean isRecentlyMatched(Long userId, Long targetUserId); @@ -51,7 +51,7 @@ public interface IeRedisService { long onlineCount(); - long waitingCount(String mode, String mood); + long waitingCount(String mode, String mood, String regionId, boolean regionScope); List hotStatuses(int limit); } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeMatchServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeMatchServiceImpl.java index a4359998..1f33d643 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeMatchServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeMatchServiceImpl.java @@ -62,7 +62,7 @@ public class IeMatchServiceImpl implements IeMatchService { redisService.refreshOnline(userId); IeHomeVO vo = new IeHomeVO(); vo.setOnlineCount(redisService.onlineCount()); - vo.setWaitingCount(redisService.waitingCount(profile.getCurrentMode(), "quiet")); + vo.setWaitingCount(redisService.waitingCount(profile.getCurrentMode(), "quiet", profile.getRegionId(), hasText(profile.getRegionId()))); vo.setDailyQuota(profile.getDailyQuota()); vo.setUsedQuota((int) redisService.todayUsedQuota(userId)); vo.setUnreadCount(totalUnreadCount(userId)); @@ -139,6 +139,8 @@ public class IeMatchServiceImpl implements IeMatchService { profile.setRecentPreference(profile.getCurrentMode()); profile.setTargetModePreference(normalizeTargetMode(dto.getTargetModePreference())); profile.setTargetGenderPreference(normalizeTargetGender(dto.getTargetGenderPreference())); + profile.setRegionId(defaultText(dto.getRegionId(), profile.getRegionId())); + profile.setRegionName(defaultText(dto.getRegionName(), profile.getRegionName())); profile.setProfileCompleted(1); profile.setLastActiveTime(new Date()); profile.setUpdateTime(new Date()); @@ -147,6 +149,8 @@ public class IeMatchServiceImpl implements IeMatchService { statusDTO.setMode(profile.getCurrentMode()); statusDTO.setMood("quiet"); statusDTO.setStatusText(defaultText(profile.getIntro(), profile.getCurrentMode())); + statusDTO.setRegionId(profile.getRegionId()); + statusDTO.setRegionName(profile.getRegionName()); statusDTO.setInterestTags(jsonToTags(profile.getInterestTags())); updateStatus(userId, statusDTO); return toProfileVO(profile); @@ -163,7 +167,9 @@ public class IeMatchServiceImpl implements IeMatchService { if (dto.getMood() == null) { dto.setMood("quiet"); } - ensureProfile(userId); + IeUserProfile profile = ensureProfile(userId); + dto.setRegionId(defaultText(dto.getRegionId(), profile.getRegionId())); + dto.setRegionName(defaultText(dto.getRegionName(), profile.getRegionName())); IeUserStatus status = userStatusMapper.selectOne(new LambdaQueryWrapper() .eq(IeUserStatus::getUserId, userId) .last("limit 1")); @@ -176,6 +182,8 @@ public class IeMatchServiceImpl implements IeMatchService { status.setMode(dto.getMode()); status.setMood(dto.getMood()); status.setStatusText(dto.getStatusText()); + status.setRegionId(dto.getRegionId()); + status.setRegionName(dto.getRegionName()); status.setOnlineStatus(1); status.setLastActiveTime(now); status.setUpdateTime(now); @@ -205,14 +213,23 @@ public class IeMatchServiceImpl implements IeMatchService { } IeStatusDTO statusDTO = new IeStatusDTO(); BeanUtils.copyProperties(dto == null ? new IeMatchStartDTO() : dto, statusDTO); - updateStatus(userId, statusDTO); String mode = normalizeMode(statusDTO.getMode(), profile.getCurrentMode()); String targetMode = normalizeTargetMode(dto == null ? null : dto.getTargetMode()); String requestedTargetGender = dto == null || dto.getTargetGender() == null ? profile.getTargetGenderPreference() : dto.getTargetGender(); String targetGender = normalizeTargetGender(requestedTargetGender); + String matchScope = normalizeMatchScope(dto == null ? null : dto.getMatchScope()); + String regionId = defaultText(dto == null ? null : dto.getRegionId(), profile.getRegionId()); + String regionName = defaultText(dto == null ? null : dto.getRegionName(), profile.getRegionName()); + boolean regionScope = "school".equals(matchScope); + if (regionScope && !hasText(regionId)) { + return fail(userId, dto, "璇峰厛閫夋嫨鏍″尯鍚庡啀鍖归厤鏈牎鍖虹敤鎴"); + } + statusDTO.setRegionId(regionId); + statusDTO.setRegionName(regionName); + updateStatus(userId, statusDTO); String mood = statusDTO.getMood() == null ? "quiet" : statusDTO.getMood(); - List candidates = candidateUsers(targetMode, mode, mood); + List candidates = candidateUsers(targetMode, mode, mood, regionId, regionScope); List matchedCandidates = new ArrayList<>(); for (Long candidateUserId : candidates) { if (candidateUserId == null || candidateUserId.equals(userId)) { @@ -225,7 +242,7 @@ public class IeMatchServiceImpl implements IeMatchService { continue; } IeUserProfile candidateProfile = findProfileByUserId(candidateUserId); - if (!matchCandidate(candidateProfile, targetMode, targetGender)) { + if (!matchCandidate(candidateProfile, targetMode, targetGender, regionId, regionScope)) { continue; } matchedCandidates.add(new MatchCandidate(candidateUserId, matchScore(statusDTO, profile, candidateProfile))); @@ -256,6 +273,8 @@ public class IeMatchServiceImpl implements IeMatchService { match.setStatus(3); match.setAnonymousName(defaultText(targetProfile.getAnonymousName(), "鍗婂尶鍚嶆紓娴佽")); match.setAvatarText(defaultText(targetProfile.getAvatarText(), "鈼")); + match.setTargetRegionId(targetProfile.getRegionId()); + match.setTargetRegionName(targetProfile.getRegionName()); match.setStateText(targetProfile.getCurrentMode() == null || "i".equals(targetProfile.getCurrentMode()) ? "鍋忓畨闈欙紝閫傚悎鎱㈡參闈犺繎" : "鍋忚交鏉撅紝鎰挎剰鍏堝紑鍦"); match.setQuoteText(defaultText(targetProfile.getIntro(), "鍙互鍏堝畨闈欏緟涓浼氾紝涓嶇敤鎬ョ潃鎵捐瘽棰樸")); match.setCreateTime(new Date()); @@ -274,6 +293,8 @@ public class IeMatchServiceImpl implements IeMatchService { vo.setRoomNo(room.getRoomNo()); vo.setAvatarUrl(targetProfile.getAvatarUrl()); vo.setGender(targetProfile.getGender()); + vo.setRegionId(targetProfile.getRegionId()); + vo.setRegionName(targetProfile.getRegionName()); vo.setIntro(targetProfile.getIntro()); vo.setInterestTags(jsonToTags(targetProfile.getInterestTags())); vo.setPersonaImages(jsonToTags(targetProfile.getPersonaImages())); @@ -349,6 +370,8 @@ public class IeMatchServiceImpl implements IeMatchService { vo.setAnonymousName(defaultText(profile.getAnonymousName(), vo.getAnonymousName())); vo.setAvatarText(defaultText(profile.getAvatarText(), vo.getAvatarText())); vo.setAvatarUrl(profile.getAvatarUrl()); + vo.setRegionId(defaultText(profile.getRegionId(), vo.getRegionId())); + vo.setRegionName(defaultText(profile.getRegionName(), vo.getRegionName())); vo.setMode(defaultText(profile.getCurrentMode(), vo.getMode())); vo.setQuoteText(defaultText(profile.getIntro(), vo.getQuoteText())); } @@ -419,6 +442,8 @@ public class IeMatchServiceImpl implements IeMatchService { vo.setTargetUserId(match.getTargetUserId()); vo.setAnonymousName(match.getAnonymousName()); vo.setAvatarText(match.getAvatarText()); + vo.setRegionId(match.getTargetRegionId()); + vo.setRegionName(match.getTargetRegionName()); vo.setMode(match.getMode()); vo.setMood(match.getMood()); vo.setStateText(match.getStateText()); @@ -469,22 +494,22 @@ public class IeMatchServiceImpl implements IeMatchService { .last("limit 1")); } - private List candidateUsers(String targetMode, String selfMode, String mood) { + private List candidateUsers(String targetMode, String selfMode, String mood, String regionId, boolean regionScope) { LinkedHashSet set = new LinkedHashSet<>(); if (!"any".equals(targetMode)) { - set.addAll(redisService.candidates(targetMode, mood, 50)); - set.addAll(statusCandidates(targetMode, 50)); - set.addAll(profileCandidates(targetMode, 50)); + set.addAll(redisService.candidates(targetMode, mood, regionId, regionScope, 50)); + set.addAll(statusCandidates(targetMode, regionId, regionScope, 50)); + set.addAll(profileCandidates(targetMode, regionId, regionScope, 50)); return new ArrayList<>(set); } - set.addAll(redisService.candidates(selfMode, mood, 30)); - set.addAll(redisService.candidates("i".equals(selfMode) ? "e" : "i", mood, 30)); - set.addAll(redisService.candidates("i", mood, 20)); - set.addAll(redisService.candidates("e", mood, 20)); - set.addAll(statusCandidates(selfMode, 30)); - set.addAll(statusCandidates("i".equals(selfMode) ? "e" : "i", 30)); - set.addAll(profileCandidates(selfMode, 30)); - set.addAll(profileCandidates("i".equals(selfMode) ? "e" : "i", 30)); + set.addAll(redisService.candidates(selfMode, mood, regionId, regionScope, 30)); + set.addAll(redisService.candidates("i".equals(selfMode) ? "e" : "i", mood, regionId, regionScope, 30)); + set.addAll(redisService.candidates("i", mood, regionId, regionScope, 20)); + set.addAll(redisService.candidates("e", mood, regionId, regionScope, 20)); + set.addAll(statusCandidates(selfMode, regionId, regionScope, 30)); + set.addAll(statusCandidates("i".equals(selfMode) ? "e" : "i", regionId, regionScope, 30)); + set.addAll(profileCandidates(selfMode, regionId, regionScope, 30)); + set.addAll(profileCandidates("i".equals(selfMode) ? "e" : "i", regionId, regionScope, 30)); return new ArrayList<>(set); } @@ -499,12 +524,14 @@ public class IeMatchServiceImpl implements IeMatchService { return count != null && count > 0; } - private List statusCandidates(String mode, int limit) { - List statuses = userStatusMapper.selectList(new LambdaQueryWrapper() + private List statusCandidates(String mode, String regionId, boolean regionScope, int limit) { + LambdaQueryWrapper query = new LambdaQueryWrapper() .eq(IeUserStatus::getOnlineStatus, 1) .eq(IeUserStatus::getMode, normalizeMode(mode, "i")) + .eq(regionScope, IeUserStatus::getRegionId, regionId) .orderByDesc(IeUserStatus::getLastActiveTime) - .last("limit " + Math.max(1, Math.min(limit, 100)))); + .last("limit " + Math.max(1, Math.min(limit, 100))); + List statuses = userStatusMapper.selectList(query); List users = new ArrayList<>(); for (IeUserStatus status : statuses) { if (status.getUserId() != null) { @@ -514,13 +541,15 @@ public class IeMatchServiceImpl implements IeMatchService { return users; } - private List profileCandidates(String mode, int limit) { - List profiles = userProfileMapper.selectList(new LambdaQueryWrapper() + private List profileCandidates(String mode, String regionId, boolean regionScope, int limit) { + LambdaQueryWrapper query = new LambdaQueryWrapper() .eq(IeUserProfile::getProfileCompleted, 1) .eq(IeUserProfile::getCurrentMode, normalizeMode(mode, "i")) .eq(IeUserProfile::getIsDeleted, 0) + .eq(regionScope, IeUserProfile::getRegionId, regionId) .orderByDesc(IeUserProfile::getLastActiveTime) - .last("limit " + Math.max(1, Math.min(limit, 100)))); + .last("limit " + Math.max(1, Math.min(limit, 100))); + List profiles = userProfileMapper.selectList(query); List users = new ArrayList<>(); for (IeUserProfile profile : profiles) { if (profile.getUserId() != null) { @@ -566,6 +595,8 @@ public class IeMatchServiceImpl implements IeMatchService { vo.setRecentPreference(profile.getRecentPreference()); vo.setTargetModePreference(profile.getTargetModePreference()); vo.setTargetGenderPreference(profile.getTargetGenderPreference()); + vo.setRegionId(profile.getRegionId()); + vo.setRegionName(profile.getRegionName()); vo.setDefaultRoomMinutes(profile.getDefaultRoomMinutes()); vo.setDailyQuota(profile.getDailyQuota()); vo.setUsedQuota(profile.getUsedQuota()); @@ -612,10 +643,24 @@ public class IeMatchServiceImpl implements IeMatchService { return "any"; } - private boolean matchCandidate(IeUserProfile candidateProfile, String selfTargetMode, String selfTargetGender) { + private String normalizeMatchScope(String value) { + if ("world".equalsIgnoreCase(value)) { + return "world"; + } + return "school"; + } + + private boolean hasText(String value) { + return value != null && !value.trim().isEmpty(); + } + + private boolean matchCandidate(IeUserProfile candidateProfile, String selfTargetMode, String selfTargetGender, String regionId, boolean regionScope) { if (candidateProfile == null) { return false; } + if (regionScope && !Objects.equals(regionId, candidateProfile.getRegionId())) { + return false; + } String candidateMode = normalizeMode(candidateProfile.getCurrentMode(), "i"); if (!"any".equals(selfTargetMode) && !selfTargetMode.equals(candidateMode)) { return false; diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeRedisServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeRedisServiceImpl.java index 508f730a..aadebe7f 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeRedisServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/service/impl/IeRedisServiceImpl.java @@ -47,6 +47,8 @@ public class IeRedisServiceImpl implements IeRedisService { stringRedisTemplate.opsForHash().put(key, "statusText", defaultText(dto.getStatusText(), "")); stringRedisTemplate.opsForHash().put(key, "longitude", dto.getLongitude() == null ? "" : String.valueOf(dto.getLongitude())); stringRedisTemplate.opsForHash().put(key, "latitude", dto.getLatitude() == null ? "" : String.valueOf(dto.getLatitude())); + stringRedisTemplate.opsForHash().put(key, "regionId", defaultText(dto.getRegionId(), "")); + stringRedisTemplate.opsForHash().put(key, "regionName", defaultText(dto.getRegionName(), "")); stringRedisTemplate.opsForHash().put(key, "interestTags", dto.getInterestTags() == null ? "[]" : JSONUtil.toJsonStr(dto.getInterestTags())); stringRedisTemplate.opsForHash().put(key, "lastActiveTime", DateUtil.now()); stringRedisTemplate.expire(key, STATUS_SECONDS, TimeUnit.SECONDS); @@ -60,7 +62,12 @@ public class IeRedisServiceImpl implements IeRedisService { removeFromMatchPools(userId); String mode = defaultText(dto.getMode(), "i"); String mood = defaultText(dto.getMood(), "quiet"); - String poolKey = IeRedisKey.matchPool(mode, mood); + List poolKeys = new ArrayList<>(); + poolKeys.add(IeRedisKey.matchPool(mode, mood)); + String regionId = defaultText(dto.getRegionId(), ""); + if (!regionId.isEmpty()) { + poolKeys.add(IeRedisKey.regionMatchPool(regionId, mode, mood)); + } double score = System.currentTimeMillis(); if ("i".equals(mode)) { score += 5000; @@ -68,16 +75,20 @@ public class IeRedisServiceImpl implements IeRedisService { if (dto.getInterestTags() != null) { score += dto.getInterestTags().size() * 100; } - stringRedisTemplate.opsForZSet().add(poolKey, String.valueOf(userId), score); - stringRedisTemplate.expire(poolKey, STATUS_SECONDS, TimeUnit.SECONDS); - stringRedisTemplate.opsForValue().set(IeRedisKey.USER_MATCH_POOL + userId, poolKey, STATUS_SECONDS, TimeUnit.SECONDS); + for (String poolKey : poolKeys) { + stringRedisTemplate.opsForZSet().add(poolKey, String.valueOf(userId), score); + stringRedisTemplate.expire(poolKey, STATUS_SECONDS, TimeUnit.SECONDS); + } + stringRedisTemplate.opsForValue().set(IeRedisKey.USER_MATCH_POOL + userId, JSONUtil.toJsonStr(poolKeys), STATUS_SECONDS, TimeUnit.SECONDS); } @Override public void removeFromMatchPools(Long userId) { - String poolKey = stringRedisTemplate.opsForValue().get(IeRedisKey.USER_MATCH_POOL + userId); - if (poolKey != null && poolKey.length() > 0) { - stringRedisTemplate.opsForZSet().remove(poolKey, String.valueOf(userId)); + String poolKeysJson = stringRedisTemplate.opsForValue().get(IeRedisKey.USER_MATCH_POOL + userId); + if (poolKeysJson != null && poolKeysJson.length() > 0) { + for (String poolKey : parsePoolKeys(poolKeysJson)) { + stringRedisTemplate.opsForZSet().remove(poolKey, String.valueOf(userId)); + } } stringRedisTemplate.delete(IeRedisKey.USER_MATCH_POOL + userId); } @@ -94,17 +105,18 @@ public class IeRedisServiceImpl implements IeRedisService { } @Override - public List candidates(String mode, String mood, int limit) { + public List candidates(String mode, String mood, String regionId, boolean regionScope, int limit) { List keys = new ArrayList<>(); String targetMode = defaultText(mode, "i"); String targetMood = defaultText(mood, "quiet"); - keys.add(IeRedisKey.matchPool(targetMode, targetMood)); + String safeRegionId = defaultText(regionId, ""); + keys.add(matchPoolKey(targetMode, targetMood, safeRegionId, regionScope)); if (!"quiet".equals(targetMood)) { - keys.add(IeRedisKey.matchPool(targetMode, "quiet")); + keys.add(matchPoolKey(targetMode, "quiet", safeRegionId, regionScope)); } - addPoolKey(keys, targetMode, "talk"); - addPoolKey(keys, targetMode, "listen"); - addPoolKey(keys, targetMode, "drift"); + addPoolKey(keys, targetMode, "talk", safeRegionId, regionScope); + addPoolKey(keys, targetMode, "listen", safeRegionId, regionScope); + addPoolKey(keys, targetMode, "drift", safeRegionId, regionScope); List result = new ArrayList<>(); for (String key : keys) { Set members = stringRedisTemplate.opsForZSet().reverseRange(key, 0, limit - 1L); @@ -121,13 +133,20 @@ public class IeRedisServiceImpl implements IeRedisService { return result; } - private void addPoolKey(List keys, String mode, String mood) { - String key = IeRedisKey.matchPool(mode, mood); + private void addPoolKey(List keys, String mode, String mood, String regionId, boolean regionScope) { + String key = matchPoolKey(mode, mood, regionId, regionScope); if (!keys.contains(key)) { keys.add(key); } } + private String matchPoolKey(String mode, String mood, String regionId, boolean regionScope) { + if (regionScope && regionId != null && !regionId.isEmpty()) { + return IeRedisKey.regionMatchPool(regionId, mode, mood); + } + return IeRedisKey.matchPool(mode, mood); + } + @Override public boolean isRecentlyMatched(Long userId, Long targetUserId) { Boolean result = stringRedisTemplate.opsForSet().isMember(IeRedisKey.RECENT_MATCH + userId, String.valueOf(targetUserId)); @@ -257,8 +276,9 @@ public class IeRedisServiceImpl implements IeRedisService { } @Override - public long waitingCount(String mode, String mood) { - Long count = stringRedisTemplate.opsForZSet().zCard(IeRedisKey.matchPool(defaultText(mode, "i"), defaultText(mood, "quiet"))); + public long waitingCount(String mode, String mood, String regionId, boolean regionScope) { + String key = matchPoolKey(defaultText(mode, "i"), defaultText(mood, "quiet"), defaultText(regionId, ""), regionScope); + Long count = stringRedisTemplate.opsForZSet().zCard(key); return count == null ? 0L : count; } @@ -276,6 +296,16 @@ public class IeRedisServiceImpl implements IeRedisService { return IeRedisKey.DAILY_QUOTA + date + ":" + userId; } + private List parsePoolKeys(String value) { + List keys = new ArrayList<>(); + try { + keys.addAll(JSONUtil.toList(JSONUtil.parseArray(value), String.class)); + } catch (Exception ignored) { + keys.add(value); + } + return keys; + } + private String defaultText(String value, String defaultValue) { return value == null || value.trim().isEmpty() ? defaultValue : value.trim(); } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/vo/IeMatchVO.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/vo/IeMatchVO.java index 5217491e..a48e16e9 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/vo/IeMatchVO.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/vo/IeMatchVO.java @@ -14,6 +14,8 @@ public class IeMatchVO { private String avatarText; private String avatarUrl; private String gender; + private String regionId; + private String regionName; private String intro; private List interestTags; private List personaImages; diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/vo/IeUserProfileVO.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/vo/IeUserProfileVO.java index ba72d57d..a52bd113 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/vo/IeUserProfileVO.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/ie/vo/IeUserProfileVO.java @@ -18,6 +18,8 @@ public class IeUserProfileVO { private String recentPreference; private String targetModePreference; private String targetGenderPreference; + private String regionId; + private String regionName; private Integer defaultRoomMinutes; private Integer dailyQuota; private Integer usedQuota; diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderAsyncConsumer.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderAsyncConsumer.java index e63aa20e..912a841e 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderAsyncConsumer.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/mq/OrderAsyncConsumer.java @@ -243,7 +243,7 @@ public class OrderAsyncConsumer { return; } try { - ieMatchService.rewardMatchQuota(Long.valueOf(userId), 1); + ieMatchService.rewardMatchQuota(Long.valueOf(userId), 3); } catch (NumberFormatException e) { log.warn("閰嶉佸畬鎴愬紓姝ュ鍔爄/e鍖归厤鏈轰細澶辫触锛岀敤鎴稩D涓嶆槸鏁板瓧 userId={}", userId); } catch (Exception e) { diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/controller/PlanetController.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/controller/PlanetController.java index 36723e2a..d2b97892 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/controller/PlanetController.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/controller/PlanetController.java @@ -4,6 +4,7 @@ import cc.hiver.core.common.exception.HiverException; import cc.hiver.core.common.utils.ResultUtil; import cc.hiver.core.common.vo.Result; import cc.hiver.mall.planet.entity.PlanetDrawRecord; +import cc.hiver.mall.planet.entity.PlanetTicket; import cc.hiver.mall.planet.pojo.*; import cc.hiver.mall.planet.service.*; import io.swagger.annotations.Api; @@ -335,6 +336,23 @@ public class PlanetController { ticketService.pageLog(query.getUserId(), query.getRegionId(), query.getPageNumber(), query.getPageSize())); } + @ApiOperation(value = "鎴戠殑瀹炴椂鏄熺悆鍒告暟閲") + @RequestMapping(value = "/ticket/count", method = RequestMethod.POST) + public Result ticketCount(@RequestBody PlanetQuery query) { + if (StringUtils.isEmpty(query.getUserId())) { + return ResultUtil.error("鐢ㄦ埛id涓嶈兘涓虹┖"); + } + if (StringUtils.isEmpty(query.getRegionId())) { + return ResultUtil.error(REGION_REQUIRED); + } + final PlanetTicket ticket = ticketService.getByUser(query.getUserId(), query.getRegionId()); + final int ticketCount = ticket == null || ticket.getTicketCount() == null ? 0 : ticket.getTicketCount(); + final Map data = new HashMap<>(2); + data.put("ticketCount", ticketCount); + data.put("myTicketCount", ticketCount); + return new ResultUtil<>().setData(data); + } + @ApiOperation(value = "缁忚惀鐜╂硶鑱氬悎鏁版嵁") @RequestMapping(value = "/operate/overview", method = RequestMethod.POST) public Result operateOverview(@RequestBody PlanetQuery query) { @@ -585,6 +603,15 @@ public class PlanetController { return new ResultUtil<>().setData(adventureService.rankTop(query.getRegionId())); } + @ApiOperation(value = "瀛﹂櫌鍔╂帹璧涙槰鏃ュ鍔辫褰") + @RequestMapping(value = "/adventure/yesterdayRewards", method = RequestMethod.POST) + public Result adventureYesterdayRewards(@RequestBody PlanetQuery query) { + if (StringUtils.isEmpty(query.getRegionId())) { + return ResultUtil.error(REGION_REQUIRED); + } + return new ResultUtil<>().setData(adventureService.yesterdayRewards(query.getRegionId())); + } + @ApiOperation(value = "瀛﹂櫌鍔╂帹璧涗粖鏃ユ渶蹇崳鑰姒") @RequestMapping(value = "/adventure/glory", method = RequestMethod.POST) public Result adventureGlory(@RequestBody PlanetQuery query) { diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/pojo/PlanetAdventureRewardRecordVo.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/pojo/PlanetAdventureRewardRecordVo.java new file mode 100644 index 00000000..7598ebbd --- /dev/null +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/pojo/PlanetAdventureRewardRecordVo.java @@ -0,0 +1,23 @@ +package cc.hiver.mall.planet.pojo; + +import io.swagger.annotations.ApiModel; +import lombok.Data; + +import java.io.Serializable; +import java.time.LocalDate; + +@Data +@ApiModel(value = "鐧藉珫鏄熺悆-瀛﹂櫌鍔╂帹璧涙槰鏃ュ鍔辫褰") +public class PlanetAdventureRewardRecordVo implements Serializable { + private static final long serialVersionUID = 1L; + private Integer rankNo; + private String userId; + private String nickname; + private String avatar; + private String college; + private LocalDate rewardDate; + private Integer durationSeconds; + private Integer score; + private Integer rewardTickets; + private String rewardContent; +} diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/PlanetAdventureService.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/PlanetAdventureService.java index d7038158..ecd81765 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/PlanetAdventureService.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/PlanetAdventureService.java @@ -5,10 +5,12 @@ import cc.hiver.mall.planet.entity.PlanetAdventureEvent; import cc.hiver.mall.planet.entity.PlanetAdventureRank; import cc.hiver.mall.planet.entity.PlanetAdventureSession; import cc.hiver.mall.planet.pojo.PlanetAdventureHomeVo; +import cc.hiver.mall.planet.pojo.PlanetAdventureRewardRecordVo; import cc.hiver.mall.planet.pojo.PlanetAdventureStartVo; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import java.util.List; +import java.util.Map; public interface PlanetAdventureService { @@ -23,6 +25,8 @@ public interface PlanetAdventureService { List rankTop(String regionId); + Map> yesterdayRewards(String regionId); + int settleDueRanks(); Page pageEvents(PageVo pageVo, String regionId); diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/impl/PlanetAdventurePkServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/impl/PlanetAdventurePkServiceImpl.java index 7c967f34..4f24ac24 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/impl/PlanetAdventurePkServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/impl/PlanetAdventurePkServiceImpl.java @@ -300,6 +300,10 @@ public class PlanetAdventurePkServiceImpl implements PlanetAdventurePkService { private void settleIfReady(PlanetAdventurePkRoom room) { List players = playerMapper.selectList(new LambdaQueryWrapper() .eq(PlanetAdventurePkPlayer::getRoomId, room.getId())); + int maxPlayers = room.getMaxPlayers() == null ? 2 : room.getMaxPlayers(); + if (maxPlayers > 2 && players.size() < maxPlayers) { + return; + } for (PlanetAdventurePkPlayer p : players) { if (!terminal(p)) return; } diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/impl/PlanetAdventureServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/impl/PlanetAdventureServiceImpl.java index 70348897..49d567ce 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/impl/PlanetAdventureServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/impl/PlanetAdventureServiceImpl.java @@ -12,6 +12,7 @@ import cc.hiver.mall.planet.mapper.PlanetAdventureRankMapper; import cc.hiver.mall.planet.mapper.PlanetAdventureSessionMapper; import cc.hiver.mall.planet.mapper.PlanetCollegeConfigMapper; import cc.hiver.mall.planet.pojo.PlanetAdventureHomeVo; +import cc.hiver.mall.planet.pojo.PlanetAdventureRewardRecordVo; import cc.hiver.mall.planet.pojo.PlanetAdventureStartVo; import cc.hiver.mall.planet.service.PlanetAdventureService; import cc.hiver.mall.planet.service.PlanetTicketService; @@ -32,6 +33,7 @@ public class PlanetAdventureServiceImpl implements PlanetAdventureService { private static final int GAME_SECONDS = 180; private static final LocalTime SETTLE_TIME = LocalTime.of(21, 30); private static final int CLEAR_BASE_PROGRESS = 100; + private static final int[] PERSONAL_REWARD_CASH = {5, 4, 3, 2, 1}; @Autowired private PlanetAdventureSessionMapper sessionMapper; @@ -177,6 +179,15 @@ public class PlanetAdventureServiceImpl implements PlanetAdventureService { .last("limit 20")); } + @Override + public Map> yesterdayRewards(String regionId) { + LocalDate yesterday = LocalDate.now().minusDays(1); + Map> data = new HashMap<>(4); + data.put("personalRewardList", yesterdayPersonalRewards(regionId, yesterday)); + data.put("collegeRewardList", yesterdayCollegeRewards(regionId, yesterday)); + return data; + } + @Override @Transactional(rollbackFor = Exception.class) public int settleDueRanks() { @@ -325,6 +336,59 @@ public class PlanetAdventureServiceImpl implements PlanetAdventureService { return rank == null || rank.getRankNo() == null ? 0 : rank.getRankNo(); } + private List yesterdayPersonalRewards(String regionId, LocalDate date) { + List sessions = sessionMapper.selectList(new LambdaQueryWrapper() + .eq(PlanetAdventureSession::getRegionId, regionId) + .eq(PlanetAdventureSession::getPlayDate, date) + .eq(PlanetAdventureSession::getStatus, PlanetConstant.ADVENTURE_SESSION_SUBMITTED) + .eq(PlanetAdventureSession::getCheatFlag, "N") + .orderByAsc(PlanetAdventureSession::getDurationSeconds) + .last("limit 5")); + List records = new ArrayList<>(); + for (int i = 0; i < sessions.size(); i++) { + PlanetAdventureSession s = sessions.get(i); + PlanetAdventureRewardRecordVo vo = new PlanetAdventureRewardRecordVo(); + vo.setRankNo(i + 1); + vo.setUserId(s.getUserId()); + vo.setNickname(s.getNickname()); + vo.setAvatar(s.getAvatar()); + vo.setCollege(s.getCollege()); + vo.setRewardDate(date); + vo.setDurationSeconds(nz(s.getDurationSeconds())); + vo.setScore(nz(s.getScore())); + vo.setRewardContent("鐜伴噾濂栧姳" + PERSONAL_REWARD_CASH[i] + "鍏"); + records.add(vo); + } + return records; + } + + private List yesterdayCollegeRewards(String regionId, LocalDate date) { + List ranks = rankMapper.selectList(new LambdaQueryWrapper() + .eq(PlanetAdventureRank::getRegionId, regionId) + .eq(PlanetAdventureRank::getRankDate, date) + .orderByDesc(PlanetAdventureRank::getScore) + .orderByAsc(PlanetAdventureRank::getUpdateTime) + .last("limit 3")); + List records = new ArrayList<>(); + for (int i = 0; i < ranks.size(); i++) { + PlanetAdventureRank r = ranks.get(i); + int rankNo = r.getRankNo() == null || r.getRankNo() <= 0 ? i + 1 : r.getRankNo(); + int rewardTickets = Math.max(0, 4 - rankNo); + PlanetAdventureRewardRecordVo vo = new PlanetAdventureRewardRecordVo(); + vo.setRankNo(rankNo); + vo.setUserId(r.getUserId()); + vo.setNickname(r.getNickname()); + vo.setAvatar(r.getAvatar()); + vo.setCollege(r.getCollege()); + vo.setRewardDate(date); + vo.setScore(nz(r.getScore())); + vo.setRewardTickets(rewardTickets); + vo.setRewardContent("瀛﹂櫌鎴愬憳姣忎汉" + rewardTickets + "寮犳槦鐞冨埜"); + records.add(vo); + } + return records; + } + private String validateClear(int n, int g, int d, int r, Integer clientScore, int seconds) { if (seconds < 20 || seconds > GAME_SECONDS + 20) return "闂叧鏃堕暱寮傚父"; if (n < 36) return "娑堥櫎鏁伴噺涓嶈冻"; diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/impl/PlanetDrawServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/impl/PlanetDrawServiceImpl.java index fa11266c..207ca587 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/impl/PlanetDrawServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/planet/service/impl/PlanetDrawServiceImpl.java @@ -272,22 +272,39 @@ public class PlanetDrawServiceImpl implements PlanetDrawService { final Set cooldownUsers = recentWinnerUserIds(pool.getRegionId(), now); boolean useCooldown = hasCandidate(poolCandidates, cooldownUsers); int winnerCount = 0; + final List missedRewards = new ArrayList<>(); for (PlanetReward reward : rewards) { final int quota = reward.getQuota() == null ? 0 : reward.getQuota(); for (int i = 0; i < quota; i++) { List usable = usableCandidates(poolCandidates, useCooldown ? cooldownUsers : Collections.emptySet()); if (usable.isEmpty()) { - break; + missedRewards.add(reward); + continue; } final Candidate winner = pickWeighted(usable); if (winner == null) { - break; + missedRewards.add(reward); + continue; } winner.win = true; saveWinner(record, reward, winner.ticket, now); winnerCount++; } } + // 绗竴杞伒寰師鏈夊喎鍗磋鍒欙紱鑻ヤ粛鏈夊椤规湭寮鍑猴紝鍒欎粠鏈腑濂栧弬涓庤呬腑琛ユ娊锛岀洿鍒板椤瑰紑瀹屾垨鍙備笌鑰呴兘涓銆 + for (PlanetReward reward : missedRewards) { + final List usable = usableCandidates(poolCandidates, Collections.emptySet()); + if (usable.isEmpty()) { + break; + } + final Candidate winner = pickWeighted(usable); + if (winner == null) { + break; + } + winner.win = true; + saveWinner(record, reward, winner.ticket, now); + winnerCount++; + } record.setWinnerCount(winnerCount); drawRecordMapper.insert(record); diff --git a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java index 61ca3056..40e7322b 100644 --- a/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java +++ b/hiver-modules/hiver-mall/src/main/java/cc/hiver/mall/serviceimpl/mybatis/MallOrderServiceImpl.java @@ -1208,7 +1208,7 @@ public class MallOrderServiceImpl extends ServiceImpl