Initial commit

This commit is contained in:
2026-04-23 16:58:11 +08:00
commit 267eba1eca
2582 changed files with 273338 additions and 0 deletions

View File

@@ -0,0 +1,40 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://maven.apache.org/POM/4.0.0"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>com.ruoyi</groupId>
<artifactId>skins-service</artifactId>
<version>4.8.2</version>
</parent>
<artifactId>service-playingmethod</artifactId>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>ruoyi-framework</artifactId>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>service-admin</artifactId>
<version>4.8.2</version>
</dependency>
<dependency>
<groupId>com.ruoyi</groupId>
<artifactId>service-user</artifactId>
<version>4.8.2</version>
</dependency>
</dependencies>
</project>

View File

@@ -0,0 +1,42 @@
package com.ruoyi.playingmethod.config;
import com.rabbitmq.client.Channel;
import com.ruoyi.admin.service.TtFightService;
import com.ruoyi.common.rabbitmq.config.DelayedQueueConfig;
import com.ruoyi.common.rabbitmq.config.FightQueueConfig;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.playingmethod.service.ApiRollService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.Date;
@Component
@Slf4j
public class FightRabbitConsumer {
// private final TtFightService ttFightService;
//
// public FightRabbitConsumer(TtFightService ttFightService) {
// this.ttFightService = ttFightService;
// }
//
// @RabbitListener(queues = FightQueueConfig.FIGHT_QUEUE)
// public void rollReceiveDelayed(String fightId,Message message, Channel channel) throws IOException {
// log.info("===============接收对战队列消息====================" + fightId);
//
// // 通知 MQ 消息已被接收,可以ACK(从队列中删除)了
// channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
// try {
// ttFightService.endFight(fightId);
// } catch (Exception e) {
// log.error("============消费失败,尝试消息补发再次消费!==============");
// log.error(e.getMessage());
// // 消息补发 true=消息退回到queue(有可能被其它的consumer(集群)接收到) false=只补发给当前的consumer
// channel.basicRecover(false);
// }
// }
}

View File

@@ -0,0 +1,39 @@
package com.ruoyi.playingmethod.config;
import com.ruoyi.admin.mapper.TtBoxOrnamentsMapper;
import com.ruoyi.admin.util.core.fight.LotteryMachine;
import com.ruoyi.admin.util.core.fight.PrizePool;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.redis.config.RedisLock;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.HashMap;
@Configuration
public class LotteryMachineConfig {
@Autowired
RedisLock redisLock;
@Autowired
RedisCache redisCache;
@Autowired
TtBoxOrnamentsMapper boxOrnamentsMapper;
@Bean
public LotteryMachine lotteryMachine(){
LotteryMachine machine = LotteryMachine.builder()
.boxOrnamentsMapper(boxOrnamentsMapper)
.redisLock(redisLock)
.redisCache(redisCache)
.prizePools(new HashMap<String, PrizePool>())
.build();
machine.preheat();
return machine;
}
}

View File

@@ -0,0 +1,62 @@
package com.ruoyi.playingmethod.config;
import cn.hutool.core.util.ObjectUtil;
import com.rabbitmq.client.Channel;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.rabbitmq.config.DelayedQueueConfig;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.playingmethod.service.ApiRollService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.ExchangeTypes;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.*;
import org.springframework.stereotype.Component;
import java.io.IOException;
@Component
@Slf4j
public class RollRabbitConsumer {
private final ApiRollService apiRollService;
public RollRabbitConsumer(ApiRollService apiRollService) {
this.apiRollService = apiRollService;
}
@RabbitListener(queues = DelayedQueueConfig.DLK_QUEUE)
public void rollReceiveDelayed(Message message, Channel channel) throws IOException {
long deliveryTag = message.getMessageProperties().getDeliveryTag();
Integer rollId = Integer.valueOf(new String(message.getBody()));
log.info("【死信队列消费】 roll房开奖,接受到的Roll房ID:{}", rollId);
try {
// Roll房分配结果逻辑
R r = apiRollService.endROLL(rollId);
if (!r.getCode().equals(200)) {
log.error("roll开奖 消费失败,尝试消息补发再次消费!" + r.getMsg());
channel.basicNack(deliveryTag, true, true);
}
if (ObjectUtil.isNotEmpty(r.getMsg())) log.info("{}", r.getMsg());
// 通知 MQ 消息已被接收,可以ACK(从队列中删除)了
channel.basicAck(deliveryTag, true);
} catch (Exception e) {
log.error("roll开奖 消费失败,尝试消息补发再次消费!");
log.error(e.getMessage());
channel.basicNack(deliveryTag, true, true);
}
}
//@RabbitListener(queues = DelayedQueueConfig.DLK_QUEUE)
public void t1(Message message, Channel channel) throws IOException {
// long deliveryTag = message.getMessageProperties().getDeliveryTag();
log.info("mq 测试");
System.out.println(new String(message.getBody()));
}
}

View File

@@ -0,0 +1,9 @@
package com.ruoyi.playingmethod.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/battleRoyale")
public class ApiBattleRoyaleController {
}

View File

@@ -0,0 +1,217 @@
package com.ruoyi.playingmethod.controller;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.admin.mapper.TtUserBlendErcashMapper;
import com.ruoyi.admin.service.TtUserAmountRecordsService;
import com.ruoyi.admin.service.TtBoxService;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.annotation.UpdateUserCache;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.domain.common.constant.TtAccountRecordSource;
import com.ruoyi.domain.common.constant.TtAccountRecordType;
import com.ruoyi.domain.common.constant.TtboxRecordSource;
import com.ruoyi.domain.common.constant.TtboxRecordStatus;
import com.ruoyi.domain.dto.boxRecords.TenTopQuery;
import com.ruoyi.domain.dto.boxRecords.queryCondition;
import com.ruoyi.domain.entity.TtUserBlendErcash;
import com.ruoyi.domain.entity.recorde.TtUserAmountRecords;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.*;
import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO;
import com.ruoyi.playingmethod.service.ApiBindBoxService;
import com.ruoyi.playingmethod.service.ApiBoxRecordsService;
import com.ruoyi.system.service.ISysConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.math.BigDecimal;
import java.util.*;
@Api(tags = "玩转盲盒模式")
@RestController
@RequestMapping("/api/bindbox")
public class ApiBindBoxController extends BaseController {
private final ISysConfigService sysConfigService;
private final ApiBindBoxService bindBoxService;
private final TtUserService userService;
private final TtBoxService boxService;
private final TtUserAmountRecordsService userAmountRecordsService;
public ApiBindBoxController(ISysConfigService sysConfigService,
ApiBindBoxService bindBoxService,
TtUserAmountRecordsService userAmountRecordsService,
TtUserService userService,
TtBoxService boxService) {
this.sysConfigService = sysConfigService;
this.bindBoxService = bindBoxService;
this.userService = userService;
this.userAmountRecordsService = userAmountRecordsService;
this.boxService = boxService;
}
@Autowired
private ApiBoxRecordsService apiBoxRecordsService;
@Autowired
private TtUserBlendErcashMapper ttUserBlendErcashMapper;
@ApiOperation("获取宝箱信息")
@Anonymous
@GetMapping("/{boxId}")
public R<TtBoxA> getBoxData(@PathVariable(value = "boxId") Integer boxId) {
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) {
return R.fail("网站维护中......");
}
String bindBoxMaintenance = sysConfigService.selectConfigByKey("bindBoxMaintenance");
if ("1".equals(bindBoxMaintenance)) {
return R.fail("盲盒开箱功能正在维护中......");
}
TtBoxA boxData = bindBoxService.getBoxData(boxId);
return R.ok(boxData);
}
@ApiOperation("获取补偿箱信息")
@GetMapping("/getCompBoxData/{boxId}")
public AjaxResult getCompBoxData(@PathVariable("boxId") Integer boxId) {
List<TtOrnamentsA> compBoxData = bindBoxService.getCompBoxData(boxId);
return success(compBoxData);
}
@ApiOperation("获取宝箱列表")
@ApiImplicitParams({
@ApiImplicitParam(name = "boxType", value = "宝箱类型1普通箱 2对战箱 3补偿箱 4九宫格", dataType = "String", dataTypeClass = String.class)
})
@Anonymous
@GetMapping("/getBoxList")
public R<List<TtBoxVO>> getBoxList(
@RequestParam(value = "boxTypeId", required = false) Integer boxTypeId,
@RequestParam(value = "homeFlag", required = false) String homeFlag,
@RequestParam(value = "boxType", required = false) String boxType
) {
List<TtBoxVO> boxData = bindBoxService.getBoxList(boxTypeId, homeFlag, boxType);
if (boxData.isEmpty()) {
return R.ok(null, "没有匹配的数据。");
}
return R.ok(boxData);
}
/**
* 盲盒开箱模式开箱
*/
@ApiOperation("盲盒开箱")
@UpdateUserCache
@PostMapping("/openBox")
public R openBox(@RequestParam("boxId") Integer boxId, @RequestParam("num") Integer num) {
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) {
return R.fail("网站维护中......");
}
String bindBoxMaintenance = sysConfigService.selectConfigByKey("bindBoxMaintenance");
if ("1".equals(bindBoxMaintenance)) {
return R.fail("盲盒开箱功能正在维护中......");
}
TtUser ttUser = userService.getById(getUserId());
TtBox ttBox = boxService.getById(boxId);
/*if ("0".equals(ttUser.getIsRealCheck())) {
return R.fail("您的账户未进行实名认证,请先进行实名认证!");
}*/
LambdaQueryWrapper<TtUserBlendErcash> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(TtUserBlendErcash::getUserId, ttUser.getUserId())
.eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode())
.eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode())
.select(TtUserBlendErcash::getTotal);
List<TtUserBlendErcash> ercash = ttUserBlendErcashMapper.selectList(queryWrapper);
if (CollectionUtils.isEmpty(ercash)) {
return R.fail("您的账户未进行充过值!");
}
// BigDecimal openBoxBeanTotal = ttBox.getPrice().multiply(new BigDecimal(num));
// if (ttUser.getAccountAmount().compareTo(openBoxBeanTotal) < 0) return R.fail("您的账户游戏币不足!");
return bindBoxService.blindBox(ttUser, ttBox, num);
}
@ApiOperation("获取历史开箱信息")
@Anonymous
@PostMapping("/getBindBoxHistory")
public R getBindBoxHistory(@RequestBody @Validated queryCondition param) {
if (param.getStatus().equals(1) || param.getStatus().equals(10)) {
return R.fail("非法的状态参数。");
}
PageUtils.clearPage();
List<TtBoxRecordsVO> ttBoxRecordsVOS = apiBoxRecordsService.byCondition(param);
return R.ok(ttBoxRecordsVOS);
}
// @ApiOperation("获取历史个人开箱信息")
// @Anonymous
// @GetMapping("/selectBoxRecordsUserLogList")
// public R selectBoxRecordsUserLogList(TtUserAmountRecordsBody ttUserAmountRecordsBody) {
// System.out.println("==================个人开箱记录====================");
// ttUserAmountRecordsBody.setUserId(getUserId().intValue());
// return userAmountRecordsService.queryList(ttUserAmountRecordsBody);
// //return R.ok(list);
// }
@ApiOperation("获取当前补偿值")
@GetMapping("/getCompValue/{boxId}")
public AjaxResult getCompValue(@PathVariable("boxId") Integer boxId) {
Long userId = getUserId();
TtUser ttUser = userService.getById(userId);
TtBox ttBox = boxService.getById(boxId);
if (Objects.isNull(ttBox))
return error("宝箱不存在");
if (!"3".equals(ttBox.getBoxType()))
return error("该宝箱不是补偿宝箱");
return success(bindBoxService.getCompValue(ttUser, ttBox));
}
@ApiOperation("盲盒top")
@GetMapping("/top")
public R<List<TtBoxRecordsVO>> topBlinkBox() {
Long userId = getUserId();
if (ObjectUtil.isNull(userId)) {
return R.fail("登录过期。");
}
queryCondition param = new queryCondition();
param.setUserId(userId.intValue());
param.setOrderByFie(2);
param.setSource(Collections.singletonList(TtboxRecordSource.BLIND_BOX.getCode()));
param.setStatus(Arrays.asList(TtboxRecordStatus.IN_PACKSACK_ON.getCode(), TtboxRecordStatus.APPLY_DELIVERY.getCode(), TtboxRecordStatus.DELIVERY_YET.getCode()));
param.setPage(1);
param.setSize(1);
PageUtils.clearPage();
List<TtBoxRecordsVO> ttBoxRecordsVOS = new ArrayList<>(apiBoxRecordsService.byCondition(param));
param.setSource(Collections.singletonList(TtboxRecordSource.FIGHT.getCode()));
ttBoxRecordsVOS.addAll(apiBoxRecordsService.byCondition(param));
param.setSource(Collections.singletonList(TtboxRecordSource.UPGRADE.getCode()));
ttBoxRecordsVOS.addAll(apiBoxRecordsService.byCondition(param));
return R.ok(ttBoxRecordsVOS);
}
@ApiOperation("十佳掉落")
@GetMapping("/tenTop")
public R<List<TtBoxRecordsVO>> tenTop() {
TenTopQuery param = new TenTopQuery();
param.setSource(Collections.singletonList(TtboxRecordSource.BLIND_BOX.getCode()));
param.setStatus(Arrays.asList(TtboxRecordStatus.IN_PACKSACK_ON.getCode(), TtboxRecordStatus.APPLY_DELIVERY.getCode(), TtboxRecordStatus.DELIVERY_YET.getCode()));
param.setPage(1);
param.setSize(10);
PageUtils.clearPage();
List<TtBoxRecordsVO> ttBoxRecordsVOS = apiBoxRecordsService.tenTopQuery(param);
return R.ok(ttBoxRecordsVOS);
}
}

View File

@@ -0,0 +1,38 @@
package com.ruoyi.playingmethod.controller;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.domain.dto.boxRecords.queryCondition;
import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO;
import com.ruoyi.playingmethod.service.ApiBoxRecordsService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
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 java.util.List;
@Api(tags = "出货记录")
@RestController
@RequestMapping("/api/boxRecords")
public class ApiBoxRecordsController {
@Autowired
private ApiBoxRecordsService apiBoxRecordsService;
@ApiOperation("获取历史出货记录")
@PostMapping("/historyByCondition")
public R getBindBoxHistory(@RequestBody @Validated queryCondition param) {
if (param.getStatus().contains(1) || param.getStatus().contains(10)) {
return R.fail("非法的状态参数。");
}
PageUtils.clearPage();
List<TtBoxRecordsVO> ttBoxRecordsVOS = apiBoxRecordsService.byCondition(param);
return R.ok(ttBoxRecordsVOS);
}
}

View File

@@ -0,0 +1,56 @@
package com.ruoyi.playingmethod.controller;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.playingmethod.service.ApiCompoundService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.domain.vo.UserPackSackDataVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "合成")
@RestController
@RequestMapping("/api/compound")
public class ApiCompoundController extends BaseController {
private final ISysConfigService sysConfigService;
private final TtUserService userService;
private final ApiCompoundService compoundService;
public ApiCompoundController(ISysConfigService sysConfigService,
TtUserService userService,
ApiCompoundService compoundService) {
this.sysConfigService = sysConfigService;
this.userService = userService;
this.compoundService = compoundService;
}
@ApiOperation("合成")
@PostMapping("/compound")
public R<UserPackSackDataVO> compound(@RequestBody List<Long> packSackIds) {
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) {
return R.fail("网站维护中......");
}
String compoundMaintenance = sysConfigService.selectConfigByKey("compoundMaintenance");
if ("1".equals(compoundMaintenance)) {
return R.fail("汰换合成功能正在维护中......");
}
TtUser ttUser = userService.getById(getUserId());
return compoundService.compound(packSackIds, ttUser);
}
@ApiOperation("获取用户合成记录")
@GetMapping("/getUserCompoundRecord")
public PageDataInfo<UserPackSackDataVO> getUserCompoundRecord() {
startPage();
List<UserPackSackDataVO> userCompoundRecord = compoundService.getUserCompoundRecord(getUserId().intValue());
return getPageData(userCompoundRecord);
}
}

View File

@@ -0,0 +1,125 @@
package com.ruoyi.playingmethod.controller;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.admin.mapper.TtUserBlendErcashMapper;
import com.ruoyi.admin.service.TtBoxService;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.annotation.UpdateUserCache;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.PageUtils;
import com.ruoyi.domain.common.constant.TtAccountRecordSource;
import com.ruoyi.domain.common.constant.TtAccountRecordType;
import com.ruoyi.domain.dto.exponent.ExponentJoinParam;
import com.ruoyi.domain.entity.TtUserBlendErcash;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.TtBox;
import com.ruoyi.playingmethod.mapper.ApiBindBoxMapper;
import com.ruoyi.playingmethod.service.ApiExponentService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiImplicitParam;
import io.swagger.annotations.ApiImplicitParams;
import io.swagger.annotations.ApiOperation;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "指数盲盒")
@RestController
@RequestMapping("/api/exponent")
public class ApiExponentController extends BaseController {
@Autowired
private ApiExponentService apiExponentService;
@Autowired
private TtUserService ttUserService;
@Autowired
private ApiBindBoxMapper bindBoxMapper;
@Autowired
private TtBoxService boxService;
@Autowired
private TtUserBlendErcashMapper ttUserBlendErcashMapper;
@ApiOperation("加入指数盲盒")
@Anonymous
@UpdateUserCache
@PostMapping("/join")
public R join(@RequestBody @Validated ExponentJoinParam param) {
TtUser ttUser = ttUserService.getById(getUserId());
LambdaQueryWrapper<TtUserBlendErcash> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(TtUserBlendErcash::getUserId, ttUser.getUserId())
.eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode())
.eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode())
.select(TtUserBlendErcash::getTotal);
List<TtUserBlendErcash> ercash = ttUserBlendErcashMapper.selectList(queryWrapper);
if (CollectionUtils.isEmpty(ercash)) {
return R.fail("您的账户未进行充过值!");
}
return apiExponentService.join(ttUser, param);
}
@ApiOperation("获取宝箱列表")
@ApiImplicitParams({
@ApiImplicitParam(name = "boxType", value = "宝箱类型1普通箱 2对战箱 3补偿箱 4九宫格 5指数盲盒", dataType = "String", dataTypeClass = String.class)
})
@Anonymous
@GetMapping("/box")
public R box(@RequestParam("id") Integer id) {
TtUser ttUser = ttUserService.getById(getUserId());
return apiExponentService.box(ttUser, id);
}
@ApiOperation("最近十期结果")
@Anonymous
@GetMapping("/recenttenperiod")
public R recenttenperiod() {
PageUtils.clearPage();
return apiExponentService.recenttenperiod();
}
@ApiOperation("结果信息")
@Anonymous
@GetMapping("/info")
public R info() {
PageUtils.clearPage();
TtUser ttUser = ttUserService.getById(getUserId());
return apiExponentService.info(ttUser);
}
@ApiOperation("指数记录")
@Anonymous
@GetMapping("/records")
public R records() {
startPage();
TtUser ttUser = ttUserService.getById(getUserId());
return apiExponentService.records(ttUser);
}
@ApiOperation("我的箱子")
@Anonymous
@GetMapping("/mybox")
public R mybox() {
TtUser ttUser = ttUserService.getById(getUserId());
return apiExponentService.mybox(ttUser);
}
@ApiOperation("盲盒开箱")
@UpdateUserCache
@GetMapping("/openBox")
public R openBox(@RequestParam("boxId") Integer boxId, @RequestParam("num") Integer num) {
TtUser ttUser = ttUserService.getById(getUserId());
TtBox ttBox = boxService.getById(boxId);
return apiExponentService.openbox(ttUser, ttBox, num);
}
}

View File

@@ -0,0 +1,492 @@
package com.ruoyi.playingmethod.controller;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.ruoyi.admin.mapper.TtUserBlendErcashMapper;
import com.ruoyi.admin.mapper.TtUserMapper;
import com.ruoyi.admin.service.*;
import com.ruoyi.admin.util.core.fight.LotteryMachine;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.annotation.UpdateUserCache;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.RandomUtil;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.domain.common.constant.TtAccountRecordSource;
import com.ruoyi.domain.common.constant.TtAccountRecordType;
import com.ruoyi.domain.common.constant.UserType;
import com.ruoyi.domain.dto.fight.FightDetailParam;
import com.ruoyi.domain.dto.fight.FightOnMyOwnParam;
import com.ruoyi.domain.entity.TtUserBlendErcash;
import com.ruoyi.domain.entity.fight.TtFight;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.CreateFightBody;
import com.ruoyi.domain.other.FightBoutData;
import com.ruoyi.domain.other.TtBox;
import com.ruoyi.domain.other.TtBoxVO;
import com.ruoyi.domain.vo.ApiFightListDataVO;
import com.ruoyi.domain.vo.FightResultDataVO;
import com.ruoyi.domain.vo.fight.FightResultVO;
import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO;
import com.ruoyi.playingmethod.model.vo.ApiFightRankingVO;
import com.ruoyi.playingmethod.service.ApiFightService;
import com.ruoyi.system.service.ISysConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.TimeUnit;
@Api(tags = "对战模式")
@Slf4j
@RestController
@RequestMapping("/api/fight")
public class ApiFightController extends BaseController {
private final TtFightResultService fightResultService;
private final ISysConfigService sysConfigService;
private final TtUserService userService;
private final ApiFightService apiFightService;
private final RedisCache redisCache;
@Autowired
private TtBoxOrnamentsService boxOrnamentsService;
@Autowired
private TtOrnamentService ttOrnamentService;
@Autowired
private LotteryMachine lotteryMachine;
@Autowired
private TtBoxService boxService;
@Autowired
private TtUserMapper ttUserMapper;
public ApiFightController(TtFightResultService fightResultService,
ISysConfigService sysConfigService,
TtUserService userService,
ApiFightService apiFightService,
RedisCache redisCache) {
this.fightResultService = fightResultService;
this.sysConfigService = sysConfigService;
this.userService = userService;
this.apiFightService = apiFightService;
this.redisCache = redisCache;
}
@Autowired
private TtUserBlendErcashMapper ttUserBlendErcashMapper;
public R checkLogin() {
Long userId;
try {
userId = getUserId();
if (ObjectUtil.isEmpty(userId)) return R.fail(401, "登录过期,请重新登录。");
return R.ok(userId);
} catch (Exception e) {
return R.fail("登录过期,请重新登录。");
}
}
/**
* Step1 创建对战房间
*/
@ApiOperation("创建对战房间")
@UpdateUserCache
@PostMapping("/createFight")
public R createFight(@Validated @RequestBody CreateFightBody createFightParam) {
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) {
return R.fail("网站维护中......");
}
String fightMaintenance = sysConfigService.selectConfigByKey("fightMaintenance");
if ("1".equals(fightMaintenance)) {
return R.fail("开箱对战功能正在维护中......");
}
R checkLogin = checkLogin();
if (!checkLogin.getCode().equals(200)) {
return checkLogin;
}
Integer userId = ((Long) checkLogin.getData()).intValue();
TtUser player = userService.getById(userId);
LambdaQueryWrapper<TtUserBlendErcash> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(TtUserBlendErcash::getUserId, userId)
.eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode())
.eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode())
.select(TtUserBlendErcash::getTotal);
List<TtUserBlendErcash> ercash = ttUserBlendErcashMapper.selectList(queryWrapper);
if (CollectionUtils.isEmpty(ercash)) {
return R.fail("您的账户未进行充过值!");
}
return apiFightService.createFight(createFightParam, player);
}
/**
* Step2 加入房间占座先行HTTP
*/
@ApiOperation("加入对战房间")
@PostMapping("/joinFightRoom")
public R joinFightRoom(@RequestParam("fightId") Integer fightId) {
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) {
return R.fail("网站维护中......");
}
String fightMaintenance = sysConfigService.selectConfigByKey("fightMaintenance");
if ("1".equals(fightMaintenance)) {
return R.fail("开箱对战功能正在维护中......");
}
R checkLogin = checkLogin();
if (!checkLogin.getCode().equals(200)) return checkLogin;
Integer userId = ((Long) checkLogin.getData()).intValue();
LambdaQueryWrapper<TtUserBlendErcash> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(TtUserBlendErcash::getUserId, userId)
.eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode())
.eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode())
.select(TtUserBlendErcash::getTotal);
List<TtUserBlendErcash> ercash = ttUserBlendErcashMapper.selectList(queryWrapper);
if (CollectionUtils.isEmpty(ercash)) {
return R.fail("您的账户未进行充过值!");
}
TtUser player = userService.getById(userId);
return apiFightService.joinFight(fightId, player);
}
/**
* 退出房间
*/
@ApiOperation("退出房间")
@PostMapping("/fightRoomExit")
public R fightRoomExit(@RequestParam("fightId") Integer fightId) {
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) return R.fail("网站维护中......");
String fightMaintenance = sysConfigService.selectConfigByKey("fightMaintenance");
if ("1".equals(fightMaintenance)) return R.fail("开箱对战功能正在维护中......");
R checkLogin = checkLogin();
if (!checkLogin.getCode().equals(200)) return checkLogin;
Integer userId = ((Long) checkLogin.getData()).intValue();
TtUser player = userService.getById(userId);
return apiFightService.fightRoomExit(fightId, player);
}
/**
* Step3 玩家准备游戏
*/
@ApiOperation("玩家准备游戏")
@UpdateUserCache
@GetMapping("/seatrReady")
public R seatrReady(@RequestParam("fightId") Integer fightId) {
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) {
return R.fail("网站维护中......");
}
String fightMaintenance = sysConfigService.selectConfigByKey("fightMaintenance");
if ("1".equals(fightMaintenance)) {
return R.fail("开箱对战功能正在维护中......");
}
R checkLogin = checkLogin();
if (!checkLogin.getCode().equals(200)) return checkLogin;
Integer userId = ((Long) checkLogin.getData()).intValue();
TtUser player = userService.getById(userId);
return apiFightService.seatrReady(fightId, player);
}
/**
* Step4 开始游戏(前端满人自动开始)
*/
@ApiOperation("开始游戏")
@GetMapping("/fightBegin")
public R fightBegin(@RequestParam("fightId") Integer fightId) {
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) {
return R.fail("网站维护中......");
}
String fightMaintenance = sysConfigService.selectConfigByKey("fightMaintenance");
if ("1".equals(fightMaintenance)) {
return R.fail("开箱对战功能正在维护中......");
}
R checkLogin = checkLogin();
if (!checkLogin.getCode().equals(200)) return checkLogin;
Integer userId = ((Long) checkLogin.getData()).intValue();
TtUser player = userService.getById(userId);
return apiFightService.fightBegin(fightId, player);
}
/**
* 观战
*/
@ApiOperation("观战")
@GetMapping("/audience")
public R audience(@RequestParam("fightId") Integer fightId) {
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) {
return R.fail("网站维护中......");
}
String fightMaintenance = sysConfigService.selectConfigByKey("fightMaintenance");
if ("1".equals(fightMaintenance)) {
return R.fail("开箱对战功能正在维护中......");
}
R checkLogin = checkLogin();
if (!checkLogin.getCode().equals(200)) return checkLogin;
return apiFightService.audience(fightId);
}
/**
* 游戏结束标记
*/
@ApiOperation("游戏结束标记")
@GetMapping("/fightEnd")
public R fightEnd(@RequestParam("fightId") Integer fightId) {
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) return R.fail("网站维护中......");
String fightMaintenance = sysConfigService.selectConfigByKey("fightMaintenance");
if ("1".equals(fightMaintenance)) return R.fail("开箱对战功能正在维护中......");
R checkLogin = checkLogin();
if (!checkLogin.getCode().equals(200)) return checkLogin;
return apiFightService.fightEnd(fightId);
}
/**
* 抽奖机测试
*/
// @ApiOperation("抽奖机测试")
// @GetMapping("LotteryMachineTest")
public R LotteryMachineTest() {
R checkLogin = checkLogin();
if (!checkLogin.getCode().equals(200)) return checkLogin;
Integer userId = ((Long) checkLogin.getData()).intValue();
TtUser player = userService.getById(userId);
TtBox box = boxService.getById(2L);
String ornamentId = lotteryMachine.singleLottery(player, box);
return R.ok(ornamentId);
}
@ApiOperation("我参与的历史对战记录")
@PostMapping("/fightOnMyOwn")
public R fightOnMyOwn(@RequestBody FightOnMyOwnParam param) {
Long userId = getUserId();
if (ObjectUtil.isNull(userId)) return R.fail(401, "登录过期。");
param.setPlayerId(userId.intValue());
List<ApiFightListDataVO> list = apiFightService.getFightList(param);
return R.ok(list);
}
@ApiOperation("全服历史对战记录")
@PostMapping("/fightOnAll")
public R fightOnAll(@RequestBody FightOnMyOwnParam param) {
List<ApiFightListDataVO> list = apiFightService.getFightList(param);
return R.ok(list);
}
@ApiOperation("历史对战详情")
@PostMapping("/fightDetail")
public R fightDetail(@RequestBody FightDetailParam param) {
return apiFightService.fightDetail(param);
}
@ApiOperation("获取指定记录更早的历史对战")
@PostMapping("/earlierHistory")
public R earlierHistory(@RequestBody FightDetailParam param) {
return apiFightService.earlierHistory(param);
}
@ApiOperation("获取对战宝箱详情")
@GetMapping("/simpleBoxDetail")
public PageDataInfo<SimpleOrnamentVO> simpleBoxDetail(Integer boxId) {
startPage();
List<SimpleOrnamentVO> list = boxOrnamentsService.simpleBoxDetail(boxId);
return getPageData(list);
}
@ApiOperation("获取对战宝箱")
@GetMapping("/getFightBoxList")
@Anonymous
public R<List<TtBoxVO>> getFightBoxList(@RequestParam(value = "boxTypeId", required = false) Integer boxTypeId) {
List<TtBoxVO> boxData = apiFightService.getFightBoxList(boxTypeId);
if (ObjectUtil.isEmpty(boxData) || boxData.isEmpty()) return R.ok(null, "没有匹配的数据。");
return R.ok(boxData);
}
@ApiOperation("获取对战列表")
@Anonymous
@GetMapping("/getFightList")
public PageDataInfo<ApiFightListDataVO> getFightList(@RequestParam(required = false) String model,
@RequestParam(required = false) String status,
@RequestParam(required = false) Integer userId) {
startPage();
List<ApiFightListDataVO> list = apiFightService.getFightList(model, status, userId, null);
return getPageData(list);
}
@ApiOperation("获取对战数据")
@GetMapping("/getFightData/{fightId}")
public R<ApiFightListDataVO> getFightData(@PathVariable("fightId") Integer fightId) {
ApiFightListDataVO apiFightListDataVO = apiFightService.getFightList(null, null, null, fightId).get(0);
return R.ok(apiFightListDataVO);
}
// @ApiOperation("获取对战结果")
// @GetMapping("/getFightResult/{fightId}")
// public R<FightResultDataVO> getFightResult(@PathVariable("fightId") Integer fightId) {
// FightResultDataVO fightResultDataVO = fightResultService.getFightResult(fightId);
// return R.ok(fightResultDataVO);
// }
@ApiOperation("获取对战结果")
@GetMapping("/getFightResult/{fightId}")
public R<FightResultVO> getFightResult(@PathVariable("fightId") Integer fightId) {
FightResultVO fightResultVO = fightResultService.getFightResult(fightId);
return R.ok(fightResultVO);
}
@ApiOperation("存储对战回合数据")
@PostMapping("/saveFightBoutData")
public R<Boolean> saveFightBoutData(@RequestBody FightBoutData fightBoutData) {
String key = "fight_bout_data:fight_" + fightBoutData.getFightId();
redisCache.setCacheObject(key, fightBoutData, fightBoutData.getExpirationTime(), TimeUnit.MILLISECONDS);
return R.ok();
}
@ApiOperation("获取对战回合数")
@PostMapping("/getFightBoutNum/{fightId}")
public R<Integer> getFightBoutNum(@PathVariable Integer fightId) {
String key = "fight_bout_data:fight_" + fightId;
FightBoutData fightBoutData = redisCache.getCacheObject(key);
if (StringUtils.isNotNull(fightBoutData)) {
return R.ok(fightBoutData.getBoutNum());
} else {
// return R.fail("对战回合不存在");
return R.ok();
}
}
/**
* 获取当前回合开箱结果
*/
// @GetMapping("/getFightRecord")
public R<FightResultDataVO> getFightRecord(@RequestParam(value = "fightId", required = false) Integer fightId,
@RequestParam(value = "round", required = false) Integer round,
@RequestParam(value = "rounds", required = false) Integer rounds) {
// System.out.println("=========================获取当前回合开箱结果=============================");
// System.out.println("fightId:" + fightId);
// System.out.println("round:" + round);
// System.out.println("rounds:" + rounds);
log.info("获取当前回合开箱结果 api /{} 参数fightId{} round{} rounds{}", "getFightRecord", fightId, round, rounds);
// 检查是否已经开始游戏
TtFight fight = apiFightService.getById(fightId);
if (ObjectUtil.isEmpty(fight)) return R.fail("不存在的对局");
if (fight.getStatus().equals(0)) return R.fail("对局尚未开始,请稍后重试。");
FightResultDataVO fightResultDataVO = apiFightService.getFightRecord(fightId, round, rounds);
if (ObjectUtil.isEmpty(fightResultDataVO)) {
return R.fail("对局fightId 没有产生结果");
}
return R.ok(fightResultDataVO);
}
@ApiOperation("获取对战排行榜")
@Anonymous
@GetMapping("/fightRanking")
public AjaxResult getFightRanking() {
// 创建日期格式化对象,指定日期格式
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
// 获取今天日期
Date today = new Date();
String formattedTodayDate = formatter.format(today);
List<ApiFightRankingVO> todayFightRanking = apiFightService.getFightRankingByDate(formattedTodayDate);
// 获取昨天日期
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, -1);
Date yesterday = cal.getTime();
String formattedYesterdayDate = formatter.format(yesterday);
List<ApiFightRankingVO> yesterdayFightRanking = apiFightService.getFightRankingByDate(formattedYesterdayDate);
Map<String, List<ApiFightRankingVO>> map = new HashMap<>();
map.put("todayFightRanking", todayFightRanking);
map.put("yesterdayFightRanking", yesterdayFightRanking);
return AjaxResult.success(map);
}
@ApiOperation("十佳对战, 1最大金额2最大增长3最大倍数")
@Anonymous
@GetMapping("/tenTopFight")
public AjaxResult tenTopFight(@RequestParam("maxType") Integer maxType) {
return AjaxResult.success(apiFightService.tenTopFight(maxType));
}
@ApiOperation("邀请机器人加入对战房间")
@PostMapping({"/joinFightRoomForRobot"})
public R joinFightRoomForRobot(@RequestParam("fightId") Integer fightId) {
String websiteMaintenance = this.sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) {
return R.fail("网站维护中......");
} else {
String fightMaintenance = this.sysConfigService.selectConfigByKey("fightMaintenance");
if ("1".equals(fightMaintenance)) {
return R.fail("开箱对战功能正在维护中......");
} else {
List<TtUser> robotIdList = new LambdaQueryChainWrapper<TtUser>(this.ttUserMapper)
.eq(TtUser::getUserType, UserType.ROBOT_USER.getCode()).list();
if (CollectionUtils.isEmpty(robotIdList)) {
return R.ok();
} else {
TtUser player = RandomUtil.choice(robotIdList);
R<Object> objectR = this.apiFightService.joinFight(fightId, player);
if (objectR.getCode() == 500) {
return objectR;
} else {
log.info("机器人{}加入对战{}", player.getUserId(), fightId);
R fight = this.apiFightService.seatrReady(fightId, player);
log.info("fight robot task fight is {}", fight == null ? "null" : fight.toString());
FightBoutData fightBoutData = new FightBoutData();
fightBoutData.setFightId(fightId);
fightBoutData.setBoutNum(1);
fightBoutData.setExpirationTime(16000);
String key = "fight_bout_data:fight_" + fightBoutData.getFightId();
this.redisCache.setCacheObject(key, fightBoutData, fightBoutData.getExpirationTime(), TimeUnit.MILLISECONDS);
this.apiFightService.fightBegin(fightId, player);
return R.ok();
}
}
}
}
}
}

View File

