feat(ff-game): 添加创建成员接口的缓存锁功能

- 在 ApiMemberController 中添加 RedisCacheLock 依赖
- 在 createMember 方法中实现基于 Redis 的分布式锁- 新增 CacheLockConstants 类用于定义缓存锁的键常量
- 在 ff-base 中添加 Redisson 相关依赖
- 新增 RedisCacheLock 类用于实现 Redis 缓存锁功能
- 修改 application-druid.yml 配置,更新 Redis 连接信息
main-cf
shi 2025-03-18 14:10:34 +08:00
parent 170584861b
commit 59af0c07a7
6 changed files with 156 additions and 51 deletions

View File

@ -121,6 +121,13 @@
<artifactId>commons-io</artifactId>
</dependency>
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.23.0</version>
</dependency>
<!-- excel工具 -->
<dependency>
<groupId>org.apache.poi</groupId>

View File

@ -61,4 +61,6 @@ public class CacheConstants
* pg
*/
public static final String PG_GAMES_BET_CURRENCY= "pg_games:bet:currency";
}

View File

@ -0,0 +1,17 @@
package com.ff.base.constant;
/**
* key
*
* @author ff
*/
public class CacheLockConstants
{
/**
* redis key
*/
public static final String CREATE_MEMBER = "CREATE:MEMBER:";
}

View File

@ -0,0 +1,71 @@
package com.ff.base.core.redis;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;
/**
* redis
*
* @author shi
* @date 2025/03/18
*/
@Component
public class RedisCacheLock {
@Resource
private RedissonClient redissonClient;
/**
* Redis
*
* @param key Redis
* @return `RLock`
*
* `RLock` Redisson 线线
*/
public RLock getLock(String key) {
return redissonClient.getLock(key);
}
/**
*
*
* @param key Redis
* @param waitTime `false`
* @param leaseTime `leaseTime`
* @return `true` `false`
*
* 线`tryLock` 线
* 线线 `waitTime` `false`
*/
public boolean tryLock(String key, long waitTime, long leaseTime) {
RLock lock = redissonClient.getLock(key);
try {
return lock.tryLock(waitTime, leaseTime, TimeUnit.SECONDS);
} catch (InterruptedException e) {
Thread.currentThread().interrupt(); // 恢复线程中断状态
return false;
}
}
/**
*
*
* @param key Redis
*
* 线 `RLock` `isHeldByCurrentThread()` `true`
* 线
*/
public void unlock(String key) {
RLock lock = redissonClient.getLock(key);
if (lock.isHeldByCurrentThread()) {
lock.unlock(); // 释放锁
}
}
}

View File

