package com.td.boss.game.complayergoods.controller;

import com.td.boss.common.controller.*;
import com.td.boss.common.pojo.Result;
import com.td.boss.config.enums.ResultEnum;
import com.td.boss.game.comexplainland.service.ComExplainLandService;
import com.td.boss.game.comexplainland.vo.ComExplainLandVo;
import com.td.boss.game.comfruit.pojo.ComFruit;
import com.td.boss.game.comfruit.service.ComFruitService;
import com.td.boss.game.comfruit.vo.ComFruitAmountVo;
import com.td.boss.game.comfruit.vo.ComFruitVo;
import com.td.boss.game.commallother.pojo.ComMallOther;
import com.td.boss.game.commallother.service.ComMallOtherService;
import com.td.boss.game.commallother.vo.ComMallOtherVo;
import com.td.boss.game.commallseed.service.ComMallSeedService;
import com.td.boss.game.commallseed.vo.ComMallSeedVo;
import com.td.boss.game.complayerdog.pojo.ComPlayerDog;
import com.td.boss.game.complayerdog.service.ComPlayerDogService;
import com.td.boss.game.complayerdog.vo.ComPlayerDogVo;
import com.td.boss.game.complayergoods.pojo.ComPlayerGoods;
import com.td.boss.game.complayergoods.vo.ComPlayerGoodsSimpleVo;
import com.td.boss.game.complayergoods.vo.ComPlayerGoodsVo;
import com.td.boss.game.complayergoods.service.ComPlayerGoodsService;
import com.td.boss.game.complayerland.pojo.ComPlayerLand;
import com.td.boss.game.complayerland.pojo.ComPlayerLandAndCanSteal;
import com.td.boss.game.complayerland.service.ComPlayerLandService;
import com.td.boss.game.complayerland.vo.ComPlayerLandVo;
import com.td.boss.game.complayerlog.pojo.ComPlayerLog;
import com.td.boss.game.complayerlog.service.ComPlayerLogService;
import com.td.boss.game.complayerlog.vo.ComPlayerLogVo;
import com.td.boss.game.complayerprofit.pojo.ComPlayerProfit;
import com.td.boss.game.complayerprofit.service.ComPlayerProfitService;
import com.td.boss.game.complayerprofit.vo.ComPlayerProfitVo;
import com.td.boss.game.complayers.vo.ComPlayersVo;
import com.td.boss.game.complayersattri.service.ComPlayersAttriService;
import com.td.boss.game.complayersattri.vo.ComPlayersAttriSimpleVo;
import com.td.boss.game.complayersattri.vo.ComPlayersAttriVo;
import com.td.boss.game.comsetting.pojo.ComSetting;
import com.td.boss.game.comsetting.service.ComSettingService;
import com.td.boss.game.comsetting.vo.ComSettingVo;
import com.td.boss.game.comsnbtran.pojo.ComSnbTran;
import com.td.boss.game.comsnbtran.service.ComSnbTranService;
import com.td.boss.game.comsnbtran.vo.ComSnbTranVo;
import com.td.boss.game.comusers.service.ComUsersService;
import com.td.boss.game.comusers.vo.ComUsersVo;
import com.td.boss.util.*;
import net.bytebuddy.asm.Advice;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.*;

import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/game/comPlayerGoods/")
public class ComPlayerGoodsController extends CommonController<ComPlayerGoodsVo, ComPlayerGoods, String> {
    @Autowired
    private ComPlayerGoodsService comPlayerGoodsService;
    @Autowired
    private ComMallSeedService comMallSeedService;
    @Autowired
    private ComFruitService comFruitService;

    @Autowired
    private ComPlayerLandService comPlayerLandService;

    @Autowired
    private ComPlayerProfitService comPlayerProfitService;

    @Autowired
    private ComUsersService comUsersService;

    @Autowired
    private ComPlayersAttriService comPlayersAttriService;

    //交易表service
    @Autowired
    private ComSnbTranService comSnbTranService;

    //背包操作日志
    @Autowired
    private ComPlayerLogService comPlayerLogService;

    @Autowired
    private ComExplainLandService comExplainLandService;

    //配置信息
    @Autowired
    private ComSettingService comSettingService;

    @Autowired
    private ComPlayerDogService comPlayerDogService;

    @Autowired
    private ComMallOtherService comMallOtherService;


    @Autowired
    private RedisLock redisLock;

    /**
     * 获取仓库 全部数据，目前使用goods_type区分种子和果实
     *
     * @param userId
     * @return
     */
    @GetMapping("getSeedAndFruit")
    public Result<Map> getSeedAndFruitFunction(@RequestParam(value = "userId") String userId) {
        List<ComPlayerGoods> comPlayerGoodsList = comPlayerGoodsService.findAllByUserId(userId);
        List<ComMallSeedVo> seedList = new ArrayList<>();
        List<ComFruitAmountVo> fruitList = new ArrayList<>();
        comPlayerGoodsList.stream().map(e -> {
            if (e.getGoodsType().equals(0)) {
                //种子类型
                ComMallSeedVo comMallSeedVo = comMallSeedService.findById(e.getGoodsIndex());
                //数量要用背包的数量
                comMallSeedVo.setAmount(e.getAmount());
                seedList.add(comMallSeedVo);

            } else if (e.getGoodsType().equals(1)) {
                //果实类型
                //数量要用背包的数量
                ComFruitVo comFruitVo = comFruitService.findById(e.getGoodsIndex());
                ComFruitAmountVo comFruitAmountVo = CopyUtil.copy(comFruitVo, ComFruitAmountVo.class);
                comFruitAmountVo.setAmount(e.getAmount());
                comFruitAmountVo.setAmountPart(e.getAmountPart());
                fruitList.add(comFruitAmountVo);
            }
            return e;
        }).collect(Collectors.toList());
        Map map = new HashMap();
        map.put("seed", seedList);
        map.put("fruit", fruitList);
        return Result.of(map);
    }


    /**
     * 获取购买的种子状态，如果webSocket未通，
     * 使用轮询方法查询更新状态
     *
     * @param userId
     * @return
     */
    @GetMapping("getSeedState")
    public Result<Map> getSeedStateFunction(
            @RequestParam(value = "userId") String userId,
            @RequestParam(value = "seedId") Integer seedId,
            @RequestParam(value = "currentAmount") Integer currentAmount
    ) {
        //查询背包种子存在的数量
        ComPlayerGoods comPlayerGood = comPlayerGoodsService.findByUserIdAndIndexAndType(userId, seedId, 0);
        if (comPlayerGood == null) {
            //如果背包种子是空的，说明还未添加上去
            return Result.of(null, false, ResultEnum.SEED_DATA_ERROR.getMessage(), ResultEnum.SEED_DATA_ERROR.getCode());
        }
        if (comPlayerGood.getAmount() <= currentAmount) {
            //如果背包数量等于或者小于传进来的，提示轮询未更新
            return Result.of(null, false, ResultEnum.SEED_AMOUNT_ERROR.getMessage(), ResultEnum.SEED_AMOUNT_ERROR.getCode());
        } else {
            Map map = new HashMap();
            map.put("msg", "背包种子更新成功！");
            map.put("goodsAmount", comPlayerGood.getAmount());
            map.put("currentAmount", currentAmount);
            return Result.of(map);
        }


    }