@@ -0,0 +1,128 @@
package com.ruoyi.playingmethod.controller;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.admin.mapper.TtUserBlendErcashMapper;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.common.annotation.Anonymous;
import com.ruoyi.common.annotation.UpdateUserCache;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.domain.common.constant.TtAccountRecordSource;
import com.ruoyi.domain.common.constant.TtAccountRecordType;
import com.ruoyi.domain.dto.upgrade.UpgradeCondition;
import com.ruoyi.domain.entity.TtUserBlendErcash;
import com.ruoyi.domain.other.ApiLuckyUpgradeBody;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.UpgradeBodyA;
import com.ruoyi.domain.vo.ApiLuckyOrnamentsDataVO;
import com.ruoyi.playingmethod.service.ApiLuckyUpgradeService;
import com.ruoyi.playingmethod.service.ApiUpgradeRecordService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.user.service.ApiUserPackSackService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.apache.commons.collections4.CollectionUtils;
import org.hibernate.validator.constraints.Range;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.math.BigDecimal;
import java.util.List;
@Api(tags = "幸运升级模式")
@RestController
@RequestMapping("/api/luckyUpgrade")
public class ApiLuckyUpgradeController extends BaseController {
@Autowired
private ApiUserPackSackService userPackSackService;
@Autowired
private ApiUpgradeRecordService upgradeRecordService;
private final TtUserService userService;
private final ApiLuckyUpgradeService apiLuckyUpgradeService;
private final ISysConfigService sysConfigService;
public ApiLuckyUpgradeController(TtUserService userService,
ApiLuckyUpgradeService apiLuckyUpgradeService,
ISysConfigService sysConfigService) {
this.userService = userService;
this.apiLuckyUpgradeService = apiLuckyUpgradeService;
this.sysConfigService = sysConfigService;
}
@Autowired
private TtUserBlendErcashMapper ttUserBlendErcashMapper;
public R checkLogin(){
Long userId;
try {
userId = getUserId();
if (ObjectUtil.isEmpty(userId)) AjaxResult.error(401,"登录过期,请重新登录。");
return R.ok(userId);
}catch (Exception e){
return R.fail(401,"登录过期,请重新登录。");
}
}
@ApiOperation("获取可升级的饰品列表")
@Anonymous
@GetMapping("/getOrnamentsList")
public R<List<ApiLuckyOrnamentsDataVO>> getOrnamentsList(ApiLuckyUpgradeBody apiLuckyUpgradeBody){
List<ApiLuckyOrnamentsDataVO> list = apiLuckyUpgradeService.getOrnamentsList(apiLuckyUpgradeBody);
return R.ok(list);
}
@ApiOperation("幸运升级")
@UpdateUserCache
@PostMapping("/upgrade")
public R upgrade(@RequestBody @Validated UpgradeBodyA upgradeParam){
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) return R.fail("网站维护中......");
R check = checkLogin();
if (!check.getCode().equals(200)) return R.fail(401,"登录过期,请重新登录。");
Integer userId = ((Long)check.getData()).intValue();
TtUser ttUser = userService.getById(userId);
BigDecimal add = ttUser.getAccountAmount();
if (add.compareTo(upgradeParam.getPrice()) <= 0) {
return R.fail("余额不足!");
}
LambdaQueryWrapper<TtUserBlendErcash> queryWrapper = Wrappers.lambdaQuery();
queryWrapper.eq(TtUserBlendErcash::getUserId, userId)
.eq(TtUserBlendErcash::getType, TtAccountRecordType.INPUT.getCode())
.eq(TtUserBlendErcash::getSource, TtAccountRecordSource.RECHARGE.getCode())
.select(TtUserBlendErcash::getTotal);
List<TtUserBlendErcash> ercash = ttUserBlendErcashMapper.selectList(queryWrapper);
if (CollectionUtils.isEmpty(ercash)) {
return R.fail("您的账户未进行充过值!");
}
return apiLuckyUpgradeService.upgrade(ttUser, upgradeParam);
}
@ApiOperation("获取幸运升级历史记录")
@PostMapping("/getUpgradeRecord")
public R getUpgradeRecord(@RequestBody @Validated UpgradeCondition param){
return upgradeRecordService.historyDetail(param);
}
@ApiOperation("获取幸运升级十佳记录")
@GetMapping("/tenTopUpgradeRecord")
public R tenTopUpgradeRecord(@RequestParam(required = false) Boolean isVictory){
return upgradeRecordService.tenTopDetail(isVictory);
}
}

View File

@@ -0,0 +1,98 @@
package com.ruoyi.playingmethod.controller;
import com.ruoyi.common.annotation.NoRepeatSubmit;
import com.ruoyi.common.annotation.UserPermission;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.domain.other.TtRedPack;
import com.ruoyi.domain.other.TtRedPacketRecord;
import com.ruoyi.playingmethod.service.ApiRedPackService;
import com.ruoyi.thirdparty.wechat.domain.PtLoginUser;
import com.ruoyi.thirdparty.wechat.service.ApiTokenService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import jakarta.servlet.http.HttpServletRequest;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
/**
* 不用这个api领红包了
*
* @see com.ruoyi.user.controller.ApiBonusController
*/
@Deprecated
@RestController
@RequestMapping("/api/redPackData")
@Api(tags = "红包")
public class ApiRedPackContoller extends BaseController {
@Autowired
private ApiTokenService tokenService;
@Autowired
private ApiRedPackService redPackService;
@PostMapping("/getRedPackList")
@UserPermission
@NoRepeatSubmit
@ApiOperation(value = "获取红包列表")
public TableDataInfo getRedPackList(HttpServletRequest request, Integer type) {
PtLoginUser user = tokenService.getLoginUser(request);
startPage();
List<TtRedPack> redPacks = redPackService.getRedPackList(user, type);
return getDataTable(redPacks);
}
@Deprecated
@PostMapping("/getRedPack")
@UserPermission
@NoRepeatSubmit
@ApiOperation(value = "领取红包")
public AjaxResult getRedPack(@RequestParam("redPackId") Long redPackId) {
Long userId = getUserId();
return redPackService.getRedPack(userId, redPackId);
}
@PostMapping("/getRedPackByKey")
@UserPermission
@NoRepeatSubmit
@ApiOperation(value = "根据红包口令兑换红包")
public AjaxResult getRedPackByKey(@RequestParam("redPackKey") String redPackKey) {
LoginUser loginUser = getLoginUser();
if (redPackKey == null || "".equals(redPackKey)) {
return AjaxResult.error("没有填写红包口令");
}
return redPackService.getRedPackByKey(loginUser, redPackKey);
}
@PostMapping("/getActivityDataHis")
@UserPermission
@NoRepeatSubmit
@ApiOperation(value = "查询参与的红包活动记录")
public TableDataInfo getBoxDataHis(HttpServletRequest request) {
PtLoginUser user = tokenService.getLoginUser(request);
startPage();
List<TtRedPacketRecord> boxRecords = redPackService.getActivityDataHis(user);
return getDataTable(boxRecords);
}
@PostMapping("/getRedPackDataDetail")
@UserPermission
@NoRepeatSubmit
@ApiOperation(value = "查询历史记录")
public AjaxResult getRedPackDataDetail(HttpServletRequest request, @RequestParam("recordId") Long recordId) {
PtLoginUser user = tokenService.getLoginUser(request);
return redPackService.getRedPackDataDetail(user, recordId);
}
}

View File

@@ -0,0 +1,42 @@
package com.ruoyi.playingmethod.controller;
import com.ruoyi.common.annotation.UserPermission;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.framework.web.service.TokenService;
import com.ruoyi.playingmethod.model.body.SynthesizeRequest;
import com.ruoyi.playingmethod.service.IApiReplacementRecordService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
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 jakarta.servlet.http.HttpServletRequest;
/**
* 汰换记录Controller
*
* @author junhai
* @date 2023-09-10
*/
@RestController
@RequestMapping("/api/replacementRecord")
@Api(tags = "汰换记录")
public class ApiReplacementRecordController extends BaseController {
@Autowired
private IApiReplacementRecordService ttReplacementRecordService;
@PostMapping("/synthesizeItems")
@UserPermission
@ApiOperation("汰换饰品")
public AjaxResult synthesizeItems(@RequestBody SynthesizeRequest synthesizeRequest) {
LoginUser loginUser = getLoginUser();
return ttReplacementRecordService.synthesizeItems(loginUser, synthesizeRequest.getBoIds());
}
}

View File

@@ -0,0 +1,176 @@
package com.ruoyi.playingmethod.controller;
import cn.hutool.core.util.ObjectUtil;
import com.ruoyi.common.core.domain.entity.SysUser;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.rabbitmq.config.DelayedQueueConfig;
import com.ruoyi.domain.dto.roll.GetRollOpenPrizeParam;
import com.ruoyi.domain.dto.roll.GetRollPlayersParam;
import com.ruoyi.domain.dto.roll.GetRollPrizePool;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.common.annotation.RepeatSubmit;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.PageDataInfo;
import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO;
import com.ruoyi.domain.vo.roll.RollJackpotOrnamentsVO;
import com.ruoyi.domain.vo.roll.RollUserVO;
import com.ruoyi.playingmethod.service.ApiRollService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.domain.vo.RollDetailsDataVO;
import com.ruoyi.domain.vo.RollListDataVO;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.annotations.ApiOperation;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.util.List;
@Api(tags = "ROLL房模式")
@RestController
@RequestMapping("/api/roll")
public class ApiRollController extends BaseController {
private final ISysConfigService sysConfigService;
private final TtUserService userService;
private final ApiRollService apiRollService;
public ApiRollController(ISysConfigService sysConfigService,
TtUserService userService,
ApiRollService apiRollService) {
this.sysConfigService = sysConfigService;
this.userService = userService;
this.apiRollService = apiRollService;
}
@AllArgsConstructor
@NoArgsConstructor
@Data
public static class JoinRollParam {
@NotNull(message = "roll房id不能为空。")
private Integer rollId;
private String rollPassword;
}
@ApiOperation("加入ROLL房")
@RepeatSubmit(message = "操作过于频繁,请稍后重试!")
@PostMapping("/joinRoll")
public R joinRoll(@RequestBody @Validated JoinRollParam param) {
String websiteMaintenance = sysConfigService.selectConfigByKey("websiteMaintenance");
if ("1".equals(websiteMaintenance)) return R.fail("网站维护中......");
String rollMaintenance = sysConfigService.selectConfigByKey("rollMaintenance");
if ("1".equals(rollMaintenance)) return R.fail("Roll房功能正在维护中......");
Long userId = getUserId();
if (ObjectUtil.isEmpty(userId)) return R.fail(401, "登录过期,请重新登录。");
TtUser player = userService.getById(userId);
if (ObjectUtil.isEmpty(player)) return R.fail("异常的用户状态。");
return apiRollService.joinRoll(param, player);
// return StringUtils.isEmpty(msg) ? R.ok("成功加入Roll房") : R.fail(msg, "加入失败!");
}
@AllArgsConstructor
@NoArgsConstructor
@Data
public static class GetRollListParam {
private String rollName;
// 0官方 1主播
@ApiModelProperty("0官方 1主播")
private Integer rollType;
// 0未开奖 1已开奖
@ApiModelProperty("0未开奖 1已开奖")
private Integer rollStatus;
private Integer userId;
}
@ApiOperation("获取ROLL房列表")
@GetMapping("/getRollList")
public TableDataInfo getRollList(GetRollListParam param) {
startPage();
List<RollListDataVO> list = apiRollService.getRollList(param);
return getDataTable(list);
}
/**
* 我参与的ROLL房列表
*/
// @GetMapping("/getMyPartRollList")
public PageDataInfo<RollListDataVO> getMyPartRollList(@RequestBody GetRollListParam param) {
startPage();
List<RollListDataVO> list = apiRollService.getRollList(param);
return getPageData(list);
}
@ApiOperation("获取ROLL房详情")
@GetMapping("/getRollDetails/{rollId}")
public R<RollDetailsDataVO> getRollDetails(@PathVariable("rollId") Integer rollId) {
return apiRollService.getRollDetails(rollId);
}
@ApiOperation("获取ROLL房参与人员详情")
@PostMapping("/getRollPlayers")
public R<List<RollUserVO>> getRollPlayers(@RequestBody @Validated GetRollPlayersParam param) {
return apiRollService.getRollPlayers(param);
}
@ApiOperation("获取ROLL房奖池详情")
@PostMapping("/getRollPrizePool")
public R<List<RollJackpotOrnamentsVO>> getRollPrizePool(@RequestBody @Validated GetRollPrizePool param) {
return apiRollService.getRollPrizePool(param);
}
@ApiOperation("获取ROLL开奖详情")
@PostMapping("/getRollOpenPrize")
public R<List<TtBoxRecordsVO>> getRollOpenPrize(@RequestBody @Validated GetRollOpenPrizeParam param) {
return apiRollService.getRollOpenPrize(param);
}
@ApiOperation("开奖测试,正式环境删除")
@GetMapping("/endRoll/{rollId}")
public R endRoll(@PathVariable("rollId") Integer rollId){
return apiRollService.endROLL(rollId);
}
// @Autowired
private RabbitTemplate rabbitTemplate;
// @GetMapping("/endRoll/{rollId}")
public String mqTest(@PathVariable("rollId") Integer rollId) {
// Calendar c = Calendar.getInstance();
// long timeInMillis = c.getTimeInMillis();
// c.add(Calendar.SECOND, 3);
// long timeInMillis1 = c.getTimeInMillis();
// long l = timeInMillis1 - timeInMillis;
MessageProperties messageProperties = new MessageProperties();
messageProperties.setExpiration("3000"); // 消息的过期属性,单位 ms
Message message = new Message("这条消息 4 秒后过期".getBytes(), messageProperties);
rabbitTemplate.convertAndSend(
DelayedQueueConfig.OPEN_ROLL_EXCHANGE,
DelayedQueueConfig.OPEN_ROLL_KEY,
message
);
return "1";
}
}

View File

@@ -0,0 +1,52 @@
package com.ruoyi.playingmethod.controller;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.playingmethod.entity.TtAttendanceRecord;
import com.ruoyi.playingmethod.service.IApiAttendanceRecordService;
import io.swagger.annotations.Api;
import org.apache.poi.ss.formula.functions.T;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@Api(tags = "签到记录")
@RestController
@RequestMapping("/api/attendanceRecord")
public class ApiTtAttendanceRecordController extends BaseController {
private final IApiAttendanceRecordService iApiAttendanceRecordService;
public ApiTtAttendanceRecordController(IApiAttendanceRecordService iApiAttendanceRecordService) {
this.iApiAttendanceRecordService = iApiAttendanceRecordService;
}
/**
* 签到
*/
// @GetMapping("/attendance")
public R<String> attendance() {
System.out.println("==================签到=================");
TtAttendanceRecord ttAttendanceRecord = iApiAttendanceRecordService.selectByUid(getUserId().intValue());
if (ttAttendanceRecord == null){
// 增加签到记录
iApiAttendanceRecordService.insert(getUserId().intValue());
return R.ok("签到成功");
}
// List<UserPackSackDataVO> userCompoundRecord = iApiAttendanceRecordService.getUserCompoundRecord(getUserId().intValue());
return R.fail("签到失败,一天只能签到一次");
}
/**
* 七天签到记录
*/
// @GetMapping("/sevenAttendance")
public R<List<TtAttendanceRecord>> sevenAttendance() {
System.out.println("==================七天签到=================");
// TtAttendanceRecord ttAttendanceRecord = iApiAttendanceRecordService.selectByUid(getUserId().intValue());
List<TtAttendanceRecord> ttAttendanceRecordList = iApiAttendanceRecordService.selectSevenAttendance(getUserId().intValue());
return R.ok(ttAttendanceRecordList);
}
}

View File

@@ -0,0 +1,30 @@
package com.ruoyi.playingmethod.controller;
import com.ruoyi.admin.service.TtUserAvatarService;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.domain.other.TtUserAvatar;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@Api(tags = "用户头像")
@RestController
@RequestMapping("/api/userAvatar")
public class ApiUserAvatarController extends BaseController {
private final TtUserAvatarService userAvatarService;
public ApiUserAvatarController(TtUserAvatarService userAvatarService) {
this.userAvatarService = userAvatarService;
}
@ApiOperation("用户头像列表")
@GetMapping("/list")
public R<List<TtUserAvatar>> list() {
List<TtUserAvatar> list = userAvatarService.list();
return R.ok(list);
}
}

View File

@@ -0,0 +1,92 @@
package com.ruoyi.playingmethod.controller;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.admin.service.TtWelfareService;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.TtBox;
import com.ruoyi.domain.other.TtWelfare;
import com.ruoyi.domain.other.TtWelfareMonthlyRecharges;
import com.ruoyi.domain.vo.OpenBoxVO;
import com.ruoyi.playingmethod.model.ApiWelfare;
import com.ruoyi.playingmethod.model.ApiWelfareRecord;
import com.ruoyi.playingmethod.service.ApiWelfareService;
import com.ruoyi.system.service.ISysConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import io.swagger.annotations.ApiResponses;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.*;
@Api(tags = "福利")
@RestController
@RequestMapping("/api/welfare")
public class ApiWelfareController extends BaseController {
@Autowired
private ApiWelfareService apiWelfareService;
@Autowired
private TtWelfareService ttWelfareService;
@Autowired
private ISysConfigService sysConfigService;
@Autowired
private TtUserService userService;
@ApiOperation("获取福利列表")
@ApiResponse(code = 200, message = "eligible【是否具备领取条件0否 1是" +
"<br>claimStatus【领取状态0未领取 1已领取")
@GetMapping("/getWelfareList")
public TableDataInfo getWelfareList() {
startPage();
Long userId = SecurityUtils.getUserId();
List<ApiWelfare> list = apiWelfareService.getWelfareList(userId);
return getDataTable(list);
}
@ApiOperation("领取福利")
@GetMapping("/claimWelfare/{welfareId}")
public AjaxResult claimWelfare(@PathVariable Integer welfareId) {
Long userId = SecurityUtils.getUserId();
// 检查福利是否存在
TtWelfare ttWelfare = ttWelfareService.selectTtWelfareByWelfareId(welfareId);
if (Objects.isNull(ttWelfare)) {
return error("福利不存在");
}
// 检查是否尚未具备领取条件
if (apiWelfareService.checkNotEligible(welfareId, userId)) {
return AjaxResult.error("尚未达到领取条件");
}
// 检查是否已领取
if (apiWelfareService.checkClaimed(welfareId, userId)) {
return AjaxResult.error("该福利已被领取");
}
// 进入开箱流程
String bindBoxMaintenance = sysConfigService.selectConfigByKey("bindBoxMaintenance");
if ("1".equals(bindBoxMaintenance)) {
return AjaxResult.error("盲盒开箱功能正在维护中......");
}
TtUser ttUser = userService.getById(getUserId());
if ("0".equals(ttUser.getIsRealCheck())) {
return AjaxResult.error("您的账户未进行实名认证,请先进行实名认证!");
}
OpenBoxVO openBoxVO = apiWelfareService.claimWelfare(welfareId, userId);
return AjaxResult.success(openBoxVO);
}
}

View File

@@ -0,0 +1,85 @@
package com.ruoyi.playingmethod.controller;
import com.ruoyi.admin.service.ITtWelfareMonthlyRechargesService;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.common.core.controller.BaseController;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.page.TableDataInfo;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.TtWelfareMonthlyRecharges;
import com.ruoyi.domain.vo.OpenBoxVO;
import com.ruoyi.playingmethod.model.ApiWelfare;
import com.ruoyi.playingmethod.model.vo.ApiWelfareMonthlyRechargesVO;
import com.ruoyi.playingmethod.service.ApiWelfareMonthlyRechargesService;
import com.ruoyi.system.service.ISysConfigService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiResponse;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Objects;
@Api(tags = "每月充值福利")
@RestController
@RequestMapping("/api/welfare/monthlyRecharges")
public class ApiWelfareMonthlyRechargesController extends BaseController {
@Autowired
private ApiWelfareMonthlyRechargesService apiWelfareMonthlyRechargesService;
@Autowired
private ITtWelfareMonthlyRechargesService ttWelfareMonthlyRechargesService;
@Autowired
private ISysConfigService sysConfigService;
@Autowired
private TtUserService userService;
@ApiOperation("获取每月充值福利列表")
@ApiResponse(code = 200, message = "eligible【是否具备领取条件0否 1是" +
"<br>claimStatus【领取状态0未领取 1已领取")
@GetMapping("/list")
public TableDataInfo getWelfareList() {
startPage();
Long userId = SecurityUtils.getUserId();
List<ApiWelfareMonthlyRechargesVO> list = apiWelfareMonthlyRechargesService.getWelfareList(userId);
return getDataTable(list);
}
@ApiOperation("领取福利")
@GetMapping("/claimWelfare/{welfareId}")
public AjaxResult claimWelfare(@PathVariable Integer welfareId) {
Long userId = SecurityUtils.getUserId();
// 检查福利是否存在
TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges = ttWelfareMonthlyRechargesService.selectTtWelfareMonthlyRechargesById(welfareId);
if (Objects.isNull(ttWelfareMonthlyRecharges)) {
return error("福利不存在");
}
// 检查是否尚未具备领取条件
if (!apiWelfareMonthlyRechargesService.checkEligible(welfareId, userId)) {
return AjaxResult.error("尚未达到领取条件");
}
// 检查是否已领取
if (apiWelfareMonthlyRechargesService.checkClaimed(welfareId, userId)) {
return AjaxResult.error("该福利已被领取");
}
// 进入开箱流程
String bindBoxMaintenance = sysConfigService.selectConfigByKey("bindBoxMaintenance");
if ("1".equals(bindBoxMaintenance)) {
return AjaxResult.error("盲盒开箱功能正在维护中......");
}
TtUser ttUser = userService.getById(getUserId());
if ("0".equals(ttUser.getIsRealCheck())) {
return AjaxResult.error("您的账户未进行实名认证,请先进行实名认证!");
}
OpenBoxVO openBoxVO = apiWelfareMonthlyRechargesService.claimWelfare(welfareId, userId);
return AjaxResult.success(openBoxVO);
}
}

View File

@@ -0,0 +1,7 @@
package com.ruoyi.playingmethod.customException;
public class CreateFightException extends RuntimeException{
public CreateFightException(String msg){
super(msg);
}
}

View File

@@ -0,0 +1,8 @@
package com.ruoyi.playingmethod.customException;
// websocket用户重复连接异常
public class WsRepetitiontException extends RuntimeException{
public WsRepetitiontException(String msg){
super(msg);
}
}

View File

@@ -0,0 +1,20 @@
package com.ruoyi.playingmethod.domain.vo;
import com.ruoyi.domain.other.TtRobotFightGroupBox;
import lombok.Data;
import java.util.List;
@Data
public class ApiRobotFightGroupVO {
private Integer groupId;
private String groupName;
private Integer seatNum;
private String model;
private List<TtRobotFightGroupBox> ttRobotFightGroupBoxList;
}

View File

@@ -0,0 +1,13 @@
package com.ruoyi.playingmethod.domain.vo;
import lombok.Data;
@Data
public class CompValueVO {
/** 当前补偿值 */
private Integer currentCompValue;
/** 设定补偿值 */
private Integer desiredCompValue;
}

View File

@@ -0,0 +1,49 @@
package com.ruoyi.playingmethod.entity;
import com.baomidou.mybatisplus.annotation.TableName;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.ruoyi.common.annotation.Excel;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.experimental.Accessors;
import java.util.Date;
@Data
@NoArgsConstructor
@AllArgsConstructor
@Accessors(chain = true)
@Builder
@TableName(value = "tt_attendance_record")
public class TtAttendanceRecord {
private static final long serialVersionUID = 1L;
/** */
private Integer id;
/** 用户ID */
@Excel(name = "用户ID")
private Integer userId;
/** 创建时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date createTime;
/** 更新时间 */
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date updateTime;
/** 获取金币数 */
@Excel(name = "获取金币数")
private Integer coin;
@Excel(name = "是否删除")
private String delFlag;
private String isStatus = "0";
}

View File

@@ -0,0 +1,19 @@
package com.ruoyi.playingmethod.mapper;
import com.ruoyi.playingmethod.entity.TtAttendanceRecord;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
@Mapper
public interface ApiAttendanceRecordMapper {
TtAttendanceRecord selectByUid(@Param("uid") Integer uid);
int insertAttendanceRecord(TtAttendanceRecord ttAttendanceRecord);
TtAttendanceRecord selectSevenAttendance(@Param("uid") Integer uid,@Param("createTime") String createTime);
}

View File

@@ -0,0 +1,7 @@
package com.ruoyi.playingmethod.mapper;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ApiBattleRoyaleMapper {
}

View File

@@ -0,0 +1,34 @@
package com.ruoyi.playingmethod.mapper;
import com.ruoyi.domain.other.TtBoxUser;
import com.ruoyi.domain.other.TtBoxA;
import com.ruoyi.domain.other.TtOrnamentsA;
import com.ruoyi.domain.other.TtBoxLevelA;
import com.ruoyi.playingmethod.controller.ApiBindBoxController;
import com.ruoyi.playingmethod.domain.vo.CompValueVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface ApiBindBoxMapper {
TtBoxA getBoxData(Integer boxId);
List<TtOrnamentsA> getBoxOrnamentsList(Integer boxId);
List<TtBoxA> getBoxList(
@Param("boxTypeId") Integer boxTypeId,
@Param("homeFlag") String homeFlag,
@Param("boxType") String boxType
);
TtOrnamentsA getOrnamentsData(@Param("boxId") Integer boxId, @Param("ornamentsId") Integer ornamentsId, @Param("id") Long id);
// List<TtBoxUser> getBindBoxHistory(ApiBindBoxController.boxHistoryParam param);
List<TtBoxLevelA> getProbabilityDistribution(Integer boxId);
TtOrnamentsA ornamentsInfo(@Param("boxId") Integer boxId,@Param("ornamentId") String ornamentId);
}

View File

@@ -0,0 +1,10 @@
package com.ruoyi.playingmethod.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.domain.other.TtBoxOrnaments;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ApiBoxOrnamentsMapper extends BaseMapper<TtBoxOrnaments> {
}

View File

@@ -0,0 +1,13 @@
package com.ruoyi.playingmethod.mapper;
import com.ruoyi.domain.vo.UserPackSackDataVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ApiCompoundMapper {
UserPackSackDataVO selectCompoundDataById(Long packSackId);
List<UserPackSackDataVO> selectCompoundRecordByUserId(Integer userId);
}

View File

@@ -0,0 +1,39 @@
package com.ruoyi.playingmethod.mapper;
import com.ruoyi.domain.other.TtBoxA;
import com.ruoyi.domain.vo.ApiFightListDataVO;
import com.ruoyi.domain.vo.JoinFightUserDataVO;
import com.ruoyi.domain.vo.UserPackSackDataVO;
import com.ruoyi.playingmethod.model.vo.ApiFightRankingVO;
import com.ruoyi.playingmethod.model.vo.ApiFightTenTopVo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.Date;
import java.util.List;
@Mapper
public interface ApiFightMapper {
JoinFightUserDataVO selectJoinFightUserData(Integer fightUserId);
List<UserPackSackDataVO> selectOrnamentsDataListByUserIdAndFightId(@Param("userId") Integer userId, @Param("fightId") Integer fightId);
List<ApiFightListDataVO> getFightList(@Param("model") String model, @Param("status") String status, @Param("userId") Integer userId, @Param("fightId") Integer fightId);
List<TtBoxA> getFightBoxList(Integer boxTypeId);
/**
* 获取指定日期的对战排行榜
*/
List<ApiFightRankingVO> getFightRankingByDate(String date);
/**
* 盲盒对战十佳对战
*/
List<ApiFightTenTopVo> tenTopFight(@Param("startDate") String startDate, @Param("endDate") String endDate, @Param("maxType") Integer maxType);
/**
* 查看对战开箱出来的总金额
*/
List<ApiFightTenTopVo> fightOrnamentsPrice(@Param("fightIds") List<Integer> fightIds);
}

View File

@@ -0,0 +1,17 @@
package com.ruoyi.playingmethod.mapper;
import com.ruoyi.domain.dto.upgrade.UpgradeCondition;
import com.ruoyi.domain.vo.ApiLuckyOrnamentsDataVO;
import com.ruoyi.domain.other.ApiLuckyUpgradeBody;
import com.ruoyi.domain.vo.ApiLuckyUpgradeRecordDataVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface ApiLuckyUpgradeMapper {
List<ApiLuckyOrnamentsDataVO> getOrnamentsList(ApiLuckyUpgradeBody apiLuckyUpgradeBody);
List<ApiLuckyUpgradeRecordDataVO> getUpgradeRecord(UpgradeCondition param);
}

View File

@@ -0,0 +1,80 @@
package com.ruoyi.playingmethod.mapper;
import com.ruoyi.domain.entity.TtOrnament;
import com.ruoyi.domain.other.TtReplacementRecord;
import com.ruoyi.domain.vo.UserPackSackDataVO;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
import java.util.List;
import java.util.Map;
/**
* 汰换记录Mapper接口
*
* @author junhai
* @date 2023-09-10
*/
public interface ApiReplacementRecordMapper {
/**
* 查询汰换记录
*
* @param id 汰换记录主键
* @return 汰换记录
*/
public TtReplacementRecord selectTtReplacementRecordById(Long id);
/**
* 查询汰换记录列表
*
* @param ttReplacementRecord 汰换记录
* @return 汰换记录集合
*/
public List<TtReplacementRecord> selectTtReplacementRecordList(TtReplacementRecord ttReplacementRecord);
/**
* 新增汰换记录
*
* @param ttReplacementRecord 汰换记录
* @return 结果
*/
public int insertTtReplacementRecord(TtReplacementRecord ttReplacementRecord);
/**
* 修改汰换记录
*
* @param ttReplacementRecord 汰换记录
* @return 结果
*/
public int updateTtReplacementRecord(TtReplacementRecord ttReplacementRecord);
/**
* 删除汰换记录
*
* @param id 汰换记录主键
* @return 结果
*/
public int deleteTtReplacementRecordById(Long id);
/**
* 批量删除汰换记录
*
* @param ids 需要删除的数据主键集合
* @return 结果
*/
public int deleteTtReplacementRecordByIds(Long[] ids);
/**
* 根据以下参数进行饰品的查询
*
* @param map
* @return
*/
List<TtOrnament> findByPriceRange(Map<String, BigDecimal> map);
/**
* 查询背包饰品
*/
List<UserPackSackDataVO> selectUserPackSack(@Param("userId") Integer userId,
@Param("itemIds") List<Long> itemIds);
}

View File

@@ -0,0 +1,12 @@
package com.ruoyi.playingmethod.mapper;
import com.ruoyi.playingmethod.domain.vo.ApiRobotFightGroupVO;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ApiRobotFightGroupMapper {
public List<ApiRobotFightGroupVO> selectApiRobotFightGroupBoxVOList();
}

View File

@@ -0,0 +1,19 @@
package com.ruoyi.playingmethod.mapper;
import com.ruoyi.domain.other.JackpotData;
import com.ruoyi.domain.vo.RollDetailsDataVO;
import com.ruoyi.domain.vo.RollListDataVO;
import com.ruoyi.playingmethod.controller.ApiRollController.GetRollListParam;
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ApiRollMapper {
List<RollListDataVO> getRollList(GetRollListParam param);
RollDetailsDataVO getRollDetails(Integer rollId);
List<JackpotData> getJackpotData(Integer jackpotId);
}

View File

@@ -0,0 +1,10 @@
package com.ruoyi.playingmethod.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.domain.entity.TtUserBlendErcash;
import com.ruoyi.domain.other.TtUpgradeRecord;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ApiTtUserBlendErcashMapper extends BaseMapper<TtUserBlendErcash> {
}

View File

@@ -0,0 +1,9 @@
package com.ruoyi.playingmethod.mapper;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ruoyi.domain.other.TtUpgradeRecord;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ApiUpgradeRecordMapper extends BaseMapper<TtUpgradeRecord> {
}

View File

@@ -0,0 +1,28 @@
package com.ruoyi.playingmethod.mapper;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.playingmethod.model.ApiWelfare;
import com.ruoyi.playingmethod.model.ApiWelfareRecord;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
import java.util.List;
@Mapper
public interface ApiWelfareMapper {
List<ApiWelfare> getWelfareList(Long userId);
/**
* 检查是否已领取
*/
boolean checkClaimed(
@Param("welfareId") Integer welfareId,
@Param("userId") Long userId,
@Param("type") String type);
Integer getBoxIdByWelfareId(Integer welfareId);
int saveClaimWelfareRecord(ApiWelfareRecord apiWelfareRecord);
}

View File

@@ -0,0 +1,33 @@
package com.ruoyi.playingmethod.mapper;
import com.ruoyi.playingmethod.model.ApiWelfare;
import com.ruoyi.playingmethod.model.ApiWelfareRecord;
import com.ruoyi.playingmethod.model.vo.ApiWelfareMonthlyRechargesVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.math.BigDecimal;
import java.util.List;
@Mapper
public interface ApiWelfareMonthlyRechargesMapper {
List<ApiWelfareMonthlyRechargesVO> getWelfareList(Long userId);
/**
* 查询当月充值量
*/
BigDecimal selectRechargeRecordsOfMonth(Long userId);
/**
* 检查是否已领取
*/
boolean checkClaimed(
@Param("welfareId") Integer welfareId,
@Param("userId") Long userId,
@Param("type") String type);
Integer getBoxIdByWelfareId(Integer welfareId);
int saveClaimWelfareRecord(ApiWelfareRecord apiWelfareRecord);
}

View File

@@ -0,0 +1,51 @@
package com.ruoyi.playingmethod.model;
import com.ruoyi.domain.other.TtBox;
import io.swagger.annotations.ApiModelProperty;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ApiWelfare {
/**
* 福利ID
*/
private Integer welfareId;
/**
* 福利名称
*/
private String welfareName;
/**
* 福利类型
*/
private String type;
/**
* VIP等级
*/
private Integer vipLevel;
/**
* 是否具备领取条件0否 1是
*/
private String eligible;
/**
* 领取状态0未领取 1已领取
*/
private String claimStatus;
/**
* 对应等级达标金额
*/
private BigDecimal rechargeThreshold;
/**
* 宝箱信息
*/
private TtBox ttBox;
}

View File

@@ -0,0 +1,17 @@
package com.ruoyi.playingmethod.model;
import lombok.Data;
import java.util.Date;
@Data
public class ApiWelfareRecord {
private Integer welfareId;
private Long userId;
private String type;
private Date createTime;
}

View File

@@ -0,0 +1,12 @@
package com.ruoyi.playingmethod.model.body;
import lombok.Data;
import java.util.List;
@Data
public class SynthesizeRequest {
private List<Long> boIds;
// private List<Long> yoIds;
}

View File

@@ -0,0 +1,17 @@
package com.ruoyi.playingmethod.model.vo;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ApiFightRankingVO {
private Long userId;
private String nickName;
private String avatar;
private BigDecimal totalBoxPrice;
}

View File

@@ -0,0 +1,21 @@
package com.ruoyi.playingmethod.model.vo;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ApiFightTenTopVo {
/**
* 对战ID
*/
private Integer id;
/**
* 宝箱开出来饰品总价值
*/
private BigDecimal totalOrnamentsPrice;
/**
* 宝箱开出来饰品减去宝箱总金额 * 玩家个数
*/
private BigDecimal increaseOrnamentsPrice;
}

View File

@@ -0,0 +1,28 @@
package com.ruoyi.playingmethod.model.vo;
import com.ruoyi.domain.other.TtBox;
import lombok.Data;
import java.math.BigDecimal;
@Data
public class ApiWelfareMonthlyRechargesVO {
/** 福利ID */
private Integer welfareId;
/** 福利名称 */
private String welfareName;
/** 充值金额 */
private BigDecimal amount;
/** 是否具备领取条件0否 1是 */
private String eligible;
/** 领取状态0未领取 1已领取 */
private String claimStatus;
/** 宝箱信息 */
private TtBox ttBox;
}

View File

@@ -0,0 +1,26 @@
package com.ruoyi.playingmethod.model.vo;
import com.ruoyi.domain.other.TtBox;
import lombok.Data;
@Data
public class ApiWelfareVO {
/** 福利ID */
private Integer welfareId;
/** 福利名称 */
private String welfareName;
/** 福利类型 */
private String type;
/** 是否具备领取条件0否 1是 */
private String eligible;
/** 领取状态0未领取 1已领取 */
private String claimStatus;
/** 宝箱信息 */
private TtBox ttBox;
}

View File

@@ -0,0 +1,78 @@
package com.ruoyi.playingmethod.scheduled;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.admin.mapper.TtUserBlendErcashMapper;
import com.ruoyi.admin.service.TtFightRankingRewardService;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.domain.common.constant.TtAccountRecordSource;
import com.ruoyi.domain.common.constant.TtAccountRecordType;
import com.ruoyi.domain.entity.TtUserBlendErcash;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.TtFightRankingReward;
import com.ruoyi.playingmethod.model.vo.ApiFightRankingVO;
import com.ruoyi.playingmethod.service.ApiFightService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
@Slf4j
@Component("FightRankingRewardTask")
public class FightRankingRewardTask {
@Autowired
private ApiFightService apiFightService;
@Autowired
private TtFightRankingRewardService ttFightRankingRewardService;
@Autowired
private TtUserService userService;
@Autowired
private TtUserBlendErcashMapper ttUserBlendErcashMapper;
/** 发放对战排行榜奖励 */
public void distributeFightRankingReward() {
log.info("开始发放对战排行榜奖励");
// 查询昨日排行榜
SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd");
Calendar cal = Calendar.getInstance();
cal.add(Calendar.DATE, -1);
Date yesterday = cal.getTime();
String formattedYesterdayDate = formatter.format(yesterday);
List<ApiFightRankingVO> yesterdayFightRanking = apiFightService.getFightRankingByDate(formattedYesterdayDate);
for (int i = 0; i < yesterdayFightRanking.size(); i++) {
// 查询排行榜对应奖励
TtFightRankingReward ttFightRankingReward = ttFightRankingRewardService.selectTtFightRankingRewardById(i + 1);
Long userId = yesterdayFightRanking.get(i).getUserId();
BigDecimal reward = ttFightRankingReward.getReward();
// 加钱
LambdaUpdateWrapper<TtUser> userUpdate = new LambdaUpdateWrapper<>();
userUpdate
.eq(TtUser::getUserId, userId)
.setSql("account_amount = account_amount + " + reward);
userService.update(userUpdate);
// 综合消费日志
TtUser ttUser = userService.getById(userId);
TtUserBlendErcash blendErcash = TtUserBlendErcash.builder()
.userId(userId.intValue())
.amount(reward.compareTo(BigDecimal.ZERO) > 0 ? reward : null)
.finalAmount(reward.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountAmount().add(reward) : null)
.total(reward.add(reward)) // 收支合计
.type(TtAccountRecordType.INPUT.getCode())
.source(TtAccountRecordSource.FIGHT_RANKING_REWARD.getCode())
.remark(TtAccountRecordSource.FIGHT_RANKING_REWARD.getMsg())
.createTime(new Timestamp(System.currentTimeMillis()))
.updateTime(new Timestamp(System.currentTimeMillis()))
.build();
ttUserBlendErcashMapper.insert(blendErcash);
}
}
}

View File

@@ -0,0 +1,325 @@
package com.ruoyi.playingmethod.scheduled;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.ruoyi.admin.mapper.TtBoxMapper;
import com.ruoyi.admin.mapper.TtFightMapper;
import com.ruoyi.admin.mapper.TtUserMapper;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.DateTimeUtils;
import com.ruoyi.common.utils.RandomUtil;
import com.ruoyi.domain.common.constant.UserType;
import com.ruoyi.domain.entity.fight.FightSeat;
import com.ruoyi.domain.entity.fight.TtFight;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.CreateFightBody;
import com.ruoyi.domain.other.FightBoutData;
import com.ruoyi.domain.other.TtBox;
import com.ruoyi.domain.other.TtRobotFightGroupBox;
import com.ruoyi.domain.vo.ApiFightListDataVO;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.playingmethod.domain.vo.ApiRobotFightGroupVO;
import com.ruoyi.playingmethod.mapper.ApiFightMapper;
import com.ruoyi.playingmethod.mapper.ApiRobotFightGroupMapper;
import com.ruoyi.playingmethod.service.ApiFightService;
import com.ruoyi.system.service.ISysConfigService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import java.math.BigDecimal;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Slf4j
@Component("FightRobotTask")
public class FightRobotTask {
@Value("${mkcsgo.fight.roundTime}")
private final Integer fightRoundTimeMs = 4000;
@Autowired
private TtFightMapper ttFightMapper;
@Autowired
private TtUserMapper ttUserMapper;
@Autowired
private ISysConfigService sysConfigService;
@Autowired
private ApiFightService apiFightService;
@Autowired
private RedisCache redisCache;
@Autowired
private ApiRobotFightGroupMapper robotFightGroupMapper;
@Autowired
private ApiFightMapper apiFightMapper;
@Autowired
private TtBoxMapper boxMapper;
private static final List<Integer[]> roomExistTime = new ArrayList<>() {{
add(new Integer[]{200, 30});
add(new Integer[]{800, 60});
add(new Integer[]{1500, 240});
}};
/**
* 机器人加入对战
*/
public void robotJoinFight() {
log.info("机器人加入对战");
// 查询所有处于等待中的对战房间ID
QueryWrapper<TtFight> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("status", 0);
queryWrapper.eq("model", 0);
List<TtFight> fightList = ttFightMapper.selectList(queryWrapper);
if (CollectionUtils.isEmpty(fightList)) {
log.info("没有等待中的对战房间");
return;
}
// 查询所有机器人ID
List<Integer> robotIdList = ttUserMapper.selectObjs(
new QueryWrapper<TtUser>()
.select("user_id")
.eq("user_type", UserType.ROBOT_USER.getCode())
);
if (robotIdList.isEmpty()) {
log.info("没有机器人");
return;
}
// 打乱对战ID列表顺序取前面指定数量的对战ID
Collections.shuffle(fightList);
List<Integer> userIdList = fightList.stream().map(TtFight::getUserId).filter(Objects::nonNull)
.collect(Collectors.toList());
if (userIdList.isEmpty()) {
return;
}
Map<Integer, TtUser> userMap = ttUserMapper.selectByIds(userIdList).stream()
.collect(Collectors.toMap(TtUser::getUserId, v -> v));
for (TtFight ttFight : fightList) {
TtUser user = userMap.get(ttFight.getUserId());
if (user == null || user.getUserType().equals(UserType.ROBOT_USER.getCode())) {
continue;
}
Optional<Integer[]> config = roomExistTime.stream()
.filter(arr -> ttFight.getBoxPriceTotal().doubleValue() <= arr[0]).findFirst();
if (config.isEmpty()) {
continue;
}
var time = config.get()[1];
if (ttFight.getCreateTime().getTime() + time * 1000 >= DateTimeUtils.now() * 1000) {
continue;
}
List<Integer> availableRobotIds = new ArrayList<>(robotIdList);
List<FightSeat> seats = ttFight.getSeats();
var inRoomPlayerIds = seats.stream()
.map(FightSeat::getPlayerId)
.filter(Objects::nonNull)
.collect(Collectors.toList());
// 只有一个空位才加入机器人
if (inRoomPlayerIds.size() != seats.size() - 1) {
continue;
}
availableRobotIds.removeAll(inRoomPlayerIds);
if (availableRobotIds.isEmpty()) {
log.warn("No available robots for fight: {}", ttFight.getId());
return;
}
for (FightSeat seat : seats) {
// 随机机器人进入空座位
if (Objects.isNull(seat.getPlayerId())) {
Integer robotId = availableRobotIds.get(RandomUtil.rand(availableRobotIds.size()));
TtUser player = ttUserMapper.selectById(robotId);
R<Object> objectR = apiFightService.joinFight(ttFight.getId(), player);
if (objectR.getCode() == 500) {
return;
}
log.info("机器人{}加入对战{}", robotId, ttFight.getId());
// 座位就绪
R fight = apiFightService.seatrReady(ttFight.getId(), player);
FightBoutData fightBoutData = new FightBoutData();
fightBoutData.setFightId(ttFight.getId());
fightBoutData.setBoutNum(1);
fightBoutData.setExpirationTime(16000);
String key = "fight_bout_data:fight_" + fightBoutData.getFightId();
redisCache.setCacheObject(key, fightBoutData, fightBoutData.getExpirationTime(), TimeUnit.MILLISECONDS);
// 调用开始对战方法
apiFightService.fightBegin(ttFight.getId(), player);
int gameTime = ttFight.getRoundNumber() * fightRoundTimeMs;
AsyncManager.me().timeout(new TimerTask() {
@Override
public void run() {
apiFightService.fightEnd(ttFight.getId());
}
}, gameTime, TimeUnit.MILLISECONDS);
}
}
}
}
/**
* 机器人创建对战
*/
public void robotCreateFight() {
log.info("机器人创建对战");
// 查询所有机器人ID
List<Integer> robotIdList = ttUserMapper.selectObjs(
new QueryWrapper<TtUser>()
.select("user_id")
.eq("user_type", UserType.ROBOT_USER.getCode())
);
if (robotIdList.isEmpty()) {
log.warn("无可用机器人,停止创建对战");
return;
}
// 定义价格档位配置
BigDecimal[] priceLimits = {BigDecimal.valueOf(200), BigDecimal.valueOf(800), BigDecimal.valueOf(1500)};
int[] targetCounts = {6, 4, 1};
List<ApiFightListDataVO> vos = apiFightMapper.getFightList(null, "0", null, null);
if (CollectionUtils.isEmpty(vos)) {
vos = new ArrayList<>();
}
vos.forEach(v -> {
if (v.getBoxPriceTotal() == null) {
v.setBoxPriceTotal(BigDecimal.ZERO);
}
});
// 统计当前各价位对战的存量
int[] currentCounts = new int[3]; // 对应 200, 800, 1500
for (ApiFightListDataVO vo : vos) {
if (vo.getBoxPriceTotal().compareTo(BigDecimal.valueOf(200)) <= 0) {
currentCounts[0]++;
} else if (vo.getBoxPriceTotal().compareTo(BigDecimal.valueOf(800)) <= 0) {
currentCounts[1]++;
} else if (vo.getBoxPriceTotal().compareTo(BigDecimal.valueOf(1500)) <= 0) {
currentCounts[2]++;
}
}
// 计算需要创建的数量
int[] needCreateCounts = new int[3];
for (int i = 0; i < 3; i++) {
needCreateCounts[i] = Math.max(targetCounts[i] - currentCounts[i], 0);
}
// 查询机器人宝箱分组
List<ApiRobotFightGroupVO> apiRobotFightGroupVOList = robotFightGroupMapper.selectApiRobotFightGroupBoxVOList();
if (CollectionUtils.isEmpty(apiRobotFightGroupVOList)) {
return;
}
Set<Integer> boxIds = apiRobotFightGroupVOList.stream()
.flatMap(v -> v.getTtRobotFightGroupBoxList().stream())
.map(TtRobotFightGroupBox::getBoxId)
.filter(Objects::nonNull)
.collect(Collectors.toSet());
List<TtBox> allBoxes = Collections.emptyList();
if (!boxIds.isEmpty()) {
allBoxes = new LambdaQueryChainWrapper<>(boxMapper)
.in(TtBox::getBoxId, boxIds)
.eq(TtBox::getIsFight, "0")
.eq(TtBox::getStatus, "0")
.list();
}
// 构建 BoxId -> Box 的 Map方便快速查找
Map<Integer, TtBox> boxMap = allBoxes.stream()
.collect(Collectors.toMap(TtBox::getBoxId, v -> v, (v1, v2) -> v1));
var groupMap = apiRobotFightGroupVOList.stream().collect(Collectors.toMap(ApiRobotFightGroupVO::getGroupId, v -> v));
Map<Integer, BigDecimal> groupPriceMap = new HashMap<>();
for (ApiRobotFightGroupVO groupVO : apiRobotFightGroupVOList) {
BigDecimal boxTotalPrice = BigDecimal.ZERO;
if (CollectionUtils.isNotEmpty(groupVO.getTtRobotFightGroupBoxList())) {
for (TtRobotFightGroupBox groupBox : groupVO.getTtRobotFightGroupBoxList()) {
TtBox box = boxMap.get(groupBox.getBoxId());
if (box != null) {
BigDecimal multiply = BigDecimal.valueOf(groupBox.getBoxNum()).multiply(box.getPrice());
boxTotalPrice = boxTotalPrice.add(multiply);
}
}
}
groupPriceMap.put(groupVO.getGroupId(), boxTotalPrice);
}
// 6. 循环处理不同价位的创建任务
for (int i = 0; i < needCreateCounts.length; i++) {
int needCount = needCreateCounts[i];
if (needCount <= 0) {
continue;
}
BigDecimal limitPrice = priceLimits[i];
BigDecimal lowPrice;
if (i > 0) {
lowPrice = priceLimits[i - 1];
} else {
lowPrice = BigDecimal.ZERO;
}
// 筛选出符合当前价位的分组
List<ApiRobotFightGroupVO> matchedGroups = groupPriceMap.entrySet().stream()
.filter(entry -> entry.getValue().compareTo(lowPrice) > 0 && entry.getValue().compareTo(limitPrice) <= 0)
.map(entry -> groupMap.get(entry.getKey()))
.collect(Collectors.toList());
if (CollectionUtils.isNotEmpty(matchedGroups)) {
createFight(matchedGroups, needCount, robotIdList);
}
}
}
private void createFight(List<ApiRobotFightGroupVO> groupList, int canCreateCount, List<Integer> robotIdList) {
if (CollectionUtils.isEmpty(groupList) || CollectionUtils.isEmpty(robotIdList)) {
return;
}
// 创建指定数量的房间
for (int i = 0; i < canCreateCount; i++) {
// 随机取一个机器人
Integer robotId = robotIdList.get(RandomUtil.rand(robotIdList.size()));
TtUser player = ttUserMapper.selectById(robotId);
// 随机取一个宝箱分组
ApiRobotFightGroupVO apiRobotFightGroupVO = groupList.get(RandomUtil.rand(groupList.size()));
log.info("机器人{}使用宝箱分组{}", robotId, apiRobotFightGroupVO.getGroupId());
// 创建对战
CreateFightBody createFightBody = new CreateFightBody();
createFightBody.setModel(apiRobotFightGroupVO.getModel());
createFightBody.setPlayerNumber(apiRobotFightGroupVO.getSeatNum());
Map<Integer, Integer> boxIdAndNumber = new HashMap<>();
for (TtRobotFightGroupBox ttRobotFightGroupBox : apiRobotFightGroupVO.getTtRobotFightGroupBoxList()) {
boxIdAndNumber.put(ttRobotFightGroupBox.getBoxId(), ttRobotFightGroupBox.getBoxNum());
}
createFightBody.setBoxIdAndNumber(boxIdAndNumber);
apiFightService.createFight(createFightBody, player);
}
}
}

View File

@@ -0,0 +1,134 @@
package com.ruoyi.playingmethod.scheduled;
import cn.hutool.core.date.DateUnit;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.ruoyi.admin.mapper.TtBoxRecordsMapper;
import com.ruoyi.admin.service.TtBoxRecordsService;
import com.ruoyi.admin.service.TtRollService;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.rabbitmq.config.DelayedQueueConfig;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.entity.fight.TtFight;
import com.ruoyi.domain.entity.roll.TtRoll;
import com.ruoyi.playingmethod.service.ApiFightService;
import com.ruoyi.playingmethod.service.ApiRollService;
import com.ruoyi.playingmethod.websocket.WsFightHall;
import com.ruoyi.playingmethod.websocket.WsFightRoom;
import com.ruoyi.playingmethod.websocket.util.WsResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.core.MessageProperties;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import static com.ruoyi.domain.common.constant.TtboxRecordStatus.IN_PACKSACK_ON;
import static com.ruoyi.playingmethod.websocket.constant.SMsgKey.ALL_FIGHT_ROOM;
@Slf4j
@Configuration // 1.主要用于标记配置类兼备Component的效果。
@EnableScheduling // 2.开启定时任务
public class RollTask {
@Value("${mkcsgo.fight.roundTime}")
private final Integer fightRoundTime = null;
@Autowired
private ApiFightService fightService;
@Autowired
private TtBoxRecordsMapper boxRecordsMapper;
@Autowired
private TtBoxRecordsService boxRecordsService;
@Autowired
private TtRollService ttRollService;
@Autowired
private Executor customThreadPoolExecutor;
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private ApiRollService apiRollService;
// 定时检查即将开奖的roll房临界区1分钟
@Scheduled(cron = "0 */1 * * * ?")
private void refreshDayTask() {
// System.out.println("11111111111111111");
//System.out.println("roll 开奖临界区检查");
// 查询两分钟以内将要开奖的roll房
Calendar c = Calendar.getInstance();
c.add(Calendar.MINUTE, 2);
Timestamp criticalTime = new Timestamp(c.getTimeInMillis());
LambdaQueryWrapper<TtRoll> wrapper = new LambdaQueryWrapper<>();
wrapper
.eq(TtRoll::getRollStatus, 0)
.eq(TtRoll::getDelFlag, 0)
.lt(TtRoll::getEndTime, criticalTime);
List<TtRoll> criticalRoll = ttRollService.list(wrapper);
if (criticalRoll.isEmpty()) return;
log.info("roll开奖临界区检查{}个roll房进入临界区", criticalRoll.size());
// 加入延时队列
for (TtRoll roll : criticalRoll) {
if (DateUtils.getNowDate().compareTo(roll.getEndTime()) > 0) {
log.info("roll房{}已经超时,直接开奖。", roll.getId());
R r = apiRollService.endROLL(roll.getId());
if (!r.getCode().equals(200)) {
log.error("roll开奖 消费失败,尝试消息补发再次消费!" + r.getMsg());
}
if (ObjectUtil.isNotEmpty(r.getMsg())) log.info("{}", r.getMsg());
// 已超时直接开奖,不再加入延时队列,避免重复开奖
continue;
}
// 相差时间未超时的roll房才加入延时队列
long betweenDay = DateUtil.between(DateUtils.getNowDate(), roll.getEndTime(), DateUnit.MS);
System.out.println("设置消息超时时间");
log.info("设置消息超时时间:{}",betweenDay);
MessageProperties messageProperties = new MessageProperties();
messageProperties.setExpiration(String.valueOf(betweenDay)); // 消息的过期属性,单位 ms
Message message = new Message(String.valueOf(roll.getId()).getBytes(), messageProperties);
// 延时队列实现开奖
rabbitTemplate.convertAndSend(
DelayedQueueConfig.OPEN_ROLL_EXCHANGE,
DelayedQueueConfig.OPEN_ROLL_KEY,
message);
log.info("Roll房{}-{}加入临界区,{}秒后开奖", roll.getId(), roll.getRollName(), betweenDay / 1000);
}
}
}

View File

@@ -0,0 +1,102 @@
package com.ruoyi.playingmethod.scheduled;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.ruoyi.admin.mapper.TtBoxRecordsMapper;
import com.ruoyi.admin.service.TtBoxRecordsService;
import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.entity.fight.TtFight;
import com.ruoyi.playingmethod.service.ApiFightService;
import com.ruoyi.playingmethod.websocket.WsFightHall;
import com.ruoyi.playingmethod.websocket.WsFightRoom;
import com.ruoyi.playingmethod.websocket.util.WsResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableScheduling;
import org.springframework.scheduling.annotation.Scheduled;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import static com.ruoyi.domain.common.constant.TtboxRecordStatus.IN_PACKSACK_ON;
import static com.ruoyi.playingmethod.websocket.constant.SMsgKey.ALL_FIGHT_ROOM;
@Slf4j
@Configuration //1.主要用于标记配置类兼备Component的效果。
@EnableScheduling // 2.开启定时任务
public class fightTask {
@Value("${mkcsgo.fight.roundTime}")
private final Integer fightRoundTime = null;
@Autowired
private ApiFightService fightService;
@Autowired
private TtBoxRecordsMapper boxRecordsMapper;
@Autowired
private TtBoxRecordsService boxRecordsService;
@Autowired
private Executor customThreadPoolExecutor;
// 定时更新超时未结束的对局
@Scheduled(cron = "0/6 * * * * ?")
private void refreshDayTask() {
log.info("定时更新超时未结束的对局");
// Timestamp now = new Timestamp(System.currentTimeMillis());
LambdaQueryWrapper<TtFight> fightQuery = new LambdaQueryWrapper<>();
fightQuery
.eq(TtFight::getStatus,1)
.and(wrapper->{
wrapper.isNull(TtFight::getEndTime);
})
.and(wrapper->{
wrapper.isNotNull(TtFight::getBeginTime);
})
// TODO: 2024/4/13 优化sql
// 对局开始时间早于当前时间减去一定时间间隔再减去6秒就会被认为是超时的对局
// begin_time < DATE_SUB(NOW(), INTERVAL 100 SECOND)
.last("AND begin_time < DATE_SUB(NOW(), INTERVAL " + fightRoundTime / 1000 + "* round_number + 60"+" second)");
List<TtFight> list = fightService.list(fightQuery);
if (list.isEmpty()) return;
List<Integer> fightIds = new ArrayList<>();
// 更新fight
list.stream().forEach(item->{
item.setEndTime(new Timestamp(System.currentTimeMillis()));
item.setStatus(3);
item.setRemark("超时强制更新结束状态");
fightIds.add(item.getId());
// 断开房间所有连接
WsFightRoom.batchClose(item);
});
fightService.updateBatchById(list);
// 异步更新对局数据
CompletableFuture.runAsync(() -> {
WsFightHall.broadcast(WsResult.ok(ALL_FIGHT_ROOM.name(), list));
}, customThreadPoolExecutor);
// 更新openBoxRecords
new LambdaUpdateChainWrapper<>(boxRecordsMapper)
// .eq(TtBoxRecords::getStatus,IN_PACKSACK_ON.getCode())
.in(TtBoxRecords::getFightId,fightIds)
.set(TtBoxRecords::getStatus,IN_PACKSACK_ON.getCode())
.update();
log.info("更新 {} 条超时未结束的对局记录",list.size());
}
}

View File

@@ -0,0 +1,4 @@
package com.ruoyi.playingmethod.service;
public interface ApiBattleRoyaleService {
}

View File

@@ -0,0 +1,35 @@
package com.ruoyi.playingmethod.service;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.domain.other.TtBox;
import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.TtBoxA;
import com.ruoyi.domain.other.TtOrnamentsA;
import com.ruoyi.domain.other.TtBoxVO;
import com.ruoyi.playingmethod.domain.vo.CompValueVO;
import java.util.List;
public interface ApiBindBoxService {
TtBoxA getBoxData(Integer boxId);
List<TtOrnamentsA> getCompBoxData(Integer boxId);
List<TtBoxVO> getBoxList(Integer boxTypeId, String homeFlag, String boxType);
List<TtBoxVO> groupByBoxType(List<TtBoxA> boxData);
List<TtOrnamentsA> openBox(TtBox ttBox, Integer num, TtUser ttUser);
List<TtBoxRecords> addBoxRecord(TtUser ttUser, TtBox ttBox, Integer num);
List<TtBoxRecords> openBoxArithmetic(Integer fightId,TtUser ttUser, TtBox ttBox, Integer num);
// List<TtBoxUser> getBindBoxHistory(ApiBindBoxController.boxHistoryParam param);
R blindBox(TtUser ttUser, TtBox ttBox, Integer num);
CompValueVO getCompValue(TtUser ttUser, TtBox ttBox);
}

View File

@@ -0,0 +1,8 @@
package com.ruoyi.playingmethod.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.domain.other.TtBoxOrnaments;
public interface ApiBoxOrnamentsService extends IService<TtBoxOrnaments> {
}

View File

@@ -0,0 +1,16 @@
package com.ruoyi.playingmethod.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.domain.dto.boxRecords.TenTopQuery;
import com.ruoyi.domain.dto.boxRecords.queryCondition;
import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO;
import java.util.List;
public interface ApiBoxRecordsService extends IService<TtBoxRecords> {
List<TtBoxRecordsVO> byCondition(queryCondition condition);
List<TtBoxRecordsVO> tenTopQuery(TenTopQuery query);
}

View File

@@ -0,0 +1,14 @@
package com.ruoyi.playingmethod.service;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.domain.vo.UserPackSackDataVO;
import java.util.List;
public interface ApiCompoundService {
R<UserPackSackDataVO> compound(List<Long> packSackIds, TtUser ttUser);
List<UserPackSackDataVO> getUserCompoundRecord(Integer userId);
}

View File

@@ -0,0 +1,38 @@
package com.ruoyi.playingmethod.service;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.domain.dto.exponent.ExponentJoinParam;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.TtBox;
public interface ApiExponentService {
/**
* 加入指数
* @param param
* @return
*/
R join(TtUser ttUser, ExponentJoinParam param);
/**
* 宝箱信息
*/
R box(TtUser ttUser, Integer id);
/**
* 最近十期指数
*/
R recenttenperiod();
/**
* 返回信息
* @param ttUser
* @return
*/
R info(TtUser ttUser);
R records(TtUser ttUser);
R mybox(TtUser ttUser);
R openbox(TtUser ttUser, TtBox ttBox, Integer num);
}

View File

@@ -0,0 +1,54 @@
package com.ruoyi.playingmethod.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.domain.dto.fight.FightDetailParam;
import com.ruoyi.domain.dto.fight.FightOnMyOwnParam;
import com.ruoyi.domain.other.CreateFightBody;
import com.ruoyi.domain.other.TtBoxVO;
import com.ruoyi.domain.entity.fight.TtFight;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.vo.ApiFightListDataVO;
import com.ruoyi.domain.vo.FightResultDataVO;
import com.ruoyi.domain.vo.fight.FightResultVO;
import com.ruoyi.domain.vo.fight.TtFightVO;
import com.ruoyi.playingmethod.model.vo.ApiFightRankingVO;
import java.util.Date;
import java.util.List;
import java.util.Map;
public interface ApiFightService extends IService<TtFight> {
R<Object> createFight(CreateFightBody createFightParamVO, TtUser ttUser);
R<Object> joinFight(Integer fightId, TtUser player);
List<TtBoxVO> getFightBoxList(Integer boxTypeId);
List<ApiFightListDataVO> getFightList(String model, String status, Integer userId, Integer fightId);
FightResultDataVO getFightRecord(Integer fightId, Integer round, Integer rounds);
R fightBegin(Integer fightId, TtUser player);
R audience(Integer fightId);
R fightEnd(Integer fightId);
R seatrReady(Integer fightId, TtUser player);
R fightRoomExit(Integer fightId, TtUser player);
List<ApiFightListDataVO> getFightList(FightOnMyOwnParam param);
R fightDetail(FightDetailParam param);
R earlierHistory(FightDetailParam param);
// AjaxResult joinFightRoom(Integer userId,Integer fightId);
List<ApiFightRankingVO> getFightRankingByDate(String date);
Map<String, List<FightResultVO>> tenTopFight(Integer maxType);
}

View File

@@ -0,0 +1,7 @@
package com.ruoyi.playingmethod.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.domain.other.TtFightUser;
public interface ApiFightUserService extends IService<TtFightUser> {
}

View File

@@ -0,0 +1,18 @@
package com.ruoyi.playingmethod.service;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.domain.other.ApiLuckyUpgradeBody;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.UpgradeBodyA;
import com.ruoyi.domain.vo.ApiLuckyOrnamentsDataVO;
import java.util.List;
public interface ApiLuckyUpgradeService {
List<ApiLuckyOrnamentsDataVO> getOrnamentsList(ApiLuckyUpgradeBody apiLuckyUpgradeBody);
R upgrade(TtUser ttUser, UpgradeBodyA upgradeBodyA);
// List<ApiLuckyUpgradeRecordDataVO> getUpgradeRecord(getUpgradeRecord param);
}

View File

@@ -0,0 +1,22 @@
package com.ruoyi.playingmethod.service;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.domain.other.TtRedPack;
import com.ruoyi.domain.other.TtRedPacketRecord;
import com.ruoyi.thirdparty.wechat.domain.PtLoginUser;
import java.util.List;
public interface ApiRedPackService {
List<TtRedPack> getRedPackList(PtLoginUser user, Integer type);
AjaxResult getRedPack(Long userId, Long redPackId);
List<TtRedPacketRecord> getActivityDataHis(PtLoginUser user);
AjaxResult getRedPackDataDetail(PtLoginUser user, Long recordId);
AjaxResult getRedPackByKey(LoginUser user, String redPackKey);
}

View File

@@ -0,0 +1,37 @@
package com.ruoyi.playingmethod.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.domain.dto.roll.GetRollOpenPrizeParam;
import com.ruoyi.domain.dto.roll.GetRollPlayersParam;
import com.ruoyi.domain.dto.roll.GetRollPrizePool;
import com.ruoyi.domain.entity.roll.TtRoll;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.vo.RollDetailsDataVO;
import com.ruoyi.domain.vo.RollListDataVO;
import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO;
import com.ruoyi.domain.vo.roll.RollJackpotOrnamentsVO;
import com.ruoyi.domain.vo.roll.RollUserVO;
import com.ruoyi.playingmethod.controller.ApiRollController.GetRollListParam;
import com.ruoyi.playingmethod.controller.ApiRollController.JoinRollParam;
import java.util.List;
public interface ApiRollService extends IService<TtRoll> {
R joinRoll(JoinRollParam param, TtUser player);
R endROLL(Integer rollId);
// List<RollListDataVO> getRollList(GetRollListParam param);
List<RollListDataVO> getRollList(GetRollListParam param);
R<RollDetailsDataVO> getRollDetails(Integer rollId);
R<List<RollUserVO>> getRollPlayers(GetRollPlayersParam param);
R<List<RollJackpotOrnamentsVO>> getRollPrizePool(GetRollPrizePool param);
R<List<TtBoxRecordsVO>> getRollOpenPrize(GetRollOpenPrizeParam param);
}

View File

@@ -0,0 +1,16 @@
package com.ruoyi.playingmethod.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.domain.dto.upgrade.UpgradeCondition;
import com.ruoyi.domain.other.*;
import java.util.List;
public interface ApiUpgradeRecordService extends IService<TtUpgradeRecord> {
R historyDetail(UpgradeCondition param);
R tenTopDetail(Boolean isVictory);
}

View File

@@ -0,0 +1,8 @@
package com.ruoyi.playingmethod.service;
import com.baomidou.mybatisplus.extension.service.IService;
import com.ruoyi.domain.entity.TtUserBlendErcash;
public interface ApiUserBlendErcashService extends IService<TtUserBlendErcash> {
}

View File

@@ -0,0 +1,30 @@
package com.ruoyi.playingmethod.service;
import com.ruoyi.domain.vo.OpenBoxVO;
import com.ruoyi.playingmethod.model.ApiWelfare;
import com.ruoyi.playingmethod.model.vo.ApiWelfareMonthlyRechargesVO;
import java.util.List;
public interface ApiWelfareMonthlyRechargesService {
/**
* 获取福利列表
*/
List<ApiWelfareMonthlyRechargesVO> getWelfareList(Long userId);
/**
* 领取福利
*/
OpenBoxVO claimWelfare(Integer welfareId, Long userId);
/**
* 检查是否符合领取条件
*/
boolean checkEligible(Integer welfareId, Long userId);
/**
* 检查是否已领取
*/
boolean checkClaimed(Integer welfareId, Long userId);
}

View File

@@ -0,0 +1,32 @@
package com.ruoyi.playingmethod.service;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.domain.vo.OpenBoxVO;
import com.ruoyi.playingmethod.model.ApiWelfare;
import com.ruoyi.playingmethod.model.ApiWelfareRecord;
import org.apache.ibatis.annotations.Param;
import java.util.List;
public interface ApiWelfareService {
/**
* 获取福利列表
*/
List<ApiWelfare> getWelfareList(Long userId);
/**
* 领取福利
*/
OpenBoxVO claimWelfare(Integer welfareId, Long userId);
/**
* 检查是否不符合领取条件
*/
boolean checkNotEligible(Integer welfareId, Long userId);
/**
* 检查是否已领取
*/
boolean checkClaimed(Integer welfareId, Long userId);
}

View File

@@ -0,0 +1,14 @@
package com.ruoyi.playingmethod.service;
import com.ruoyi.playingmethod.entity.TtAttendanceRecord;
import java.util.List;
public interface IApiAttendanceRecordService {
TtAttendanceRecord selectByUid(int uid);
int insert(int uid);
List<TtAttendanceRecord> selectSevenAttendance(int uid);
}

View File

@@ -0,0 +1,11 @@
package com.ruoyi.playingmethod.service;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import java.util.List;
public interface IApiReplacementRecordService {
AjaxResult synthesizeItems(LoginUser user, List<Long> itemIds);
}

View File

@@ -0,0 +1,109 @@
package com.ruoyi.playingmethod.service.impl;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import com.ruoyi.playingmethod.entity.TtAttendanceRecord;
import com.ruoyi.playingmethod.mapper.ApiAttendanceRecordMapper;
import com.ruoyi.playingmethod.service.IApiAttendanceRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.text.SimpleDateFormat;
import java.util.*;
@Service
@Slf4j
public class ApiAttendanceRecordServiceImpl implements IApiAttendanceRecordService{
private final ApiAttendanceRecordMapper apiTtAttendanceRecordMapper;
public ApiAttendanceRecordServiceImpl(ApiAttendanceRecordMapper apiTtAttendanceRecordMapper) {
this.apiTtAttendanceRecordMapper = apiTtAttendanceRecordMapper;
}
@Override
public TtAttendanceRecord selectByUid(int uid) {
return apiTtAttendanceRecordMapper.selectByUid(uid);
}
@Override
public int insert(int uid) {
TtAttendanceRecord ttAttendanceRecord = new TtAttendanceRecord();
ttAttendanceRecord.setUserId(uid);
ttAttendanceRecord.setCreateTime(new Date());
ttAttendanceRecord.setCoin(1);
return apiTtAttendanceRecordMapper.insertAttendanceRecord(ttAttendanceRecord);
}
@Override
public List<TtAttendanceRecord> selectSevenAttendance(int uid) {
List<TtAttendanceRecord> ttAttendanceRecords = new ArrayList<>();
//周一
DateTime dateTime = DateUtil.beginOfWeek(new Date());
System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(dateTime));
TtAttendanceRecord zhouYittAttendanceRecor = new TtAttendanceRecord();
if (apiTtAttendanceRecordMapper.selectSevenAttendance(uid,new SimpleDateFormat("yyyy-MM-dd").format(dateTime)) != null )zhouYittAttendanceRecor.setIsStatus("1");
zhouYittAttendanceRecor.setCreateTime(dateTime);
ttAttendanceRecords.add(zhouYittAttendanceRecor);
//周二
Calendar calendar = new GregorianCalendar();
calendar.setTime(dateTime);
calendar.add(Calendar.DATE, 1);
System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime()));
TtAttendanceRecord zhouErttAttendanceRecord = new TtAttendanceRecord();
if (apiTtAttendanceRecordMapper.selectSevenAttendance(uid,new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())) != null)zhouErttAttendanceRecord.setIsStatus("1");
zhouErttAttendanceRecord.setCreateTime(calendar.getTime());
ttAttendanceRecords.add(zhouErttAttendanceRecord);
//周三
calendar.add(Calendar.DATE, 1);
System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime()));
TtAttendanceRecord zhouSanttAttendanceRecord = new TtAttendanceRecord();
if (apiTtAttendanceRecordMapper.selectSevenAttendance(uid,new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())) != null )zhouSanttAttendanceRecord.setIsStatus("1");
zhouSanttAttendanceRecord.setCreateTime(calendar.getTime());
ttAttendanceRecords.add(zhouSanttAttendanceRecord);
//周四
calendar.add(Calendar.DATE, 1);
System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime()));
TtAttendanceRecord zhouSittAttendanceRecord = new TtAttendanceRecord();
if (apiTtAttendanceRecordMapper.selectSevenAttendance(uid,new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())) != null )zhouSittAttendanceRecord.setIsStatus("1");
zhouSittAttendanceRecord.setCreateTime(calendar.getTime());
ttAttendanceRecords.add(zhouSittAttendanceRecord);
//周五
calendar.add(Calendar.DATE, 1);
System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime()));
TtAttendanceRecord zhouWuttAttendanceRecord = new TtAttendanceRecord();
if (apiTtAttendanceRecordMapper.selectSevenAttendance(uid,new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())) != null )zhouWuttAttendanceRecord.setIsStatus("1");
zhouWuttAttendanceRecord.setCreateTime(calendar.getTime());
ttAttendanceRecords.add(zhouWuttAttendanceRecord);
//周六
calendar.add(Calendar.DATE, 1);
System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime()));
TtAttendanceRecord zhouLiuttAttendanceRecord = new TtAttendanceRecord();
if (apiTtAttendanceRecordMapper.selectSevenAttendance(uid,new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())) != null )zhouLiuttAttendanceRecord.setIsStatus("1");
zhouLiuttAttendanceRecord.setCreateTime(calendar.getTime());
ttAttendanceRecords.add(zhouLiuttAttendanceRecord);
//周天
calendar.add(Calendar.DATE, 1);
System.out.println(new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime()));
TtAttendanceRecord zhouRittAttendanceRecord = new TtAttendanceRecord();
if (apiTtAttendanceRecordMapper.selectSevenAttendance(uid,new SimpleDateFormat("yyyy-MM-dd").format(calendar.getTime())) != null )zhouRittAttendanceRecord.setIsStatus("1");
zhouRittAttendanceRecord.setCreateTime(calendar.getTime());
ttAttendanceRecords.add(zhouRittAttendanceRecord);
return ttAttendanceRecords;
}
}