@ -7,9 +7,11 @@ import com.ff.api.request.MemberInfoAllApiRequest;
import com.ff.api.request.MemberInfoApiRequest;
import com.ff.api.response.MemberInfoAllResponse;
import com.ff.api.response.MemberInfoResponse;
import com.ff.base.constant.CacheLockConstants;
import com.ff.base.constant.Constants;
import com.ff.base.core.controller.BaseController;
import com.ff.base.core.domain.AjaxResult;
import com.ff.base.core.redis.RedisCacheLock;
import com.ff.base.enums.ErrorCode;
import com.ff.base.exception.base.ApiException;
import com.ff.base.exception.base.BaseException;
@ -18,7 +20,6 @@ import com.ff.base.system.domain.TenantSecretKey;
import com.ff.config.KeyConfig;
import com.ff.game.api.IGamesService;
import com.ff.game.api.request.*;
import com.ff.game.domain.GameSecretKey;
import com.ff.game.dto.GameSecretKeyCurrencyDTO;
import com.ff.game.service.IGameSecretKeyCurrencyService;
import com.ff.game.service.IGameSecretKeyService;
@ -30,9 +31,9 @@ import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.transaction.annotation.Isolation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
@ -84,6 +85,10 @@ public class ApiMemberController extends BaseController {
@Resource
private IGameSecretKeyCurrencyService gameSecretKeyCurrencyService;
@Resource
private RedisCacheLock redisCacheLock;
/**
*
*
@ -91,13 +96,20 @@ public class ApiMemberController extends BaseController {
* @return {@link AjaxResult }
*/
@PostMapping("/create")
@Transactional
public synchronized AjaxResult createMember(@Validated @RequestBody MemberCreateApiRequest memberCreateApiRequest) {
@Transactional(isolation = Isolation.READ_COMMITTED)
public AjaxResult createMember(@Validated @RequestBody MemberCreateApiRequest memberCreateApiRequest) {
TenantSecretKey tenantSecretKey = keyConfig.get();
String lockName = CacheLockConstants.CREATE_MEMBER + memberCreateApiRequest.getAccount() + memberCreateApiRequest.getCurrencyCode() + memberCreateApiRequest.getPlatformCode() + tenantSecretKey.getTenantSn();
//加锁防止重复
boolean tryLock = redisCacheLock.tryLock(lockName, 10, 10);
try {
if (tryLock) {
IGamesService iGamesService = gamesService.get(memberCreateApiRequest.getPlatformCode() + Constants.SERVICE);
ApiException.notNull(iGamesService, ErrorCode.PLATFORM_NOT_EXIST.getCode());
TenantSecretKey tenantSecretKey = keyConfig.get();
GameSecretKeyCurrencyDTO gameSecretKey = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
.platformCode(memberCreateApiRequest.getPlatformCode())
.systemCurrency(memberCreateApiRequest.getCurrencyCode()).build());
@ -106,16 +118,11 @@ public class ApiMemberController extends BaseController {
String gameAccount = StringUtils.addSuffix(memberService.getMemberGameAccount(), tenantSecretKey.getTenantSn());
// 获取用户信息
Member gameMember = memberService.selectMemberByAccount(memberCreateApiRequest.getAccount(), memberCreateApiRequest.getCurrencyCode(), memberCreateApiRequest.getPlatformCode());
if (!ObjectUtils.isEmpty(gameMember)) {
throw new ApiException(ErrorCode.GAME_ACCOUNT_CREATION_FAILED.getCode());
}
//注册本地账号
Member member = Member.builder()
.tenantKey(tenantSecretKey.getTenantKey())
@ -136,8 +143,13 @@ public class ApiMemberController extends BaseController {
.build();
Boolean result = iGamesService.createMember(gamesBaseRequestDTO);
Assert.isTrue(result, "建立游戏账号失败");
} else {
throw new ApiException(ErrorCode.FREQUENT_INTERFACE_REQUESTS.getCode());
}
return toAjax(Boolean.TRUE);
} finally {
redisCacheLock.unlock(lockName);
}
}
@ -166,7 +178,6 @@ public class ApiMemberController extends BaseController {
ApiException.notNull(member, ErrorCode.ACCOUNT_NOT_EXIST.getCode());
//向第三方查询账号
MemberInfoRequestDTO gamesBaseRequestDTO = MemberInfoRequestDTO.builder()
.accounts(member.getGameAccount())
@ -191,9 +202,6 @@ public class ApiMemberController extends BaseController {
public AjaxResult infoAll(@Validated @RequestBody MemberInfoAllApiRequest memberInfoAllApiRequest) {
List<GameSecretKeyCurrencyDTO> gameSecretKeys = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTOList(GameSecretKeyCurrencyDTO.builder()
.systemCurrency(memberInfoAllApiRequest.getCurrencyCode()).build());

View File

@ -3,13 +3,13 @@ spring:
# redis 配置
redis:
# 地址
host: 127.0.0.1
host: 192.168.50.11
# 端口默认为6379
port: 6379
port: 26379
# 数据库索引
database: 1
database: 10
# 密码
password:
password: reAa123456
# 连接超时时间
timeout: 10s
lettuce: