Compare commits

...

94 Commits

Author SHA1 Message Date
shi fc7ff3a3e0 refactor(game): 将 ApiCFBalanceTransferResponseDTO 中的 bankID可以避免在处理大额交易时可能出现的溢出问题 2025-04-24 15:31:23 +08:00
shi 7f04d31fa1 refactor(game): 将 ApiCFBalanceTransferResponseDTO 中的 bankID 2025-04-24 15:30:56 +08:00
shi 0a02e3b9f6 feat(game): 优化平台列表查询逻辑
- 在查询平台列表时添加了停用状态过滤条件- 仅缓存未停用的平台信息,提高数据准确性
2025-04-24 13:41:38 +08:00
shi da64d2304a feat(game): 优化平台列表查询逻辑
- 在查询平台列表时添加了停用状态过滤条件- 仅缓存未停用的平台信息,提高数据准确性
2025-04-23 14:41:15 +08:00
shi 58064fc479 feat(game): 优化平台列表查询逻辑
- 在查询平台列表时添加了停用状态过滤条件- 仅缓存未停用的平台信息,提高数据准确性
2025-04-23 14:29:31 +08:00
shi 75ede9c934 feat(game): 添加 PP 游戏平台支持
- 新增 PP游戏平台的 API 接口实现类
- 添加 PP 游戏平台的缓存常量和时间点
- 实现 PP游戏平台的会员创建、登录、转账等功能
- 解析 PP 游戏平台的注单记录
- 添加 PP 游戏平台的地址源和成功条件判断
2025-04-21 14:03:07 +08:00
shi 5a62a0c6a5 feat(game): 添加平台管理全部获取接口并优化登录逻辑
- 在 GamePlatformController 中添加了获取所有平台信息的接口
- 优化了 SysLoginController 中的验证码校验逻辑
- 新增了平台服务的依赖注入
- 添加了权限控制注解
2025-04-12 10:13:27 +08:00
shi 16f83851e5 feat(fc): 优化 FC 地址来源
- 在 UrlInfo 类中添加 https 和 host 字段,用于存储 HTTPS 协议和端口信息
- 修改 MyFCAddressSource 类中的 getAddress 方法,使用新的 https 和 host 字段
-通过这些改动,提高了地址信息的灵活性和可配置性
2025-04-12 09:38:51 +08:00
shi e12fd287be refactor(game): 修改游戏 ID 类型并更新相关接口
-将 ApiGameInfoResponseDTO 中的 systemGameId 字段类型从 Long 改为 String
- 在 GameMapper.xml 中添加 game_id 字段的查询
- 更新 GamesPGServiceImpl 中的代码,使用 game.getGameId() 替代 game.getId()
2025-04-11 19:43:01 +08:00
shi f097c0f8aa refactor(game): 修改游戏 ID 类型并更新相关接口
-将 ApiGameInfoResponseDTO 中的 systemGameId 字段类型从 Long 改为 String
- 在 GameMapper.xml 中添加 game_id 字段的查询
- 更新 GamesPGServiceImpl 中的代码,使用 game.getGameId() 替代 game.getId()
2025-04-11 19:15:47 +08:00
shi 82cd1456a5 refactor(game): 修改游戏 ID 类型并更新相关接口
-将 ApiGameInfoResponseDTO 中的 systemGameId 字段类型从 Long 改为 String
- 在 GameMapper.xml 中添加 game_id 字段的查询
- 更新 GamesPGServiceImpl 中的代码,使用 game.getGameId() 替代 game.getId()
2025-04-11 19:08:48 +08:00
shi 908194d767 refactor(game): 恢复 DBSports 实时记录接口时间参数使用
- 重新启用 getRealtimeRecord 接口中 startTime 和 endTime 参数
- 移除测试用的硬编码时间值
-优化代码格式和缩进
2025-04-11 18:14:42 +08:00
shi dda84a866e refactor(game): 恢复 DBSports 实时记录接口时间参数使用
- 重新启用 getRealtimeRecord 接口中 startTime 和 endTime 参数
- 移除测试用的硬编码时间值
-优化代码格式和缩进
2025-04-11 17:28:38 +08:00
shi d2ced1154d refactor(game): 恢复 DBSports 实时记录接口时间参数使用
- 重新启用 getRealtimeRecord 接口中 startTime 和 endTime 参数
- 移除测试用的硬编码时间值
-优化代码格式和缩进
2025-04-11 16:58:57 +08:00
shi 9352c50a22 Merge branch 'main-meitian' into main-pgt 2025-04-11 16:56:37 +08:00
shi 39dcd026c0 fix(game): 修复游戏余额扣除和订单回滚相关问题
- 在扣除余额时增加检查,避免重复回滚增加余额
-调整订单状态更新的定时任务执行频率,从每30分钟改为每5分钟
-优化平台交易状态查询逻辑,避免不必要的错误提示
2025-04-11 16:13:15 +08:00
shi 724d5f8f06 feat(game): 实现余额转移状态查询功能
- 新增余额转移状态查询接口和相关 DTO 类- 完善定时任务处理逻辑,增加步进状态处理
- 优化游戏兑换货币相关逻辑,增加空列表判断
- 修复美天游戏同步记录方法中的类型转换问题
- 完善平台交易服务中的异常处理
2025-04-11 15:47:42 +08:00
shi 88bdfc9c16 refactor(ff-game): 重构游戏余额转移功能
- 移除冗余的平台和货币信息查询逻辑
- 使用 GameExchangeDTO封装余额转移数据
-引入 StepProcessorFactory 处理余额转移步骤
- 实现异步执行余额转移操作
-优化错误处理和日志记录
2025-04-11 15:20:58 +08:00
shi 3236b4ab55 refactor(game): 重构游戏平台交易ID生成逻辑
- 移除 CreateOrderServiceImpl 中的 getTransactionId 方法
- 在每个游戏平台的实现类中添加 getTransactionId 方法,具体实现如下:
  - GamesAEServiceImpl
  - GamesDGServiceImpl
  - GamesFCServiceImpl
  - GamesJILIServiceImpl
  - GamesKMServiceImpl - GamesPGServiceImpl
  - GamesPGTServiceImpl
  - GamesPGXServiceImpl
  - GamesSAServiceImpl
- 更新 DBSportsServiceImpl 和 FBSportsServiceImpl 中的交易ID生成逻辑
- 重构后的交易ID生成逻辑更清晰,每个平台有自己的实现方式
2025-04-11 15:20:45 +08:00
liaoyong 10c9601bbe refactor(ff-game): 重构注单拉取接口和数据处理- 在 DBSportsClient 中为 getBetList 方法添加 Content-type 头
- 修改 DBSportsServiceImpl 中的 getRealtimeRecord 方法,使用固定的时间戳进行测试
- 更新 GetBetListRequest 中的 timestamp 字段默认值为当前时间戳
- 重构 GetBetListResponse 结构,将列表数据封装到 DataDTO 中
2025-04-11 11:23:33 +08:00
shi 265b626b6e Merge branch 'main-meitian' into main-pgt 2025-04-11 11:03:29 +08:00
shi 537b23a420 refactor(ff-game): 重构 FB 和 SV388 平台的转账逻辑
- 移除 FBSports 和 SV388 平台的重复代码
- 优化交易 ID 生成逻辑,支持不同平台的格式
-调整 CreateOrderServiceImpl 中的平台判断逻辑
- 重构 FBSportsServiceImpl 和 SV388GamesServiceImpl 中的转账方法
2025-04-11 11:03:24 +08:00
liaoyong 0ce67f08ab feat(sports): 重构 DB体育接口
- 修改了 CreateUserRequest 和 CreateUserResponse 的字段名称
- 重写了 DBSportsClient 接口中的方法
- 更新了 DBSportsServiceImpl 中的实现逻辑
- 新增了 GetBetListRequest 和 GetBetListResponse 类
-调整了 GetMemberInfoRequest 的结构
2025-04-11 10:59:27 +08:00
shi 9fa259268e Merge branch 'main-meitian' into main-pgt
# Conflicts:
#	ff-base/src/main/java/com/ff/base/enums/ErrorCode.java
#	ff-game/src/main/java/com/ff/common/service/impl/TenantGameQuotaServiceImpl.java
2025-04-11 10:56:12 +08:00
shi f32794285a refactor(game): 重构游戏余额转移功能
- 新增抽象步进处理类 AbstractStepProcessor 实现通用的处理和回滚逻辑- 新增具体步进处理实现类 CreateOrderServiceImpl、DeductBalanceServiceImpl 和 AddBalanceServiceImpl
-优化 ApiGameController 中的余额转移接口,采用异步处理方式- 重构 GameExchangeMoneyService 中的插入逻辑,支持更新
- 新增 GameExchangeDTO 用于游戏兑换货币的相关操作
- 更新相关枚举类和 DTO 类以支持新的业务逻辑
2025-04-11 10:52:17 +08:00
liaoyong bf955ed14b refactor(game): 重构游戏相关接口和数据结构
- 修改游戏 ID 类型从 Long 到 String
- 更新游戏创建和验证逻辑
- 调整游戏数据传输对象结构
- 优化游戏列表响应格式
2025-04-10 13:50:46 +08:00
liaoyong fb6138f9a0 refactor(game): 重构 AE 和 SV388 的投注记录获取逻辑
- 为 GamesAEServiceImpl 和 SV388GamesServiceImpl 类添加 DelayService 依赖
- 实现 GetRealtimeRecordTask 和 GetHistoryRecordTask 类继承 DelayTask
- 重写 getBetRecordByTime 和 getBetRecordByHistoryTime 方法,使用延迟任务处理大量数据
- 优化 batchInsert 方法,增加空数据判断和日志记录
- 调整 kickMember 方法,移除不必要的日志输出
2025-04-10 10:20:05 +08:00
liaoyong b0c058345d refactor(ff-game): 重构 SV388 投注记录获取逻辑
- 引入 DelayService 和 DelayTask 机制,实现异步处理投注记录请求
-将原有的同步方法拆分为实时记录和历史记录两个异步任务- 优化了错误处理逻辑,移除了不必要的异常抛出
- 调整了参数处理和日志记录,提高了代码可读性和维护性
2025-04-09 20:25:57 +08:00
liaoyong ff36a40799 feat(sports): 新增 DB体育平台支持
- 添加 DB体育相关的缓存常量、数据传输对象、接口客户端等
- 实现 DB 体育平台的服务逻辑,包括用户创建、资金转账、获取会员信息等功能
- 新增延迟任务处理机制,用于处理异步任务
- 修改错误码枚举,增加新的错误类型
- 更新游戏平台枚举,添加 DB 体育选项
2025-04-09 20:05:22 +08:00
liaoyong 6410c6bb56 feat(game): 添加 SV388 游戏平台支持
- 新增 SV388 游戏平台的 API 客户端和服务实现
- 添加 SV388 相关的 DTO 类和枚举定义
- 实现 SV388 的会员管理、资金转移、投注记录查询等功能
2025-04-09 15:14:06 +08:00
liaoyong 9d88f8e08f feat(fb-sports): 实现 FB 体育数据对接
- 新增 FB 体育类型枚举类 FBSportsType
- 实现 FB 体育客户端接口,包括获取 token、订单文件列表和订单数据- 重构 FBSportsServiceImpl 类,支持按时间和历史时间获取投注记录
- 优化数据处理逻辑,实现批量插入功能
-移除不必要的定时任务配置
2025-04-09 11:18:49 +08:00
shi 6e1afc61b0 refactor(game): 重构游戏列表和金额转移相关逻辑
- 更新了 PGX、SA 和 XK游戏平台的游戏列表获取逻辑- 优化了游戏数据插入和更新的处理流程
- 改进了金额转移的处理方式,增加了状态和步骤记录
-调整了投注记录获取的接口调用方式
- 优化了代码结构,移除了不必要的注释和冗余代码
2025-04-09 09:18:47 +08:00
shi c45f69fcb2 refactor(game): 重构游戏列表和金额转移相关逻辑
- 更新了 PGX、SA 和 XK游戏平台的游戏列表获取逻辑- 优化了游戏数据插入和更新的处理流程
- 改进了金额转移的处理方式,增加了状态和步骤记录
-调整了投注记录获取的接口调用方式
- 优化了代码结构,移除了不必要的注释和冗余代码
2025-04-09 09:16:58 +08:00
liaoyong 59dc248c3e Merge remote-tracking branch 'origin/main-pgt' into main-meitian
# Conflicts:
#	ff-base/src/main/java/com/ff/base/enums/GamePlatforms.java
#	ff-game/src/main/java/com/ff/game/api/meitian/impl/MeiTianGameServiceImpl.java
#	ff-game/src/main/resources/mapper/game/GameMapper.xml
2025-04-09 09:16:20 +08:00
shi 58becaf578 refactor(ff-game): 移除未使用的代码和依赖
- 删除了 ApiMemberController 中未使用的 GameSecretKeyCurrencyDTO 相关代码
- 移除了 GamesPGTServiceImpl 中未使用的 IGamePlatformService、IGameSecretKeyService、IGameSecretKeyCurrencyService 和 IGameNameService 的注入
- 清理了 GamesPGTServiceImpl 中未
2025-04-08 17:12:40 +08:00
liaoyong 8ec1afd875 feat(fb-sports): 实现 FB 体育数据对接
- 新增 FB 体育类型枚举类 FBSportsType
- 实现 FB 体育客户端接口,包括获取 token、订单文件列表和订单数据- 重构 FBSportsServiceImpl 类,支持按时间和历史时间获取投注记录
- 优化数据处理逻辑,实现批量插入功能
-移除不必要的定时任务配置
2025-04-08 17:11:31 +08:00
liaoyong b087305ba1 test(fb): 修改游戏 ID 生成逻辑
- 将静态常量 GAME_ID 修改为 11111
- 在创建新游戏时,将游戏 ID 设置为 GAME_ID,而不是使用 Snowflake算法生成的随机 ID
2025-04-08 13:36:57 +08:00
liaoyong 72810d4d0e feat(fb): 新增 FB 体育平台支持
- 添加 FB 体育相关的数据结构和接口定义
- 实现 FB 体育平台的会员创建、资金转账、获取会员信息等功能
- 集成 FB 体育平台的 URL 获取和登录逻辑
- 为 FB 体育平台添加错误码定义
-优化游戏列表获取逻辑,支持 FB 体育游戏数据同步
2025-04-08 13:33:18 +08:00
shi b466e052f1 refactor(ff-game): 移除未使用的代码和依赖
- 删除了 ApiMemberController 中未使用的 GameSecretKeyCurrencyDTO 相关代码
- 移除了 GamesPGTServiceImpl 中未使用的 IGamePlatformService、IGameSecretKeyService、IGameSecretKeyCurrencyService 和 IGameNameService 的注入
- 清理了 GamesPGTServiceImpl 中未使用的 import 语句
2025-04-07 15:47:59 +08:00
shi 0183bad8e7 refactor(game): 移除未使用的常量
- 删除了 GamesAEServiceImpl 类中的 PLATFORM_ID 和 GAME_NAME_ID 两个常量- 这些常量在代码中未被使用,移除它们可以提高代码的可读性和维护性
2025-04-07 15:36:11 +08:00
shi d6141bb716 Merge branch 'main-meitian' into main-pgt 2025-04-07 15:25:55 +08:00
liaoyong f35179c8bc refactor(ff-game): 重构游戏模块代码
- 移除了 GameName、GamePlatform 和 GameSecretKey 相关的实体类、Mapper 和 Service
- 优化了 ApiGameController 中的代码结构
- 更新了 GameBalanceExchange 类的继承关系
- 调整了 GamesAEServiceImpl 和 GamesDGServiceImpl 中的方法实现
2025-04-07 15:22:12 +08:00
liaoyong 947d54590a refactor(ff-game): 重构游戏模块代码
- 移除了 GameName、GamePlatform 和 GameSecretKey 相关的实体类、Mapper 和 Service
- 优化了 ApiGameController 中的代码结构
- 更新了 GameBalanceExchange 类的继承关系
- 调整了 GamesAEServiceImpl 和 GamesDGServiceImpl 中的方法实现
2025-04-07 15:22:04 +08:00
shi 22de0fc207 Merge branch 'main-meitian' into main-pgt 2025-04-07 14:45:02 +08:00
shi 905893df96 feat(game): 添加 PGT 游戏接口实现
- 新增 PGT 游戏列表获取功能- 实现 PGT 投注记录获取和处理
- 添加 PGT 成员踢出功能
- 优化 PGT 客户端接口定义
- 新增相关枚举类和数据传输对象
2025-04-07 14:44:55 +08:00
liaoyong b35fad13d1 refactor(ff-game): 重构游戏平台相关代码
- 移除了 GameSecretKeyCurrency 相关的使用
- 优化了货币转换逻辑
- 简化了游戏平台和货币信息的处理- 调整了批量插入逻辑,使其更加通用
2025-04-07 11:22:47 +08:00
shi 4e5fb79858 Merge branch 'main-meitian' into main-pgt 2025-04-07 10:54:02 +08:00
shi 2013a12aee feat(game): 添加 PGT 游戏平台支持
- 新增 PGT 游戏平台的 API接口和实现类
- 添加 PGT 游戏平台的会员账户生成逻辑
- 实现 PGT 游戏平台的余额查询、转账等功能
- 添加 PGT 游戏平台的错误响应和数据模型类
2025-04-07 10:53:56 +08:00
liaoyong 40208aab98 feat(game): 新增游戏平台支持
- 添加 AE 和 DG 游戏平台的接口实现
- 新增游戏列表、兑换转账、投注记录等功能
- 优化游戏数据构建和处理逻辑
- 移除不必要的服务和缓存操作
2025-04-07 10:15:35 +08:00
shi 3ce8dba98f feat(game): 添加 PGT 游戏平台支持
- 新增 PGT 游戏平台的 API接口和实现类
- 添加 PGT 游戏平台的会员账户生成逻辑
- 实现 PGT 游戏平台的余额查询、转账等功能
- 添加 PGT 游戏平台的错误响应和数据模型类
2025-04-03 16:57:14 +08:00
shi ebbf2e14a2 Merge branch 'main-meitian' into main-pgt 2025-04-03 16:29:23 +08:00
shi e020b73282 feat(game): 添加 PGT 游戏平台支持
- 新增 PGT 游戏平台的 API接口和实现类
- 添加 PGT 游戏平台的会员账户生成逻辑
- 实现 PGT 游戏平台的余额查询、转账等功能
- 添加 PGT 游戏平台的错误响应和数据模型类
2025-04-03 16:27:48 +08:00
liaoyong 22b613ece0 refactor(ff-game): 重构游戏 API 控制器
- 移除了对 GameSecretKeyCurrencyDTO 和 GameSecretKeyLangDTO 的依赖- 引入了 Platform 和 KeyInfo 类
- 优化了货币和语言的处理逻辑
-简化了游戏平台的配置和访问
- 调整了线程池的使用方式
2025-04-03 14:34:49 +08:00
liaoyong e2375788b4 refactor(ff-game): 重构 KM 游戏平台接口请求
- 移除了 Constants 类中与 KM 相关的冗余常量定义
- 更新了 GamesKMServiceImpl 类中的 URL 构造方式,使用平台服务获取登录 URL
- 优化了代码结构,提高了可维护性和可扩展性
2025-04-03 11:35:39 +08:00
liaoyong ad40fc712d refactor(game): 重构游戏 API 地址来源
- 替换 ISysConfigService 为 IPlatformService
- 使用 GamePlatforms枚举替代硬编码的平台名称
- 优化地址获取逻辑,提高代码复用性
- 删除未使用的 Random 类导入
-调整代码格式,提高可读性
2025-04-03 11:32:14 +08:00
liaoyong f1aa9d26e7 Merge remote-tracking branch 'origin/main' into main-meitian
# Conflicts:
#	ff-base/src/main/java/com/ff/base/constant/CacheConstants.java
2025-04-03 11:21:43 +08:00
liaoyong b7ff4ada0e feat(game): 新增平台管理功能
- 添加平台管理相关的实体类、Mapper、Service及其实现类
- 实现平台信息的缓存加载和获取
- 新增美天棋牌平台的支持
-重构原有的地址源获取逻辑,使用平台代码进行配置
- 删除了未使用的旧代码和注释
2025-04-03 11:20:05 +08:00
shi 8ffe94204c feat(game): 添加 KM 游戏平台支持
- 新增 KM游戏平台的接口实现类 GamesKMServiceImpl
- 添加 KM 游戏平台的相关配置和常量- 实现 KM 游戏平台的登录、获取游戏列表、下注记录等功能
- 增加 KM 游戏平台的错误处理和日志记录
2025-04-03 10:40:43 +08:00
shi 019b4e0117 feat(game): 添加 KM 游戏平台支持
- 新增 KM游戏平台的接口实现类 GamesKMServiceImpl
- 添加 KM 游戏平台的相关配置和常量- 实现 KM 游戏平台的登录、获取游戏列表、下注记录等功能
- 增加 KM 游戏平台的错误处理和日志记录
2025-04-03 09:50:51 +08:00
liaoyong 3b8cddd2ec fix(game): 修复每日游戏接口调用路径和签名逻辑
- 在 MeiTianClient 中添加斜杠以匹配服务端路径
- 修改签名逻辑为服务端 key 加密原文本,确保与服务端保持一致
2025-04-02 09:36:36 +08:00
shi 5cddf1b783 refactor(game): 重构游戏服务实现类
- 优化 GameServiceImpl 类的代码结构,增加日志记录和依赖注入
- 新增 insertGameBettingDetails 方法处理游戏投注详细信息插入
- 修改 GameTask 类,使用新的游戏服务方法
- 更新 IGameService 接口,添加新的方法定义
2025-04-02 09:16:06 +08:00
liaoyong 3571c94fd3 Merge remote-tracking branch 'origin/main-meitian' 2025-04-01 17:46:57 +08:00
shi dbde2ba5d5 feat(game): 接入 AE游戏平台
- 新增 AE 游戏平台的下注记录获取功能
- 实现 AE平台的余额转换状态查询接口
- 添加 AE 平台的玩家登出接口
- 重构原有 XK 平台的相关代码,改为支持 AE 平台
2025-04-01 17:46:33 +08:00
liaoyong d3fcfad870 fix(game): 修复美天平台同步异常问题
- 修正平台代码和游戏类型获取逻辑
- 优化数据同步流程,支持按日期同步
- 增加错误日志记录
- 完善配置插入和更新逻辑
2025-04-01 17:38:16 +08:00
liaoyong 953c94ff3f refactor(game): 调整美天游戏登录参数
- 注释掉 "gameHall" 参数- 解除 "gameCode" 参数的注释
- 移除多余的空格和注释
2025-04-01 16:48:03 +08:00
liaoyong c520a5fa24 refactor(ff-game): 重构美天游戏记录同步逻辑
- 修改类名和字段名以适应新的数据结构
- 实现按记录ID和日期同步游戏记录的功能- 优化数据处理和插入逻辑
- 更新游戏平台枚举和游戏类型枚举
- 新增系统配置相关方法
2025-04-01 16:47:19 +08:00
shi 8e32200728 Merge branch 'main-ae' 2025-04-01 16:14:20 +08:00
shi 922d59591d feat(game): 接入 AE游戏平台
- 新增 AE 游戏平台的下注记录获取功能
- 实现 AE平台的余额转换状态查询接口
- 添加 AE 平台的玩家登出接口
- 重构原有 XK 平台的相关代码,改为支持 AE 平台
2025-04-01 16:13:44 +08:00
liaoyong 1c498595e1 Merge remote-tracking branch 'origin/main' into main-meitian
# Conflicts:
#	ff-base/src/main/java/com/ff/base/enums/GamePlatforms.java
2025-04-01 11:39:04 +08:00
liaoyong e632c378a2 feat(game): 添加美天棋牌平台支持
- 新增美天棋牌相关的常量、API接口和数据模型
- 实现美天棋牌的会员创建、余额查询、登录、转账等功能
- 添加美天棋牌的游戏数据和记录查询接口
2025-04-01 11:38:30 +08:00
shi 61d956b88a Merge branch 'main-ae' 2025-04-01 11:18:48 +08:00
shi 3e96c67d57 feat(api): 添加 AE 平台相关功能
- 新增 AEClient 接口和相关 DTO 类- 实现 AE 平台的登录、获取会员信息、存款、
2025-04-01 11:18:26 +08:00
shi ed4c981ef7 refactor(game): 更新 PGXBetHistoryResponse 和 PGXClient
- 将 PGXBetHistoryResponse 中的 int 类型改为 Long 类型
- 修改 PGXClient 中的 API 调用地址
2025-03-31 16:55:26 +08:00
shi d211dd2d7e refactor(game): 更新 PGXBetHistoryResponse 和 PGXClient
- 将 PGXBetHistoryResponse 中的 int 类型改为 Long 类型
- 修改 PGXClient 中的 API 调用地址
2025-03-31 16:48:58 +08:00
shi 210d878de7 feat(member): 添加会员投注限额功能
- 在会员创建接口中增加投注限额参数
- 更新相关 DTO 和服务实现以支持投注限额- 使用 JSON 序列化投注限额对象以适应 AE 接口要求
2025-03-31 15:23:56 +08:00
liaoyong 5f9af0e041 chore: 添加 .gitignore 文件
- 创建 .gitignore 文件,排除构建工具、IDE 和其他工具产生的临时文件和目录
-保留必要的构建脚本文件
- 优化项目结构,提高代码可读性和可维护性
2025-03-31 14:51:25 +08:00
shi dd65af91b8 fix(game): 修复游戏平台注单拉取数据为空时的处理
- 在 JILI 和 XK游戏平台的注单拉取逻辑中,添加了对返回数据为空的处理
- 确保在数据为空时不会出现空指针异常,提高了代码的健壮性
2025-03-31 13:38:34 +08:00
shi 1d21ed26ad fix(game): 补充 pgx 彩种开奖接口 url 2025-03-31 11:23:52 +08:00
shi 8732f3e85f refactor(game): 优化游戏列表接口参数传递方式
- 在 GamesPPServiceImpl 中,将 JSONUtils.toJSONString(params) 替换为直接使用 params
- 在 PPClient 中,修改 getGameList 方法参数类型从 String 改为 Map<String, Object>
2025-03-28 19:23:26 +08:00
shi de4a7e9286 feat(game): 添加 PGX 游戏平台支持
- 新增 PGX 游戏平台的 API接口实现类 GamesPGXServiceImpl
- 添加 PGX 游戏类型枚举类 GPXGameType
- 在 GamePlatforms 枚举中添加 PGX 游戏平台
- 更新相关服务和 Mapper 类以支持 PGX 游戏平台
2025-03-28 14:54:21 +08:00
shi cc17d83e16 feat(game): 添加 PP 游戏接口相关类和方法
- 新增 PP 游戏相关的常量和配置
- 添加 PP 游戏用户信息、登录、游戏列表等接口的请求和响应类- 实现 PP 游戏服务的基本功能,包括创建用户、获取游戏列表等
- 添加地址源和客户端接口,用于与 PP游戏服务器通信
2025-03-27 17:07:17 +08:00
shi d293f638bf feat(game): 更新 DG 平台相关功能
- 修改会员游戏账号生成逻辑,支持 DG 平台
- 实现 DG 平台的踢人和踢出所有人的功能
- 更新 DG 平台的注单记录获取逻辑
- 优化 DG 客户端的接口调用
2025-03-27 14:36:05 +08:00
shi 41a9e3dee4 feat(game): 添加 DG 游戏平台支持
- 新增 DG游戏平台的 API 接口和实现类
- 添加 DG游戏的登录、转账、获取游戏列表等功能
- 实现 DG 游戏的注单记录获取和处理
- 在数据库中添加 DG 游戏平台的相关配置
2025-03-27 11:07:59 +08:00
shi aa7848e342 Merge remote-tracking branch 'origin/main' 2025-03-26 18:59:27 +08:00
shi 871b31555b refactor(game): 重构游戏平台不支持的方法
- 在 ErrorCode枚举中添加 PLATFORM_NOT_METHODS 错误码
- 修改 GamesFCServiceImpl、GamesSAServiceImpl 和 GamesXKServiceImpl 中不支持的方法,统一抛出 PLATFORM_NOT_METHODS异常
- 优化 SAClient 中的 kickMember 方法,返回 String 类型
- 新增 SAKickUserResponse 类用于处理踢出用户请求的返回
2025-03-26 18:58:31 +08:00
shi 8332c4fdcd feat(game): 添加 SA 游戏平台支持
- 新增 SA游戏平台的接口实现类 GamesSAServiceImpl
- 添加 SA 游戏平台相关的错误码和缓存常量
- 实现 SA游戏平台的用户登录、余额转移、投注记录查询等功能
- 新增 SA 游戏类型枚举类 SAGameType
- 添加 XML 响应对象的序列化和反序列化支持
2025-03-26 17:45:28 +08:00
998998 106bad5be4 修复问题 2025-03-26 16:20:34 +08:00
998998 138ae4c2f9 修复问题 2025-03-26 16:19:09 +08:00
998998 c45a9113bf 修复问题 2025-03-26 16:17:20 +08:00
shi 9f6d1710b0 feat(game): 添加 SA 游戏接口实现
- 新增 SA游戏的登录、获取会员信息、创建会员等功能实现
- 添加 SA 游戏相关的配置常量和工具类
- 实现 SA 游戏的 XML 数据解析和加密解密逻辑
- 优化原有的 DES 加密工具类,支持 CBC 模式和 PKCS5 填充
2025-03-26 14:34:04 +08:00
shi 1fd1e10339 Merge branch 'main' into main-sa 2025-03-25 17:36:42 +08:00
shi 9f79161986 Merge branch 'main-cf' 2025-03-25 17:04:41 +08:00
shi 61ebcb15aa feat(game): 添加 SA 游戏平台支持
- 新增 SA游戏平台的 API 接口和实现类
- 添加 DESEncryptUtils 工具类用于加密解密
- 在 Constants 中添加 SA_API_BASE_URL 常量
- 新增 MySAAddressSource 类用于获取 SA 游戏平台的地址
- 在 pom.xml 中添加 Jackson XML 数据格式支持依赖
2025-03-25 10:00:42 +08:00
998998 ded6eead74 修复问题 2025-03-24 17:52:01 +08:00
305 changed files with 21259 additions and 3697 deletions

49
.gitignore vendored 100644
View File