View File

@@ -0,0 +1,8 @@
package com.ruoyi.playingmethod.service.impl;
import com.ruoyi.playingmethod.service.ApiBattleRoyaleService;
import org.springframework.stereotype.Service;
@Service
public class ApiBattleRoyaleServiceImpl implements ApiBattleRoyaleService {
}

View File

@@ -0,0 +1,481 @@
package com.ruoyi.playingmethod.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.ruoyi.admin.mapper.TtBoxOrnamentsMapper;
import com.ruoyi.admin.mapper.TtBoxRecordsMapper;
import com.ruoyi.admin.mapper.TtBoxTypeMapper;
import com.ruoyi.admin.mapper.TtCompRecordMapper;
import com.ruoyi.admin.model.UpdateUserAccountBo;
import com.ruoyi.admin.service.TtBoxRecordsService;
import com.ruoyi.admin.service.TtBoxService;
import com.ruoyi.admin.service.TtOrnamentsLevelService;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.admin.util.core.fight.LotteryMachine;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.domain.common.constant.TtAccountRecordSource;
import com.ruoyi.domain.common.constant.TtAccountRecordType;
import com.ruoyi.domain.common.constant.TtboxRecordSource;
import com.ruoyi.domain.dto.boxRecords.TtBoxRecordsNum;
import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.entity.TtUserBlendErcash;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.*;
import com.ruoyi.domain.vo.OpenBoxVO;
import com.ruoyi.playingmethod.domain.vo.CompValueVO;
import com.ruoyi.playingmethod.mapper.ApiBindBoxMapper;
import com.ruoyi.playingmethod.mapper.ApiTtUserBlendErcashMapper;
import com.ruoyi.playingmethod.service.ApiBindBoxService;
import com.ruoyi.playingmethod.utils.customException.OrnamentNullException;
import com.ruoyi.playingmethod.websocket.WsBindBox;
import com.ruoyi.playingmethod.websocket.constant.SMsgKey;
import com.ruoyi.playingmethod.websocket.util.WsResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.ObjectUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.stream.Collectors;
import static com.ruoyi.domain.common.constant.TtboxRecordStatus.IN_PACKSACK_ON;
import static java.math.BigDecimal.ROUND_HALF_UP;
@Service
@Slf4j
public class ApiBindBoxServiceImpl implements ApiBindBoxService {
private final TtUserService userService;
private final TtBoxService boxService;
private final TtBoxTypeMapper boxTypeMapper;
private final ApiBindBoxMapper bindBoxMapper;
private final TtBoxOrnamentsMapper boxOrnamentsMapper;
private final TtBoxRecordsService boxRecordsService;
private final RedisCache redisCache;
private final Executor customThreadPoolExecutor;
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private LotteryMachine lotteryMachine;
@Autowired
private TtCompRecordMapper ttCompRecordMapper;
@Autowired
private ApiTtUserBlendErcashMapper apiTtUserBlendErcashMapper;
@Autowired
private TtBoxRecordsMapper ttBoxRecordsMapper;
@Autowired
private TtOrnamentsLevelService ornamentsLevelService;
public ApiBindBoxServiceImpl(TtUserService userService,
TtBoxService boxService,
TtBoxTypeMapper boxTypeMapper,
ApiBindBoxMapper bindBoxMapper,
TtBoxOrnamentsMapper boxOrnamentsMapper,
TtBoxRecordsService boxRecordsService,
RedisCache redisCache,
Executor customThreadPoolExecutor) {
this.userService = userService;
this.boxService = boxService;
this.boxTypeMapper = boxTypeMapper;
this.bindBoxMapper = bindBoxMapper;
this.boxOrnamentsMapper = boxOrnamentsMapper;
this.boxRecordsService = boxRecordsService;
this.redisCache = redisCache;
this.customThreadPoolExecutor = customThreadPoolExecutor;
}
//@PostConstruct
public void postConstruct() {
List<Integer> boxIds = boxOrnamentsMapper.selectBoxIdList();
if (ObjectUtils.isEmpty(boxIds)) return;
for (Integer boxId : boxIds) {
boxService.isReplenishment(boxId);
}
}
@Override
public TtBoxA getBoxData(Integer boxId) {
TtBoxA boxData = bindBoxMapper.getBoxData(boxId);
if (ObjectUtils.isEmpty(boxData)) return null;
List<TtOrnamentsA> boxOrnamentsList = bindBoxMapper.getBoxOrnamentsList(boxId);
if (ObjectUtils.isEmpty(boxOrnamentsList)) return null;
// 计算概率
BigDecimal sum = new BigDecimal(boxOrnamentsList.stream().mapToInt(TtOrnamentsA::getOdds).sum());
BigDecimal oneHundred = new BigDecimal("100").setScale(2);
BigDecimal max = BigDecimal.ZERO;
Integer maxId = null;
BigDecimal add = BigDecimal.ZERO;
List<Long> ornamentIds = new ArrayList<>();
for (TtOrnamentsA vo : boxOrnamentsList) {
Integer odds = vo.getOdds();
BigDecimal oddsResult = new BigDecimal(odds).divide(sum, 4, ROUND_HALF_UP).multiply(oneHundred).setScale(2);
add = oddsResult.add(add);
if (max.compareTo(oddsResult) <= 0) {
max = oddsResult;
maxId = vo.getOrnamentId();
}
vo.setOddsResult(oddsResult);
ornamentIds.add(Long.valueOf(vo.getOrnamentId()));
}
// 平差
BigDecimal erroValue = oneHundred.subtract(add);
for (TtOrnamentsA vo : boxOrnamentsList) {
if (vo.getOrnamentId().equals(maxId)) {
vo.setOddsResult(vo.getOddsResult().add(erroValue));
break;
}
}
if (ObjectUtils.isEmpty(boxOrnamentsList)) return null;
boxOrnamentsList = boxOrnamentsList.stream().peek(boxOrnamentsDataVO -> boxOrnamentsDataVO.setOdds(null)).collect(Collectors.toList());
List<TtBoxLevelA> probabilityDistribution = bindBoxMapper.getProbabilityDistribution(boxId);
boxOrnamentsList.sort((o1, o2) -> o2.getUsePrice().compareTo(o1.getUsePrice()));
boxData.setBoxOrnamentsList(boxOrnamentsList);
boxData.setProbabilityDistribution(probabilityDistribution);
// 计算真实开奖
List<TtBoxRecordsNum> boxRecordsNums = ttBoxRecordsMapper.selectBoxRecordsByOrnamentIds(boxId, ornamentIds, null);
Map<Integer, Integer> ornamentIdNum = new HashMap<>();
sum = new BigDecimal(0);
add = BigDecimal.ZERO;
max = BigDecimal.ZERO;
maxId = null;
oneHundred = new BigDecimal("100").setScale(2);
if (CollectionUtils.isNotEmpty(boxRecordsNums)) {
for (TtBoxRecordsNum num : boxRecordsNums) {
ornamentIdNum.put(num.getOrnamentId().intValue(), num.getNum());
sum = sum.add(new BigDecimal(num.getNum()));
}
}
boxData.setTotalOpenOdds(sum.intValue());
// 某个颜色等级真是开奖的数量
Map<Integer, Integer> levelNum = new HashMap<>();
for (TtOrnamentsA vo : boxOrnamentsList) {
Integer openOdds = ornamentIdNum.getOrDefault(vo.getOrnamentId(), 0);
vo.setOpenOdds(openOdds);
levelNum.put(vo.getOrnamentsLevelId(), levelNum.getOrDefault(vo.getOrnamentsLevelId(), 0) + openOdds);
BigDecimal openOddsResult = new BigDecimal(0);
if (sum.compareTo(new BigDecimal(0)) > 0) {
openOddsResult = new BigDecimal(openOdds).divide(sum, 4, RoundingMode.HALF_UP).multiply(oneHundred).setScale(2);
}
add = openOddsResult.add(add);
if (max.compareTo(openOddsResult) <= 0) {
max = openOddsResult;
maxId = vo.getOrnamentId();
}
vo.setOpenOddsResult(openOddsResult);
}
// 平差
erroValue = oneHundred.subtract(add);
for (TtOrnamentsA vo : boxOrnamentsList) {
if (vo.getOrnamentId().equals(maxId)) {
vo.setOpenOddsResult(vo.getOpenOddsResult().add(erroValue));
break;
}
}
List<TtBoxLevelA> openProbabilityDistribution = new ArrayList<>();
List<TtOrnamentsLevel> levels = ornamentsLevelService.list();
if (CollectionUtils.isNotEmpty(levels)) {
for (TtOrnamentsLevel level : levels) {
TtBoxLevelA a = new TtBoxLevelA();
a.setLevel(level.getLevel());
a.setOrnamentsLevelId(level.getId());
BigDecimal probability = new BigDecimal(0);
if (sum.compareTo(new BigDecimal(0)) > 0) {
probability = new BigDecimal(levelNum.getOrDefault(level.getId(), 0)).divide(sum, 4, RoundingMode.HALF_UP).multiply(oneHundred).setScale(2);
}
a.setProbability(probability.toString());
openProbabilityDistribution.add(a);
}
}
boxData.setOpenProbabilityDistribution(openProbabilityDistribution);
return boxData;
}
@Override
public List<TtOrnamentsA> getCompBoxData(Integer boxId) {
List<TtOrnamentsA> boxOrnamentsList = bindBoxMapper.getBoxOrnamentsList(boxId);
if (ObjectUtils.isEmpty(boxOrnamentsList))
return null;
// 剔除非补偿物品
boxOrnamentsList.removeIf(ttOrnamentsA -> ttOrnamentsA.getCompOdds() == 0);
// 计算概率
BigDecimal sum = new BigDecimal(boxOrnamentsList.stream().mapToInt(TtOrnamentsA::getCompOdds).sum());
BigDecimal oneHundred = new BigDecimal("100").setScale(2);
BigDecimal max = BigDecimal.ZERO;
Integer maxId = null;
BigDecimal add = BigDecimal.ZERO;
for (TtOrnamentsA ttOrnamentsA : boxOrnamentsList) {
Integer compOdds = ttOrnamentsA.getCompOdds();
BigDecimal compOddsResult = new BigDecimal(compOdds).divide(sum, 4, ROUND_HALF_UP).multiply(oneHundred).setScale(2);
add = compOddsResult.add(add);
if (max.compareTo(compOddsResult) <= 0) {
max = compOddsResult;
maxId = ttOrnamentsA.getOrnamentId();
}
ttOrnamentsA.setCompOddsResult(compOddsResult);
}
// 平差
BigDecimal errorValue = oneHundred.subtract(add);
for (TtOrnamentsA vo : boxOrnamentsList) {
if (vo.getOrnamentId().equals(maxId)) {
vo.setOddsResult(vo.getCompOddsResult().add(errorValue));
break;
}
}
return boxOrnamentsList;
}
@Override
public List<TtBoxVO> getBoxList(Integer boxTypeId, String homeFlag, String boxType) {
List<TtBoxA> boxList = bindBoxMapper.getBoxList(boxTypeId, homeFlag, boxType);
// 过滤空箱子
List<TtBoxA> collect = boxList.stream().filter(item -> {
List<TtBoxOrnaments> list = new LambdaQueryChainWrapper<>(boxOrnamentsMapper)
.eq(TtBoxOrnaments::getBoxId, item.getBoxId())
.list();
if (ObjectUtil.isEmpty(list) || list.isEmpty()) return false;
boolean flag = false;
for (TtBoxOrnaments ornaments : list) {
if (ornaments.getRealOdds() > 0) {
return !flag;
}
}
return flag;
}).collect(Collectors.toList());
return groupByBoxType(collect);
}
@Override
public List<TtBoxVO> groupByBoxType(List<TtBoxA> boxData) {
Map<Integer, List<TtBoxA>> collect = boxData.stream().collect(Collectors.groupingBy(TtBoxA::getBoxTypeId));
List<TtBoxVO> resultList = new ArrayList<>();
for (Map.Entry<Integer, List<TtBoxA>> entry : collect.entrySet()) {
Integer key = entry.getKey();
List<TtBoxA> value = entry.getValue();
TtBoxType ttBoxType = boxTypeMapper.selectById(key);
if (StringUtils.isNull(ttBoxType)) continue;
TtBoxVO boxTypeA = TtBoxVO.builder().build();
boxTypeA.setBoxTypeId(ttBoxType.getId());
boxTypeA.setBoxTypeName(ttBoxType.getName());
boxTypeA.setIcon(ttBoxType.getIcon());
boxTypeA.setBoxList(value);
resultList.add(boxTypeA);
}
return resultList;
}
@Override
public List<TtOrnamentsA> openBox(TtBox ttBox, Integer num, TtUser ttUser) {
return null;
}
@Override
public List<TtBoxRecords> addBoxRecord(TtUser ttUser, TtBox ttBox, Integer num) {
return null;
}
@Override
public List<TtBoxRecords> openBoxArithmetic(Integer fightId, TtUser ttUser, TtBox ttBox, Integer num) {
return null;
}
@Override
@Transactional
public R blindBox(TtUser user, TtBox box, Integer num) {
// 同步扣款
BigDecimal totalmoney = box.getPrice().multiply(new BigDecimal(num));
R r = upgradeAccounting(totalmoney, user);
if (R.isError(r)) {
return R.fail(r.getMsg());
}
// 抽奖
List<TtBoxRecords> boxRecords = new ArrayList<>();
Map<Long, TtOrnamentsA> boxToOrnament = new HashMap<>();
for (int i = 0; i < num; i++) {
String ornamentId = lotteryMachine.singleLottery(user, box);
if (StringUtils.isBlank(ornamentId)) {
throw new OrnamentNullException("单次抽奖无结果#"+box.getBoxId()+"#"+ornamentId);
//return R.fail("系统繁忙,请稍后重试。");
}
// 宝箱详细信息
TtOrnamentsA ornamentsData = bindBoxMapper.ornamentsInfo(box.getBoxId(), ornamentId);
if (ObjectUtil.isEmpty(ornamentsData)){
throw new OrnamentNullException("没有符合条件的宝箱信息#"+box.getBoxId()+"#"+ornamentId);
}
// 比对补偿箱类型开箱价格和奖品价值,记录补偿值。如果饰品大于等于门票,清空补偿值
if ("3".equals(box.getBoxType()) && ornamentsData.getUsePrice().compareTo(box.getPrice()) < 0) {
compRecord(user.getUserId(), box.getBoxId());
} else {
ttCompRecordMapper.deleteTtCompRecord(user.getUserId(), box.getBoxId());
}
// 抽奖成功,构建开箱记录数据
TtBoxRecords boxRecord = TtBoxRecords.builder()
.userId(user.getUserId())
.boxId(box.getBoxId())
.boxName(box.getBoxName())
.boxPrice(box.getPrice())
.ornamentId(Long.valueOf(ornamentId))
// .marketHashName(ornamentsData.)
.ornamentName(ObjectUtil.isNotEmpty(ornamentsData.getName()) ? ornamentsData.getName() : ObjectUtil.isNotEmpty(ornamentsData.getShortName()) ? ornamentsData.getShortName() : "无名称")
.imageUrl(ornamentsData.getImageUrl())
.ornamentsPrice(ornamentsData.getUsePrice())
.ornamentsLevelId(ornamentsData.getOrnamentsLevelId())
.ornamentLevelImg(ornamentsData.getLevelImg())
.holderUserId(user.getUserId())
.source(TtboxRecordSource.BLIND_BOX.getCode())
.status(IN_PACKSACK_ON.getCode())
.createTime(new Timestamp(System.currentTimeMillis()))
.updateTime(new Timestamp(System.currentTimeMillis()))
.build();
boxRecords.add(boxRecord);
boxToOrnament.put(Long.valueOf(ornamentId), ornamentsData);
}
// 同步保存游戏记录
boxRecordsService.saveBatch(boxRecords);
// 返回值
List<OpenBoxVO> openBoxVOs = boxRecords.stream().map(item -> {
OpenBoxVO openBoxVO = new OpenBoxVO();
BeanUtil.copyProperties(item, openBoxVO);
TtOrnamentsA ornamentsData = boxToOrnament.get(item.getOrnamentId());
if (ornamentsData != null) {
openBoxVO.setShortName(ornamentsData.getShortName());
openBoxVO.setExteriorName(ornamentsData.getExteriorName());
}
openBoxVO.setUsePrice(openBoxVO.getOrnamentsPrice());
openBoxVO.setLevelImg(item.getOrnamentLevelImg());
return openBoxVO;
}).collect(Collectors.toList());
// 异步任务 广播开箱消息
broadcastToBoxRoom(boxRecords);
return R.ok(openBoxVOs);
}
@Override
public CompValueVO getCompValue(TtUser ttUser, TtBox ttBox) {
CompValueVO compValueVO = new CompValueVO();
// 查询宝箱设定的补偿值
compValueVO.setDesiredCompValue(ttBox.getCompAmount());
// 查询用户当前补偿值
TtCompRecord ttCompRecord = ttCompRecordMapper.selectTtCompRecord(ttUser.getUserId(), ttBox.getBoxId());
if (Objects.isNull(ttCompRecord))
compValueVO.setCurrentCompValue(0);
else
compValueVO.setCurrentCompValue(ttCompRecord.getCompAmount());
return compValueVO;
}
// 扣款
public R upgradeAccounting(BigDecimal consumption, TtUser player) {
// 再次检查余额
player = userService.getById(player.getUserId());
if (player.getAccountAmount() == null || player.getAccountAmount().compareTo(consumption) < 0) {
return R.fail("余额不足");
}
return userService.updateUserAccount(player.getUserId(), consumption.negate(), TtAccountRecordSource.GAME_TYPE_01);
}
// 异步任务 广播开箱消息
public void broadcastToBoxRoom(List<TtBoxRecords> boxRecords) {
CompletableFuture.runAsync(() -> {
// 数据按boxId排序
Collections.sort(boxRecords, new Comparator<TtBoxRecords>() {
@Override
public int compare(TtBoxRecords o1, TtBoxRecords o2) {
return o1.getBoxId().compareTo(o2.getBoxId());
}
});
// 缓冲区
List<TtBoxRecords> cache = new ArrayList<>();
Integer flagBoxId = -1;
for (TtBoxRecords record : boxRecords) {
if (cache.isEmpty()) {
// 缓冲区为空,直接加入
cache.add(record);
flagBoxId = record.getBoxId();
continue;
} else {
// 不为空
if (record.getBoxId().equals(flagBoxId)) {
cache.add(record);
} else {
Integer bid = cache.get(0).getBoxId();
// 广播最新数据
WsBindBox.broadcastToBoxRoom(bid, WsResult.ok(SMsgKey.Blind_Box_Current_Data.name(), cache, "开盒子更新。"));
cache.clear();
}
}
}
Integer bid = cache.get(0).getBoxId();
// 广播最新数据
WsBindBox.broadcastToBoxRoom(bid, WsResult.ok(SMsgKey.Blind_Box_Current_Data.name(), cache, "开盒子更新。"));
}, customThreadPoolExecutor);
}
// 记录补偿值
public void compRecord(Integer userId, Integer boxId) {
// 查询当前补偿值
TtCompRecord ttCompRecord = ttCompRecordMapper.selectTtCompRecord(userId, boxId);
if (!Objects.isNull(ttCompRecord)) {
// 补偿值+1
ttCompRecord.setCompAmount(ttCompRecord.getCompAmount() + 1);
ttCompRecordMapper.updateTtCompRecord(ttCompRecord);
}
}
}

View File

@@ -0,0 +1,11 @@
package com.ruoyi.playingmethod.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.domain.other.TtBoxOrnaments;
import com.ruoyi.playingmethod.mapper.ApiBoxOrnamentsMapper;
import com.ruoyi.playingmethod.service.ApiBoxOrnamentsService;
import org.springframework.stereotype.Service;
@Service
public class ApiBoxOrnamentsServiceImpl extends ServiceImpl<ApiBoxOrnamentsMapper, TtBoxOrnaments> implements ApiBoxOrnamentsService {
}

View File

@@ -0,0 +1,75 @@
package com.ruoyi.playingmethod.service.impl;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.admin.mapper.*;
import com.ruoyi.domain.dto.boxRecords.TenTopQuery;
import com.ruoyi.domain.dto.boxRecords.queryCondition;
import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO;
import com.ruoyi.playingmethod.service.ApiBoxRecordsService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import java.util.*;
@Service
@Slf4j
public class ApiBoxRecordsServiceImpl extends ServiceImpl<TtBoxRecordsMapper, TtBoxRecords> implements ApiBoxRecordsService {
@Override
public List<TtBoxRecordsVO> byCondition(queryCondition condition) {
condition.setLimit((condition.getPage() - 1) * condition.getSize());
if (ObjectUtil.isNull(condition.getOrderByFie())) condition.setOrderByFie(1);
List<TtBoxRecordsVO> list = this.baseMapper.byCondition(
condition.getUserId(),
condition.getLimit(),
condition.getSize(),
condition.getOrderByFie(),
condition.getBoxRecordId(),
condition.getBoxId(),
condition.getUserType(),
condition.getSource(),
condition.getStatus(),
condition.getOrnamentPriceMin(),
condition.getOrnamentPriceMax(),
condition.getOrnamentLevelIds()
);
// 排序
// Comparator<TtBoxRecordsVO> comparator = null;
// if (ObjectUtil.isNull(condition.getOrderByFie()) || condition.getOrderByFie().equals(1)) {
// comparator = new Comparator<TtBoxRecordsVO>() {
// @Override
// public int compare(TtBoxRecordsVO o1, TtBoxRecordsVO o2) {
// return o1.getCreateTime().compareTo(o2.getCreateTime());
// }
// };
// } else if (condition.getOrderByFie().equals(0)) {
// comparator = new Comparator<TtBoxRecordsVO>() {
// @Override
// public int compare(TtBoxRecordsVO o1, TtBoxRecordsVO o2) {
// return o2.getCreateTime().compareTo(o1.getCreateTime());
// }
// };
// }
//
// Collections.sort(list, comparator);
return list;
}
@Override
public List<TtBoxRecordsVO> tenTopQuery(TenTopQuery query) {
query.setLimit((query.getPage() - 1) * query.getSize());
return this.baseMapper.tenTopQuery(
query.getLimit(),
query.getSize(),
query.getSource(),
query.getStatus()
);
}
}

View File

@@ -0,0 +1,121 @@
package com.ruoyi.playingmethod.service.impl;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.ruoyi.admin.mapper.TtOrnamentMapper;
import com.ruoyi.domain.common.constant.TtboxRecordStatus;
import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.entity.TtOrnament;
import com.ruoyi.domain.other.TtOrnamentsLevel;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.admin.service.TtBoxRecordsService;
import com.ruoyi.admin.service.TtOrnamentsLevelService;
import com.ruoyi.admin.service.WebsitePropertyService;
import com.ruoyi.admin.util.RandomUtils;
import com.ruoyi.domain.vo.WebsitePropertyDataVO;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.playingmethod.mapper.ApiCompoundMapper;
import com.ruoyi.playingmethod.service.ApiCompoundService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.user.service.ApiUserPackSackService;
import com.ruoyi.domain.vo.UserPackSackDataVO;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.ObjectUtils;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Comparator;
import java.util.Date;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;
import static com.ruoyi.domain.common.constant.TtboxRecordSource.MALL_EXCHANGE;
@Service
@Slf4j
public class ApiCompoundServiceImpl implements ApiCompoundService {
private final ISysConfigService configService;
private final ApiUserPackSackService userPackSackService;
private final WebsitePropertyService websitePropertyService;
private final TtBoxRecordsService boxRecordsService;
private final TtOrnamentMapper ornamentsMapper;
private final TtOrnamentsLevelService ornamentsLevelService;
private final ApiCompoundMapper apiCompoundMapper;
public ApiCompoundServiceImpl(ISysConfigService configService,
ApiUserPackSackService userPackSackService,
WebsitePropertyService websitePropertyService,
TtBoxRecordsService boxRecordsService,
TtOrnamentMapper ornamentsMapper,
TtOrnamentsLevelService ornamentsLevelService,
ApiCompoundMapper apiCompoundMapper) {
this.configService = configService;
this.userPackSackService = userPackSackService;
this.websitePropertyService = websitePropertyService;
this.boxRecordsService = boxRecordsService;
this.ornamentsMapper = ornamentsMapper;
this.ornamentsLevelService = ornamentsLevelService;
this.apiCompoundMapper = apiCompoundMapper;
}
@Override
public R<UserPackSackDataVO> compound(List<Long> packSackIds, TtUser ttUser) {
List<TtBoxRecords> boxRecordsList = userPackSackService.packSackHandle(packSackIds, ttUser, 3);
if (ObjectUtils.isEmpty(boxRecordsList)) return R.fail("请选择饰品后再进行合成!");
List<Long> ornamentsIds = boxRecordsList.stream().map(TtBoxRecords::getOrnamentId).collect(Collectors.toList());
List<Long> websitePropertyIds = websitePropertyService.list().stream().map(WebsitePropertyDataVO::getId).collect(Collectors.toList());
websitePropertyIds.retainAll(ornamentsIds);
if (!websitePropertyIds.isEmpty()) return R.fail("选择的饰品中存在网站专属道具,请重新选择!");
String maxCompoundNumStr = configService.selectConfigByKey("maxCompoundNum");
int maxCompoundNum = Integer.parseInt(maxCompoundNumStr);
if (boxRecordsList.size() > maxCompoundNum) return R.fail("最多选择" + maxCompoundNum + "个饰品!");
BigDecimal compoundBeanTotal = boxRecordsList.stream().map(TtBoxRecords::getOrnamentsPrice).reduce(BigDecimal.ZERO, BigDecimal::add);
String compoundMinPriceStr = configService.selectConfigByKey("compoundMinPrice"); // 获取配置中的汰换最低价
BigDecimal compoundMinPrice = new BigDecimal(compoundMinPriceStr);
if (compoundBeanTotal.compareTo(compoundMinPrice) < 0)
return R.fail("选择的饰品总价必须大于" + compoundMinPrice + "游戏币才可以汰换哦!");
Optional<BigDecimal> maxPriceOptional = boxRecordsList.stream().map(TtBoxRecords::getOrnamentsPrice).max(Comparator.naturalOrder());
if (!maxPriceOptional.isPresent()) return null;
try {
String compoundMinPremiumRateStr = configService.selectConfigByKey("compoundMinPremiumRate");
BigDecimal minPremiumRatePrice = new BigDecimal(compoundMinPremiumRateStr).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
.multiply(maxPriceOptional.get());
String compoundMaxPremiumRateStr = configService.selectConfigByKey("compoundMaxPremiumRate");
BigDecimal maxPremiumRatePrice = new BigDecimal(compoundMaxPremiumRateStr).divide(new BigDecimal(100), 2, RoundingMode.HALF_UP)
.multiply(compoundBeanTotal);
BigDecimal minPrice = maxPriceOptional.get().subtract(minPremiumRatePrice);
BigDecimal maxPrice = compoundBeanTotal.subtract(maxPremiumRatePrice);
List<TtOrnament> ornamentsList = new LambdaQueryChainWrapper<>(ornamentsMapper).in(TtOrnament::getType, 2, 3, 4, 5)
.between(TtOrnament::getUsePrice, minPrice, maxPrice).list();
if (StringUtils.isNull(ornamentsList) || ornamentsList.isEmpty())
return R.fail("汰换失败,饰品池中未筛选出符合您价格区间的饰品!");
int randomIndex = RandomUtils.getRandomIndex(ornamentsList.size());
TtOrnament ttOrnament = ornamentsList.get(randomIndex);
List<Integer> levelIds = ornamentsLevelService.list().stream().map(TtOrnamentsLevel::getId).collect(Collectors.toList());
Integer ornamentsLevelId = levelIds.get(RandomUtils.getRandomIndex(levelIds.size()));
boxRecordsService.updateBatchById(boxRecordsList, 1);
TtBoxRecords boxRecords = TtBoxRecords.builder().build();
boxRecords.setUserId(ttUser.getUserId());
boxRecords.setOrnamentId(ttOrnament.getId());
boxRecords.setOrnamentsPrice(ttOrnament.getUsePrice());
boxRecords.setOrnamentsLevelId(ornamentsLevelId);
boxRecords.setStatus(TtboxRecordStatus.IN_PACKSACK_ON.getCode());
boxRecords.setCreateTime(new Date());
boxRecords.setSource(MALL_EXCHANGE.getCode()); //// TODO: 2024/4/1 类型不对
boxRecords.setHolderUserId(ttUser.getUserId());
boxRecordsService.save(boxRecords);
UserPackSackDataVO compoundData = apiCompoundMapper.selectCompoundDataById(boxRecords.getId());
return R.ok(compoundData);
} catch (Exception e) {
return R.fail("数据异常!");
}
}
@Override
public List<UserPackSackDataVO> getUserCompoundRecord(Integer userId) {
return apiCompoundMapper.selectCompoundRecordByUserId(userId);
}
}

View File

