|
@@ -0,0 +1,325 @@
|
|
|
+package com.xs.core.service.Impl.coin;
|
|
|
+
|
|
|
+import cn.hutool.core.util.ObjUtil;
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
+import com.xs.core.common.constant.GoldCoinConstant;
|
|
|
+import com.xs.core.common.content.UserContext;
|
|
|
+import com.xs.core.common.content.UserContextHolder;
|
|
|
+import com.xs.core.common.enums.CoinTransactionCategoryEnum;
|
|
|
+import com.xs.core.common.enums.CoinTransactionTypeEnum;
|
|
|
+import com.xs.core.common.validation.CheckUtils;
|
|
|
+import com.xs.core.model.coin.dto.GoldRateUpgradesState;
|
|
|
+import com.xs.core.model.coin.entity.CoinSpeedUpgradesRules;
|
|
|
+import com.xs.core.model.coin.entity.GoldCoinProdRecord;
|
|
|
+import com.xs.core.model.coin.msg.CoinProducerMessage;
|
|
|
+import com.xs.core.model.coin.entity.GoldCoinProdState;
|
|
|
+import com.xs.core.model.coin.entity.UserCoinSpeedUpgradesRecord;
|
|
|
+import com.xs.core.model.coin.req.CoinSpeedUpgradesRulesReq;
|
|
|
+import com.xs.core.model.coin.req.TemporaryRateReq;
|
|
|
+import com.xs.core.model.coin.resp.GoldCoinProdStateResp;
|
|
|
+import com.xs.core.model.user.entity.TgUser;
|
|
|
+import com.xs.core.mq.producer.GoldCoinMessageProducer;
|
|
|
+import com.xs.core.service.coin.*;
|
|
|
+import com.xs.core.service.redis.RedisService;
|
|
|
+import com.xs.core.service.user.ITgUserService;
|
|
|
+import lombok.extern.slf4j.Slf4j;
|
|
|
+import org.springframework.beans.BeanUtils;
|
|
|
+import org.springframework.beans.factory.annotation.Autowired;
|
|
|
+import org.springframework.stereotype.Service;
|
|
|
+import org.springframework.transaction.annotation.Transactional;
|
|
|
+
|
|
|
+import java.math.BigDecimal;
|
|
|
+import java.time.Instant;
|
|
|
+import java.time.LocalDateTime;
|
|
|
+import java.util.List;
|
|
|
+
|
|
|
+@Service
|
|
|
+@Slf4j
|
|
|
+public class GoldCoinServiceImpl implements GoldCoinService {
|
|
|
+ @Autowired
|
|
|
+ private IGoldCoinProdRecordService recordService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private ITgUserService userService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private RedisService redisService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IUserCoinSpeedUpgradesRecordService speedRecordService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private IGoldCoinProdStateService stateService;
|
|
|
+
|
|
|
+ @Autowired
|
|
|
+ private GoldCoinMessageProducer messageProducer;
|
|
|
+
|
|
|
+ private ICoinSpeedUpgradesRulesService speedRulesService;
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public void startProductGoldCoin() {
|
|
|
+ UserContext userContext = UserContextHolder.getContext();
|
|
|
+ //先获取金币产出状态
|
|
|
+ GoldCoinProdState goldCoinProdState = stateService.getGoldCoinProdStateByUser(userContext.getId());
|
|
|
+ if (ObjUtil.isNull(goldCoinProdState)) {
|
|
|
+ //如果为空,就创建一个
|
|
|
+ goldCoinProdState = firstInitGoldCoinProdState(userContext);
|
|
|
+ stateService.save(goldCoinProdState);
|
|
|
+ }
|
|
|
+ //判断是否已经在产出中 或者 待领取的金币未领取
|
|
|
+ checkProductState(goldCoinProdState, userContext);
|
|
|
+ //1、初始化产出信息
|
|
|
+ initializeProductInfo(goldCoinProdState, userContext);
|
|
|
+ //2、保存金币产出状态到redis
|
|
|
+ saveSateToRedis(goldCoinProdState, userContext.getId());
|
|
|
+ //3、保存金币产出状态到数据库
|
|
|
+ stateService.updateById(goldCoinProdState);
|
|
|
+ //4、发送延时循环计算的消息
|
|
|
+ CoinProducerMessage message = new CoinProducerMessage();
|
|
|
+ BeanUtils.copyProperties(goldCoinProdState, message);
|
|
|
+ messageProducer.sendDelayCalculationMessage(message);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void checkProductState(GoldCoinProdState goldCoinProdState, UserContext userContext) {
|
|
|
+ CheckUtils.throwIf(Boolean.TRUE.equals(goldCoinProdState.getRunning()), "金币产出中,不能重复开启");
|
|
|
+ //若是非运行状态 则检查待领取的金币
|
|
|
+ GoldCoinProdRecord record = recordService.getNoReciviedGoldCoinProdRecordByUserId(userContext.getId());
|
|
|
+ CheckUtils.throwIf(ObjUtil.isNotNull(record) && !record.getReceiving(), "待领取的金币未领取,不能重复开启");
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 初始化产出信息
|
|
|
+ *
|
|
|
+ * @param goldCoinProdState 金币产出状态
|
|
|
+ * @param userContext 用户上下文
|
|
|
+ */
|
|
|
+ private void initializeProductInfo(GoldCoinProdState goldCoinProdState, UserContext userContext) {
|
|
|
+ //1、开启金币产出状态
|
|
|
+ goldCoinProdState.setRunning(Boolean.TRUE);
|
|
|
+ long secondsTimestamp = Instant.now().getEpochSecond();
|
|
|
+ goldCoinProdState.setStartTimestamp(secondsTimestamp);
|
|
|
+ goldCoinProdState.setEndTimestamp(secondsTimestamp + GoldCoinConstant.GOLD_COIN_PRODUCT_TIME);
|
|
|
+ goldCoinProdState.setLastTimestamp(secondsTimestamp);
|
|
|
+ goldCoinProdState.setResidueTimestamp(GoldCoinConstant.GOLD_COIN_PRODUCT_TIME);
|
|
|
+ goldCoinProdState.setCurrentValue(BigDecimal.ZERO);
|
|
|
+ //2、获取永久速率
|
|
|
+ UserCoinSpeedUpgradesRecord speedUpgradesRecord = speedRecordService.getSpeedUpgradesRecordByUserId(userContext.getId());
|
|
|
+ goldCoinProdState.setCurrentRate(speedUpgradesRecord.getProductRate());
|
|
|
+ goldCoinProdState.setDurableRate(speedUpgradesRecord.getProductRate());
|
|
|
+ }
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 首次初始化状态信息
|
|
|
+ *
|
|
|
+ * @param userContext
|
|
|
+ * @return
|
|
|
+ */
|
|
|
+ private GoldCoinProdState firstInitGoldCoinProdState(UserContext userContext) {
|
|
|
+ GoldCoinProdState goldCoinProdState = new GoldCoinProdState();
|
|
|
+ goldCoinProdState.setUserId(userContext.getId());
|
|
|
+ goldCoinProdState.setRunning(Boolean.FALSE);
|
|
|
+ //获取永久速率
|
|
|
+ UserCoinSpeedUpgradesRecord speedUpgradesRecord = speedRecordService.getSpeedUpgradesRecordByUserId(userContext.getId());
|
|
|
+ if (ObjUtil.isNull(speedUpgradesRecord)) {
|
|
|
+ //如果为空,就创建一个
|
|
|
+ speedUpgradesRecord = new UserCoinSpeedUpgradesRecord();
|
|
|
+ speedUpgradesRecord.setUserId(userContext.getId());
|
|
|
+ speedUpgradesRecord.setSpeedLevel(0);
|
|
|
+ speedUpgradesRecord.setProductRate(GoldCoinConstant.GOLD_INIT_RATE);
|
|
|
+ speedUpgradesRecord.setUpgradesTime(LocalDateTime.now());
|
|
|
+ speedRecordService.save(speedUpgradesRecord);
|
|
|
+ }
|
|
|
+ goldCoinProdState.setCurrentRate(speedUpgradesRecord.getProductRate());
|
|
|
+ goldCoinProdState.setCurrentValue(BigDecimal.ZERO);
|
|
|
+ //long secondsTimestamp = LocalDateTime.now().toEpochSecond(ZoneOffset.UTC);
|
|
|
+ long secondsTimestamp = Instant.now().getEpochSecond();
|
|
|
+ goldCoinProdState.setStartTimestamp(secondsTimestamp);
|
|
|
+ goldCoinProdState.setEndTimestamp(secondsTimestamp + GoldCoinConstant.GOLD_COIN_PRODUCT_TIME);
|
|
|
+ goldCoinProdState.setResidueTimestamp(GoldCoinConstant.GOLD_COIN_PRODUCT_TIME);
|
|
|
+ goldCoinProdState.setLastTimestamp(secondsTimestamp);
|
|
|
+ goldCoinProdState.setDurableRate(speedUpgradesRecord.getProductRate());
|
|
|
+ goldCoinProdState.setTemporaryRate("0");
|
|
|
+ return goldCoinProdState;
|
|
|
+ }
|
|
|
+
|
|
|
+ private void saveSateToRedis(GoldCoinProdState goldCoinProdState, Long userId) {
|
|
|
+ String key = GoldCoinConstant.GOLD_COIN_STATE_KEY + userId;
|
|
|
+ redisService.set(key, goldCoinProdState);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public void collectGoldCoin() {
|
|
|
+ UserContext userContext = UserContextHolder.getContext();
|
|
|
+ //先查询是否存在待领取的金币
|
|
|
+ GoldCoinProdRecord record = recordService.getNoReciviedGoldCoinProdRecordByUserId(userContext.getId());
|
|
|
+ GoldCoinProdState state = stateService.getGoldCoinProdStateByUser(userContext.getId());
|
|
|
+ CheckUtils.throwIfNull(record, "没有待领取的金币");
|
|
|
+ //把金币结算到用户的账户中
|
|
|
+ userService.coinTransaction(CoinTransactionTypeEnum.ADD, CoinTransactionCategoryEnum.PRODUCT, "金币产出结算", "挂机产出金币结算", record.getGoldCoinYieldTotal(), userContext.getId());
|
|
|
+ //更新金币产出记录的领取状态
|
|
|
+ resetGoldCoinProdState(state);
|
|
|
+ record.setReceiving(Boolean.TRUE);
|
|
|
+ record.setReceivedTime(LocalDateTime.now());
|
|
|
+ stateService.updateById(state);
|
|
|
+ recordService.updateById(record);
|
|
|
+ }
|
|
|
+
|
|
|
+ private void resetGoldCoinProdState(GoldCoinProdState state) {
|
|
|
+ state.setCurrentValue(BigDecimal.ZERO);
|
|
|
+ state.setLastTimestamp(null);
|
|
|
+ state.setEndTimestamp(null);
|
|
|
+ state.setResidueTimestamp(null);
|
|
|
+ state.setCurrentRate(state.getDurableRate());
|
|
|
+ state.setTemporaryRate("0");
|
|
|
+ state.setRunning(Boolean.FALSE);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void permanentBoostProductGoldCoinRate(CoinSpeedUpgradesRulesReq req) {
|
|
|
+ UserContext context = UserContextHolder.getContext();
|
|
|
+ //获取金币升级速率
|
|
|
+ CoinSpeedUpgradesRules rules = speedRulesService.getById(req.getRuleId());
|
|
|
+ TgUser user = userService.getById(context.getId());
|
|
|
+ BigDecimal goldCoinAmount = user.getGoldCoinAmount();
|
|
|
+ Integer consumeGoldCoin = rules.getConsumeGoldCoin();
|
|
|
+ CheckUtils.throwIf(consumeGoldCoin > goldCoinAmount.intValue(), "金币不足");
|
|
|
+ //扣除金币
|
|
|
+ userService.coinTransaction(CoinTransactionTypeEnum.SUBTRACT, CoinTransactionCategoryEnum.SPEED_UPGRADES, "金币产出速率升级", "金币产出速率升级", new BigDecimal(consumeGoldCoin), context.getId());
|
|
|
+ //增加一条速率升级记录
|
|
|
+ UserCoinSpeedUpgradesRecord speedUpgradesRecord = new UserCoinSpeedUpgradesRecord();
|
|
|
+ speedUpgradesRecord.setSpeedLevel(rules.getLevel());
|
|
|
+ String numericalValue = rules.getNumericalValue();
|
|
|
+ speedUpgradesRecord.setProductRate(numericalValue);
|
|
|
+ speedUpgradesRecord.setUserId(context.getId());
|
|
|
+ speedUpgradesRecord.setUpgradesTime(LocalDateTime.now());
|
|
|
+ speedRecordService.save(speedUpgradesRecord);
|
|
|
+ String rateKey = GoldCoinConstant.GOLD_COIN_RATE_UPGRADES_KEY + context.getId();
|
|
|
+ //判断当前是否在在产出中
|
|
|
+ GoldCoinProdState goldCoinProdState = stateService.getGoldCoinProdStateByUser(context.getId());
|
|
|
+ if (ObjUtil.isNotNull(goldCoinProdState) && Boolean.TRUE.equals(goldCoinProdState.getRunning())) {
|
|
|
+ long epochSecond = Instant.now().getEpochSecond();
|
|
|
+ //把速率写入到redis中
|
|
|
+ //若是存在key 说明之前进行过速率升级 则把当前的速率加上新的速率
|
|
|
+ if (redisService.hasKey(rateKey)) {
|
|
|
+ GoldRateUpgradesState upgradesState = redisService.get(rateKey, GoldRateUpgradesState.class);
|
|
|
+ String temporaryRateStr = goldCoinProdState.getTemporaryRate();
|
|
|
+
|
|
|
+ BigDecimal temporaryRate = new BigDecimal(temporaryRateStr);
|
|
|
+ BigDecimal numericalValueDec = new BigDecimal(numericalValue);
|
|
|
+ BigDecimal currentRate = temporaryRate.add(numericalValueDec);
|
|
|
+
|
|
|
+ upgradesState.setCurrentRate(currentRate.toPlainString());
|
|
|
+ upgradesState.setDurableRate(numericalValue);
|
|
|
+ upgradesState.setRateUpdateFlag(1);
|
|
|
+ upgradesState.setUpdateTimestamp(epochSecond);
|
|
|
+ //设置最新永久速率
|
|
|
+ redisService.set(rateKey, upgradesState);
|
|
|
+ } else {
|
|
|
+ //设置用户新永久速率 不存在key则说明是第一次进行速率升级
|
|
|
+ GoldRateUpgradesState upgradesState = new GoldRateUpgradesState();
|
|
|
+ BigDecimal currentRate = new BigDecimal(numericalValue);
|
|
|
+
|
|
|
+ upgradesState.setHasTemporaryRate(false);
|
|
|
+ upgradesState.setDurableRate(numericalValue);
|
|
|
+ upgradesState.setCurrentRate(currentRate.toPlainString());
|
|
|
+ upgradesState.setUpdateTimestamp(epochSecond);
|
|
|
+ upgradesState.setTemporaryRate("");
|
|
|
+ upgradesState.setRateUpdateFlag(1);
|
|
|
+ redisService.set(rateKey, upgradesState);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public void temporaryBoostProductGoldCoinRate(TemporaryRateReq temporaryRateReq) {
|
|
|
+ String rate = temporaryRateReq.getRate();
|
|
|
+ UserContext context = UserContextHolder.getContext();
|
|
|
+ CheckUtils.throwIfNull(context, "用户未登录或用户信息为空");
|
|
|
+ //获取用户的金币产出状态
|
|
|
+ GoldCoinProdState goldCoinProdState = stateService.getGoldCoinProdStateByUser(context.getId());
|
|
|
+ CheckUtils.throwIf(Boolean.FALSE.equals(goldCoinProdState.getRunning()), "用户未开启金币产出");
|
|
|
+ //获取用户的临时速率
|
|
|
+ String rateTemporarilyKey = GoldCoinConstant.GOLD_COIN_RATE_UPGRADES_KEY + context.getId();
|
|
|
+ String goldCoinStateKey = GoldCoinConstant.GOLD_COIN_STATE_KEY + context.getId();
|
|
|
+ long epochSecond = Instant.now().getEpochSecond();
|
|
|
+ //判断用户的速率升级记录是否存在 存在则不允许升级
|
|
|
+ if (redisService.hasKey(rateTemporarilyKey)) {
|
|
|
+ GoldRateUpgradesState upgradesState = redisService.get(rateTemporarilyKey, GoldRateUpgradesState.class);
|
|
|
+ CheckUtils.throwIf(upgradesState.isHasTemporaryRate(), "当前生产批次已存在临时速率,暂不允许升级");
|
|
|
+ upgradesState.setHasTemporaryRate(true);
|
|
|
+ String currentRateStr = upgradesState.getCurrentRate();
|
|
|
+ BigDecimal currentRate = new BigDecimal(currentRateStr);
|
|
|
+ BigDecimal added = currentRate.add(new BigDecimal(rate));
|
|
|
+ upgradesState.setCurrentRate(added.toPlainString());
|
|
|
+ upgradesState.setTemporaryRate(rate);
|
|
|
+ upgradesState.setRateUpdateFlag(1);
|
|
|
+ upgradesState.setUpdateTimestamp(epochSecond);
|
|
|
+ //设置用户的临时速率
|
|
|
+ redisService.set(rateTemporarilyKey, upgradesState);
|
|
|
+ } else {
|
|
|
+ //设置用户的临时速率
|
|
|
+ GoldRateUpgradesState upgradesState = new GoldRateUpgradesState();
|
|
|
+ upgradesState.setHasTemporaryRate(true);
|
|
|
+ GoldCoinProdState coinProdState = redisService.get(goldCoinStateKey, GoldCoinProdState.class);
|
|
|
+ CheckUtils.throwIfNull(coinProdState, "用户未开启金币产出");
|
|
|
+ String currentRateStr = coinProdState.getCurrentRate();
|
|
|
+ BigDecimal currentRate = new BigDecimal(currentRateStr);
|
|
|
+ BigDecimal added = currentRate.add(new BigDecimal(rate));
|
|
|
+ upgradesState.setCurrentRate(added.toPlainString());
|
|
|
+ upgradesState.setUpdateTimestamp(epochSecond);
|
|
|
+ upgradesState.setTemporaryRate(rate);
|
|
|
+ upgradesState.setRateUpdateFlag(1);
|
|
|
+ redisService.set(rateTemporarilyKey, upgradesState);
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public GoldCoinProdStateResp getGoldCoinProdState() {
|
|
|
+ UserContext context = UserContextHolder.getContext();
|
|
|
+ CheckUtils.throwIfNull(context, "用户未登录或用户信息为空");
|
|
|
+ GoldCoinProdStateResp state = stateService.getGoldCoinProdRespStateByUser(context.getId());
|
|
|
+ if (state == null) {
|
|
|
+ state = new GoldCoinProdStateResp();
|
|
|
+ UserCoinSpeedUpgradesRecord speedUpgradesRecord = speedRecordService.getSpeedUpgradesRecordByUserId(context.getId());
|
|
|
+ state.setCurrentValue(BigDecimal.ZERO);
|
|
|
+ state.setCurrentRate(ObjUtil.isNotNull(speedUpgradesRecord) ? speedUpgradesRecord.getProductRate() : GoldCoinConstant.GOLD_INIT_RATE);
|
|
|
+ state.setDurableRate(ObjUtil.isNotNull(speedUpgradesRecord) ? speedUpgradesRecord.getProductRate() : GoldCoinConstant.GOLD_INIT_RATE);
|
|
|
+ state.setStatus(0);
|
|
|
+ }
|
|
|
+ return state;
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ @Transactional
|
|
|
+ public void completedGoldCoinSettlement(GoldCoinProdState coinProdState) {
|
|
|
+ String productKey = GoldCoinConstant.GOLD_COIN_STATE_KEY + coinProdState.getUserId();
|
|
|
+ String rateTemporarilyKey = GoldCoinConstant.GOLD_COIN_RATE_UPGRADES_KEY + coinProdState.getUserId();
|
|
|
+
|
|
|
+ //生成金币生产记录
|
|
|
+ GoldCoinProdRecord goldCoinProdRecord = new GoldCoinProdRecord();
|
|
|
+ goldCoinProdRecord.setUserId(coinProdState.getUserId());
|
|
|
+ goldCoinProdRecord.setGoldCoinYieldTotal(coinProdState.getCurrentValue());
|
|
|
+ goldCoinProdRecord.setReceiving(false);
|
|
|
+ goldCoinProdRecord.setYieldTime(LocalDateTime.now());
|
|
|
+ goldCoinProdRecord.setStartTimestamp(coinProdState.getStartTimestamp());
|
|
|
+ goldCoinProdRecord.setEndTimestamp(coinProdState.getEndTimestamp());
|
|
|
+
|
|
|
+ // 重置金币产出状态
|
|
|
+ coinProdState.setCompleted(Boolean.TRUE);
|
|
|
+ coinProdState.setRunning(Boolean.FALSE);
|
|
|
+ recordService.save(goldCoinProdRecord);
|
|
|
+ stateService.updateById(coinProdState);
|
|
|
+ //清除redis中的金币产出状态
|
|
|
+ redisService.del(productKey);
|
|
|
+ //清除redis中的速率升级记录
|
|
|
+ redisService.del(rateTemporarilyKey);
|
|
|
+ }
|
|
|
+
|
|
|
+ @Override
|
|
|
+ public List<CoinSpeedUpgradesRules> getRateUpgradesRulesByUser() {
|
|
|
+ UserContext context = UserContextHolder.getContext();
|
|
|
+ return speedRulesService.getCoinSpeedUpgradesRulesByUserId(context.getId());
|
|
|
+ }
|
|
|
+}
|