diff --git a/ff-base/src/main/java/com/ff/base/constant/CacheConstants.java b/ff-base/src/main/java/com/ff/base/constant/CacheConstants.java index 013c73c..1e5c620 100644 --- a/ff-base/src/main/java/com/ff/base/constant/CacheConstants.java +++ b/ff-base/src/main/java/com/ff/base/constant/CacheConstants.java @@ -51,6 +51,13 @@ public class CacheConstants { */ public static final String XK_GAMES = "xk_games:"; + + /** + * km游戏 + */ + public static final String KM_GAMES = "km_games:"; + + /** * pgx游戏 */ @@ -94,6 +101,12 @@ public class CacheConstants { public static final String Platform = "platform:"; + + /** + * km用户令牌 + */ + public static final String KM_USER_TOKEN = "km:user:token:"; + } diff --git a/ff-base/src/main/java/com/ff/base/constant/Constants.java b/ff-base/src/main/java/com/ff/base/constant/Constants.java index 73cdc9a..dfbc43c 100644 --- a/ff-base/src/main/java/com/ff/base/constant/Constants.java +++ b/ff-base/src/main/java/com/ff/base/constant/Constants.java @@ -227,6 +227,17 @@ public class Constants { */ public static final String MEITIAN_API_BASE_URL = "meitian.api.base.url"; + + /** + * km-api基本url + */ + public static final String KM_API_BASE_URL_LOGIN = "km.api.base.url"; + /** + * km-api基本登录url + */ + public static final String KM_API_BASE_LOGIN_URL = "km.api.base.login.url"; + + /** * 服务 */ @@ -238,15 +249,12 @@ public class Constants { public static final String SYSTEM = "system"; - - /** * 主 数据源 */ public static final String DATA_SOURCE = "master"; - /** * 租户id */ @@ -300,5 +308,5 @@ public class Constants { /** * 一百 */ - public static final BigDecimal HUNDRED =new BigDecimal("100") ; + public static final BigDecimal HUNDRED = new BigDecimal("100"); } diff --git a/ff-base/src/main/java/com/ff/base/enums/GamePlatforms.java b/ff-base/src/main/java/com/ff/base/enums/GamePlatforms.java index 19a1227..5689a02 100644 --- a/ff-base/src/main/java/com/ff/base/enums/GamePlatforms.java +++ b/ff-base/src/main/java/com/ff/base/enums/GamePlatforms.java @@ -13,6 +13,7 @@ public enum GamePlatforms { DG("DG", "DG"), MT("MT", "美天棋牌"), AE("AE", "AE"), + KM("KM", "KM") ; private final String code; diff --git a/ff-base/src/main/java/com/ff/base/enums/KMGameType.java b/ff-base/src/main/java/com/ff/base/enums/KMGameType.java new file mode 100644 index 0000000..460795c --- /dev/null +++ b/ff-base/src/main/java/com/ff/base/enums/KMGameType.java @@ -0,0 +1,70 @@ +package com.ff.base.enums; + + +import java.util.Optional; +import java.util.stream.Stream; + + +/** + * xkgame类型 + * + * @author shi + * @date 2024/11/13 + */ +public enum KMGameType { + + ELECTRON("KMQM", 1,"电子") + + ; + + private final String code; + private final Integer systemCode; + private final String info; + KMGameType(String code, Integer systemCode, String info) + { + this.code = code; + this.systemCode = systemCode; + this.info = info; + } + + public String getCode() + { + return code; + } + + public Integer getSystemCode() + { + return systemCode; + } + public String getInfo() + { + return info; + } + /** + * 按代码查找系统 + * + * @param code 代码 + * @return {@link String } + */ + public static Integer findSystemByCode(String code) { + Optional system = Stream.of(KMGameType.values()) + .filter(gameType -> gameType.getCode().equals(code)) + .map(KMGameType::getSystemCode) + .findFirst(); + return system.orElse(null); + } + + /** + * 按代码查找信息 + * + * @param code 代码 + * @return {@link String } + */ + public static String findInfoByCode(String code) { + Optional system = Stream.of(KMGameType.values()) + .filter(gameType -> gameType.getCode().equals(code)) + .map(KMGameType::getInfo) + .findFirst(); + return system.orElse(null); + } +} diff --git a/ff-base/src/main/java/com/ff/base/utils/JsonUtil.java b/ff-base/src/main/java/com/ff/base/utils/JsonUtil.java index f87c94e..b760170 100644 --- a/ff-base/src/main/java/com/ff/base/utils/JsonUtil.java +++ b/ff-base/src/main/java/com/ff/base/utils/JsonUtil.java @@ -108,7 +108,7 @@ public class JsonUtil { * * @param map 地图 * @return {@link String } - */// 将LinkedHashMap转换为查询字符串 + */ // 将LinkedHashMap转换为查询字符串 public static String mapToQueryString(Map map) { return map.entrySet().stream() .map(entry -> entry.getKey() + "=" + entry.getValue()) diff --git a/ff-game/src/main/java/com/ff/api/controller/ApiGameController.java b/ff-game/src/main/java/com/ff/api/controller/ApiGameController.java index 8abd9d3..0c29474 100644 --- a/ff-game/src/main/java/com/ff/api/controller/ApiGameController.java +++ b/ff-game/src/main/java/com/ff/api/controller/ApiGameController.java @@ -422,7 +422,6 @@ public class ApiGameController extends BaseController { ApiException.notNull(iGamesService, ErrorCode.PLATFORM_NOT_EXIST.getCode()); - KickMemberAllDTO kickMemberAllDTO = KickMemberAllDTO.builder() .agentId(gameSecretKey.getCode()) .agentKey(gameSecretKey.getKey()) @@ -589,4 +588,43 @@ public class ApiGameController extends BaseController { return AjaxResult.success(balanceMap); } + + + /** + * 演示登录 + * + * @param gameDemoLoginRequest 游戏演示登录请求 + * @return {@link AjaxResult } + */ + @PostMapping("/demo/login") + public AjaxResult demoLogin(@Validated @RequestBody GameDemoLoginRequest gameDemoLoginRequest) { + + Game game = gameService.selectGameById(gameDemoLoginRequest.getGameId()); + ApiException.notNull(game, ErrorCode.GAME_NOT_EXIST.getCode()); + + GamePlatform gamePlatform = gamePlatformService.selectGamePlatformById(game.getPlatformId()); + ApiException.notNull(gamePlatform, ErrorCode.PLATFORM_NOT_EXIST.getCode()); + + + IGamesService iGamesService = gamesService.get(gamePlatform.getPlatformCode() + Constants.SERVICE); + + + GameSecretKeyLangDTO gameSecretKeyLangDTO = gameSecretKeyLangService.findGameSecretKeyLangDTO(GameSecretKeyLangDTO.builder() + .platformCode(gamePlatform.getPlatformCode()) + .systemLangCode(gameDemoLoginRequest.getLangCode()) + .build()); + ApiException.notNull(gameSecretKeyLangDTO, ErrorCode.LANG_NOT_EXIST.getCode()); + + + GameDemoLoginRequestDTO gamesLogin = GameDemoLoginRequestDTO.builder() + .gameId(game.getGameCode()) + .gameType(game.getGameSourceType()) + .lang(gameSecretKeyLangDTO.getLang()) + .build(); + + GameDemoLoginResponseDTO gameDemoLoginResponseDTO = iGamesService.gameDemoLogin(gamesLogin); + GameDemoLoginResponse gameDemoLoginResponse = new GameDemoLoginResponse(); + BeanUtils.copyProperties(gameDemoLoginResponseDTO, gameDemoLoginResponse); + return AjaxResult.success(gameDemoLoginResponse); + } } diff --git a/ff-game/src/main/java/com/ff/api/controller/ApiMemberController.java b/ff-game/src/main/java/com/ff/api/controller/ApiMemberController.java index 95fdd5a..0a7454b 100644 --- a/ff-game/src/main/java/com/ff/api/controller/ApiMemberController.java +++ b/ff-game/src/main/java/com/ff/api/controller/ApiMemberController.java @@ -133,6 +133,7 @@ public class ApiMemberController extends BaseController { .agentId(gameSecretKey.getCode()) .agentKey(gameSecretKey.getKey()) .betLimit(memberCreateApiRequest.getBetLimit()) + .platformType(memberCreateApiRequest.getPlatformType()) .currency(gameSecretKey.getCurrency()) .build(); Boolean result = iGamesService.createMember(gamesBaseRequestDTO); diff --git a/ff-game/src/main/java/com/ff/api/request/GameDemoLoginRequest.java b/ff-game/src/main/java/com/ff/api/request/GameDemoLoginRequest.java new file mode 100644 index 0000000..315fdda --- /dev/null +++ b/ff-game/src/main/java/com/ff/api/request/GameDemoLoginRequest.java @@ -0,0 +1,53 @@ +package com.ff.api.request; + +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; +import java.util.Map; + +/** + * 游戏登录请求 + * + * @author shi + * @date 2025/02/11 + */ +@Data +public class GameDemoLoginRequest implements Serializable { + private final static long serialVersionUID = 7699430372422335056L; + + + + + /** + * 语种id + */ + @NotBlank(message = "langCode不能为空") + @Length(max = 32, message = "langCode长度不能超过32个字符") + private String langCode; + + + /** + * 游戏id + */ + @NotNull(message = "gameId不能为空") + private Long gameId; + + + /** + * 游戏回主页功能导向位置 + */ + private String homeUrl; + /** + * 带入 web 或是 app + */ + private String platform; + /** + * 带入 1 即关闭全屏幕模式 + */ + private Integer disableFullScreen; + + +} diff --git a/ff-game/src/main/java/com/ff/api/request/MemberCreateApiRequest.java b/ff-game/src/main/java/com/ff/api/request/MemberCreateApiRequest.java index 1644857..83af3f7 100644 --- a/ff-game/src/main/java/com/ff/api/request/MemberCreateApiRequest.java +++ b/ff-game/src/main/java/com/ff/api/request/MemberCreateApiRequest.java @@ -40,4 +40,8 @@ public class MemberCreateApiRequest implements Serializable{ */ private Map>> betLimit; + /** + * 平台类型 0 桌面 1 移动 + */ + private Integer platformType; } diff --git a/ff-game/src/main/java/com/ff/api/response/GameDemoLoginResponse.java b/ff-game/src/main/java/com/ff/api/response/GameDemoLoginResponse.java new file mode 100644 index 0000000..1978ee0 --- /dev/null +++ b/ff-game/src/main/java/com/ff/api/response/GameDemoLoginResponse.java @@ -0,0 +1,28 @@ +package com.ff.api.response; + +import lombok.Data; +import org.hibernate.validator.constraints.Length; + +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; +import java.io.Serializable; + + +/** + * 游戏演示登录响应 + * + * @author shi + * @date 2025/04/03 + */ +@Data +public class GameDemoLoginResponse implements Serializable { + private final static long serialVersionUID = 7699430372422335056L; + + + + /** + * 网址 + */ + private String url; + +} diff --git a/ff-game/src/main/java/com/ff/game/api/IGamesService.java b/ff-game/src/main/java/com/ff/game/api/IGamesService.java index 4d7c6f3..1ee439d 100644 --- a/ff-game/src/main/java/com/ff/game/api/IGamesService.java +++ b/ff-game/src/main/java/com/ff/game/api/IGamesService.java @@ -96,6 +96,8 @@ public interface IGamesService { */ Boolean createFreeSpin(CreateFreeSpinRequestDTO createFreeSpinRequest); + + /** * 获取游戏详细信息 * @@ -136,6 +138,13 @@ public interface IGamesService { */ Boolean cancelFreeSpin(CancelFreeSpinRequestDTO cancelFreeSpinRequestDTO); + /** + * 游戏演示登录 + * + * @param gameDemoLoginRequestDTO 游戏演示登录请求dto + * @return {@link GameDemoLoginResponseDTO } + */ + GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO); /** diff --git a/ff-game/src/main/java/com/ff/game/api/ae/impl/GamesAEServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/ae/impl/GamesAEServiceImpl.java index 2c66655..ac359be 100644 --- a/ff-game/src/main/java/com/ff/game/api/ae/impl/GamesAEServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/ae/impl/GamesAEServiceImpl.java @@ -531,6 +531,17 @@ public class GamesAEServiceImpl implements IGamesService { throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } + /** + * 游戏演示登录 + * + * @param gameDemoLoginRequestDTO 游戏演示登录请求dto + * @return {@link GameDemoLoginResponseDTO } + */ + @Override + public GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + /** * 批量插入 diff --git a/ff-game/src/main/java/com/ff/game/api/dg/service/impl/GamesDGServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/dg/service/impl/GamesDGServiceImpl.java index b946566..7153ed0 100644 --- a/ff-game/src/main/java/com/ff/game/api/dg/service/impl/GamesDGServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/dg/service/impl/GamesDGServiceImpl.java @@ -501,6 +501,19 @@ public class GamesDGServiceImpl implements IGamesService { throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } + /** + * 游戏演示登录 + * + * @param gameDemoLoginRequestDTO 游戏演示登录请求dto + * @return {@link GameDemoLoginResponseDTO } + */ + @Override + public GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + + + /** * 取消赠送免费局数 * diff --git a/ff-game/src/main/java/com/ff/game/api/fc/impl/GamesFCServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/fc/impl/GamesFCServiceImpl.java index 5a4e93c..df5c607 100644 --- a/ff-game/src/main/java/com/ff/game/api/fc/impl/GamesFCServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/fc/impl/GamesFCServiceImpl.java @@ -610,6 +610,17 @@ public class GamesFCServiceImpl implements IGamesService { throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } + /** + * 游戏演示登录 + * + * @param gameDemoLoginRequestDTO 游戏演示登录请求dto + * @return {@link GameDemoLoginResponseDTO } + */ + @Override + public GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + /** * 数据构建 diff --git a/ff-game/src/main/java/com/ff/game/api/jili/service/impl/GamesJILIServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/jili/service/impl/GamesJILIServiceImpl.java index ac9b2f4..86b3d74 100644 --- a/ff-game/src/main/java/com/ff/game/api/jili/service/impl/GamesJILIServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/jili/service/impl/GamesJILIServiceImpl.java @@ -692,6 +692,17 @@ public class GamesJILIServiceImpl implements IGamesService { throw new BaseException(cancelFreeSpinResponseDTO.getMessage()); } } + /** + * 游戏演示登录 + * + * @param gameDemoLoginRequestDTO 游戏演示登录请求dto + * @return {@link GameDemoLoginResponseDTO } + */ + @Override + public GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + /** diff --git a/ff-game/src/main/java/com/ff/game/api/km/address/MyKMAddressSource.java b/ff-game/src/main/java/com/ff/game/api/km/address/MyKMAddressSource.java new file mode 100644 index 0000000..a5a3621 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/km/address/MyKMAddressSource.java @@ -0,0 +1,31 @@ +package com.ff.game.api.km.address; + +import com.dtflys.forest.callback.AddressSource; +import com.dtflys.forest.http.ForestAddress; +import com.dtflys.forest.http.ForestRequest; +import com.ff.base.constant.Constants; +import com.ff.base.system.service.ISysConfigService; +import org.springframework.stereotype.Component; + +import javax.annotation.Resource; + + +/** + * 我jili address来源 + * + * @author shi + * @date 2025/02/10 + */ +@Component +public class MyKMAddressSource implements AddressSource { + + @Resource + private ISysConfigService configService; + + + @Override + public ForestAddress getAddress(ForestRequest request) { + String apiBaseUrl = configService.selectConfigByKey(Constants.KM_API_BASE_URL_LOGIN); + return new ForestAddress("https",apiBaseUrl, 443,"api"); + } +} \ No newline at end of file diff --git a/ff-game/src/main/java/com/ff/game/api/km/client/KMClient.java b/ff-game/src/main/java/com/ff/game/api/km/client/KMClient.java new file mode 100644 index 0000000..9a480ea --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/km/client/KMClient.java @@ -0,0 +1,95 @@ +package com.ff.game.api.km.client; + + +import com.dtflys.forest.annotation.*; +import com.ff.game.api.dg.dto.DGBetRecordResponseDTO; +import com.ff.game.api.dg.dto.DGResponse; +import com.ff.game.api.dg.dto.DGUserListResponseDTO; +import com.ff.game.api.km.address.MyKMAddressSource; +import com.ff.game.api.km.dto.*; +import com.ff.game.api.km.success.MyKMSuccessCondition; +import com.ff.game.api.xk.dto.XKBetRecordResponseDTO; + +import java.util.Map; + +/** + * dg 请求 + * + * @author shi + * @date 2025/02/10 + */ +@Address(source = MyKMAddressSource.class) +@Success(condition = MyKMSuccessCondition.class) +public interface KMClient { + /** + * 创建成员 + * + * @param params 参数 + * @param header 头球 + * @return {@link DGResponse } + */ + @Post("/player/authorize") + KMAuthTokenResponse createMember(@JSONBody Map params, @Header Map header); + + /** + * 获取会员信息 + * + * @param params 参数 + */ + @Get("/player/balance?{params}") + KMBalanceResponse getMemberInfo(@Var("params") String params, @Header Map header); + + + /** + * 游戏 + * + * @param params 参数 + * @param header 头球 + * @return {@link KMGameResponse } + */ + @Get("/games?{params}") + KMGameResponse getGameList(@Var("params") String params, @Header Map header); + + + /** + * 加钱 + * + * @param params 参数 + * @param header 头球 + * @return {@link KMTransactionResponse } + */ + @Post(url = "/wallet/credit") + KMTransactionResponse credit(@JSONBody Map params, @Header Map header); + + + /** + * 扣钱 + * + * @param params 参数 + * @param header 头球 + * @return {@link KMTransactionResponse } + */ + @Post(url = "/wallet/debit") + KMTransactionResponse debit(@JSONBody Map params, @Header Map header); + + /** + * 按时间获取投注记录 + * + * @param params 参数 + * @return {@link KMBetRecordResponse } + */ + @Get(url ="/v2/history/bets?{params}") + KMBetRecordResponse getBetRecordByTime(@Var("params")String params, @Header Map header); + + + /** + * 踢腿队员 + * + * @param params 参数 + * @param header 头球 + * @return {@link KMKickMemberResponse } + */ + @Post(url = "/player/deauthorize") + KMKickMemberResponse kickMember(@JSONBody Map params, @Header Map header); + +} diff --git a/ff-game/src/main/java/com/ff/game/api/km/dto/KMAuthTokenResponse.java b/ff-game/src/main/java/com/ff/game/api/km/dto/KMAuthTokenResponse.java new file mode 100644 index 0000000..46c55b5 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/km/dto/KMAuthTokenResponse.java @@ -0,0 +1,37 @@ +package com.ff.game.api.km.dto; + + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * kmaust令牌响应 + * + * @author shi + * @date 2025/04/02 + */ +@Data +public class KMAuthTokenResponse { + /** + * 错误代码 + */ + @JsonProperty("err") + private Integer errorCode; + + /** + * 错误描述 + */ + @JsonProperty("errdesc") + private String errorDescription; + /** + * 身份验证令牌 + */ + @JsonProperty("authtoken") + private String authToken; + + /** + * 是否为新用户 + */ + @JsonProperty("isnew") + private Boolean isNew; +} diff --git a/ff-game/src/main/java/com/ff/game/api/km/dto/KMBalanceResponse.java b/ff-game/src/main/java/com/ff/game/api/km/dto/KMBalanceResponse.java new file mode 100644 index 0000000..4a32921 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/km/dto/KMBalanceResponse.java @@ -0,0 +1,38 @@ +package com.ff.game.api.km.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * km平衡响应 + * + * @author shi + * @date 2025/04/02 + */ +@Data +public class KMBalanceResponse { + /** + * 错误代码 + */ + @JsonProperty("err") + private Integer errorCode; + + /** + * 错误描述 + */ + @JsonProperty("errdesc") + private String errorDescription; + /** + * 余额 + */ + @JsonProperty("bal") + private BigDecimal balance; + + /** + * 货币代码 + */ + @JsonProperty("cur") + private String currency; +} diff --git a/ff-game/src/main/java/com/ff/game/api/km/dto/KMBetRecordResponse.java b/ff-game/src/main/java/com/ff/game/api/km/dto/KMBetRecordResponse.java new file mode 100644 index 0000000..9f36e9b --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/km/dto/KMBetRecordResponse.java @@ -0,0 +1,145 @@ +package com.ff.game.api.km.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.Date; +import java.util.List; + +/** + * kmbet记录响应 + * + * @author shi + * @date 2025/04/02 + */ +@Data +public class KMBetRecordResponse { + /** + * 错误代码 + */ + @JsonProperty("err") + private Integer errorCode; + + /** + * 错误描述 + */ + @JsonProperty("errdesc") + private String errorDescription; + + /** + * bets 包含下列字段的对象数组容器。 + */ + @JsonProperty("bets") + private List bets; + + @Data + public static class Bet { + + /** + * KM内部投注辨识码。此字段是唯一的标识符。 + */ + @JsonProperty("id") + private String id; + + /** + * 记录在KM服务器的投注时间,采用ISO 8601格式。 + */ + @JsonProperty("beton") + private Date beton; // 使用 Date 类型来表示 ISO 8601 格式的时间 + + /** + * 投注关闭的时间,采用ISO 8601格式。如果回合尚未结束,则为null。 + */ + @JsonProperty("closedon") + private Date closedon; // 使用 Date 类型来表示 ISO 8601 格式的时间 + + /** + * 交易回合的唯一辨识代码。 + */ + @JsonProperty("roundid") + private String roundId; + + /** + * 游戏交易执行回合时的游戏供应商辨识码。 + */ + @JsonProperty("externalroundid") + private String externalRoundId; + + /** + * 游戏供应商代码,例如 "KMQM"。 + */ + @JsonProperty("gpcode") + private String gpCode; + + /** + * 游戏代码。 + */ + @JsonProperty("gcode") + private String gCode; + + /** + * 游戏平台的类型。 + */ + @JsonProperty("platformtype") + private Integer platformType; + + /** + * 玩家专属辨识代码。 + */ + @JsonProperty("userid") + private String userId; + + /** + * 玩家在KM系统使用的货币。 + */ + @JsonProperty("cur") + private String currency; + + /** + * 投注的总金额,为负数表示从账户扣款。 + */ + @JsonProperty("riskamt") + private BigDecimal riskAmount; + + /** + * 投注赢的金额,若玩家赢得金额则为正数,若没有输赢则为0。 + */ + @JsonProperty("winamt") + private BigDecimal winAmount; + + /** + * 回合中所有投注的有效投注总金额。 + */ + @JsonProperty("validbet") + private BigDecimal validBet; + + /** + * 下注玩家的佣金金额。 + */ + @JsonProperty("commission") + private BigDecimal commission; + + /** + * 累积奖金对象,包含贡献金额和中奖金额。若没有配置累积奖金,则为null。 + */ + @JsonProperty("jackpot") + private Jackpot jackpot; + + @Data + public static class Jackpot { + + /** + * 贡献到累积奖金的金额。 + */ + @JsonProperty("contrib") + private BigDecimal contrib; + + /** + * 本注的累积奖金赢取金额。 + */ + @JsonProperty("winamt") + private BigDecimal winAmount; + } + } +} diff --git a/ff-game/src/main/java/com/ff/game/api/km/dto/KMGameResponse.java b/ff-game/src/main/java/com/ff/game/api/km/dto/KMGameResponse.java new file mode 100644 index 0000000..cd160d1 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/km/dto/KMGameResponse.java @@ -0,0 +1,130 @@ +package com.ff.game.api.km.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.math.BigDecimal; +import java.util.List; + +/** + * kmgame响应 + * + * @author shi + * @date 2025/04/02 + */ +@Data +public class KMGameResponse { + /** + * 错误代码 + */ + @JsonProperty("err") + private Integer errorCode; + + /** + * 错误描述 + */ + @JsonProperty("errdesc") + private String errorDescription; + /** + * 游戏图标解析度 + */ + @JsonProperty("iconres") + private List iconResolutions; + + /** + * 游戏数组,包含具体的游戏信息 + */ + @JsonProperty("games") + private List games; + + @Data + public static class Game { + + /** + * 游戏供应商游戏专属辨识代码 + */ + @JsonProperty("externalid") + private String externalId; + + /** + * 游戏代码 + */ + @JsonProperty("code") + private String code; + + /** + * 游戏名称 + */ + @JsonProperty("name") + private String name; + + /** + * 游戏说明 + */ + @JsonProperty("description") + private String description; + + /** + * 游戏供应商代码 + */ + @JsonProperty("providercode") + private String providerCode; + + /** + * 游戏的状态 + */ + @JsonProperty("isactive") + private Boolean isActive; + + /** + * 游戏种类代码 + */ + @JsonProperty("type") + private Integer type; + + /** + * 游戏图标 URL(此字段不可用) + */ + @JsonProperty("iconurl") + private String iconUrl; + + /** + * 系统游戏id + */ + private Long systemGameId; + + /** + * 游戏是否提供试玩 + */ + @JsonProperty("supportdemourl") + private Boolean supportDemoUrl; + + /** + * 投注限额数组(如果有的话) + */ + @JsonProperty("betlimits") + private List betLimits; + + @Data + public static class BetLimit { + + /** + * 游戏供应商的投注限制辨识码 + */ + @JsonProperty("id") + private String id; + + /** + * 最小投注金额 + */ + @JsonProperty("min") + private BigDecimal min; + + /** + * 最大投注金额 + */ + @JsonProperty("max") + private BigDecimal max; + } + } +} diff --git a/ff-game/src/main/java/com/ff/game/api/km/dto/KMKickMemberResponse.java b/ff-game/src/main/java/com/ff/game/api/km/dto/KMKickMemberResponse.java new file mode 100644 index 0000000..cf782dd --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/km/dto/KMKickMemberResponse.java @@ -0,0 +1,37 @@ +package com.ff.game.api.km.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +/** + * kmkick成员响应 + * + * @author shi + * @date 2025/04/03 + */ +@Data +public class KMKickMemberResponse { + /** + * 错误代码 + */ + @JsonProperty("err") + private Integer errorCode; + + /** + * 错误描述 + */ + @JsonProperty("errdesc") + private String errorDescription; + + /** + * 成功标志,true 表示操作成功,false 表示操作失败 + */ + @JsonProperty("success") + private boolean success; + + /** + * 描述信息 + */ + @JsonProperty("desc") + private String description; +} diff --git a/ff-game/src/main/java/com/ff/game/api/km/dto/KMTransactionResponse.java b/ff-game/src/main/java/com/ff/game/api/km/dto/KMTransactionResponse.java new file mode 100644 index 0000000..66112ef --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/km/dto/KMTransactionResponse.java @@ -0,0 +1,59 @@ +package com.ff.game.api.km.dto; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.Data; + +import java.math.BigDecimal; + +/** + * km事务响应 + * + * @author shi + * @date 2025/04/02 + */ +@Data +public class KMTransactionResponse { + + /** + * 错误代码 + */ + @JsonProperty("err") + private Integer errorCode; + + /** + * 错误描述 + */ + @JsonProperty("errdesc") + private String errorDescription; + + /** + * 余额 + */ + @JsonProperty("bal") + private BigDecimal balance; + + /** + * 货币类型 + */ + @JsonProperty("cur") + private String currency; + + /** + * 交易 ID + */ + @JsonProperty("txid") + private String txId; + + /** + * 平台交易 ID + */ + @JsonProperty("ptxid") + private String ptxId; + + /** + * 是否为重复交易 + */ + @JsonProperty("dup") + private Boolean isDuplicate; + +} diff --git a/ff-game/src/main/java/com/ff/game/api/km/impl/GamesKMServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/km/impl/GamesKMServiceImpl.java new file mode 100644 index 0000000..225e41b --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/km/impl/GamesKMServiceImpl.java @@ -0,0 +1,733 @@ +package com.ff.game.api.km.impl; + +import cn.hutool.core.util.IdUtil; +import cn.hutool.core.util.NumberUtil; +import com.alibaba.druid.support.json.JSONUtils; +import com.ff.base.constant.CacheConstants; +import com.ff.base.constant.ConfigConstants; +import com.ff.base.constant.Constants; +import com.ff.base.core.redis.RedisCache; +import com.ff.base.enums.*; +import com.ff.base.exception.base.ApiException; +import com.ff.base.exception.base.BaseException; +import com.ff.base.system.service.ISysConfigService; +import com.ff.base.utils.DateUtils; +import com.ff.base.utils.JsonUtil; +import com.ff.base.utils.StringUtils; +import com.ff.base.utils.ip.IpUtils; +import com.ff.base.utils.sign.Md5Utils; +import com.ff.base.utils.uuid.IdUtils; +import com.ff.config.KeyConfig; +import com.ff.game.api.IGamesService; +import com.ff.game.api.dg.dto.DGBetRecordResponseDTO; +import com.ff.game.api.dg.dto.DGResponse; +import com.ff.game.api.dg.dto.DGUserListResponseDTO; +import com.ff.game.api.jili.dto.JILIGamesDataDTO; +import com.ff.game.api.km.client.KMClient; +import com.ff.game.api.km.dto.*; +import com.ff.game.api.request.*; +import com.ff.game.api.xk.dto.XKGamesDTO; +import com.ff.game.domain.*; +import com.ff.game.dto.GameSecretKeyCurrencyDTO; +import com.ff.game.service.*; +import com.ff.member.domain.Member; +import com.ff.member.service.IMemberService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.util.Assert; +import org.springframework.util.CollectionUtils; +import org.springframework.util.ObjectUtils; + +import javax.annotation.Resource; +import java.math.BigDecimal; +import java.net.URLEncoder; +import java.util.*; +import java.util.concurrent.TimeUnit; +import java.util.stream.Collectors; + + +/** + * DG 游戏 impl + * + * @author shi + * @date 2024/11/12 + */ +@Service("KMService") +@Slf4j +public class GamesKMServiceImpl implements IGamesService { + + + @Resource + private ISysConfigService configService; + + @Resource + private RedisCache redisCache; + + @Resource + private IGameExchangeMoneyService gameExchangeMoneyService; + + + @Resource + private IGamePlatformService gamePlatformService; + + + @Resource + private IGameService gameService; + + + @Resource + private IMemberService memberService; + + @Resource + private IGameFreeRecordService gameFreeRecordService; + @Resource + private IGameSecretKeyService gameSecretKeyService; + + @Resource + private IGameSecretKeyCurrencyService gameSecretKeyCurrencyService; + + @Resource + private KMClient KMClient; + + + @Resource + private KeyConfig keyConfig; + + @Resource + private IGameBettingDetailsService gameBettingDetailsService; + + @Resource + private IGameNameService gameNameService; + + + /** + * 游戏id + */ + private static final Long GAME_ID = 1904452832756003817L; + + /** + * 平台ID + */ + private static final Long PLATFORM_ID = 1904411420157108325L; + + /** + * 游戏名称id + */ + private static final Long GAME_NAME_ID = 1904452832756002317L; + + /** + * 获得就是成功 + * + * @param errorCode 错误代码 + * @return {@link Boolean } + */ + private Boolean getIsSuccess(Integer errorCode) { + return 200 == errorCode; + } + + + /** + * 获取密钥 + * + * @param gamesBaseRequestDTO 游戏请求dto + * @return {@link String } + */ + private Map getKey(GamesBaseRequestDTO gamesBaseRequestDTO) { + + //取出对应的key跟密钥跟请求参数 + String agentKey = gamesBaseRequestDTO.getAgentKey(); + String agentId = gamesBaseRequestDTO.getAgentId(); + + Map keyMap = new HashMap<>(); + keyMap.put("X-QM-ClientId", agentId); + keyMap.put("X-QM-ClientSecret", agentKey); + return keyMap; + } + + /** + * 创建成员 + * + * @param createMemberRequestDTO 创建成员请求dto + * @return {@link Boolean } + */ + @Override + public Boolean createMember(CreateMemberRequestDTO createMemberRequestDTO) { + log.info("GamesKMServiceImpl [createMember] 请求参数 {}", createMemberRequestDTO); + + Map params = new LinkedHashMap<>(); + params.put("ipaddress", IpUtils.getHostIp()); + params.put("username", createMemberRequestDTO.getAccount()); + params.put("userid", createMemberRequestDTO.getAccount()); + params.put("lang", "en-US"); + params.put("cur", createMemberRequestDTO.getCurrency()); + /* 1 青铜-基本额度 + 2 白银-升级额度 + 3 黄金-高级额度 + 4 白金-贵宾额度*/ + params.put("betlimitid", 4); + //TODO 后面去掉 + params.put("istestplayer", Boolean.TRUE); + params.put("platformtype", ObjectUtils.isEmpty(createMemberRequestDTO.getPlatformType()) ? 1 : createMemberRequestDTO.getPlatformType()); + + Map headerMap = this.getKey(createMemberRequestDTO); + KMAuthTokenResponse member = KMClient.createMember(params, headerMap); + if (ObjectUtils.isEmpty(member.getErrorCode()) || this.getIsSuccess(member.getErrorCode())) { + redisCache.setCacheObject(CacheConstants.KM_USER_TOKEN + createMemberRequestDTO.getAccount(), member.getAuthToken()); + return Boolean.TRUE; + } + //判断是否获取成功 + return Boolean.FALSE; + } + + + /** + * 获取会员信息 + * + * @param memberInfoRequestDTO 会员信息请求dto + * @return {@link MemberInfoResponseDTO } + */ + @Override + public MemberInfoResponseDTO getMemberInfo(MemberInfoRequestDTO memberInfoRequestDTO) { + log.info("GamesDGServiceImpl [getMemberInfo] 请求参数 {}", memberInfoRequestDTO); + Map paramsMap = new HashMap<>(); + Member member = memberService.selectMemberByGameAccount(memberInfoRequestDTO.getAccounts()); + GameSecretKeyCurrency currencyDTO = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder() + .platformCode(GamePlatforms.KM.getInfo()) + .currency(member.getCurrencyCode()) + .build()); + + paramsMap.put("userid", memberInfoRequestDTO.getAccounts()); + paramsMap.put("cur", currencyDTO.getCurrency()); + Map headerMap = this.getKey(memberInfoRequestDTO); + KMBalanceResponse memberInfo = KMClient.getMemberInfo(JsonUtil.mapToQueryString(paramsMap), headerMap); + if (ObjectUtils.isEmpty(memberInfo.getErrorCode()) || this.getIsSuccess(memberInfo.getErrorCode())) { + return MemberInfoResponseDTO.builder().account(memberInfoRequestDTO.getAccounts()).balance(memberInfo.getBalance()).status(GameMemberStatus.UNKNOWN.getCode()).build(); + } else { + throw new ApiException(ErrorCode.ACCOUNT_NOT_EXIST.getCode()); + } + } + + /** + * 无重定向登录 + * + * @param gamesLogin 游戏登录 + * @return {@link String } + */ + @Override + public String loginWithoutRedirect(GamesLogin gamesLogin) { + log.info("GamesKMServiceImpl [loginWithoutRedirect] 请求参数 {}", gamesLogin); + String kmUserToken = redisCache.getCacheObject(CacheConstants.KM_USER_TOKEN + gamesLogin.getAccount()); + if (StringUtils.isEmpty(kmUserToken)) { + Member member = memberService.selectMemberByGameAccount(gamesLogin.getAccount()); + GameSecretKeyCurrency currencyDTO = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder() + .platformCode(GamePlatforms.KM.getInfo()) + .currency(member.getCurrencyCode()) + .build()); + + CreateMemberRequestDTO gamesBaseRequestDTO = CreateMemberRequestDTO.builder() + .account(gamesLogin.getAccount()) + .agentId(gamesLogin.getAgentId()) + .agentKey(gamesLogin.getAgentKey()) + .currency(currencyDTO.getCurrency()) + .build(); + this.createMember(gamesBaseRequestDTO); + kmUserToken = redisCache.getCacheObject(CacheConstants.KM_USER_TOKEN + gamesLogin.getAccount()); + } + + + String selectConfigByKey = configService.selectConfigByKey(Constants.KM_API_BASE_LOGIN_URL); + + return selectConfigByKey + "/gamelauncher?gpcode=" + gamesLogin.getGameType() + + "&gcode=" + gamesLogin.getGameId() + + "&token=" + kmUserToken + + "&lang=" + gamesLogin.getLang(); + } + + + /** + * 获取游戏列表 + * + * @param gamesBaseRequestDTO 游戏请求dto + * @return {@link String } + */ + @Transactional + @Override + public String getGameList(GamesBaseRequestDTO gamesBaseRequestDTO) { + + List gamesDatas = redisCache.getCacheList(CacheConstants.KM_GAMES); + if (!CollectionUtils.isEmpty(gamesDatas)) { + return CacheConstants.KM_GAMES; + } + + + Map params = new LinkedHashMap<>(); + params.put("lang", "zh-CN"); + params.put("platformtype", 1); + Map headerMap = this.getKey(gamesBaseRequestDTO); + KMGameResponse gameMobiles = KMClient.getGameList(JsonUtil.mapToQueryString(params), headerMap); + //判断是否获取成功 + List gameListData = new ArrayList<>(); + if (ObjectUtils.isEmpty(gameMobiles.getErrorCode()) || this.getIsSuccess(gameMobiles.getErrorCode())) { + gameListData.addAll(this.gameList(gameMobiles, IngressType.MOBILE_WEB.getValue())); + + } else { + throw new BaseException(gameMobiles.getErrorDescription()); + + } + params = new LinkedHashMap<>(); + params.put("lang", "zh-CN"); + params.put("platformtype", 0); + KMGameResponse gamePCs = KMClient.getGameList(JsonUtil.mapToQueryString(params), headerMap); + //判断是否获取成功 + if (ObjectUtils.isEmpty(gamePCs.getErrorCode()) || this.getIsSuccess(gamePCs.getErrorCode())) { + gameListData.addAll(this.gameList(gamePCs, IngressType.PC_WEB.getValue())); + + } else { + throw new BaseException(gamePCs.getErrorDescription()); + } + // 使用集合操作来找出相同 code 的对象 + List games = gameMobiles.getGames().stream() + .filter(mobile -> gamePCs.getGames().stream() + .anyMatch(pc -> pc.getCode().equals(mobile.getCode()))) + .collect(Collectors.toList()); + for (KMGameResponse.Game game : games) { + GamePlatform gamePlatform = GamePlatform.builder() + .platformType(KMGameType.findSystemByCode(game.getProviderCode())) + .platformCode(GamePlatforms.KM.getCode()) + .build(); + List gamePlatforms = gamePlatformService.selectGamePlatformList(gamePlatform); + //没有此平台就新增一个平台 + if (CollectionUtils.isEmpty(gamePlatforms)) { + gamePlatform.setPlatformName(GamePlatforms.KM.getInfo() + KMGameType.findInfoByCode(game.getProviderCode())); + gamePlatform.setSortNo(gamePlatformService.selectMaxSortNo() + 1); + gamePlatform.setCreateBy(Constants.SYSTEM); + gamePlatformService.insertGamePlatform(gamePlatform); + } else { + gamePlatform = gamePlatforms.get(0); + } + Game gameOne = Game.builder() + .platformId(gamePlatform.getId()) + .gameCode(game.getCode()) + .build(); + List gameOnes = gameService.selectGameList(gameOne); + for (Game one : gameOnes) { + one.setIngress(IngressType.PC_AND_MOBILE_WEB.getValue()); + gameService.updateGame(one); + } + } + + redisCache.deleteObject(CacheConstants.KM_GAMES); + redisCache.setCacheList(CacheConstants.KM_GAMES, gameListData); + redisCache.expire(CacheConstants.KM_GAMES, 5L, TimeUnit.HOURS); + return CacheConstants.KM_GAMES; + } + + /** + * 游戏列表 + * + * @param gameList 游戏列表 + * @param ingress 进入 + * @return {@link List }<{@link KMGameResponse.Game }> + */ + private List gameList(KMGameResponse gameList, Integer ingress) { + for (KMGameResponse.Game gamesDataDTO : gameList.getGames()) { + GamePlatform gamePlatform = GamePlatform.builder() + .platformType(KMGameType.findSystemByCode(gamesDataDTO.getProviderCode())) + .platformCode(GamePlatforms.KM.getCode()) + .build(); + List gamePlatforms = gamePlatformService.selectGamePlatformList(gamePlatform); + //没有此平台就新增一个平台 + if (CollectionUtils.isEmpty(gamePlatforms)) { + gamePlatform.setPlatformName(GamePlatforms.KM.getInfo() + KMGameType.findInfoByCode(gamesDataDTO.getProviderCode())); + gamePlatform.setSortNo(gamePlatformService.selectMaxSortNo() + 1); + gamePlatform.setCreateBy(Constants.SYSTEM); + gamePlatformService.insertGamePlatform(gamePlatform); + } else { + gamePlatform = gamePlatforms.get(0); + } + Game game = Game.builder() + .platformId(gamePlatform.getId()) + .gameCode(gamesDataDTO.getCode()) + .build(); + List games = gameService.selectGameList(game); + //不存在这个游戏 + if (CollectionUtils.isEmpty(games)) { + game.setGameSourceType(gamesDataDTO.getProviderCode()); + game.setFreespin(Boolean.FALSE); + game.setDemoStatus(gamesDataDTO.getSupportDemoUrl()); + game.setSortNo(gameService.selectMaxSortNoByPlatformId(gamePlatform.getId()) + 1); + game.setGameName(gamesDataDTO.getName()); + game.setCreateBy(Constants.SYSTEM); + game.setIngress(ingress); + gameService.insertGame(game); + } else { + game = games.get(0); + } + gamesDataDTO.setSystemGameId(game.getId()); + List gameNames = gameNameService.selectGameNameList(GameName.builder().gameId(game.getId()).gameName(game.getGameName()).build()); + if (CollectionUtils.isEmpty(gameNames)) { + gameNameService.insertGameName(GameName.builder() + .gameId(game.getId()) + .gameName(game.getGameName()) + .langCode("zh-CN") + .createBy(Constants.SYSTEM) + .build()); + } + + } + return gameList.getGames(); + } + + /** + * 按代理id进行交换转账 + * + * @param exchangeTransferMoneyRequestDTO 外汇转账moeny dto + * @return {@link Long } + */ + @Override + @Transactional + public Long exchangeTransferByAgentId(ExchangeTransferMoneyRequestDTO exchangeTransferMoneyRequestDTO) { + log.info("GamesKMServiceImpl [exchangeTransferByAgentId] 请求参数 {}", exchangeTransferMoneyRequestDTO); + GameSecretKeyCurrency currencyDTO = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder() + .platformCode(GamePlatforms.KM.getInfo()) + .currency(exchangeTransferMoneyRequestDTO.getCurrency()) + .build()); + + Member member = memberService.selectMemberByGameAccount(exchangeTransferMoneyRequestDTO.getAccount()); + String transactionId = GamePlatforms.KM.getInfo() + IdUtils.simpleUUID(); + List gameExchangeMonies = gameExchangeMoneyService.selectGameExchangeMoneyList( + GameExchangeMoney.builder() + .tenantKey(exchangeTransferMoneyRequestDTO.getTenantKey()) + .orderId(exchangeTransferMoneyRequestDTO.getOrderId()) + .build() + ); + Assert.isTrue(CollectionUtils.isEmpty(gameExchangeMonies), "订单号重复"); + + //获取下一个自增id + GameExchangeMoney exchangeMoney = GameExchangeMoney + .builder() + .tenantKey(exchangeTransferMoneyRequestDTO.getTenantKey()) + .orderId(exchangeTransferMoneyRequestDTO.getOrderId()) + .quota(exchangeTransferMoneyRequestDTO.getQuota()) + .balance(exchangeTransferMoneyRequestDTO.getAmount()) + .exchangeType(exchangeTransferMoneyRequestDTO.getTransferType()) + .currencyCode(currencyDTO.getSystemCurrency()) + .memberId(member.getId()) + .transactionId(transactionId) + .platformCode(GamePlatforms.KM.getInfo()) + .build(); + exchangeMoney.setCreateBy(Constants.SYSTEM); + + BigDecimal amount = exchangeTransferMoneyRequestDTO.getAmount(); + if (TransferType.ALL.getCode().equals(exchangeTransferMoneyRequestDTO.getTransferType())) { + // 获取第三方钱包余额 + MemberInfoRequestDTO gamesBaseRequestDTO = MemberInfoRequestDTO.builder() + .accounts(member.getGameAccount()) + .agentId(exchangeTransferMoneyRequestDTO.getAgentId()) + .currency(exchangeTransferMoneyRequestDTO.getCurrency()) + .agentKey(exchangeTransferMoneyRequestDTO.getAgentKey()) + .build(); + + amount = this.getMemberInfo(gamesBaseRequestDTO).getBalance(); + } + + Map params = new LinkedHashMap<>(); + params.put("userid", exchangeTransferMoneyRequestDTO.getAccount()); + params.put("amt", amount); + params.put("cur", currencyDTO.getCurrency()); + params.put("txid", transactionId); + + Map headerMap = this.getKey(exchangeTransferMoneyRequestDTO); + KMTransactionResponse kmTransactionResponse; + if (TransferType.ALL.getCode().equals(exchangeTransferMoneyRequestDTO.getTransferType())) { + kmTransactionResponse = KMClient.debit(params, headerMap); + } else { + kmTransactionResponse = KMClient.credit(params, headerMap); + } + + //判断是否转移成功 + if (ObjectUtils.isEmpty(kmTransactionResponse.getErrorCode()) || this.getIsSuccess(kmTransactionResponse.getErrorCode())) { + + //更新数据 + exchangeMoney.setBalance(amount); + exchangeMoney.setCoinBefore(NumberUtil.sub(amount, kmTransactionResponse.getBalance()).abs()); + exchangeMoney.setCoinAfter(kmTransactionResponse.getBalance()); + exchangeMoney.setCurrencyBefore(exchangeMoney.getCoinBefore()); + exchangeMoney.setCurrencyAfter(exchangeMoney.getCoinAfter()); + exchangeMoney.setStatus(StatusType.SUCCESS.getValue()); + gameExchangeMoneyService.insertGameExchangeMoney(exchangeMoney); + } else { + log.error("GamesDGServiceImpl [exchangeTransferByAgentId] 金额转移失败,错误代码{},错误信息{}", kmTransactionResponse.getErrorCode(), kmTransactionResponse.getErrorDescription()); + throw new ApiException(ErrorCode.BALANCE_TRANSFER_FAILED.getCode()); + } + + return exchangeMoney.getId(); + } + + /** + * 汇兑转移状态 + * + * @param exchangeTransferMoneyRequestDTO 兑换转账请求dto + * @return {@link Boolean } + */ + @Override + public Boolean exchangeTransferStatus(ExchangeTransferStatusRequestDTO exchangeTransferMoneyRequestDTO) { + return Boolean.TRUE; + } + + + /** + * 按时间获取投注记录 + * + * @param betRecordByTimeDTO 按时间dto投注记录 + * @return {@link List }<{@link GameBettingDetails }> + */ + @Override + public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) { + //请求参数 + + log.info("GamesKMServiceImpl [getBetRecordByTime] 请求参数 {}", betRecordByTimeDTO); + Map key = this.getKey(betRecordByTimeDTO); + String startTime = null; + String endTime = null; + try { + + + startTime = URLEncoder.encode(DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getStartTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00", "UTF-8"); + endTime = URLEncoder.encode(DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getEndTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00", "UTF-8"); + } catch (Exception e) { + throw new BaseException(e.getMessage()); + } + Map params = new LinkedHashMap<>(); + params.put("startdate", startTime); + params.put("enddate", endTime); + params.put("includetestplayers", Boolean.TRUE); + params.put("issettled", Boolean.TRUE); + + KMBetRecordResponse betRecordByTime = KMClient.getBetRecordByTime(JsonUtil.mapToQueryString(params), key); + + if (ObjectUtils.isEmpty(betRecordByTime.getErrorCode()) || this.getIsSuccess(betRecordByTime.getErrorCode())) { + this.batchInsert(betRecordByTime); + + + return Boolean.TRUE; + } else { + log.error("GamesKMServiceImpl [getBetRecordByTime] 获取投注记录失败,错误代码{},错误信息{}", betRecordByTime.getErrorCode(), betRecordByTime.getErrorDescription()); + throw new BaseException(betRecordByTime.getErrorDescription()); + } + + + } + + /** + * 按历史时间获取投注记录 + * + * @param betRecordByTimeDTO 按时间dto投注记录 + * @return {@link Boolean } + */ + @Override + public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) { + return null; + } + + /** + * 赠送免费局数 + * + * @param createFreeSpinRequest 创建自由旋转请求 + * @return {@link Boolean } + */ + @Override + public Boolean createFreeSpin(CreateFreeSpinRequestDTO createFreeSpinRequest) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + + /** + * 获取游戏详细信息 + * + * @param getGameDetailRequestDTO 获取游戏详细信息请求dto + * @return {@link GetGameDetailResponseDTO } + */ + @Override + public GetGameDetailResponseDTO getGameDetail(GetGameDetailRequestDTO getGameDetailRequestDTO) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + + /** + * 强制会员从游戏注销 + * + * @param kickMemberRequestDTO 踢会员请求dto + * @return {@link Boolean } + */ + @Override + public Boolean kickMember(KickMemberRequestDTO kickMemberRequestDTO) { + log.info("GamesKMServiceImpl [kickMember] 请求参数 {}", kickMemberRequestDTO); + Map key = this.getKey(kickMemberRequestDTO); + Map params = new LinkedHashMap<>(); + params.put("userid", kickMemberRequestDTO.getAccount()); + KMKickMemberResponse kickMember = KMClient.kickMember(params, key); + if (ObjectUtils.isEmpty(kickMember.getErrorCode()) || this.getIsSuccess(kickMember.getErrorCode())) { + redisCache.deleteObject(CacheConstants.KM_USER_TOKEN + kickMemberRequestDTO.getAccount()); + return kickMember.isSuccess(); + }else { + throw new BaseException(kickMember.getDescription()); + } + } + + /** + * 踢成员全部 + * + * @param kickMemberAllDTO 踢成员全部dto + * @return {@link Boolean } + */ + @Override + public Boolean kickMemberAll(KickMemberAllDTO kickMemberAllDTO) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + + /** + * 免费游戏玩家使用的纪录 + * + * @param getFreeSpinDashflowRequestDTO 获取自由旋转dashflow请求dto + * @return {@link List }<{@link GameFreeRecord }> + */ + @Override + public List getFreeSpinDashflow(GetFreeSpinDashflowRequestDTO getFreeSpinDashflowRequestDTO) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + + /** + * 取消赠送免费局数 + * + * @param cancelFreeSpinRequestDTO 取消免费旋转请求 + * @return {@link Boolean } + */ + @Override + public Boolean cancelFreeSpin(CancelFreeSpinRequestDTO cancelFreeSpinRequestDTO) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + + /** + * 游戏演示登录 + * + * @param gameDemoLoginRequestDTO 游戏演示登录请求dto + * @return {@link GameDemoLoginResponseDTO } + */ + @Override + public GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO) { + log.info("GamesKMServiceImpl [gameDemoLogin] 请求参数 {}", gameDemoLoginRequestDTO); + Map params = new LinkedHashMap<>(); + params.put("lang", gameDemoLoginRequestDTO.getLang()); + String selectConfigByKey = configService.selectConfigByKey(Constants.KM_API_BASE_LOGIN_URL); + return GameDemoLoginResponseDTO.builder() + .url(selectConfigByKey + "/demolauncher?gpcode=" + gameDemoLoginRequestDTO.getGameType() + + "&gcode=" + gameDemoLoginRequestDTO.getGameId() + + "&lang=" + gameDemoLoginRequestDTO.getLang()) + .build(); + } + + + /** + * 批量插入 + * + * @param betRecordByTime dg投注记录响应dto + */ + private void batchInsert(KMBetRecordResponse betRecordByTime) { + List gameBettingDetails = new ArrayList<>(); + List wagersIds = new ArrayList<>(); + //数据组装 + List report = betRecordByTime.getBets(); + //数据转化 + for (KMBetRecordResponse.Bet bean : report) { + GameBettingDetails bettingDetails = this.dataBuild(GamesDataBuildDTO.builder().data(bean).build()); + if (!ObjectUtils.isEmpty(bettingDetails)) { + bettingDetails.setId(IdUtil.getSnowflakeNextId()); + gameBettingDetails.add(bettingDetails); + } + wagersIds.add(String.valueOf(bean.getId())); + } + if (!CollectionUtils.isEmpty(gameBettingDetails)) { + //查询重复数据id + List removeWagersIds = gameBettingDetailsService.selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.KM.getInfo()); + //用steam流清除list中与wagersIds集合相同的数据 + gameBettingDetails = gameBettingDetails.stream() + .filter(detail -> !removeWagersIds.contains(detail.getWagersId())) + .collect(Collectors.toList()); + if (!CollectionUtils.isEmpty(gameBettingDetails)) { + gameBettingDetailsService.batchInsert(gameBettingDetails); + } + } + + } + + /** + * 数据构建 + * + * @param gamesDataBuildDTO 数据 + * @return {@link GameBettingDetails } + */ + @Override + public GameBettingDetails dataBuild(GamesDataBuildDTO gamesDataBuildDTO) { + //转化类 + KMBetRecordResponse.Bet resultBean = (KMBetRecordResponse.Bet) gamesDataBuildDTO.getData(); + + + GameSecretKeyCurrency currencyDTO = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder() + .platformCode(GamePlatforms.KM.getInfo()) + .currency(resultBean.getCurrency()) + .build()); + + + Member member = memberService.selectMemberByGameAccount(resultBean.getUserId()); + if (ObjectUtils.isEmpty(member)) { + return null; + } + List gamesDatas = redisCache.getCacheList(CacheConstants.KM_GAMES); + Map dataDTOMap = gamesDatas.stream() .collect(Collectors.toMap( + KMGameResponse.Game::getCode, + e -> e, + (existing, replacement) -> existing + ));; + KMGameResponse.Game gamesDataDTO = dataDTOMap.get(resultBean.getGCode()); + + //输赢状态 + Integer gameStatus = GameStatus.FLAT.getCode(); + BigDecimal payoffAmount =NumberUtil.sub(resultBean.getWinAmount(), resultBean.getRiskAmount().abs()); + if (payoffAmount.compareTo(BigDecimal.ZERO) > 0) { + gameStatus = GameStatus.WIN.getCode(); + } else if (payoffAmount.compareTo(BigDecimal.ZERO) < 0) { + gameStatus = GameStatus.FAIL.getCode(); + } + + + //数据构造 + GameBettingDetails gameBettingDetails = GameBettingDetails.builder() + .tenantKey(member.getTenantKey()) + //保存我们的币种id + .currencyCode(currencyDTO.getSystemCurrency()) + .memberId(member.getId()) + .gameCode(gamesDataDTO.getCode()) + .gameType(PlatformType.CARD_GAME.getCode()) + .platformCode(GamePlatforms.KM.getInfo()) + .gameId(GAME_ID) + .gameName(gamesDataDTO.getName()) + .gameStatus(gameStatus) + .gameStatusType(1) + .gameCurrencyCode(currencyDTO.getCurrency()) + .account(resultBean.getUserId()) + .wagersId(resultBean.getId()) + .wagersTime(resultBean.getBeton().getTime()) + .betAmount(resultBean.getRiskAmount().abs()) + .payoffTime(resultBean.getClosedon().getTime()) + .payoffAmount(payoffAmount.abs()) + .settlementTime(resultBean.getClosedon().getTime()) + .turnover(resultBean.getValidBet()) + .settlementStatus(SettlementStatusEnum.COMPLETED.getCode()) + .orderNo(resultBean.getExternalRoundId()) + .round(resultBean.getRoundId()) + .build(); + gameBettingDetails.setCreateBy(Constants.SYSTEM); + gameBettingDetails.setCreateTime(DateUtils.getNowDate()); + return gameBettingDetails; + } +} diff --git a/ff-game/src/main/java/com/ff/game/api/km/success/MyKMSuccessCondition.java b/ff-game/src/main/java/com/ff/game/api/km/success/MyKMSuccessCondition.java new file mode 100644 index 0000000..3a29b10 --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/km/success/MyKMSuccessCondition.java @@ -0,0 +1,31 @@ +package com.ff.game.api.km.success; + + +import com.dtflys.forest.callback.SuccessWhen; +import com.dtflys.forest.http.ForestRequest; +import com.dtflys.forest.http.ForestResponse; + +/** + * 我kmsuccess状态 + * + * @author shi + * @date 2025/04/02 + */ +public class MyKMSuccessCondition implements SuccessWhen { + + /** + * 请求成功条件 + * @param req Forest请求对象 + * @param res Forest响应对象 + * @return 是否成功,true: 请求成功,false: 请求失败 + */ + @Override + public boolean successWhen(ForestRequest req, ForestResponse res) { + // req 为Forest请求对象,即 ForestRequest 类实例 + // res 为Forest响应对象,即 ForestResponse 类实例 + // 返回值为 ture 则表示请求成功,false 表示请求失败 + return res.noException(); + // 当然在这里也可以写其它条件,比如 通过 res.getResult() 或 res.getContent() 获取业务数据 + // 再根据业务数据判断是否成功 + } +} diff --git a/ff-game/src/main/java/com/ff/game/api/meitian/impl/MeiTianGameServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/meitian/impl/MeiTianGameServiceImpl.java index fe60da4..23b9863 100644 --- a/ff-game/src/main/java/com/ff/game/api/meitian/impl/MeiTianGameServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/meitian/impl/MeiTianGameServiceImpl.java @@ -683,6 +683,17 @@ public class MeiTianGameServiceImpl implements IGamesService { throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } + /** + * 游戏演示登录 + * + * @param gameDemoLoginRequestDTO 游戏演示登录请求dto + * @return {@link GameDemoLoginResponseDTO } + */ + @Override + public GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + /** * 批量插入 diff --git a/ff-game/src/main/java/com/ff/game/api/ng/service/impl/GamesPGServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/ng/service/impl/GamesPGServiceImpl.java index c314f47..33bdc76 100644 --- a/ff-game/src/main/java/com/ff/game/api/ng/service/impl/GamesPGServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/ng/service/impl/GamesPGServiceImpl.java @@ -645,7 +645,7 @@ public class GamesPGServiceImpl implements IGamesService { */ @Override public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) { - return null; + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } @@ -659,7 +659,7 @@ public class GamesPGServiceImpl implements IGamesService { public Boolean createFreeSpin(CreateFreeSpinRequestDTO createFreeSpinRequest) { - return null; + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } /** @@ -689,7 +689,7 @@ public class GamesPGServiceImpl implements IGamesService { @Override public Boolean kickMember(KickMemberRequestDTO kickMemberRequestDTO) { - return null; + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } /** @@ -701,7 +701,7 @@ public class GamesPGServiceImpl implements IGamesService { @Override public Boolean kickMemberAll(KickMemberAllDTO kickMemberAllDTO) { - return null; + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } /** @@ -714,7 +714,7 @@ public class GamesPGServiceImpl implements IGamesService { public List getFreeSpinDashflow(GetFreeSpinDashflowRequestDTO getFreeSpinDashflowRequestDTO) { - return Collections.emptyList(); + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } /** @@ -726,8 +726,19 @@ public class GamesPGServiceImpl implements IGamesService { @Override public Boolean cancelFreeSpin(CancelFreeSpinRequestDTO cancelFreeSpinRequestDTO) { - return null; + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } + /** + * 游戏演示登录 + * + * @param gameDemoLoginRequestDTO 游戏演示登录请求dto + * @return {@link GameDemoLoginResponseDTO } + */ + @Override + public GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + /** diff --git a/ff-game/src/main/java/com/ff/game/api/pgx/impl/GamesPGXServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/pgx/impl/GamesPGXServiceImpl.java index e15a390..38d9294 100644 --- a/ff-game/src/main/java/com/ff/game/api/pgx/impl/GamesPGXServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/pgx/impl/GamesPGXServiceImpl.java @@ -559,6 +559,17 @@ public class GamesPGXServiceImpl implements IGamesService { throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } + /** + * 游戏演示登录 + * + * @param gameDemoLoginRequestDTO 游戏演示登录请求dto + * @return {@link GameDemoLoginResponseDTO } + */ + @Override + public GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + /** * 批量插入 diff --git a/ff-game/src/main/java/com/ff/game/api/request/CreateMemberRequestDTO.java b/ff-game/src/main/java/com/ff/game/api/request/CreateMemberRequestDTO.java index f651b69..b6d1dac 100644 --- a/ff-game/src/main/java/com/ff/game/api/request/CreateMemberRequestDTO.java +++ b/ff-game/src/main/java/com/ff/game/api/request/CreateMemberRequestDTO.java @@ -29,5 +29,10 @@ public class CreateMemberRequestDTO extends GamesBaseRequestDTO { */ private Map>> betLimit; + /** + * 平台类型 0 桌面 1 移动 + */ + private Integer platformType; + } diff --git a/ff-game/src/main/java/com/ff/game/api/request/GameDemoLoginRequestDTO.java b/ff-game/src/main/java/com/ff/game/api/request/GameDemoLoginRequestDTO.java new file mode 100644 index 0000000..31d095c --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/request/GameDemoLoginRequestDTO.java @@ -0,0 +1,34 @@ +package com.ff.game.api.request; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.experimental.SuperBuilder; + +/** + * 游戏演示登录请求dto + * + * @author shi + * @date 2025/04/03 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@SuperBuilder +public class GameDemoLoginRequestDTO extends GamesBaseRequestDTO{ + /** + * 语言 + */ + private String lang; + + + /** + * 游戏类型 + */ + private String gameType; + + /** + * 游戏id + */ + private String gameId; +} diff --git a/ff-game/src/main/java/com/ff/game/api/request/GameDemoLoginResponseDTO.java b/ff-game/src/main/java/com/ff/game/api/request/GameDemoLoginResponseDTO.java new file mode 100644 index 0000000..6f9a2aa --- /dev/null +++ b/ff-game/src/main/java/com/ff/game/api/request/GameDemoLoginResponseDTO.java @@ -0,0 +1,23 @@ +package com.ff.game.api.request; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +/** + * 游戏演示登录响应dto + * + * @author shi + * @date 2025/04/03 + */ +@Data +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class GameDemoLoginResponseDTO { + /** + * 网址 + */ + private String url; +} diff --git a/ff-game/src/main/java/com/ff/game/api/sa/impl/GamesSAServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/sa/impl/GamesSAServiceImpl.java index d1b265e..2703b42 100644 --- a/ff-game/src/main/java/com/ff/game/api/sa/impl/GamesSAServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/sa/impl/GamesSAServiceImpl.java @@ -524,6 +524,17 @@ public class GamesSAServiceImpl implements IGamesService { throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } + /** + * 游戏演示登录 + * + * @param gameDemoLoginRequestDTO 游戏演示登录请求dto + * @return {@link GameDemoLoginResponseDTO } + */ + @Override + public GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + /** * 批量插入 diff --git a/ff-game/src/main/java/com/ff/game/api/xk/service/impl/GamesXKServiceImpl.java b/ff-game/src/main/java/com/ff/game/api/xk/service/impl/GamesXKServiceImpl.java index 6e8bc49..76f0223 100644 --- a/ff-game/src/main/java/com/ff/game/api/xk/service/impl/GamesXKServiceImpl.java +++ b/ff-game/src/main/java/com/ff/game/api/xk/service/impl/GamesXKServiceImpl.java @@ -535,6 +535,17 @@ public class GamesXKServiceImpl implements IGamesService { public Boolean cancelFreeSpin(CancelFreeSpinRequestDTO cancelFreeSpinRequestDTO) { throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); } + /** + * 游戏演示登录 + * + * @param gameDemoLoginRequestDTO 游戏演示登录请求dto + * @return {@link GameDemoLoginResponseDTO } + */ + @Override + public GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO) { + throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode()); + } + /** diff --git a/ff-game/src/main/java/com/ff/member/service/impl/MemberServiceImpl.java b/ff-game/src/main/java/com/ff/member/service/impl/MemberServiceImpl.java index 428270b..971752c 100644 --- a/ff-game/src/main/java/com/ff/member/service/impl/MemberServiceImpl.java +++ b/ff-game/src/main/java/com/ff/member/service/impl/MemberServiceImpl.java @@ -55,15 +55,15 @@ public class MemberServiceImpl implements IMemberService { @Override public synchronized String getMemberGameAccount(String platformCode) { String gameAccount = null; - if (GamePlatforms.DG.getInfo().equals(platformCode)) { + if (GamePlatforms.DG.getInfo().equals(platformCode) || GamePlatforms.KM.getInfo().equals(platformCode)) { do { gameAccount = RandomGeneratorUtils.generateRandomAccountUpper(); } while (!ObjectUtils.isEmpty(memberMapper.selectMemberByGameAccount(gameAccount))); - }else if (GamePlatforms.PG.getInfo().equals(platformCode)||GamePlatforms.PGX.getInfo().equals(platformCode)){ + } else if (GamePlatforms.PG.getInfo().equals(platformCode) || GamePlatforms.PGX.getInfo().equals(platformCode)) { do { gameAccount = RandomGeneratorUtils.generateRandomAccountLower(); } while (!ObjectUtils.isEmpty(memberMapper.selectMemberByGameAccount(gameAccount))); - }else { + } else { do { gameAccount = RandomGeneratorUtils.generateRandomAccount(); } while (!ObjectUtils.isEmpty(memberMapper.selectMemberByGameAccount(gameAccount)));