@@ -0,0 +1,555 @@
package com.ruoyi.playingmethod.service.impl;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.ruoyi.admin.mapper.TtBoxMapper;
import com.ruoyi.admin.mapper.TtExponentMapper;
import com.ruoyi.admin.mapper.TtExponentUserBoxMapper;
import com.ruoyi.admin.mapper.TtExponentUserMapper;
import com.ruoyi.admin.service.TtBoxRecordsService;
import com.ruoyi.admin.service.TtBoxService;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.admin.util.core.fight.LotteryMachine;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.domain.common.constant.TtAccountRecordSource;
import com.ruoyi.domain.common.constant.TtAccountRecordType;
import com.ruoyi.domain.common.constant.TtboxRecordSource;
import com.ruoyi.domain.dto.exponent.*;
import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.entity.TtUserBlendErcash;
import com.ruoyi.domain.entity.exponent.TtExponent;
import com.ruoyi.domain.entity.exponent.TtExponentUser;
import com.ruoyi.domain.entity.exponent.TtExponentUserBox;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.TtBox;
import com.ruoyi.domain.other.TtBoxA;
import com.ruoyi.domain.other.TtOrnamentsA;
import com.ruoyi.domain.vo.OpenBoxVO;
import com.ruoyi.playingmethod.mapper.ApiBindBoxMapper;
import com.ruoyi.playingmethod.mapper.ApiTtUserBlendErcashMapper;
import com.ruoyi.playingmethod.service.ApiExponentService;
import com.ruoyi.playingmethod.utils.customException.OrnamentNullException;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import static com.ruoyi.domain.common.constant.TtboxRecordStatus.IN_PACKSACK_ON;
@Service
@Slf4j
public class ApiExponentServiceImpl implements ApiExponentService {
@Autowired
private TtExponentMapper ttExponentMapper;
@Autowired
private TtExponentUserMapper ttExponentUserMapper;
@Autowired
private TtBoxService boxService;
@Autowired
private TtUserService userService;
@Autowired
private LotteryMachine lotteryMachine;
@Autowired
private ApiBindBoxMapper bindBoxMapper;
@Autowired
private TtBoxRecordsService boxRecordsService;
@Autowired
private ApiTtUserBlendErcashMapper apiTtUserBlendErcashMapper;
@Autowired
private TtBoxMapper ttBoxMapper;
@Autowired
private TtExponentUserBoxMapper ttExponentUserBoxMapper;
@Override
public R join(TtUser ttUser, ExponentJoinParam param) {
if (param == null || param.getId() == null || param.getBoxId() == null || param.getForward() == null) {
return R.fail("参数不合法");
}
TtBox ttBox = boxService.getById(param.getBoxId());
if (ttBox == null) {
return R.fail("宝箱参数不合法");
}
// 同步扣款
R<Map<String, BigDecimal>> mapR = upgradeAccounting(ttBox.getPrice(), ttUser);
if (!mapR.getCode().equals(200)) {
return mapR;
}
TtExponentUser exponentUser = new TtExponentUser();
exponentUser.setExponentId(param.getId());
exponentUser.setBoxId(param.getBoxId());
exponentUser.setUserId(ttUser.getUserId());
exponentUser.setPrice(ttBox.getPrice());
exponentUser.setForward(param.getForward());
exponentUser.setStatus(0);
exponentUser.setBoxRecordId(0L);
exponentUser.setCreateTime(new Date());
exponentUser.setUpdateTime(new Date());
ttExponentUserMapper.insert(exponentUser);
return R.ok();
}
// 扣款
public R<Map<String, BigDecimal>> upgradeAccounting(BigDecimal consumption, TtUser player) {
// 再次检查余额
player = userService.getById(player.getUserId());
if (player.getAccountAmount() == null || player.getAccountAmount().compareTo(consumption) < 0) {
return R.fail("余额不足");
}
LambdaUpdateWrapper<TtUser> userUpdate = new LambdaUpdateWrapper<>();
userUpdate.eq(TtUser::getUserId, player.getUserId());
Map<String, BigDecimal> map;
if (player.getAccountAmount().compareTo(consumption) >= 0) {
userUpdate.set(TtUser::getAccountAmount, player.getAccountAmount().subtract(consumption));
map = MapUtil.builder("Amount", consumption).map();
} else {
BigDecimal subtract = consumption.subtract(player.getAccountAmount());
userUpdate
.set(TtUser::getAccountAmount, 0)
.set(TtUser::getAccountCredits, player.getAccountCredits().subtract(subtract));
map = MapUtil.builder("Amount", player.getAccountAmount()).map();
map.put("Credits", subtract);
}
userService.update(userUpdate);
// 综合消费日志
TtUserBlendErcash blendErcash = TtUserBlendErcash.builder()
.userId(player.getUserId())
.amount(null)
.finalAmount(null)
.credits(null)
.finalCredits(null)
.total(consumption.negate()) // 收支合计
.type(TtAccountRecordType.OUTPUT.getCode())
.source(TtAccountRecordSource.EXPONENT_GAME_TYPE_01.getCode())
.remark(TtAccountRecordSource.EXPONENT_GAME_TYPE_01.getMsg())
.createTime(new Timestamp(System.currentTimeMillis()))
.updateTime(new Timestamp(System.currentTimeMillis()))
.build();
apiTtUserBlendErcashMapper.insert(blendErcash);
return R.ok(map);
}
@Override
public R box(TtUser ttUser, Integer id) {
List<TtBoxA> boxList = bindBoxMapper.getBoxList(null, null, "5");
if (boxList.isEmpty()) {
return R.ok(new ArrayList<>(), "没有匹配的数据。");
}
List<ExponentBoxVo> boxVos = new ArrayList<>();
List<Integer> boxIds = new ArrayList<>();
for (TtBoxA ttBoxA : boxList) {
ExponentBoxVo boxVo = new ExponentBoxVo();
BeanUtils.copyProperties(ttBoxA, boxVo);
boxVo.setBoxId(ttBoxA.getBoxId());
boxIds.add(ttBoxA.getBoxId());
boxVos.add(boxVo);
}
// 查看当前登录者是否购买本期指数
LambdaQueryWrapper<TtExponentUser> wrapper1 = Wrappers.lambdaQuery();
wrapper1.in(TtExponentUser::getBoxId, boxIds);
wrapper1.eq(TtExponentUser::getExponentId, id);
wrapper1.eq(TtExponentUser::getUserId, ttUser.getUserId());
List<TtExponentUser> exponentUsers = ttExponentUserMapper.selectList(wrapper1);
Map<Integer, Integer> boxIdToUp = new HashMap<>();
Map<Integer, Integer> boxIdToDown = new HashMap<>();
if (CollectionUtils.isNotEmpty(exponentUsers)) {
for (TtExponentUser user : exponentUsers) {
if (user == null) {
continue;
}
if (Objects.equals(user.getForward(), 1)) {
int num = boxIdToUp.getOrDefault(user.getBoxId(), 0);
boxIdToUp.put(user.getBoxId(), num + 1);
}
if (Objects.equals(user.getForward(), 2)) {
int num = boxIdToDown.getOrDefault(user.getBoxId(), 0);
boxIdToDown.put(user.getBoxId(), num + 1);
}
}
}
for (ExponentBoxVo vo : boxVos) {
vo.setBuy_forward_up(boxIdToUp.getOrDefault(vo.getBoxId(), 0));
vo.setBuy_forward_down(boxIdToDown.getOrDefault(vo.getBoxId(), 0));
}
return R.ok(boxVos);
}
@Override
public R recenttenperiod() {
LambdaQueryWrapper<TtExponent> wrapper = Wrappers.lambdaQuery();
wrapper.orderByDesc(TtExponent::getId);
wrapper.last("limit 10");
List<TtExponent> db = ttExponentMapper.selectList(wrapper);
return R.ok(db);
}
/**
*
*/
@Override
public R info(TtUser ttUser) {
LambdaQueryWrapper<TtExponent> wrapper = Wrappers.lambdaQuery();
wrapper.orderByDesc(TtExponent::getId);
wrapper.last("limit 1");
TtExponent db = ttExponentMapper.selectOne(wrapper);
if (db == null) {
return R.fail("指数信息不存在");
}
ExponentInfoVo vo = new ExponentInfoVo();
Integer id = db.getId() + 1;
vo.setId(id);
vo.setExponent(0);
// 创建日期格式化对象,指定日期格式
SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
// 获取Calendar实例
Calendar calendar = Calendar.getInstance();
// 设置时间为今天凌晨
calendar.set(Calendar.HOUR_OF_DAY, 0);
calendar.set(Calendar.MINUTE, 0);
calendar.set(Calendar.SECOND, 0);
calendar.set(Calendar.MILLISECOND, 0);
// 获取今天凌晨时间
Date todayMidnight = calendar.getTime();
String period = "";
if (db.getCreateTime().after(todayMidnight)) {
period = formatter.format(new Date()) + String.format("%04d", (db.getNumber() + 1));
} else {
period = formatter.format(new Date()) + "0001";
}
vo.setPeriod(period);
int createTime = (int) (db.getCreateTime().getTime() / 1000);
vo.setEnd_time(createTime + 60);
vo.setPoints("");
vo.setState(0);
vo.setNumber(db.getNumber() + 1);
vo.setForward(0);
vo.setCreatetime(createTime + 30);
vo.setUpdatetime(createTime + 30);
int nowTime = (int) (System.currentTimeMillis() / 1000);
vo.setTimer(vo.getCreatetime() - nowTime);
// 查看当前登录者是否购买本期指数
LambdaQueryWrapper<TtExponentUser> wrapper2 = Wrappers.lambdaQuery();
wrapper2.eq(TtExponentUser::getExponentId, id);
wrapper2.eq(TtExponentUser::getUserId, ttUser.getUserId());
List<TtExponentUser> exponentUsers2 = ttExponentUserMapper.selectList(wrapper2);
// 若购买
if (CollectionUtils.isNotEmpty(exponentUsers2)) {
vo.setBuy(exponentUsers2.size());
BigDecimal up = BigDecimal.ZERO;
BigDecimal down = BigDecimal.ZERO;
for (TtExponentUser exponentUser : exponentUsers2) {
if (exponentUser == null) {
continue;
}
if (Objects.equals(exponentUser.getForward(), 1)) {
up = up.add(exponentUser.getPrice());
}
if (Objects.equals(exponentUser.getForward(), 2)) {
down = down.add(exponentUser.getPrice());
}
}
vo.setBuy_forward_up(up);
vo.setBuy_forward_down(down);
}
ExponentInfoVo last = new ExponentInfoVo();
last.setId(db.getId());
last.setExponent(db.getExponent());
period = formatter.format(db.getCreateTime()) + String.format("%04d", db.getNumber());
last.setPeriod(period);
int lastCreateTime = (int) (db.getCreateTime().getTime() / 1000);
last.setEnd_time(lastCreateTime + 30);
last.setPoints(db.getPoints());
last.setState(2);
last.setNumber(db.getNumber());
last.setForward(db.getForward());
last.setCreatetime(lastCreateTime);
last.setUpdatetime(lastCreateTime);
vo.setLast(last);
// 查看当前登录者是否购买本期指数
LambdaQueryWrapper<TtExponentUser> wrapper1 = Wrappers.lambdaQuery();
wrapper1.eq(TtExponentUser::getExponentId, db.getId());
wrapper1.eq(TtExponentUser::getUserId, ttUser.getUserId());
List<TtExponentUser> exponentUsers = ttExponentUserMapper.selectList(wrapper1);
ExponentJieguoVo jieguo = new ExponentJieguoVo();
jieguo.setPeriod(period);
jieguo.setExponent(db.getExponent());
jieguo.setForward(db.getForward());
// 若购买
if (CollectionUtils.isNotEmpty(exponentUsers)) {
jieguo.setSkinarr(openExponent(db.getForward(), ttUser, exponentUsers));
jieguo.setBuy(exponentUsers.size());
BigDecimal up = BigDecimal.ZERO;
BigDecimal down = BigDecimal.ZERO;
BigDecimal price = BigDecimal.ZERO;
for (TtExponentUser exponentUser : exponentUsers) {
if (exponentUser == null) {
continue;
}
price = price.add(exponentUser.getPrice());
if (Objects.equals(exponentUser.getForward(), 1)) {
up = up.add(exponentUser.getPrice());
}
if (Objects.equals(exponentUser.getForward(), 2)) {
down = down.add(exponentUser.getPrice());
}
}
jieguo.setBuy_money(price);
jieguo.setBuy_forward_up(up);
jieguo.setBuy_forward_down(down);
}
vo.setJieguo(jieguo);
return R.ok(vo);
}
/**
* 指数开箱
*/
private List<ExponentSkinarrVo> openExponent(int forward, TtUser ttUser, List<TtExponentUser> exponentUsers) {
Map<Integer, Integer> boxIdToNum = new HashMap<>();
// 判断箱子是否已判定结果
Map<Integer, Boolean> boxIdToOpen = new HashMap<>();
for (TtExponentUser user : exponentUsers) {
if (user == null) {
continue;
}
// 等于1则代表指数结果已判定结果
if (Objects.equals(user.getStatus(), 1)) {
boxIdToOpen.put(user.getBoxId(), true);
int num = boxIdToNum.getOrDefault(user.getBoxId(), 0);
boxIdToNum.put(user.getBoxId(), num + 1);
continue;
}
// 等于2则代表指数结果已判定结果
if (Objects.equals(user.getStatus(), 2)) {
boxIdToOpen.put(user.getBoxId(), true);
boxIdToNum.put(user.getBoxId(), 0);
continue;
}
if (Objects.equals(user.getForward(), forward)) {
int num = boxIdToNum.getOrDefault(user.getBoxId(), 0);
boxIdToNum.put(user.getBoxId(), num + 1);
user.setStatus(1);
} else {
user.setStatus(2);
}
user.setUpdateTime(new Date());
ttExponentUserMapper.updateById(user);
}
if (MapUtils.isEmpty(boxIdToNum)) {
return new ArrayList<>();
}
// 查询箱子信息
LambdaQueryWrapper<TtBox> wrapper = Wrappers.lambdaQuery();
wrapper.in(TtBox::getBoxId, boxIdToNum.keySet());
List<TtBox> ttBoxes = ttBoxMapper.selectList(wrapper);
Map<Integer, TtBox> ttBoxMap = ttBoxes.stream().collect(Collectors.toMap(TtBox::getBoxId, Function.identity()));
// 查询该用户已拥有的箱子信息
LambdaQueryWrapper<TtExponentUserBox> wrapper1 = Wrappers.lambdaQuery();
wrapper1.in(TtExponentUserBox::getBoxId, boxIdToNum.keySet());
wrapper1.eq(TtExponentUserBox::getUserId, ttUser.getUserId());
List<TtExponentUserBox> userBoxes = ttExponentUserBoxMapper.selectList(wrapper1);
Map<Integer, TtExponentUserBox> userBoxeMap = userBoxes.stream().collect(Collectors.toMap(TtExponentUserBox::getBoxId, Function.identity()));
List<ExponentSkinarrVo> vos = new ArrayList<>();
for (Map.Entry<Integer, Integer> entry : boxIdToNum.entrySet()) {
ExponentSkinarrVo vo = new ExponentSkinarrVo();
vo.setId(entry.getKey());
vo.setCount(entry.getValue());
TtBox ttBox = ttBoxMap.get(entry.getKey());
if (ttBox != null) {
vo.setImage(ttBox.getBoxImg01());
vo.setImage2(ttBox.getBoxImg02());
}
vos.add(vo);
// 若指数没有判定过,则进行放入箱子数量
if (!boxIdToOpen.getOrDefault(entry.getKey(), false)) {
TtExponentUserBox userBox = userBoxeMap.get(entry.getKey());
if (userBox == null) {
userBox = new TtExponentUserBox();
userBox.setBoxId(entry.getKey());
userBox.setUserId(ttUser.getUserId());
userBox.setNumber(entry.getValue());
userBox.setCreateTime(new Date());
userBox.setUpdateTime(new Date());
ttExponentUserBoxMapper.insert(userBox);
} else {
userBox.setNumber(userBox.getNumber() + entry.getValue());
userBox.setUpdateTime(new Date());
ttExponentUserBoxMapper.updateById(userBox);
}
}
}
return vos;
}
@Override
public R records(TtUser ttUser) {
List<ExponentOpenBoxVo> vos = ttExponentMapper.exponentOpenBox(ttUser.getUserId());
if (CollectionUtils.isEmpty(vos)) {
return R.ok(new ArrayList<>());
}
List<Integer> boxIds = new ArrayList<>();
for (ExponentOpenBoxVo vo : vos) {
boxIds.add(vo.getBoxId());
if (vo.getFailed() != null && vo.getFailed() > 0) {
vo.setState(2);
}
if (vo.getSuccess() != null && vo.getSuccess() > 0) {
vo.setState(1);
}
}
// 查询箱子信息
LambdaQueryWrapper<TtBox> wrapper = Wrappers.lambdaQuery();
wrapper.in(TtBox::getBoxId, boxIds);
List<TtBox> ttBoxes = ttBoxMapper.selectList(wrapper);
Map<Integer, TtBox> ttBoxMap = ttBoxes.stream().collect(Collectors.toMap(TtBox::getBoxId, Function.identity()));
for (ExponentOpenBoxVo vo : vos) {
TtBox ttBox = ttBoxMap.get(vo.getBoxId());
if (ttBox != null) {
vo.setImage(ttBox.getBoxImg01());
vo.setImage2(ttBox.getBoxImg02());
}
}
return R.ok(vos);
}
@Override
public R mybox(TtUser ttUser) {
// 查询该用户已拥有的箱子信息
LambdaQueryWrapper<TtExponentUserBox> wrapper1 = Wrappers.lambdaQuery();
wrapper1.eq(TtExponentUserBox::getUserId, ttUser.getUserId());
List<TtExponentUserBox> userBoxes = ttExponentUserBoxMapper.selectList(wrapper1);
if (CollectionUtils.isEmpty(userBoxes)) {
return R.ok(new ArrayList<>());
}
List<ExponentSkinarrVo> vos = new ArrayList<>();
List<Integer> boxIds = new ArrayList<>();
for (TtExponentUserBox userBox : userBoxes) {
ExponentSkinarrVo vo = new ExponentSkinarrVo();
vo.setId(userBox.getBoxId());
boxIds.add(userBox.getBoxId());
vo.setCount(userBox.getNumber());
vos.add(vo);
}
// 查询箱子信息
LambdaQueryWrapper<TtBox> wrapper = Wrappers.lambdaQuery();
wrapper.in(TtBox::getBoxId, boxIds);
List<TtBox> ttBoxes = ttBoxMapper.selectList(wrapper);
Map<Integer, TtBox> ttBoxMap = ttBoxes.stream().collect(Collectors.toMap(TtBox::getBoxId, Function.identity()));
for (ExponentSkinarrVo vo : vos) {
TtBox ttBox = ttBoxMap.get(vo.getId());
if (ttBox != null) {
vo.setImage(ttBox.getBoxImg01());
vo.setImage2(ttBox.getBoxImg02());
}
}
return R.ok(vos);
}
@Override
public R openbox(TtUser user, TtBox box, Integer num) {
// 查询该用户已拥有的箱子信息
LambdaQueryWrapper<TtExponentUserBox> wrapper1 = Wrappers.lambdaQuery();
wrapper1.eq(TtExponentUserBox::getUserId, user.getUserId());
wrapper1.eq(TtExponentUserBox::getBoxId, box.getBoxId());
List<TtExponentUserBox> userBoxes = ttExponentUserBoxMapper.selectList(wrapper1);
if (CollectionUtils.isEmpty(userBoxes) || userBoxes.get(0) == null || userBoxes.get(0).getNumber() < num) {
return R.ok(new ArrayList<>());
}
TtExponentUserBox userBox = userBoxes.get(0);
// 抽奖
List<TtBoxRecords> boxRecords = new ArrayList<>();
Map<Long, TtOrnamentsA> boxToOrnament = new HashMap<>();
for (int i = 0; i < num; i++) {
String ornamentId = lotteryMachine.singleLottery(user, box);
if (StringUtils.isBlank(ornamentId)) {
throw new OrnamentNullException("单次抽奖无结果#"+box.getBoxId()+"#"+ornamentId);
//return R.fail("系统繁忙,请稍后重试。");
}
// 宝箱详细信息
TtOrnamentsA ornamentsData = bindBoxMapper.ornamentsInfo(box.getBoxId(), ornamentId);
if (ObjectUtil.isEmpty(ornamentsData)){
throw new OrnamentNullException("没有符合条件的宝箱信息#"+box.getBoxId()+"#"+ornamentId);
}
// 抽奖成功,构建开箱记录数据
TtBoxRecords boxRecord = TtBoxRecords.builder()
.userId(user.getUserId())
.boxId(box.getBoxId())
.boxName(box.getBoxName())
.boxPrice(box.getPrice())
.ornamentId(Long.valueOf(ornamentId))
// .marketHashName(ornamentsData.)
.ornamentName(ObjectUtil.isNotEmpty(ornamentsData.getName()) ? ornamentsData.getName() : ObjectUtil.isNotEmpty(ornamentsData.getShortName()) ? ornamentsData.getShortName() : "无名称")
.imageUrl(ornamentsData.getImageUrl())
.ornamentsPrice(ornamentsData.getUsePrice())
.ornamentsLevelId(ornamentsData.getOrnamentsLevelId())
.ornamentLevelImg(ornamentsData.getLevelImg())
.holderUserId(user.getUserId())
.source(TtboxRecordSource.EXPONENT_BOX.getCode())
.status(IN_PACKSACK_ON.getCode())
.createTime(new Timestamp(System.currentTimeMillis()))
.updateTime(new Timestamp(System.currentTimeMillis()))
.build();
boxRecords.add(boxRecord);
boxToOrnament.put(Long.valueOf(ornamentId), ornamentsData);
}
// 同步保存游戏记录
boxRecordsService.saveBatch(boxRecords);
// 返回值
List<OpenBoxVO> openBoxVOs = boxRecords.stream().map(item -> {
OpenBoxVO openBoxVO = new OpenBoxVO();
BeanUtil.copyProperties(item, openBoxVO);
TtOrnamentsA ornamentsData = boxToOrnament.get(item.getOrnamentId());
if (ornamentsData != null) {
openBoxVO.setShortName(ornamentsData.getShortName());
openBoxVO.setExteriorName(ornamentsData.getExteriorName());
}
openBoxVO.setUsePrice(openBoxVO.getOrnamentsPrice());
openBoxVO.setLevelImg(item.getOrnamentLevelImg());
return openBoxVO;
}).collect(Collectors.toList());
userBox.setNumber(userBox.getNumber() - num);
userBox.setUpdateTime(new Date());
ttExponentUserBoxMapper.updateById(userBox);
return R.ok(openBoxVOs);
}
}

View File

@@ -0,0 +1,11 @@
package com.ruoyi.playingmethod.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.admin.mapper.TtFightUserMapper;
import com.ruoyi.domain.other.TtFightUser;
import com.ruoyi.playingmethod.service.ApiFightUserService;
import org.springframework.stereotype.Service;
@Service
public class ApiFightUserServiceImpl extends ServiceImpl<TtFightUserMapper, TtFightUser> implements ApiFightUserService {
}

View File

@@ -0,0 +1,489 @@
package com.ruoyi.playingmethod.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.ruoyi.admin.config.RedisConstants;
import com.ruoyi.admin.mapper.TtBoxRecordsMapper;
import com.ruoyi.admin.mapper.TtOrnamentMapper;
import com.ruoyi.admin.mapper.TtOrnamentsLevelMapper;
import com.ruoyi.admin.mapper.TtUpgradeFailOrnamentsMapper;
import com.ruoyi.admin.mapper.TtUpgradeOrnamentsMapper;
import com.ruoyi.admin.mapper.TtUpgradeRecordMapper;
import com.ruoyi.admin.model.UpdateUserAccountBo;
import com.ruoyi.admin.service.TtBoxRecordsService;
import com.ruoyi.admin.service.TtUpgradeOrnamentsService;
import com.ruoyi.admin.service.TtUpgradeRecordService;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.redis.config.RedisLock;
import com.ruoyi.domain.common.constant.LockKey.UpgradeLock;
import com.ruoyi.domain.common.constant.TtAccountRecordSource;
import com.ruoyi.domain.common.constant.TtboxRecordSource;
import com.ruoyi.domain.common.constant.TtboxRecordStatus;
import com.ruoyi.domain.common.constant.UserType;
import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.ApiLuckyUpgradeBody;
import com.ruoyi.domain.other.TtUpgradeOrnaments;
import com.ruoyi.domain.other.TtUpgradeRecord;
import com.ruoyi.domain.other.UpgradeBodyA;
import com.ruoyi.domain.vo.ApiLuckyOrnamentsDataVO;
import com.ruoyi.domain.vo.upgrade.SimpleOrnamentVO;
import com.ruoyi.playingmethod.mapper.ApiLuckyUpgradeMapper;
import com.ruoyi.playingmethod.mapper.ApiTtUserBlendErcashMapper;
import com.ruoyi.playingmethod.service.ApiLuckyUpgradeService;
import com.ruoyi.system.service.ISysConfigService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Date;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Slf4j
@Service
public class ApiLuckyUpgradeServiceImpl implements ApiLuckyUpgradeService {
@Value("${mkcsgo.upgrade.defaultRequired}")
private Integer defaultRequired;
@Value("${mkcsgo.upgrade.anchorDefaultRequired}")
private Integer anchorDefaultRequired;
@Autowired
private Executor customThreadPoolExecutor;
@Autowired
private ApiTtUserBlendErcashMapper apiTtUserBlendErcashMapper;
private final TtUserService userService;
private final ISysConfigService configService;
private final ApiLuckyUpgradeMapper apiLuckyUpgradeMapper;
private final TtUpgradeOrnamentsMapper upgradeOrnamentsMapper;
private final TtUpgradeFailOrnamentsMapper upgradeFailOrnamentsMapper;
private final TtUpgradeRecordMapper upgradeRecordMapper;
private final TtOrnamentMapper ornamentsMapper;
private final TtBoxRecordsMapper boxRecordsMapper;
private final TtOrnamentsLevelMapper ornamentsLevelMapper;
@Autowired
private RabbitTemplate rabbitTemplate;
@Autowired
private RedisLock redisLock;
@Autowired
private RedisCache redisCache;
public ApiLuckyUpgradeServiceImpl(TtUserService userService,
ISysConfigService configService,
ApiLuckyUpgradeMapper apiLuckyUpgradeMapper,
TtUpgradeOrnamentsMapper upgradeOrnamentsMapper,
TtUpgradeFailOrnamentsMapper upgradeFailOrnamentsMapper,
TtUpgradeRecordMapper upgradeRecordMapper,
TtOrnamentMapper ornamentsMapper,
TtBoxRecordsMapper boxRecordsMapper,
TtOrnamentsLevelMapper ornamentsLevelMapper) {
this.userService = userService;
this.configService = configService;
this.apiLuckyUpgradeMapper = apiLuckyUpgradeMapper;
this.upgradeOrnamentsMapper = upgradeOrnamentsMapper;
this.upgradeFailOrnamentsMapper = upgradeFailOrnamentsMapper;
this.upgradeRecordMapper = upgradeRecordMapper;
this.ornamentsMapper = ornamentsMapper;
this.boxRecordsMapper = boxRecordsMapper;
this.ornamentsLevelMapper = ornamentsLevelMapper;
}
@Autowired
private TtUpgradeOrnamentsService ttUpgradeOrnamentsService;
@Autowired
private TtUpgradeOrnamentsMapper ttUpgradeOrnamentsMapper;
@Autowired
private TtBoxRecordsService ttBoxRecordsService;
@Autowired
private TtUpgradeRecordService ttUpgradeRecordService;
@Override
public List<ApiLuckyOrnamentsDataVO> getOrnamentsList(ApiLuckyUpgradeBody apiLuckyUpgradeBody) {
apiLuckyUpgradeBody.setType(null);
return apiLuckyUpgradeMapper.getOrnamentsList(apiLuckyUpgradeBody);
}
public R upgradeCheck(TtUser player, UpgradeBodyA param) {
if (player.getAccountAmount() == null || player.getAccountAmount().compareTo(param.getPrice()) <= 0) {
return R.fail("余额不足");
}
if (!player.getUserType().equals(UserType.ANCHOR.getCode()) && !player.getUserType().equals(UserType.COMMON_USER.getCode())) {
return R.fail("非法的用户类型");
}
return R.ok();
}
@Override
@Transactional
public R upgrade(TtUser ttUser, UpgradeBodyA upgradeParam) {
TtUpgradeOrnaments upgradeOrnament = new LambdaQueryChainWrapper<>(upgradeOrnamentsMapper)
.eq(TtUpgradeOrnaments::getId, upgradeParam.getUpgradeOrnamentId())
.eq(TtUpgradeOrnaments::getStatus, "0")
.one();
if (ObjectUtil.isNull(upgradeOrnament)) {
return R.fail("该饰品未在幸运升级开放。");
}
// 检查
R check = upgradeCheck(ttUser, upgradeParam);
if (!check.getCode().equals(200)) return check;
// lock_key
Integer upgradeOrnamentId = upgradeParam.getUpgradeOrnamentId();
String userType = ttUser.getUserType();
String upgradeLock = UpgradeLock.UPGRADE_LOCK.getLock() + upgradeOrnamentId + ":" + userType;
// 2 尝试获取锁
Boolean lock = false;
for (int t = 0; t < 2; t++) {
lock = redisLock.tryLock(upgradeLock, 2L, 7L, TimeUnit.SECONDS);
if (!lock) {
} else {
break;
}
}
if (!lock) return R.fail("系统繁忙,请稍后重试。");
// 3 扣款
R<Map<String, BigDecimal>> mapR = upgradeAccounting(upgradeParam.getPrice(), ttUser);
if (!mapR.getCode().equals(200)) {
redisLock.unlock(upgradeLock);
return mapR;
}
// 4 计算游戏
boolean isVictory;
try {
// 升级核心
isVictory = playing(ttUser, upgradeOrnament, upgradeParam);
} catch (Exception e) {
e.printStackTrace();
return R.fail("系统繁忙,请稍后重试。");
} finally {
redisLock.unlock(upgradeLock);
}
// 5 同步保存本次结果。
TtUpgradeRecord upgradeRecord; // 升级记录
List<TtBoxRecords> boxRecords; // 获得物品记录
if (isVictory) {
Long ornamentId = upgradeOrnament.getOrnamentsId();
// 胜
List<SimpleOrnamentVO> prizeList = ornamentsMapper.simpleOrnamentInfo(Arrays.asList(ornamentId));
SimpleOrnamentVO ornament = prizeList.get(0);
// 奖励集合 并统计奖励总价值
// List<SimpleOrnamentVO> prizeList = Arrays.asList(ornament);
BigDecimal prizeTotal = BigDecimal.ZERO;
for (SimpleOrnamentVO vo : prizeList) prizeTotal = prizeTotal.add(vo.getOrnamentPrice());
// 构造升级记录
upgradeRecord = TtUpgradeRecord.builder()
.userId(ttUser.getUserId())
.userType(ttUser.getUserType())
.nickName(ObjectUtil.isNotEmpty(ttUser.getNickName()) ? ttUser.getNickName() : ttUser.getUserName())
.isVictory(isVictory)
.amountConsumed(upgradeParam.getPrice())
.probability(upgradeParam.getProbability())
.targetUpgradeId(upgradeOrnament.getId())
.targetOrnamentId(ornamentId)
.targetOrnamentPrice(ornament.getOrnamentPrice())
.gainOrnamentList(JSONUtil.toJsonStr(prizeList))
.gainOrnamentsPrice(prizeTotal)
.openTime(new Date())
.build();
// 构造获得物品记录
boxRecords = Arrays.asList(TtBoxRecords.builder()
.source(TtboxRecordSource.UPGRADE.getCode())
.ornamentId(ornamentId)
.ornamentName(ornament.getOrnamentName())
.userId(ttUser.getUserId())
.holderUserId(ttUser.getUserId())
.imageUrl(ornament.getOrnamentImgUrl())
.ornamentsPrice(ornament.getOrnamentPrice())
.status(TtboxRecordStatus.IN_PACKSACK_ON.getCode())
.marketHashName(ornament.getOrnamentHashName())
.createTime(new Date())
.updateTime(new Date())
.build());
} else {
// 负
// 奖励集合 并统计奖励总价值
List<SimpleOrnamentVO> failPrize = upgradeFailOrnamentsMapper.ornamentInfoByUpgradeId(upgradeOrnament.getId());
BigDecimal prizeTotal = BigDecimal.ZERO;
if (!failPrize.isEmpty()) {
for (SimpleOrnamentVO item : failPrize) {
prizeTotal = prizeTotal.add(item.getOrnamentPrice().multiply(new BigDecimal(item.getOrnamentNumber())));
}
}
// 构造升级记录
upgradeRecord = TtUpgradeRecord.builder()
.userId(ttUser.getUserId())
.userType(ttUser.getUserType())
.nickName(ObjectUtil.isNotEmpty(ttUser.getNickName()) ? ttUser.getNickName() : ttUser.getUserName())
.amountConsumed(upgradeParam.getPrice())
.probability(upgradeParam.getProbability())
.isVictory(isVictory)
.targetUpgradeId(upgradeOrnament.getId())
.targetOrnamentId(upgradeOrnament.getOrnamentsId())
.targetOrnamentPrice(upgradeOrnament.getOrnamentPrice())
.gainOrnamentList(failPrize.isEmpty() ? "" : JSONUtil.toJsonStr(failPrize))
.gainOrnamentsPrice(prizeTotal)
.openTime(new Date())
.build();
// 构造获得物品记录
boxRecords = failPrize.stream().map(item -> {
return TtBoxRecords.builder()
.source(TtboxRecordSource.UPGRADE.getCode())
.ornamentId(item.getOrnamentId())
.ornamentName(item.getOrnamentName())
.userId(ttUser.getUserId())
.holderUserId(ttUser.getUserId())
.imageUrl(item.getOrnamentImgUrl())
.ornamentsPrice(item.getOrnamentPrice())
.status(TtboxRecordStatus.IN_PACKSACK_ON.getCode())
.marketHashName(item.getOrnamentHashName())
.createTime(new Date())
.updateTime(new Date())
.build();
}).collect(Collectors.toList());
}
// 游戏记录
ttUpgradeRecordService.save(upgradeRecord);
ttBoxRecordsService.saveBatch(boxRecords);
return R.ok(upgradeRecord);
}
public Boolean playing(TtUser ttUser, TtUpgradeOrnaments upgradeOrnament, UpgradeBodyA upgradeParam) {
Integer upgradeOrnamentId = upgradeOrnament.getId();
String userType = ttUser.getUserType();
String rangeFixedKey = RedisConstants.UPGRADE_RANGE_FIXED + upgradeOrnamentId + ":" + userType;
String rangeFloatKey = RedisConstants.UPGRADE_RANGE + upgradeOrnamentId + ":" + userType;
// 读redis的固定概率 [x,y] x进度y空间大小
Object rangeFixedObj = redisCache.getCacheObject(rangeFixedKey);
String rangeFixedStr = JSONUtil.toJsonStr(rangeFixedObj);
Integer[] rangeFixed = JSONUtil.parseArray(rangeFixedStr).toArray(new Integer[2]);
// 读redis的饰品概率区间 [begin,end]
Object rangeFloatObj = redisCache.getCacheObject(rangeFloatKey);
String rangeFloatStr = JSONUtil.toJsonStr(rangeFloatObj);
Integer[] rangeFloat = JSONUtil.parseArray(rangeFloatStr).toArray(new Integer[2]);
if (ObjectUtil.isNotEmpty(rangeFixed)) {
// 抽奖
return doUpgrade(
ttUser,
upgradeOrnament,
rangeFixed,
upgradeParam,
rangeFixedKey,
rangeFloatKey,
rangeFloat);
}
// 没有使用redis的概率区间新建
if (ObjectUtil.isNotEmpty(rangeFloat)) {
// 创建固定概率,
Integer[] newRangeFixed = new Integer[]{0, null};
for (int i = 0; i < 5; i++) {
Random random = new Random();
int r = random.nextInt(rangeFloat[1] - rangeFloat[0]) + rangeFloat[0];
newRangeFixed[1] = r;
}
// 抽奖
return doUpgrade(
ttUser,
upgradeOrnament,
newRangeFixed,
upgradeParam,
rangeFixedKey,
rangeFloatKey,
rangeFloat);
}
// 也没有概率区间用mysql的数据创建固定概率抽奖
if (userType.equals(UserType.ANCHOR.getCode())) {
String anchorLuckSection = upgradeOrnament.getAnchorLuckSection();
List<Integer> list = JSONUtil.toList(anchorLuckSection, Integer.class);
rangeFloat = list.toArray(new Integer[2]);
} else if (userType.equals(UserType.COMMON_USER.getCode())) {
String luckSection = upgradeOrnament.getLuckSection();
List<Integer> list = JSONUtil.toList(luckSection, Integer.class);
rangeFloat = list.toArray(new Integer[2]);
}
rangeFixed = createRangeFixed(rangeFloat, 0);
// 缓存固定概率
// redisCache.setCacheList(rangeFixedKey, Arrays.asList(rangeFixed));
// redisCache.setCacheObject(rangeFixedKey, Arrays.asList(rangeFixed));
// 缓存概率区间
redisCache.setCacheObject(rangeFloatKey, rangeFloat, 1, TimeUnit.DAYS);
// 抽奖
return doUpgrade(
ttUser,
upgradeOrnament,
rangeFixed,
upgradeParam,
rangeFixedKey,
rangeFloatKey,
rangeFloat);
}
/**
* @param rangeFixed 本次的固定概率
* @param param http请求参数
* @param rangeFixedKey redis固定概率key
* @param rangeFloat 升级饰品的概率区间
* @return
*/
public Boolean doUpgrade(TtUser ttUser,
TtUpgradeOrnaments upgradeOrnament,
Integer[] rangeFixed,
UpgradeBodyA param,
String rangeFixedKey,
String rangeFloatKey,
Integer[] rangeFloat) {
// 计算进度
Integer currentV = rangeFixed[0];
Integer fullV = rangeFixed[1];
int sub = fullV - (param.getProbability() + currentV);
if (sub > 0) {
// 失败,记录进度
int i = param.getProbability() + currentV;
rangeFixed[0] = i;
// redisCache.setCacheList(rangeFixedKey, Arrays.asList(rangeFixed));
redisCache.setCacheObject(rangeFixedKey, rangeFixed, 1, TimeUnit.DAYS);
return false;
}
// 成功
// 如果没有概率区间,新建
if (ObjectUtil.isNotEmpty(rangeFloat)) {
if (ttUser.getUserType().equals(UserType.ANCHOR.getCode())) {
String anchorLuckSection = upgradeOrnament.getAnchorLuckSection();
List<Integer> list = JSONUtil.toList(anchorLuckSection, Integer.class);
rangeFloat = list.toArray(new Integer[2]);
} else if (ttUser.getUserType().equals(UserType.COMMON_USER.getCode())) {
String luckSection = upgradeOrnament.getLuckSection();
List<Integer> list = JSONUtil.toList(luckSection, Integer.class);
rangeFloat = list.toArray(new Integer[2]);
}
// 缓存概率区间
redisCache.setCacheObject(rangeFloatKey, rangeFloat, 1, TimeUnit.DAYS);
// rangeFixed = createRangeFixed(rangeFloat,0);
}
// 成功,随机创建新的固定概率
Integer[] newRangeFixed = createRangeFixed(rangeFloat, Math.abs(sub));
// 保存新的固定概率
// redisCache.setCacheList(rangeFixedKey, Arrays.asList(newRangeFixed));
redisCache.setCacheObject(rangeFixedKey, newRangeFixed, 1, TimeUnit.DAYS);
return true;
}
public Integer[] createRangeFixed(Integer[] rangeFloat, Integer initVal) {
// 创建固定概率,
Integer[] newRangeFixed = new Integer[2];
newRangeFixed[0] = initVal;
int r = -1;
for (int i = 0; i < 5; i++) {
Random random = new Random();
r = random.nextInt(rangeFloat[1] - rangeFloat[0]) + rangeFloat[0];
}
newRangeFixed[1] = r;
return newRangeFixed;
}
// 扣款
public R<Map<String, BigDecimal>> upgradeAccounting(BigDecimal consumption, TtUser player) {
R<UpdateUserAccountBo> updateResult = userService.updateUserAccount(player.getUserId(), consumption.negate(),
TtAccountRecordSource.GOLD_DEVICE);
if (R.isError(updateResult)) {
return R.fail(updateResult.getMsg());
}
LambdaUpdateWrapper<TtUser> userUpdate = new LambdaUpdateWrapper<>();
userUpdate.eq(TtUser::getUserId, player.getUserId());
Map<String, BigDecimal> map = new HashMap<>();
map.put("Amount", updateResult.getData().getAccountAmount());
map.put("Credits", updateResult.getData().getAccountCredits());
map.put("total", consumption);
return R.ok(map);
}
}

View File