    /**
     * 收获时候，传入土地id和种子id
     * <p>
     * <p>
     * 收获时候，操作用户snb时候，需要加锁，防止用户收取时候遗漏计算snb
     *
     * @param userId
     * @param landId
     * @return
     */
    @PostMapping("addFruit")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public Result<Map> addFruitFunction(
            @RequestParam(value = "userId") String userId,
            @RequestParam(value = "landId") Integer landId) {
        //todo 确认下是否多倍收成
        ComPlayerLand comPlayerLand = comPlayerLandService.findByUserIdAndLandId(userId, landId);
        //土地数据不存在
        if (comPlayerLand == null) {
            return Result.of(null, false, ResultEnum.LAND_DATA_ERROR.getMessage(), ResultEnum.LAND_DATA_ERROR.getCode());
        }
        //土地未租赁
        if (!comPlayerLand.getIsLease().equals(1)) {
            return Result.of(null, false, ResultEnum.LAND_NOT_LEASE.getMessage(), ResultEnum.LAND_NOT_LEASE.getCode());
        }
        //土地未种植
        if (comPlayerLand.getIsPlant().equals(0)) {
            return Result.of(null, false, ResultEnum.LAND_NOT_PLANT.getMessage(), ResultEnum.LAND_NOT_PLANT.getCode());
        }
        //todo 判断土地租赁是否到期，是到期不给种植
        //租赁的剩余天数 。
        Long leaseDaysMill = comPlayerLand.getLeaseTime().getTime() - DateUtil.getNowDate().getTime();
        if (leaseDaysMill <= 0) {
            // 清除记录的数据
            // 1.土地租赁到期，删除土地租赁数据
            comPlayerLandService.delete(comPlayerLand.getId());
            // 2.把删除的这个土地的数据记录到com_explain_land中去
            ComExplainLandVo comExplainLandVo = new ComExplainLandVo();
            comExplainLandVo = CopyUtil.copy(comPlayerLand, ComExplainLandVo.class);
            comExplainLandVo.setLandId(comPlayerLand.getConfigLandId());
            comExplainLandVo.setLandCreate(comPlayerLand.getCreateTime());
            comExplainLandVo.setExplainType(0);
            comExplainLandVo.setExplainDescribe("收获果实时:土地租赁过期回收");
            comExplainLandVo.setCreateTime(new Date());
            comExplainLandVo.setUpdateTime(new Date());
            comExplainLandService.save(comExplainLandVo);
            return Result.of(null, false, ResultEnum.LAND_LEASE_EXPIRED.getMessage(), ResultEnum.LAND_LEASE_EXPIRED.getCode());
        }

        //获取种植的种子
        ComMallSeedVo comMallSeedVo = comMallSeedService.findById(comPlayerLand.getPlantId());
        if (comMallSeedVo == null) {
            return Result.of(null, false, ResultEnum.SEED_DATA_ERROR.getMessage(), ResultEnum.SEED_DATA_ERROR.getCode());
        }

        // 收获，同时重置土地状态，先判断是否成熟
        //当前时间减去 成熟期，判断是否成熟
        Date _harvestTime = DateUtil.getNowDateMinusDay(comMallSeedVo.getMaturity());
        // 两个时间差, 逻辑是_harvestTime 时间慢慢接近种植时间
        long diff = comPlayerLand.getPlantStart().getTime() - _harvestTime.getTime();
        //if (DateUtil.getNowDateMinusDay(comMallSeedVo.getMaturity()).getTime() < comPlayerLand.getPlantStart().getTime())
        if (diff > 0) {
            //如果当前时间减去种子的成熟期，还是小于种植日期，则判断未成熟
            return Result.of(null, false, ResultEnum.SEED_DATE_ERROR.getMessage(), ResultEnum.SEED_DATE_ERROR.getCode());
        }
        //获取数据库相关配置
        ComSettingVo comSettingVo = comSettingService.get("1").getData();
        if (comSettingVo.equals(null)) {
            return Result.of(null, false, ResultEnum.SETTING_IS_NULL.getMessage(), ResultEnum.SETTING_IS_NULL.getCode());
        }

        //需要redis 加锁
        long time = System.currentTimeMillis() + RedisData.getPlayerGoodsTimeout();
        //获取用户的plantFlag，因为之前的用户没有设置这个flag，所以设为第一次，用 userId 和 landId 组合成key
        String _redisKey = comPlayerLand.getPlantFlag();
        if (!StringUtils.hasText(_redisKey)) {
            //return Result.of(null, false, ResultEnum.LAND_PLANT_FLAG_IS_NULL.getMessage(), ResultEnum.LAND_PLANT_FLAG_IS_NULL.getCode());
            _redisKey = comPlayerLand.getUserId() + "or" + comPlayerLand.getConfigLandId();
        }
        Map map = new HashMap();
        //todo 总共偷去的数量
        Double _stealProfits = 0d;
        try {
            // 如果存在plantFlag 说明是新种植的
            if (!redisLock.lock(_redisKey, String.valueOf(time))) {
                return Result.of(null, false, ResultEnum.LAND_STEAL_LOCK.getMessage(), ResultEnum.LAND_STEAL_LOCK.getCode());
            }

            //设置一个未种植状态即可
            comPlayerLand.setIsPlant(0);
            ComPlayerLandVo comPlayerLandVo = CopyUtil.copy(comPlayerLand, ComPlayerLandVo.class);
            comPlayerLandService.save(comPlayerLandVo);

            //计算一个收获量, 租赁倍数* 租赁日期下的产量
            Integer _amount = 0;
            Double _amountPart = 0d;
            if (comPlayerLand.getLeaseDate().equals(1)) {
                _amount = comPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest1();
            } else if (comPlayerLand.getLeaseDate().equals(2)) {
                _amount = comPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest2();
            } else if (comPlayerLand.getLeaseDate().equals(3)) {
                _amount = comPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest3();
            }
            //todo 收获果实时候,需要减去一个被偷取的利润，然后判断一个是否大于设置的利润，超出了报异常,并且收取的数量不能为小于0
            //     拿出计算利润，收获量减去种子的成本(snb)后的百分之20 ，后面需要后台可调整
            Integer _profit = _amount - comPlayerLand.getLeaseMultiple() * comMallSeedVo.getPriceSnb();
            // 配置的参数。这里先直接定义
            Double profitConfig = comSettingVo.getProfit(), stealRatioMaxConfig = comSettingVo.getStealMaxRatio(), stealRatioMinConfig = comSettingVo.getStealMinRatio(), finallyGetRatioConfig = comSettingVo.getFinalRatio();

            Double _profitDouble = _profit.doubleValue();
            Double _residualProfit = DoubleUtil.mul(_profitDouble, DoubleUtil.sub(1d, profitConfig));//如果 profitConfig 0.2，剩余利润就是0.8；
            Double _stealAmount = DoubleUtil.sub(_profitDouble, _residualProfit); //可偷取的利润

            //todo 计算总共损失的数量
            List<ComPlayerProfit> comPlayerProfits = comPlayerProfitService.findByUserIdAndPlantFlagAndLandId(userId, _redisKey, comPlayerLand.getConfigLandId());
            for (int i = 0; i < comPlayerProfits.size(); i++) {
                ComPlayerProfit temp = comPlayerProfits.get(i);
                _stealProfits = DoubleUtil.add(_stealProfits, temp.getStolen());
            }
            Double _userHarvestPart = DoubleUtil.sub(_stealAmount, _stealProfits);
            if (_userHarvestPart < 0.0) {
                //差值不能为负数
                _userHarvestPart = 0d;
                //相当于被偷取全部利润，这里返回一个被偷去的全部利润值
                map.put("lossAmount", _stealAmount);
            } else {
                //todo 返回一个被偷的数量
                map.put("lossAmount", _stealProfits);
            }

            //todo 利润
            _amount -= _profit;
            //用户收取的对应数量,固定收入+被偷取后剩余的部分
            _amountPart = DoubleUtil.add(_residualProfit, _userHarvestPart);
            //果实 Type =1
            ComPlayerGoods comPlayerGoodsSimpleVo = comPlayerGoodsService.findByUserIdAndIndexAndType(userId, comMallSeedVo.getHarvestId(), 1);
            Integer _beforeAmount = 0;
            Double _beforeProfitPart = 0d, _afterProfitPart = 0d;
            if (comPlayerGoodsSimpleVo == null) {
                //背包增加果实
                comPlayerGoodsSimpleVo = new ComPlayerGoods();
                comPlayerGoodsSimpleVo.setUserId(userId);
                comPlayerGoodsSimpleVo.setGoodsIndex(comMallSeedVo.getHarvestId());
                comPlayerGoodsSimpleVo.setGoodsType(1);
                comPlayerGoodsSimpleVo.setName(comMallSeedVo.getHarvestName());
                //用种子的picture
                comPlayerGoodsSimpleVo.setPictureName(comMallSeedVo.getPicture());
                comPlayerGoodsSimpleVo.setAmount(_amount);
                comPlayerGoodsSimpleVo.setAmountPart(_amountPart);

                _beforeAmount = 0;
                _beforeProfitPart = 0d;
                _afterProfitPart = comPlayerGoodsSimpleVo.getAmountPart();
            } else {
                _beforeAmount = comPlayerGoodsSimpleVo.getAmount();
                _beforeProfitPart = comPlayerGoodsSimpleVo.getAmountPart();

                comPlayerGoodsSimpleVo.setAmount(comPlayerGoodsSimpleVo.getAmount() + _amount);
                //用户利润部分
                comPlayerGoodsSimpleVo.setAmountPart(DoubleUtil.add(comPlayerGoodsSimpleVo.getAmountPart(), _amountPart));

                _afterProfitPart = comPlayerGoodsSimpleVo.getAmountPart();
            }

            ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoodsSimpleVo, ComPlayerGoodsVo.class);
            comPlayerGoodsService.save(comPlayerGoodsVo);

            //todo 记录一个收获的操作日志
            ComPlayerLog _playerLog = new ComPlayerLog();
            _playerLog.setUserId(userId);
            _playerLog.setTId(comMallSeedVo.getHarvestId());
            _playerLog.setTName(comMallSeedVo.getHarvestName());
            _playerLog.setTType(0);//设置一个默认水果id
            _playerLog.setTAmount(_amount);
            _playerLog.setBeforeAmount(_beforeAmount);
            ////交易后的数据
            _playerLog.setAfterAmount(comPlayerGoodsSimpleVo.getAmount());
            //收取时候信息记录
            _playerLog.setTPart(_amountPart);
            _playerLog.setTLoss(_stealProfits); //损失的部分
            _playerLog.setBeforePart(_beforeProfitPart);
            _playerLog.setAfterPart(_afterProfitPart);
            _playerLog.setLMultiple(comPlayerLand.getLeaseMultiple());
            ComPlayerLogVo _playerLogVo = CopyUtil.copy(_playerLog, ComPlayerLogVo.class);
            comPlayerLogService.save(_playerLogVo);

            //对应的，存在plantFlag则解锁
            redisLock.unlock(_redisKey, String.valueOf(time));
        } catch (Exception e) {
            //对应的，存在plantFlag则解锁
            redisLock.unlock(_redisKey, String.valueOf(time));
            throw new RuntimeException(e.getMessage());
        }
        map.put("msg", "成功收取果实！");
        return Result.of(map);
    }

    /**
     * 按土地收取水果果实时候，传入对方用户id 和土地id
     * <p>
     * <p>
     * 收获时候，操作用户snb时候，需要加锁，防止用户收取时候遗漏计算snb
     *
     * @param otherUserId
     * @param otherLandId
     * @return
     */
    @PostMapping("stealFruit")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public Result<Map> stealFruitFunction(
            @RequestParam(value = "userId") String userId,
            @RequestParam(value = "otherUserId") String otherUserId,
            @RequestParam(value = "otherLandId") Integer otherLandId) {
        Map map = new HashMap();

        //todo 计入狗和打狗棒
        Boolean _dogWork = false;//狗是否工作，默认不工作
        Boolean _wasTheDogBitten = false; //默认没有被狗咬
        Boolean _holdTheStick = false;//默认没持有棒子
        Boolean _hasReduceSnb = false;//是否可以扣除偷菜的snb
        //对方的狗
        ComPlayerDog _otherDogVo = comPlayerDogService.findByUserIdAndType(otherUserId, 0);
        if (_otherDogVo != null) {
            //有狗或者有狗粮时候，处理狗的事件
            //todo 计算狗是否到期
            long _runDay = DateUtil.getDays(DateUtil.getNowDate().getTime() - _otherDogVo.getEffectiveStartTime().getTime());
            int _remainingDay = _otherDogVo.getEffectiveDay() - (int) _runDay;
            // 存在时间
            if (_remainingDay > 0) {
                //todo 这里判断狗粮是否足够
                long _diffConsumptionMill = _otherDogVo.getConsumptionUpdate().getTime() - DateUtil.getNowDate().getTime();
                long _workHours = DateUtil.getHours(_diffConsumptionMill);
                //工作多少个小时,消耗多少狗粮
                long _needConsumption = _otherDogVo.getConsumption() - _workHours;
                if (_needConsumption > 0) {
                    //口粮足够，工作
                    _dogWork = true;
                    //狗存在并工作，获取狗的数据
                    ComMallOtherVo _mallDogData = comMallOtherService.get(_otherDogVo.getOtherIndex()).getData();
                    Double _triggerPro = _mallDogData.getTriggerPro();//触发概率
                    if (new Random().nextDouble() <= _triggerPro) {
                        //被狗咬了
                        _wasTheDogBitten = true;
                    }
                }
            }

        }

        //
        //己方的打狗棒
        ComPlayerDog _beatDogStick = comPlayerDogService.findByUserIdAndType(userId, 1);

        if (_beatDogStick != null) {
            //todo 判断打狗棒是否到期
            long _stickRunDay = DateUtil.getDays(DateUtil.getNowDate().getTime() - _beatDogStick.getEffectiveStartTime().getTime());
            int _stickRemainingDay = _beatDogStick.getEffectiveDay() - (int) _stickRunDay;
            if(_stickRemainingDay>0){
                //持有打狗棒
                _holdTheStick = true;
            }
        }

        //获取数据库相关配置
        ComSettingVo comSettingVo = comSettingService.get("1").getData();
        if (comSettingVo.equals(null)) {
            return Result.of(null, false, ResultEnum.SETTING_IS_NULL.getMessage(), ResultEnum.SETTING_IS_NULL.getCode());
        }
        //todo 后台可配置,获取一个链上数据
        Integer _configStrength = comSettingVo.getMaxStrength(), _chainStrength = DappUtil.getChildrenCount(userId);
        //前端的显示,输出一个不小于0的体力值
        Integer _maxStrength = _configStrength + _chainStrength;
        //1.先判断用户是否有足够的体力，根据snb判断。2. 收取到果实，扣除用户一个体力值
        ComUsersVo comUsersVo = comUsersService.findByUserId(userId);
        ComPlayersAttriVo playersAttribute = comPlayersAttriService.findByUserId(userId);
        Integer _out = _maxStrength - Math.abs(playersAttribute.getStrength());
        Double _userAllSnb = DoubleUtil.add(comUsersVo.getSnb().doubleValue(), comUsersVo.getSnbPart());
        //判断是否有足够的snb
        if (DoubleUtil.compare(_userAllSnb, comSettingVo.getDeductSnb()) == -1) {
            _hasReduceSnb = false;
            //没有足够的snb
            //如果没有被狗咬，，正常扣除1点体力
            //如果被狗咬了，除了正常扣除1点体力外，额外扣除1点体力
            if (_wasTheDogBitten) {
                //判断是否有双倍体力
                if (_out < comSettingVo.getSnbUnitStrength()) {
                    map.put("msg", "需要双倍体力：" + comSettingVo.getSnbUnitStrength());
                    return Result.of(map, false, ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getMessage(), ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getCode());
                }
            }
        } else {
            _hasReduceSnb = true;
            //正常判断体力
            if (_out < comSettingVo.getUnitStrength()) {
                map.put("msg", "需要单倍体力：" + comSettingVo.getUnitStrength());
                return Result.of(map, false, ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getMessage(), ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getCode());
            }
        }

        //List<Map> _stealLandInfos = new ArrayList<>();
        Double _finalStealAmountSum = 0d;
        //todo 偷取用户目标的信息,即otherUserId和otherLandId
        ComPlayerLand otherPlayerLand = comPlayerLandService.findByUserIdAndLandId(otherUserId, otherLandId);
        if (otherPlayerLand == null) {
            return Result.of(null, false, ResultEnum.LAND_CAN_STEAL_IS_NULL.getMessage(), ResultEnum.LAND_CAN_STEAL_IS_NULL.getCode());
        }
        //snb 的key
        String _redisSNBKey = "SNB_SAVE_" + userId;
        //获取用户的plantFlag，因为之前的用户没有设置这个flag，所以设为第一次，用 userId 和 landId 组合成key
        String _redisKey = otherPlayerLand.getPlantFlag();
        if (!StringUtils.hasText(_redisKey)) {
            //return Result.of(null, false, ResultEnum.LAND_PLANT_FLAG_IS_NULL.getMessage(), ResultEnum.LAND_PLANT_FLAG_IS_NULL.getCode());
            _redisKey = otherPlayerLand.getUserId() + "or" + otherPlayerLand.getConfigLandId();
        }
        //todo 先判断是否偷取过
        ComPlayerProfit comPlayerProfit = comPlayerProfitService.findByUserIdAndOtherUserIdAndPlantFlag(userId, otherUserId, _redisKey);
        if (comPlayerProfit != null) {
            return Result.of(null, false, ResultEnum.LAND_HARVEST_STOLEN.getMessage(), ResultEnum.LAND_HARVEST_STOLEN.getCode());
        }
        //获取当前种植的种子
        ComMallSeedVo comMallSeedVo = comMallSeedService.findById(otherPlayerLand.getPlantId());
        //需要redis 加锁
        long time = System.currentTimeMillis() + RedisData.getPlayerGoodsTimeout();

        try {
            if (!redisLock.lock(_redisSNBKey, String.valueOf(time))) {
                //如果有snb冲突锁
                return Result.of(null, false, ResultEnum.USER_LOGIN_LOCK.getMessage(), ResultEnum.USER_LOGIN_LOCK.getCode());

            }
            if (!redisLock.lock(_redisKey, String.valueOf(time))) {
                //如果有冲突锁
                return Result.of(null, false, ResultEnum.LAND_STEAL_LOCK.getMessage(), ResultEnum.LAND_STEAL_LOCK.getCode());
            }
            //todo 如果当前人员偷窃水果。记录一个信息，存储当前玩家已偷过的水果的信息
            //计算一个偷取的收获量, 租赁倍数* 租赁日期下的产量
            Integer _otherAmount = 0;
            if (otherPlayerLand.getLeaseDate().equals(1)) {
                _otherAmount = otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest1();
            } else if (otherPlayerLand.getLeaseDate().equals(2)) {
                _otherAmount = otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest2();
            } else if (otherPlayerLand.getLeaseDate().equals(3)) {
                _otherAmount = otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest3();
            }
            //todo 拿出计算利润，收获量减去种子的成本(snb)后的百分之30 ，后面需要后台可调整
            Integer _profit = _otherAmount - otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getPriceSnb();
            // 配置的参数。这里先直接定义
            // 需要根据 狗是否生效，小偷是否装备打狗棒来确定参数
            Double profitConfig = _dogWork ? comSettingVo.getProfitDog() : comSettingVo.getProfit(),
                    stealRatioMaxConfig = comSettingVo.getStealMaxRatio(),
                    stealRatioMinConfig = comSettingVo.getStealMinRatio(),
                    finallyGetRatioConfig = comSettingVo.getFinalRatio();
            Double _stealRatio = new Random().nextDouble() * DoubleUtil.sub(stealRatioMaxConfig, stealRatioMinConfig) + stealRatioMinConfig;// 1%-3%
            //todo 被偷取的人扣除 一个损失量，初利润算 _profit
            //  如果狗生效，根据装备了打狗棒，设置对应的偷窃比例
            if (_dogWork) {
                _stealRatio = _holdTheStick ? comSettingVo.getStealRatioHasStick() : comSettingVo.getStealRatioNoStick();
            }
            Double _stolenAmount = DoubleUtil.mul(_profit.doubleValue(), _stealRatio);
            //这里根据plantFlag判断目标用户当前种植被偷取完，记录更新用户 comPlayerLand plantSteal 字段；保存一个偷取状态
            Double _sumStolen = comPlayerProfitService.getStolenSumByOtherUserIdAndPlantFlag(otherUserId, _redisKey);
            //todo 这里的可偷利润应该是最大值，用户不能超过这个，需要限制判断
            Double _maxAmount = DoubleUtil.mul(_profit.doubleValue(), profitConfig); //可偷取的利润
            //1 单偷操作 就是 _sumStolen>_maxAmount 或者 _sumStolen == _maxAmount ,被偷完了
            if (!DoubleUtil.compare(_sumStolen, _maxAmount).equals(-1)) {
                // 需要redis 解锁
                redisLock.unlock(_redisKey, String.valueOf(time));
                redisLock.unlock(_redisSNBKey, String.valueOf(time));
                //记录一个不可偷取的状态
                otherPlayerLand.setPlantSteal(0);
                comPlayerLandService.save(CopyUtil.copy(otherPlayerLand, ComPlayerLandVo.class));
                map.put("plant_steal", otherPlayerLand.getPlantSteal());
                return Result.of(map, false, ResultEnum.LAND_CAN_STEAL_IS_MAX.getMessage(), ResultEnum.LAND_CAN_STEAL_IS_MAX.getCode());
            }
            //计算剩下可偷利润的差，防止过多偷取用户利润 _stolenAmount
            Double _diff = Math.abs(DoubleUtil.sub(_maxAmount, _sumStolen));
            if (DoubleUtil.compare(_stolenAmount, _diff).equals(1)) {
                //_stolenAmount>_diff
                _stolenAmount = _diff;
            }
            //最终偷窃人获取的是偷取的是目标用户损失量的10% finallyGetRatioConfig
            Double _finalStealAmount = DoubleUtil.mul(_stolenAmount, finallyGetRatioConfig);
            //偷窃后，系统回收部分
            Double _lostStealAmount = DoubleUtil.sub(1d, finallyGetRatioConfig) * _stolenAmount;
            //果实 Type =1
            //todo 增加一个字段处理背包果实，拿出当前用户背包果实
            ComPlayerGoods comPlayerGoods = comPlayerGoodsService.findByUserIdAndIndexAndType(userId, comMallSeedVo.getHarvestId(), 1);
            Double _beforeStealAmount = 0.0;
            if (comPlayerGoods == null) {
                //背包增加收取到别的用户对应的类型数量。比如当前是偷取到的果实
                comPlayerGoods = new ComPlayerGoods();
                comPlayerGoods.setUserId(userId);
                comPlayerGoods.setGoodsIndex(comMallSeedVo.getHarvestId());
                comPlayerGoods.setGoodsType(1);
                comPlayerGoods.setName(comMallSeedVo.getHarvestName());
                //用种子的picture
                comPlayerGoods.setPictureName(comMallSeedVo.getPicture());
                //amount 设置0
                comPlayerGoods.setAmount(0);
                comPlayerGoods.setAmountPart(_finalStealAmount);
                _beforeStealAmount = 0.0;
            } else {
                _beforeStealAmount = comPlayerGoods.getAmountPart();
                //修改偷取字段
                comPlayerGoods.setAmountPart(DoubleUtil.add(comPlayerGoods.getAmountPart(), _finalStealAmount));
            }
            ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoods, ComPlayerGoodsVo.class);
            comPlayerGoodsService.save(comPlayerGoodsVo);

            //todo 偷窃损失，处理目标用户收取果实时候，减扣一部分被偷取的数量
            comPlayerProfit = new ComPlayerProfit();
            comPlayerProfit.setUserId(userId);
            comPlayerProfit.setTargetId(otherUserId);
            comPlayerProfit.setPlantFlag(_redisKey);
            comPlayerProfit.setLandId(otherPlayerLand.getConfigLandId());
            comPlayerProfit.setLeaseMultiple(otherPlayerLand.getLeaseMultiple());
            comPlayerProfit.setLeaseDate(otherPlayerLand.getLeaseDate());
            comPlayerProfit.setHarvest(_otherAmount);
            comPlayerProfit.setProfit(_profit.doubleValue());//目标用户可偷的初始利润值
            comPlayerProfit.setStolen(_stolenAmount);//目标用户被偷的数量
            comPlayerProfit.setFinalSteal(_finalStealAmount);//用户最终偷取的量，是被偷的数量 10%左右
            comPlayerProfit.setProfitAfter(DoubleUtil.sub(_profit.doubleValue(), DoubleUtil.add(_stolenAmount, _sumStolen))); //可偷的减去被偷的
            //记录相关比例
            comPlayerProfit.setProfitRatio(profitConfig);
            comPlayerProfit.setStealRatio(_stealRatio);
            comPlayerProfit.setFinalRatio(finallyGetRatioConfig);
            ComPlayerProfitVo comPlayerProfitVo = CopyUtil.copy(comPlayerProfit, ComPlayerProfitVo.class);
            comPlayerProfitService.save(comPlayerProfitVo);

            //todo 记录一个收获的操作日志，这里的数量都与偷窃知道相关。amountPart，注意不是果实实际总数 amount
            ComPlayerLog _playerLog = new ComPlayerLog();
            _playerLog.setUserId(userId);
            _playerLog.setTId(comMallSeedVo.getHarvestId());
            _playerLog.setTName(comMallSeedVo.getHarvestName());
            _playerLog.setTType(6);//偷取果实时候，type设置6
            //偷水果时候这部分数据应该是不变的
            _playerLog.setTAmount(0);
            _playerLog.setBeforeAmount(comPlayerGoods.getAmount());
            _playerLog.setAfterAmount(comPlayerGoods.getAmount());
            //偷取用户果实时候，收取时候信息记录
            _playerLog.setBeforePart(_beforeStealAmount);
            _playerLog.setTPart(_finalStealAmount);
            _playerLog.setTLoss(_lostStealAmount); //损失的部分
            _playerLog.setAfterPart(comPlayerGoods.getAmountPart());
            _playerLog.setLMultiple(otherPlayerLand.getLeaseMultiple());
            ComPlayerLogVo _playerLogVo = CopyUtil.copy(_playerLog, ComPlayerLogVo.class);
            comPlayerLogService.save(_playerLogVo);

            //todo 成功收取记录一个土地id
            Map _landMap = new HashMap();
            //记录一个id
            _landMap.put("landId", otherPlayerLand.getConfigLandId());
            //
            _landMap.put("stealAmount", _finalStealAmount);

            //固定成本
            _landMap.put("cost", otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getPriceSnb());
            //当前利润
            _landMap.put("profit", _profit);
            //打印一个比例
            _landMap.put("profitRatio", profitConfig);
            _landMap.put("intervalStealRatio", _stealRatio);
            _landMap.put("finallyGetRatioConfig", finallyGetRatioConfig);

            _finalStealAmountSum = DoubleUtil.add(_finalStealAmount, _finalStealAmountSum);
            // 需要redis 解锁
            redisLock.unlock(_redisKey, String.valueOf(time));

            map.put("msg", "偷取果实.");
            map.put("stealInfo", _landMap);
            map.put("stealSum", _finalStealAmountSum);

            //todo 体力值可以后台配置
            Integer _addStrength = comSettingVo.getUnitStrength();
            if (_hasReduceSnb) {
                /**
                 * todo 偷取果实时候snb变动
                 */
                ComSnbTranVo _snbTran = new ComSnbTranVo();
                _snbTran.setBeforeSnb(comUsersVo.getSnb());
                _snbTran.setBeforeSnbPart(comUsersVo.getSnbPart());

                //如果有snb，扣除对应的snb
                //先判断 SnbPart 部分是否有足够来扣取
                if (DoubleUtil.compare(comUsersVo.getSnbPart(), comSettingVo.getDeductSnb()) == -1) {
                    //如果不足，取1扣取
                    Integer _deductSnbMaxInt = (int) Math.ceil(comSettingVo.getDeductSnb());
                    comUsersVo.setSnb(comUsersVo.getSnb() - _deductSnbMaxInt);
                    Double _deductSnbPart = DoubleUtil.sub(1d, comSettingVo.getDeductSnb());
                    comUsersVo.setSnbPart(DoubleUtil.add(comUsersVo.getSnbPart(), _deductSnbPart));

                    _snbTran.setTranSnb(0);
                    _snbTran.setTranSnbPart(comSettingVo.getDeductSnb());
                } else {
                    //直接用snbPart 减去小数点部分即可
                    Double _deductSnbPart = DoubleUtil.sub(comUsersVo.getSnbPart(), comSettingVo.getDeductSnb());
                    comUsersVo.setSnbPart(_deductSnbPart);
                    _snbTran.setTranSnb(0);//整数部分没有进行交易
                    _snbTran.setTranSnbPart(comSettingVo.getDeductSnb());
                }
                //记录修改的数据
                comUsersService.save(comUsersVo);

                //记录果实id,记录交易数据
                _snbTran.setTranId(comMallSeedVo.getHarvestId().toString());
                _snbTran.setUserId(userId);
                _snbTran.setTranName("偷取果实");
                _snbTran.setTranType(4);
                _snbTran.setTranAmount(0); //此数量会操作背包记录
                _snbTran.setTranAmountPart(_finalStealAmount);
                _snbTran.setTranPrice(0);
                _snbTran.setTranDescribe("偷取用户果实消耗的snb");
                _snbTran.setIsAdd(0);//减少
                _snbTran.setAfterSnb(comUsersVo.getSnb());
                _snbTran.setAfterSnbPart(comUsersVo.getSnbPart());
                comSnbTranService.save(_snbTran);

            } else {
                //被狗咬掉双倍体力
                if (_wasTheDogBitten) {
                    _addStrength = comSettingVo.getSnbUnitStrength();
                }
            }

            playersAttribute.setStrength(playersAttribute.getStrength() + _addStrength);
            comPlayersAttriService.save(playersAttribute);
            ComPlayersAttriSimpleVo comPlayersAttriSimpleVo = CopyUtil.copy(playersAttribute, ComPlayersAttriSimpleVo.class);
            //前端的显示,输出一个不小于0的体力值
            Integer _outEnd = _maxStrength - Math.abs(playersAttribute.getStrength());
            comPlayersAttriSimpleVo.setCurrentStrength(_outEnd < 0 ? 0 : _outEnd);
            //最大值是配置的值和链上数据的值相加
            comPlayersAttriSimpleVo.setMaxStrength(_maxStrength);

            map.put("playerAttribute", comPlayersAttriSimpleVo);

            //输出对应的判定值
            map.put("dogWork", _dogWork);
            map.put("wasTheDogBitten", _wasTheDogBitten);
            map.put("holdTheStick", _holdTheStick);
            map.put("hasReduceSnb", _hasReduceSnb);
            //如果被狗咬后扣除的snb，和对应扣除的体力
            map.put("deductSnb", comSettingVo.getDeductSnb());
            map.put("deductStrength", _addStrength);

            //用户snb
            map.put("snb", comUsersVo.getSnb());
            map.put("snbPart", comUsersVo.getSnbPart());

            //解除snb锁
            redisLock.unlock(_redisSNBKey, String.valueOf(time));
            return Result.of(map);
        } catch (Exception e) {
            // 需要redis 解锁
            redisLock.unlock(_redisKey, String.valueOf(time));
            redisLock.unlock(_redisSNBKey, String.valueOf(time));
            throw new RuntimeException(e.getMessage());
        }

    }


    /**
     * 处理一键收取水果果实时候，传入对方用户id 即可
     * <p>
     * <p>
     * 收获时候，操作用户snb时候，需要加锁，防止用户收取时候遗漏计算snb
     *
     * @param otherUserId
     * @return
     */
    @PostMapping("stealAllFruit")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public Result<Map> stealAllFruitFunction(
            @RequestParam(value = "userId") String userId,
            @RequestParam(value = "otherUserId") String otherUserId) {

        Map map = new HashMap();

        //获取数据库相关配置
        ComSettingVo comSettingVo = comSettingService.get("1").getData();
        if (comSettingVo.equals(null)) {
            return Result.of(null, false, ResultEnum.SETTING_IS_NULL.getMessage(), ResultEnum.SETTING_IS_NULL.getCode());
        }
        //todo 后台可配置,获取一个链上数据
        Integer _configStrength = comSettingVo.getMaxStrength(), _chainStrength = DappUtil.getChildrenCount(userId);
        //前端的显示,输出一个不小于0的体力值
        Integer _maxStrength = _configStrength + _chainStrength;
        //1.先判断用户是否有足够的体力，根据snb判断。2. 收取到果实，扣除用户一个体力值
        ComUsersVo comUsersVo = comUsersService.findByUserId(userId);
        ComPlayersAttriVo playersAttribute = comPlayersAttriService.findByUserId(userId);
        Integer _out = _maxStrength - Math.abs(playersAttribute.getStrength());
        if (comUsersVo.getSnb() < comSettingVo.getDeductSnb()) {
            //判断是否有双倍体力
            if (_out < comSettingVo.getSnbUnitStrength()) {
                map.put("msg", "需要双倍体力：" + comSettingVo.getSnbUnitStrength());
                return Result.of(map, false, ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getMessage(), ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getCode());
            }
        } else {
            //正常判断体力
            if (_out < comSettingVo.getUnitStrength()) {
                map.put("msg", "需要单倍体力：" + comSettingVo.getUnitStrength());
                return Result.of(map, false, ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getMessage(), ResultEnum.PLAYER_ATTRIBUTE_STRENGTH_IS_NOT.getCode());
            }
        }

        List<Map> _stealLandInfos = new ArrayList<>();
        Double _finalStealAmountSum = 0d;
        //todo 偷取用户的对象信息等id
        List<ComPlayerLandAndCanSteal> otherPlayerLandAndCanSteal = comPlayerLandService.findCanStealByUserIdAndOtherUserId(userId, otherUserId);
        //List<ComPlayerLand> otherPlayerLands = comPlayerLandService.findAllByCanStealOtherLands(userId, otherUserId);
        List<ComPlayerLand> otherPlayerLands = new ArrayList<>();
        for (int i = 0; i < otherPlayerLandAndCanSteal.size(); i++) {
            if (otherPlayerLandAndCanSteal.get(i).getCanSteal().equals(1)) {
                otherPlayerLands.add(CopyUtil.copy(otherPlayerLandAndCanSteal.get(i), ComPlayerLand.class));
            }
        }
        if (otherPlayerLands.size() == 0) {
            return Result.of(null, false, ResultEnum.LAND_CAN_STEAL_IS_ZERO.getMessage(), ResultEnum.LAND_CAN_STEAL_IS_ZERO.getCode());
        }

        for (int i = 0; i < otherPlayerLands.size(); i++) {
            ComPlayerLand otherPlayerLand = otherPlayerLands.get(i);
            //获取当前种植的种子
            ComMallSeedVo comMallSeedVo = comMallSeedService.findById(otherPlayerLand.getPlantId());
            //需要redis 加锁
            long time = System.currentTimeMillis() + RedisData.getPlayerGoodsTimeout();
            //获取用户的plantFlag，因为之前的用户没有设置这个flag，所以设为第一次，用 userId 和 landId 组合成key
            String _redisKey = otherPlayerLand.getPlantFlag();
            if (!StringUtils.hasText(_redisKey)) {
                //return Result.of(null, false, ResultEnum.LAND_PLANT_FLAG_IS_NULL.getMessage(), ResultEnum.LAND_PLANT_FLAG_IS_NULL.getCode());
                _redisKey = otherPlayerLand.getUserId() + "or" + otherPlayerLand.getConfigLandId();
            }
            try {
                if (!redisLock.lock(_redisKey, String.valueOf(time))) {
                    //如果有冲突锁，跳过
                    continue;
                }
                //todo 如果当前人员偷窃水果。记录一个信息，存储当前玩家已偷过的水果的信息
                //计算一个偷取的收获量, 租赁倍数* 租赁日期下的产量
                Integer _otherAmount = 0;
                if (otherPlayerLand.getLeaseDate().equals(1)) {
                    _otherAmount = otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest1();
                } else if (otherPlayerLand.getLeaseDate().equals(2)) {
                    _otherAmount = otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest2();
                } else if (otherPlayerLand.getLeaseDate().equals(3)) {
                    _otherAmount = otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getHarvest3();
                }
                //todo 拿出计算利润，收获量减去种子的成本(snb)后的百分之30 ，后面需要后台可调整
                Integer _profit = _otherAmount - otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getPriceSnb();
                // 配置的参数。这里先直接定义
                Double profitConfig = comSettingVo.getProfit(), stealRatioMaxConfig = comSettingVo.getStealMaxRatio(), stealRatioMinConfig = comSettingVo.getStealMinRatio(), finallyGetRatioConfig = comSettingVo.getFinalRatio();
                Double _stealRatio = new Random().nextDouble() * DoubleUtil.sub(stealRatioMaxConfig, stealRatioMinConfig) + stealRatioMinConfig;// 1%-3%
                //todo 被偷取的人扣除 一个损失量，初利润算 _profit
                Double _stolenAmount = DoubleUtil.mul(_profit.doubleValue(), _stealRatio);
                //这里根据plantFlag判断目标用户当前种植被偷取完，看下是否需要处理更新前端信息，暂不处理
                Double _sumStolen = comPlayerProfitService.getStolenSumByOtherUserIdAndPlantFlag(otherUserId, _redisKey);
                //todo 这里的可偷利润应该是最大值，用户不能超过这个，需要限制判断
                Double _maxAmount = DoubleUtil.mul(_profit.doubleValue(), profitConfig); //可偷取的利润
                //1 一键 就是 _sumStolen>_maxAmount 或者 _sumStolen == _maxAmount ,被偷完了
                if (!DoubleUtil.compare(_sumStolen, _maxAmount).equals(-1)) {
                    // 需要redis 解锁
                    redisLock.unlock(_redisKey, String.valueOf(time));
                    //记录一个不可偷取的状态
                    otherPlayerLand.setPlantSteal(0);
                    comPlayerLandService.save(CopyUtil.copy(otherPlayerLand, ComPlayerLandVo.class));
                    continue;
                }
                //计算剩下可偷利润的差，防止过多偷取用户利润 _stolenAmount
                Double _diff = Math.abs(DoubleUtil.sub(_maxAmount, _sumStolen));
                if (DoubleUtil.compare(_stolenAmount, _diff).equals(1)) {
                    //_stolenAmount>_diff
                    _stolenAmount = _diff;
                }
                //最终偷窃人获取的是偷取的是目标用户损失量的10% finallyGetRatioConfig
                Double _finalStealAmount = DoubleUtil.mul(_stolenAmount, finallyGetRatioConfig);
                //偷窃后，系统回收部分
                Double _lostStealAmount = DoubleUtil.sub(1d, finallyGetRatioConfig) * _stolenAmount;
                //果实 Type =1
                //todo 增加一个字段处理背包果实，拿出当前用户背包果实
                ComPlayerGoods comPlayerGoods = comPlayerGoodsService.findByUserIdAndIndexAndType(userId, comMallSeedVo.getHarvestId(), 1);
                Double _beforeStealAmount = 0.0;
                if (comPlayerGoods == null) {
                    //背包增加收取到别的用户对应的类型数量。比如当前是偷取到的果实
                    comPlayerGoods = new ComPlayerGoods();
                    comPlayerGoods.setUserId(userId);
                    comPlayerGoods.setGoodsIndex(comMallSeedVo.getHarvestId());
                    comPlayerGoods.setGoodsType(1);
                    comPlayerGoods.setName(comMallSeedVo.getHarvestName());
                    //用种子的picture
                    comPlayerGoods.setPictureName(comMallSeedVo.getPicture());
                    //amount 设置0
                    comPlayerGoods.setAmount(0);
                    comPlayerGoods.setAmountPart(_finalStealAmount);
                    _beforeStealAmount = 0.0;
                } else {
                    _beforeStealAmount = comPlayerGoods.getAmountPart();
                    //修改偷取字段
                    comPlayerGoods.setAmountPart(DoubleUtil.add(comPlayerGoods.getAmountPart(), _finalStealAmount));
                }
                ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoods, ComPlayerGoodsVo.class);
                comPlayerGoodsService.save(comPlayerGoodsVo);

                //todo 偷窃损失，处理目标用户收取果实时候，减扣一部分被偷取的数量
                ComPlayerProfit comPlayerProfit = new ComPlayerProfit();
                comPlayerProfit.setUserId(userId);
                comPlayerProfit.setTargetId(otherUserId);
                comPlayerProfit.setPlantFlag(_redisKey);
                comPlayerProfit.setLandId(otherPlayerLand.getConfigLandId());
                comPlayerProfit.setLeaseMultiple(otherPlayerLand.getLeaseMultiple());
                comPlayerProfit.setLeaseDate(otherPlayerLand.getLeaseDate());
                comPlayerProfit.setHarvest(_otherAmount);
                comPlayerProfit.setProfit(_profit.doubleValue());//目标用户可偷的初始利润值
                comPlayerProfit.setStolen(_stolenAmount);//目标用户被偷的数量
                comPlayerProfit.setFinalSteal(_finalStealAmount);//用户最终偷取的量，是被偷的数量 10%左右
                comPlayerProfit.setProfitAfter(DoubleUtil.sub(_profit.doubleValue(), DoubleUtil.add(_stolenAmount, _sumStolen))); //可偷的减去被偷的
                //记录相关比例
                comPlayerProfit.setProfitRatio(profitConfig);
                comPlayerProfit.setStealRatio(_stealRatio);
                comPlayerProfit.setFinalRatio(finallyGetRatioConfig);
                ComPlayerProfitVo comPlayerProfitVo = CopyUtil.copy(comPlayerProfit, ComPlayerProfitVo.class);
                comPlayerProfitService.save(comPlayerProfitVo);

                //todo 记录一个收获的操作日志，这里由于偷取果实是有小数点的，小数点处理成整数，单位 暂定5位 100000
                //      这里的数量都与偷窃知道相关。amountPart，注意不是果实实际总数 amount
                ComPlayerLog _playerLog = new ComPlayerLog();
                _playerLog.setUserId(userId);
                _playerLog.setTId(comMallSeedVo.getHarvestId());
                _playerLog.setTName(comMallSeedVo.getHarvestName());
                _playerLog.setTType(6);//偷取果实时候，type设置6
                //偷水果时候这部分数据应该是不变的
                _playerLog.setTAmount(0);
                _playerLog.setBeforeAmount(comPlayerGoods.getAmount());
                _playerLog.setAfterAmount(comPlayerGoods.getAmount());
                //偷取用户果实时候，收取时候信息记录
                _playerLog.setBeforePart(_beforeStealAmount);
                _playerLog.setTPart(_finalStealAmount);
                _playerLog.setTLoss(_lostStealAmount); //损失的部分
                _playerLog.setAfterPart(comPlayerGoods.getAmountPart());
                _playerLog.setLMultiple(otherPlayerLand.getLeaseMultiple());
                ComPlayerLogVo _playerLogVo = CopyUtil.copy(_playerLog, ComPlayerLogVo.class);
                comPlayerLogService.save(_playerLogVo);

                //todo 成功收取记录一个土地id
                Map _landMap = new HashMap();
                //记录一个id
                _landMap.put("landId", otherPlayerLand.getConfigLandId());
                //
                _landMap.put("stealAmount", _finalStealAmount);

                //固定成本
                _landMap.put("cost", otherPlayerLand.getLeaseMultiple() * comMallSeedVo.getPriceSnb());
                //当前利润
                _landMap.put("profit", _profit);
                //打印一个比例
                _landMap.put("profitRatio", profitConfig);
                _landMap.put("intervalStealRatio", _stealRatio);
                _landMap.put("finallyGetRatioConfig", finallyGetRatioConfig);

                _finalStealAmountSum = DoubleUtil.add(_finalStealAmount, _finalStealAmountSum);
                _stealLandInfos.add(_landMap);
                // 需要redis 解锁
                redisLock.unlock(_redisKey, String.valueOf(time));
            } catch (Exception e) {
                // 需要redis 解锁
                redisLock.unlock(_redisKey, String.valueOf(time));
                throw new RuntimeException(e.getMessage());
            }
        }

        map.put("msg", "偷取果实.");
        map.put("steals", _stealLandInfos);
        map.put("stealSum", _finalStealAmountSum);

        if (_stealLandInfos.size() > 0) {
            //todo 体力值可以后台配置
            Integer _addStrength = comUsersVo.getSnb() < comSettingVo.getDeductSnb() ? comSettingVo.getSnbUnitStrength() : comSettingVo.getUnitStrength();
            playersAttribute.setStrength(playersAttribute.getStrength() + _addStrength);
            comPlayersAttriService.save(playersAttribute);
            ComPlayersAttriSimpleVo comPlayersAttriSimpleVo = CopyUtil.copy(playersAttribute, ComPlayersAttriSimpleVo.class);
            //前端的显示,输出一个不小于0的体力值
            Integer _outEnd = _maxStrength - Math.abs(playersAttribute.getStrength());
            comPlayersAttriSimpleVo.setCurrentStrength(_outEnd < 0 ? 0 : _outEnd);
            //最大值是配置的值和链上数据的值相加
            comPlayersAttriSimpleVo.setMaxStrength(_maxStrength);

            map.put("playerAttribute", comPlayersAttriSimpleVo);
        }

        return Result.of(map);
    }


    /**
     * 出售果实
     * <p>
     * todo 需要记录出售果实的snb操作记录
     *
     * @param userId
     * @param amount
     * @return
     */
    @PostMapping("saleFruit")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public Result<Map> saleFruitFunction(
            @RequestParam(value = "userId") String userId,
            @RequestParam(value = "fruitId") Integer fruitId,
            @RequestParam(value = "amount") Integer amount) {

        //
        ComFruitVo comFruitVo = comFruitService.findById(fruitId);

        if (comFruitVo == null) {
            return Result.of(null, false, ResultEnum.FRUIT_DATA_ERROR.getMessage(), ResultEnum.FRUIT_DATA_ERROR.getCode());
        }
        //果实
        ComPlayerGoods comPlayerGoodsSimpleVo = comPlayerGoodsService.findByUserIdAndIndexAndType(userId, fruitId, 1);

        //销售果实数量
        if (comPlayerGoodsSimpleVo == null) {
            return Result.of(null, false, ResultEnum.FRUIT_DATA_ERROR.getMessage(), ResultEnum.FRUIT_DATA_ERROR.getCode());
        }
        //添加了 amountPart，需要加入判断整数部分
        Integer _userFloorAmount = comPlayerGoodsSimpleVo.getAmount() + (int) Math.floor(comPlayerGoodsSimpleVo.getAmountPart());
        //旧判断 amount > comPlayerGoodsSimpleVo.getAmount()
        if (amount > _userFloorAmount) {
            return Result.of(null, false, ResultEnum.FRUIT_AMOUNT_ERROR.getMessage(), ResultEnum.FRUIT_AMOUNT_ERROR.getCode());
        }
        //todo 销售果实时候，收取5%手续费，最少出售20的倍数
        //   1.判定销售数量, 如果果实输入是0，或者不是20的倍数，返回提示
        // || amount % 20 != 0
        if (amount.equals(0)) {
            return Result.of(null, false, ResultEnum.FRUIT_AMOUNT_IS_ZERO_AND_NOT_MULTIPLE.getMessage(), ResultEnum.FRUIT_AMOUNT_IS_ZERO_AND_NOT_MULTIPLE.getCode());
        }
        //todo snb冻结加锁
        long time = System.currentTimeMillis() + RedisData.getSnbTimeout();
        String _redisKey = "SNB_SAVE_" + userId;

        try {
            //todo snb冻结加锁
            if (!redisLock.lock(_redisKey, String.valueOf(time))) {
                return Result.of(null, false, ResultEnum.PLAYER_GOODS_SAVE_LOCK.getMessage(), ResultEnum.PLAYER_GOODS_SAVE_LOCK.getCode());
            }

            Integer _selfBeforeFruitAmount = comPlayerGoodsSimpleVo.getAmount();
            Integer _selfAfterFruitAmount = 0;
            Double _selfBeforeFruitAmountPart = comPlayerGoodsSimpleVo.getAmountPart();
            Double _selfAfterFruitAmountPart = 0d;
            Integer _tAmount = 0;
            Double _tAmountPart = 0d;

            //if (comPlayerGoodsSimpleVo.getAmount().equals(amount)) {
            //    //如果库存量和出售的果实数量一样，删除
            //    comPlayerGoodsService.delete(comPlayerGoodsSimpleVo.getGoodsId());
            //    _selfAfterFruitAmount = 0;
            //} else {
            //    comPlayerGoodsSimpleVo.setAmount(comPlayerGoodsSimpleVo.getAmount() - amount);
            //    _selfAfterFruitAmount = comPlayerGoodsSimpleVo.getAmount();
            //    ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoodsSimpleVo, ComPlayerGoodsVo.class);
            //    comPlayerGoodsService.save(comPlayerGoodsVo);
            //}
            Integer _amountDif = comPlayerGoodsSimpleVo.getAmount() - amount;
            //优先处理背包的amount
            if (_amountDif > 0) {
                //当前交易值
                _tAmount = amount;
                //如果amount 数量足够，直接记录 他们的差值
                comPlayerGoodsSimpleVo.setAmount(_amountDif);
                _selfAfterFruitAmount = comPlayerGoodsSimpleVo.getAmount();
                _selfAfterFruitAmountPart = comPlayerGoodsSimpleVo.getAmountPart();
                ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoodsSimpleVo, ComPlayerGoodsVo.class);
                comPlayerGoodsService.save(comPlayerGoodsVo);
            } else {
                _selfAfterFruitAmount = 0;

                //保证精度
                BigDecimal bigDecimal1 = new BigDecimal(Double.toString(_amountDif));
                BigDecimal bigDecimal2 = new BigDecimal(Double.toString(comPlayerGoodsSimpleVo.getAmountPart()));
                BigDecimal _amountPart = bigDecimal1.add(bigDecimal2);

                //当前交易值，_amountDif 小于0 说明 amount 是全全部扣除，到小数部分了。
                _tAmount = comPlayerGoodsSimpleVo.getAmount();
                _tAmountPart = Math.abs(bigDecimal1.doubleValue());

                System.out.print("_amountPart{}," + _amountPart.doubleValue());
                //前面判断了。所以 _amountPart 不会比0.0小
                if (_amountPart.compareTo(new BigDecimal(0.0)) <= 0) {
                    //如果库存量 amount 和 amountPart 都为零，删除
                    comPlayerGoodsService.delete(comPlayerGoodsSimpleVo.getGoodsId());
                    //这里记录一下，看是否会是一个负数
                    _selfAfterFruitAmountPart = _amountPart.doubleValue();
                } else {
                    comPlayerGoodsSimpleVo.setAmount(0);
                    comPlayerGoodsSimpleVo.setAmountPart(_amountPart.doubleValue());
                    _selfAfterFruitAmountPart = comPlayerGoodsSimpleVo.getAmountPart();
                    ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoodsSimpleVo, ComPlayerGoodsVo.class);
                    comPlayerGoodsService.save(comPlayerGoodsVo);
                }
            }
            //todo 收取手续费之后
            int _endAmount = (int) (amount * 1);
            //添加SNB
            ComUsersVo comUsersVo = comUsersService.findByUserId(userId);
            ComSnbTranVo _snbTran = new ComSnbTranVo();
            _snbTran.setBeforeSnbPart(comUsersVo.getSnbPart());
            //交易前的snb数据
            Integer _beforeSnb = comUsersVo.getSnb();
            //数量乘以售价
            int _saleSnb = comFruitVo.getPriceSnb() * _endAmount; //此时的售价数量已经扣除了手续费，相当于减少了几个果实
            //当前操作的snb
            Integer _currentSnb = comUsersVo.getSnb() + _saleSnb;
            comUsersVo.setSnb(_currentSnb);
            comUsersService.save(comUsersVo);

            /**
             * todo 果实出售snb数据记录
             */

            //记录果实id
            _snbTran.setTranId(comFruitVo.getId().toString());
            _snbTran.setUserId(userId);
            _snbTran.setTranName(comFruitVo.getName());
            _snbTran.setTranType(0);
            _snbTran.setTranAmount(amount); //此数量会操作背包记录
            _snbTran.setTranAmountPart(0d);
            _snbTran.setTranPrice(comFruitVo.getPriceSnb());
            //"出售果实价格：" + comFruitVo.getPriceSnb() + "，数量：" + amount
            _snbTran.setTranDescribe("手续费系数:1,最终果实数量为：" + _endAmount + "，销售价格：" + _saleSnb);
            _snbTran.setIsAdd(1);//增加收入
            _snbTran.setBeforeSnb(_beforeSnb);
            _snbTran.setTranSnb(_saleSnb);
            _snbTran.setAfterSnb(_currentSnb);

            _snbTran.setTranSnbPart(0d);
            _snbTran.setAfterSnbPart(comUsersVo.getSnbPart());

            comSnbTranService.save(_snbTran);

            /**
             * todo 出售果实时候，记录操作 PlayerLog
             */
            //当前用户
            ComPlayerLog _saleFruitPlayerLog = new ComPlayerLog();
            _saleFruitPlayerLog.setUserId(userId);
            _saleFruitPlayerLog.setTId(comFruitVo.getId());
            _saleFruitPlayerLog.setTName(comFruitVo.getName());
            _saleFruitPlayerLog.setTType(5);//出售水果时候，type设置5
            _saleFruitPlayerLog.setTAmount(_tAmount);
            _saleFruitPlayerLog.setBeforeAmount(_selfBeforeFruitAmount);
            ////交易后的数据
            _saleFruitPlayerLog.setAfterAmount(_selfAfterFruitAmount);
            //增加一个小数部分记录
            _saleFruitPlayerLog.setTPart(_tAmountPart);
            _saleFruitPlayerLog.setTLoss(0d);
            _saleFruitPlayerLog.setBeforePart(_selfBeforeFruitAmountPart);
            _saleFruitPlayerLog.setAfterPart(_selfAfterFruitAmountPart);
            _saleFruitPlayerLog.setLMultiple(0);//收获时候，会有一个倍数
            ComPlayerLogVo _saleFruitPlayerLogVo = CopyUtil.copy(_saleFruitPlayerLog, ComPlayerLogVo.class);
            comPlayerLogService.save(_saleFruitPlayerLogVo);


            redisLock.unlock(_redisKey, String.valueOf(time));
        } catch (Exception e) {
            redisLock.unlock(_redisKey, String.valueOf(time));
            throw new RuntimeException(e.getMessage());
        }


        Map map = new HashMap();
        map.put("msg", "成功出售果实！");
        return Result.of(map);
    }


    /**
     * 根据用户地址送果实
     *
     * @param userId
     * @param address 目标用户地址
     * @return
     */
    @PostMapping("grantFruit")
    @ResponseBody
    @Transactional(rollbackFor = Exception.class)
    public Result<Map> grantFruitFunction(
            @RequestParam(value = "userId") String userId,
            @RequestParam(value = "fruitId") Integer fruitId,
            @RequestParam(value = "amount") Integer amount,
            @RequestParam(value = "address") String address) {

        //目标用户的背包
        ComUsersVo _targetUserVo = comUsersService.findByAddress(address);
        if (_targetUserVo == null) {
            return Result.of(null, false, ResultEnum.USER_NOT_ADDRESS.getMessage(), ResultEnum.USER_NOT_ADDRESS.getCode());
        }

        if (_targetUserVo.getUserId().equals(userId)) {
            return Result.of(null, false, ResultEnum.FRUIT_SEND_SELF.getMessage(), ResultEnum.FRUIT_SEND_SELF.getCode());
        }

        ComFruitVo comFruitVo = comFruitService.findById(fruitId);

        if (comFruitVo == null) {
            return Result.of(null, false, ResultEnum.FRUIT_DATA_ERROR.getMessage(), ResultEnum.FRUIT_DATA_ERROR.getCode());
        }
        //果实type =1
        ComPlayerGoods comPlayerGoodsSimpleVo = comPlayerGoodsService.findByUserIdAndIndexAndType(userId, fruitId, 1);

        //销售果实数量
        if (comPlayerGoodsSimpleVo == null) {
            return Result.of(null, false, ResultEnum.FRUIT_DATA_ERROR.getMessage(), ResultEnum.FRUIT_DATA_ERROR.getCode());
        }
        //添加了 amountPart，需要加入判断整数部分
        Integer _userFloorAmount = comPlayerGoodsSimpleVo.getAmount() + (int) Math.floor(comPlayerGoodsSimpleVo.getAmountPart());
        if (amount > _userFloorAmount) {
            return Result.of(null, false, ResultEnum.FRUIT_AMOUNT_ERROR.getMessage(), ResultEnum.FRUIT_AMOUNT_ERROR.getCode());
        }
        try {
            Integer _selfBeforeAmount = comPlayerGoodsSimpleVo.getAmount();
            Integer _selfAfterAmount = 0;
            Double _selfBeforeFruitAmountPart = comPlayerGoodsSimpleVo.getAmountPart();
            Double _selfAfterFruitAmountPart = 0d;
            Integer _tAmount = 0;
            Double _tAmountPart = 0d;

            //if (comPlayerGoodsSimpleVo.getAmount().equals(amount)) {
            //    //如果库存量和出售的果实数量一样，删除
            //    comPlayerGoodsService.delete(comPlayerGoodsSimpleVo.getGoodsId());
            //    _selfAfterAmount = 0;
            //} else {
            //    comPlayerGoodsSimpleVo.setAmount(comPlayerGoodsSimpleVo.getAmount() - amount);
            //    _selfAfterAmount = comPlayerGoodsSimpleVo.getAmount();
            //    ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoodsSimpleVo, ComPlayerGoodsVo.class);
            //    comPlayerGoodsService.save(comPlayerGoodsVo);
            //}
            Integer _amountDif = comPlayerGoodsSimpleVo.getAmount() - amount;
            //优先处理背包的amount
            if (_amountDif > 0) {
                //当前交易值
                _tAmount = amount;
                //如果amount 数量足够，直接记录 他们的差值
                comPlayerGoodsSimpleVo.setAmount(_amountDif);
                _selfAfterAmount = comPlayerGoodsSimpleVo.getAmount();
                _selfAfterFruitAmountPart = comPlayerGoodsSimpleVo.getAmountPart();
                ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoodsSimpleVo, ComPlayerGoodsVo.class);
                comPlayerGoodsService.save(comPlayerGoodsVo);
            } else {
                _selfAfterAmount = 0;
                //保证精度
                BigDecimal bigDecimal1 = new BigDecimal(Double.toString(_amountDif));
                BigDecimal bigDecimal2 = new BigDecimal(Double.toString(comPlayerGoodsSimpleVo.getAmountPart()));
                BigDecimal _amountPart = bigDecimal1.add(bigDecimal2);
                //当前交易值，_amountDif 小于0 说明 amount 是全全部扣除，到小数部分了。
                _tAmount = comPlayerGoodsSimpleVo.getAmount();
                _tAmountPart = Math.abs(bigDecimal1.doubleValue());
                System.out.print("_amountPart{}," + _amountPart.doubleValue());
                //前面判断了。所以 _amountPart 不会比0.0小
                if (_amountPart.compareTo(new BigDecimal(0.0)) <= 0) {
                    //如果库存量 amount 和 amountPart 都为零，删除
                    comPlayerGoodsService.delete(comPlayerGoodsSimpleVo.getGoodsId());
                    //这里记录一下，看是否会是一个负数
                    _selfAfterFruitAmountPart = _amountPart.doubleValue();
                } else {
                    comPlayerGoodsSimpleVo.setAmount(0);
                    comPlayerGoodsSimpleVo.setAmountPart(_amountPart.doubleValue());
                    _selfAfterFruitAmountPart = comPlayerGoodsSimpleVo.getAmountPart();
                    ComPlayerGoodsVo comPlayerGoodsVo = CopyUtil.copy(comPlayerGoodsSimpleVo, ComPlayerGoodsVo.class);
                    comPlayerGoodsService.save(comPlayerGoodsVo);
                }
            }

            //todo 发送果实，记录当前用户的playerLog
            //当前用户
            ComPlayerLog _selfPlayerLog = new ComPlayerLog();
            _selfPlayerLog.setUserId(userId);
            _selfPlayerLog.setTId(comFruitVo.getId());
            _selfPlayerLog.setTName(comFruitVo.getName());
            _selfPlayerLog.setTType(2);//发送水果的类型：2
            _selfPlayerLog.setTAmount(amount);
            _selfPlayerLog.setBeforeAmount(_selfBeforeAmount);
            ////交易后的数据
            _selfPlayerLog.setAfterAmount(_selfAfterAmount);
            _selfPlayerLog.setLMultiple(0);//收获时候，会有一个倍数
            ComPlayerLogVo _selfPlayerLogVo = CopyUtil.copy(_selfPlayerLog, ComPlayerLogVo.class);
            comPlayerLogService.save(_selfPlayerLogVo);

            //接受人的背包数据
            ComPlayerGoods _targetGoods = comPlayerGoodsService.findByUserIdAndIndexAndType(_targetUserVo.getUserId(), comFruitVo.getId(), 1);
            Integer _targetBeforeAmount = 0;
            Integer _targetAfterAmount = 0;
            if (_targetGoods == null) {
                //背包增加一个果实
                _targetGoods = new ComPlayerGoods();
                //目标id
                _targetGoods.setUserId(_targetUserVo.getUserId());
                _targetGoods.setGoodsIndex(comFruitVo.getId());
                _targetGoods.setGoodsType(1);
                _targetGoods.setName(comFruitVo.getName());
                //用种子的picture
                _targetGoods.setPictureName(comFruitVo.getPicture());
                _targetGoods.setAmount(amount);
                _targetGoods.setAmountPart(0d);
                _targetAfterAmount = _targetGoods.getAmount();
            } else {
                _targetBeforeAmount = _targetGoods.getAmount();
                _targetGoods.setAmount(_targetGoods.getAmount() + amount);
                _targetAfterAmount = _targetGoods.getAmount();
            }
            ComPlayerGoodsVo _targetGoodsVo = CopyUtil.copy(_targetGoods, ComPlayerGoodsVo.class);
            comPlayerGoodsService.save(_targetGoodsVo);
            //todo 接收果实，记录目标用户的playerLog
            ComPlayerLog _targetPlayerLog = new ComPlayerLog();
            _targetPlayerLog.setUserId(_targetUserVo.getUserId());
            _targetPlayerLog.setTId(comFruitVo.getId());
            _targetPlayerLog.setTName(comFruitVo.getName());
            _targetPlayerLog.setTType(3);//接收用户赠送水果，type设置 3
            _targetPlayerLog.setTAmount(_tAmount);
            _targetPlayerLog.setBeforeAmount(_targetBeforeAmount);
            ////交易后的数据
            _targetPlayerLog.setAfterAmount(_targetAfterAmount);
            //增加一个小数部分记录
            _targetPlayerLog.setTPart(_tAmountPart);
            _targetPlayerLog.setTLoss(0d);
            _targetPlayerLog.setBeforePart(_selfBeforeFruitAmountPart);
            _targetPlayerLog.setAfterPart(_selfAfterFruitAmountPart);

            _targetPlayerLog.setLMultiple(0);
            ComPlayerLogVo _targetPlayerLogVo = CopyUtil.copy(_targetPlayerLog, ComPlayerLogVo.class);
            comPlayerLogService.save(_targetPlayerLogVo);
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage());
        }


        Map map = new HashMap();
        map.put("msg", "成功发送果实！");
        return Result.of(map);
    }
}