@ -0,0 +1,49 @@
######################################################################
# Build Tools
.gradle
/build/
!gradle/wrapper/gradle-wrapper.jar
target/
!.mvn/wrapper/maven-wrapper.jar
######################################################################
# IDE
### STS ###
.apt_generated
.classpath
.factorypath
.project
.settings
.springBeans
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
### JRebel ###
rebel.xml
### NetBeans ###
nbproject/private/
build/*
nbbuild/
dist/
nbdist/
.nb-gradle/
######################################################################
# Others
*.log
*.xml.versionsBackup
*.swp
!*/build/*.java
!*/build/*.html
!*/build/*.xml
logs/
.idea/

View File

@ -5,8 +5,7 @@ package com.ff.base.constant;
*
* @author ff
*/
public class CacheConstants
{
public class CacheConstants {
/**
* redis key
*/
@ -45,29 +44,109 @@ public class CacheConstants
/**
* jili
*/
public static final String JILI_GAMES= "jili_games:";
public static final String JILI_GAMES = "jili_games:";
/**
* xk
*/
public static final String XK_GAMES= "xk_games:";
public static final String XK_GAMES = "xk_games:";
/**
* ae
*/
public static final String AE_GAMES = "ae_games:";
/**
* km
*/
public static final String KM_GAMES = "km_games:";
/**
* pgx
*/
public static final String PGX_GAMES = "pgx_games:";
/**
* pg
*/
public static final String PG_GAMES= "pg_games:";
public static final String PG_GAMES = "pg_games:";
/**
* pgt
*/
public static final String PGT_GAMES = "pgt_games:";
/**
* fc
*/
public static final String FC_GAMES= "fc_games:";
public static final String FC_GAMES = "fc_games:";
/**
* sa
*/
public static final String SA_GAMES = "sa_games:";
/**
* dg
*/
public static final String DG_GAMES = "dg_games:";
/**
* fb
*/
public static final String FB_Sports = "fp_sports:";
/**
* db
*/
public static final String DB_Sports = "db_sports:";
/**
* pg
*/
public static final String PG_GAMES_BET_CURRENCY= "pg_games:bet:currency";
public static final String PG_GAMES_BET_CURRENCY = "pg_games:bet:currency";
/**
* pgtid
*/
public static final String PGT_NEXT_ID = "pgt_next:id:";
/**
* ae
*/
public static final String AE_TIME_FROM= "ae:time:from";
/**
*
*/
public static final String MeiTian_GAMES = "meitian_games:";
public static final String Platform = "platform:";
/**
* km
*/
public static final String KM_USER_TOKEN = "km:user:token:";
/**
* ae
*/
public static final String SV388_TIME_FROM= "sv388:time:from";
public static final String SV388_GAMES = "sv388_games:";
/**
* pp
*/
public static final String PP_GAMES= "pp_games:";
/**
* pp
*/
public static final String PP_TIME_POINT = "pp:time:point:";
}

View File

@ -16,6 +16,8 @@ public class ConfigConstants
public static final String VIEW_FILE_URL = "view.file.url";
/**
* nginx
*/
@ -26,6 +28,18 @@ public class ConfigConstants
*/
public static final String DOMAIN_NGINX_CONFIG_PARAM = "domain.nginx.config.param";
/**
*sa
*/
public static final String SA_API_LOGIN_URL = "sa.api.login.url";
/**
* sa-api
*/
public static final String SA_API_HALL_CODE = "sa.api.hall.code";
/**
* url
*/

View File

@ -178,30 +178,7 @@ public class Constants {
*/
public static final String PASS_PREFIX = "FF_";
/**
* jili
*/
public static final String JILI_API_BASE_URL = "jili.api.base.url";
/**
* ng
*/
public static final String NG_API_BASE_URL = "ng.api.base.url";
/**
* fc-apiurl
*/
public static final String FC_API_BASE_URL = "fc.api.base.url";
/**
*
*/
public static final String JILI_GAME_DOME = "jili.game.dome";
/**
* xk
*/
public static final String XK_API_BASE_URL = "xk.api.base.url";
/**
*
*/
@ -213,15 +190,12 @@ public class Constants {
public static final String SYSTEM = "system";
/**
*
*/
public static final String DATA_SOURCE = "master";
/**
* id
*/
@ -275,5 +249,5 @@ public class Constants {
/**
*
*/
public static final BigDecimal HUNDRED =new BigDecimal("100") ;
public static final BigDecimal HUNDRED = new BigDecimal("100");
}

View File

@ -13,21 +13,31 @@ import java.util.stream.Stream;
*/
@Getter
public enum ErrorCode {
ERROR(500, "系统业务异常"),
ERROR(500, "业务异常"),
GAME_ACCOUNT_CREATION_FAILED(1001, "当前游戏账号已存在"),
ACCOUNT_NOT_EXIST(1002, "当前游戏账号不存在"),
PLATFORM_NOT_EXIST(1003, "游戏平台不存在"),
CURRENCY_NOT_EXIST(1004, "游戏平台不支持的货币"),
GAME_NOT_EXIST(1005, "游戏不存在"),
CURRENCY_EXCHANGE(1006, "不支持币种的汇率"),
FREQUENT_INTERFACE_REQUESTS (1007, "接口请求频繁"),
BALANCE_TRANSFER_FAILED (1008, "余额转移失败"),
FREQUENT_INTERFACE_REQUESTS(1007, "接口请求频繁"),
BALANCE_TRANSFER_FAILED(1008, "余额转移失败"),
LANG_NOT_EXIST(1009, "游戏平台不支持的语言"),
ORDER_NOT_EXIST(1010, "订单不存在"),
PLAYERS_ARE_PLAYING(1011, "玩家游玩中"),
INSUFFICIENT_PLAYER_BALANCE(1012, "玩家余额不足"),
KICK_OUT_AILED(1013, "玩家踢出失败"),
ACCOUNT_NOT_ONLINE(1014, "账号不在线"),
FREQUENT_BALANCE_TRANSFER(1015, "当前游戏账号余额转移频繁"),
PLATFORM_NOT_METHODS(1016, "游戏平台不支持的方法"),
Create_Member_Failure(1017, "创建会员失败"),
Transfer_In_Failure(1018, "转入失败"),
Transfer_Out_Failure(1019, "转出失败"),
Get_Member_Info_Failure(1020, "获取会员信息失败"),
Transfer_Not_Exist(1021, "转帐操作不存在"),
Get_Url_Failure(1022, "获取URL失败"),
Miss_Config(1023, "缺少配置"),
DUPLICATE_ORDER_ID (1024, "重复的订单id"),
;
// 获取错误码

View File

@ -0,0 +1,60 @@
package com.ff.base.enums;
import lombok.Getter;
import java.util.Optional;
import java.util.stream.Stream;
/**
* xkgame
*
* @author shi
* @date 2024/11/13
*/
@Getter
public enum FBSportsType {
Sports("8", 8,"FB体育"),
;
private final String code;
private final Integer systemCode;
private final String info;
FBSportsType(String code, Integer systemCode, String info)
{
this.code = code;
this.systemCode = systemCode;
this.info = info;
}
/**
*
*
* @param code
* @return {@link String }
*/
public static Integer findSystemByCode(String code) {
Optional<Integer> system = Stream.of(FBSportsType.values())
.filter(gameType -> gameType.getCode().equals(code))
.map(FBSportsType::getSystemCode)
.findFirst();
return system.orElse(null);
}
/**
*
*
* @param code
* @return {@link String }
*/
public static String findInfoByCode(String code) {
Optional<String> system = Stream.of(FBSportsType.values())
.filter(gameType -> gameType.getCode().equals(code))
.map(FBSportsType::getInfo)
.findFirst();
return system.orElse(null);
}
}

View File

@ -0,0 +1,55 @@
package com.ff.base.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Optional;
import java.util.stream.Stream;
/**
* xkgame
*
* @author shi
* @date 2024/11/13
*/
@Getter
@AllArgsConstructor
public enum GPXGameType {
SL("SL", 1,"电子")
;
private final String code;
private final Integer systemCode;
private final String info;
/**
*
*
* @param code
* @return {@link String }
*/
public static Integer findSystemByCode(String code) {
Optional<Integer> system = Stream.of(XKGameType.values())
.filter(gameType -> gameType.getCode().equals(code))
.map(XKGameType::getSystemCode)
.findFirst();
return system.orElse(null);
}
/**
*
*
* @param code
* @return {@link String }
*/
public static String findInfoByCode(String code) {
Optional<String> system = Stream.of(XKGameType.values())
.filter(gameType -> gameType.getCode().equals(code))
.map(XKGameType::getInfo)
.findFirst();
return system.orElse(null);
}
}

View File

@ -0,0 +1,34 @@
package com.ff.base.enums;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
/**
*
*
* @author shi
* @date 2025/04/08
*/
@Getter
@AllArgsConstructor
public enum GameExchangeStep {
CREATE_ORDER(1, "创建订单"),
DEDUCT_BALANCE(2, "转入提前扣租户余额"),
PLATFORM_TRANSACTION(3, "平台交易成功"),
TENANT_QUOTA_DEDUCTED(4, "转出租户额度增加成功");
private final Integer code;
private final String description;
// 根据 code 获取对应的枚举
public static GameExchangeStep getByCode(int code) {
for (GameExchangeStep step : GameExchangeStep.values()) {
if (step.getCode() == code) {
return step;
}
}
return null;
}
}

View File

@ -0,0 +1,21 @@
package com.ff.base.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*
*
* @author shi
* @date 2025/04/08
*/
@Getter
@AllArgsConstructor
public enum GameExchangeStepStatus {
IN_PROGRESS(0, "进行中"),
SUCCESS(1, "成功"),
FAILURE(2, "失败");
private final int code;
private final String description;
}

View File

@ -1,5 +1,7 @@
package com.ff.base.enums;
import com.ff.base.exception.base.ApiException;
import java.util.ArrayList;
import java.util.List;
@ -7,22 +9,44 @@ public enum GamePlatforms {
JILI("JILI", "JILI"),
XK("XK", "XK"),
PG("PG", "PG"),
PGX("PGX", "PGX"),
FC("FC", "FC"),
;
SA("SA", "SA"),
DG("DG", "DG"),
MT("MT", "美天棋牌"),
AE("AE", "AE"),
KM("KM", "KM"),
PGT("PGT", "PGT"),
FBSports("FBSports", "FB体育"),
SV388("SV388", "SV388真人"),
PP("PP", "PP"),
DBSports("DBSports", "DB体育");
private final String code;
private final String info;
GamePlatforms(String code, String info)
{
GamePlatforms(String code, String info) {
this.code = code;
this.info = info;
}
/**
*
*
* @param code
* @return {@link GamePlatforms }
*/
public static GamePlatforms getByCode(String code) {
for (GamePlatforms platform : GamePlatforms.values()) {
if (platform.getCode().equals(code)) {
return platform;
}
}
throw new ApiException(ErrorCode.PLATFORM_NOT_EXIST.getCode());
}
public static List<String> getCodes()
{
List<String> result=new ArrayList<>();
public static List<String> getCodes() {
List<String> result = new ArrayList<>();
GamePlatforms[] values = GamePlatforms.values();
for (GamePlatforms value : values) {
result.add(value.getCode());
@ -30,13 +54,11 @@ public enum GamePlatforms {
return result;
}
public String getCode()
{
public String getCode() {
return code;
}
public String getInfo()
{
public String getInfo() {
return info;
}

View File

@ -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<Integer> 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<String> system = Stream.of(KMGameType.values())
.filter(gameType -> gameType.getCode().equals(code))
.map(KMGameType::getInfo)
.findFirst();
return system.orElse(null);
}
}

View File

@ -0,0 +1,53 @@
package com.ff.base.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Optional;
import java.util.stream.Stream;
/**
* @author cengy
*/
@Getter
@AllArgsConstructor
public enum MeiTianGameType {
ELECTRON(4, 1, "电子"),
CHESS(2, 2, "棋牌"),
GAME_HALL(0, 3, "游戏大厅"),
CATCH_FISH(3, 4, "捕鱼"),
bai_ren(1, 5,"百人场")
,
;
private final Integer code;
private final Integer systemCode;
private final String info;
/**
*
*
* @param code
* @return {@link String }
*/
public static Integer findSystemByCode(Integer code) {
Optional<Integer> system = Stream.of(MeiTianGameType.values())
.filter(gameType -> gameType.getCode().equals(code))
.map(MeiTianGameType::getSystemCode)
.findFirst();
return system.orElse(null);
}
/**
*
*
* @param code
* @return {@link String }
*/
public static String findInfoByCode(Integer code) {
Optional<String> system = Stream.of(MeiTianGameType.values())
.filter(gameType -> gameType.getCode().equals(code))
.map(MeiTianGameType::getInfo)
.findFirst();
return system.orElse(null);
}
}

View File

@ -0,0 +1,24 @@
package com.ff.base.enums;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
/**
*
*
* @author shi
* @date 2025/04/07
*/
@Getter
@AllArgsConstructor
public enum PGTBetStatus {
OPEN(1, "打开 或 未结算", "OPEN"),
SETTLED(2, "已结算", "SETTLED"),
UNSETTLED(1, "未结算", "UNSETTLED"),
VOID(3, "作废 或 无效", "VOID");
private final int code;
private final String description;
private final String type;
}

View File

@ -0,0 +1,43 @@
package com.ff.base.enums;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
import java.util.Optional;
import java.util.stream.Stream;
/**
* pgt
*
* @author shi
* @date 2025/04/07
*/
@Getter
@AllArgsConstructor
public enum PGTGameType {
EGAMES("EGAMES",1, "游戏老虎机"),
LIVECASINO("LIVECASINO",2, "现场赌场"),
SPORT("SPORT",8, "体育"),
POKER("POKER",2, "扑克"),
TRADING("TRADING",1, "贸易");
// 枚举字段
private final String code;
private final Integer systemCode;
private final String description;
/**
*
*
* @param code
* @return {@link Integer }
*/
public static Integer findSystemByCode(String code) {
Optional<Integer> system = Stream.of(PGTGameType.values())
.filter(gameType -> gameType.getCode().equals(code))
.map(PGTGameType::getSystemCode)
.findFirst();
return system.orElse(null);
}
}

View File

@ -0,0 +1,35 @@
package com.ff.base.enums;
import com.dtflys.forest.annotation.Get;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
/**
* pgtplayout
*
* @author shi
* @date 2025/04/07
*/
@Getter
@AllArgsConstructor
public enum PGTPayoutStatus {
LOSE("LOSE", 2, "输"),
WIN("WIN",1, "赢"),
DRAW("DRAW",3, "平"),
UNKNOWN("UNKNOWN",4, "未知");
private final String code;
private final Integer systemCode;
private final String description;
public static PGTPayoutStatus getByCode(String code) {
for (PGTPayoutStatus status : PGTPayoutStatus.values()) {
if (status.getCode().equals(code)) {
return status;
}
}
return UNKNOWN;
}
}

View File

@ -0,0 +1,40 @@
package com.ff.base.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Optional;
import java.util.stream.Stream;
/**
* pgxbet
*
* @author shi
* @date 2025/03/28
*/
@Getter
@AllArgsConstructor
public enum PGXBetRecordStatus {
VALID(1, 2,"有效注单"),
RUNNING(0,0, "赛事进行中"),
INVALID(-1,3, "无效注单 (作废, 取消)");
private final Integer code;
private final Integer systemCode;
private final String description;
/**
*
*
* @param code
* @return {@link Integer }
*/
public static Integer findSystemCodeByCode(Integer code) {
Optional<Integer> system = Stream.of(PGXBetRecordStatus.values())
.filter(transactionStatusValue -> transactionStatusValue.getCode().equals(code))
.map(PGXBetRecordStatus::getSystemCode)
.findFirst();
return system.orElse(null);
}
}

View File

@ -0,0 +1,41 @@
package com.ff.base.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Optional;
import java.util.stream.Stream;
/**
*
*
* @author shi
* @date 2025/03/28
*/
@AllArgsConstructor
@Getter
public enum PGXTransactionStatus {
SUCCESS("SUCCESS",1, "成功"),
PROCESSING("PROCESSING",0, "进行中,请稍后再确认"),
FAILED("FAILED",2, "失败");
// 枚举字段
private final String code;
private final Integer value;
private final String description;
/**
*
*
* @param code
* @return {@link Integer }
*/
public static Integer findValueByCode(String code) {
Optional<Integer> system = Stream.of(PGXTransactionStatus.values())
.filter(transactionStatusValue -> transactionStatusValue.getCode().equals(code))
.map(PGXTransactionStatus::getValue)
.findFirst();
return system.orElse(null);
}
}

View File

@ -0,0 +1,22 @@
package com.ff.base.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*
*
* @author shi
* @date 2025/03/12
*/
@Getter
@AllArgsConstructor
public enum PGXTransferType {
WITHDRAW("1", "提出"),
DEPOSIT("0", "存入");
private final String code;
private final String description;
}

View File

@ -0,0 +1,64 @@
package com.ff.base.enums;
import lombok.AllArgsConstructor;
import lombok.Getter;
import java.util.Optional;
import java.util.stream.Stream;
/**
* xkgame
*
* @author shi
* @date 2024/11/13
*/
@Getter
@AllArgsConstructor
public enum PPGameType {
ELECTRONIC("vs", 1,"电子"),
// CARD_GAME("rl",2, "轮盘"),
// VIDEO("vp",6, "视频扑克"),
// SCRATCH_CARD("sc",7, "刮刮乐"),
// BLACK_JACK("bj",2, "21点"),
// CLASSIC_SLOTS("cs",1, "电子"),
// BACCARAT_NEW("bn",2, "百家乐新"),
// BACCARAT("bc",2, "百家乐"),
// LIVE_GAMES("lg",6, "现场游戏"),
;
private final String code;
private final Integer systemCode;
private final String info;
/**
*
*
* @param code
* @return {@link String }
*/
public static Integer findSystemByCode(String code) {
Optional<Integer> system = Stream.of(PPGameType.values())
.filter(gameType -> gameType.getCode().equals(code))
.map(PPGameType::getSystemCode)
.findFirst();
return system.orElse(null);
}
/**
*
*
* @param code
* @return {@link String }
*/
public static String findInfoByCode(String code) {
Optional<String> system = Stream.of(PPGameType.values())
.filter(gameType -> gameType.getCode().equals(code))
.map(PPGameType::getInfo)
.findFirst();
return system.orElse(null);
}
}

View File

@ -18,10 +18,11 @@ public enum PlatformType {
GAME_HALL(3, "游戏大厅"),
FISHING(4, "捕鱼"),
BETTING_MACHINE(5, "押分机 (含宾果)"),
VIDEO(6, "视讯"),
VIDEO(6, "真人视讯"),
LOTTERY(7, "彩票"),
SPORTS(8, "体育"),
HUNTING(9, "捕猎");
HUNTING(9, "捕猎"),
BaiRen(10, "百人场");
private final int code;
private final String name;

View File

@ -0,0 +1,64 @@
package com.ff.base.enums;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
/**
* sagame
*
* @author shi
* @date 2025/03/26
*/
@Getter
@AllArgsConstructor
public enum SAGameType {
// 编号与名称映射
M_COLOR_DISC(531, "M 色碟"),
M_DICE_BAO(532, "M 骰宝"),
M_ROULETTE(533, "M 轮盘"),
M_DRAGON_TIGER(534, "M 龙虎"),
M_BODING(535, "M 博丁"),
M_BLACKJACK(536, "M 黑杰克"),
M_ANDABAH(537, "M 安达巴哈"),
M_INDIA_ZHA_ZHENG(538, "M 印度炸金花"),
M_THAI_DICE_BAO(539, "M 泰国骰宝"),
M_FISH_SHRIMP_CRAB(540, "M 鱼虾蟹"),
C_ROULETTE(861, "C 轮盘"),
C_BACCARAT_C01(871, "百家乐 C01"),
C_BACCARAT_C02(872, "百家乐 C02"),
C_BACCARAT_C03(873, "百家乐 C03"),
C_BACCARAT_C04(874, "百家乐 C04"),
C_BACCARAT_C05(875, "百家乐 C05"),
C_BACCARAT_C06(876, "百家乐 C06"),
C_BACCARAT_C07(877, "百家乐 C07"),
C_FAST_BACCARAT_C08(878, "极速百家乐 C08"),
D_BACCARAT_D01(901, "百家乐 D01"),
D_BACCARAT_D02(902, "百家乐 D02"),
D_BACCARAT_D03(903, "百家乐 D03"),
D_BACCARAT_D04(904, "百家乐 D04"),
D_BACCARAT_D05(905, "百家乐 D05"),
D_BACCARAT_D06(906, "百家乐 D06"),
D_BACCARAT_D07(907, "百家乐 D07"),
D_FAST_BACCARAT_D08(908, "极速百家乐 D08"),
D_BLACKJACK(921, "D 黑杰克"),
D_DRAGON_TIGER(922, "D 龙虎"),
D_BODING(923, "D 博丁"),
D_ROULETTE(924, "D 轮盘"),
D_DICE_BAO(925, "D 骰宝"),
D_THAI_DICE_BAO(926, "D 泰国骰宝"),
D_COLOR_DISC(927, "D 色碟");
private final int code;
private final String name;
public static String getNameByCode(int code) {
for (SAGameType gameType : values()) {
if (gameType.getCode() == code) {
return gameType.getName();
}
}
return null;
}
}

View File

@ -0,0 +1,25 @@
package com.ff.base.enums;
import com.alibaba.druid.filter.AutoLoad;
import lombok.AllArgsConstructor;
import lombok.Getter;
/**
*
*
* @author shi
* @date 2025/04/09
*/
@AllArgsConstructor
@Getter
public enum TimeOutType {
GAME_EXCHANGE_MONEY("gameExchangeMoney", "游戏余额转移");
/**
*
*/
private final String code;
/**
*
*/
private final String info;
}

View File

@ -9,7 +9,8 @@ package com.ff.base.enums;
*/
public enum TransferType {
ALL(1, "从游戏商转移额度到平台商(不看amount值全部转出"),
GAMES(2, "从平台商转移额度到游戏商");
GAMES(2, "从平台商转移额度到游戏商"),
;
private final Integer code;
private final String info;

View File

@ -0,0 +1,21 @@
package com.ff.base.enums;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.Getter;
/**
*
*
* @author shi
* @date 2025/04/09
*/
@Getter
@AllArgsConstructor
public enum TriggerType {
MANUAL(1, "用户调用手动触发"),
TIMER(2, "定时器触发");
private final int code;
private final String description;
}

View File

@ -0,0 +1,59 @@
package com.ff.base.handler;
import com.alibaba.fastjson2.JSON;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* mybatis json
*
* @author cengy
*/
public class JsonHandler<T> extends BaseTypeHandler<T> {
private final Class<T> type;
public JsonHandler(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.type = type;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, JSON.toJSONString(parameter));
}
@Override
public T getNullableResult(ResultSet rs, String columnName) throws SQLException {
String json = rs.getString(columnName);
if (json != null) {
return JSON.parseObject(json, type);
}
return null;
}
@Override
public T getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String json = rs.getString(columnIndex);
if (json != null) {
return JSON.parseObject(json, type);
}
return null;
}
@Override
public T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String json = cs.getString(columnIndex);
if (json != null) {
return JSON.parseObject(json, type);
}
return null;
}
}

View File

@ -0,0 +1,64 @@
package com.ff.base.handler;
import com.alibaba.fastjson2.JSON;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.List;
/**
* mybatis json
*
* @author cengy
*/
public class JsonListHandler<T> extends BaseTypeHandler<List<T>> {
private final Class<T> type;
public JsonListHandler(Class<T> type) {
if (type == null) {
throw new IllegalArgumentException("Type argument cannot be null");
}
this.type = type;
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, List<T> parameter, JdbcType jdbcType) throws SQLException {
// Convert the List<Item> to JSON string
String json = JSON.toJSONString(parameter);
ps.setString(i, json);
}
@Override
public List<T> getNullableResult(ResultSet rs, String columnName) throws SQLException {
String json = rs.getString(columnName);
if (json == null) {
return null;
}
return JSON.parseArray(json, type);
}
@Override
public List<T> getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
String json = rs.getString(columnIndex);
if (json == null) {
return null;
}
return JSON.parseArray(json, type);
}
@Override
public List<T> getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
String json = cs.getString(columnIndex);
if (json == null) {
return null;
}
return JSON.parseArray(json, type);
}
}

View File

@ -2,11 +2,12 @@ package com.ff.base.manager;
import com.ff.base.utils.Threads;
import com.ff.base.utils.spring.SpringUtils;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.ArrayList;
import java.util.List;
import java.util.TimerTask;
import java.util.concurrent.Callable;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.*;
/**
*
@ -24,10 +25,33 @@ public class AsyncManager {
*/
private ScheduledExecutorService executor = SpringUtils.getBean("scheduledExecutorService");
/**
* 线
*/
private List<ExecutorService> orderedExecutor = new ArrayList<>();
/**
* 线
*/
public static final int MAX_THREAD_COUNT = 16;
/**
* 线
*/
private ThreadPoolTaskExecutor taskExecutor;
/**
*
*/
private AsyncManager() {
for (int i = 0; i < MAX_THREAD_COUNT; i++) {
orderedExecutor.add(Executors.newSingleThreadExecutor());
}
executor = SpringUtils.getBean("scheduledExecutorService");
taskExecutor = SpringUtils.getBean("threadPoolTaskExecutor");
}
private static AsyncManager me = new AsyncManager();
@ -36,6 +60,22 @@ public class AsyncManager {
return me;
}
public void executeOrdered(String key, Runnable task) {
if (null == key || key.isEmpty()) {
taskExecutor.execute(task);
return;
}
int hash = key.hashCode();
int index = Math.abs(hash % MAX_THREAD_COUNT);
orderedExecutor.get(index).execute(task);
}
/**
*
*

View File

@ -11,6 +11,8 @@ import java.util.List;
*/
public interface ISysConfigService
{
SysConfig getByConfigKey(String configKey);
/**
*
*

View File

@ -32,6 +32,12 @@ public class SysConfigServiceImpl implements ISysConfigService {
@Autowired
private RedisCache redisCache;
@Override
public SysConfig getByConfigKey(String configKey) {
SysConfig config = new SysConfig();
config.setConfigKey(configKey);
return configMapper.selectConfig(config);
}
/**
*

View File

@ -65,6 +65,8 @@ public class TenantSecretKeyServiceImpl implements ITenantSecretKeyService {
@Resource
private ITenantPlatformService tenantPlatformService;
/**
*
*
@ -220,11 +222,7 @@ public class TenantSecretKeyServiceImpl implements ITenantSecretKeyService {
if (insertedTenantSecretKey > 0) {
for (TenantPlatform tenantPlatform : createTenantDTO.getTenantPlatforms()) {
TenantPlatform platformById = tenantPlatformService.selectTenantPlatformById(tenantPlatform.getId());
// 成本比平台成本大
if (platformById.getCost().compareTo(tenantPlatform.getCost())>0){
throw new BaseException("成本比例不允许比最低比例小");
}
tenantPlatform.setTenantId(tenantSecretKey.getId());
tenantPlatform.setCreateBy(createTenantDTO.getAccount());

View File

@ -0,0 +1,84 @@
package com.ff.base.utils;
import com.ff.base.exception.base.BaseException;
import lombok.Data;
import javax.crypto.*;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.spec.InvalidKeySpecException;
import java.security.spec.KeySpec;
import java.util.Base64;
/**
*
*
* @author shi
* @date 2025/03/24
*/
public class DESEncryptUtils {
/**
*
*
* @param inString
* @param key
* @return {@link String }
* @throws Exception
*/
public static String DESEncrypt(String inString, String key) {
try {
byte[] encryptKey = key.getBytes(); // encryptKey
KeySpec keySpec = new DESKeySpec(encryptKey);
SecretKey myDesKey = SecretKeyFactory.getInstance("DES").generateSecret(keySpec);
IvParameterSpec iv = new IvParameterSpec(encryptKey);
KeyGenerator keygenerator = KeyGenerator.getInstance("DES");
Cipher desCipher;
// Create the cipher
desCipher = Cipher.getInstance("DES/CBC/PKCS5Padding");
// Initialize the cipher for encryption
desCipher.init(Cipher.ENCRYPT_MODE, myDesKey, iv);
//sensitive information
String source = inString;
byte[] text = source.getBytes();
System.out.println("Text [Byte Format] : " + text);
System.out.println("Text : " + new String(text));
// Encrypt the text
byte[] textEncrypted = desCipher.doFinal(text);
String t = Base64.getEncoder().encodeToString(textEncrypted);
System.out.println("Text Encryted [Byte Format] : " + textEncrypted);
System.out.println("Text Encryted : " + t);
// Initialize the same cipher for decryption
desCipher.init(Cipher.DECRYPT_MODE, myDesKey, iv);
// Decrypt the text
byte[] textDecrypted = desCipher.doFinal(textEncrypted);
System.out.println("Text Decryted : " + new String(textDecrypted));
return t;
} catch (Exception e) {
throw new BaseException("解密失败");
}
}
}

View File

@ -38,6 +38,12 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
public static String DAY_END_TIME = "23:59:59";
public static final String ISO_8601_FORMAT = "yyyy-MM-dd'T'HH:mm:ss";
public static final String ISO_8601_FORMAT_Z = "yyyy-MM-dd'T'HH:mm:ss'Z'";
/**
* Date
*
@ -202,6 +208,28 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
return zdt.format(formatter);
}
/**
*
*
* @param timestamp
* @param format yyyy-MM-dd'T'HH:mm:ssXXX
* @param timeZone GMT+8UTC
* @return
*/
public static String convertTimestampToFormattedDate(long timestamp, String format, String timeZone) {
// 创建日期格式化对象
SimpleDateFormat sdf = new SimpleDateFormat(format);
// 设置时区为GMT+8
sdf.setTimeZone(TimeZone.getTimeZone(timeZone));
// 转换为 Date 对象
Date date = new Date(timestamp);
// 返回格式化后的时间字符串
return sdf.format(date);
}
/**
*
*
@ -222,7 +250,6 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
}
/**
* LocalDate ==> Date
*/
@ -914,4 +941,57 @@ public class DateUtils extends org.apache.commons.lang3.time.DateUtils {
public static boolean isBetween(Long value, Long minValue, Long maxValue) {
return value >= minValue && value <= maxValue;
}
/**
*
*
* @param dateString "yyyy-MM-dd'T'HH:mm:ss.SSS"
* @param timezone UTCAsia/Shanghai
* @return
* @throws Exception
*/
public static long convertToMillisWithTimezone(String dateString, String timezone) {
try {
// 设置日期格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS");
// 设置解析时使用的时区
sdf.setTimeZone(TimeZone.getTimeZone(timezone));
// 解析日期字符串为 Date 对象
Date date = sdf.parse(dateString);
// 获取项目默认时区
TimeZone defaultTimeZone = TimeZone.getDefault();
// 使用默认时区的 Calendar 计算
Calendar calendar = Calendar.getInstance(defaultTimeZone);
calendar.setTime(date);
// 返回该时区对应的毫秒时间戳
return calendar.getTimeInMillis();
} catch (Exception e) {
return 0;
}
}
/**
* (UTC)
*
* @param dateStr
* @param pattern ("yyyy-MM-dd HH:mm:ss")
* @return
* @throws ParseException
*/
public static long toTimestamp(String dateStr, String pattern) {
try {
SimpleDateFormat sdf = new SimpleDateFormat(pattern);
sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
return sdf.parse(dateStr).getTime();
} catch (Exception e) {
return 0;
}
}
}

View File

@ -108,7 +108,7 @@ public class JsonUtil {
*
* @param map
* @return {@link String }
*/// 将LinkedHashMap转换为查询字符串
*/ // 将LinkedHashMap转换为查询字符串
public static String mapToQueryString(Map<String, Object> map) {
return map.entrySet().stream()
.map(entry -> entry.getKey() + "=" + entry.getValue())

View File

@ -9,7 +9,6 @@ import java.util.concurrent.ThreadLocalRandom;
public class NumberUtils {
/**
*
*
@ -68,17 +67,13 @@ public class NumberUtils {
Random random = new Random();
StringBuilder sb = new StringBuilder();
// 每位字符可以是字母A-Z, a-z或数字0-9
for (int i = 0; i <size ; i++) {
int choice = random.nextInt(3);
// 每位字符可以是大写字母A-Z或数字0-9
for (int i = 0; i < size; i++) {
int choice = random.nextInt(2);
if (choice == 0) {
// 随机字母(大写或小写)
// 随机大写字母
char letter = (char) ('A' + random.nextInt(26));
sb.append(letter);
} else if (choice == 1) {
// 随机小写字母
char letter = (char) ('a' + random.nextInt(26));
sb.append(letter);
} else {
// 随机数字
sb.append(random.nextInt(10));
@ -88,4 +83,5 @@ public class NumberUtils {
return sb.toString();
}
}

View File

@ -29,4 +29,42 @@ public class RandomGeneratorUtils {
return sb.toString();
}
/**
* 5-8
*
* @return {@link String }
*/
public static String generateRandomAccountUpper() {
int length = 5 + RANDOM.nextInt(4);
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char randomChar = CHARACTERS.charAt(RANDOM.nextInt(CHARACTERS.length()));
// 转换为大写字母或保持数字
if (Character.isLetter(randomChar)) {
sb.append(Character.toUpperCase(randomChar));
} else {
sb.append(randomChar);
}
}
return sb.toString();
}
/**
* 5-8
*
* @return {@link String }
*/
public static String generateRandomAccountLower() {
int length = 5 + RANDOM.nextInt(4);
StringBuilder sb = new StringBuilder(length);
for (int i = 0; i < length; i++) {
char randomChar = CHARACTERS.charAt(RANDOM.nextInt(CHARACTERS.length()));
// 保持字母小写,数字不变
sb.append(randomChar);
}
return sb.toString();
}
}

View File

@ -585,12 +585,12 @@ public class StringUtils extends org.apache.commons.lang3.StringUtils {
/**
*
*
* @param account
* @param suffix
* @param code
* @param suffix
* @return {@link String }
*/
public static String addSuffix(String account, String suffix) {
return account + suffix;
public static String addSuffix(String code, Object suffix) {
return code +"_"+ suffix;
}
/**

View File

@ -0,0 +1,55 @@
package com.ff.base.utils;
import com.ff.base.exception.base.BaseException;
import org.apache.poi.ss.formula.functions.T;
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import javax.crypto.spec.IvParameterSpec;
import javax.xml.bind.JAXBContext;
import javax.xml.bind.JAXBException;
import javax.xml.bind.Unmarshaller;
import java.io.StringReader;
import java.security.spec.KeySpec;
import java.util.Base64;
/**
*
*
* @author shi
* @date 2025/03/24
*/
public class XmlUtils {
/**
* xml
*
* @param xmlString xml
* @param clas
* @param <T>
* @return {@link T}
*/
public static <T> T xmlDecrypt(String xmlString, Class<T> clas) {
try {
// 创建 JAXBContext 对象,绑定目标类
JAXBContext jaxbContext = JAXBContext.newInstance(clas);
// 创建 Unmarshaller
Unmarshaller unmarshaller = jaxbContext.createUnmarshaller();
// 将 XML 字符串转换为目标类对象
return clas.cast(unmarshaller.unmarshal(new StringReader(xmlString)));
} catch (JAXBException e) {
// 在异常中包含原始异常信息
throw new BaseException("XML 转化失败失败,错误信息:" + e.getMessage());
}
}
}

View File

@ -4,7 +4,6 @@ import com.ff.base.constant.Constants;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.misc.BASE64Encoder;
import java.nio.charset.StandardCharsets;
import java.security.MessageDigest;
@ -15,27 +14,23 @@ import java.security.NoSuchAlgorithmException;
*
* @author ff
*/
public class Md5Utils
{
public class Md5Utils {
private static final Logger log = LoggerFactory.getLogger(Md5Utils.class);
private static byte[] md5(String s)
{
private static byte[] md5(String s) {
MessageDigest algorithm;
try
{
try {
algorithm = MessageDigest.getInstance("MD5");
algorithm.reset();
algorithm.update(s.getBytes("UTF-8"));
byte[] messageDigest = algorithm.digest();
return messageDigest;
}
catch (Exception e)
{
} catch (Exception e) {
log.error("MD5 Error...", e);
}
return null;
}
public static String md5New(String input) {
try {
MessageDigest md = MessageDigest.getInstance("MD5");
@ -63,30 +58,24 @@ public class Md5Utils
return null;
}
String result = "";
try{
try {
MessageDigest md5 = MessageDigest.getInstance("MD5");
BASE64Encoder base64en = new BASE64Encoder();
result = base64en.encode(md5.digest(str.getBytes(StandardCharsets.UTF_8)));
}catch (Exception e){
log.error("encoderByMd5...{0}", str, e);
result = Base64.encode(md5.digest(str.getBytes(StandardCharsets.UTF_8)));
} catch (Exception e) {
log.error("encoderByMd5...{}", str, e);
}
return result;
}
private static final String toHex(byte hash[])
{
if (hash == null)
{
private static final String toHex(byte hash[]) {
if (hash == null) {
return null;
}
StringBuffer buf = new StringBuffer(hash.length * 2);
int i;
for (i = 0; i < hash.length; i++)
{
if ((hash[i] & 0xff) < 0x10)
{
for (i = 0; i < hash.length; i++) {
if ((hash[i] & 0xff) < 0x10) {
buf.append("0");
}
buf.append(Long.toString(hash[i] & 0xff, 16));
@ -94,15 +83,11 @@ public class Md5Utils
return buf.toString();
}
public static String hash(String s)
{
try
{
return new String(toHex(md5(Constants.PASS_PREFIX +s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
}
catch (Exception e)
{
log.error("not supported charset...{}", e);
public static String hash(String s) {
try {
return new String(toHex(md5(Constants.PASS_PREFIX + s)).getBytes(StandardCharsets.UTF_8), StandardCharsets.UTF_8);
} catch (Exception e) {
log.error("not supported charset...", e);
return s;
}
}

View File

@ -16,13 +16,22 @@
<dependencies>
<!--<dependency>
<groupId>org.apache.rocketmq</groupId>
<artifactId>rocketmq-spring-boot-starter</artifactId>
<version>2.4.10</version> &lt;!&ndash; 请检查最新版本 &ndash;&gt;
</dependency>-->
<!-- spring-boot-devtools -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional> <!-- 表示依赖不会传递 -->
</dependency>
<!-- Jackson XML 数据格式支持 -->
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
</dependency>
<dependency>
<groupId>org.springframework</groupId>

View File

@ -10,6 +10,7 @@ import com.ff.base.core.domain.AjaxResult;
import com.ff.base.core.page.TableDataInfo;
import com.ff.base.enums.OperationType;
import com.ff.base.enums.QuotaType;
import com.ff.base.exception.base.BaseException;
import com.ff.base.system.domain.TenantPlatform;
import com.ff.base.system.dto.CreateTenantDTO;
import com.ff.base.system.dto.TenantSecretKeyDTO;
@ -22,10 +23,12 @@ import com.ff.base.utils.ip.IpUtils;
import com.ff.base.system.domain.TenantSecretKey;
import com.ff.common.domain.TenantAgentPlatform;
import com.ff.common.dto.BalanceChangesDTO;
import com.ff.common.service.ITenantAgentPlatformService;
import com.ff.common.service.ITenantGameQuotaService;
import com.ff.base.system.service.ITenantSecretKeyService;
import com.ff.base.system.service.ITenantPlatformService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
@ -56,7 +59,8 @@ public class AgentController extends BaseController {
@Resource
private ITenantPlatformService tenantPlatformService;
@Resource
private ITenantAgentPlatformService tenantAgentPlatformService;
/**
*
*
@ -99,6 +103,11 @@ public class AgentController extends BaseController {
//构造租户的充值信息
List<TenantPlatform> tenantPlatforms = new ArrayList<>();
for (TenantAgentPlatform tenantAgentPlatform : agentCreateTenantDTO.getTenantAgentPlatforms()) {
TenantAgentPlatform agentPlatform = tenantAgentPlatformService.selectTenantAgentPlatformById(tenantAgentPlatform.getId());
// 成本比平台成本大
if (agentPlatform.getCost().compareTo(tenantAgentPlatform.getCost())>0){
throw new BaseException("成本比例不允许比最低比例小");
}
TenantPlatform tenantPlatform = new TenantPlatform();
BeanUtils.copyProperties(tenantAgentPlatform, tenantPlatform);
tenantPlatforms.add(tenantPlatform);

View File

@ -1,7 +1,7 @@
package com.ff.api.controller;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.IdUtil;
import com.ff.annotation.CheckHeader;
import com.ff.api.request.*;
import com.ff.api.response.*;
@ -11,27 +11,29 @@ import com.ff.base.core.domain.AjaxResult;
import com.ff.base.core.page.TableDataInfo;
import com.ff.base.enums.*;
import com.ff.base.exception.base.ApiException;
import com.ff.base.exception.base.BaseException;
import com.ff.base.manager.AsyncManager;
import com.ff.base.system.domain.TenantSecretKey;
import com.ff.base.utils.StringUtils;
import com.ff.base.utils.bean.BeanUtils;
import com.ff.base.system.domain.TenantSecretKey;
import com.ff.common.dto.GameBalanceExchange;
import com.ff.common.service.ITenantGameQuotaFlowService;
import com.ff.common.service.ITenantGameQuotaService;
import com.ff.config.KeyConfig;
import com.ff.game.api.IGamesService;
import com.ff.game.api.exchange.StepProcessorFactory;
import com.ff.game.api.exchange.dto.GameExchangeDTO;
import com.ff.game.api.request.*;
import com.ff.game.domain.*;
import com.ff.game.dto.GameBettingDetailsDTO;
import com.ff.game.dto.GameSecretKeyCurrencyDTO;
import com.ff.game.dto.GameSecretKeyLangDTO;
import com.ff.game.service.*;
import com.ff.member.domain.Member;
import com.ff.member.service.IMemberService;
import com.github.pagehelper.PageHelper;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.http.ResponseEntity;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;
@ -42,13 +44,14 @@ import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Future;
import java.util.stream.Collectors;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
/**
* api
@ -74,22 +77,12 @@ public class ApiGameController extends BaseController {
@Resource
private KeyConfig keyConfig;
@Resource
private IGameSecretKeyService gameSecretKeyService;
@Resource
private IMemberService memberService;
@Resource
private IGamePlatformService gamePlatformService;
@Resource
private ITenantGameQuotaService tenantGameQuotaService;
@Resource
private ITenantGameQuotaFlowService tenantGameQuotaFlowService;
@Resource
private IGameBettingDetailsService gameBettingDetailsService;
@ -98,17 +91,16 @@ public class ApiGameController extends BaseController {
@Resource
private IGameExchangeMoneyService gameExchangeMoneyService;
@Resource
private IGameSecretKeyCurrencyService gameSecretKeyCurrencyService;
@Resource
private IGameSecretKeyLangService gameSecretKeyLangService;
@Autowired
@Qualifier("threadPoolTaskExecutor")
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Resource
private IPlatformService platformService;
@Resource
private StepProcessorFactory stepProcessorFactory;
/**
*
@ -118,11 +110,15 @@ public class ApiGameController extends BaseController {
@PostMapping("/list")
public AjaxResult list() {
List<GameResponse> gameResponses = gameService.selectGameResponseList();
for (GameResponse gameRespons : gameResponses) {
for (GameResponse gameResponse : gameResponses) {
Platform platform = platformService.get(gameResponse.getPlatformCode());
if (null == platform) {
continue;
}
List<String> currencyCode = new ArrayList<>(platform.getCurrencyInfo().keySet());
gameResponse.setCurrencyCode(currencyCode);
List<GameSecretKeyCurrencyDTO> gameSecretKeyCurrencies = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTOList(GameSecretKeyCurrencyDTO.builder().platformCode(gameRespons.getPlatformCode()).build());
List<String> currencyCode = gameSecretKeyCurrencies.stream().map(GameSecretKeyCurrencyDTO::getSystemCurrency).collect(Collectors.toList());
gameRespons.setCurrencyCode(currencyCode);
}
return AjaxResult.success(gameResponses);
}
@ -131,49 +127,59 @@ public class ApiGameController extends BaseController {
/**
*
*
* @param memberCreateApiRequest api
* @param loginRequest
* @return {@link AjaxResult }
*/
@PostMapping("/login")
public AjaxResult login(@Validated @RequestBody GameLoginRequest memberCreateApiRequest) {
public AjaxResult login(@Validated @RequestBody GameLoginRequest loginRequest) {
Game game = gameService.selectGameById(memberCreateApiRequest.getGameId());
Game game = gameService.selectGameByGameId(loginRequest.getGameId());
ApiException.notNull(game, ErrorCode.GAME_NOT_EXIST.getCode());
GamePlatform gamePlatform = gamePlatformService.selectGamePlatformById(game.getPlatformId());
ApiException.notNull(gamePlatform, ErrorCode.PLATFORM_NOT_EXIST.getCode());
Platform platform = platformService.get(game.getPlatformCode());
ApiException.notNull(platform, ErrorCode.PLATFORM_NOT_EXIST.getCode());
IGamesService iGamesService = gamesService.get(platform.getPlatformCode() + Constants.SERVICE);
ApiException.notNull(iGamesService, ErrorCode.PLATFORM_NOT_EXIST.getCode());
String targetCurrency = platform.getCurrencyInfo().get(loginRequest.getCurrencyCode());
ApiException.notNull(targetCurrency, ErrorCode.CURRENCY_NOT_EXIST.getCode());
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(loginRequest.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(loginRequest.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
ApiException.notNull(keyInfo, ErrorCode.CURRENCY_NOT_EXIST.getCode());
String targetLang = platform.getLangInfo().get(loginRequest.getLangCode());
ApiException.notNull(targetLang, ErrorCode.LANG_NOT_EXIST.getCode());
IGamesService iGamesService = gamesService.get(gamePlatform.getPlatformCode() + Constants.SERVICE);
ApiException.isTrue(game.getStopStatus(), ErrorCode.GAME_NOT_EXIST.getCode());
TenantSecretKey tenantSecretKey = keyConfig.get();
GameSecretKeyCurrencyDTO secretKeyCurrencyDTO = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
.platformCode(gamePlatform.getPlatformCode())
.systemCurrency(memberCreateApiRequest.getCurrencyCode()).build());
ApiException.notNull(secretKeyCurrencyDTO, ErrorCode.CURRENCY_NOT_EXIST.getCode());
GameSecretKeyLangDTO gameSecretKeyLangDTO = gameSecretKeyLangService.findGameSecretKeyLangDTO(GameSecretKeyLangDTO.builder()
.platformCode(gamePlatform.getPlatformCode())
.systemLangCode(memberCreateApiRequest.getLangCode())
.build());
ApiException.notNull(gameSecretKeyLangDTO, ErrorCode.LANG_NOT_EXIST.getCode());
Member member = memberService.selectMemberByAccount(memberCreateApiRequest.getAccount(), memberCreateApiRequest.getCurrencyCode(), gamePlatform.getPlatformCode());
Member member = memberService.selectMemberByAccount(loginRequest.getAccount(), loginRequest.getCurrencyCode(), platform.getPlatformCode());
ApiException.notNull(member, ErrorCode.ACCOUNT_NOT_EXIST.getCode());
GamesLogin gamesLogin = GamesLogin.builder()
.agentId(secretKeyCurrencyDTO.getCode())
.agentKey(secretKeyCurrencyDTO.getKey())
.agentId(keyInfo.getCode())
.agentKey(keyInfo.getKey())
.account(member.getGameAccount())
.gameType(game.getGameSourceType())
.currency(secretKeyCurrencyDTO.getCurrency())
.currency(/*secretKeyCurrencyDTO.getCurrency()*/targetCurrency)
.gameId(game.getGameCode())
.homeUrl(memberCreateApiRequest.getHomeUrl())
.platform(memberCreateApiRequest.getPlatform())
.disableFullScreen(memberCreateApiRequest.getDisableFullScreen())
.lang(gameSecretKeyLangDTO.getLang())
.homeUrl(loginRequest.getHomeUrl())
.betLimit(loginRequest.getBetLimit())
.platform(loginRequest.getPlatform())
.disableFullScreen(loginRequest.getDisableFullScreen())
.lang(/*gameSecretKeyLangDTO.getLang()*/ targetLang)
.vendor(platform)
.keyInfo(keyInfo)
.systemCurrency(loginRequest.getCurrencyCode())
.build();
String login = iGamesService.loginWithoutRedirect(gamesLogin);
@ -189,53 +195,101 @@ public class ApiGameController extends BaseController {
* @return {@link AjaxResult }
*/
@PostMapping("/exchange/balance")
@Transactional
public AjaxResult exchangeBalance(@Validated @RequestBody GameExchangeBalanceRequest gameExchangeBalanceRequest) {
public DeferredResult<AjaxResult> exchangeBalance(@Validated @RequestBody GameExchangeBalanceRequest gameExchangeBalanceRequest) {
IGamesService iGamesService = gamesService.get(gameExchangeBalanceRequest.getPlatformCode() + Constants.SERVICE);
ApiException.notNull(iGamesService, ErrorCode.PLATFORM_NOT_EXIST.getCode());
TenantSecretKey tenantSecretKey = keyConfig.get();
GameSecretKeyCurrencyDTO gameSecretKey = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
.platformCode(gameExchangeBalanceRequest.getPlatformCode())
.systemCurrency(gameExchangeBalanceRequest.getCurrencyCode()).build());
ApiException.notNull(gameSecretKey, ErrorCode.CURRENCY_NOT_EXIST.getCode());
Platform platform = platformService.get(gameExchangeBalanceRequest.getPlatformCode());
ApiException.notNull(platform, ErrorCode.PLATFORM_NOT_EXIST.getCode());
BigDecimal quota = tenantGameQuotaService.gameBalanceExchange(GameBalanceExchange.builder()
.platformCode(gameExchangeBalanceRequest.getPlatformCode())
.sourceId(gameExchangeBalanceRequest.getOrderId())
.currencyCode(gameExchangeBalanceRequest.getCurrencyCode())
.transferType(gameExchangeBalanceRequest.getTransferType())
.amount(gameExchangeBalanceRequest.getAmount())
.account(gameExchangeBalanceRequest.getAccount())
.tenantKey(tenantSecretKey.getTenantKey())
.build());
String targetCurrency = platform.getCurrencyInfo().get(gameExchangeBalanceRequest.getCurrencyCode());
ApiException.notNull(targetCurrency, ErrorCode.CURRENCY_NOT_EXIST.getCode());
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(gameExchangeBalanceRequest.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(gameExchangeBalanceRequest.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
ApiException.notNull(keyInfo, ErrorCode.CURRENCY_NOT_EXIST.getCode());
// 获取用户信息
Member member = memberService.selectMemberByAccount(gameExchangeBalanceRequest.getAccount(), gameExchangeBalanceRequest.getCurrencyCode(), gameExchangeBalanceRequest.getPlatformCode());
ApiException.notNull(member, ErrorCode.ACCOUNT_NOT_EXIST.getCode());
List<GameExchangeMoney> gameExchangeMonies = gameExchangeMoneyService.selectGameExchangeMoneyList(
GameExchangeMoney.builder()
.tenantKey(tenantSecretKey.getTenantKey())
.orderId(gameExchangeBalanceRequest.getOrderId())
.build()
);
//操作第三方额度接口
ExchangeTransferMoneyRequestDTO exchangeTransferMoneyRequestDTO = ExchangeTransferMoneyRequestDTO.builder()
.agentId(gameSecretKey.getCode())
.agentKey(gameSecretKey.getKey())
.orderId(gameExchangeBalanceRequest.getOrderId())
.account(member.getGameAccount())
.currency(gameSecretKey.getCurrency())
ApiException.isTrue(CollectionUtils.isEmpty(gameExchangeMonies), ErrorCode.DUPLICATE_ORDER_ID.getCode());
Long gameExchangeMoneyId = IdUtil.getSnowflakeNextId();
GameExchangeDTO exchangeMoney = GameExchangeDTO.builder()
.id(gameExchangeMoneyId)
.tenantKey(tenantSecretKey.getTenantKey())
.quota(quota)
.amount(gameExchangeBalanceRequest.getAmount())
.transferType(gameExchangeBalanceRequest.getTransferType())
.build();
Long exchangeTransferId = iGamesService.exchangeTransferByAgentId(exchangeTransferMoneyRequestDTO);
GameExchangeMoney gameExchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(exchangeTransferId);
GameExchangeBalanceResponse gameExchangeBalanceResponse = new GameExchangeBalanceResponse();
BeanUtils.copyProperties(gameExchangeMoney, gameExchangeBalanceResponse);
return AjaxResult.success(gameExchangeBalanceResponse);
.memberId(member.getId())
.gameAccount(member.getGameAccount())
.memberAccount(member.getMemberAccount())
.exchangeType(gameExchangeBalanceRequest.getTransferType())
.currencyCode(gameExchangeBalanceRequest.getCurrencyCode())
.orderId(gameExchangeBalanceRequest.getOrderId())
.balance(gameExchangeBalanceRequest.getAmount())
.triggerType(TriggerType.MANUAL.getCode())
.platformCode(gameExchangeBalanceRequest.getPlatformCode()).build();
//转出设置转出金额为0
if (TransferType.ALL.getCode().equals(gameExchangeBalanceRequest.getTransferType())){
exchangeMoney.setBalance(BigDecimal.ZERO);
}
ExtInfo extInfo = platform.getExtInfo();
Long timeout = 5000L;
if (extInfo != null) {
timeout = extInfo.getTimeout(TimeOutType.GAME_EXCHANGE_MONEY.getCode());
}
DeferredResult<AjaxResult> output = new DeferredResult<>(timeout);
AsyncManager.me().executeOrdered(
exchangeMoney.getOrderId(),
() -> {
try {
stepProcessorFactory.getStepProcessor(GameExchangeStep.CREATE_ORDER).process(exchangeMoney);
GameExchangeMoney gameExchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(gameExchangeMoneyId);
GameExchangeBalanceResponse gameExchangeBalanceResponse = new GameExchangeBalanceResponse();
BeanUtils.copyProperties(gameExchangeMoney, gameExchangeBalanceResponse);
output.setResult(AjaxResult.success(gameExchangeBalanceResponse));
} catch (Exception e) {
log.error("ApiGameController [exchangeBalance] 余额转移失败 gameExchangeMoneyId {}", gameExchangeMoneyId,e);
stepProcessorFactory.getStepProcessor(GameExchangeStep.getByCode(exchangeMoney.getStep())).rollBack(exchangeMoney);
output.setErrorResult(AjaxResult.error(ErrorCode.BALANCE_TRANSFER_FAILED.getCode(), ErrorCode.BALANCE_TRANSFER_FAILED.getMessage()));
}
}
);
// 超时时间处理逻辑
output.onTimeout(() -> {
GameExchangeMoney gameExchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(gameExchangeMoneyId);
GameExchangeBalanceResponse gameExchangeBalanceResponse = new GameExchangeBalanceResponse();
BeanUtils.copyProperties(gameExchangeMoney, gameExchangeBalanceResponse);
output.setErrorResult(AjaxResult.success(gameExchangeBalanceResponse));
});
return output;
}
@ -275,12 +329,22 @@ public class ApiGameController extends BaseController {
ApiException.notNull(iGamesService, ErrorCode.PLATFORM_NOT_EXIST.getCode());
TenantSecretKey tenantSecretKey = keyConfig.get();
GameSecretKeyCurrencyDTO gameSecretKey = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
.platformCode(gameCreateFreeSpinRequest.getPlatformCode())
.systemCurrency(gameCreateFreeSpinRequest.getCurrencyCode()).build());
ApiException.notNull(gameSecretKey, ErrorCode.CURRENCY_NOT_EXIST.getCode());
Platform platform = platformService.get(gameCreateFreeSpinRequest.getPlatformCode());
ApiException.notNull(platform, ErrorCode.PLATFORM_NOT_EXIST.getCode());
String targetCurrency = platform.getCurrencyInfo().get(gameCreateFreeSpinRequest.getCurrencyCode());
ApiException.notNull(targetCurrency, ErrorCode.CURRENCY_NOT_EXIST.getCode());
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(gameCreateFreeSpinRequest.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(gameCreateFreeSpinRequest.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
ApiException.notNull(keyInfo, ErrorCode.CURRENCY_NOT_EXIST.getCode());
Member member = memberService.selectMemberByAccount(gameCreateFreeSpinRequest.getAccount(), gameCreateFreeSpinRequest.getCurrencyCode(), gameCreateFreeSpinRequest.getPlatformCode());
ApiException.notNull(member, ErrorCode.ACCOUNT_NOT_EXIST.getCode());
@ -288,15 +352,18 @@ public class ApiGameController extends BaseController {
CreateFreeSpinRequestDTO createFreeSpinRequestDTO = CreateFreeSpinRequestDTO.builder()
.account(member.getGameAccount())
.currency(gameCreateFreeSpinRequest.getCurrencyCode())
.agentId(gameSecretKey.getCode())
.agentKey(gameSecretKey.getKey())
.currency(/*gameCreateFreeSpinRequest.getCurrencyCode()*/targetCurrency)
.agentId(keyInfo.getCode())
.agentKey(keyInfo.getKey())
.referenceId(gameCreateFreeSpinRequest.getReferenceId())
.freeSpinValidity(gameCreateFreeSpinRequest.getFreeSpinValidity())
.numberOfRounds(gameCreateFreeSpinRequest.getNumberOfRounds())
.gameIds(gameCreateFreeSpinRequest.getGameIds())
.betValue(gameCreateFreeSpinRequest.getBetValue())
.startTime(gameCreateFreeSpinRequest.getStartTime())
.vendor(platform)
.keyInfo(keyInfo)
.systemCurrency(gameCreateFreeSpinRequest.getCurrencyCode())
.build();
@ -349,32 +416,53 @@ public class ApiGameController extends BaseController {
*/
@PostMapping("/get/detail")
public AjaxResult getDetail(@Validated @RequestBody GameGetDetailRequest gameGetDetailRequest) {
Platform platform = platformService.get(gameGetDetailRequest.getPlatformCode());
ApiException.notNull(platform, ErrorCode.PLATFORM_NOT_EXIST.getCode());
String targetCurrency = platform.getCurrencyInfo().get(gameGetDetailRequest.getCurrencyCode());
ApiException.notNull(targetCurrency, ErrorCode.CURRENCY_NOT_EXIST.getCode());
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(gameGetDetailRequest.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(gameGetDetailRequest.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
ApiException.notNull(keyInfo, ErrorCode.CURRENCY_NOT_EXIST.getCode());
String targetLang = platform.getLangInfo().get(gameGetDetailRequest.getLangCode());
ApiException.notNull(targetLang, ErrorCode.LANG_NOT_EXIST.getCode());
// GameSecretKeyCurrencyDTO gameSecretKey = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
// .platformCode(gameGetDetailRequest.getPlatformCode())
// .systemCurrency(gameGetDetailRequest.getCurrencyCode()).build());
GameSecretKeyCurrencyDTO gameSecretKey = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
.platformCode(gameGetDetailRequest.getPlatformCode())
.systemCurrency(gameGetDetailRequest.getCurrencyCode()).build());
ApiException.notNull(gameSecretKey, ErrorCode.CURRENCY_NOT_EXIST.getCode());
// ApiException.notNull(gameSecretKey, ErrorCode.CURRENCY_NOT_EXIST.getCode());
IGamesService iGamesService = gamesService.get(gameGetDetailRequest.getPlatformCode() + Constants.SERVICE);
ApiException.notNull(iGamesService, ErrorCode.PLATFORM_NOT_EXIST.getCode());
GameSecretKeyLangDTO gameSecretKeyLang = gameSecretKeyLangService.findGameSecretKeyLangDTO(GameSecretKeyLangDTO.builder()
.platformCode(gameGetDetailRequest.getPlatformCode())
.systemLangCode(gameGetDetailRequest.getLangCode())
.build());
ApiException.notNull(gameSecretKeyLang, ErrorCode.LANG_NOT_EXIST.getCode());
// GameSecretKeyLangDTO gameSecretKeyLang = gameSecretKeyLangService.findGameSecretKeyLangDTO(GameSecretKeyLangDTO.builder()
// .platformCode(gameGetDetailRequest.getPlatformCode())
// .systemLangCode(gameGetDetailRequest.getLangCode())
// .build());
// ApiException.notNull(gameSecretKeyLang, ErrorCode.LANG_NOT_EXIST.getCode());
GetGameDetailResponseDTO gameDetail = iGamesService.getGameDetail(GetGameDetailRequestDTO.builder()
.wagersId(gameGetDetailRequest.getWagersId())
.lang(gameSecretKeyLang.getLang())
.agentId(gameSecretKey.getCode())
.agentKey(gameSecretKey.getKey())
.lang(/*gameSecretKeyLang.getLang()*/targetLang)
.agentId(keyInfo.getCode())
.agentKey(keyInfo.getKey())
.vendor(platform)
.keyInfo(keyInfo)
.systemCurrency(gameGetDetailRequest.getCurrencyCode())
.build());
return AjaxResult.success(gameDetail);
}
@ -387,45 +475,82 @@ public class ApiGameController extends BaseController {
*/
@PostMapping("/kick/member")
public AjaxResult kickMember(@Validated @RequestBody GameKickMemeberRequest gameKickMemeberRequest) {
GameSecretKeyCurrencyDTO gameSecretKey = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
.platformCode(gameKickMemeberRequest.getPlatformCode())
.systemCurrency(gameKickMemeberRequest.getCurrencyCode()).build());
ApiException.notNull(gameSecretKey, ErrorCode.CURRENCY_NOT_EXIST.getCode());
// GameSecretKeyCurrencyDTO gameSecretKey = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
// .platformCode(gameKickMemeberRequest.getPlatformCode())
// .systemCurrency(gameKickMemeberRequest.getCurrencyCode()).build());
// ApiException.notNull(gameSecretKey, ErrorCode.CURRENCY_NOT_EXIST.getCode());
Platform platform = platformService.get(gameKickMemeberRequest.getPlatformCode());
ApiException.notNull(platform, ErrorCode.PLATFORM_NOT_EXIST.getCode());
Member member = memberService.selectMemberByAccount(gameKickMemeberRequest.getAccount(), gameKickMemeberRequest.getCurrencyCode(), gameKickMemeberRequest.getPlatformCode());
ApiException.notNull(member, ErrorCode.ACCOUNT_NOT_EXIST.getCode());
String targetCurrency = platform.getCurrencyInfo().get(gameKickMemeberRequest.getCurrencyCode());
ApiException.notNull(targetCurrency, ErrorCode.CURRENCY_NOT_EXIST.getCode());
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(gameKickMemeberRequest.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(gameKickMemeberRequest.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
ApiException.notNull(keyInfo, ErrorCode.CURRENCY_NOT_EXIST.getCode());
IGamesService iGamesService = gamesService.get(gameKickMemeberRequest.getPlatformCode() + Constants.SERVICE);
ApiException.notNull(iGamesService, ErrorCode.PLATFORM_NOT_EXIST.getCode());
Member member = memberService.selectMemberByAccount(gameKickMemeberRequest.getAccount(), gameKickMemeberRequest.getCurrencyCode(), gameKickMemeberRequest.getPlatformCode());
ApiException.notNull(member, ErrorCode.ACCOUNT_NOT_EXIST.getCode());
return AjaxResult.success(iGamesService.kickMember(KickMemberRequestDTO.builder()
.account(member.getGameAccount())
.agentId(gameSecretKey.getCode())
.currency(gameSecretKey.getCurrency())
.agentKey(gameSecretKey.getKey())
.agentId(keyInfo.getCode())
.currency(targetCurrency)
.agentKey(keyInfo.getKey())
.vendor(platform)
.keyInfo(keyInfo)
.systemCurrency(gameKickMemeberRequest.getCurrencyCode())
.build()));
}
@PostMapping("/kick/member/all")
public AjaxResult kickMemberAll(@Validated @RequestBody GameKickMemeberAllRequest gameKickMemeberAllRequest) {
GameSecretKeyCurrencyDTO gameSecretKey = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
.platformCode(gameKickMemeberAllRequest.getPlatformCode())
.systemCurrency(gameKickMemeberAllRequest.getCurrencyCode()).build());
// GameSecretKeyCurrencyDTO gameSecretKey = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
// .platformCode(gameKickMemeberAllRequest.getPlatformCode())
// .systemCurrency(gameKickMemeberAllRequest.getCurrencyCode()).build());
//
// ApiException.notNull(gameSecretKey, ErrorCode.CURRENCY_NOT_EXIST.getCode());
Platform platform = platformService.get(gameKickMemeberAllRequest.getPlatformCode());
ApiException.notNull(platform, ErrorCode.PLATFORM_NOT_EXIST.getCode());
ApiException.notNull(gameSecretKey, ErrorCode.CURRENCY_NOT_EXIST.getCode());
String targetCurrency = platform.getCurrencyInfo().get(gameKickMemeberAllRequest.getCurrencyCode());
ApiException.notNull(targetCurrency, ErrorCode.CURRENCY_NOT_EXIST.getCode());
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(gameKickMemeberAllRequest.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(gameKickMemeberAllRequest.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
ApiException.notNull(keyInfo, ErrorCode.CURRENCY_NOT_EXIST.getCode());
IGamesService iGamesService = gamesService.get(gameKickMemeberAllRequest.getPlatformCode() + Constants.SERVICE);
ApiException.notNull(iGamesService, ErrorCode.PLATFORM_NOT_EXIST.getCode());
KickMemberAllDTO kickMemberAllDTO = KickMemberAllDTO.builder()
.agentId(gameSecretKey.getCode())
.agentKey(gameSecretKey.getKey())
.currency(gameSecretKey.getCurrency())
.agentId(keyInfo.getCode())
.agentKey(keyInfo.getKey())
.currency(/*gameSecretKey.getCurrency()*/targetCurrency)
.vendor(platform)
.keyInfo(keyInfo)
.systemCurrency(gameKickMemeberAllRequest.getCurrencyCode())
.build();
if (!ObjectUtils.isEmpty(gameKickMemeberAllRequest.getGameId())) {
Game game = gameService.selectGameById(gameKickMemeberAllRequest.getGameId());
@ -474,23 +599,43 @@ public class ApiGameController extends BaseController {
/**
*
*
* @param gameGetFreeSpinDashflowRequest dashflow
* @param request dashflow
* @return {@link TableDataInfo }
*/
@PostMapping("/cancel/free/spin")
public AjaxResult cancelFreeSpin(@Validated @RequestBody GameCancelFreeSpinRequest gameGetFreeSpinDashflowRequest) {
GameSecretKeyCurrencyDTO gameSecretKey = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
.platformCode(gameGetFreeSpinDashflowRequest.getPlatformCode())
.systemCurrency(gameGetFreeSpinDashflowRequest.getCurrencyCode()).build());
ApiException.notNull(gameSecretKey, ErrorCode.CURRENCY_NOT_EXIST.getCode());
public AjaxResult cancelFreeSpin(@Validated @RequestBody GameCancelFreeSpinRequest request) {
// GameSecretKeyCurrencyDTO gameSecretKey = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
// .platformCode(gameGetFreeSpinDashflowRequest.getPlatformCode())
// .systemCurrency(gameGetFreeSpinDashflowRequest.getCurrencyCode()).build());
// ApiException.notNull(gameSecretKey, ErrorCode.CURRENCY_NOT_EXIST.getCode());
IGamesService iGamesService = gamesService.get(gameGetFreeSpinDashflowRequest.getPlatformCode() + Constants.SERVICE);
Platform platform = platformService.get(request.getPlatformCode());
ApiException.notNull(platform, ErrorCode.PLATFORM_NOT_EXIST.getCode());
String targetCurrency = platform.getCurrencyInfo().get(request.getCurrencyCode());
ApiException.notNull(targetCurrency, ErrorCode.CURRENCY_NOT_EXIST.getCode());
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(request.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(request.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
ApiException.notNull(keyInfo, ErrorCode.CURRENCY_NOT_EXIST.getCode());
IGamesService iGamesService = gamesService.get(request.getPlatformCode() + Constants.SERVICE);
ApiException.notNull(iGamesService, ErrorCode.PLATFORM_NOT_EXIST.getCode());
Boolean cancelFreeSpin = iGamesService.cancelFreeSpin(CancelFreeSpinRequestDTO.builder()
.agentId(gameSecretKey.getCode())
.agentKey(gameSecretKey.getKey())
.referenceId(gameGetFreeSpinDashflowRequest.getReferenceId())
.agentId(keyInfo.getCode())
.agentKey(keyInfo.getKey())
.referenceId(request.getReferenceId())
.vendor(platform)
.keyInfo(keyInfo)
.systemCurrency(request.getCurrencyCode())
.build());
return AjaxResult.success(cancelFreeSpin);
}
@ -498,45 +643,80 @@ public class ApiGameController extends BaseController {
/**
*
*
* @param gameExchangeBalanceAllRequest api
* @param request api
* @return {@link AjaxResult }
*/
*//*
@PostMapping("/exchange/balance/all")
public AjaxResult exchangeBalanceAll(@Validated @RequestBody GameExchangeBalanceAllRequest gameExchangeBalanceAllRequest) {
public AjaxResult exchangeBalanceAll(@Validated @RequestBody GameExchangeBalanceAllRequest request) {
TenantSecretKey tenantSecretKey = keyConfig.get();
// List<GameSecretKeyCurrencyDTO> gameSecretKeys = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTOList(GameSecretKeyCurrencyDTO.builder()
// .systemCurrency(gameExchangeBalanceAllRequest.getCurrencyCode()).build());
List<GameSecretKeyCurrencyDTO> gameSecretKeys = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTOList(GameSecretKeyCurrencyDTO.builder()
.systemCurrency(gameExchangeBalanceAllRequest.getCurrencyCode()).build());
List<Key> keys = new ArrayList<>();
for (GamePlatforms platformEnum : GamePlatforms.values()) {
Platform platform = platformService.get(platformEnum.getCode());
String targetCurrency = platform.getCurrencyInfo().get(request.getCurrencyCode());
if (StringUtils.isEmpty(targetCurrency)) {
continue;
}
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(request.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(request.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
if (null == keyInfo) {
continue;
}
Key key = new Key();
key.setPlatformCode(platform.getPlatformCode());
key.setCode(keyInfo.getCode());
key.setKey(keyInfo.getKey());
key.setCurrency(targetCurrency);
key.setPlatform(platform);
key.setKeyInfo(keyInfo);
key.setSystemCurrency(request.getCurrencyCode());
keys.add(key);
}
// 创建线程池
Map<String, BigDecimal> balanceMap = new LinkedHashMap<>();
CountDownLatch latch = new CountDownLatch(gameSecretKeys.size());
CountDownLatch latch = new CountDownLatch(keys.size());
// 使用List存储Future对象用于获取异步执行的结果
List<Future<Long>> futures = new ArrayList<>();
// 提交异步任务到线程池
for (GameSecretKeyCurrencyDTO gameSecretKeyCurrencyDTO : gameSecretKeys) {
// for (GameSecretKeyCurrencyDTO gameSecretKeyCurrencyDTO : gameSecretKeys) {
for (Key key : keys) {
futures.add(threadPoolTaskExecutor.submit(() -> {
try {
IGamesService iGamesService = gamesService.get(gameSecretKeyCurrencyDTO.getPlatformCode() + Constants.SERVICE);
IGamesService iGamesService = gamesService.get(key.getPlatformCode() + Constants.SERVICE);
Member member = memberService.selectMemberByAccount(gameExchangeBalanceAllRequest.getAccount(), gameExchangeBalanceAllRequest.getCurrencyCode(), gameSecretKeyCurrencyDTO.getPlatformCode());
Member member = memberService.selectMemberByAccount(request.getAccount(), request.getCurrencyCode(), key.getPlatformCode());
ApiException.notNull(member, ErrorCode.ACCOUNT_NOT_EXIST.getCode());
//操作第三方钱包
ExchangeTransferMoneyRequestDTO exchangeTransferMoneyRequestDTO = ExchangeTransferMoneyRequestDTO.builder()
.agentId(gameSecretKeyCurrencyDTO.getCode())
.agentKey(gameSecretKeyCurrencyDTO.getKey())
.orderId(gameExchangeBalanceAllRequest.getOrderId())
.agentId(key.getCode())
.agentKey(key.getKey())
.orderId(request.getOrderId())
.amount(BigDecimal.ONE)
.currency(gameSecretKeyCurrencyDTO.getCurrency())
.currency(*//*gameSecretKeyCurrencyDTO.getCurrency()*//*key.currency)
.tenantKey(tenantSecretKey.getTenantKey())
.account(member.getGameAccount())
.vendor(key.getPlatform())
.keyInfo(key.getKeyInfo())
.systemCurrency(key.systemCurrency)
.transferType(TransferType.ALL.getCode())
.build();
return iGamesService.exchangeTransferByAgentId(exchangeTransferMoneyRequestDTO);
@ -571,11 +751,15 @@ public class ApiGameController extends BaseController {
tenantGameQuotaService.gameBalanceExchange(GameBalanceExchange.builder()
.platformCode(gameExchangeMoney.getPlatformCode())
.currencyCode(gameExchangeMoney.getCurrencyCode())
.sourceId(gameExchangeBalanceAllRequest.getOrderId())
.sourceId(request.getOrderId())
.transferType(TransferType.ALL.getCode())
.amount(gameExchangeMoney.getBalance())
.account(member.getMemberAccount())
.tenantKey(tenantSecretKey.getTenantKey())
.currency()
.systemCurrency(gameExchangeMoney.getCurrencyCode())
.agentId()
.agentKey()
.build());
}
@ -587,5 +771,77 @@ public class ApiGameController extends BaseController {
return AjaxResult.success(balanceMap);
}*/
/**
*
*
* @param request
* @return {@link AjaxResult }
*/
@PostMapping("/demo/login")
public AjaxResult demoLogin(@Validated @RequestBody GameDemoLoginRequest request) {
Game game = gameService.selectGameById(request.getGameId());
ApiException.notNull(game, ErrorCode.GAME_NOT_EXIST.getCode());
//GamePlatform gamePlatform = gamePlatformService.selectGamePlatformById(game.getPlatformId());
//ApiException.notNull(gamePlatform, ErrorCode.PLATFORM_NOT_EXIST.getCode());
Platform platform = platformService.get(game.getPlatformCode());
ApiException.notNull(platform, ErrorCode.PLATFORM_NOT_EXIST.getCode());
IGamesService iGamesService = gamesService.get(platform.getPlatformCode() + Constants.SERVICE);
ApiException.notNull(iGamesService, ErrorCode.PLATFORM_NOT_EXIST.getCode());
String targetCurrency = platform.getCurrencyInfo().get(request.getCurrencyCode());
ApiException.notNull(targetCurrency, ErrorCode.CURRENCY_NOT_EXIST.getCode());
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(request.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(request.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
ApiException.notNull(keyInfo, ErrorCode.CURRENCY_NOT_EXIST.getCode());
String targetLang = platform.getLangInfo().get(request.getLangCode());
ApiException.notNull(targetLang, ErrorCode.LANG_NOT_EXIST.getCode());
// 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()*/targetLang)
.vendor(platform)
.keyInfo(keyInfo)
.systemCurrency(request.getCurrencyCode())
.build();
GameDemoLoginResponseDTO gameDemoLoginResponseDTO = iGamesService.gameDemoLogin(gamesLogin);
GameDemoLoginResponse gameDemoLoginResponse = new GameDemoLoginResponse();
BeanUtils.copyProperties(gameDemoLoginResponseDTO, gameDemoLoginResponse);
return AjaxResult.success(gameDemoLoginResponse);
}
@Data
class Key {
private String platformCode;
private String code;
private String currency;
private String key;
private Platform platform;
private KeyInfo keyInfo;
private String systemCurrency;
}
}

View File

@ -11,20 +11,22 @@ import com.ff.base.constant.Constants;
import com.ff.base.core.controller.BaseController;
import com.ff.base.core.domain.AjaxResult;
import com.ff.base.enums.ErrorCode;
import com.ff.base.enums.GamePlatforms;
import com.ff.base.exception.base.ApiException;
import com.ff.base.exception.base.BaseException;
import com.ff.base.utils.StringUtils;
import com.ff.base.system.domain.TenantSecretKey;
import com.ff.base.utils.StringUtils;
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;
import com.ff.game.service.IGameService;
import com.ff.game.api.request.CreateMemberRequestDTO;
import com.ff.game.api.request.MemberInfoRequestDTO;
import com.ff.game.api.request.MemberInfoResponseDTO;
import com.ff.game.domain.KeyInfo;
import com.ff.game.domain.Platform;
import com.ff.game.service.IPlatformService;
import com.ff.member.domain.Member;
import com.ff.member.service.IMemberService;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
@ -32,10 +34,12 @@ import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
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.*;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.math.BigDecimal;
@ -58,31 +62,21 @@ import java.util.concurrent.Future;
@Slf4j
public class ApiMemberController extends BaseController {
@Autowired
private Map<String, IGamesService> gamesService;
@Resource
private IGameService gameService;
@Resource
private KeyConfig keyConfig;
@Resource
private IGameSecretKeyService gameSecretKeyService;
@Resource
private IMemberService memberService;
@Autowired
@Qualifier("threadPoolTaskExecutor")
private ThreadPoolTaskExecutor threadPoolTaskExecutor;
@Resource
private IGameSecretKeyCurrencyService gameSecretKeyCurrencyService;
private IPlatformService platformService;
/**
*
@ -98,24 +92,33 @@ public class ApiMemberController extends BaseController {
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());
ApiException.notNull(gameSecretKey, ErrorCode.CURRENCY_NOT_EXIST.getCode());
Platform platform = platformService.get(memberCreateApiRequest.getPlatformCode());
ApiException.notNull(platform, ErrorCode.CURRENCY_NOT_EXIST.getCode());
String targetCurrency = platform.getCurrencyInfo().get(memberCreateApiRequest.getCurrencyCode());
ApiException.notNull(targetCurrency, ErrorCode.CURRENCY_NOT_EXIST.getCode());
String gameAccount = StringUtils.addSuffix(memberService.getMemberGameAccount(), tenantSecretKey.getTenantSn());
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(memberCreateApiRequest.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(memberCreateApiRequest.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
ApiException.notNull(keyInfo, ErrorCode.CURRENCY_NOT_EXIST.getCode());
String gameAccount =memberService.getMemberGameAccount(memberCreateApiRequest.getPlatformCode(), tenantSecretKey.getTenantSn());
// 获取用户信息
Member gameMember = memberService.selectMemberByAccount(memberCreateApiRequest.getAccount(), memberCreateApiRequest.getCurrencyCode(), memberCreateApiRequest.getPlatformCode());
if (!ObjectUtils.isEmpty(gameMember)){
if (!ObjectUtils.isEmpty(gameMember)) {
throw new ApiException(ErrorCode.GAME_ACCOUNT_CREATION_FAILED.getCode());
}
//注册本地账号
Member member = Member.builder()
.tenantKey(tenantSecretKey.getTenantKey())
@ -130,12 +133,21 @@ public class ApiMemberController extends BaseController {
//向第三方注册账号
CreateMemberRequestDTO gamesBaseRequestDTO = CreateMemberRequestDTO.builder()
.account(gameAccount)
.agentId(gameSecretKey.getCode())
.agentKey(gameSecretKey.getKey())
.currency(gameSecretKey.getCurrency())
.agentId(keyInfo.getCode())
.agentKey(keyInfo.getKey())
.betLimit(memberCreateApiRequest.getBetLimit())
.platformType(memberCreateApiRequest.getPlatformType())
.currency(targetCurrency)
.vendor(platform)
.keyInfo(keyInfo)
.systemCurrency(memberCreateApiRequest.getCurrencyCode())
.build();
Boolean result = iGamesService.createMember(gamesBaseRequestDTO);
Assert.isTrue(result, "建立游戏账号失败");
return toAjax(Boolean.TRUE);
}
@ -144,35 +156,46 @@ public class ApiMemberController extends BaseController {
/**
*
*
* @param memberInfoApiRequest api
* @param request api
* @return {@link AjaxResult }
*/
@PostMapping("/info")
public AjaxResult getMemberInfo(@Validated @RequestBody MemberInfoApiRequest memberInfoApiRequest) {
IGamesService iGamesService = gamesService.get(memberInfoApiRequest.getPlatformCode() + Constants.SERVICE);
public AjaxResult getMemberInfo(@Validated @RequestBody MemberInfoApiRequest request) {
IGamesService iGamesService = gamesService.get(request.getPlatformCode() + Constants.SERVICE);
ApiException.notNull(iGamesService, ErrorCode.PLATFORM_NOT_EXIST.getCode());
TenantSecretKey tenantSecretKey = keyConfig.get();
GameSecretKeyCurrencyDTO gameSecretKey = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
.platformCode(memberInfoApiRequest.getPlatformCode())
.systemCurrency(memberInfoApiRequest.getCurrencyCode()).build());
Platform platform = platformService.get(request.getPlatformCode());
ApiException.notNull(platform, ErrorCode.PLATFORM_NOT_EXIST.getCode());
ApiException.notNull(gameSecretKey, ErrorCode.CURRENCY_NOT_EXIST.getCode());
String targetCurrency = platform.getCurrencyInfo().get(request.getCurrencyCode());
ApiException.notNull(targetCurrency, ErrorCode.CURRENCY_NOT_EXIST.getCode());
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(request.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(request.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
ApiException.notNull(keyInfo, ErrorCode.CURRENCY_NOT_EXIST.getCode());
// 获取用户信息
Member member = memberService.selectMemberByAccount(memberInfoApiRequest.getAccount(), memberInfoApiRequest.getCurrencyCode(), memberInfoApiRequest.getPlatformCode());
Member member = memberService.selectMemberByAccount(request.getAccount(), request.getCurrencyCode(), request.getPlatformCode());
ApiException.notNull(member, ErrorCode.ACCOUNT_NOT_EXIST.getCode());
//向第三方查询账号
MemberInfoRequestDTO gamesBaseRequestDTO = MemberInfoRequestDTO.builder()
.accounts(member.getGameAccount())
.agentId(gameSecretKey.getCode())
.currency(gameSecretKey.getCurrency())
.agentKey(gameSecretKey.getKey())
.agentId(keyInfo.getCode())
.currency(targetCurrency)
.agentKey(keyInfo.getKey())
.vendor(platform)
.keyInfo(keyInfo)
.systemCurrency(request.getCurrencyCode())
.build();
MemberInfoResponseDTO memberInfo = iGamesService.getMemberInfo(gamesBaseRequestDTO);
MemberInfoResponse memberInfoResponse = new MemberInfoResponse();
@ -191,22 +214,50 @@ public class ApiMemberController extends BaseController {
public AjaxResult infoAll(@Validated @RequestBody MemberInfoAllApiRequest memberInfoAllApiRequest) {
// List<GameSecretKeyCurrencyDTO> gameSecretKeys = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTOList(
// GameSecretKeyCurrencyDTO.builder()
// .systemCurrency(memberInfoAllApiRequest.getCurrencyCode()).build());
List<Key> keys = new ArrayList<>();
for (GamePlatforms platformEnum : GamePlatforms.values()) {
Platform platform = platformService.get(platformEnum.getCode());
String targetCurrency = platform.getCurrencyInfo().get(memberInfoAllApiRequest.getCurrencyCode());
if (StringUtils.isEmpty(targetCurrency)) {
continue;
}
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(memberInfoAllApiRequest.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(memberInfoAllApiRequest.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
if (null == keyInfo) {
continue;
}
List<GameSecretKeyCurrencyDTO> gameSecretKeys = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTOList(GameSecretKeyCurrencyDTO.builder()
.systemCurrency(memberInfoAllApiRequest.getCurrencyCode()).build());
Key key = new Key();
key.setPlatformCode(platform.getPlatformCode());
key.setCode(keyInfo.getCode());
key.setKey(keyInfo.getKey());
key.setCurrency(targetCurrency);
key.setSystemCurrency(memberInfoAllApiRequest.getCurrencyCode());
keys.add(key);
}
// 创建线程池
Map<String, BigDecimal> balanceMap = new LinkedHashMap<>();
CountDownLatch latch = new CountDownLatch(gameSecretKeys.size());
CountDownLatch latch = new CountDownLatch(keys.size());
// 使用List存储Future对象用于获取异步执行的结果
List<Future<MemberInfoAllResponse>> futures = new ArrayList<>();
// 提交异步任务到线程池
for (GameSecretKeyCurrencyDTO gameSecretKey : gameSecretKeys) {
for (Key gameSecretKey : keys) {
futures.add(threadPoolTaskExecutor.submit(() -> {
try {
IGamesService iGamesService = gamesService.get(gameSecretKey.getPlatformCode() + Constants.SERVICE);
@ -219,6 +270,7 @@ public class ApiMemberController extends BaseController {
.accounts(member.getGameAccount())
.agentId(gameSecretKey.getCode())
.currency(gameSecretKey.getCurrency())
.systemCurrency(gameSecretKey.getSystemCurrency())
.agentKey(gameSecretKey.getKey())
.build();
//查询余额
@ -254,5 +306,13 @@ public class ApiMemberController extends BaseController {
return AjaxResult.success(balanceMap);
}
@Data
class Key {
private String platformCode;
private String code;
private String currency;
private String key;
private String systemCurrency;
}
}

View File

@ -0,0 +1,59 @@
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;
/**
*
*/
@NotBlank(message = "currencyCode不能为空")
@Length(max = 32, message = "currencyCode长度不能超过32个字符")
private String currencyCode;
}

View File

@ -6,6 +6,7 @@ import org.hibernate.validator.constraints.Length;
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import java.io.Serializable;
import java.util.Map;
/**
*
@ -43,8 +44,8 @@ public class GameLoginRequest implements Serializable {
/**
* id
*/
@NotNull(message = "gameId不能为空")
private Long gameId;
@NotBlank(message = "gameId不能为空")
private String gameId;
/**
@ -59,4 +60,9 @@ public class GameLoginRequest implements Serializable {
* 1
*/
private Integer disableFullScreen;
/**
* ae
*/
private Map<String, Map<String, Map<String, Object>>> betLimit;
}

View File

@ -8,6 +8,7 @@ import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Pattern;
import java.io.Serializable;
import java.util.Map;
/**
*
@ -22,7 +23,6 @@ public class MemberCreateApiRequest implements Serializable{
@NotBlank(message = "account不能为空")
@Length(max = 64, message = "account长度不能超过64个字符")
@Pattern(regexp = "^[a-z0-9]+$", message = "account只能包含小写字母和数字")
private String account;
/** 平台编码 */
@ -35,4 +35,13 @@ public class MemberCreateApiRequest implements Serializable{
@Length(max = 32, message = "currencyCode长度不能超过32个字符")
private String currencyCode;
/**
*
*/
private Map<String, Map<String, Map<String, Object>>> betLimit;
/**
* 0 1
*/
private Integer platformType;
}

View File

@ -38,7 +38,7 @@ public class GameBettingDetailsResponse implements Serializable
/** 游戏id */
private Long gameId;
private String gameId;
/** 游戏类型 ff_game_type 字典 */
private Integer gameType;
@ -62,7 +62,7 @@ public class GameBettingDetailsResponse implements Serializable
private String account;
/** 游戏注单唯一值 */
private Long wagersId;
private String wagersId;
/** 投注时间 (Unix 时间戳) */
private Long wagersTime;

View File

@ -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;
}

View File

@ -1,5 +1,6 @@
package com.ff.api.response;
import com.ff.game.domain.NameInfo;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@ -27,7 +28,7 @@ public class GameResponse implements Serializable {
/**
* id
*/
private Long id;
private String id;
/**
*
@ -60,5 +61,8 @@ public class GameResponse implements Serializable {
*
*/
private List<String> currencyCode;
/**
*
*/
private List<NameInfo> nameInfo;
}

View File

@ -1,5 +1,6 @@
package com.ff.common.dto;
import com.ff.game.api.request.GamesBaseRequestDTO;
import io.swagger.models.auth.In;
import lombok.AllArgsConstructor;
import lombok.Builder;
@ -21,7 +22,7 @@ import java.math.BigDecimal;
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class GameBalanceExchange implements Serializable {
public class GameBalanceExchange extends GamesBaseRequestDTO implements Serializable {
private final static long serialVersionUID = 3452954102791311247L;
@ -46,6 +47,12 @@ public class GameBalanceExchange implements Serializable {
*/
private BigDecimal amount;
/**
*
*/
private BigDecimal amountActual;
/**
*
*/
@ -60,4 +67,9 @@ public class GameBalanceExchange implements Serializable {
*/
private String sourceId;
/**
*
*/
private Boolean isAll;
}

View File

@ -26,13 +26,8 @@ import com.ff.common.service.ITenantQuotaExchangeService;
import com.ff.base.system.service.ITenantSecretKeyService;
import com.ff.game.api.IGamesService;
import com.ff.game.api.request.MemberInfoRequestDTO;
import com.ff.game.domain.GameSecretKey;
import com.ff.game.dto.GameSecretKeyCurrencyDTO;
import com.ff.game.service.IGameSecretKeyCurrencyService;
import com.ff.game.service.IGameSecretKeyService;
import com.ff.member.domain.Member;
import com.ff.member.service.IMemberService;
import nonapi.io.github.classgraph.json.Id;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import com.ff.common.mapper.TenantGameQuotaMapper;
@ -61,30 +56,18 @@ public class TenantGameQuotaServiceImpl implements ITenantGameQuotaService {
@Resource
private ITenantSecretKeyService tenantSecretKeyService;
@Resource
private IMemberService memberService;
@Resource
private IGameSecretKeyService gameSecretKeyService;
@Autowired
private Map<String, IGamesService> gamesService;
@Resource
private ITenantQuotaExchangeService tenantQuotaExchangeService;
@Resource
private ITenantPlatformService tenantPlatformService;
@Resource
private IGameSecretKeyCurrencyService gameSecretKeyCurrencyService;
/**
*
*
@ -169,7 +152,7 @@ public class TenantGameQuotaServiceImpl implements ITenantGameQuotaService {
BigDecimal balance = balanceChangesDTO.getBalance();
//如果有汇率 则需要计算真实扣除额度
if (!ObjectUtils.isEmpty(balanceChangesDTO.getActualBalance())) {
balance = NumberUtil.div(balance, balanceChangesDTO.getActualBalance(), 2, RoundingMode.FLOOR);
balance = NumberUtil.div(balance, balanceChangesDTO.getActualBalance(), 5, RoundingMode.FLOOR);
}
if (BigDecimal.ZERO.compareTo(balance) >= 0) {
@ -239,7 +222,7 @@ public class TenantGameQuotaServiceImpl implements ITenantGameQuotaService {
BigDecimal balance = balanceRealChangesDTO.getBalance();
//如果有汇率 则需要计算真实扣除额度
if (!ObjectUtils.isEmpty(balanceRealChangesDTO.getActualBalance())) {
balance = NumberUtil.div(balance, balanceRealChangesDTO.getActualBalance(), 2, RoundingMode.FLOOR);
balance = NumberUtil.div(balance, balanceRealChangesDTO.getActualBalance(), 5, RoundingMode.FLOOR);
}
if (BigDecimal.ZERO.compareTo(balance) >= 0) {
@ -288,13 +271,6 @@ public class TenantGameQuotaServiceImpl implements ITenantGameQuotaService {
*/
@Override
public BigDecimal gameBalanceExchange(GameBalanceExchange gameBalanceExchange) {
// 获取平台接口密钥
GameSecretKeyCurrencyDTO gameSecretKey = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
.platformCode(gameBalanceExchange.getPlatformCode())
.currency(gameBalanceExchange.getCurrencyCode()).build());
// 检查平台密钥是否存在,否则抛出异常
ApiException.notNull(gameSecretKey, ErrorCode.CURRENCY_NOT_EXIST.getCode());
TenantQuotaExchange tenantQuotaExchange = tenantQuotaExchangeService.getTenantQuotaExchange(Constants.USDT, gameBalanceExchange.getCurrencyCode());
ApiException.notNull(tenantQuotaExchange, ErrorCode.CURRENCY_EXCHANGE.getCode());
@ -313,14 +289,11 @@ public class TenantGameQuotaServiceImpl implements ITenantGameQuotaService {
ApiException.notNull(tenantPlatform, ErrorCode.PLATFORM_NOT_EXIST.getCode());
// 获取用户信息
Member member = memberService.selectMemberByAccount(gameBalanceExchange.getAccount(), gameBalanceExchange.getCurrencyCode(), gameBalanceExchange.getPlatformCode());
ApiException.notNull(member, ErrorCode.ACCOUNT_NOT_EXIST.getCode());
// 检查用户是否存在,否则抛出异常
ApiException.notNull(member, ErrorCode.ACCOUNT_NOT_EXIST.getCode());
@ -341,14 +314,21 @@ public class TenantGameQuotaServiceImpl implements ITenantGameQuotaService {
// 如果是出账操作
if (isOut) {
// 获取第三方钱包余额
MemberInfoRequestDTO gamesBaseRequestDTO = MemberInfoRequestDTO.builder()
.accounts(member.getGameAccount())
.agentId(gameSecretKey.getCode())
.currency(gameSecretKey.getCurrency())
.agentKey(gameSecretKey.getKey())
.build();
balanceRequestAmount = iGamesService.getMemberInfo(gamesBaseRequestDTO).getBalance();
if (ObjectUtils.isEmpty(gameBalanceExchange.getIsAll())||gameBalanceExchange.getIsAll()){
// 获取第三方钱包余额
MemberInfoRequestDTO gamesBaseRequestDTO = MemberInfoRequestDTO.builder()
.accounts(member.getGameAccount())
.agentId(gameBalanceExchange.getAgentId())
.currency(gameBalanceExchange.getCurrency())
.agentKey(gameBalanceExchange.getAgentKey())
.build();
balanceRequestAmount = iGamesService.getMemberInfo(gamesBaseRequestDTO).getBalance();
if (balanceRequestAmount.compareTo(BigDecimal.ZERO) <= 0) {
throw new ApiException(ErrorCode.INSUFFICIENT_PLAYER_BALANCE.getCode());
}
}
balanceRequestAmount = NumberUtil.add(balanceRequestAmount, NumberUtil.mul(balanceRequestAmount, NumberUtil.div(tenantPlatform.getUseCost(), Constants.HUNDRED)));
// 计算累计转入和转出金额
@ -410,12 +390,10 @@ public class TenantGameQuotaServiceImpl implements ITenantGameQuotaService {
// 如果是扣账操作,首先处理假额度
String falseTenantQuotaType = QuotaUtils.getFalseTenantQuota(gameBalanceExchange.getPlatformCode(), gameBalanceExchange.getCurrencyCode());
TenantGameQuota falseTenantQuota = selectTenantGameQuotaByTenantKey(tenantSecretKey.getTenantKey(), falseTenantQuotaType);
balanceRequestAmount = NumberUtil.add(gameBalanceExchange.getAmount(), NumberUtil.mul(gameBalanceExchange.getAmount(), NumberUtil.div(tenantPlatform.getUseCost(), Constants.HUNDRED)));
balanceRequestAmount = NumberUtil.add(gameBalanceExchange.getAmount(), NumberUtil.mul(gameBalanceExchange.getAmount(), NumberUtil.div(tenantPlatform.getUseCost(), Constants.HUNDRED)));
if (falseTenantQuota.getBalance().compareTo(BigDecimal.ZERO) > 0) {
BigDecimal falseQuotaBalance = falseTenantQuota.getBalance();
if (falseQuotaBalance.compareTo(balanceRequestAmount) >= 0) {
// 假额度足够扣除本次所需金额

View File

@ -5,6 +5,7 @@ import com.ff.base.system.domain.SysDatasource;
import com.ff.base.system.mapper.SysDatasourceMapper;
import com.ff.base.system.service.ISysConfigService;
import com.ff.base.system.service.ISysDictTypeService;
import com.ff.game.service.IPlatformService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.context.ApplicationListener;
@ -34,6 +35,9 @@ public class ContentRefreshedEventListener implements ApplicationListener<Contex
@Resource
private ISysDictTypeService sysDictTypeService;
@Resource
private IPlatformService platformService;
/**
*
*
@ -50,5 +54,7 @@ public class ContentRefreshedEventListener implements ApplicationListener<Contex
}
sysConfigService.loadingConfigCache();
sysDictTypeService.loadingDictCache();
platformService.loadToCache();
}
}

View File

@ -0,0 +1,65 @@
package com.ff.delay;
import com.ff.base.manager.AsyncManager;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.concurrent.DelayQueue;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;
/**
* @author cengy
*/
@Service
@Slf4j
public class DelayService {
private DelayQueue<DelayTask> delayQueue = new DelayQueue<>();
private static final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public void addTask(DelayTask delayTask) {
delayQueue.put(delayTask);
}
// 启动延迟队列任务处理
@PostConstruct
public void startProcessing() {
// 每 1 秒钟执行一次任务检查
scheduler.scheduleAtFixedRate(this::processTasks, 10, 1, TimeUnit.SECONDS);
}
// 处理过期任务
private void processTasks() {
DelayTask task = null; // 阻塞,直到队列中有任务到期
try {
task = delayQueue.take();
} catch (InterruptedException e) {
log.error("获取过期任务失败", e);
}
if (null == task) {
return;
}
AsyncManager.me().execute(new DelayRunnable(task));
}
public static class DelayRunnable implements Runnable {
DelayTask delayTask;
public DelayRunnable(DelayTask delayTask) {
this.delayTask = delayTask;
}
@Override
public void run() {
try {
delayTask.execute();
} catch (Exception e) {
log.error("处理过期任务出错", e);
}
}
}
}

View File

@ -0,0 +1,44 @@
package com.ff.delay;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.concurrent.Delayed;
import java.util.concurrent.TimeUnit;
/**
* @author cengy
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public abstract class DelayTask implements Delayed {
private long delayTime;
private long expireTime;
public DelayTask(long delayTime) {
this.delayTime = delayTime;
this.expireTime = System.currentTimeMillis() + delayTime; // 设置过期时间
}
@Override
public long getDelay(TimeUnit unit) {
long diff = expireTime - System.currentTimeMillis();
return unit.convert(diff, TimeUnit.MILLISECONDS);
}
@Override
public int compareTo(Delayed o) {
if (this.expireTime < ((DelayTask) o).expireTime) {
return -1;
}
if (this.expireTime > ((DelayTask) o).expireTime) {
return 1;
}
return 0;
}
abstract public void execute();
}

View File

@ -52,6 +52,14 @@ public interface IGamesService {
*/
String getGameList(GamesBaseRequestDTO gamesBaseRequestDTO);
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO);
/**
* id
@ -66,9 +74,9 @@ public interface IGamesService {
*
*
* @param exchangeTransferMoneyRequestDTO dto
* @return {@link Boolean }
* @return {@link ExchangeTransferStatusResponseDTO }
*/
Boolean exchangeTransferStatus(ExchangeTransferStatusRequestDTO exchangeTransferMoneyRequestDTO);
ExchangeTransferStatusResponseDTO exchangeTransferStatus(ExchangeTransferStatusRequestDTO exchangeTransferMoneyRequestDTO);
/**
@ -96,6 +104,8 @@ public interface IGamesService {
*/
Boolean createFreeSpin(CreateFreeSpinRequestDTO createFreeSpinRequest);
/**
*
*
@ -136,6 +146,13 @@ public interface IGamesService {
*/
Boolean cancelFreeSpin(CancelFreeSpinRequestDTO cancelFreeSpinRequestDTO);
/**
*
*
* @param gameDemoLoginRequestDTO dto
* @return {@link GameDemoLoginResponseDTO }
*/
GameDemoLoginResponseDTO gameDemoLogin(GameDemoLoginRequestDTO gameDemoLoginRequestDTO);
/**

View File

@ -0,0 +1,31 @@
package com.ff.game.api.ae.address;
import com.dtflys.forest.callback.AddressSource;
import com.dtflys.forest.http.ForestAddress;
import com.dtflys.forest.http.ForestRequest;
import com.ff.base.enums.GamePlatforms;
import com.ff.game.service.IPlatformService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* jili address
*
* @author shi
* @date 2025/02/10
*/
@Component
public class MyAEAddressSource implements AddressSource {
@Resource
private IPlatformService platformService;
@Override
public ForestAddress getAddress(ForestRequest request) {
String apiBaseUrl = platformService.get(GamePlatforms.AE.getCode())
.getUrlInfo().getUrl();
return new ForestAddress("https", apiBaseUrl, 443, "");
}
}

View File

@ -0,0 +1,132 @@
package com.ff.game.api.ae.client;
import com.dtflys.forest.annotation.*;
import com.ff.game.api.ae.address.MyAEAddressSource;
import com.ff.game.api.ae.dto.*;
import com.ff.game.api.jili.dto.*;
import com.ff.game.api.xk.dto.XKKickMemberAllDTO;
import com.ff.game.api.xk.dto.XKKickMemberDTO;
import java.util.Map;
/**
* xk
*
* @author shi
* @date 2025/02/10
*/
@Address(source = MyAEAddressSource.class)
public interface AEClient {
/**
*
*
* @param params
* @return {@link String }
*/
@Post(url ="/wallet/createMember",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
AEResponse createMember(@Body Map<String, Object> params);
/**
*
*
* @param params
* @return {@link AEMemberInfo }
*/
@Post(url ="/wallet/getBalance",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
AEMemberInfo getMemberInfo(@Body Map<String, Object> params);
/**
*
*
* @param params
* @return {@link AELoginResponse }
*/
@Post("/wallet/login")
AELoginResponse loginWithoutRedirect(@Body Map<String, Object> params);
/**
* id
*
* @param params
* @return {@link JILIExchangeMoneyResponseDTO }
*/
@Post(url ="/wallet/deposit",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
AETransactionResponse deposit(@Body Map<String, Object> params);
@Post(url ="/wallet/withdraw",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
AETransactionResponse withdraw(@Body Map<String, Object> params);
/**
*
*
* @param params
* @return {@link AEExchangeTransferStatusResponse }
*/
@Post(url ="/wallet/checkTransferOperation",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
AEExchangeTransferStatusResponse exchangeTransferStatus(@Body Map<String, Object> params);
/**
*
*
* @param params
* @return {@link AEBetRecordResponse }
*/
@Post(url ="https://tttfetch.apihub55.com/fetch/gzip/getTransactionByUpdateDate",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
AEBetRecordResponse getBetRecordByTime(@Body Map<String, Object> params);
/**
*
*
* @param params
* @return {@link AEBetRecordResponse }
*/
@Post(url ="https://tttfetch.apihub55.com/fetch/gzip/getTransactionByTxTime",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
AEBetRecordResponse getBetHistoryRecordByTime(@Body Map<String, Object> params);
/**
*
*
* @param params
* @return {@link JILIKickMemberDTO }
*/
@Post(url ="/wallet/logout",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
XKKickMemberDTO kickMember(@Body Map<String, Object> params);
/**
*
*
* @param params
* @return {@link JILIKickMemberAllDTO }
*/
@Get("/kickMemberAll")
XKKickMemberAllDTO kickMemberAll(@JSONBody Map<String, Object> params);
}

View File

@ -0,0 +1,186 @@
package com.ff.game.api.ae.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* aebet
*
* @author shi
* @date 2025/04/01
*/
@Data
public class AEBetRecordResponse {
/**
*
*/
@JsonProperty("status")
private Integer status;
/**
* (String)
* "Success" -
* -
*/
private String desc;
/**
*
*/
@JsonProperty("transactions")
private List<Transaction> transactions;
@Data
public static class Transaction {
/**
*
*/
@JsonProperty("gameType")
private String gameType;
/**
*
*/
@JsonProperty("winAmount")
private BigDecimal winAmount;
/**
*
*/
@JsonProperty("txTime")
private Date txTime;
/**
*
*/
@JsonProperty("settleStatus")
private Integer settleStatus;
/**
*
*/
@JsonProperty("gameInfo")
private String gameInfo;
/**
*
*/
@JsonProperty("realWinAmount")
private BigDecimal realWinAmount;
/**
*
*/
@JsonProperty("updateTime")
private String updateTime;
/**
*
*/
@JsonProperty("realBetAmount")
private BigDecimal realBetAmount;
/**
* ID
*/
@JsonProperty("userId")
private String userId;
/**
*
*/
@JsonProperty("betType")
private String betType;
/**
*
*/
@JsonProperty("platform")
private String platform;
/**
*
*/
@JsonProperty("txStatus")
private Integer txStatus;
/**
*
*/
@JsonProperty("betAmount")
private BigDecimal betAmount;
/**
*
*/
@JsonProperty("gameName")
private String gameName;
/**
*
*/
@JsonProperty("platformTxId")
private String platformTxId;
/**
*
*/
@JsonProperty("betTime")
private Date betTime;
/**
*
*/
@JsonProperty("gameCode")
private String gameCode;
/**
*
*/
@JsonProperty("currency")
private String currency;
/**
*
*/
@JsonProperty("jackpotWinAmount")
private BigDecimal jackpotWinAmount;
/**
*
*/
@JsonProperty("jackpotBetAmount")
private BigDecimal jackpotBetAmount;
/**
*
*/
@JsonProperty("turnover")
private BigDecimal turnover;
/**
*
*/
@JsonProperty("roundId")
private String roundId;
/**
*
*/
@JsonProperty("refPlatformTxId")
private String refPlatformTxId;
/**
*
*/
@JsonProperty("isPremium")
private Boolean isPremium;
}
}

View File

@ -0,0 +1,56 @@
package com.ff.game.api.ae.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
* aeexchange
*
* @author shi
* @date 2025/04/01
*/
@Data
public class AEExchangeTransferStatusResponse {
/**
*
*/
@JsonProperty("status")
private String status;
/**
* 1 status = 0000 & txStatus = 1 /
* 2 status = 0000 & txStatus = 0 /
* 3 status = 0000 & txStatus = 2 / 3 checkTransferOperation API txStatus = 0 1
* 4 status = 1017
*/
@JsonProperty("txStatus")
private String txStatus;
/**
*
*/
@JsonProperty("balance")
private BigDecimal balance;
/**
*
*/
@JsonProperty("transferAmount")
private BigDecimal transferAmount;
/**
* DEPOSIT WITHDRAW
*/
@JsonProperty("transferType")
private String transferType;
/**
*
*/
@JsonProperty("txCode")
private String txCode;
}

View File

@ -0,0 +1,41 @@
package com.ff.game.api.ae.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
* aelogin
*
* @author shi
* @date 2025/04/01
*/
@Data
public class AELoginResponse {
/**
*
*/
@JsonProperty("status")
private Integer status;
/**
* (String)
* "Success" -
* -
*/
private String desc;
/**
* URL
*/
@JsonProperty("url")
private String url;
/**
*
*/
@JsonProperty("extension")
private List<String> extension;
}

View File

@ -0,0 +1,33 @@
package com.ff.game.api.ae.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
*
*
* @author shi
* @date 2025/04/01
*/
@Data
public class AELogoutUserResponse {
/**
*
*/
@JsonProperty("status")
private String status;
/**
*
*/
@JsonProperty("logoutUsers")
private List<String> logoutUsers;
/**
*
*/
@JsonProperty("count")
private Integer count;
}

View File

@ -0,0 +1,65 @@
package com.ff.game.api.ae.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.List;
/**
* AEMEMBER
*
* @author shi
* @date 2025/03/31
*/
@Data
public class AEMemberInfo {
/**
*
*/
@JsonProperty("results")
private List<UserInfo> result;
/**
*
*/
@JsonProperty("count")
private int count;
/**
*
*/
@JsonProperty("querytime")
private Date queryTime;
/**
*
*/
@JsonProperty("status")
private Integer status;
@Data
public static class UserInfo {
/**
* ID
*/
@JsonProperty("userId")
private String userId;
/**
*
*/
@JsonProperty("balance")
private BigDecimal balance;
/**
*
*/
@JsonProperty("lastModified")
private Date lastModified;
}
}

View File

@ -0,0 +1,26 @@
package com.ff.game.api.ae.dto;
import lombok.Data;
/**
*
*
* @author shi
* @date 2025/03/28
*/
@Data
public class AEResponse {
/**
* (String)
* 0000 -
* -
*/
private String status;
/**
* (String)
* "Success" -
* -
*/
private String desc;
}

View File

@ -0,0 +1,60 @@
package com.ff.game.api.ae.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.time.ZonedDateTime;
/**
*
*
* @author shi
* @date 2025/04/01
*/
@Data
public class AETransactionResponse {
/**
*
*/
@JsonProperty("status")
private String status;
/**
*
*/
@JsonProperty("amount")
private BigDecimal amount;
/**
* DEPOSIT, WITHDRAW
*/
@JsonProperty("method")
private String method;
/**
* ID
*/
@JsonProperty("databaseId")
private int databaseId;
/**
*
*/
@JsonProperty("currentBalance")
private BigDecimal currentBalance;
/**
*
*/
@JsonProperty("lastModified")
private ZonedDateTime lastModified;
/**
*
*/
@JsonProperty("txCode")
private String txCode;
}

View File

@ -0,0 +1,646 @@
package com.ff.game.api.ae.impl;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import com.ff.base.constant.CacheConstants;
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.uuid.IdUtils;
import com.ff.common.dto.GameBalanceExchange;
import com.ff.common.service.ITenantGameQuotaService;
import com.ff.config.KeyConfig;
import com.ff.delay.DelayService;
import com.ff.delay.DelayTask;
import com.ff.game.api.IGamesService;
import com.ff.game.api.ae.client.AEClient;
import com.ff.game.api.ae.dto.*;
import com.ff.game.api.request.*;
import com.ff.game.api.sv388.impl.SV388GamesServiceImpl;
import com.ff.game.api.xk.dto.XKKickMemberDTO;
import com.ff.game.domain.*;
import com.ff.game.service.IGameBettingDetailsService;
import com.ff.game.service.IGameExchangeMoneyService;
import com.ff.game.service.IGameService;
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.util.*;
import java.util.stream.Collectors;
/**
* AE impl
*
* @author shi
* @date 2024/11/12
*/
@Service("AEService")
@Slf4j
public class GamesAEServiceImpl implements IGamesService {
@Resource
private ISysConfigService configService;
@Resource
private RedisCache redisCache;
@Resource
private IGameExchangeMoneyService gameExchangeMoneyService;
@Resource
private IGameService gameService;
@Resource
private ITenantGameQuotaService tenantGameQuotaService;
@Resource
private IMemberService memberService;
@Resource
private AEClient AEClient;
@Resource
private KeyConfig keyConfig;
@Resource
private IGameBettingDetailsService gameBettingDetailsService;
@Resource
private DelayService delayService;
/**
* id
*/
private static final Long GAME_ID = 1904452832756013817L;
/**
*
*
* @param errorCode
* @return {@link Boolean }
*/
private Boolean getIsSuccess(Integer errorCode) {
return 0 == errorCode;
}
/**
*
*
* @param gamesBaseRequestDTO dto
* @return {@link Map }<{@link String }, {@link Object }>
*/
private Map<String, Object> getKey(GamesBaseRequestDTO gamesBaseRequestDTO) {
Map<String, Object> params = new LinkedHashMap<>();
params.put("cert", gamesBaseRequestDTO.getAgentKey());
params.put("agentId", gamesBaseRequestDTO.getAgentId());
return params;
}
/**
*
*
* @param createMemberRequestDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean createMember(CreateMemberRequestDTO createMemberRequestDTO) {
log.info("GamesAEServiceImpl [createMember] 请求参数 {}", createMemberRequestDTO);
Map<String, Object> params = this.getKey(createMemberRequestDTO);
params.put("userId", createMemberRequestDTO.getAccount());
params.put("currency", createMemberRequestDTO.getCurrency());
params.put("betLimit", JsonUtil.objToString(createMemberRequestDTO.getBetLimit()));
AEResponse aeResponse = AEClient.createMember(params);
String errorCode = aeResponse.getStatus();
if (this.getIsSuccess(Integer.valueOf(errorCode))) {
return Boolean.TRUE;
}
//判断是否获取成功
return Boolean.FALSE;
}
/**
*
*
* @param memberInfoRequestDTO dto
* @return {@link MemberInfoResponseDTO }
*/
@Override
public MemberInfoResponseDTO getMemberInfo(MemberInfoRequestDTO memberInfoRequestDTO) {
log.info("GamesAEServiceImpl [getMemberInfo] 请求参数 {}", memberInfoRequestDTO);
Map<String, Object> params = this.getKey(memberInfoRequestDTO);
params.put("alluser", 0);
params.put("userIds", memberInfoRequestDTO.getAccounts());
AEMemberInfo memberInfo = AEClient.getMemberInfo(params);
//判断是否获取成功
if (this.getIsSuccess(memberInfo.getStatus())) {
AEMemberInfo.UserInfo userInfo = memberInfo.getResult().get(0);
return MemberInfoResponseDTO.builder()
.status(GameMemberStatus.UNKNOWN.getCode())
.balance(userInfo.getBalance())
.account(memberInfoRequestDTO.getAccounts())
.build();
} else {
throw new ApiException(ErrorCode.ERROR.getCode());
}
}
/**
*
*
* @param gamesLogin
* @return {@link String }
*/
@Override
public String loginWithoutRedirect(GamesLogin gamesLogin) {
log.info("GamesAEServiceImpl [loginWithoutRedirect] 请求参数 {}", gamesLogin);
Map<String, Object> params = this.getKey(gamesLogin);
params.put("userId", gamesLogin.getAccount());
params.put("externalURL", gamesLogin.getHomeUrl());
params.put("language", gamesLogin.getLang());
params.put("betLimit", gamesLogin.getBetLimit());
AELoginResponse aeLoginResponse = AEClient.loginWithoutRedirect(params);
//判断是否获取成功
if (this.getIsSuccess(aeLoginResponse.getStatus())) {
return aeLoginResponse.getUrl();
} else {
throw new BaseException(aeLoginResponse.getDesc());
}
}
/**
*
*
* @param gamesBaseRequestDTO dto
* @return {@link String }
*/
@Transactional
@Override
public String getGameList(GamesBaseRequestDTO gamesBaseRequestDTO) {
Platform platform = gamesBaseRequestDTO.getVendor();
Game game = gameService.selectGameById(GAME_ID);
//不存在这个游戏
if (ObjectUtils.isEmpty(game)) {
game = new Game();
game.setId(GAME_ID);
game.setSortNo(gameService.selectMaxSortNo(PlatformType.VIDEO.getCode(), GamePlatforms.AE.getCode()) + 1);
game.setPlatformCode(platform.getPlatformCode());
game.setPlatformType(PlatformType.VIDEO.getCode());
game.setGameCode("1");
game.setGameSourceType(String.valueOf(1));
game.setGameName("AE大厅");
game.setCreateBy(Constants.SYSTEM);
NameInfo nameInfo = new NameInfo();
nameInfo.setLang("zh-CN");
nameInfo.setName("AE大厅");
game.setNameInfo(Collections.singletonList(nameInfo));
game.setGameId(StringUtils.addSuffix(GamePlatforms.AE.getCode(), 1));
gameService.insertGame(game);
}else {
NameInfo nameInfo = new NameInfo();
nameInfo.setLang("zh-CN");
nameInfo.setName("AE大厅");
game.setNameInfo(Collections.singletonList(nameInfo));
gameService.updateGame(game);
}
return CacheConstants.AE_GAMES;
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return GamePlatforms.AE.getCode() + IdUtils.simpleUUID();
}
/**
* id
*
* @param exchangeTransferMoneyRequestDTO moeny dto
* @return {@link Long }
*/
@Override
@Transactional
public Long exchangeTransferByAgentId(ExchangeTransferMoneyRequestDTO exchangeTransferMoneyRequestDTO) {
log.info("GamesAEServiceImpl [exchangeTransferByAgentId] 请求参数 {}", exchangeTransferMoneyRequestDTO);
GameExchangeMoney exchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(exchangeTransferMoneyRequestDTO.getGameExchangeId());
Map<String, Object> params = this.getKey(exchangeTransferMoneyRequestDTO);
AETransactionResponse deposit = null;
try {
if (TransferType.GAMES.getCode().equals(exchangeTransferMoneyRequestDTO.getTransferType())) {
params.put("userId", exchangeTransferMoneyRequestDTO.getAccount());
params.put("txCode", exchangeTransferMoneyRequestDTO.getTransactionId());
params.put("transferAmount", exchangeTransferMoneyRequestDTO.getAmount());
deposit = AEClient.deposit(params);
} else {
params.put("userId", exchangeTransferMoneyRequestDTO.getAccount());
params.put("txCode", exchangeTransferMoneyRequestDTO.getTransactionId());
params.put("withdrawType", 1);
deposit = AEClient.withdraw(params);
}
}finally {
BigDecimal coinBefore;
if (TransferType.GAMES.getCode().equals(exchangeTransferMoneyRequestDTO.getTransferType())) {
coinBefore = NumberUtil.sub(deposit.getCurrentBalance(), deposit.getAmount());
} else {
coinBefore = NumberUtil.add(deposit.getCurrentBalance(), deposit.getAmount());
}
//判断是否转移成功
if ("0000".equals(deposit.getStatus())) {
exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
exchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode());
} else {
exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
exchangeMoney.setStepStatus(GameExchangeStepStatus.IN_PROGRESS.getCode());
}
//更新数据
exchangeMoney.setBalance(deposit.getAmount());
exchangeMoney.setCoinBefore(coinBefore);
exchangeMoney.setCoinAfter(deposit.getCurrentBalance());
exchangeMoney.setCurrencyBefore(exchangeMoney.getCoinBefore());
exchangeMoney.setCurrencyAfter(exchangeMoney.getCoinAfter());
gameExchangeMoneyService.updateGameExchangeMoney(exchangeMoney);
}
return exchangeMoney.getId();
}
/**
*
*
* @param exchangeTransferMoneyRequestDTO dto
* @return {@link ExchangeTransferStatusResponseDTO }
*/
@Override
public ExchangeTransferStatusResponseDTO exchangeTransferStatus(ExchangeTransferStatusRequestDTO exchangeTransferMoneyRequestDTO) {
log.info("GamesAEServiceImpl [exchangeTransferStatus] 请求参数 {}", exchangeTransferMoneyRequestDTO);
Map<String, Object> paramsMap = this.getKey(exchangeTransferMoneyRequestDTO);
paramsMap.put("txCode", exchangeTransferMoneyRequestDTO.getOrderId());
AEExchangeTransferStatusResponse exchangeTransferStatusResponse = AEClient.exchangeTransferStatus(paramsMap);
Integer status = StatusType.IN_PROGRESS.getValue();
if ("0000".equals(exchangeTransferStatusResponse.getStatus()) && "1".equals(exchangeTransferStatusResponse.getTxStatus())) {
status = StatusType.SUCCESS.getValue();
} else if ("0000".equals(exchangeTransferStatusResponse.getStatus()) && "0".equals(exchangeTransferStatusResponse.getTxStatus())) {
status = StatusType.FAILURE.getValue();
} else if ("1017".equals(exchangeTransferStatusResponse.getStatus())) {
status = StatusType.FAILURE.getValue();
}
GameExchangeMoney exchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(exchangeTransferMoneyRequestDTO.getGameExchangeMoneyId());
//更新
BigDecimal coinBefore;
if (TransferType.GAMES.getCode().equals(exchangeMoney.getExchangeType())) {
coinBefore = NumberUtil.sub(exchangeTransferStatusResponse.getBalance(), exchangeTransferStatusResponse.getTransferAmount());
} else {
coinBefore = NumberUtil.add(exchangeTransferStatusResponse.getBalance(), exchangeTransferStatusResponse.getTransferAmount());
}
return ExchangeTransferStatusResponseDTO.builder()
.statusType(status)
.balance(exchangeTransferStatusResponse.getTransferAmount())
.coinBefore(coinBefore)
.coinAfter(exchangeTransferStatusResponse.getBalance())
.build();
}
class GetRealtimeRecordTask extends DelayTask {
BetRecordByTimeDTO betRecordByTimeDTO;
public GetRealtimeRecordTask(BetRecordByTimeDTO betRecordByTimeDTO) {
this.betRecordByTimeDTO = betRecordByTimeDTO;
}
@Override
public void execute() {
getRealtimeRecord(betRecordByTimeDTO);
}
}
void getRealtimeRecord(BetRecordByTimeDTO betRecordByTimeDTO) {
//请求参数
log.info("GamesAEServiceImpl [getBetRecordByTime] 请求参数 {}", betRecordByTimeDTO);
Map<String, Object> params = this.getKey(betRecordByTimeDTO);
String timeFrom = redisCache.getCacheObject(CacheConstants.AE_TIME_FROM);
if (StringUtils.isEmpty(timeFrom)) {
timeFrom = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getEndTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00";
}
params.put("timeFrom", timeFrom);
params.put("platform", "SEXYBCRT");
params.put("delayTime", 10000);
AEBetRecordResponse aeBetRecordResponse = AEClient.getBetRecordByTime(params);
//判断是否获取成功
if (this.getIsSuccess(aeBetRecordResponse.getStatus())) {
//数据组装
this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO);
if (aeBetRecordResponse.getTransactions().size() >= 20000) {
delayService.addTask(new GetRealtimeRecordTask(betRecordByTimeDTO));
}
//return Boolean.TRUE;
} else {
redisCache.deleteObject(CacheConstants.AE_TIME_FROM);
log.error("GamesAEServiceImpl [getBetRecordByTime] 获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc());
//throw new BaseException(aeBetRecordResponse.getDesc());
}
}
class GetHistoryRecordTask extends DelayTask {
BetRecordByTimeDTO betRecordByTimeDTO;
public GetHistoryRecordTask(BetRecordByTimeDTO betRecordByTimeDTO) {
this.betRecordByTimeDTO = betRecordByTimeDTO;
}
@Override
public void execute() {
getHistoryRecord(betRecordByTimeDTO);
}
}
void getHistoryRecord(BetRecordByTimeDTO betRecordByTimeDTO) {
log.info("GamesAEServiceImpl [getBetRecordByHistoryTime] 请求参数 {}", betRecordByTimeDTO);
Map<String, Object> params = this.getKey(betRecordByTimeDTO);
String startTime = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getStartTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00";
String endTime = DateUtils.convertTimestampToFormattedDate(betRecordByTimeDTO.getEndTime(), DateUtils.ISO_8601_FORMAT, "GMT+8") + "+08:00";
params.put("startTime", startTime);
params.put("endTime", endTime);
params.put("platform", "SEXYBCRT");
AEBetRecordResponse aeBetRecordResponse = AEClient.getBetHistoryRecordByTime(params);
//判断是否获取成功
if (this.getIsSuccess(aeBetRecordResponse.getStatus())) {
//数据组装
this.batchInsert(aeBetRecordResponse, betRecordByTimeDTO);
if (aeBetRecordResponse.getTransactions().size() >= 20000) {
delayService.addTask(new GetHistoryRecordTask(betRecordByTimeDTO));
}
// return Boolean.TRUE;
} else {
log.error("GamesAEServiceImpl [getBetRecordByHistoryTime] 获取投注记录失败,错误代码{},错误信息{}", aeBetRecordResponse.getStatus(), aeBetRecordResponse.getDesc());
// throw new BaseException(aeBetRecordResponse.getDesc());
}
}
/**
*
*
* @param betRecordByTimeDTO dto
* @return {@link List }<{@link GameBettingDetails }>
*/
@Override
public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) {
delayService.addTask(new GetRealtimeRecordTask(betRecordByTimeDTO));
return Boolean.TRUE;
}
/**
*
*
* @param betRecordByTimeDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) {
delayService.addTask(new GetHistoryRecordTask(betRecordByTimeDTO));
return Boolean.TRUE;
}
/**
*
*
* @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("GamesAEServiceImpl [kickMember] 请求参数 {}", kickMemberRequestDTO);
Map<String, Object> params = this.getKey(kickMemberRequestDTO);
params.put("userIds", kickMemberRequestDTO.getAccount());
XKKickMemberDTO xkKickMemberDTO = AEClient.kickMember(params);
//判断是否获取成功
if (this.getIsSuccess(xkKickMemberDTO.getCode())) {
return Boolean.TRUE;
} else {
throw new BaseException(xkKickMemberDTO.getMsg());
}
}
/**
*
*
* @param kickMemberAllDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean kickMemberAll(KickMemberAllDTO kickMemberAllDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
* 使
*
* @param getFreeSpinDashflowRequestDTO dashflowdto
* @return {@link List }<{@link GameFreeRecord }>
*/
@Override
public List<GameFreeRecord> 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) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param aeBetRecordResponse aedto
*/
private synchronized void batchInsert(AEBetRecordResponse aeBetRecordResponse, BetRecordByTimeDTO betRecordByTimeDTO) {
List<GameBettingDetails> gameBettingDetails = new ArrayList<>();
List<String> wagersIds = new ArrayList<>();
//数据组装
List<AEBetRecordResponse.Transaction> dataBean = aeBetRecordResponse.getTransactions();
String timeFrom = null;
//数据转化
for (AEBetRecordResponse.Transaction bean : dataBean) {
GameBettingDetails bettingDetails = this.dataBuild(GamesDataBuildDTO.builder()
.platform(betRecordByTimeDTO.getVendor())
.data(bean).build());
if (!ObjectUtils.isEmpty(bettingDetails)) {
bettingDetails.setId(IdUtil.getSnowflakeNextId());
gameBettingDetails.add(bettingDetails);
}
wagersIds.add(bean.getPlatform() + bean.getPlatformTxId());
timeFrom = bean.getUpdateTime();
}
if (!CollectionUtils.isEmpty(gameBettingDetails)) {
//查询重复数据id
List<String> removeWagersIds = gameBettingDetailsService.selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.AE.getInfo());
//用steam流清除list中与wagersIds集合相同的数据
gameBettingDetails = gameBettingDetails.stream()
.filter(detail -> !removeWagersIds.contains(detail.getWagersId()))
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(gameBettingDetails)) {
gameBettingDetailsService.batchInsert(gameBettingDetails);
}
}
if (StringUtils.isEmpty(timeFrom)) {
timeFrom = DateUtils.convertTimestampToFormattedDate(DateUtils.getNowDate(), DateUtils.ISO_8601_FORMAT, "UTC+8") + "+08:00";
}
redisCache.setCacheObject(CacheConstants.AE_TIME_FROM, timeFrom);
}
/**
*
*
* @param gamesDataBuildDTO
* @return {@link GameBettingDetails }
*/
@Override
public GameBettingDetails dataBuild(GamesDataBuildDTO gamesDataBuildDTO) {
//转化类
AEBetRecordResponse.Transaction resultBean = (AEBetRecordResponse.Transaction) gamesDataBuildDTO.getData();
Member member = memberService.selectMemberByGameAccount(resultBean.getUserId());
if (ObjectUtils.isEmpty(member)) {
return null;
}
// 判断输赢
Integer gameStatus = GameStatus.FLAT.getCode();
BigDecimal payoffAmount = BigDecimal.ZERO;
if (resultBean.getRealWinAmount().compareTo(resultBean.getRealBetAmount()) > 0) {
gameStatus = GameStatus.WIN.getCode();
payoffAmount = resultBean.getRealWinAmount().subtract(resultBean.getRealBetAmount());
} else if (resultBean.getRealWinAmount().compareTo(resultBean.getRealBetAmount()) < 0) {
gameStatus = GameStatus.FAIL.getCode();
payoffAmount = resultBean.getRealWinAmount().subtract(resultBean.getRealBetAmount()).abs();
}
//结算状态
int settlementStatus = SettlementStatusEnum.REVOKED.getCode();
if (resultBean.getTxStatus() == 1) {
settlementStatus = SettlementStatusEnum.COMPLETED.getCode();
}
//数据构造
GameBettingDetails gameBettingDetails = GameBettingDetails.builder()
.tenantKey(member.getTenantKey())
//保存我们的币种id
.currencyCode(gamesDataBuildDTO.getPlatform().getOurCurrency(resultBean.getCurrency()))
.memberId(member.getId())
.gameCode(resultBean.getGameCode())
.gameType(PlatformType.GAME_HALL.getCode())
.platformCode(GamePlatforms.AE.getCode())
.gameId(/*GAME_ID*/ StringUtils.addSuffix(GamePlatforms.AE.getCode(), 1))
.gameName(resultBean.getGameName())
.gameStatus(gameStatus)
.gameStatusType(resultBean.getSettleStatus())
.gameCurrencyCode(resultBean.getCurrency())
.account(resultBean.getUserId())
.wagersId(resultBean.getPlatform() + resultBean.getPlatformTxId())
.wagersTime(resultBean.getBetTime().getTime())
.betAmount(resultBean.getRealBetAmount())
.payoffTime(resultBean.getTxTime().getTime())
.payoffAmount(payoffAmount)
.betContent(resultBean.getGameInfo())
.settlementTime(resultBean.getTxTime().getTime())
.turnover(resultBean.getTurnover())
.orderNo(String.valueOf(resultBean.getRoundId()))
.settlementStatus(settlementStatus)
.build();
gameBettingDetails.setCreateBy(Constants.SYSTEM);
gameBettingDetails.setCreateTime(DateUtils.getNowDate());
return gameBettingDetails;
}
}

View File

@ -0,0 +1,32 @@
package com.ff.game.api.db.address;
import com.dtflys.forest.callback.AddressSource;
import com.dtflys.forest.http.ForestAddress;
import com.dtflys.forest.http.ForestRequest;
import com.ff.base.enums.GamePlatforms;
import com.ff.game.service.IPlatformService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* <a href="http://api-doc-2.dbsporxxxw1box.com/#/login">DB</a>
* api
* a12345678
*
* @author cengy
*/
@Component
public class DBSportsAddress implements AddressSource {
@Resource
private IPlatformService platformService;
@Override
public ForestAddress getAddress(ForestRequest request) {
String apiBaseUrl = platformService.get(GamePlatforms.DBSports.getCode())
.getUrlInfo().getUrl();
return new ForestAddress("https", apiBaseUrl, 443, "");
}
}

View File

@ -0,0 +1,110 @@
package com.ff.game.api.db.client;
import com.dtflys.forest.annotation.*;
import com.ff.game.api.db.address.DBSportsAddress;
import com.ff.game.api.db.dto.*;
/**
* @author cengy
*/
@Address(source = DBSportsAddress.class)
public interface DBSportsClient {
/**
*
*
* @return {@link CreateUserResponse}
*/
@Post(url = "/api/user/create",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
CreateUserResponse createMember(@Body CreateUserRequest request,
@Header("requestId") @Var("requestId") String requestId);
/**
* ()
*
* @param request
* @param requestId
* @return
*/
@Post(url = "/api/user/login",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
LoginResponse login(@Body LoginRequest request,
@Header("requestId") String requestId);
/**
* DB0.01
*
* @param request
* @return {@link TransferResponse}
*/
@Post(url = "/api/fund/transfer",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
TransferResponse transferIn(@Body TransferRequest request,
@Header("requestId") @Var("requestId") String requestId
);
@Post(url = "/api/fund/transfer",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
TransferResponse transferOut(@Body TransferRequest request,
@Header("requestId") String requestId
);
@Post(url = "/api/fund/checkBalance",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
GetMemberInfoResponse getMemberInfo(@Body GetMemberInfoRequest request,
@Header("requestId") String requestId);
/**
* ID
*/
@Post(url = "/api/fund/getTransferRecord",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
TransferDetailResponse transferDetail(@Body TransferDetailRequest request,
@Header("requestId") String requestId);
/**
*
*
* @param request
* @param requestId
* @return
*/
@Post(value = "/api/user/kickOutUser",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
KickUserResponse kickUser(@Body KickUserRequest request,
@Header("requestId") String requestId);
/**
* Json
*/
@Post(url = "/api/bet/queryBetListV2",
headers = {
"Content-type: application/x-www-form-urlencoded"
}
)
GetBetListResponse getBetList(@Body GetBetListRequest request,
@Header("requestId") String requestId);
}

View File

@ -0,0 +1,28 @@
package com.ff.game.api.db.dto;
import com.ff.base.utils.sign.Md5Utils;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class CreateUserRequest implements Serializable {
private static final long serialVersionUID = 1L;
private String userName; // 用户名(可以包含但是不要等同于特殊字符或者空格长度控制在30个字符以下)
private String merchantCode; // 商户code
private String timestamp = System.currentTimeMillis() + ""; // 13位时间戳
private String currency; // 币种
private String nickname; // N 昵称
private String agentId; // N 信用网(代理id)
private String signature; // 签名 signature =MD5(MD5(userName +”&”+ merchantCode +”&”+ timestamp) + ”&”+ key)
public void buildSignature(String key) {
String signature = Md5Utils.md5New(Md5Utils.md5New(userName + "&" + merchantCode + "&" + timestamp) + "&" + key);
this.signature = signature;
}
}

View File

@ -0,0 +1,26 @@
package com.ff.game.api.db.dto;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class CreateUserResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean status;
private String msg;
private String code;
private Long serverTime;
private UserDTO data;
@Data
public static class UserDTO {
private String userId;
}
}

View File

@ -0,0 +1,9 @@
package com.ff.game.api.db.dto;
/**
* @author cengy
*/
public class Enums {
}

View File

@ -0,0 +1,93 @@
package com.ff.game.api.db.dto;
import com.ff.base.utils.sign.Md5Utils;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class GetBetListRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
*
*/
private String userName;
/**
* 13
*/
private String startTime;
/**
* 13
*/
private String endTime;
/**
*
*/
private String merchantCode;
/**
* ID
*/
private Integer sportId;
/**
* ID
*/
private Long tournamentId;
/**
*
* 0:
* 1:
* 2:
* 3:
* 4:
* 5:
*/
private Integer settleStatus;
/**
* 1
*/
private Integer pageNum;
/**
* 1000
*/
private Integer pageSize;
/**
* 13
*/
private String timestamp = String.valueOf(System.currentTimeMillis());
/**
*
* 1:
* 2:
*/
private Integer orderBy;
/**
* "en"
*/
private String language;
/**
*
*/
private String signature;
public void buildSignature(String key) {
String signature = Md5Utils.md5New(Md5Utils.md5New(merchantCode + "&" + startTime + "&" + endTime + "&" + timestamp) + "&" + key);
this.signature = signature;
}
}

View File

@ -0,0 +1,199 @@
package com.ff.game.api.db.dto;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
/**
* @author cengy
*/
@Data
public class GetBetListResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean status;
private String msg;
private String code;
private Long serverTime;
private DataDTO data;
@Data
public static class DataDTO {
// 当前页码
private Integer pageNum;
// 每页条数
private Integer pageSize;
// 总条数
private Integer totalCount;
// 注单列表
private List<OrderItemDTO> list;
}
@Data
public static class OrderItemDTO {
// 用户名
private String userName;
// 商户编码
private String merchantCode;
// 订单ID
private String orderNo;
// 订单状态,具体见参数字段映射
private Integer orderStatus;
// 投注时间13位时间戳
private Long createTime;
// 订单更新时间13位时间戳
private Long modifyTime;
// 实际投注金额
private String orderAmount;
// 注单项数量
private Integer betCount;
// 结算时间13位时间戳
private Long settleTime;
// 结算金额
private Double settleAmount;
// 提前结算投注金额
private Double preBetAmount;
// 盈利金额
private Double profitAmount;
// 注单结算结果
// 2:走水3:输4:赢5:赢半6:输半7:赛事取消8:赛事延期
private Integer outcome;
// 串关类型
private Integer seriesType;
// 串关值
private String seriesValue;
// 结算次数
private Integer settleTimes;
// 设备类型1-H52-PC3-Android4-IOS
private String deviceType;
// 移动设备标识
private String deviceImei;
// 用户IP地址
private String ip;
// 币种
private String currency;
// 汇率
private BigDecimal exchangeRate;
// 最大中奖金额
private Double maxWinAmount;
// VIP等级
private Integer vipLevel;
// 投注前余额
private BigDecimal beforeTransfer;
// 投注后余额
private BigDecimal afterTransfer;
// 有效投注金额
private BigDecimal validOrderAmount;
// 注单详情列表
private List<DetailItemDTO> detailList;
}
@Data
public static class DetailItemDTO {
// 投注项编号
private Long betNo;
// 投注项ID
private Long playOptionsId;
// 赛事ID
private Long matchId;
// 比赛开始时间13位时间戳
private Long beginTime;
// 注单金额
private Double betAmount;
// 联赛名称
private String matchName;
// 比赛对阵
private String matchInfo;
// 投注类型:
// 1早盘2滚球盘3冠军盘4虚拟赛事5电竞赛事
private Integer matchType;
// 赛种ID
private Integer sportId;
// 玩法ID
private Integer playId;
// 投注项(如主客队)
private String playOptions;
// 游戏名称
private String sportName;
// 联赛ID
private Long tournamentId;
// 投注项名称
private String playOptionName;
// 玩法名称
private String playName;
// 盘口类型
private String marketType;
// 盘口值
private String marketValue;
// 让球值
private BigDecimal handicap;
// 结算比分(我方处理后所得,数据商可能未提供)
private String settleScore;
// 基准分
private String scoreBenchmark;
// 当前赔率(欧洲盘表示)
private BigDecimal oddsValue;
// 注单结算结果:
// 0:无结果2:走水3:输4:赢5:赢一半6:输一半,
// 7:赛事取消8:赛事延期11:比赛延迟12:比赛中断,
// 13:未知15:比赛放弃16:异常盘口17:未知状态,
// 18:比赛取消19:比赛延期
private String betResult;
// 最终赔率(按盘口类型)
private BigDecimal oddFinally;
}
}

View File

@ -0,0 +1,36 @@
package com.ff.game.api.db.dto;
import com.ff.base.utils.sign.Md5Utils;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class GetMemberInfoRequest implements Serializable {
private static final long serialVersionUID = 1L;
/**
* ,
*/
private String userName;
/**
*
*/
private String merchantCode;
/**
* Long(13)
*/
private String timestamp = System.currentTimeMillis() + "";
private String signature;
public void buildSignature(String key) {
String signature = Md5Utils.md5New(Md5Utils.md5New(merchantCode + "&" + userName + "&" + timestamp) + "&" + key);
this.signature = signature;
}
}

View File

@ -0,0 +1,27 @@
package com.ff.game.api.db.dto;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
import java.util.List;
/**
* @author cengy
*/
@Data
public class GetMemberInfoResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean status;
private String msg;
private String code;
private Long serverTime;
private MemberInfoDTO data;
@Data
public static class MemberInfoDTO {
private BigDecimal balance;
private String userName;
}
}

View File

@ -0,0 +1,24 @@
package com.ff.game.api.db.dto;
import com.ff.base.utils.sign.Md5Utils;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class KickUserRequest implements Serializable {
private static final long serialVersionUID = 1L;
private String userName;
private String merchantCode;
private String timestamp = System.currentTimeMillis() + "";
private String signature;
public void buildSignature(String key) {
String signature = Md5Utils.md5New(Md5Utils.md5New(merchantCode + "&" + userName + "&" + timestamp) + "&" + key);
this.signature = signature;
}
}

View File

@ -0,0 +1,20 @@
package com.ff.game.api.db.dto;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class KickUserResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean status;
private String msg;
private String code;
private Long serverTime;
}

View File

@ -0,0 +1,57 @@
package com.ff.game.api.db.dto;
import com.ff.base.utils.sign.Md5Utils;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @author cengy
*/
@Data
public class LoginRequest implements Serializable {
private static final long serialVersionUID = 1L;
private String userName;
private String merchantCode;
private String timestamp = System.currentTimeMillis() + "";
private String signature;
// 不能传错,用户可能受到设备类型相关风控措施的错误限制,进而影响正常用户货量,并产生客诉
private String terminal; // 终端类型【电脑传值pc】【移动设备传值mobile】 注这个参数传值必须是pc或者mobile
private BigDecimal balance; // 用户余额,N
private String currency; // 币种(见参数映射:支持币种 )会员首次登录,必须填写币种参数,否则注册失败
private String callbackUrl; // 玩家会话失效跳转的商户地址url(非必传)
private String stoken; // 非必传字段(商户方用户会话)
// 非必传字段(支持体育游戏其他端的跳转)
// C端⽀持跳转的游戏如果C端⽀持跳转多个游戏
// 则使⽤逗号区分。 具体字段可查看参数字段映射第6条 参数字段映射
private String jumpsupport;
private String jumpfrom; // 非必传字段,同上
private String agentId; // 信用网(代理id),N
// 用户语种:
//zh中文
//en英文
//vi越南语
//tw中文繁体
//th泰语
//ms马来语
//ad印尼语
//ko韩语
//mya:缅甸语
//pt:葡萄牙语
//es:西班牙语
//非必传字段
private String language;
private String ip; // 非必传字段 说明商户端在该字段上传入用户登陆时端ip
public void buildSignature(String key) {
String signature = Md5Utils.md5New(Md5Utils.md5New(merchantCode + "&" + userName + "&" + terminal + "&" + timestamp) + "&" + key);
this.signature = signature;
}
}

View File

@ -0,0 +1,34 @@
package com.ff.game.api.db.dto;
import lombok.Data;
import java.io.Serializable;
import java.util.List;
/**
* @author cengy
*/
@Data
public class LoginResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean status;
private String msg;
private String code;
private Long serverTime;
private LoginRespDTO data;
@Data
public static class LoginRespDTO {
private String domain; // 体育游戏前端URL
private String token; // 带有登录状态的token
private String apiDomain;// 赛事API的域名(若没有对接赛事API为空)
private String imgDomain; // 静态资源域名
private String loginUrl; // 登录体育URL可直接跳转
private String userId; // 用户id
private String url; // 参加活动的商户使用,客户端域名和参数拼接后提供商户使用
private List<String> loginUrlArr; // loginUrl数组多个域名调试使用
}
}

View File

@ -0,0 +1,38 @@
package com.ff.game.api.db.dto;
import com.ff.base.utils.sign.Md5Utils;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class TransferDetailRequest implements Serializable {
/**
* ,N
*/
private String userName;
/**
* code
*/
private String merchantCode;
/**
* id(19)
*/
private String transferId;
private String timestamp = System.currentTimeMillis() + "";
/**
*
*/
private String signature;
public void buildSignature(String key) {
String signature = Md5Utils.md5New(Md5Utils.md5New(merchantCode + "&" + transferId + "&" + timestamp) + "&" + key);
this.signature = signature;
}
}

View File

@ -0,0 +1,39 @@
package com.ff.game.api.db.dto;
import lombok.Data;
import java.io.Serializable;
import java.math.BigDecimal;
/**
* @author cengy
*/
@Data
public class TransferDetailResponse implements Serializable {
private static final long serialVersionUID = 1L;
private TransferDetailDTO data;
private Boolean status;
private String msg;
private String code;
private Long serverTime;
@Data
public static class TransferDetailDTO implements Serializable {
private static final long serialVersionUID = 1L;
private String transferId; // 交易id
private String merchantCode; // 商户代码
private Long userId; // 用户id
private Integer transferType; // 交易类型 1加款2扣款
private BigDecimal amount; // 交易金额
private BigDecimal beforeTransfer; // 转账前余额
private Integer afterTransfer; // 转账后余额
private Integer status; // 转账成功与否(0:失败1:成功)
private String mag; // 转账模式 1免转2转账
private Integer transferMode; // 转账涉及订单(transferMode为2时该字段为空)
private Long createTime; // 交易时间
}
}

View File

@ -0,0 +1,28 @@
package com.ff.game.api.db.dto;
import com.ff.base.utils.sign.Md5Utils;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class TransferRequest implements Serializable {
private String userName;
private String merchantCode;
private int transferType; // 1加款 2扣款
private String amount; // 金额小数2位
private String transferId; // 交易的讯息号唯一标示不可重复19位长度的字符串
private String timestamp = String.valueOf(System.currentTimeMillis());
private String signature;
public void buildSignature(String key) {
String signature = Md5Utils.md5New(Md5Utils.md5New(merchantCode + "&" + userName + "&" + transferType + "&" + amount + "&" + transferId + "&" + timestamp) + "&" + key);
this.signature = signature;
}
}

View File

@ -0,0 +1,25 @@
package com.ff.game.api.db.dto;
import lombok.Data;
import java.io.Serializable;
/**
* @author cengy
*/
@Data
public class TransferResponse implements Serializable {
private static final long serialVersionUID = 1L;
private Boolean status;
private String msg;
private String code;
private Long serverTime;
private TransferDTO data;
@Data
public static class TransferDTO {
private String userName;
}
}

View File

@ -0,0 +1,609 @@
package com.ff.game.api.db.impl;
import cn.hutool.core.util.IdUtil;
import com.ff.base.constant.CacheConstants;
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.utils.DateUtils;
import com.ff.base.utils.StringUtils;
import com.ff.base.utils.sign.Md5Utils;
import com.ff.base.utils.uuid.IdUtils;
import com.ff.game.api.IGamesService;
import com.ff.game.api.db.dto.*;
import com.ff.game.api.request.*;
import com.ff.game.domain.*;
import com.ff.game.service.IGameBettingDetailsService;
import com.ff.game.service.IGameExchangeMoneyService;
import com.ff.game.service.IGameService;
import com.ff.member.domain.Member;
import com.ff.member.service.IMemberService;
import com.ff.game.api.db.client.DBSportsClient;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ObjectUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
/**
* DB
*
* @author cengy
* @date 2024/10/21
*/
@Service("DBSportsService")
@Slf4j
public class DBSportsServiceImpl implements IGamesService {
@Resource
private RedisCache redisCache;
@Resource
private IGameExchangeMoneyService gameExchangeMoneyService;
@Resource
private IGameService gameService;
@Resource
private IMemberService memberService;
@Resource
private DBSportsClient dbSportsClient;
@Resource
private IGameBettingDetailsService gameBettingDetailsService;
/**
*
*
* @param errorCode
* @return {@link Boolean }
*/
private Boolean isSuccess(String errorCode) {
return "0000".equals(errorCode);
}
String getSign(String bodyJsonString, String merchantId, String merchantApiSecret, long requestTimestamp) {
String stringThatNeedsToBeSigned = bodyJsonString + "." + merchantId + "." + requestTimestamp + "." + merchantApiSecret;
String sign = Md5Utils.md5New(stringThatNeedsToBeSigned);
return sign;
}
/**
*
*
* @param requestDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean createMember(CreateMemberRequestDTO requestDTO) {
CreateUserRequest request = new CreateUserRequest();
request.setUserName(requestDTO.getAccount());
request.setMerchantCode(requestDTO.getAgentId());
request.setCurrency(requestDTO.getCurrency());
request.buildSignature(requestDTO.getAgentKey());
//String lang = requestDTO.getLan
String requestId = IdUtils.fastUUID();
CreateUserResponse response = dbSportsClient.createMember(request, requestId);
if (isSuccess(response.getCode())) {
return Boolean.TRUE;
}
log.error("创建会员失败, errorCode:{}, errorMessage:{}", response.getCode(), response.getMsg());
throw new ApiException(ErrorCode.Create_Member_Failure.getCode());
}
/**
* id
*
* @param requestDTO
* @return {@link Long }
*/
@Override
@Transactional
public Long exchangeTransferByAgentId(ExchangeTransferMoneyRequestDTO requestDTO) {
GameExchangeMoney exchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(requestDTO.getGameExchangeId());
// 转入
if (requestDTO.getTransferType().equals(TransferType.GAMES.getCode())) {
TransferRequest request = new TransferRequest();
request.setUserName(requestDTO.getAccount());
request.setTransferType(1);
request.setTransferId(requestDTO.getTransactionId());
request.setMerchantCode(requestDTO.getAgentId());
request.setAmount(requestDTO.getAmount().toString());
request.buildSignature(requestDTO.getAgentKey());
TransferResponse response = dbSportsClient.transferIn(
request, IdUtils.fastUUID()
);
if (isSuccess(response.getCode())) {
GetMemberInfoRequest queryMemberRequest = new GetMemberInfoRequest();
queryMemberRequest.setUserName(requestDTO.getAccount());
queryMemberRequest.setMerchantCode(requestDTO.getAgentId());
queryMemberRequest.buildSignature(requestDTO.getAgentKey());
String requestId = IdUtils.fastUUID();
try {
GetMemberInfoResponse queryMemberResponse = dbSportsClient.getMemberInfo(queryMemberRequest, requestId);
if (this.isSuccess(queryMemberResponse.getCode())) {
BigDecimal transAmount = requestDTO.getAmount();
BigDecimal afterAmount = queryMemberResponse.getData().getBalance();
BigDecimal beforeAmount = afterAmount.subtract(transAmount);
exchangeMoney.setBalance(transAmount);
exchangeMoney.setCoinBefore(beforeAmount);
exchangeMoney.setCoinAfter(afterAmount);
exchangeMoney.setCurrencyBefore(beforeAmount);
exchangeMoney.setCurrencyAfter(afterAmount);
}
} catch (Exception e) {
log.error("查询会员失败, errorCode:{}, errorMessage:{}", response.getCode(), response.getMsg(), e);
}
exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
exchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode());
gameExchangeMoneyService.updateGameExchangeMoney(exchangeMoney);
} else {
exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
exchangeMoney.setStepStatus(GameExchangeStepStatus.FAILURE.getCode());
gameExchangeMoneyService.updateGameExchangeMoney(exchangeMoney);
throw new ApiException(ErrorCode.Transfer_In_Failure.getCode());
}
} else {
// 获取第三方钱包余额
MemberInfoRequestDTO memberInfoRequestDTO = MemberInfoRequestDTO.builder()
.accounts(requestDTO.getAccount())
.agentId(requestDTO.getAgentId())
.agentKey(requestDTO.getAgentKey())
.build();
BigDecimal balance = this.getMemberInfo(memberInfoRequestDTO).getBalance();
if (balance.compareTo(BigDecimal.ZERO) <= 0) {
throw new ApiException(ErrorCode.INSUFFICIENT_PLAYER_BALANCE.getCode());
}
TransferRequest request = new TransferRequest();
request.setUserName(requestDTO.getAccount());
request.setTransferType(2); // 转出
request.setTransferId(requestDTO.getTransactionId());
request.setMerchantCode(requestDTO.getAgentId());
request.setAmount(/*requestDTO.getAmount().toString()*/ balance.toString());
request.buildSignature(requestDTO.getAgentKey());
TransferResponse response = dbSportsClient
.transferOut(
request, IdUtils.fastUUID()
);
//判断是否转移成功
if (this.isSuccess(response.getCode())) {
BigDecimal transAmount = balance;
BigDecimal beforeAmount = balance;
BigDecimal afterAmount = BigDecimal.ZERO;
//更新数据
exchangeMoney.setBalance(transAmount);
exchangeMoney.setCoinBefore(beforeAmount);
exchangeMoney.setCoinAfter(afterAmount);
exchangeMoney.setCurrencyBefore(beforeAmount);
exchangeMoney.setCurrencyAfter(afterAmount);
exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
exchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode());
gameExchangeMoneyService.updateGameExchangeMoney(exchangeMoney);
} else {
exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
exchangeMoney.setStepStatus(GameExchangeStepStatus.FAILURE.getCode());
gameExchangeMoneyService.updateGameExchangeMoney(exchangeMoney);
throw new ApiException(ErrorCode.Transfer_Out_Failure.getCode());
}
}
return exchangeMoney.getId();
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return gameExchangeMoneyService.getTransactionId(GamePlatforms.DBSports.getInfo(), 11);
}
/**
*
*
* @param requestDTO dto
* @return {@link MemberInfoResponseDTO }
*/
@Override
public MemberInfoResponseDTO getMemberInfo(MemberInfoRequestDTO requestDTO) {
GetMemberInfoRequest request = new GetMemberInfoRequest();
request.setUserName(requestDTO.getAccounts());
request.setMerchantCode(requestDTO.getAgentId());
request.buildSignature(requestDTO.getAgentKey());
String requestId = IdUtils.fastUUID();
GetMemberInfoResponse response = dbSportsClient.getMemberInfo(request, requestId);
//判断是否获取成功
if (this.isSuccess(response.getCode())) {
return MemberInfoResponseDTO.builder()
.status(GameMemberStatus.UNKNOWN.getCode())
.balance(response.getData().getBalance())
.account(requestDTO.getAccounts())
.build();
}
throw new ApiException(ErrorCode.Get_Member_Info_Failure.getCode());
}
/**
*
*
* @param requestDTO
* @return {@link String }
*/
@Override
public String loginWithoutRedirect(GamesLogin requestDTO) {
LoginRequest request = new LoginRequest();
request.setUserName(requestDTO.getAccount());
request.setMerchantCode(requestDTO.getAgentId());
if (requestDTO.getPlatform().equalsIgnoreCase("web")) {
request.setTerminal("pc");
} else {
request.setTerminal("mobile");
}
request.setLanguage(requestDTO.getLang());
request.setCurrency(requestDTO.getCurrency());
request.buildSignature(requestDTO.getAgentKey());
LoginResponse response = dbSportsClient.login(
request, IdUtils.fastUUID()
);
if (this.isSuccess(response.getCode())) {
LoginResponse.LoginRespDTO respDTO = response.getData();
String loginURL = respDTO.getLoginUrl();
if (StringUtils.isEmpty(loginURL)) {
throw new ApiException(ErrorCode.Get_Url_Failure.getCode());
}
return loginURL;
}
throw new ApiException(ErrorCode.Get_Url_Failure.getCode());
}
/**
*
*
* @param gamesBaseRequestDTO dto
* @return {@link String }
*/
@Transactional
@Override
public String getGameList(GamesBaseRequestDTO gamesBaseRequestDTO) {
Platform platform = gamesBaseRequestDTO.getVendor();
Game condition = new Game();
condition.setPlatformCode(platform.getPlatformCode());
condition.setPlatformType(PlatformType.SPORTS.getCode());
List<Game> gameList = gameService.selectGameList(condition);
//不存在这个游戏
if (ObjectUtils.isEmpty(gameList)) {
Game game = new Game();
game.setId(IdUtil.getSnowflakeNextId());
game.setSortNo(1);
game.setPlatformCode(platform.getPlatformCode());
game.setPlatformType(PlatformType.SPORTS.getCode());
game.setGameCode("1");
game.setGameSourceType(String.valueOf(1));
game.setGameName(GamePlatforms.DBSports.getInfo());
game.setCreateBy(Constants.SYSTEM);
NameInfo nameInfo = new NameInfo();
nameInfo.setLang("zh-CN");
nameInfo.setName("DB体育");
game.setNameInfo(Collections.singletonList(nameInfo));
gameService.insertGame(game);
}
/*GameName gameName = gameNameService.selectGameNameById(GAME_NAME_ID);
if (ObjectUtils.isEmpty(gameName)) {
gameNameService.insertGameName(GameName.builder()
.id(GAME_NAME_ID)
.gameId(game.getId())
.gameName(game.getGameName())
.langCode("zh-CN")
.createBy(Constants.SYSTEM)
.build());
}*/
return CacheConstants.DB_Sports;
}
/**
*
*
* @param requestDTO dto
* @return {@link Boolean }
*/
@Override
public ExchangeTransferStatusResponseDTO exchangeTransferStatus(ExchangeTransferStatusRequestDTO requestDTO) {
GameExchangeMoney gameExchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(requestDTO.getGameExchangeMoneyId());
if (null == gameExchangeMoney) {
throw new ApiException(ErrorCode.Transfer_Not_Exist.getCode());
}
Integer status = StatusType.IN_PROGRESS.getValue();
if (!Objects.equals(gameExchangeMoney.getStatus(), StatusType.SUCCESS.getValue())) {
TransferDetailRequest request = new TransferDetailRequest();
request.setMerchantCode(requestDTO.getAgentId());
request.setTransferId(gameExchangeMoney.getOrderId());
request.buildSignature(requestDTO.getAgentKey());
TransferDetailResponse response = dbSportsClient.transferDetail(
request, IdUtils.fastUUID()
);
if (this.isSuccess(response.getCode()) && response.getData().getStatus() == 1) {
status = StatusType.SUCCESS.getValue();
} else {
status = StatusType.FAILURE.getValue();
}
}
return ExchangeTransferStatusResponseDTO.builder()
.statusType(status)
.balance(gameExchangeMoney.getBalance())
.coinBefore(gameExchangeMoney.getCoinBefore())
.coinAfter(gameExchangeMoney.getCoinAfter())
.build();
}
/**
*
*
* @param requestDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean getBetRecordByTime(BetRecordByTimeDTO requestDTO) {
this.getRealtimeRecord(requestDTO, 1);
return Boolean.TRUE;
}
void getRealtimeRecord(BetRecordByTimeDTO requestDTO, int pageNum) {
GetBetListRequest request = new GetBetListRequest();
request.setStartTime(String.valueOf(requestDTO.getStartTime()));
request.setEndTime(String.valueOf(requestDTO.getEndTime()));
request.setMerchantCode(requestDTO.getAgentId());
request.setPageNum(pageNum);
request.setPageSize(1000);
request.buildSignature(requestDTO.getAgentKey());
GetBetListResponse response = dbSportsClient.getBetList(
request, IdUtils.fastUUID()
);
if (this.isSuccess(response.getCode())) {
List<GetBetListResponse.OrderItemDTO> list = response.getData().getList();
if (CollectionUtils.isEmpty(list)) {
return;
}
this.batchInsert(list, requestDTO);
getRealtimeRecord(requestDTO, ++pageNum);
}
}
/**
*
*
* @param requestDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO requestDTO) {
return Boolean.TRUE;
}
/**
*
*
* @param createFreeSpinRequest
* @return {@link Boolean }
*/
@Override
public Boolean createFreeSpin(CreateFreeSpinRequestDTO createFreeSpinRequest) {
throw new BaseException("暂不支持免费局数");
}
/**
*
*
* @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) {
KickUserRequest request = new KickUserRequest();
request.setUserName(kickMemberRequestDTO.getAccount());
request.setMerchantCode(kickMemberRequestDTO.getAgentId());
request.buildSignature(kickMemberRequestDTO.getAgentKey());
KickUserResponse kickUserResponse = dbSportsClient.kickUser(request, IdUtils.fastUUID());
if (this.isSuccess(kickUserResponse.getCode())) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
/**
*
*
* @param kickMemberAllDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean kickMemberAll(KickMemberAllDTO kickMemberAllDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
* 使
*
* @param getFreeSpinDashflowRequestDTO dashflowdto
* @return {@link List }<{@link GameFreeRecord }>
*/
@Override
public List<GameFreeRecord> 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) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param settledOrderList
*/
private void batchInsert(List<GetBetListResponse.OrderItemDTO> settledOrderList, BetRecordByTimeDTO betRecordByTimeDTO) {
List<GameBettingDetails> gameBettingDetails = new ArrayList<>();
List<String> wagersIds = new ArrayList<>();
//数据组装
List<GetBetListResponse.OrderItemDTO> dataList = settledOrderList;
if (CollectionUtils.isEmpty(dataList)) {
return;
}
//数据转化
for (GetBetListResponse.OrderItemDTO dataBean : dataList) {
if (dataBean.getOrderStatus() != 1) { // 只关心结算的
continue;
}
GameBettingDetails bettingDetails = this.dataBuild(GamesDataBuildDTO.builder()
.platform(betRecordByTimeDTO.getVendor())
.data(dataBean).build());
if (!ObjectUtils.isEmpty(bettingDetails)) {
bettingDetails.setId(IdUtil.getSnowflakeNextId());
gameBettingDetails.add(bettingDetails);
}
wagersIds.add(dataBean.getOrderNo());
}
if (CollectionUtils.isEmpty(gameBettingDetails)) {
return;
}
//查询重复数据id
List<String> removeWagersIds = gameBettingDetailsService
.selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.DBSports.getCode());
//用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) {
//转化类
GetBetListResponse.OrderItemDTO dataBean = (GetBetListResponse.OrderItemDTO) gamesDataBuildDTO.getData();
Member member = memberService.selectMemberByGameAccount(dataBean.getUserName());
if (ObjectUtils.isEmpty(member)) {
return null;
}
//List<Game> gameList = redisCache.getCacheList(CacheConstants.DB_Sports);
//Game game = gameList.get(0);
BigDecimal originPayoffAmount = BigDecimal.valueOf(dataBean.getSettleAmount());
BigDecimal betAmount = new BigDecimal(dataBean.getOrderAmount());
int compareResult = originPayoffAmount.compareTo(betAmount);
long payoffTime = dataBean.getSettleTime();
long createTime = dataBean.getCreateTime();
Platform platform = gamesDataBuildDTO.getPlatform();
String systemCurrency = platform.getOurCurrency(dataBean.getCurrency());
//数据构造
GameBettingDetails gameBettingDetails = GameBettingDetails.builder()
.tenantKey(member.getTenantKey())
//保存我们的币种id
.currencyCode(systemCurrency)
.memberId(member.getId())
.gameCode("1")
.gameType(PlatformType.SPORTS.getCode()) // 体育
.platformCode(GamePlatforms.DBSports.getCode())
.gameId(GamePlatforms.DBSports.getCode() + "_1")
.gameName(GamePlatforms.DBSports.getInfo())
.gameStatus(compareResult > 0 ? GameStatus.WIN.getCode() : compareResult < 0 ? GameStatus.FAIL.getCode() : GameStatus.FLAT.getCode())
.gameStatusType(1) // 一般下注
.gameCurrencyCode(dataBean.getCurrency())
.account(dataBean.getUserName())
.wagersId(dataBean.getOrderNo())
.wagersTime(createTime)
.betAmount(betAmount)
.payoffTime(payoffTime)
.payoffAmount(originPayoffAmount.abs())
.settlementTime(payoffTime)
.turnover(betAmount)
.orderNo(dataBean.getOrderNo())
.settlementStatus(SettlementStatusEnum.COMPLETED.getCode())
.build();
gameBettingDetails.setCreateBy(Constants.SYSTEM);
gameBettingDetails.setCreateTime(DateUtils.getNowDate());
return gameBettingDetails;
}
}

View File

@ -0,0 +1,31 @@
package com.ff.game.api.dg.address;
import com.dtflys.forest.callback.AddressSource;
import com.dtflys.forest.http.ForestAddress;
import com.dtflys.forest.http.ForestRequest;
import com.ff.base.enums.GamePlatforms;
import com.ff.game.service.IPlatformService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* jili address
*
* @author shi
* @date 2025/02/10
*/
@Component
public class MyDGAddressSource implements AddressSource {
@Resource
private IPlatformService platformService;
@Override
public ForestAddress getAddress(ForestRequest request) {
String apiBaseUrl = platformService.get(GamePlatforms.DG.getCode())
.getUrlInfo().getUrl();
return new ForestAddress("http", apiBaseUrl, 80, "");
}
}

View File

@ -0,0 +1,116 @@
package com.ff.game.api.dg.client;
import com.dtflys.forest.annotation.*;
import com.ff.game.api.dg.address.MyDGAddressSource;
import com.ff.game.api.dg.dto.*;
import com.ff.game.api.jili.dto.*;
import com.ff.game.api.xk.address.MyXKAddressSource;
import com.ff.game.api.xk.dto.*;
import java.util.Map;
/**
* dg
*
* @author shi
* @date 2025/02/10
*/
@Address(source = MyDGAddressSource.class)
public interface DGClient {
/**
*
*
* @param params
* @param header
* @return {@link DGResponse }
*/
@Post("/v2/api/signup")
DGResponse createMember(@JSONBody Map<String, Object> params, @Header Map<String, Object> header);
/**
*
*
* @param params
*/
@Post("/v2/api/balance")
DGUserAccountResponse getMemberInfo(@JSONBody Map<String, Object> params, @Header Map<String, Object> header);
/**
*
*
* @param params
* @param header
* @return {@link DGLoginWithoutRedirectResponse }
*/
@Post("/v2/api/login")
DGLoginWithoutRedirectResponse loginWithoutRedirect(@JSONBody Map<String, Object> params, @Header Map<String, Object> header);
/**
* id
*
* @param params
* @return {@link DGTransactionResponseDTO }
*/
@Post(url = "/v2/api/transfer")
DGTransactionResponseDTO exchangeTransferByAgentId(@JSONBody Map<String, Object> params, @Header Map<String, Object> header);
/**
*
*
* @param params
* @param header
* @return {@link DGTransactionResponseDTO }
*/
@Post(url = "/v2/api/checkTransfer")
DGTransactionResponseDTO exchangeTransferStatus(@JSONBody Map<String, Object> params, @Header Map<String, Object> header);
/**
*
*
* @param params
* @return {@link XKBetRecordResponseDTO }
*/
@Post(url ="/v2/api/report",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
DGBetRecordResponseDTO getBetRecordByTime(@Header Map<String, Object> params);
/**
*
*
* @param params
* @return {@link DGResponse }
*/
@Post(url = "/v2/api/markReport")
DGResponse markReport(@JSONBody Map<String, Object> params, @Header Map<String, Object> header);
/**
*
*
* @param params
* @return {@link DGResponse }
*/
@Post("/v2/api/offline")
DGResponse kickMember(@JSONBody Map<String, Object> params, @Header Map<String, Object> header);
@Post("/v2/api/offlineAll")
DGResponse kickMemberAll(@JSONBody Map<String, Object> params, @Header Map<String, Object> header);
/**
* 线
*
* @param params
* @return {@link DGUserListResponseDTO }
*/
@Post(url ="/v2/api/online",
headers = {
"Content-type: application/x-www-form-urlencoded"
})
DGUserListResponseDTO memberOnlines(@Header Map<String, Object> params);
}

View File

@ -0,0 +1,129 @@
package com.ff.game.api.dg.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
import java.util.Date;
import java.util.List;
/**
* dgbet
*
* @author shi
* @date 2025/03/27
*/
@Data
public class DGBetRecordResponseDTO {
/** 错误码 (参考文档定义) */
@JsonProperty("codeId")
private Integer codeId;
/** 错误信息 */
@JsonProperty("msg")
private String msg;
/** 注单报告 */
@JsonProperty("list")
private List<ReportDTO> list;
/**
*
*/
@Data
public static class ReportDTO {
/** 注单ID唯一 */
@JsonProperty("id")
private Long id;
/** 游戏桌号(红包小费记录没有) */
@JsonProperty("tableId")
private Integer tableId;
/** 游戏靴号(红包小费记录没有) */
@JsonProperty("shoeId")
private Long shoeId;
/** 当靴局号(红包小费记录没有) */
@JsonProperty("playId")
private Long playId;
/** 游戏厅号1:旗舰厅2:亚洲厅34:现场厅5:性感厅8,9:区块链厅) */
@JsonProperty("lobbyId")
private Integer lobbyId;
/** 注单类型1:注单2:红包小费) */
@JsonProperty("gameType")
private Integer gameType;
/** 游戏类型(百家乐,龙虎等) */
@JsonProperty("gameId")
private Integer gameId;
/** 下注时间 */
@JsonProperty("betTime")
private Date betTime;
/** 结算时间 */
@JsonProperty("calTime")
private Date calTime;
/** 派彩金额(含本金) */
@JsonProperty("winOrLoss")
private BigDecimal winOrLoss;
/** 下注前余额(仅作参考) */
@JsonProperty("balanceBefore")
private BigDecimal balanceBefore;
/** 下注金额(下注扣款金额) */
@JsonProperty("betPoints")
private BigDecimal betPoints;
/** 洗码金额(用于计算佣金) */
@JsonProperty("availableBet")
private BigDecimal availableBet;
/** 会员账号 */
@JsonProperty("userName")
private String userName;
/** 游戏结果 */
@JsonProperty("result")
private String result;
/** 注单详情 */
@JsonProperty("betDetail")
private String betDetail;
/** 客户端IP */
@JsonProperty("ip")
private String ip;
/** 游戏唯一局号 */
@JsonProperty("ext")
private String ext;
/** 结算状态0:未结算1:已结算2:已撤销3:冻结) */
@JsonProperty("isRevocation")
private Integer isRevocation;
/** 更改单时对应的注单记录 */
@JsonProperty("parentBetId")
private Long parentBetId;
/** 币种ID请参考对应关系说明 */
@JsonProperty("currencyId")
private Integer currencyId;
/** 客户端平台ID1: PC 2: 安卓 3: 苹果 5: H5 */
@JsonProperty("deviceType")
private Integer deviceType;
/** 钱包扣款记录转账模式API没有 */
@JsonProperty("transfers")
private String transfers;
}
}

View File

@ -0,0 +1,74 @@
package com.ff.game.api.dg.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
*
*/
@Data
public class DGLoginWithoutRedirectResponse {
/**
* ID
*/
@JsonProperty("codeId")
private int codeId;
/**
*
*/
@JsonProperty("msg")
private String msg;
/**
* token
*/
@JsonProperty("token")
private String token;
/**
*
*/
@JsonProperty("domains")
private String domains;
/**
*
*/
@JsonProperty("list")
private List<String> list;
/**
*
*/
@JsonProperty("limitGroup")
private String limitGroup;
/**
*
*/
@JsonProperty("limits")
private List<Limit> limits;
/**
*
*/
@Data
public static class Limit {
/**
*
*/
@JsonProperty("min")
private int min;
/**
*
*/
@JsonProperty("max")
private int max;
}
}

View File

@ -0,0 +1,26 @@
package com.ff.game.api.dg.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
/**
*
*
* @author shi
* @date 2025/03/26
*/
@Data
public class DGResponse {
/**
* ID
*/
@JsonProperty("codeId")
private int codeId;
/**
*
*/
@JsonProperty("msg")
private String msg;
}

View File

@ -0,0 +1,45 @@
package com.ff.game.api.dg.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
*
*
* @author shi
* @date 2025/03/27
*/
@Data
public class DGTransactionResponseDTO {
/** 响应代码 */
@JsonProperty("codeId")
private Integer codeId;
/** 响应消息 */
@JsonProperty("msg")
private String msg;
/** 用户名 */
@JsonProperty("username")
private String username;
/** 交易金额 */
@JsonProperty("amount")
private BigDecimal amount;
/** 账户余额 */
@JsonProperty("balance")
private BigDecimal balance;
/** 交易流水号 */
@JsonProperty("serial")
private String serial;
/** 交易时间 */
@JsonProperty("time")
private String time;
}

View File

@ -0,0 +1,40 @@
package com.ff.game.api.dg.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.math.BigDecimal;
/**
*
*
* @author shi
* @date 2025/03/26
*/
@Data
public class DGUserAccountResponse {
/**
* ID
*/
@JsonProperty("codeId")
private int codeId;
/**
*
*/
@JsonProperty("msg")
private String msg;
/**
*
*/
@JsonProperty("username")
private String username;
/**
*
*/
@JsonProperty("balance")
private BigDecimal balance;
}

View File

@ -0,0 +1,70 @@
package com.ff.game.api.dg.dto;
import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.Data;
import java.util.List;
/**
*
*
* @author shi
* @date 2025/03/27
*/
@Data
public class DGUserListResponseDTO {
/**
* ID
*/
@JsonProperty("codeId")
private int codeId;
/**
*
*/
@JsonProperty("msg")
private String msg;
/** 用户列表 */
@JsonProperty("list")
private List<UserDTO> list;
/**
*
*/
@Data
public static class UserDTO {
/** 用户名 */
@JsonProperty("username")
private String username;
/** 昵称 */
@JsonProperty("nickname")
private String nickname;
/** 币种名称 */
@JsonProperty("currencyName")
private String currencyName;
/** 用户IP */
@JsonProperty("ip")
private String ip;
/** 用户设备 */
@JsonProperty("device")
private String device;
/** 登录时间 */
@JsonProperty("login")
private String login;
/** 会员ID */
@JsonProperty("memberId")
private Long memberId;
/** 用户余额 */
@JsonProperty("balance")
private Double balance;
}
}

View File

@ -0,0 +1,595 @@
package com.ff.game.api.dg.service.impl;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.NumberUtil;
import com.ff.base.constant.CacheConstants;
import com.ff.base.constant.Constants;
import com.ff.base.enums.*;
import com.ff.base.exception.base.ApiException;
import com.ff.base.exception.base.BaseException;
import com.ff.base.utils.DateUtils;
import com.ff.base.utils.SleepUtil;
import com.ff.base.utils.StringUtils;
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.ae.dto.AEExchangeTransferStatusResponse;
import com.ff.game.api.dg.client.DGClient;
import com.ff.game.api.dg.dto.*;
import com.ff.game.api.request.*;
import com.ff.game.domain.*;
import com.ff.game.service.IGameBettingDetailsService;
import com.ff.game.service.IGameExchangeMoneyService;
import com.ff.game.service.IGameService;
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.util.*;
import java.util.stream.Collectors;
/**
* DG impl
*
* @author shi
* @date 2024/11/12
*/
@Service("DGService")
@Slf4j
public class GamesDGServiceImpl implements IGamesService {
@Resource
private IGameExchangeMoneyService gameExchangeMoneyService;
@Resource
private IGameService gameService;
@Resource
private IMemberService memberService;
@Resource
private DGClient DGClient;
@Resource
private KeyConfig keyConfig;
@Resource
private IGameBettingDetailsService gameBettingDetailsService;
/**
* id
*/
private static final Long GAME_ID = 1904452832756003817L;
/**
*
*
* @param errorCode
* @return {@link Boolean }
*/
private Boolean getIsSuccess(Integer errorCode) {
ApiException.isTrue(103 != errorCode, ErrorCode.GAME_ACCOUNT_CREATION_FAILED.getCode());
return 0 == errorCode;
}
/**
*
*
* @param gamesBaseRequestDTO dto
* @return {@link String }
*/
private Map<String, Object> getKey(GamesBaseRequestDTO gamesBaseRequestDTO) {
Long time = DateUtils.getNowDate();
//取出对应的key跟密钥跟请求参数
String agentKey = gamesBaseRequestDTO.getAgentKey();
String agentId = gamesBaseRequestDTO.getAgentId();
String sign = Md5Utils.md5New(agentId + agentKey + time);
Map<String, Object> keyMap = new HashMap<>();
keyMap.put("agent", agentId);
keyMap.put("sign", sign);
keyMap.put("time", time);
return keyMap;
}
/**
*
*
* @param createMemberRequestDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean createMember(CreateMemberRequestDTO createMemberRequestDTO) {
log.info("GamesDGServiceImpl [createMember] 请求参数 {}", createMemberRequestDTO);
Map<String, Object> params = new LinkedHashMap<>();
params.put("username", createMemberRequestDTO.getAccount());
params.put("currencyName", createMemberRequestDTO.getCurrency());
params.put("winLimit", 0);
params.put("password", Md5Utils.md5New(createMemberRequestDTO.getAccount()));
Map<String, Object> headerMap = this.getKey(createMemberRequestDTO);
DGResponse response = DGClient.createMember(params, headerMap);
if (this.getIsSuccess(response.getCodeId())) {
return Boolean.TRUE;
}
//判断是否获取成功
return Boolean.FALSE;
}
/**
*
*
* @param memberInfoRequestDTO dto
* @return {@link MemberInfoResponseDTO }
*/
@Override
public MemberInfoResponseDTO getMemberInfo(MemberInfoRequestDTO memberInfoRequestDTO) {
log.info("GamesDGServiceImpl [getMemberInfo] 请求参数 {}", memberInfoRequestDTO);
Map<String, Object> paramsMap = new HashMap<>();
paramsMap.put("username", memberInfoRequestDTO.getAccounts());
Map<String, Object> headerMap = this.getKey(memberInfoRequestDTO);
DGUserAccountResponse memberInfo = DGClient.getMemberInfo(paramsMap, headerMap);
int errorCode = memberInfo.getCodeId();
if (this.getIsSuccess(errorCode)) {
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("GamesDGServiceImpl [loginWithoutRedirect] 请求参数 {}", gamesLogin);
Map<String, Object> paramsMap = new HashMap<>();
paramsMap.put("username", gamesLogin.getAccount());
paramsMap.put("currencyName", gamesLogin.getCurrency());
paramsMap.put("winLimit", 0);
paramsMap.put("language", gamesLogin.getLang());
Map<String, Object> headerMap = this.getKey(gamesLogin);
DGLoginWithoutRedirectResponse loginWithoutRedirectResponse = DGClient.loginWithoutRedirect(paramsMap, headerMap);
if (this.getIsSuccess(loginWithoutRedirectResponse.getCodeId())) {
String url = loginWithoutRedirectResponse.getList().get(0) + "&showapp=off";
if (!StringUtils.isEmpty(gamesLogin.getHomeUrl())) {
url += "&backUrl=" + gamesLogin.getHomeUrl();
}
return url;
} else {
throw new ApiException(ErrorCode.ACCOUNT_NOT_EXIST.getCode());
}
}
/**
*
*
* @param gamesBaseRequestDTO dto
* @return {@link String }
*/
@Transactional
@Override
public String getGameList(GamesBaseRequestDTO gamesBaseRequestDTO) {
Game game = gameService.selectGameById(GAME_ID);
//不存在这个游戏
if (ObjectUtils.isEmpty(game)) {
game = new Game();
game.setId(GAME_ID);
game.setSortNo(1);
game.setPlatformCode(GamePlatforms.DG.getCode());
game.setPlatformType(PlatformType.VIDEO.getCode());
game.setGameCode("1");
game.setGameSourceType(String.valueOf(1));
game.setGameName("真人棋牌");
game.setCreateBy(Constants.SYSTEM);
game.setNameInfo(Collections.singletonList(new NameInfo("真人棋牌", "zh-CN")));
game.setGameId(StringUtils.addSuffix(GamePlatforms.DG.getCode(), 1));
gameService.insertGame(game);
}else {
game.setNameInfo(Collections.singletonList(new NameInfo("真人棋牌", "zh-CN")));
gameService.updateGame(game);
}
return CacheConstants.DG_GAMES;
}
/**
* id
*
* @param transactionIdRequestDTO iddto
* @return {@link String }
*/
@Override
public String getTransactionId(TransactionIdRequestDTO transactionIdRequestDTO) {
return GamePlatforms.DG.getInfo() + IdUtils.simpleUUID();
}
/**
* id
*
* @param exchangeTransferMoneyRequestDTO moeny dto
* @return {@link Long }
*/
@Override
@Transactional
public Long exchangeTransferByAgentId(ExchangeTransferMoneyRequestDTO exchangeTransferMoneyRequestDTO) {
log.info("GamesDGServiceImpl [exchangeTransferByAgentId] 请求参数 {}", exchangeTransferMoneyRequestDTO);
GameExchangeMoney exchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(exchangeTransferMoneyRequestDTO.getGameExchangeId());
BigDecimal amount = exchangeTransferMoneyRequestDTO.getAmount();
if (TransferType.ALL.getCode().equals(exchangeTransferMoneyRequestDTO.getTransferType())) {
// 获取第三方钱包余额
MemberInfoRequestDTO gamesBaseRequestDTO = MemberInfoRequestDTO.builder()
.accounts(exchangeTransferMoneyRequestDTO.getAccount())
.agentId(exchangeTransferMoneyRequestDTO.getAgentId())
.currency(exchangeTransferMoneyRequestDTO.getCurrency())
.agentKey(exchangeTransferMoneyRequestDTO.getAgentKey())
.build();
BigDecimal balance = this.getMemberInfo(gamesBaseRequestDTO).getBalance();
amount = balance.compareTo(BigDecimal.ZERO) > 0 ? balance.negate() : balance;
}
Map<String, Object> params = new LinkedHashMap<>();
params.put("username", exchangeTransferMoneyRequestDTO.getAccount());
params.put("amount", amount);
params.put("serial", exchangeTransferMoneyRequestDTO.getTransactionId());
Map<String, Object> headerMap = this.getKey(exchangeTransferMoneyRequestDTO);
DGTransactionResponseDTO dgTransactionResponseDTO = DGClient.exchangeTransferByAgentId(params, headerMap);
//判断是否转移成功
if (this.getIsSuccess(dgTransactionResponseDTO.getCodeId())) {
//更新数据
exchangeMoney.setBalance(dgTransactionResponseDTO.getAmount().abs());
exchangeMoney.setCoinBefore(NumberUtil.sub(dgTransactionResponseDTO.getBalance(), dgTransactionResponseDTO.getAmount().abs()).abs());
exchangeMoney.setCoinAfter(dgTransactionResponseDTO.getBalance());
exchangeMoney.setCurrencyBefore(exchangeMoney.getCoinBefore());
exchangeMoney.setCurrencyAfter(exchangeMoney.getCoinAfter());
exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
exchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode());
gameExchangeMoneyService.updateGameExchangeMoney(exchangeMoney);
} else {
exchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
exchangeMoney.setStepStatus(GameExchangeStepStatus.FAILURE.getCode());
gameExchangeMoneyService.updateGameExchangeMoney(exchangeMoney);
log.error("GamesDGServiceImpl [exchangeTransferByAgentId] 金额转移失败,错误代码{},错误信息{}", dgTransactionResponseDTO.getCodeId(), dgTransactionResponseDTO.getMsg());
throw new ApiException(ErrorCode.BALANCE_TRANSFER_FAILED.getCode());
}
return exchangeMoney.getId();
}
/**
*
*
* @param exchangeTransferMoneyRequestDTO dto
* @return {@link ExchangeTransferStatusResponseDTO }
*/
@Override
public ExchangeTransferStatusResponseDTO exchangeTransferStatus(ExchangeTransferStatusRequestDTO exchangeTransferMoneyRequestDTO) {
log.info("GamesDGServiceImpl [exchangeTransferStatus] 请求参数 {}", exchangeTransferMoneyRequestDTO);
Map<String, Object> params = new LinkedHashMap<>();
params.put("serial", exchangeTransferMoneyRequestDTO.getOrderId());
Map<String, Object> headerMap = this.getKey(exchangeTransferMoneyRequestDTO);
DGTransactionResponseDTO dgTransactionResponseDTO = DGClient.exchangeTransferStatus(params, headerMap);
Integer status = StatusType.IN_PROGRESS.getValue();
if (this.getIsSuccess(dgTransactionResponseDTO.getCodeId())) {
status = StatusType.SUCCESS.getValue();
} else {
status = StatusType.FAILURE.getValue();
}
return ExchangeTransferStatusResponseDTO.builder()
.statusType(status)
.balance(dgTransactionResponseDTO.getAmount().abs())
.coinBefore(NumberUtil.sub(dgTransactionResponseDTO.getBalance(), dgTransactionResponseDTO.getAmount().abs()).abs())
.coinAfter(dgTransactionResponseDTO.getBalance())
.build();
}
/**
*
*
* @param betRecordByTimeDTO dto
* @return {@link List }<{@link GameBettingDetails }>
*/
@Override
public Boolean getBetRecordByTime(BetRecordByTimeDTO betRecordByTimeDTO) {
SleepUtil.sleep(50000);
//请求参数
log.info("GamesDGServiceImpl [getBetRecordByTime] 请求参数 {}", betRecordByTimeDTO);
Map<String, Object> key = this.getKey(betRecordByTimeDTO);
DGBetRecordResponseDTO betRecordByTime = DGClient.getBetRecordByTime(key);
if (this.getIsSuccess(betRecordByTime.getCodeId())) {
this.batchInsert(betRecordByTime, betRecordByTimeDTO);
return Boolean.TRUE;
} else {
log.error("GamesDGServiceImpl [getBetRecordByTime] 获取投注记录失败,错误代码{},错误信息{}", betRecordByTime.getCodeId(), betRecordByTime.getMsg());
throw new BaseException(betRecordByTime.getMsg());
}
}
/**
*
*
* @param betRecordByTimeDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean getBetRecordByHistoryTime(BetRecordByTimeDTO betRecordByTimeDTO) {
return Boolean.FALSE;
}
/**
*
*
* @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("GamesDGServiceImpl [kickMember] 请求参数 {}", kickMemberRequestDTO);
Map<String, Object> key = this.getKey(kickMemberRequestDTO);
DGUserListResponseDTO userListResponseDTO = DGClient.memberOnlines(key);
if (!this.getIsSuccess(userListResponseDTO.getCodeId())) {
throw new ApiException(ErrorCode.KICK_OUT_AILED.getCode());
}
if (CollectionUtils.isEmpty(userListResponseDTO.getList())) {
return Boolean.TRUE;
}
DGUserListResponseDTO.UserDTO userDTO = userListResponseDTO.getList().stream()
.filter(user -> user.getUsername().equals(kickMemberRequestDTO.getAccount()))
.findFirst()
.orElse(null);
if (Objects.isNull(userDTO)) {
return Boolean.TRUE;
}
List<Long> memberIds = new ArrayList<>();
memberIds.add(userDTO.getMemberId());
Map<String, Object> params = new LinkedHashMap<>();
params.put("list", memberIds);
DGResponse dgResponse = DGClient.kickMember(params, key);
//判断是否获取成功
if (this.getIsSuccess(dgResponse.getCodeId())) {
return Boolean.TRUE;
} else {
throw new BaseException(dgResponse.getMsg());
}
}
/**
*
*
* @param kickMemberAllDTO dto
* @return {@link Boolean }
*/
@Override
public Boolean kickMemberAll(KickMemberAllDTO kickMemberAllDTO) {
log.info("GamesDGServiceImpl [kickMemberAll] 请求参数 {}", kickMemberAllDTO);
Map<String, Object> key = this.getKey(kickMemberAllDTO);
DGUserListResponseDTO userListResponseDTO = DGClient.memberOnlines(key);
if (!this.getIsSuccess(userListResponseDTO.getCodeId())) {
throw new ApiException(ErrorCode.KICK_OUT_AILED.getCode());
}
if (CollectionUtils.isEmpty(userListResponseDTO.getList())) {
return Boolean.TRUE;
}
List<Long> memberIds = userListResponseDTO.getList().stream().map(DGUserListResponseDTO.UserDTO::getMemberId).collect(Collectors.toList());
Map<String, Object> params = new LinkedHashMap<>();
params.put("list", memberIds);
DGResponse dgResponse = DGClient.kickMember(params, key);
//判断是否获取成功
if (this.getIsSuccess(dgResponse.getCodeId())) {
return Boolean.TRUE;
} else {
throw new BaseException(dgResponse.getMsg());
}
}
/**
* 使
*
* @param getFreeSpinDashflowRequestDTO dashflowdto
* @return {@link List }<{@link GameFreeRecord }>
*/
@Override
public List<GameFreeRecord> getFreeSpinDashflow(GetFreeSpinDashflowRequestDTO getFreeSpinDashflowRequestDTO) {
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());
}
/**
*
*
* @param cancelFreeSpinRequestDTO
* @return {@link Boolean }
*/
@Override
public Boolean cancelFreeSpin(CancelFreeSpinRequestDTO cancelFreeSpinRequestDTO) {
throw new ApiException(ErrorCode.PLATFORM_NOT_METHODS.getCode());
}
/**
*
*
* @param dgBetRecordResponseDTO dgdto
*/
private void batchInsert(DGBetRecordResponseDTO dgBetRecordResponseDTO, GamesBaseRequestDTO gamesBaseRequestDTO) {
List<GameBettingDetails> gameBettingDetails = new ArrayList<>();
List<String> wagersIds = new ArrayList<>();
//数据组装
List<DGBetRecordResponseDTO.ReportDTO> report = dgBetRecordResponseDTO.getList();
//数据转化
for (DGBetRecordResponseDTO.ReportDTO bean : report) {
GameBettingDetails bettingDetails = this.dataBuild(GamesDataBuildDTO.builder()
.platform(gamesBaseRequestDTO.getVendor())
.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<String> removeWagersIds = gameBettingDetailsService.selectGameBettingDetailsByWagersId(wagersIds, GamePlatforms.DG.getInfo());
//用steam流清除list中与wagersIds集合相同的数据
gameBettingDetails = gameBettingDetails.stream()
.filter(detail -> !removeWagersIds.contains(detail.getWagersId()))
.collect(Collectors.toList());
if (!CollectionUtils.isEmpty(gameBettingDetails)) {
gameBettingDetailsService.batchInsert(gameBettingDetails);
Map<String, Object> key = this.getKey(gamesBaseRequestDTO);
Map<String, Object> params = new HashMap<>();
params.put("list", wagersIds);
DGClient.markReport(params, key);
}
}
}
/**
*
*
* @param gamesDataBuildDTO
* @return {@link GameBettingDetails }
*/
@Override
public GameBettingDetails dataBuild(GamesDataBuildDTO gamesDataBuildDTO) {
//转化类
DGBetRecordResponseDTO.ReportDTO resultBean = (DGBetRecordResponseDTO.ReportDTO) gamesDataBuildDTO.getData();
// GameSecretKeyCurrency currencyDTO = gameSecretKeyCurrencyService.findByGameSecretKeyCurrencyDTO(GameSecretKeyCurrencyDTO.builder()
// .platformCode(GamePlatforms.DG.getInfo())
// .currencyId(String.valueOf(resultBean.getCurrencyId()))
// .build());
Member member = memberService.selectMemberByGameAccount(resultBean.getUserName());
if (ObjectUtils.isEmpty(member)) {
return null;
}
//未结算的不要
if (0 == resultBean.getIsRevocation()) {
return null;
}
BigDecimal payoffAmount = BigDecimal.ZERO;
BigDecimal winOrLoss = resultBean.getWinOrLoss();
//输赢状态
Integer gameStatus = GameStatus.FLAT.getCode();
if (winOrLoss.compareTo(resultBean.getBetPoints()) > 0) {
payoffAmount = NumberUtil.sub(winOrLoss, resultBean.getBetPoints());
gameStatus = GameStatus.WIN.getCode();
} else if (winOrLoss.compareTo(resultBean.getBetPoints()) < 0) {
payoffAmount = NumberUtil.sub(resultBean.getWinOrLoss(), resultBean.getBetPoints()).negate();
gameStatus = GameStatus.FAIL.getCode();
}
Game game = gameService.selectGameById(GAME_ID);
//数据构造
GameBettingDetails gameBettingDetails = GameBettingDetails.builder()
.tenantKey(member.getTenantKey())
//保存我们的币种id
.currencyCode(gamesDataBuildDTO.getPlatform().getExtInfo().getOurCurrency(String.valueOf(resultBean.getCurrencyId())))
.memberId(member.getId())
.gameCode(String.valueOf(resultBean.getGameId()))
.gameType(PlatformType.CARD_GAME.getCode())
.platformCode(GamePlatforms.DG.getCode())
//.gameId(GAME_ID)
.gameId(game.getGameId())
.gameName(game.getGameName())
.gameStatus(gameStatus)
.gameStatusType(resultBean.getGameType())
.gameCurrencyCode(/*gamesDataBuildDTO.getCurrencyCode()*/String.valueOf(resultBean.getCurrencyId()))
.account(String.valueOf(resultBean.getUserName()))
.wagersId(String.valueOf(resultBean.getId()))
.wagersTime(resultBean.getBetTime().getTime())
.betAmount(resultBean.getBetPoints())
.betContent(resultBean.getBetDetail())
.payoffTime(resultBean.getCalTime().getTime())
.payoffAmount(payoffAmount)
.settlementTime(resultBean.getCalTime().getTime())
.turnover(resultBean.getAvailableBet())
.settlementStatus(SettlementStatusEnum.COMPLETED.getCode())
.build();
gameBettingDetails.setCreateBy(Constants.SYSTEM);
gameBettingDetails.setCreateTime(DateUtils.getNowDate());
return gameBettingDetails;
}
}

View File

@ -0,0 +1,118 @@
package com.ff.game.api.exchange;
import com.ff.base.enums.GameExchangeStep;
import com.ff.base.enums.GameExchangeStepStatus;
import com.ff.base.exception.base.BaseException;
import com.ff.game.api.exchange.dto.GameExchangeDTO;
import com.ff.game.domain.GameExchangeMoney;
import com.ff.game.service.IGameExchangeMoneyService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Resource;
/**
*
*
* @author shi
* @date 2025/04/09
*/
@Slf4j
public abstract class AbstractStepProcessor implements StepProcessorService {
@Resource
private StepProcessorFactory stepProcessorFactory;
@Resource
private IGameExchangeMoneyService gameExchangeMoneyService;
/**
*
*
* @param gameExchangeMoney
*/
@Override
public void process(GameExchangeDTO gameExchangeMoney) {
boolean processResult = false;
try {
processResult = doProcess(gameExchangeMoney);
} catch (Exception e) {
log.error("游戏余额转移{}-步骤任务{}-执行异常", gameExchangeMoney.getId(), stepKey().getDescription(), e);
throw new BaseException("游戏余额转移"+gameExchangeMoney.getId()+"-步骤任务"+stepKey().getDescription()+"-执行异常" );
}
//更新状态
if (processResult) {
log.info("游戏余额转移{}-步骤任务{}-执行成功", gameExchangeMoney.getId(), stepKey().getDescription());
triggerNextStep(gameExchangeMoney);
}
}
/**
*
*
* @param gameExchangeMoney
*/
@Override
public void rollBack(GameExchangeDTO gameExchangeMoney){
boolean processResult = false;
try {
processResult = doRollBack(gameExchangeMoney);
} catch (Exception e) {
log.error("游戏余额转移{}-步骤任务{}-回滚异常", gameExchangeMoney.getId(), stepKey().getDescription(), e);
}
if (processResult) {
log.info("游戏余额转移{}-步骤任务{}-回滚成功", gameExchangeMoney.getId(), stepKey().getDescription());
triggerBackStep(gameExchangeMoney);
}
}
/**
* 退
*
* @param exchangeMoney
*/
private void triggerBackStep(GameExchangeDTO exchangeMoney) {
GameExchangeStep nextStep = backStepProcessor();
if (nextStep != null) {
stepProcessorFactory.getStepProcessor(nextStep).rollBack(exchangeMoney);
}
}
/**
*
*
* @param exchangeMoney
*/
private void triggerNextStep(GameExchangeDTO exchangeMoney) {
GameExchangeStep nextStep = nextStepProcessor();
if (nextStep != null) {
stepProcessorFactory.getStepProcessor(nextStep).process(exchangeMoney);
}
}
/**
*
*
* @param gameExchangeMoney
* @return boolean
* @throws Exception
*/
abstract public boolean doRollBack(GameExchangeDTO gameExchangeMoney) throws Exception;
/**
* do
*
* @param gameExchangeMoney
* @return boolean
* @throws Exception
*/
abstract public boolean doProcess(GameExchangeDTO gameExchangeMoney) throws Exception;
}

View File

@ -0,0 +1,47 @@
package com.ff.game.api.exchange;
import com.ff.base.enums.GameExchangeStep;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import javax.annotation.PostConstruct;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
*
*
* @author shi
* @date 2025/04/09
*/
@Component
public class StepProcessorFactory {
@Autowired
List<StepProcessorService> stepProcessors;
private Map<GameExchangeStep, StepProcessorService> processorMap;
@PostConstruct
public void init() {
Map<GameExchangeStep, StepProcessorService> map = new HashMap<>();
for (StepProcessorService stepProcessor : stepProcessors) {
map.put(stepProcessor.stepKey(), stepProcessor);
}
processorMap = Collections.unmodifiableMap(map);
}
/**
*
*
* @param stepKey
* @return {@link StepProcessorService }
*/
public StepProcessorService getStepProcessor(GameExchangeStep stepKey) {
return processorMap.get(stepKey);
}
}

View File

@ -0,0 +1,56 @@
package com.ff.game.api.exchange;
import com.ff.base.enums.GameExchangeStep;
import com.ff.game.api.exchange.dto.GameExchangeDTO;
/**
*
*
* @author shi
* @date 2025/04/09
*/
public interface StepProcessorService {
/**
*
*
* @return {@link GameExchangeStep }
*/
GameExchangeStep stepKey();
/**
*
*
* @param gameExchangeMoney
*/
void process(GameExchangeDTO gameExchangeMoney);
/**
* 退
*
* @param gameExchangeMoney
*/
void rollBack(GameExchangeDTO gameExchangeMoney);
/**
*
*
*
* @return
*/
GameExchangeStep nextStepProcessor();
/**
*
*
*
* @return {@link GameExchangeStep }
*/
GameExchangeStep backStepProcessor();
}

View File

@ -0,0 +1,32 @@
package com.ff.game.api.exchange.dto;
import com.ff.base.annotation.Excel;
import com.ff.game.domain.GameExchangeMoney;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.SuperBuilder;
/**
* dto
*
* @author shi
* @date 2025/04/09
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
@SuperBuilder
public class GameExchangeDTO extends GameExchangeMoney {
/**
* 1 2
*/
private Integer triggerType;
/** 会员账号 */
private String memberAccount;
/** 游戏账号 */
private String gameAccount;
}

View File

@ -0,0 +1,170 @@
package com.ff.game.api.exchange.impl;
import com.ff.base.constant.Constants;
import com.ff.base.enums.*;
import com.ff.base.exception.base.ApiException;
import com.ff.base.utils.StringUtils;
import com.ff.common.domain.TenantGameQuotaFlow;
import com.ff.common.dto.BalanceChangesDTO;
import com.ff.common.dto.GameBalanceExchange;
import com.ff.common.service.ITenantGameQuotaFlowService;
import com.ff.common.service.ITenantGameQuotaService;
import com.ff.game.api.IGamesService;
import com.ff.game.api.exchange.AbstractStepProcessor;
import com.ff.game.api.exchange.dto.GameExchangeDTO;
import com.ff.game.api.request.ExchangeTransferMoneyRequestDTO;
import com.ff.game.api.request.ExchangeTransferStatusRequestDTO;
import com.ff.game.api.request.ExchangeTransferStatusResponseDTO;
import com.ff.game.domain.KeyInfo;
import com.ff.game.domain.Platform;
import com.ff.game.service.IGameExchangeMoneyService;
import com.ff.game.service.IPlatformService;
import com.ff.member.domain.Member;
import com.ff.member.service.IMemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
/**
*
*
* @author shi
* @date 2025/04/09
*/
@Service
@Order(4)
public class AddBalanceServiceImpl extends AbstractStepProcessor {
@Resource
private IMemberService memberService;
@Resource
private ITenantGameQuotaService tenantGameQuotaService;
@Resource
private IGameExchangeMoneyService gameExchangeMoneyService;
@Resource
private PlatformTransactionManager transactionManager;
@Resource
private IPlatformService platformService;
/**
*
*
* @return {@link GameExchangeStep }
*/
@Override
public GameExchangeStep stepKey() {
return GameExchangeStep.TENANT_QUOTA_DEDUCTED;
}
/**
* do
*
* @param gameExchangeMoney
* @return boolean
*/
@Override
public boolean doProcess(GameExchangeDTO gameExchangeMoney) {
//如果不是之前的步骤代码执行过了 或者是转入操作
if (!gameExchangeMoney.getStep().equals(GameExchangeStep.PLATFORM_TRANSACTION.getCode()) ) {
return Boolean.TRUE;
}
if (!gameExchangeMoney.getStepStatus().equals(StatusType.SUCCESS.getValue())){
return Boolean.TRUE;
}
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
Boolean result = transactionTemplate.execute(transactionStatus -> {
//不是转入操作
if (!TransferType.ALL.getCode().equals(gameExchangeMoney.getExchangeType())) {
gameExchangeMoney.setStep(GameExchangeStep.DEDUCT_BALANCE.getCode());
gameExchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode());
gameExchangeMoney.setStatus(StatusType.SUCCESS.getValue());
return gameExchangeMoneyService.updateGameExchangeMoney(gameExchangeMoney) > 0;
}
Platform platform = platformService.get(gameExchangeMoney.getPlatformCode());
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(gameExchangeMoney.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(gameExchangeMoney.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
BigDecimal decimal = tenantGameQuotaService.gameBalanceExchange(GameBalanceExchange.builder()
.platformCode(gameExchangeMoney.getPlatformCode())
.sourceId(String.valueOf(gameExchangeMoney.getId()))
.currencyCode(gameExchangeMoney.getCurrencyCode())
.currency(gameExchangeMoney.getCurrencyCode())
.transferType(gameExchangeMoney.getExchangeType())
.amount(gameExchangeMoney.getBalance())
.isAll(Boolean.FALSE)
.account(memberService.selectMemberById(gameExchangeMoney.getMemberId()).getMemberAccount())
.tenantKey(gameExchangeMoney.getTenantKey())
.systemCurrency(gameExchangeMoney.getCurrencyCode())
.agentId(keyInfo.getCode())
.agentKey(keyInfo.getKey())
.build());
gameExchangeMoney.setQuota(decimal);
gameExchangeMoney.setStep(GameExchangeStep.TENANT_QUOTA_DEDUCTED.getCode());
gameExchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode());
gameExchangeMoney.setStatus(StatusType.SUCCESS.getValue());
return gameExchangeMoneyService.updateGameExchangeMoney(gameExchangeMoney) > 0;
});
return Boolean.TRUE.equals(result);
}
/**
*
*
* @param gameExchangeMoney
* @return boolean
*/
@Override
public boolean doRollBack(GameExchangeDTO gameExchangeMoney) {
return Boolean.TRUE;
}
/**
*
*
* @return {@link GameExchangeStep }
*/
@Override
public GameExchangeStep nextStepProcessor() {
return null;
}
/**
*
*
* @return {@link GameExchangeStep }
*/
@Override
public GameExchangeStep backStepProcessor() {
{
return null;
}
}
}

View File

@ -0,0 +1,112 @@
package com.ff.game.api.exchange.impl;
import com.ff.base.constant.Constants;
import com.ff.base.enums.*;
import com.ff.base.exception.base.ApiException;
import com.ff.base.manager.AsyncManager;
import com.ff.base.utils.DateUtils;
import com.ff.base.utils.uuid.IdUtils;
import com.ff.game.api.IGamesService;
import com.ff.game.api.exchange.AbstractStepProcessor;
import com.ff.game.api.exchange.StepProcessorFactory;
import com.ff.game.api.exchange.StepProcessorService;
import com.ff.game.api.exchange.dto.GameExchangeDTO;
import com.ff.game.api.request.ExchangeTransferStatusRequestDTO;
import com.ff.game.api.request.TransactionIdRequestDTO;
import com.ff.game.domain.GameExchangeMoney;
import com.ff.game.service.IGameExchangeMoneyService;
import com.ff.member.domain.Member;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import javax.crypto.spec.SecretKeySpec;
import java.util.Base64;
import java.util.Map;
/**
* impl
*
* @author shi
* @date 2025/04/09
*/
@Service
@Order(1)
public class CreateOrderServiceImpl extends AbstractStepProcessor {
@Resource
private IGameExchangeMoneyService gameExchangeMoneyService;
@Resource
private StepProcessorFactory stepProcessorFactory;
@Autowired
private Map<String, IGamesService> gamesService;
/**
*
*
* @return {@link GameExchangeStep }
*/
@Override
public GameExchangeStep stepKey() {
return GameExchangeStep.CREATE_ORDER;
}
/**
* do
*
* @param gameExchangeMoney
* @return boolean
*/
@Override
public boolean doProcess(GameExchangeDTO gameExchangeMoney) {
String transactionId = gamesService.get(gameExchangeMoney.getPlatformCode()+Constants.SERVICE).getTransactionId(TransactionIdRequestDTO.builder().exchangeType(gameExchangeMoney.getExchangeType()).gameAccount(gameExchangeMoney.getGameAccount()).build());
gameExchangeMoney.setTransactionId(transactionId);
gameExchangeMoney.setCreateBy(Constants.SYSTEM);
gameExchangeMoney.setStatus(StatusType.IN_PROGRESS.getValue());
gameExchangeMoney.setStep(GameExchangeStep.CREATE_ORDER.getCode());
gameExchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode());
return gameExchangeMoneyService.insertGameExchangeMoney(gameExchangeMoney) > 0;
}
/**
*
*
* @param gameExchangeMoney
* @return boolean
*/
@Override
public boolean doRollBack(GameExchangeDTO gameExchangeMoney) {
gameExchangeMoney.setStep(GameExchangeStep.CREATE_ORDER.getCode());
gameExchangeMoney.setStepStatus(GameExchangeStepStatus.FAILURE.getCode());
gameExchangeMoney.setStatus(StatusType.FAILURE.getValue());
gameExchangeMoney.setUpdateBy(Constants.SYSTEM);
gameExchangeMoney.setUpdateTime(DateUtils.getNowDate());
return gameExchangeMoneyService.updateGameExchangeMoney(gameExchangeMoney) > 0;
}
/**
*
*
* @return {@link GameExchangeStep }
*/
@Override
public GameExchangeStep nextStepProcessor() {
return GameExchangeStep.DEDUCT_BALANCE;
}
/**
*
*
* @return {@link GameExchangeStep }
*/
@Override
public GameExchangeStep backStepProcessor() {
return null;
}
}

View File

@ -0,0 +1,207 @@
package com.ff.game.api.exchange.impl;
import com.ff.base.constant.Constants;
import com.ff.base.enums.*;
import com.ff.base.exception.base.ApiException;
import com.ff.base.utils.DateUtils;
import com.ff.base.utils.QuotaUtils;
import com.ff.base.utils.StringUtils;
import com.ff.base.utils.uuid.IdUtils;
import com.ff.common.domain.TenantGameQuota;
import com.ff.common.domain.TenantGameQuotaFlow;
import com.ff.common.dto.BalanceChangesDTO;
import com.ff.common.dto.GameBalanceExchange;
import com.ff.common.service.ITenantGameQuotaFlowService;
import com.ff.common.service.ITenantGameQuotaService;
import com.ff.game.api.exchange.AbstractStepProcessor;
import com.ff.game.api.exchange.StepProcessorFactory;
import com.ff.game.api.exchange.dto.GameExchangeDTO;
import com.ff.game.domain.GameExchangeMoney;
import com.ff.game.domain.KeyInfo;
import com.ff.game.domain.Platform;
import com.ff.game.service.IGameExchangeMoneyService;
import com.ff.game.service.IPlatformService;
import com.ff.member.service.IMemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.TransactionTemplate;
import org.springframework.util.CollectionUtils;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
/**
* impl
*
* @author shi
* @date 2025/04/09
*/
@Service
@Order(2)
public class DeductBalanceServiceImpl extends AbstractStepProcessor {
@Resource
private IGameExchangeMoneyService gameExchangeMoneyService;
@Resource
private ITenantGameQuotaService tenantGameQuotaService;
@Resource
private IMemberService memberService;
@Resource
private IPlatformService platformService;
@Resource
private ITenantGameQuotaFlowService tenantGameQuotaFlowService;
@Autowired
private PlatformTransactionManager transactionManager;
/**
*
*
* @return {@link GameExchangeStep }
*/
@Override
public GameExchangeStep stepKey() {
return GameExchangeStep.DEDUCT_BALANCE;
}
/**
* do
*
* @param gameExchangeMoney
* @return boolean
*/
@Override
public boolean doProcess(GameExchangeDTO gameExchangeMoney) {
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
Boolean result = transactionTemplate.execute(transactionStatus -> {
//如果不是之前的步骤代码执行过了
if (!gameExchangeMoney.getStep().equals(this.backStepProcessor().getCode())) {
return Boolean.TRUE;
}
//不是转入操作
if (!TransferType.GAMES.getCode().equals(gameExchangeMoney.getExchangeType())) {
gameExchangeMoney.setStep(GameExchangeStep.DEDUCT_BALANCE.getCode());
gameExchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode());
return gameExchangeMoneyService.updateGameExchangeMoney(gameExchangeMoney) > 0;
}
Platform platform = platformService.get(gameExchangeMoney.getPlatformCode());
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(gameExchangeMoney.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(gameExchangeMoney.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
BigDecimal decimal = tenantGameQuotaService.gameBalanceExchange(GameBalanceExchange.builder()
.platformCode(gameExchangeMoney.getPlatformCode())
.sourceId(String.valueOf(gameExchangeMoney.getId()))
.currencyCode(gameExchangeMoney.getCurrencyCode())
.currency(gameExchangeMoney.getCurrencyCode())
.transferType(gameExchangeMoney.getExchangeType())
.amount(gameExchangeMoney.getBalance())
.account(memberService.selectMemberById(gameExchangeMoney.getMemberId()).getMemberAccount())
.tenantKey(gameExchangeMoney.getTenantKey())
.systemCurrency(gameExchangeMoney.getCurrencyCode())
.agentId(keyInfo.getCode())
.isAll(Boolean.TRUE)
.agentKey(keyInfo.getKey())
.build());
gameExchangeMoney.setQuota(decimal);
gameExchangeMoney.setStep(GameExchangeStep.DEDUCT_BALANCE.getCode());
gameExchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode());
return gameExchangeMoneyService.updateGameExchangeMoney(gameExchangeMoney) > 0;
});
return Boolean.TRUE.equals(result);
}
/**
*
*
* @param gameExchangeMoney
* @return boolean
*/
@Override
public boolean doRollBack(GameExchangeDTO gameExchangeMoney) {
//如果不是之前的步骤代码执行过了 或者是转入操作
if (!gameExchangeMoney.getStep().equals(this.stepKey().getCode())) {
return Boolean.TRUE;
}
TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager);
transactionTemplate.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
//如果该订单已经回滚过增加余额
List<TenantGameQuotaFlow> tenantGameQuotaFlows = tenantGameQuotaFlowService.selectTenantGameQuotaFlowList(
TenantGameQuotaFlow.builder()
.sourceId(String.valueOf(gameExchangeMoney.getId()))
.isOut(Boolean.TRUE)
.build());
if (!CollectionUtils.isEmpty(tenantGameQuotaFlows)){
return Boolean.TRUE;
}
tenantGameQuotaFlows = tenantGameQuotaFlowService.selectTenantGameQuotaFlowList(
TenantGameQuotaFlow.builder()
.sourceId(String.valueOf(gameExchangeMoney.getId()))
.isOut(Boolean.FALSE)
.build());
if (CollectionUtils.isEmpty(tenantGameQuotaFlows)) {
return Boolean.TRUE;
}
//取出第一个
TenantGameQuotaFlow tenantGameQuotaFlow = tenantGameQuotaFlows.get(0);
return tenantGameQuotaService.balanceChanges(BalanceChangesDTO.builder()
.isOut(Boolean.TRUE)
.platformCode(tenantGameQuotaFlow.getPlatformCode())
.currencyCode(tenantGameQuotaFlow.getCurrencyCode())
.tenantKey(tenantGameQuotaFlow.getTenantKey())
.balance(tenantGameQuotaFlow.getBalance())
.sourceId(tenantGameQuotaFlow.getSourceId())
.memberId(gameExchangeMoney.getMemberId())
.operationType(OperationType.API_BALANCE.getCode())
.remark(OperationType.API_BALANCE.getDescription())
.quotaType(tenantGameQuotaFlow.getQuotaType()).build());
}
/**
*
*
* @return {@link GameExchangeStep }
*/
@Override
public GameExchangeStep nextStepProcessor() {
return GameExchangeStep.PLATFORM_TRANSACTION;
}
/**
*
*
* @return {@link GameExchangeStep }
*/
@Override
public GameExchangeStep backStepProcessor() {
{
return GameExchangeStep.CREATE_ORDER;
}
}
}

View File

@ -0,0 +1,214 @@
package com.ff.game.api.exchange.impl;
import com.ff.base.constant.Constants;
import com.ff.base.enums.*;
import com.ff.base.exception.base.ApiException;
import com.ff.base.utils.StringUtils;
import com.ff.common.domain.TenantGameQuotaFlow;
import com.ff.common.dto.BalanceChangesDTO;
import com.ff.common.dto.GameBalanceExchange;
import com.ff.common.service.ITenantGameQuotaFlowService;
import com.ff.common.service.ITenantGameQuotaService;
import com.ff.game.api.IGamesService;
import com.ff.game.api.exchange.AbstractStepProcessor;
import com.ff.game.api.exchange.dto.GameExchangeDTO;
import com.ff.game.api.request.ExchangeTransferMoneyRequestDTO;
import com.ff.game.api.request.ExchangeTransferStatusRequestDTO;
import com.ff.game.api.request.ExchangeTransferStatusResponseDTO;
import com.ff.game.domain.GameExchangeMoney;
import com.ff.game.domain.KeyInfo;
import com.ff.game.domain.Platform;
import com.ff.game.service.IGameExchangeMoneyService;
import com.ff.game.service.IPlatformService;
import com.ff.member.domain.Member;
import com.ff.member.service.IMemberService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
/**
*
*
* @author shi
* @date 2025/04/09
*/
@Service
@Order(3)
public class PlatformTransactionServiceImpl extends AbstractStepProcessor {
@Resource
private IGameExchangeMoneyService gameExchangeMoneyService;
@Resource
private IMemberService memberService;
@Resource
private IPlatformService platformService;
@Autowired
private Map<String, IGamesService> gamesService;
/**
*
*
* @return {@link GameExchangeStep }
*/
@Override
public GameExchangeStep stepKey() {
return GameExchangeStep.PLATFORM_TRANSACTION;
}
/**
* do
*
* @param gameExchangeMoney
* @return boolean
*/
@Override
public boolean doProcess(GameExchangeDTO gameExchangeMoney) {
//如果不是之前的步骤代码执行过了 或者当前步骤成功的
if (!gameExchangeMoney.getStep().equals(this.backStepProcessor().getCode())&&
(GameExchangeStep.PLATFORM_TRANSACTION.getCode().equals(gameExchangeMoney.getStep())&&
gameExchangeMoney.getStepStatus().equals(GameExchangeStepStatus.SUCCESS.getCode()))
) {
return Boolean.TRUE;
}
Platform platform = platformService.get(gameExchangeMoney.getPlatformCode());
String targetCurrency = platform.getCurrencyInfo().get(gameExchangeMoney.getCurrencyCode());
ApiException.notNull(targetCurrency, ErrorCode.CURRENCY_NOT_EXIST.getCode());
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(gameExchangeMoney.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(gameExchangeMoney.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
Member member = memberService.selectMemberById(gameExchangeMoney.getMemberId());
IGamesService iGamesService = gamesService.get(gameExchangeMoney.getPlatformCode() + Constants.SERVICE);
//查询订单详情
if (TriggerType.TIMER.getCode() == gameExchangeMoney.getTriggerType()) {
//做二次确认调订单详情接口
this.doRollBack(gameExchangeMoney);
if (GameExchangeStepStatus.FAILURE.getCode() == gameExchangeMoney.getStepStatus()){
throw new ApiException(ErrorCode.BALANCE_TRANSFER_FAILED.getCode());
}
}
try {
//操作第三方额度接口
ExchangeTransferMoneyRequestDTO exchangeTransferMoneyRequestDTO = ExchangeTransferMoneyRequestDTO.builder()
.agentId(keyInfo.getCode())
.agentKey(keyInfo.getKey())
.account(member.getGameAccount())
.currency(targetCurrency)
.amount(gameExchangeMoney.getBalance())
.transferType(gameExchangeMoney.getExchangeType())
.transactionId(gameExchangeMoney.getTransactionId())
.gameExchangeId(gameExchangeMoney.getId())
.vendor(platform)
.keyInfo(keyInfo)
.build();
Long id = iGamesService.exchangeTransferByAgentId(exchangeTransferMoneyRequestDTO);
}catch (Exception e){
//做二次确认调订单详情接口
this.doRollBack(gameExchangeMoney);
if (GameExchangeStepStatus.FAILURE.getCode() == gameExchangeMoney.getStepStatus()){
throw new ApiException(ErrorCode.BALANCE_TRANSFER_FAILED.getCode());
}
}
GameExchangeMoney exchangeMoney = gameExchangeMoneyService.selectGameExchangeMoneyById(gameExchangeMoney.getId());
gameExchangeMoney.setBalance(exchangeMoney.getBalance());
gameExchangeMoney.setStep(exchangeMoney.getStep());
gameExchangeMoney.setStepStatus(exchangeMoney.getStepStatus());
return gameExchangeMoneyService.updateGameExchangeMoney(gameExchangeMoney) > 0;
}
/**
*
*
* @param gameExchangeMoney
* @return boolean
*/
@Override
public boolean doRollBack(GameExchangeDTO gameExchangeMoney) {
Platform platform = platformService.get(gameExchangeMoney.getPlatformCode());
String targetCurrency = platform.getCurrencyInfo().get(gameExchangeMoney.getCurrencyCode());
ApiException.notNull(targetCurrency, ErrorCode.CURRENCY_NOT_EXIST.getCode());
KeyInfo keyInfo = null;
for (KeyInfo keyData : platform.getKeyInfo()) {
if (StringUtils.isNotEmpty(gameExchangeMoney.getCurrencyCode())) {
if (keyData.getCurrency().equalsIgnoreCase(gameExchangeMoney.getCurrencyCode())) {
keyInfo = keyData;
break;
}
}
}
Member member = memberService.selectMemberById(gameExchangeMoney.getMemberId());
IGamesService iGamesService = gamesService.get(gameExchangeMoney.getPlatformCode() + Constants.SERVICE);
ExchangeTransferStatusRequestDTO exchangeTransferStatusRequestDTO = new ExchangeTransferStatusRequestDTO();
exchangeTransferStatusRequestDTO.setAccount(member.getGameAccount());
exchangeTransferStatusRequestDTO.setCurrency(targetCurrency);
exchangeTransferStatusRequestDTO.setOrderId(gameExchangeMoney.getTransactionId());
exchangeTransferStatusRequestDTO.setAgentId(keyInfo.getCode());
exchangeTransferStatusRequestDTO.setAgentKey(keyInfo.getKey());
exchangeTransferStatusRequestDTO.setGameExchangeMoneyId(gameExchangeMoney.getId());
exchangeTransferStatusRequestDTO.setVendor(platform);
exchangeTransferStatusRequestDTO.setKeyInfo(keyInfo);
exchangeTransferStatusRequestDTO.setSystemCurrency(gameExchangeMoney.getCurrencyCode());
ExchangeTransferStatusResponseDTO statusResponseDTO = iGamesService.exchangeTransferStatus(exchangeTransferStatusRequestDTO);
//订单已成功
if (StatusType.SUCCESS.getValue().equals(statusResponseDTO.getStatusType())) {
gameExchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
gameExchangeMoney.setStepStatus(GameExchangeStepStatus.SUCCESS.getCode());
return gameExchangeMoneyService.updateGameExchangeMoney(gameExchangeMoney) > 0;
}
gameExchangeMoney.setStep(GameExchangeStep.PLATFORM_TRANSACTION.getCode());
gameExchangeMoney.setStepStatus(GameExchangeStepStatus.FAILURE.getCode());
return gameExchangeMoneyService.updateGameExchangeMoney(gameExchangeMoney) > 0;
}
/**
*
*
* @return {@link GameExchangeStep }
*/
@Override
public GameExchangeStep nextStepProcessor() {
return GameExchangeStep.TENANT_QUOTA_DEDUCTED;
}
/**
*
*
* @return {@link GameExchangeStep }
*/
@Override
public GameExchangeStep backStepProcessor() {
{
return GameExchangeStep.DEDUCT_BALANCE;
}
}
}

View File

@ -0,0 +1,30 @@
package com.ff.game.api.fb.address;
import com.dtflys.forest.callback.AddressSource;
import com.dtflys.forest.http.ForestAddress;
import com.dtflys.forest.http.ForestRequest;
import com.ff.base.enums.GamePlatforms;
import com.ff.game.service.IPlatformService;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
/**
* <a href="https://doc.newsportspro.com/apidoc_data.html#%E5%85%A5%E5%8F%82"></a>
*
* @author cengy
*/
@Component
public class FBSportsAddress implements AddressSource {
@Resource
private IPlatformService platformService;
@Override
public ForestAddress getAddress(ForestRequest request) {
String apiBaseUrl = platformService.get(GamePlatforms.FBSports.getCode())
.getUrlInfo().getUrl();
return new ForestAddress("https", apiBaseUrl, 443, "fb/data");
}
}

Some files were not shown because too many files have changed in this diff Show More