@@ -0,0 +1,282 @@
package com.ruoyi.playingmethod.service.impl;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.ruoyi.admin.mapper.TtRedPacketMapper;
import com.ruoyi.admin.mapper.TtRedPacketRecordMapper;
import com.ruoyi.admin.mapper.TtUserBlendErcashMapper;
import com.ruoyi.admin.service.TtRedPackService;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.admin.util.RandomUtils;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.redis.config.RedisLock;
import com.ruoyi.domain.common.constant.TtAccountRecordSource;
import com.ruoyi.domain.common.constant.TtAccountRecordType;
import com.ruoyi.domain.entity.TtUserBlendErcash;
import com.ruoyi.domain.entity.fight.TtFight;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.TtRedPack;
import com.ruoyi.domain.other.TtRedPacket;
import com.ruoyi.domain.other.TtRedPacketRecord;
import com.ruoyi.domain.param.RedPackRecordParam;
import com.ruoyi.playingmethod.service.ApiRedPackService;
import com.ruoyi.thirdparty.wechat.domain.PtLoginUser;
import com.ruoyi.thirdparty.wechat.entity.TtCoinRechargeParam;
import com.ruoyi.thirdparty.wechat.entity.TtCoinRechargeRecord;
import com.ruoyi.thirdparty.wechat.service.ITtCoinRechargeRecordService;
import com.ruoyi.thirdparty.wechat.service.ITtCoinRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
@Service
@Slf4j
public class ApiRedPackServiceImpl implements ApiRedPackService {
@Autowired
private TtRedPackService ttRedPackService;
@Autowired
private ITtCoinRechargeRecordService coinRechargeRecordService;
@Autowired
private ITtCoinRecordService recordService;
@Autowired
private TtUserService ttUserService;
@Autowired
private TtUserBlendErcashMapper ttUserBlendErcashMapper;
@Autowired
private TtRedPacketRecordMapper ttRedPacketRecordMapper;
@Autowired
private TtRedPacketMapper ttRedPacketMapper;
@Autowired
private TtUserService userService;
@Autowired
private RedisLock redisLock;
@Override
public List<TtRedPack> getRedPackList(PtLoginUser user, Integer type) {
TtRedPack redPackParam = new TtRedPack();
// redPackParam.setStatus(1L);
// redPackParam.setPackType(type + "");
return ttRedPackService.selectTtRedPackList(redPackParam);
}
@Override
@Transactional
public AjaxResult getRedPack(Long userId, Long redPackId) {
TtUser user = ttUserService.getById(userId);
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
TtRedPack redPack = ttRedPackService.selectTtRedPackById(redPackId);
TtCoinRechargeParam rechargeParam = new TtCoinRechargeParam();
rechargeParam.setUid(user.getUserId().longValue());
rechargeParam.setBeginTime(format.format(redPack.getBeginTime()));
rechargeParam.setEndTime(format.format(redPack.getEndTime()));
rechargeParam.setCoin(redPack.getRechargeLimit());
// 查询用户在指定时间内的大于最小充值金额的充值记录
LambdaQueryWrapper<TtUserBlendErcash> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.eq(TtUserBlendErcash::getUserId, rechargeParam.getUid())
.eq(TtUserBlendErcash::getType, 1)
.eq(TtUserBlendErcash::getSource, 0)
.gt(TtUserBlendErcash::getTotal, rechargeParam.getCoin())
.gt(TtUserBlendErcash::getCreateTime, rechargeParam.getBeginTime())
.lt(TtUserBlendErcash::getCreateTime, rechargeParam.getEndTime());
List<TtUserBlendErcash> rechargeRecords = ttUserBlendErcashMapper.selectList(queryWrapper);
// 根据红包ID和用户ID查询红包记录检查用户是否已经领取过某个特定的红包
LambdaQueryWrapper<TtRedPacketRecord> ttRedPacketRecordWrapper = new LambdaQueryWrapper<>();
ttRedPacketRecordWrapper
.eq(TtRedPacketRecord::getRedPacketId, redPack.getId())
.eq(TtRedPacketRecord::getUserId, user.getUserId());
List<TtRedPacketRecord> redPackRecords = ttRedPacketRecordMapper.selectList(ttRedPacketRecordWrapper);
if (redPackRecords.size() >= rechargeRecords.size()) {
return AjaxResult.error("可领取次数不足");
}
// TODO 减红包剩余数
String lockName = "RED_PACK_LOCK:" + redPackId;
boolean lock = false;
try {
// 尝试获取锁最多尝试10次每次等待3秒锁定时间7秒
for (int i = 0; i < 10; i++) {
lock = redisLock.tryLock(lockName, 3, 7, TimeUnit.SECONDS);
if (lock) {
break;
}
}
if (!lock) {
return AjaxResult.error("服务器繁忙,稍后重试。");
}
// 执行业务逻辑
TtRedPack ttRedPack = ttRedPackService.selectTtRedPackById(redPackId);
if (ttRedPack.getValidCount() <= 0) {
return AjaxResult.error("红包已被领完");
} else {
ttRedPack.setValidCount(ttRedPack.getValidCount() - 1);
ttRedPackService.updateTtRedPack(ttRedPack);
}
} finally {
if (lock) {
redisLock.unlock(lockName);
}
}
TtRedPacketRecord ttRedPackRecord = new TtRedPacketRecord();
BigDecimal randomValue = RandomUtils.getRandomaBigdecimal(redPack.getValueMin(), redPack.getValueMax());
ttRedPackRecord.setRedPacketId(redPackId.intValue());
ttRedPackRecord.setUserId(user.getUserId());
ttRedPackRecord.setReceiveAmount(randomValue);
ttRedPackRecord.setReceiveTime(new Date());
ttRedPacketRecordMapper.insert(ttRedPackRecord);
// 加钱
LambdaUpdateWrapper<TtUser> userUpdate = new LambdaUpdateWrapper<>();
userUpdate
.eq(TtUser::getUserId, user.getUserId())
.setSql("account_credits = account_credits + " + randomValue);
userService.update(userUpdate);
// 综合消费日志
TtUser ttUser = userService.getById(user.getUserId());
TtUserBlendErcash blendErcash = TtUserBlendErcash.builder()
.userId(user.getUserId())
.credits(randomValue.compareTo(BigDecimal.ZERO) > 0 ? randomValue : null)
.finalCredits(randomValue.compareTo(BigDecimal.ZERO) > 0 ? ttUser.getAccountCredits().add(randomValue) : null)
.total(randomValue.add(randomValue)) // 收支合计
.type(TtAccountRecordType.INPUT.getCode())
.source(TtAccountRecordSource.CHARGE_RED_PACKET.getCode())
.remark(TtAccountRecordSource.CHARGE_RED_PACKET.getMsg())
.createTime(new Timestamp(System.currentTimeMillis()))
.updateTime(new Timestamp(System.currentTimeMillis()))
.build();
ttUserBlendErcashMapper.insert(blendErcash);
return AjaxResult.success(ttRedPackRecord);
}
@Override
@Transactional
public AjaxResult getRedPackByKey(LoginUser user, String redPackKey) {
SimpleDateFormat format = new SimpleDateFormat("yyyy-MM-dd");
// 根据KEY查询红包
LambdaUpdateWrapper<TtRedPacket> selectRedPackByKeyWrapper = new LambdaUpdateWrapper<>();
selectRedPackByKeyWrapper
.eq(TtRedPacket::getPassword, redPackKey);
TtRedPacket redPack = ttRedPacketMapper.selectOne(selectRedPackByKeyWrapper);
if (redPack == null){
return AjaxResult.error("未找到红包,请检查口令是否正确");
}
TtCoinRechargeParam rechargeParam = new TtCoinRechargeParam();
rechargeParam.setUid(user.getUserId());
rechargeParam.setBeginTime(format.format(new Date()) + " 00:00:00");
rechargeParam.setEndTime(format.format(new Date()) + " 23:59:59");
LambdaQueryWrapper<TtRedPacketRecord> ttRedPacketRecordWrapper = new LambdaQueryWrapper<>();
ttRedPacketRecordWrapper
.eq(TtRedPacketRecord::getRedPacketId, redPack.getId())
.eq(TtRedPacketRecord::getUserId, user.getUserId().intValue());
List<TtRedPacketRecord> redPackRecords = ttRedPacketRecordMapper.selectList(ttRedPacketRecordWrapper);
if (redPackRecords.size() > 0){
return AjaxResult.error("您已经领取过。");
}
TtRedPacketRecord ttRedPackRecord = new TtRedPacketRecord();
// 此处解析最小值和最大值[1, 5]
String amount = redPack.getAmount();
// 去除方括号和空格,得到 "1,5"
String cleanString = amount.replaceAll("\\[|\\]|\\s", "");
// 分割字符串,得到字符串数组 ["1", "5"]
String[] parts = cleanString.split(",");
// 转换为 BigDecimal
BigDecimal minValue = new BigDecimal(parts[0]);
BigDecimal maxValue = new BigDecimal(parts[1]);
BigDecimal randomValue = RandomUtils.getRandomaBigdecimal(minValue, maxValue);
ttRedPackRecord.setRedPacketId(redPack.getId());
ttRedPackRecord.setUserId(user.getUserId().intValue());
ttRedPackRecord.setReceivePassword(redPackKey);
ttRedPackRecord.setReceiveAmount(randomValue);
ttRedPackRecord.setReceiveTime(new Date());
ttRedPacketRecordMapper.insert(ttRedPackRecord);
// 加钱
userService.updateOnlyUserAccount(user.getUserId().intValue(), randomValue, TtAccountRecordSource.RECEIVE_RED_PACKET);
return AjaxResult.success(ttRedPackRecord);
}
@Override
public List<TtRedPacketRecord> getActivityDataHis(PtLoginUser user) {
RedPackRecordParam redPackRecordParam = new RedPackRecordParam();
redPackRecordParam.setuId(user.getUserId().intValue());
LambdaQueryWrapper<TtRedPacketRecord> ttRedPacketRecordWrapper = new LambdaQueryWrapper<>();
ttRedPacketRecordWrapper
.eq(TtRedPacketRecord::getUserId, user.getUserId().intValue());
List<TtRedPacketRecord> redPackRecords = ttRedPacketRecordMapper.selectList(ttRedPacketRecordWrapper);
return redPackRecords;
}
@Override
public AjaxResult getRedPackDataDetail(PtLoginUser user, Long recordId) {
TtRedPacketRecord ttRedPackRecord = ttRedPacketRecordMapper.selectById(recordId);
if (ttRedPackRecord.getReceivePassword() != null) {
TtRedPacket redPackKey = ttRedPacketMapper.selectById(ttRedPackRecord.getRedPacketId());
// TODO 缺少属性
// ttRedPackRecord.setRedPackKey(redPackKey);
} else {
TtRedPack redPack = ttRedPackService.selectTtRedPackById(ttRedPackRecord.getRedPacketId().longValue());
// TODO 缺少属性
// ttRedPackRecord.setRedPack(redPack);
}
return AjaxResult.success(ttRedPackRecord);
}
/**
* 解析红包最小值和最大值
*/
public static List<BigDecimal> parseBigDecimalList(String input) {
// 去掉中括号和空格
String cleanInput = input.replace("[", "").replace("]", "").replaceAll("\\s+", "");
// 按逗号分割成字符串数组
String[] parts = cleanInput.split(",");
// 转换为 BigDecimal 对象列表
List<BigDecimal> bigDecimals = Arrays.stream(parts)
.map(BigDecimal::new) // 使用 BigDecimal 的构造方法转换为 BigDecimal 对象
.collect(Collectors.toList());
return bigDecimals;
}
}

View File

@@ -0,0 +1,198 @@
package com.ruoyi.playingmethod.service.impl;
import com.ruoyi.admin.mapper.TtBoxMapper;
import com.ruoyi.admin.mapper.TtBoxRecordsMapper;
import com.ruoyi.admin.mapper.TtOrnamentMapper;
import com.ruoyi.admin.service.TtBoxRecordsService;
import com.ruoyi.admin.service.TtOrnamentService;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.common.core.domain.AjaxResult;
import com.ruoyi.common.core.domain.model.LoginUser;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.domain.common.constant.TtboxRecordSource;
import com.ruoyi.domain.dto.packSack.PackSackCondition;
import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.entity.TtOrnament;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.TtBox;
import com.ruoyi.domain.other.TtReplacementRecord;
import com.ruoyi.domain.vo.UserPackSackDataVO;
import com.ruoyi.playingmethod.mapper.ApiReplacementRecordMapper;
import com.ruoyi.playingmethod.service.IApiReplacementRecordService;
import com.ruoyi.system.service.ISysConfigService;
import com.ruoyi.thirdparty.wechat.domain.PtLoginUser;
import com.ruoyi.user.mapper.ApiUserPackSackMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.*;
/**
* 业务端汰换逻辑实现
*
* @author junhai
*/
@Service
public class ApiReplacementRecordServiceImpl implements IApiReplacementRecordService {
@Autowired
private ApiReplacementRecordMapper apiReplacementRecordMapper;
@Autowired
private TtBoxRecordsService ttBoxRecordsService;
@Autowired
private TtBoxRecordsMapper ttBoxRecordsMapper;
@Autowired
private ISysConfigService sysConfigService;
@Autowired
private TtOrnamentMapper ttOrnamentMapper;
// 概率配置数组
private static final double[][] percent = {
{0.5, 0.6},
{0.6, 0.7},
{0.7, 0.8},
{0.8, 0.9},
{0.9, 1},
{1, 2}
};
@Override
@Transactional
public AjaxResult synthesizeItems(LoginUser user, List<Long> itemIds) {
int size = 0;
int size1 = 0;
if (itemIds != null) {
size = itemIds.size();
}
int allCount = size + size1;
if (allCount < 3 || allCount > 9) {
return AjaxResult.error("汰换数量在3~9个");
}
BigDecimal totalPrice = new BigDecimal(0);
//查询饰品列表
// 1.先查询宝箱记录里面的饰品
if(!itemIds.isEmpty()){
// 从我的背包中查询饰品总价值
List<UserPackSackDataVO> boxs = apiReplacementRecordMapper
.selectUserPackSack(user.getUserId().intValue(), itemIds);
for (int i = 0; i < boxs.size(); i++) {
BigDecimal bigDecimal = boxs.get(i).getOrnamentsPrice();
totalPrice = totalPrice.add(bigDecimal);
}
}
//累加装备总价值
String status = "8";
//更新记录的状态 updateStatusByIds
if(!itemIds.isEmpty()){
ttBoxRecordsMapper.updateStatusByIds(itemIds, status);
}
//获取最小汰换金额
BigDecimal minPrice = new BigDecimal(sysConfigService.selectConfigByKey("minExchangeAmount"));
// 汰换饰品合计总价不得低于
if (totalPrice.compareTo(minPrice) < 0) {
return AjaxResult.error("汰换饰品合计总价不得低于" + minPrice);
}
//获取饰品的id
Long id = getOId(totalPrice);
//获取饰品详情
TtOrnament ttOrnament = ttOrnamentMapper.selectOrnamentById(id);
//进行汰换的日志记录
TtReplacementRecord replacementRecord = new TtReplacementRecord();
replacementRecord.setUid(user.getUserId());
replacementRecord.setUname(user.getUserData().getNickName());
replacementRecord.setAwardOid(ttOrnament.getId());
replacementRecord.setAwardOname(ttOrnament.getName());
replacementRecord.setAwardOimg(ttOrnament.getImageUrl());
replacementRecord.setAwardOprice(ttOrnament.getUsePrice());
replacementRecord.setTime(DateUtils.getNowDate());
replacementRecord.setCreateTime(DateUtils.getNowDate());
apiReplacementRecordMapper.insertTtReplacementRecord(replacementRecord);
//将汰换的奖品加入背包
TtBoxRecords ttBoxRecords = new TtBoxRecords();
ttBoxRecords.setSource(TtboxRecordSource.REPLACEMENT.getCode());
ttBoxRecords.setUserId(user.getUserId().intValue());
ttBoxRecords.setHolderUserId(user.getUserId().intValue());
ttBoxRecords.setBoxName("汰换奖品");
ttBoxRecords.setOrnamentName(ttOrnament.getName());
ttBoxRecords.setImageUrl(ttOrnament.getImageUrl());
ttBoxRecords.setOrnamentId(ttOrnament.getId());
ttBoxRecords.setOrnamentsPrice(ttOrnament.getUsePrice());
ttBoxRecords.setStatus(0);
ttBoxRecords.setCreateTime(DateUtils.getNowDate());
ttBoxRecordsService.save(ttBoxRecords);
return AjaxResult.success(replacementRecord);
}
/**
* 随机产生饰品id
*
* @param bean
* @return
*/
public Long getOId(BigDecimal bean) {
List<TtOrnament> ornament = new ArrayList<>();
initArray(bean, ornament);
Random random = new Random();
int randomIndex = random.nextInt(ornament.size());
return ornament.get(randomIndex).getId();
}
/**
* 初始化 饰品列表
*
* @param bean
* @param ornaments
*/
private void initArray(BigDecimal bean, List<TtOrnament> ornaments) {
for (double[] range : percent) {
BigDecimal start = bean.multiply(BigDecimal.valueOf(range[0]));
BigDecimal end = bean.multiply(BigDecimal.valueOf(range[1]));
Map<String, BigDecimal> map = new HashMap<>();
map.put("start", start);
map.put("end", end);
int configQuantity = 1;
if (configQuantity > 0) {
List<TtOrnament> eligibleOrnaments = apiReplacementRecordMapper.findByPriceRange(map);
shuffleList(eligibleOrnaments);
int quantityToAdd = Math.min(configQuantity, eligibleOrnaments.size());
for (int i = 0; i < quantityToAdd; i++) {
ornaments.add(eligibleOrnaments.get(i));
}
}
}
}
/**
* 采用了 Fisher-Yates 随机洗牌算法,
*
* @param list
*/
private void shuffleList(List<TtOrnament> list) {
Random random = new Random();
int n = list.size();
for (int i = n - 1; i > 0; i--) {
int j = random.nextInt(i + 1);
TtOrnament temp = list.get(i);
list.set(i, list.get(j));
list.set(j, temp);
}
}
}

View File

@@ -0,0 +1,651 @@
package com.ruoyi.playingmethod.service.impl;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.baomidou.mybatisplus.extension.conditions.update.LambdaUpdateChainWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.admin.config.RedisConstants;
import com.ruoyi.admin.mapper.*;
import com.ruoyi.admin.service.TtBoxRecordsService;
import com.ruoyi.admin.service.TtOrnamentsLevelService;
import com.ruoyi.admin.service.TtRollUserService;
import com.ruoyi.admin.util.RandomUtils;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.redis.config.RedisLock;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.domain.common.constant.TtboxRecordSource;
import com.ruoyi.domain.common.constant.UserType;
import com.ruoyi.domain.common.constant.roll.RollGetPrizeWay;
import com.ruoyi.domain.common.constant.roll.RollStatus;
import com.ruoyi.domain.common.constant.roll.RollType;
import com.ruoyi.domain.dto.roll.GetRollOpenPrizeParam;
import com.ruoyi.domain.dto.roll.GetRollPlayersParam;
import com.ruoyi.domain.dto.roll.GetRollPrizePool;
import com.ruoyi.domain.entity.*;
import com.ruoyi.domain.entity.roll.*;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.*;
import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO;
import com.ruoyi.domain.vo.roll.RollJackpotOrnamentsVO;
import com.ruoyi.domain.vo.roll.RollUserPrizeVO;
import com.ruoyi.domain.vo.roll.RollUserVO;
import com.ruoyi.domain.vo.roll.SimpleRollOrnamentVO;
import com.ruoyi.framework.manager.AsyncManager;
import com.ruoyi.playingmethod.controller.ApiRollController.GetRollListParam;
import com.ruoyi.playingmethod.controller.ApiRollController.JoinRollParam;
import com.ruoyi.playingmethod.mapper.ApiRollMapper;
import com.ruoyi.playingmethod.service.ApiRollService;
import com.ruoyi.domain.vo.RollDetailsDataVO;
import com.ruoyi.domain.vo.RollListDataVO;
import com.ruoyi.thirdparty.common.service.ApiNoticeService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.*;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import static com.ruoyi.domain.common.constant.TtboxRecordStatus.IN_PACKSACK_ON;
@Service
@Slf4j
public class ApiRollServiceImpl extends ServiceImpl<TtRollMapper, TtRoll> implements ApiRollService {
private final ApiRollMapper apiRollMapper;
private final TtUserMapper userMapper;
private final TtOrnamentMapper ornamentsMapper;
private final TtBoxRecordsService boxRecordsService;
private final TtRollMapper rollMapper;
private final TtRollUserService rollUserService;
private final TtRollJackpotOrnamentsMapper rollJackpotOrnamentsMapper;
private final TtRechargeRecordMapper rechargeRecordMapper;
private final RedisLock redisLock;
public ApiRollServiceImpl(ApiRollMapper apiRollMapper,
TtUserMapper userMapper,
TtOrnamentMapper ornamentsMapper,
TtBoxRecordsService boxRecordsService,
TtRollMapper rollMapper,
TtRollUserService rollUserService,
TtRollJackpotOrnamentsMapper rollJackpotOrnamentsMapper,
TtRechargeRecordMapper rechargeRecordMapper,
RedisLock redisLock) {
this.apiRollMapper = apiRollMapper;
this.userMapper = userMapper;
this.ornamentsMapper = ornamentsMapper;
this.boxRecordsService = boxRecordsService;
this.rollMapper = rollMapper;
this.rollUserService = rollUserService;
this.rollJackpotOrnamentsMapper = rollJackpotOrnamentsMapper;
this.rechargeRecordMapper = rechargeRecordMapper;
this.redisLock = redisLock;
}
@Autowired
private TtRollUserMapper ttRollUserMapper;
@Autowired
private ApiNoticeService apiNoticeService;
@Autowired
private TtRollUserPrizeMapper ttRollUserPrizeMapper;
@Autowired
private TtRollJackpotMapper ttRollJackpotMapper;
@Autowired
private TtRollJackpotOrnamentsMapper ttRollJackpotOrnamentsMapper;
@Autowired
private TtRollMapper ttRollMapper;
@Autowired
private TtOrnamentMapper ttOrnamentMapper;
@Autowired
private TtBoxRecordsService ttBoxRecordsService;
@Autowired
private TtOrnamentsLevelMapper ttOrnamentsLevelMapper;
@Autowired
private TtOrnamentsLevelService ttOrnamentsLevelService;
@Autowired
private TtBoxRecordsMapper ttBoxRecordsMapper;
public R<TtRoll> joinRollCheck(TtRoll ttRoll, TtRollUser rollUser, TtUser player, JoinRollParam param) {
if (StringUtils.isNull(ttRoll)) return R.fail("当前Roll房已结束");
if (StringUtils.isNotNull(rollUser)) return R.fail("您当前已在该房间内,请勿重复加入房间!");
String password = ttRoll.getRollPassword();
if (StringUtils.isNotEmpty(password)) {
if (StringUtils.isEmpty(param.getRollPassword())) return R.fail("请输入密码!");
if (!password.equals(param.getRollPassword())) return R.fail("密码错误!");
}
// 检查加入条件:充值下限
BigDecimal minRecharge = ttRoll.getMinRecharge();
if (minRecharge.compareTo(BigDecimal.ZERO) > 0) {
BigDecimal rechargeTotalAmount = getRechargeTotalAmount(player.getUserId(), ttRoll.getRechargeStartTime());
if (minRecharge.compareTo(rechargeTotalAmount) > 0) return R.fail("未满足该房间的充值条件!");
}
return R.ok();
}
@Override
public R joinRoll(JoinRollParam param, TtUser player) {
// roll房
TtRoll ttRoll = new LambdaQueryChainWrapper<>(rollMapper)
.eq(TtRoll::getId, param.getRollId())
.eq(TtRoll::getRollStatus, "0")
.gt(TtRoll::getEndTime, DateUtils.getNowDate())
.one();
// roll房成员
TtRollUser rollUser = new LambdaQueryChainWrapper<>(rollUserService.getBaseMapper())
.eq(TtRollUser::getUserId, player.getUserId())
.eq(TtRollUser::getRollId, param.getRollId())
.one();
// 检查
R<TtRoll> check = joinRollCheck(ttRoll, rollUser, player, param);
if (!check.getCode().equals(200)) return check;
// 如果是主播房
if (ttRoll.getRollType().equals(RollType.ANCHOR.getCode())) {
TtUser anchor = new LambdaQueryChainWrapper<>(userMapper)
.eq(TtUser::getUserId, ttRoll.getUserId())
.eq(TtUser::getUserType, UserType.ANCHOR.getCode())
.eq(TtUser::getDelFlag, "0")
.one();
// 检查是否粉丝
Integer parentId = player.getParentId();
if (StringUtils.isNull(parentId) || !anchor.getUserId().equals(parentId)) {
return R.fail("您不是该主播的粉丝,请先加入主播粉丝团才可加入房间!");
}
}
// 加入房间
Boolean lock = false;
for (int i = 0; i < 2; i++) {
lock = redisLock.tryLock(RedisConstants.JOIN_ROLL_LOCK + param.getRollId(), 2L, 7L, TimeUnit.SECONDS);
if (!lock) {
try {
TimeUnit.SECONDS.sleep(1L);
} catch (Exception e) {
log.warn("try lock sleep warn");
} finally {
continue;
}
} else {
break;
}
}
if (lock) {
try {
List<TtRollUser> rollUserList = new LambdaQueryChainWrapper<>(rollUserService.getBaseMapper())
.eq(TtRollUser::getRollId, param.getRollId())
.list();
if (ttRoll.getPeopleNum() <= rollUserList.size()) {
return R.fail("roll房人满。");
}
TtRollUser ttRollUser = TtRollUser.builder()
.rollId(param.getRollId())
.userId(player.getUserId())
.userName(player.getUserName())
.nickName(player.getNickName())
.avatar(player.getAvatar())
.joinTime(DateUtils.getNowDate())
.build();
rollUserService.getBaseMapper().insert(ttRollUser);
return R.ok();
} catch (Exception e) {
e.printStackTrace();
return R.fail("加入失败,请稍后重试。");
} finally {
redisLock.unlock(RedisConstants.JOIN_ROLL_LOCK + param.getRollId());
}
}
redisLock.unlock(RedisConstants.JOIN_ROLL_LOCK + param.getRollId());
return R.fail("服务器繁忙。");
}
// roll房开奖
@Override
public R endROLL(Integer rollId) {
System.out.println("server roll房开奖" + rollId);
// 1 检查
TtRoll ttRoll = new LambdaQueryChainWrapper<>(rollMapper)
.eq(TtRoll::getId, rollId)
.eq(TtRoll::getDelFlag, "0")
.one();
if (ObjectUtil.isEmpty(ttRoll)) return R.fail("不存在的roll房。");
if(ttRoll.getRollStatus().equals("1")) return R.ok("已开奖,拒绝重复开奖");
// 用数据库乐观锁:将 rollStatus 从 0 改为 1原子操作防止并发重复开奖
boolean locked = new LambdaUpdateChainWrapper<>(rollMapper)
.eq(TtRoll::getId, rollId)
.eq(TtRoll::getRollStatus, "0")
.set(TtRoll::getRollStatus, "1")
.update();
if (!locked) {
return R.ok("已开奖,拒绝重复开奖");
}
// 重新查询final 变量供 lambda 使用
final TtRoll finalRoll = new LambdaQueryChainWrapper<>(rollMapper)
.eq(TtRoll::getId, rollId)
.eq(TtRoll::getDelFlag, "0")
.one();
ttRoll = finalRoll;
List<TtRollUser> list = new LambdaQueryChainWrapper<>(rollUserService.getBaseMapper())
.eq(TtRollUser::getRollId, rollId)
.list();
if (ObjectUtil.isEmpty(list) || list.isEmpty()) {
ttRoll.setRollStatus(RollStatus.END.getCode());
rollMapper.updateById(ttRoll);
return R.ok("roll房无人参与。");
}
// 2 分配系统指定,并返回剩余奖品
List<TtRollUser> rollUsers = new LambdaQueryChainWrapper<>(ttRollUserMapper)
.eq(TtRollUser::getRollId, ttRoll.getId())
.eq(TtRollUser::getGetPrizeWay, RollGetPrizeWay.SYS.getCode())
.list();
log.info("所有参与人员" + rollUsers);
Map<Long, TtRollJackpotOrnaments> surplusPrize = rollSurplusOrnaments(rollUsers, ttRoll);
// 3 过滤出未分配奖品的用户
LambdaQueryWrapper<TtRollUser> rollUserQuery = new LambdaQueryWrapper<>();
rollUserQuery
.eq(TtRollUser::getRollId, rollId)
.eq(TtRollUser::getStatus, "0");
if (!rollUsers.isEmpty()) {
List<Integer> rollUserIds = rollUsers.stream()
.map(TtRollUser::getId)
.collect(Collectors.toList());
rollUserQuery.notIn(TtRollUser::getId, rollUserIds);
}
List<TtRollUser> rollUserList = rollUserService.list(rollUserQuery);
// 4 打乱用户顺序
Collections.shuffle(rollUserList);
log.info("过滤系统指定以后" + rollUserList);
// 5 分配奖品、
List<TtBoxRecords> res = new ArrayList<>();
// 一人拿一件
boolean iGetIt = false;
for (TtRollUser rollUser : rollUserList) {
// if (iGetIt) continue;
Set<Map.Entry<Long, TtRollJackpotOrnaments>> entries = surplusPrize.entrySet();
for (Map.Entry<Long, TtRollJackpotOrnaments> entry : entries) {
Integer numb = entry.getValue().getOrnamentsNum();
if (numb <= 0) continue;
// 等级信息
TtOrnamentsLevel level = ttOrnamentsLevelService.getById(entry.getValue().getOrnamentLevelId());
TtBoxRecords boxRecords = TtBoxRecords.builder()
.rollId(rollId)
.createTime(new Timestamp(System.currentTimeMillis()))
.holderUserId(rollUser.getUserId())
.ornamentId(entry.getKey())
.ornamentsPrice(entry.getValue().getPrice())
.imageUrl(entry.getValue().getImgUrl())
.ornamentName(entry.getValue().getOrnamentName())
.ornamentLevelImg(level.getLevelImg())
.source(TtboxRecordSource.ROLL.getCode())
.userId(rollUser.getUserId())
.updateTime(new Timestamp(System.currentTimeMillis()))
.status(IN_PACKSACK_ON.getCode())
.build();
res.add(boxRecords);
log.info("{}获得{}", rollUser.getNickName(), entry.getValue().getOrnamentName());
// 扣减数量
TtRollJackpotOrnaments value = entry.getValue();
value.setOrnamentsNum(numb - 1);
surplusPrize.put(entry.getKey(), value);
break;
}
}
log.info("保存开奖结果:" + res);
boxRecordsService.saveBatch(res);
// ===== 新增Roll房开奖通知过滤机器人用户 userType=03=====
TtRoll finalTtRoll = ttRoll;
AsyncManager.me().run(() -> sendRollResultNotice(res, finalTtRoll));
// ===== 新增roll房开奖通知结束 =====
// 6 更新roll房状态
new LambdaUpdateChainWrapper<>(rollMapper)
.eq(TtRoll::getId, ttRoll.getId())
.set(TtRoll::getRollStatus, RollStatus.END.getCode())
.set(TtRoll::getUpdateTime, new Date())
.update();
System.out.println("server roll房开奖" + ttRoll.getId() + ttRoll.getRollName() + "成功");
return R.ok();
}
/**
* Roll房开奖结果通知过滤机器人用户
* 示例【roll房名字】 张三的开奖结果AK-47(120元)、蝴蝶刀(500元),共获得价值 620 元的饰品。
*/
private void sendRollResultNotice(List<TtBoxRecords> res, TtRoll ttRoll) {
try {
if (res.isEmpty()) return;
// 按用户分组饰品
Map<Integer, List<TtBoxRecords>> userItemsMap = res.stream()
.filter(r -> r.getHolderUserId() != null)
.collect(Collectors.groupingBy(TtBoxRecords::getHolderUserId));
if (userItemsMap.isEmpty()) return;
// 过滤机器人,只取真实用户
List<Integer> userIds = new ArrayList<>(userItemsMap.keySet());
List<TtUser> realUsers = new LambdaQueryChainWrapper<>(userMapper)
.in(TtUser::getUserId, userIds)
.ne(TtUser::getUserType, UserType.ROBOT_USER.getCode())
.list();
String rollName = ttRoll.getRollName();
for (TtUser user : realUsers) {
List<TtBoxRecords> items = userItemsMap.get(user.getUserId());
if (items == null || items.isEmpty()) continue;
// 拼接通知内容
StringBuilder contentBuilder = new StringBuilder();
contentBuilder.append("").append(rollName).append("")
.append(user.getNickName()).append("的开奖结果:");
BigDecimal totalPrice = BigDecimal.ZERO;
for (TtBoxRecords item : items) {
contentBuilder.append(item.getOrnamentName())
.append("(").append(item.getOrnamentsPrice()).append("元)、");
if (item.getOrnamentsPrice() != null) {
totalPrice = totalPrice.add(item.getOrnamentsPrice());
}
}
String contentStr = contentBuilder.toString();
if (contentStr.endsWith("")) {
contentStr = contentStr.substring(0, contentStr.length() - 1);
}
contentStr += ",共获得价值 " + totalPrice + " 元的饰品。";
apiNoticeService.sendNotice(Long.valueOf(user.getUserId()), "Roll房开奖通知", contentStr);
}
} catch (Exception e) {
log.error("Roll房开奖通知推送失败rollId={}, error={}", ttRoll.getId(), e.getMessage(), e);
}
}
//{ornamentId:TtRollJackpotOrnaments} TtRollJackpotOrnaments中有数量
public Map<Long, TtRollJackpotOrnaments> rollSurplusOrnaments(List<TtRollUser> rollUsers, TtRoll ttRoll) {
// 奖池所有物品
List<TtRollJackpotOrnaments> jackpotOrnaments = new LambdaQueryChainWrapper<>(rollJackpotOrnamentsMapper)
.eq(TtRollJackpotOrnaments::getJackpotId, ttRoll.getJackpotId())
.list();
HashMap<Long, TtRollJackpotOrnaments> AllPrizePool = new HashMap<>();
for (TtRollJackpotOrnaments i : jackpotOrnaments) {
AllPrizePool.put(i.getOrnamentsId(), i);
}
// 没有系统指定,直接返回奖池所有物品
if (rollUsers.isEmpty()) return AllPrizePool;
List<Integer> rollUserIds = rollUsers.stream()
.map(TtRollUser::getId)
.collect(Collectors.toList());
// 系统指定信息
List<RollUserPrizeVO> RollUserPrizeVOs = ttRollUserPrizeMapper.byRollUserIds(rollUserIds);
// 没有系统指定,直接返回奖池所有物品
if (RollUserPrizeVOs.isEmpty()) return AllPrizePool;
// 分配系统指定
List<TtBoxRecords> res = new ArrayList<>();
for (RollUserPrizeVO userPrize : RollUserPrizeVOs) {
// 分配奖品
for (int i = 0; i < userPrize.getNumber(); i++) {
TtBoxRecords boxRecords = TtBoxRecords.builder()
.rollId(ttRoll.getId())
.createTime(new Timestamp(System.currentTimeMillis()))
.holderUserId(userPrize.getUserId())
.ornamentsPrice(userPrize.getPrice())
.imageUrl(userPrize.getImgUrl())
.ornamentId(userPrize.getOrnamentId())
.source(TtboxRecordSource.ROLL.getCode())
.userId(userPrize.getUserId())
.updateTime(new Timestamp(System.currentTimeMillis()))
.status(IN_PACKSACK_ON.getCode())
.build();
res.add(boxRecords);
}
// 在所有奖品中减去当前用户获得的奖品
for (Long ornamentId : AllPrizePool.keySet()) {
if (ornamentId.equals(userPrize.getOrnamentId())) {
// 减去
TtRollJackpotOrnaments ornaments = AllPrizePool.get(ornamentId);
int i = ornaments.getOrnamentsNum() - userPrize.getNumber();
ornaments.setOrnamentsNum(Math.max(i, 0));
AllPrizePool.put(ornamentId, ornaments);
break;
}
}
}
// 保存已分配
boxRecordsService.saveBatch(res, 1);
return AllPrizePool;
}
@Override
public List<RollListDataVO> getRollList(GetRollListParam param) {
List<RollListDataVO> rollList = apiRollMapper.getRollList(param);
if (CollectionUtils.isEmpty(rollList)) {
return new ArrayList<>();
}
// 对数据进行处理
rollList.forEach(item -> {
// 补充奖池信息
TtRoll roll = this.getById(item.getId());
List<SimpleRollOrnamentVO> prizeList = ttRollJackpotOrnamentsMapper.rollShow(roll.getJackpotId());
item.setOrnamentsList(JSONUtil.toJsonStr(prizeList));
if (CollectionUtils.isNotEmpty(prizeList)) {
Integer ornamentsNumberOfRoll = ttRollJackpotOrnamentsMapper.ornamentsNumberOfRoll(prizeList.get(0).getJackpotId());
item.setOrnamentsNum(ornamentsNumberOfRoll);
}
if (!StringUtils.isBlank(item.getRollPassword())) {
item.setHasPW(true);
item.setRollPassword(null);
} else {
item.setHasPW(false);
}
});
return rollList;
}
@Override
public R<RollDetailsDataVO> getRollDetails(Integer rollId) {
TtRoll ttRoll = getById(rollId);
if (ObjectUtil.isEmpty(ttRoll)) return R.fail("不存在的roll房。");
RollDetailsDataVO data = apiRollMapper.getRollDetails(rollId);
data.setHasPW(!StringUtils.isBlank(ttRoll.getRollPassword()));
// 成员
// List<TtRollUser> list = new LambdaQueryChainWrapper<>(ttRollUserMapper)
// .eq(TtRollUser::getRollId, rollId)
// .list();
// List<SimpleRollUserVO> players = list.stream().map(item -> {
// SimpleRollUserVO playerVo = new SimpleRollUserVO();
// BeanUtil.copyProperties(item, playerVo);
// playerVo.setUserName(null);
// return playerVo;
// }).collect(Collectors.toList());
// data.setPlayerList(players);
// 奖池
List<SimpleRollOrnamentVO> prizeList = ttRollJackpotOrnamentsMapper.rollShow(ttRoll.getJackpotId());
data.setJackpotOrnamentsDataList(prizeList);
// 获奖名单
// List<TtBoxRecordsVO> boxRecords = ttBoxRecordsMapper.rollOpenPrize(rollId);
// data.setOpenPrizeList(boxRecords);
return R.ok(data);
}
@Override
public R<List<RollUserVO>> getRollPlayers(GetRollPlayersParam param) {
param.setLimit((param.getPage() - 1) * param.getSize());
List<RollUserVO> collect = ttRollUserMapper.pageByRollId(param);
return R.ok(collect);
}
@Override
public R<List<RollJackpotOrnamentsVO>> getRollPrizePool(GetRollPrizePool param) {
param.setLimit((param.getPage() - 1) * param.getSize());
TtRoll one = new LambdaQueryChainWrapper<>(ttRollMapper)
.eq(TtRoll::getId, param.getRollId())
.eq(TtRoll::getDelFlag, 0)
.one();
if (ObjectUtil.isNull(one)) return R.fail("不存在的roll房。");
param.setJackpotId(one.getJackpotId());
List<RollJackpotOrnamentsVO> list = ttRollJackpotOrnamentsMapper.listByRollId(param);
return R.ok(list);
}
@Override
public R<List<TtBoxRecordsVO>> getRollOpenPrize(GetRollOpenPrizeParam param) {
param.setLimit((param.getPage() - 1) * param.getSize());
// 获奖名单
List<TtBoxRecordsVO> boxRecords = ttBoxRecordsMapper.rollOpenPrize(param);
return R.ok(boxRecords);
}
private TtBoxRecords addBoxRecordsData(Integer rollId, Long ornamentsId, Integer userId, TtRollJackpotOrnaments rollJackpotOrnaments) {
TtOrnament ttOrnament = new LambdaQueryChainWrapper<>(ornamentsMapper).eq(TtOrnament::getId, ornamentsId).one();
TtBoxRecords boxRecords = TtBoxRecords.builder().build();
boxRecords.setUserId(userId);
boxRecords.setOrnamentId(ornamentsId);
boxRecords.setOrnamentsPrice(ttOrnament.getUsePrice());
boxRecords.setOrnamentsLevelId(rollJackpotOrnaments.getOrnamentLevelId());
boxRecords.setStatus(IN_PACKSACK_ON.getCode());
boxRecords.setCreateTime(DateUtils.getNowDate());
boxRecords.setSource(TtboxRecordSource.ROLL.getCode());
boxRecords.setRollId(rollId);
boxRecords.setHolderUserId(userId);
return boxRecords;
}
private TtRollJackpotOrnaments getRollJackpotOrnaments(Long ornamentsId, Map<Long, TtRollJackpotOrnaments> map, Integer jackpotId) {
TtRollJackpotOrnaments ttRollJackpotOrnaments = map.get(ornamentsId);
if (ttRollJackpotOrnaments == null) {
ttRollJackpotOrnaments = new LambdaQueryChainWrapper<>(rollJackpotOrnamentsMapper).eq(TtRollJackpotOrnaments::getJackpotId, jackpotId)
.eq(TtRollJackpotOrnaments::getOrnamentsId, ornamentsId).one();
map.put(ornamentsId, ttRollJackpotOrnaments);
}
return ttRollJackpotOrnaments;
}
private List<Long> getSurplusOrnamentsList(Integer rollId, Integer jackpotId) {
// roll房奖池
List<TtRollJackpotOrnaments> ornamentsList = new LambdaQueryChainWrapper<>(rollJackpotOrnamentsMapper)
.eq(TtRollJackpotOrnaments::getJackpotId, jackpotId)
.list();
Map<Long, Integer> data = new HashMap<>();
for (TtRollJackpotOrnaments ornaments : ornamentsList) {
data.put(ornaments.getOrnamentsId(), ornaments.getOrnamentsNum());
}
// 打乱奖池
List<Long> surplusOrnamentsList = RandomUtils.toList(data);
// 系统指定的获奖者
List<TtRollUser> allocatedRollUserList = new LambdaQueryChainWrapper<>(rollUserService.getBaseMapper())
.eq(TtRollUser::getRollId, rollId)
.eq(TtRollUser::getStatus, "2")
.list();
List<Long> allocated = allocatedRollUserList.stream().map(TtRollUser::getOrnamentsId).collect(Collectors.toList());
for (Long i : allocated) {
Iterator<Long> iterator = surplusOrnamentsList.iterator();
while (iterator.hasNext()) {
Long next = iterator.next();
if (Objects.equals(next, i)) {
iterator.remove();
break;
}
}
}
return surplusOrnamentsList;
}
private BigDecimal getRechargeTotalAmount(Integer userId, Date rechargeStartTime) {
LambdaQueryChainWrapper<TtRechargeRecord> wrapper = new LambdaQueryChainWrapper<>(rechargeRecordMapper).eq(TtRechargeRecord::getUserId, userId);
if (StringUtils.isNotNull(rechargeStartTime)) wrapper.gt(TtRechargeRecord::getCreateTime, rechargeStartTime);
return wrapper.list().stream().map(TtRechargeRecord::getAmountActuallyPaid).reduce(BigDecimal.ZERO, BigDecimal::add);
}
}

View File

@@ -0,0 +1,100 @@
package com.ruoyi.playingmethod.service.impl;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.domain.dto.upgrade.UpgradeCondition;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.*;
import com.ruoyi.playingmethod.mapper.ApiUpgradeRecordMapper;
import com.ruoyi.playingmethod.service.ApiUpgradeRecordService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
@Service
@Slf4j
public class ApiUpgradeRecordServiceImpl extends ServiceImpl<ApiUpgradeRecordMapper, TtUpgradeRecord> implements ApiUpgradeRecordService {
@Autowired
TtUserService userService;
@Override
public R historyDetail(UpgradeCondition param) {
Page<TtUpgradeRecord> pageInfo = new Page<>(param.getPage(), param.getSize());
pageInfo.setOptimizeCountSql(false);
LambdaQueryWrapper<TtUpgradeRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(ObjectUtil.isNotNull(param.getUpgradeRecordId()),TtUpgradeRecord::getId,param.getUpgradeRecordId());
wrapper.eq(ObjectUtil.isNotNull(param.getOrnamentId()),TtUpgradeRecord::getTargetOrnamentId,param.getOrnamentId());
wrapper.eq(ObjectUtil.isNotNull(param.getUserType()),TtUpgradeRecord::getUserType,param.getUserType());
wrapper.eq(ObjectUtil.isNotNull(param.getUserId()),TtUpgradeRecord::getUserId,param.getUserId());
wrapper.orderByDesc(TtUpgradeRecord::getOpenTime);
pageInfo = this.page(pageInfo, wrapper);
if (pageInfo.getRecords().size() == 0){
return R.ok(pageInfo);
}
Set<Integer> userIds = pageInfo.getRecords().stream().map(TtUpgradeRecord::getUserId).collect(Collectors.toSet());
//<2F><>װ<EFBFBD>û<EFBFBD>ͷ<EFBFBD><CDB7>
List<TtUser> list = userService.list(Wrappers.lambdaQuery(TtUser.class)
.in(TtUser::getUserId, userIds));
//ͷ<><CDB7>map
Map<Integer, String> avatarMap = list.stream().collect(Collectors.toMap(TtUser::getUserId, TtUser::getAvatar));
pageInfo.getRecords().forEach(r->{
if (r.getUserId() !=null){
r.setUserAvatar(avatarMap.get(r.getUserId()));
}
});
return R.ok(pageInfo);
}
@Override
public R tenTopDetail(Boolean isVictory) {
Page<TtUpgradeRecord> pageInfo = new Page<>(1, 10);
pageInfo.setOptimizeCountSql(false);
LambdaQueryWrapper<TtUpgradeRecord> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(isVictory != null, TtUpgradeRecord::getIsVictory, isVictory);
wrapper.orderByDesc(TtUpgradeRecord::getTargetOrnamentPrice);
pageInfo = this.page(pageInfo, wrapper);
if (pageInfo.getRecords().size() == 0){
return R.ok(pageInfo);
}
Set<Integer> userIds = pageInfo.getRecords().stream().map(TtUpgradeRecord::getUserId).collect(Collectors.toSet());
List<TtUser> list = userService.list(Wrappers.lambdaQuery(TtUser.class)
.in(TtUser::getUserId, userIds));
Map<Integer, String> avatarMap = list.stream().collect(Collectors.toMap(TtUser::getUserId, TtUser::getAvatar));
pageInfo.getRecords().forEach(r->{
if (r.getUserId() !=null){
r.setUserAvatar(avatarMap.get(r.getUserId()));
}
});
return R.ok(pageInfo);
}
}

View File

@@ -0,0 +1,18 @@
package com.ruoyi.playingmethod.service.impl;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.domain.entity.TtUserBlendErcash;
import com.ruoyi.playingmethod.mapper.ApiTtUserBlendErcashMapper;
import com.ruoyi.playingmethod.service.ApiUserBlendErcashService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
@Service
@Slf4j
public class ApiUserBlendErcashServiceImpl extends ServiceImpl<ApiTtUserBlendErcashMapper, TtUserBlendErcash> implements ApiUserBlendErcashService {
}

View File

@@ -0,0 +1,147 @@
package com.ruoyi.playingmethod.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.admin.mapper.TtWelfareMonthlyRechargesMapper;
import com.ruoyi.admin.service.TtBoxRecordsService;
import com.ruoyi.admin.service.TtBoxService;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.admin.util.core.fight.LotteryMachine;
import com.ruoyi.domain.common.constant.TtboxRecordSource;
import com.ruoyi.domain.common.constant.TtboxRecordStatus;
import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.TtBox;
import com.ruoyi.domain.other.TtOrnamentsA;
import com.ruoyi.domain.other.TtWelfareMonthlyRecharges;
import com.ruoyi.domain.vo.OpenBoxVO;
import com.ruoyi.playingmethod.mapper.ApiBindBoxMapper;
import com.ruoyi.playingmethod.mapper.ApiWelfareMonthlyRechargesMapper;
import com.ruoyi.playingmethod.model.ApiWelfare;
import com.ruoyi.playingmethod.model.ApiWelfareRecord;
import com.ruoyi.playingmethod.model.vo.ApiWelfareMonthlyRechargesVO;
import com.ruoyi.playingmethod.service.ApiWelfareMonthlyRechargesService;
import com.ruoyi.playingmethod.utils.customException.OrnamentNullException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@Service
public class ApiWelfareMonthlyRechargesServiceImpl implements ApiWelfareMonthlyRechargesService {
@Autowired
private ApiWelfareMonthlyRechargesMapper apiWelfareMonthlyRechargesMapper;
@Autowired
private TtUserService ttUserService;
@Autowired
private TtBoxService ttBoxService;
@Autowired
private LotteryMachine lotteryMachine;
@Autowired
private ApiBindBoxMapper bindBoxMapper;
@Autowired
private TtBoxRecordsService boxRecordsService;
@Autowired
private TtWelfareMonthlyRechargesMapper ttWelfareMonthlyRechargesMapper;
@Override
public List<ApiWelfareMonthlyRechargesVO> getWelfareList(Long userId) {
List<ApiWelfareMonthlyRechargesVO> apiWelfareMonthlyRechargesVOS =
apiWelfareMonthlyRechargesMapper.getWelfareList(userId);
for (ApiWelfareMonthlyRechargesVO apiWelfareMonthlyRechargesVO : apiWelfareMonthlyRechargesVOS) {
// 检查领取条件
// BigDecimal rechargeRecordsOfMonth = apiWelfareMonthlyRechargesMapper.selectRechargeRecordsOfMonth(userId);
// if (rechargeRecordsOfMonth.compareTo(apiWelfareMonthlyRechargesVO.getAmount()) >= 0) {
// apiWelfareMonthlyRechargesVO.setEligible("1");
// } else {
// apiWelfareMonthlyRechargesVO.setEligible("0");
// }
if (checkEligible(apiWelfareMonthlyRechargesVO.getWelfareId(), userId))
apiWelfareMonthlyRechargesVO.setEligible("1");
else
apiWelfareMonthlyRechargesVO.setEligible("0");
// 检查是否领取
boolean isClaimed = apiWelfareMonthlyRechargesMapper.checkClaimed(
apiWelfareMonthlyRechargesVO.getWelfareId(),
userId,
"2");
apiWelfareMonthlyRechargesVO.setClaimStatus(isClaimed ? "1" : "0");
}
return apiWelfareMonthlyRechargesVOS;
}
@Override
public OpenBoxVO claimWelfare(Integer welfareId, Long userId) {
// 1.获取福利对应的宝箱信息
Integer boxId = apiWelfareMonthlyRechargesMapper.getBoxIdByWelfareId(welfareId);
TtUser ttUser = ttUserService.getById(userId);
TtBox ttBox = ttBoxService.getById(boxId);
// 2.抽奖
String ornamentId = lotteryMachine.singleLottery(ttUser, ttBox);
if (Objects.isNull(ornamentId)) {
throw new OrnamentNullException("福利开箱未抽中");
}
// 3.获取宝箱详细信息
TtOrnamentsA ornamentsData = bindBoxMapper.ornamentsInfo(boxId, ornamentId);
if (Objects.isNull(ornamentsData)) {
throw new OrnamentNullException("福利开箱未抽中");
}
// 4.构建并存储开箱记录
TtBoxRecords ttBoxRecords = new TtBoxRecords();
ttBoxRecords.setUserId(ttUser.getUserId());
ttBoxRecords.setBoxId(ttBox.getBoxId());
ttBoxRecords.setBoxName(ttBox.getBoxName());
ttBoxRecords.setBoxPrice(ttBox.getPrice());
ttBoxRecords.setOrnamentId(Long.valueOf(ornamentId));
ttBoxRecords.setOrnamentName(ornamentsData.getName());
ttBoxRecords.setImageUrl(ornamentsData.getImageUrl());
ttBoxRecords.setOrnamentsPrice(ornamentsData.getUsePrice());
ttBoxRecords.setOrnamentsLevelId(ornamentsData.getOrnamentsLevelId());
ttBoxRecords.setOrnamentLevelImg(ornamentsData.getLevelImg());
ttBoxRecords.setHolderUserId(ttUser.getUserId());
ttBoxRecords.setSource(TtboxRecordSource.BLIND_BOX.getCode());
ttBoxRecords.setStatus(TtboxRecordStatus.IN_PACKSACK_ON.getCode());
ttBoxRecords.setCreateTime(new Timestamp(System.currentTimeMillis()));
boxRecordsService.save(ttBoxRecords);
// 5.存储领取记录
ApiWelfareRecord apiWelfareRecord = new ApiWelfareRecord();
apiWelfareRecord.setWelfareId(welfareId);
apiWelfareRecord.setUserId(userId);
apiWelfareRecord.setType("2");
apiWelfareRecord.setCreateTime(new Date());
apiWelfareMonthlyRechargesMapper.saveClaimWelfareRecord(apiWelfareRecord);
// 6.返回前端数据
OpenBoxVO openBoxVO = new OpenBoxVO();
BeanUtil.copyProperties(ttBoxRecords, openBoxVO);
openBoxVO.setUsePrice(openBoxVO.getOrnamentsPrice());
openBoxVO.setLevelImg(ttBoxRecords.getOrnamentLevelImg());
return openBoxVO;
}
@Override
public boolean checkEligible(Integer welfareId, Long userId) {
BigDecimal rechargeRecordsOfMonth = apiWelfareMonthlyRechargesMapper.selectRechargeRecordsOfMonth(userId);
if (Objects.isNull(rechargeRecordsOfMonth))
return false;
TtWelfareMonthlyRecharges ttWelfareMonthlyRecharges = ttWelfareMonthlyRechargesMapper.selectTtWelfareMonthlyRechargesById(welfareId);
TtUser ttUser = ttUserService.getById(userId);
boolean isRechargeAmountSufficient = rechargeRecordsOfMonth.compareTo(ttWelfareMonthlyRecharges.getAmount()) >= 0;
boolean isVipLevelSufficient = ttUser.getVipLevel() >= ttWelfareMonthlyRecharges.getVipLevel();
return isRechargeAmountSufficient && isVipLevelSufficient;
}
@Override
public boolean checkClaimed(Integer welfareId, Long userId) {
return apiWelfareMonthlyRechargesMapper.checkClaimed(welfareId, userId, "2");
}
}

View File

@@ -0,0 +1,141 @@
package com.ruoyi.playingmethod.service.impl;
import cn.hutool.core.bean.BeanUtil;
import com.ruoyi.admin.mapper.TtWelfareMapper;
import com.ruoyi.admin.service.TtBoxRecordsService;
import com.ruoyi.admin.service.TtBoxService;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.admin.util.core.fight.LotteryMachine;
import com.ruoyi.domain.common.constant.TtboxRecordSource;
import com.ruoyi.domain.common.constant.TtboxRecordStatus;
import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.TtBox;
import com.ruoyi.domain.other.TtOrnamentsA;
import com.ruoyi.domain.other.TtWelfare;
import com.ruoyi.domain.vo.OpenBoxVO;
import com.ruoyi.playingmethod.mapper.ApiBindBoxMapper;
import com.ruoyi.playingmethod.mapper.ApiWelfareMapper;
import com.ruoyi.playingmethod.model.ApiWelfare;
import com.ruoyi.playingmethod.model.ApiWelfareRecord;
import com.ruoyi.playingmethod.service.ApiWelfareService;
import com.ruoyi.playingmethod.utils.customException.OrnamentNullException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.util.Date;
import java.util.List;
import java.util.Objects;
@Service
public class ApiWelfareServiceImpl implements ApiWelfareService {
@Autowired
private ApiWelfareMapper apiWelfareMapper;
@Autowired
private TtUserService ttUserService;
@Autowired
private TtBoxService ttBoxService;
@Autowired
private LotteryMachine lotteryMachine;
@Autowired
private ApiBindBoxMapper bindBoxMapper;
@Autowired
private TtBoxRecordsService boxRecordsService;
@Autowired
private TtWelfareMapper ttWelfareMapper;
@Override
public List<ApiWelfare> getWelfareList(Long userId) {
List<ApiWelfare> welfareList = apiWelfareMapper.getWelfareList(userId);
for (ApiWelfare apiWelfare : welfareList) {
if ("1".equals(apiWelfare.getType())) {
boolean isEligible = checkVipUpgradeEligibility(userId, apiWelfare.getVipLevel());
apiWelfare.setEligible(isEligible ? "1" : "0");
boolean isClaimed = apiWelfareMapper.checkClaimed(apiWelfare.getWelfareId(), userId, "1");
apiWelfare.setClaimStatus(isClaimed ? "1" : "0");
}
}
return welfareList;
}
@Transactional
@Override
public OpenBoxVO claimWelfare(Integer welfareId, Long userId) {
// 1.获取福利对应的宝箱信息
Integer boxId = apiWelfareMapper.getBoxIdByWelfareId(welfareId);
TtUser ttUser = ttUserService.getById(userId);
TtBox ttBox = ttBoxService.getById(boxId);
// 2.抽奖
String ornamentId = lotteryMachine.singleLottery(ttUser, ttBox);
if (Objects.isNull(ornamentId)) {
throw new OrnamentNullException("福利开箱未抽中");
}
// 3.获取宝箱详细信息
TtOrnamentsA ornamentsData = bindBoxMapper.ornamentsInfo(boxId, ornamentId);
if (Objects.isNull(ornamentsData)) {
throw new OrnamentNullException("福利开箱未抽中");
}
// 4.构建并存储开箱记录
TtBoxRecords ttBoxRecords = new TtBoxRecords();
ttBoxRecords.setUserId(ttUser.getUserId());
ttBoxRecords.setBoxId(ttBox.getBoxId());
ttBoxRecords.setBoxName(ttBox.getBoxName());
ttBoxRecords.setBoxPrice(ttBox.getPrice());
ttBoxRecords.setOrnamentId(Long.valueOf(ornamentId));
ttBoxRecords.setOrnamentName(ornamentsData.getName());
ttBoxRecords.setImageUrl(ornamentsData.getImageUrl());
ttBoxRecords.setOrnamentsPrice(ornamentsData.getUsePrice());
ttBoxRecords.setOrnamentsLevelId(ornamentsData.getOrnamentsLevelId());
ttBoxRecords.setOrnamentLevelImg(ornamentsData.getLevelImg());
ttBoxRecords.setHolderUserId(ttUser.getUserId());
ttBoxRecords.setSource(TtboxRecordSource.BLIND_BOX.getCode());
ttBoxRecords.setStatus(TtboxRecordStatus.IN_PACKSACK_ON.getCode());
ttBoxRecords.setCreateTime(new Timestamp(System.currentTimeMillis()));
boxRecordsService.save(ttBoxRecords);
// 5.存储领取记录
ApiWelfareRecord apiWelfareRecord = new ApiWelfareRecord();
apiWelfareRecord.setWelfareId(welfareId);
apiWelfareRecord.setUserId(userId);
apiWelfareRecord.setType("1");
apiWelfareRecord.setCreateTime(new Date());
apiWelfareMapper.saveClaimWelfareRecord(apiWelfareRecord);
// 6.返回前端数据
OpenBoxVO openBoxVO = new OpenBoxVO();
BeanUtil.copyProperties(ttBoxRecords, openBoxVO);
openBoxVO.setUsePrice(openBoxVO.getOrnamentsPrice());
openBoxVO.setLevelImg(ttBoxRecords.getOrnamentLevelImg());
return openBoxVO;
}
@Override
public boolean checkNotEligible(Integer welfareId, Long userId) {
TtWelfare ttWelfare = ttWelfareMapper.selectTtWelfareByWelfareId(welfareId);
if ("1".equals(ttWelfare.getType())) {
return !checkVipUpgradeEligibility(userId, ttWelfare.getVipLevel());
}
return false;
}
@Override
public boolean checkClaimed(Integer welfareId, Long userId) {
return apiWelfareMapper.checkClaimed(welfareId, userId, "1");
}
/**
* 检查是否具备VIP等级福利领取条件
*/
private boolean checkVipUpgradeEligibility(Long userId, Integer vipLevel) {
TtUser ttUser = ttUserService.getById(userId);
return ttUser.getVipLevel() >= vipLevel;
}
}

View File

@@ -0,0 +1,10 @@
package com.ruoyi.playingmethod.utils.Constant;
public enum OPPS {
// 展示爆率
OPPS,
// 真实爆率
REAL_OPPS,
// 主播爆率
ANCHOR_ODDS;
}

View File

@@ -0,0 +1,4 @@
package com.ruoyi.playingmethod.utils.Constant;
public enum RedisLockKey {
}

View File

@@ -0,0 +1,7 @@
package com.ruoyi.playingmethod.utils.customException;
public class OrnamentNullException extends RuntimeException {
public OrnamentNullException(String msg){
super(msg);
}
}

View File

@@ -0,0 +1,96 @@
package com.ruoyi.playingmethod.websocket;
import com.ruoyi.playingmethod.websocket.entity.SessionData;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.util.Collection;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
public class WebSocketUsers {
private static final Logger LOGGER = LoggerFactory.getLogger(WebSocketUsers.class);
private static Map<String, SessionData> USERS = new ConcurrentHashMap<String, SessionData>();
public static void put(String key, SessionData sessionData) {
USERS.put(key, sessionData);
}
public static boolean remove(SessionData sessionData) {
String key = null;
boolean flag = USERS.containsValue(sessionData);
if (flag) {
Set<Map.Entry<String, SessionData>> entries = USERS.entrySet();
for (Map.Entry<String, SessionData> entry : entries) {
SessionData value = entry.getValue();
if (value.getSession().equals(sessionData.getSession())) {
key = entry.getKey();
break;
}
}
} else {
return true;
}
return remove(key);
}
public static boolean remove(String key) {
LOGGER.info("\n 正在移出用户 - {}", key);
SessionData remove = USERS.remove(key);
if (remove.getSession() != null) {
boolean containsValue = USERS.containsValue(remove);
LOGGER.info("\n 移出结果 - {}", containsValue ? "失败" : "成功");
LOGGER.info("\n 当前人数 - {}", WebSocketUsers.getUsers().size());
return containsValue;
} else {
return true;
}
}
public static Map<String, SessionData> getUsers() {
return USERS;
}
public static void sendMessageToUsersByText(String message) {
Collection<SessionData> values = USERS.values();
for (SessionData value : values) {
try {
value.getSession().getBasicRemote().sendText(message);
} catch (IOException e) {
//LOGGER.error("\n[发送消息异常]", e);
LOGGER.error("\n[发送消息异常]");
}
}
}
public static void sendMessageToUserByText(Integer userId, String message) {
Collection<SessionData> values = USERS.values();
if (userId == 0) {
for (SessionData value : values) {
if (value.getSession() != null && Objects.equals(value.getUserId(), userId)) {
try {
value.getSession().getBasicRemote().sendText(message);
} catch (IOException e) {
LOGGER.error("\n[发送消息异常]", e);
}
}
}
} else {
for (SessionData value : values) {
if (value.getSession() != null && Objects.equals(value.getUserId(), userId)) {
try {
value.getSession().getBasicRemote().sendText(message);
return;
} catch (IOException e) {
LOGGER.error("\n[发送消息异常]", e);
return;
}
}
}
}
}
}

View File

@@ -0,0 +1,233 @@
package com.ruoyi.playingmethod.websocket;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.ruoyi.admin.mapper.TtUserMapper;
import com.ruoyi.admin.service.TtBoxService;
import com.ruoyi.admin.service.TtUserService;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.domain.common.constant.TtboxRecordSource;
import com.ruoyi.domain.common.constant.TtboxRecordStatus;
import com.ruoyi.domain.common.constant.sys.UserStatus;
import com.ruoyi.domain.dto.boxRecords.queryCondition;
import com.ruoyi.domain.entity.sys.TtUser;
import com.ruoyi.domain.other.TtBox;
import com.ruoyi.domain.vo.boxRecords.TtBoxRecordsVO;
import com.ruoyi.playingmethod.service.ApiBoxRecordsService;
import com.ruoyi.playingmethod.websocket.constant.SMsgKey;
import com.ruoyi.playingmethod.websocket.util.WsResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
/**
* 绑定箱子
*/
@Slf4j
@Component
@ServerEndpoint("/ws/bindBox/{boxId}/{userId}")
public class WsBindBox {
// 用来记录当前连接数的变量
private static volatile int onlineCount = 0;
// concurrent包的线程安全Set用来存放每个客户端对应的MyWebSocket对象
private static ConcurrentHashMap<String, WsBindBox> allRoomUserMap = new ConcurrentHashMap<>();
// 与某个客户端的连接会话,需要通过它来与客户端进行数据收发
private Session session;
private static ApiBoxRecordsService apiBoxRecordsService;
private static TtBoxService boxService;
private static TtUserService userService;
private static TtUserMapper userMapper;
private Integer userId = null;
private Integer boxId = null;
private String key = "";
@Autowired
public void ttFightService(ApiBoxRecordsService apiBoxRecordsService){
WsBindBox.apiBoxRecordsService = apiBoxRecordsService;
}
@Autowired
public void ttFightService(TtBoxService boxService){
WsBindBox.boxService = boxService;
}
@Autowired
public void ttFightService(TtUserService userService){
WsBindBox.userService = userService;
}
@Autowired
public void ttFightService(TtUserMapper userMapper){
WsBindBox.userMapper = userMapper;
}
@OnOpen
public void onOpen(Session session,
@PathParam("boxId") Integer boxId,
@PathParam("userId") Integer userId) throws IOException {
// 更新连接池
addBoxRoomUser(userId, boxId, session);
// 用户信息
TtUser player = new LambdaQueryChainWrapper<>(userMapper)
.eq(TtUser::getUserId, userId)
.eq(TtUser::getStatus, UserStatus.NORMAL.getCode())
.eq(TtUser::getDelFlag, 0)
.one();
// 检查连接
R check = connectCheck(player, boxId);
if (!check.getCode().equals(200)) {
session.getBasicRemote().sendText(check.getMsg());
session.close();
return;
}
log.debug("/ws/bindBox > > onOpen");
log.info("用户{}进入盲盒游戏房间,在线人数{}", userId, WsBindBox.onlineCount);
sendMessage("用户" + userId + "进入盲盒游戏房间,在线人数" + WsBindBox.onlineCount);
// 首次连接获取最新的一组盲盒开箱数据
List<Integer> sources = Arrays.asList(TtboxRecordSource.BLIND_BOX.getCode());
List<Integer> status = Arrays.asList(
TtboxRecordStatus.IN_PACKSACK_ON.getCode(),
TtboxRecordStatus.DELIVERY_YET.getCode(),
TtboxRecordStatus.APPLY_DELIVERY.getCode(),
TtboxRecordStatus.RESOLVE.getCode());
queryCondition param = queryCondition.builder()
.boxId(boxId)
// .userType(player.getUserType())
.source(sources)
.status(status)
.orderByFie(0)
.page(1)
.size(10)
.build();
List<TtBoxRecordsVO> ttBoxRecordsVOS = apiBoxRecordsService.byCondition(param);
sendMessage(WsResult.ok(SMsgKey.Blind_Box_Init_Data.name(),ttBoxRecordsVOS,"初始化历史开箱记录"));
}
@OnClose
public void onClose(Session session) {
removeRoomUser();
log.info("关闭连接,正常在线人数:" + WsBindBox.onlineCount);
}
@OnMessage
public void onMessage(String message, Session session) {
log.info("收到消息");
}
@OnError
public void onError(Session session, Throwable exception) throws Exception {
log.info("出现错误");
}
/**
* 连接检查
*/
private R connectCheck(TtUser player, Integer boxId) {
if (ObjectUtil.isNotEmpty(WsFightRoom.allRoomUserMap.get(player.getUserId() + "_" + boxId))) {
R.fail("用户" + player.getUserId() + "已经连接宝箱房间" + boxId + "。请勿重复调用。");
}
TtBox box = boxService.getById(boxId);
if (ObjectUtil.isEmpty(box)) {
return R.fail("不存在的宝箱,id" + boxId);
}
return R.ok();
}
/**
* 更新在线人数
*/
private int addOnlineCount(int number) {
synchronized (this){
WsBindBox.onlineCount += number;
return WsBindBox.onlineCount;
}
}
/**
* 全局广播
*/
public static void broadcast(Object message) {
// ObjectMapper objectMapper = new ObjectMapper();
String msg = JSON.toJSONString(message);
Collection<WsBindBox> wss = WsBindBox.allRoomUserMap.values();
for (WsBindBox ws : wss){
try {
ws.session.getBasicRemote().sendText(msg);
} catch (IOException e) {
log.warn("WS大厅广播消息异常。错误信息{}", msg);
}
}
}
/**
* 房间广播
*/
public static void broadcastToBoxRoom(Integer boxId, Object message) {
String msg = JSON.toJSONString(message);
Collection<WsBindBox> wslist = WsBindBox.allRoomUserMap.values();
for (WsBindBox ws : wslist) {
try {
if (!ws.boxId.equals(boxId)) continue;
// log.info(String.valueOf(ws.session.isOpen()));
if (!ws.session.isOpen()) continue;
RemoteEndpoint.Basic basicRemote = ws.session.getBasicRemote();
basicRemote.sendText(msg);
} catch (IOException e) {
log.warn("WS推送广播给{}_{}消息异常。", ws.userId, ws.boxId);
}
}
}
/**
* 实现服务器主动推送
*/
public void sendMessage(Object message) {
try {
this.session.getBasicRemote().sendText(JSON.toJSONString(message));
} catch (IOException e) {
log.warn("服务器推送消息异常。");
}
}
/**
* 添加连接用户
*/
private WsBindBox addBoxRoomUser(Integer userId, Integer boxId, Session session) {
addOnlineCount(1);
this.userId = userId;
this.boxId = boxId;
this.key = userId + "_" + boxId;
this.session = session;
WsBindBox.allRoomUserMap.put(key, this);
return this;
}
/**
* 移除连接用户
*/
private WsBindBox removeRoomUser(){
addOnlineCount(-1);
WsBindBox.allRoomUserMap.remove(this.key);
return this;
}
}

View File

@@ -0,0 +1,290 @@
package com.ruoyi.playingmethod.websocket;
import cn.hutool.core.util.ObjectUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.ruoyi.admin.mapper.TtBoxMapper;
import com.ruoyi.domain.entity.fight.TtFight;
import com.ruoyi.domain.other.TtBox;
import com.ruoyi.domain.vo.fight.FightBoxVO;
import com.ruoyi.playingmethod.service.ApiFightService;
import com.ruoyi.playingmethod.websocket.constant.SMsgKey;
import com.ruoyi.playingmethod.websocket.util.WsResult;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import jakarta.websocket.OnClose;
import jakarta.websocket.OnError;
import jakarta.websocket.OnMessage;
import jakarta.websocket.OnOpen;
import jakarta.websocket.Session;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.stream.Collectors;
/**
* 对战大厅
*/
@Slf4j
@Component
@ServerEndpoint("/ws/fight/hall/{userId}")
public class WsFightHall {
// 用来记录当前连接数的变量
private static int onlineCount = 0;
public static synchronized void increaseOnlineCount() {
onlineCount++;
}
public static synchronized void decreaseOnlineCount() {
if (onlineCount > 0) {
onlineCount--;
}
}
// concurrent包的线程安全Set用来存放每个客户端对应的MyWebSocket对象
private static ConcurrentHashMap<String, WsFightHall> allHallUserMap = new ConcurrentHashMap<>();
// 与某个客户端的连接会话,需要通过它来与客户端进行数据收发
private Session session;
private static ApiFightService apiFightService;
private String userId = "";
@Autowired
public void ttFightService(ApiFightService apiFightService) {
WsFightHall.apiFightService = apiFightService;
}
private static TtBoxMapper ttBoxMapper;
@Autowired
public void ttBoxMapper(TtBoxMapper ttBoxMapper) {
WsFightHall.ttBoxMapper = ttBoxMapper;
}
@OnOpen
public void onOpen(Session session, @PathParam("userId") String userId) throws IOException {
// 获取请求参数
Map<String, List<String>> parameters = session.getRequestParameterMap();
int pageNum = 1;
int pageSize = 10;
String status = null;
String model = null;
BigDecimal boxPriceTotalMin = null;
BigDecimal boxPriceTotalMax = null;
List<String> pageNums = parameters.get("pageNum");
if (!Objects.isNull(pageNums) && !pageNums.isEmpty()) {
pageNum = Integer.parseInt(pageNums.get(0));
}
List<String> pageSizes = parameters.get("pageSize");
if (!Objects.isNull(pageSizes) && !pageSizes.isEmpty()) {
pageSize = Integer.parseInt(pageSizes.get(0));
pageSize = Math.min(pageSize, 10);
}
List<String> statuses = parameters.get("status");
if (!Objects.isNull(statuses) && !statuses.isEmpty()) {
status = statuses.get(0);
}
List<String> models = parameters.get("model");
if (!Objects.isNull(models) && !models.isEmpty()) {
model = models.get(0);
}
List<String> boxPriceTotalMines = parameters.get("boxPriceTotalMin");
if (!Objects.isNull(boxPriceTotalMines) && !boxPriceTotalMines.isEmpty()) {
boxPriceTotalMin = new BigDecimal(boxPriceTotalMines.get(0));
}
List<String> boxPriceTotalMaxes = parameters.get("boxPriceTotalMax");
if (!Objects.isNull(boxPriceTotalMaxes) && !boxPriceTotalMaxes.isEmpty()) {
boxPriceTotalMax = new BigDecimal(boxPriceTotalMaxes.get(0));
}
if (ObjectUtil.isNotEmpty(WsFightHall.allHallUserMap.get(userId))) {
session.getBasicRemote().sendText("用户" + userId + "已连接,请勿重复调用");
session.close();
return;
}
addHallUser(userId, session);
Page<TtFight> pageInfo = new Page<>(pageNum, pageSize);
pageInfo.setOptimizeCountSql(false);
LambdaQueryWrapper<TtFight> fightQuery = new LambdaQueryWrapper<>();
fightQuery
.eq(status != null, TtFight::getStatus, status)
.eq(model != null, TtFight::getModel, model)
.between(boxPriceTotalMin != null && boxPriceTotalMax != null,
TtFight::getBoxPriceTotal, boxPriceTotalMin, boxPriceTotalMax)
.ge(boxPriceTotalMin == null && boxPriceTotalMax != null,
TtFight::getBoxPriceTotal, boxPriceTotalMax)
.orderByDesc(TtFight::getCreateTime);
List<TtFight> fightList = apiFightService.page(pageInfo, fightQuery).getRecords();
if (CollectionUtils.isEmpty(fightList)) {
Map<String, Object> map = new HashMap<>();
map.put("rows", new ArrayList<>());
map.put("total", 0);
sendMessage(WsResult.ok(SMsgKey.ALL_FIGHT_ROOM.name(), map, "所有对战房间"));
} else {
List<Integer> boxIds = new ArrayList<>();
for (TtFight fight : fightList) {
if (fight == null || MapUtils.isEmpty(fight.getBoxData())) {
continue;
}
for (Map.Entry<String, FightBoxVO> entry : fight.getBoxData().entrySet()) {
Object value = entry.getValue();
if (value == null) {
continue;
}
Integer boxId;
if (value instanceof LinkedHashMap) {
Map<String, Object> map = (LinkedHashMap) value;
boxId = map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null;
} else {
boxId = ((FightBoxVO) value).getBoxId();
}
if (boxId == null) {
continue;
}
boxIds.add(boxId);
}
}
LambdaQueryWrapper<TtBox> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(TtBox::getBoxId, boxIds);
List<TtBox> boxes = ttBoxMapper.selectList(queryWrapper);
Map<Integer, BigDecimal> boxMap = boxes.stream().filter(Objects::nonNull).collect(Collectors.toMap(TtBox::getBoxId, TtBox::getPrice));
for (TtFight fight : fightList) {
if (fight == null || MapUtils.isEmpty(fight.getBoxData())) {
continue;
}
for (Map.Entry<String, FightBoxVO> entry : fight.getBoxData().entrySet()) {
Object value = entry.getValue();
if (value == null) {
continue;
}
FightBoxVO vo;
Integer boxId;
if (value instanceof LinkedHashMap) {
Map<String, Object> map = (LinkedHashMap) value;
vo = new FightBoxVO(map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null,
map.get("number") != null ? Integer.valueOf(map.get("number").toString()) : null,
map.get("boxImg01") != null ? map.get("boxImg01").toString() : null,
map.get("boxImg02") != null ? map.get("boxImg02").toString() : null);
vo.setPrice(boxMap.getOrDefault(vo.getBoxId(), new BigDecimal(0)));
} else {
vo = (FightBoxVO) value;
vo.setPrice(boxMap.getOrDefault(vo.getBoxId(), new BigDecimal(0)));
}
fight.getBoxData().put(vo.getBoxId().toString(), vo);
}
}
Map<String, Object> map = new HashMap<>();
map.put("rows", fightList);
map.put("total", apiFightService.page(pageInfo, fightQuery).getTotal());
sendMessage(WsResult.ok(SMsgKey.ALL_FIGHT_ROOM.name(), map, "所有对战房间"));
}
log.info("用户{}进入对战游戏大厅,广播{}个房间,在线人数{}", userId, fightList.size(), WsFightHall.onlineCount);
}
@OnClose
public void onClose(Session session) {
// removeHallUser();
removeHallUser(session.getPathParameters().get("userId"));
log.info("关闭连接,正常在线人数:" + WsFightHall.onlineCount);
}
@OnMessage
public void onMessage(String message, Session session) {
log.info("收到消息");
}
@OnError
public void onError(Session session, Throwable exception) throws Exception {
log.error("出现错误", exception);
}
/**
* 更新在线人数
*/
private int addOnlineCount(int number) {
synchronized (new Object()) {
WsFightHall.onlineCount += number;
return WsFightHall.onlineCount;
}
}
/**
* 全局广播
*/
public static void broadcast(Object message) {
// ObjectMapper objectMapper = new ObjectMapper();
String msg = JSON.toJSONString(message);
Collection<WsFightHall> wss = WsFightHall.allHallUserMap.values();
for (WsFightHall ws : wss) {
try {
ws.session.getBasicRemote().sendText(msg);
} catch (IOException e) {
log.warn("ws大厅广播给消息异常。msg:{}", msg);
}
}
}
/**
* 实现服务器主动推送
*/
public void sendMessage(Object message) {
try {
this.session.getBasicRemote().sendText(JSON.toJSONString(message));
} catch (IOException e) {
log.warn("服务器推送消息异常");
}
}
/**
* 添加连接用户
*/
private WsFightHall addHallUser(String userId, Session session) {
addOnlineCount(1);
this.userId = userId;
this.session = session;
WsFightHall.allHallUserMap.put(userId, this);
return this;
}
/**
* 移除连接用户
*/
private WsFightHall removeHallUser() {
addOnlineCount(-1);
WsFightHall.allHallUserMap.remove(userId);
return this;
}
/**
* 移除连接用户
*/
private WsFightHall removeHallUser(String userId) {
addOnlineCount(-1);
WsFightHall.allHallUserMap.remove(userId);
return this;
}
}

View File

@@ -0,0 +1,406 @@
package com.ruoyi.playingmethod.websocket;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ruoyi.admin.mapper.TtBoxMapper;
import com.ruoyi.admin.mapper.TtBoxOrnamentsMapper;
import com.ruoyi.admin.service.TtBoxRecordsService;
import com.ruoyi.common.core.domain.R;
import com.ruoyi.common.core.redis.RedisCache;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.entity.fight.FightSeat;
import com.ruoyi.domain.entity.fight.TtFight;
import com.ruoyi.domain.other.FightBoutData;
import com.ruoyi.domain.other.TtBox;
import com.ruoyi.domain.vo.TtBoxOrnamentsDataVO;
import com.ruoyi.domain.vo.fight.FightBoxVO;
import com.ruoyi.domain.vo.fight.FightResultVO;
import com.ruoyi.playingmethod.service.ApiFightService;
import com.ruoyi.playingmethod.service.ApiFightUserService;
import com.ruoyi.playingmethod.websocket.constant.SMsgKey;
import com.ruoyi.playingmethod.websocket.util.WsResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.math.BigDecimal;
import java.sql.Timestamp;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;
/**
* 对战房间
*/
@Slf4j
@Component
@ServerEndpoint("/ws/fight/room/{userId}/{fightId}")
public class WsFightRoom {
static Map<String, Session> sessionMap = new ConcurrentHashMap<>();
static volatile AtomicInteger viewersCount = new AtomicInteger(0);
// 用来记录当前连接数的变量
private static volatile int onlineCount = 0;
// concurrent包的线程安全Set用来存放每个客户端对应的MyWebSocket对象
public static ConcurrentHashMap<String, WsFightRoom> allRoomUserMap = new ConcurrentHashMap<>();
// 与某个客户端的连接会话,需要通过它来与客户端进行数据收发
private Session session;
private static ApiFightService apiFightService;
private static TtBoxRecordsService boxRecordsService;
private static TtBoxOrnamentsMapper boxOrnamentsMapper;
// private static TtBoxOrnamentsService ttBoxOrnamentsService;
private static ApiFightUserService apiFightUserService;
private static Integer fightRoundTime = null; // 战斗回合时间
private Integer userId = null;
private Integer fightId = null;
private String key = "";
private static RedisCache redisCache;
private static TtBoxMapper ttBoxMapper;
@Value("${mkcsgo.fight.roundTime}")
public void ttFightService(Integer fightRoundTime) {
WsFightRoom.fightRoundTime = fightRoundTime;
}
@Autowired
public void ttFightService(ApiFightService apiFightService) {
WsFightRoom.apiFightService = apiFightService;
}
@Autowired
public void boxOrnamentsMapper(TtBoxOrnamentsMapper boxOrnamentsMapper) {
WsFightRoom.boxOrnamentsMapper = boxOrnamentsMapper;
}
@Autowired
public void boxRecordsService(TtBoxRecordsService boxRecordsService) {
WsFightRoom.boxRecordsService = boxRecordsService;
}
public void apiFightUserService(ApiFightUserService apiFightUserService) {
WsFightRoom.apiFightUserService = apiFightUserService;
}
@Autowired
public void ttBoxMapper(TtBoxMapper ttBoxMapper) {
WsFightRoom.ttBoxMapper = ttBoxMapper;
}
@Autowired
public void redisCache(RedisCache redisCache) {
WsFightRoom.redisCache = redisCache;
}
@OnOpen
public void onOpen(Session session,
@PathParam("userId") Integer userId,
@PathParam("fightId") Integer fightId) {
try {
addFightRoomUser(userId, fightId, session);
R check = connectCheck(userId, fightId);
if (!check.getCode().equals(200)) {
session.getBasicRemote().sendText(check.getMsg());
session.close();
return;
}
log.debug("/ws/fight/room > > onOpen");
log.info("用户{}进入房间{}" + "在线人数{}", userId, fightId, WsFightRoom.onlineCount);
sendMsgToPlayers("用户:" + userId + "进入房间," + fightId + "在线人数" + WsFightRoom.onlineCount, null);
// 首次连接获取房间最新数据
LambdaQueryWrapper<TtFight> fightQuery = new LambdaQueryWrapper<>();
fightQuery
.eq(TtFight::getId, fightId);
// .eq(TtFight::getStatus,0);
TtFight fight = apiFightService.getOne(fightQuery);
// 构建结果集
// 根据宝箱id查询关联的所有饰品
Map<String, FightBoxVO> boxData = fight.getBoxData();
ArrayList<FightBoxVO> fightBoxVOList = new ArrayList<>();
boxData.keySet().forEach(boxId -> {
List<TtBoxOrnamentsDataVO> boxOrnamentsVOS = boxOrnamentsMapper.selectTtBoxOrnamentsList(Integer.valueOf(boxId));
FightBoxVO fightBoxVO = JSONUtil.toBean(JSONUtil.toJsonStr(boxData.get(boxId)), FightBoxVO.class);
fightBoxVO.setOrnaments(boxOrnamentsVOS);
boxData.put(boxId,fightBoxVO);
fightBoxVOList.add(boxData.get(boxId));
});
LambdaQueryWrapper<TtBox> queryWrapper = new LambdaQueryWrapper<>();
queryWrapper.in(TtBox::getBoxId, boxData.keySet());
List<TtBox> boxes = ttBoxMapper.selectList(queryWrapper);
var boxMapObj = boxes.stream().filter(Objects::nonNull).collect(Collectors.groupingBy(TtBox::getBoxId));
Map<Integer, BigDecimal> boxMap = boxes.stream().filter(Objects::nonNull).collect(Collectors.toMap(TtBox::getBoxId, TtBox::getPrice));
for (FightBoxVO vo : fightBoxVOList) {
vo.setPrice(boxMap.getOrDefault(vo.getBoxId(), new BigDecimal(0)));
if (boxMapObj.get(vo.getBoxId()) != null) {
vo.setBoxName(boxMapObj.get(vo.getBoxId()).getFirst().getBoxName());
}
}
fight.getBoxData().keySet().forEach(boxId -> {
Object value = fight.getBoxData().get(boxId);
FightBoxVO a = null;
if (value instanceof LinkedHashMap) {
Map<String, Object> map = (LinkedHashMap) value;
a = new FightBoxVO(map.get("boxId") != null ? Integer.valueOf(map.get("boxId").toString()) : null,
map.get("number") != null ? Integer.valueOf(map.get("number").toString()) : null,
map.get("boxImg01") != null ? map.get("boxImg01").toString() : null,
map.get("boxImg02") != null ? map.get("boxImg02").toString() : null);
a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO));
} else {
a = (FightBoxVO) value;
a.setPrice(boxMap.getOrDefault(a.getBoxId(), BigDecimal.ZERO));
}
fight.getBoxData().put(boxId, a);
});
// 如果游戏结束,发送结果集数据
if (fight.getStatus().equals(2) || fight.getStatus().equals(3)) {
// 所有对战结果记录
LambdaQueryWrapper<TtBoxRecords> boxRecordsQuery = new LambdaQueryWrapper<>();
boxRecordsQuery
.eq(TtBoxRecords::getFightId, fightId);
List<TtBoxRecords> allBoxRecords = boxRecordsService.list(boxRecordsQuery);
FightResultVO resultVO = FightResultVO.builder()
.currentRound(-1L)
.fight(fight)
.winnerIds(fight.getWinnerIds())
.fightResult(allBoxRecords)
.fightBoxVOList(fightBoxVOList)
.build();
broadcastFight(fightId, WsResult.ok(SMsgKey.FIGHT_RESULT.name(), resultVO, "对局已结束,对局最新信息"));
log.info("room onOpen 广播数据成功。");
} else if (fight.getStatus().equals(1)) {
// 所有对战结果记录
LambdaQueryWrapper<TtBoxRecords> boxRecordsQuery = new LambdaQueryWrapper<>();
boxRecordsQuery
.eq(TtBoxRecords::getFightId, fightId)
.eq(TtBoxRecords::getStatus, 0);
// 首先从Redis中查询对战结果没有则在从数据库中查询
List<TtBoxRecords> allBoxRecords;
String key = "fight_result:fight_" + fightId;
allBoxRecords = redisCache.getCacheObject(key);
if (Objects.isNull(allBoxRecords)) {
allBoxRecords = boxRecordsService.list(boxRecordsQuery);
}
// 时间差,计算当前进行到第几回合
// LocalDateTime now = LocalDateTime.now();
// Timestamp beginTime = fight.getBeginTime();
// LocalDateTime beginTime1 = beginTime.toLocalDateTime();
// Duration duration = Duration.between(beginTime1, now);
// Long currentRound = duration.toMillis() / fightRoundTime; // 这个秒要和前端的【每回合时间】同步
// 获取当前回合数
Integer currentRound = 0;
FightBoutData fightBoutData = redisCache.getCacheObject("fight_bout_data:fight_" + fightId);
if (!Objects.isNull(fightBoutData)) {
currentRound = fightBoutData.getBoutNum();
}
FightResultVO resultVO = FightResultVO.builder()
.currentRound(currentRound.longValue())
.fight(fight)
.winnerIds(fight.getWinnerIds())
.fightResult(allBoxRecords)
.fightBoxVOList(fightBoxVOList)
.build();
// broadcastFight(fightId, WsResult.ok(SMsgKey.FIGHT_RESULT.name(), resultVO, "对局进行中,对局最新信息"));
sendMsgToPlayers(WsResult.ok(SMsgKey.FIGHT_RESULT.name(), resultVO, "对局进行中,对局最新信息"), null);
log.info("room onOpen 广播数据成功。");
} else if (fight.getStatus().equals(0)) {
// 没有结束,返回房间信息即可
sendMsgToPlayers(WsResult.ok(SMsgKey.FIGHT_ROOM_INFO.name(), fight, "对局准备中,对战房间最新信息"), null);
log.info("room onOpen 广播数据成功。");
}
} catch (Exception e) {
e.printStackTrace();
log.warn("onopen warn");
}
}
@OnClose
public void onClose(Session session) {
removeFightRoomUser();
log.info("onClose");
log.info("正常在线人数:" + WsFightRoom.onlineCount);
}
@OnError
public void onError(Session session, Throwable exception) throws Exception {
log.info("onError");
}
@OnMessage
public void onMessage(String message, Session session) {
log.info("onMessage");
}
/**
* 连接检查
*/
private R connectCheck(Integer userId, Integer fightId) {
if (ObjectUtil.isNotEmpty(WsFightRoom.allRoomUserMap.get(userId + "_" + fightId))) {
R.fail("用户" + userId + "已经连接对局" + fightId + "。请勿重复调用。");
}
TtFight fight = apiFightService.getById(fightId);
if (ObjectUtil.isEmpty(fight)) {
return R.fail("不存在的对局ID" + fightId);
}
// if (fight.getStatus().equals(2) || fight.getStatus().equals(3)){
// return R.fail("对局"+fightId+"已结束。");
// }
return R.ok();
}
/**
* 更新在线人数
*/
private int addOnlineCount(int number) {
synchronized (new Object()) {
WsFightRoom.onlineCount = WsFightRoom.onlineCount + number;
return WsFightRoom.onlineCount;
}
}
/**
* 单点推送
*/
public void sendMsgToPlayers(Object message, List<String> keys) {
String msg = JSON.toJSONString(message);
try {
if (ObjectUtil.isEmpty(keys) || keys.size() == 0) {
// 推送给自己
this.session.getBasicRemote().sendText(msg);
} else {
for (String key : keys) {
WsFightRoom ws = WsFightRoom.allRoomUserMap.get(key);
ws.session.getBasicRemote().sendText(msg);
}
}
} catch (Exception e) {
log.warn("推送消息异常。msg:" + msg);
}
}
/**
* 房间广播
*/
public static void broadcastFight(Integer fightId, Object message) {
String msg = JSON.toJSONString(message);
Collection<WsFightRoom> wslist = WsFightRoom.allRoomUserMap.values();
for (WsFightRoom ws : wslist) {
try {
if (!ws.fightId.equals(fightId)) continue;
log.info(String.valueOf(ws.session.isOpen()));
if (!ws.session.isOpen()) continue;
RemoteEndpoint.Basic basicRemote = ws.session.getBasicRemote();
basicRemote.sendText(msg);
} catch (IOException e) {
log.warn("ws推送广播给{}_{}消息异常。", ws.userId, ws.fightId);
}
}
}
/**
* 全局广播
*/
public static void broadcast(Object message) {
ObjectMapper objectMapper = new ObjectMapper();
String msg = "";
try {
msg = objectMapper.writeValueAsString(message);
} catch (JsonProcessingException e) {
log.warn("ws解析广播消息异常。");
}
Collection<WsFightRoom> wss = WsFightRoom.allRoomUserMap.values();
for (WsFightRoom ws : wss) {
try {
ws.session.getBasicRemote().sendText(msg);
} catch (IOException e) {
log.warn("ws推送广播给{}_{}消息异常。", ws.userId, ws.fightId);
}
}
}
/**
* 添加连接用户
*/
private WsFightRoom addFightRoomUser(Integer userId, Integer fightId, Session session) {
addOnlineCount(1);
this.userId = userId;
this.fightId = fightId;
this.key = userId + "_" + fightId;
this.session = session;
WsFightRoom.allRoomUserMap.put(key, this);
return this;
}
/**
* 移除连接用户
*/
private WsFightRoom removeFightRoomUser() {
addOnlineCount(-1);
WsFightRoom.allRoomUserMap.remove(userId + "_" + fightId);
return this;
}
/**
* 断开房间所有连接
*/
public static Boolean batchClose(TtFight fight) {
List<FightSeat> seats = fight.getSeats();
for (int i = 0; i < seats.size(); i++) {
FightSeat seat = JSONUtil.toBean(JSONUtil.toJsonStr(seats.get(i)), FightSeat.class);
Integer playerId = seat.getPlayerId();
String key = ObjectUtil.isNotEmpty(playerId) ? String.valueOf(playerId) : "" + "_" + fight.getId();
WsFightRoom ws = WsFightRoom.allRoomUserMap.get(key);
if (ObjectUtil.isEmpty(ws)) continue;
try {
ws.session.close();
} catch (IOException e) {
log.warn("关闭连接异常");
}
}
return true;
}
}

View File

@@ -0,0 +1,256 @@
package com.ruoyi.playingmethod.websocket;
import cn.hutool.json.JSONUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.ruoyi.admin.mapper.TtBoxOrnamentsMapper;
import com.ruoyi.admin.service.TtBoxRecordsService;
import com.ruoyi.domain.entity.TtBoxRecords;
import com.ruoyi.domain.entity.fight.TtFight;
import com.ruoyi.domain.vo.TtBoxOrnamentsDataVO;
import com.ruoyi.domain.vo.fight.FightBoxVO;
import com.ruoyi.domain.vo.fight.FightResultVO;
import com.ruoyi.playingmethod.service.ApiFightService;
import com.ruoyi.playingmethod.websocket.constant.SMsgKey;
import com.ruoyi.playingmethod.websocket.util.WsResult;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import java.io.IOException;
import java.sql.Timestamp;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
/**
* 对战房间
*/
@Slf4j
@ServerEndpoint("/ws/fight/room2/{userId}/{fightId}")
@Component
public class WsFightRoom2 {
static Map<String, Session> sessionMap = new ConcurrentHashMap<>();
static Map<String, Session> viewersSessionMap = new ConcurrentHashMap<>();
private String sessionKey;
private static Integer fightRoundTime;
private static ApiFightService apiFightService;
private static TtBoxRecordsService ttBoxRecordsService;
private static TtBoxOrnamentsMapper ttBoxOrnamentsMapper;
@Value("${mkcsgo.fight.roundTime}")
public void setFightRoundTime(Integer fightRoundTime) {
WsFightRoom2.fightRoundTime = fightRoundTime;
}
@Autowired
public void ttFightService(ApiFightService apiFightService) {
WsFightRoom2.apiFightService = apiFightService;
}
@Autowired
public void boxRecordsService(TtBoxRecordsService ttBoxRecordsService) {
WsFightRoom2.ttBoxRecordsService = ttBoxRecordsService;
}
@Autowired
public void boxOrnamentsMapper(TtBoxOrnamentsMapper ttBoxOrnamentsMapper) {
WsFightRoom2.ttBoxOrnamentsMapper = ttBoxOrnamentsMapper;
}
@OnOpen
public void onOpen(Session session,
@PathParam("userId") Integer userId,
@PathParam("fightId") Integer fightId) {
sessionKey = fightId.toString() + "#" + userId.toString();
// 检查用户是否已经进入某个房间
if (!Objects.isNull(sessionMap.get(sessionKey))) {
try {
session.getBasicRemote().sendText("用户" + userId + "已加入" + fightId +"房间,不得重复进入");
session.close();
return;
} catch (IOException e) {
e.printStackTrace();
}
} else {
sessionMap.put(sessionKey, session);
}
// 首次连接获取房间最新数据
LambdaQueryWrapper<TtFight> fightQuery = new LambdaQueryWrapper<>();
fightQuery
.eq(TtFight::getId, fightId);
TtFight ttFight = apiFightService.getOne(fightQuery);
// 检查房间是否存在
if (Objects.isNull(ttFight)) {
try {
session.getBasicRemote().sendText("房间" + fightId + "不存在");
session.close();
return;
} catch (IOException e) {
e.printStackTrace();
}
}
// 判断对战状态
if (ttFight.getStatus() == 2) {
// 构建结果集
// 根据宝箱ID查询关联的所有饰品
Map<String, FightBoxVO> boxData = ttFight.getBoxData();
ArrayList<FightBoxVO> fightBoxVOList = new ArrayList<>();
boxData.keySet().forEach(boxId -> {
fightBoxVOList.add(boxData.get(boxId));
});
// 所有对战结果记录
LambdaQueryWrapper<TtBoxRecords> boxRecordsQuery = new LambdaQueryWrapper<>();
boxRecordsQuery
.eq(TtBoxRecords::getFightId, fightId)
.eq(TtBoxRecords::getStatus, 0);
List<TtBoxRecords> allBoxRecords = ttBoxRecordsService.list(boxRecordsQuery);
FightResultVO resultVO = FightResultVO.builder()
.currentRound(-1L)
.fight(ttFight)
.winnerIds(ttFight.getWinnerIds())
.fightResult(allBoxRecords)
.fightBoxVOList(fightBoxVOList)
.build();
WsResult<FightResultVO> result = WsResult.ok(SMsgKey.FIGHT_RESULT.name(), resultVO, "对局已结束,对局最新信息");
String message = JSON.toJSONString(result);
broadcastMessageToFight(fightId, message);
} else if (ttFight.getStatus() == 1) {
// 构建结果集
// 根据宝箱ID查询关联的所有饰品
Map<String, FightBoxVO> boxData = ttFight.getBoxData();
ArrayList<FightBoxVO> fightBoxVOList = new ArrayList<>();
boxData.keySet().forEach(boxId -> {
List<TtBoxOrnamentsDataVO> boxOrnamentsVOS = ttBoxOrnamentsMapper.selectTtBoxOrnamentsList(Integer.valueOf(boxId));
FightBoxVO fightBoxVO = JSONUtil.toBean(JSONUtil.toJsonStr(boxData.get(boxId)), FightBoxVO.class);
fightBoxVO.setOrnaments(boxOrnamentsVOS);
boxData.put(boxId,fightBoxVO);
fightBoxVOList.add(boxData.get(boxId));
});
// 所有对战结果记录
LambdaQueryWrapper<TtBoxRecords> boxRecordsQuery = new LambdaQueryWrapper<>();
boxRecordsQuery
.eq(TtBoxRecords::getFightId, fightId)
.eq(TtBoxRecords::getStatus, 0);
List<TtBoxRecords> allBoxRecords = ttBoxRecordsService.list(boxRecordsQuery);
// 时间差,计算当前进行到第几回合
LocalDateTime now = LocalDateTime.now();
Timestamp beginTime = ttFight.getBeginTime();
LocalDateTime localDateTime = beginTime.toLocalDateTime();
Duration duration = Duration.between(localDateTime, now);
Long currentRound = duration.toMillis() / fightRoundTime; // 这个秒要和前端的【每回合时间】同步
FightResultVO resultVO = FightResultVO.builder()
.currentRound(currentRound)
.fight(ttFight)
.winnerIds(ttFight.getWinnerIds())
.fightResult(allBoxRecords)
.fightBoxVOList(fightBoxVOList)
.build();
WsResult<FightResultVO> result = WsResult.ok(SMsgKey.FIGHT_RESULT.name(), resultVO, "对局进行中,对局最新信息");
String message = JSON.toJSONString(result);
broadcastMessageToFight(fightId, message);
// 存储观战人,广播房间内观战人数
viewersSessionMap.put(sessionKey, session);
broadcastViewersCountToFight(fightId);
} else if (ttFight.getStatus() == 0) {
WsResult<TtFight> result = WsResult.ok(SMsgKey.FIGHT_ROOM_INFO.name(), ttFight, "对局准备中,对战房间最新信息");
String message = JSON.toJSONString(result);
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
@OnMessage
public void onMessage(Session session, String message) {
}
@OnClose
public void onClose(Session session) {
if (session.equals(sessionMap.get(sessionKey))) {
sessionMap.remove(sessionKey);
}
if (session.equals(viewersSessionMap.get(sessionKey))) {
viewersSessionMap.remove(sessionKey);
Integer fightId = Integer.parseInt(sessionKey.substring(0, sessionKey.indexOf('#')));
broadcastViewersCountToFight(fightId);
}
}
@OnError
public void onError(Session session, Throwable throwable) {
}
// 向指定房间的用户广播消息
private void broadcastMessageToFight(Integer fightId, String message) {
for (Map.Entry<String, Session> entry : sessionMap.entrySet()) {
String sessionKey = entry.getKey();
Session session = entry.getValue();
if (sessionKey.substring(0, sessionKey.indexOf('#')).equals(fightId.toString())) {
try {
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
// 向指定房间的用户广播观战人数
private void broadcastViewersCountToFight(Integer fightId) {
int viewersCount = 0;
// 统计本房间的观战人数
for (Map.Entry<String, Session> entry : viewersSessionMap.entrySet()) {
String sessionKey = entry.getKey();
if (sessionKey.substring(0, sessionKey.indexOf('#')).equals(fightId.toString())) {
viewersCount++;
}
}
// 向该房间所有用户广播
for (Map.Entry<String, Session> entry : sessionMap.entrySet()) {
String sessionKey = entry.getKey();
Session session = entry.getValue();
if (sessionKey.substring(0, sessionKey.indexOf('#')).equals(fightId.toString())) {
try {
Map<String, Object> map = new HashMap<>();
map.put("viewersCount", viewersCount);
map.put("message", "当前房间观战人数");
String message = JSON.toJSONString(map);
session.getBasicRemote().sendText(message);
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}

View File

@@ -0,0 +1,13 @@
package com.ruoyi.playingmethod.websocket.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.socket.server.standard.ServerEndpointExporter;
@Configuration
public class WebSocketConfig {
@Bean
public ServerEndpointExporter serverEndpointExporter() {
return new ServerEndpointExporter();
}
}

View File

@@ -0,0 +1,8 @@
package com.ruoyi.playingmethod.websocket.constant;
// 客户端发送消息key
public enum CMsgKey {
FIGHT_END;
}

View File

@@ -0,0 +1,21 @@
package com.ruoyi.playingmethod.websocket.constant;
// 服务端发送消息key
public enum SMsgKey {
// 所有对战房间列表
ALL_FIGHT_ROOM,
// 对战结果
FIGHT_RESULT,
// 房间信息
FIGHT_ROOM_INFO,
// 盲盒房间连接第一次数据
Blind_Box_Init_Data,
// 盲盒房间实时数据
Blind_Box_Current_Data;
}

View File

@@ -0,0 +1,17 @@
package com.ruoyi.playingmethod.websocket.entity;
import lombok.Data;
import java.io.Serializable;
@Data
public class ResultData<T> implements Serializable {
private static final long serialVersionUID = 1L;
private int code;
private String typeName;
private T data;
}

View File

@@ -0,0 +1,11 @@
package com.ruoyi.playingmethod.websocket.entity;
import lombok.Data;
import jakarta.websocket.Session;
@Data
public class SessionData {
private Integer userId = 0;
private Session session;
}

View File

@@ -0,0 +1,31 @@
package com.ruoyi.playingmethod.websocket.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.concurrent.Semaphore;
public class SemaphoreUtils {
private static final Logger LOGGER = LoggerFactory.getLogger(SemaphoreUtils.class);
public static boolean tryAcquire(Semaphore semaphore) {
boolean flag = false;
try {
flag = semaphore.tryAcquire();
} catch (Exception e) {
LOGGER.error("获取信号量异常", e);
}
return flag;
}
public static void release(Semaphore semaphore) {
try {
semaphore.release();
} catch (Exception e) {
LOGGER.error("释放信号量异常", e);
}
}
}

View File

@@ -0,0 +1,81 @@
package com.ruoyi.playingmethod.websocket.util;
import com.ruoyi.common.constant.HttpStatus;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
/**
* 响应信息主体
*
* @author ruoyi
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class WsResult<T> implements Serializable
{
private static final long serialVersionUID = 1L;
/** 成功 */
public static final int SUCCESS = HttpStatus.SUCCESS;
/** 失败 */
public static final int FAIL = HttpStatus.ERROR;
private String key;
private T data;
private int code;
private String msg;
public static <T> WsResult<T> ok()
{
return new WsResult(null,null, SUCCESS, "操作成功");
}
public static <T> WsResult<T> ok(String key, T data)
{
return new WsResult(key,data, SUCCESS, "操作成功");
}
public static <T> WsResult<T> ok(String key,T data,String msg)
{
return new WsResult(key,data, SUCCESS, msg);
}
public static <T> WsResult<T> ok(String msg)
{
return new WsResult(null,null, SUCCESS, msg);
}
public static <T> WsResult<T> fail()
{
return new WsResult(null,null, FAIL, "操作失败");
}
public static <T> WsResult<T> fail(String key,T data,String msg)
{
return new WsResult(key,data, FAIL, msg);
}
public static <T> WsResult<T> fail(String msg)
{
return new WsResult(null,null, FAIL, msg);
}
public static <T> Boolean isError(WsResult<T> ret)
{
return !isSuccess(ret);
}
public static <T> Boolean isSuccess(WsResult<T> ret)
{
return WsResult.SUCCESS == ret.getCode();
}
}

View File

@@ -0,0 +1,66 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.playingmethod.mapper.ApiAttendanceRecordMapper">
<resultMap type="com.ruoyi.playingmethod.entity.TtAttendanceRecord" id="TtCoinRecordResult">
<result property="id" column="id" />
<result property="userId" column="user_id" />
<result property="coin" column="coin" />
<result property="createTime" column="create_time" />
<result property="updateTime" column="update_time" />
<result property="delFlag" column="del_flag" />
</resultMap>
<select id="selectByUid" resultType="com.ruoyi.playingmethod.entity.TtAttendanceRecord">
SELECT
id,
user_id,
coin
FROM tt_attendance_record
<where>
<if test="uid != null">and user_id = #{uid}</if>
AND del_flag = '0'
AND (create_time >= DATE_FORMAT(NOW() - INTERVAL 1 HOUR, '%Y-%m-%d %H:00:00')
or create_time &lt; DATE_FORMAT(NOW() - INTERVAL 1 HOUR, '%Y-%m-%d %H:59:59')
)
</where>
</select>
<insert id="insertAttendanceRecord" parameterType="com.ruoyi.playingmethod.entity.TtAttendanceRecord" useGeneratedKeys="true" keyProperty="id">
insert into tt_attendance_record
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="userId != null">user_id,</if>
<if test="coin != null">coin,</if>
<if test="createTime != null">create_time,</if>
<if test="updateTime != null">update_time,</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="userId != null">#{userId},</if>
<if test="coin != null">#{coin},</if>
<if test="createTime != null">#{createTime},</if>
<if test="updateTime != null">#{updateTime},</if>
</trim>
</insert>
<select id="selectSevenAttendance" resultType="com.ruoyi.playingmethod.entity.TtAttendanceRecord">
SELECT
id,
user_id,
coin,
create_time
FROM tt_attendance_record
<where>
<if test="uid != null">and user_id = #{uid}</if>
<!-- <if test="beginTime != null"> <![CDATA[ and create_time >= #{beginTime} ]]></if>-->
<!-- <if test="endTime != null"> <![CDATA[ and create_time <= #{endTime} ]]></if>-->
<if test="createTime != null and createTime != ''"> and DATE_FORMAT(create_time, '%Y-%m-%d') = #{createTime}</if>
AND del_flag = '0'
</where>
</select>
</mapper>

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.playingmethod.mapper.ApiBattleRoyaleMapper">
</mapper>

View File

@@ -0,0 +1,133 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.ruoyi.playingmethod.mapper.ApiBindBoxMapper">
<select id="getBoxData" resultType="com.ruoyi.domain.other.TtBoxA">
SELECT box_id,
box_name,
box_type_id,
price,
box_img01,
box_img02
FROM tt_box
WHERE box_id = #{boxId}
AND del_flag = '0'
AND status = '0';
</select>
<select id="getBoxOrnamentsList" resultType="com.ruoyi.domain.other.TtOrnamentsA">
SELECT tto.id AS ornament_id,
tto.name,
tto.short_name,
tto.use_price,
tto.image_url,
tto.exterior_name,
tol.id AS ornamentsLevelId,
tol.level_img,
tbo.odds,
tbo.comp_odds
FROM tt_box_ornaments tbo
LEFT JOIN tt_ornament tto ON tbo.ornament_id = tto.id
LEFT JOIN tt_ornaments_level tol ON tbo.level = tol.id
WHERE tbo.box_id = #{boxId}
</select>
<select id="getBoxList" resultType="com.ruoyi.domain.other.TtBoxA">
SELECT
box_id,
box_name,
price,
box_img01,
box_img02,
box_type_id,
box_type
FROM tt_box
<where>
<if test="boxTypeId != null">and box_type_id = #{boxTypeId}</if>
<if test="homeFlag != null">and is_home = '0'</if>
<if test="boxType != null">and box_type = #{boxType}</if>
AND del_flag = '0' AND status = '0'
</where>
</select>
<select id="getOrnamentsData" resultType="com.ruoyi.domain.other.TtOrnamentsA">
SELECT #{id} as id,
tbo.ornaments_id,
tto.name,
tto.short_name,
tto.use_price,
tto.image_url,
tto.exterior_name,
tbo.level as ornamentsLevelId,
tol.level_img
FROM tt_box_ornaments tbo
LEFT JOIN tt_ornament tto ON tbo.ornaments_id = tto.id
LEFT JOIN tt_ornaments_level tol ON tbo.level = tol.id
WHERE tbo.box_id = #{boxId}
AND tbo.ornaments_id = #{ornamentsId}
LIMIT 1
</select>
<select id="getBindBoxHistory" resultType="com.ruoyi.domain.other.TtBoxUser">
SELECT tbr.id,
tbr.box_id,
tbr.user_id,
ttu.user_name,
ttu.nick_name,
ttu.avatar,
tbr.ornament_id,
tto.name,
tto.short_name,
tto.use_price,
tto.image_url,
tto.exterior_name,
tbr.ornaments_level_id,
tol.level_img,
tbr.source
FROM tt_box_records tbr
LEFT JOIN tt_user ttu ON tbr.user_id = ttu.user_id
LEFT JOIN tt_ornament tto ON tbr.ornament_id = tto.id
LEFT JOIN tt_ornaments_level tol ON tbr.ornaments_level_id = tol.id
<where>
<if test="boxId != null"> AND tbr.box_id = #{boxId} </if>
<if test="userType != null"> AND ttu.user_type = #{userType} </if>
AND tbr.source = '1'
</where>
ORDER BY tbr.create_time DESC
</select>
<select id="getProbabilityDistribution"
resultType="com.ruoyi.domain.other.TtBoxLevelA">
SELECT
tbo.`level` AS ornamentsLevelId,
tol.`level`,
CONCAT(FORMAT(SUM(tbo.odds) / (SELECT SUM(odds) FROM tt_box_ornaments WHERE box_id = #{boxId}) * 100, 2), '%') AS probability
FROM tt_box_ornaments tbo
LEFT JOIN tt_ornaments_level tol ON tbo.`level` = tol.id
WHERE tbo.box_id = #{boxId}
GROUP BY tbo.`level`;
</select>
<select id="ornamentsInfo" resultType="com.ruoyi.domain.other.TtOrnamentsA">
SELECT
tto.id as ornament_id,
tto.name as name,
tto.short_name,
tto.use_price,
tto.image_url,
tto.exterior_name,
tbo.level as ornamentsLevelId,
tol.level_img
FROM tt_box_ornaments tbo
LEFT JOIN tt_ornament tto ON tbo.ornament_id = tto.id
LEFT JOIN tt_ornaments_level tol ON tbo.level = tol.id
WHERE tbo.box_id = #{boxId}
AND tbo.ornament_id = #{ornamentId}
LIMIT 1
</select>
</mapper>

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