package org.tio.sitexxx.service.service.pay.impl;

import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import org.redisson.api.RBucket;
import org.redisson.api.RList;
import org.redisson.api.RedissonClient;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.tio.http.common.HttpRequest;
import org.tio.jfinal.kit.Kv;
import org.tio.jfinal.kit.Ret;
import org.tio.jfinal.plugin.activerecord.Db;
import org.tio.jfinal.plugin.activerecord.Record;
import org.tio.jfinal.plugin.activerecord.SqlPara;
import org.tio.sitexxx.im.server.vo.*;
import org.tio.sitexxx.service.init.RedisInit;
import org.tio.sitexxx.service.model.main.*;
import org.tio.sitexxx.service.service.ConfService;
import org.tio.sitexxx.service.service.atom.AbsTxAtom;
import org.tio.sitexxx.service.service.pay.WalletQueueApi;
import org.tio.sitexxx.service.service.pay.base.BaseBizPay;
import org.tio.sitexxx.service.service.pay.base.BasePay;
import org.tio.sitexxx.service.service.pay.base.BasePayReq;
import org.tio.sitexxx.service.service.pay.base.BasePayResp;
import org.tio.sitexxx.service.utils.RetUtils;
import org.tio.sitexxx.service.vo.Const;
import org.tio.sitexxx.service.vo.PayConst;
import org.tio.utils.json.Json;
import org.tio.utils.lock.LockUtils;

import java.util.*;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * <pre>
 *    @author : vagrancy
 *    e-mail : 18050829067@163.com
 *    time   : 2025-11-26 11:48
 *    desc   :  * 标准支付业务实现
 *  * 1、所有api基于自己的服务
 *  * 2、所有的支付接口服务端发起
 * </pre>
 */
public class PayStdBizApi implements BaseBizPay {

    private static Logger log = LoggerFactory.getLogger(PayStdBizApi.class);

    private BasePay<BasePayReq, BasePayResp> basePay;

    /**
     * @param basePay
     */
    public PayStdBizApi(BasePay<BasePayReq, BasePayResp> basePay) {
        this.basePay = basePay;
    }

    @Override
    public Ret updateUser(Map<String, Object> resp, Boolean isAtom) {
        return RetUtils.okOper();
    }

    @Override
    public Ret authRealname(Map<String, Object> resp, Boolean isAtom) {
        return RetUtils.okOper();
    }

    @Override
    public Ret recharge(BasePayResp resp, RechargeVo rechargeVo, Boolean isAtom) {
        String respCheck = resp.getMerMsg();
        if (StrUtil.isNotBlank(respCheck)) {
            WxWalletRechargeItem rechargeItem = WxWalletRechargeItem.mapToBean(resp.getResp());
            rechargeItem.setAmount(Long.parseLong(rechargeVo.getAmount()));
            rechargeItem.setTimeout(ConfService.getShort(Const.ConfMapping.WX_WALLET_RECHARGE_TIMEOUT, (short) 5));
            rechargeItem.setAgrno(rechargeVo.getAgrno());
            rechargeItem.setStatus(PayConst.WalletChangeStatus.FAIL);
            rechargeItem.setNotifyurl(rechargeVo.getNotifyUrl());
            rechargeItem.setRemark(rechargeVo.getRemark());
            if (StrUtil.isBlank(rechargeItem.getBizcompletetime())) {
                rechargeItem.setBizcompletetime(DateUtil.format(new DateTime(), "yyyy-MM-dd HH:mm:ss"));
            }
            boolean save = rechargeItem.save();
            if (!save) {
                log.error("保存错误预下单充值信息失败");
            }
            return RetUtils.failMsg(respCheck);
        }
        WxWalletRechargeItem rechargeItem = WxWalletRechargeItem.mapToBean(resp.getResp());
        rechargeItem.setAmount(Long.parseLong(rechargeVo.getAmount()));
        rechargeItem.setTimeout(ConfService.getShort(Const.ConfMapping.WX_WALLET_RECHARGE_TIMEOUT, (short) 5));
        rechargeItem.setAgrno(rechargeVo.getAgrno());
        rechargeItem.setMerstatus("-1");
        rechargeItem.setStatus(PayConst.WalletChangeStatus.LOCAL);
        rechargeItem.setNotifyurl(rechargeVo.getNotifyUrl());
        rechargeItem.setRemark(rechargeVo.getRemark());
        boolean save = rechargeItem.save();
        if (!save) {
            return RetUtils.failMsg("保存预下单充值信息失败");
        }
        return RetUtils.okData(rechargeItem);
    }

    @Override
    public Ret initRedpacket(SendRedpacketVo redpacketVo, Boolean isAtom) {
        if (isAtom == null) {
            isAtom = true;
        }
        short timeout = ConfService.getShort(Const.ConfMapping.WX_WALLET_REDPACKET_INT_TIMEOUT, (short) 20);
        Integer uid = redpacketVo.getUid();
        Short mode = redpacketVo.getMode();
        Short totalNum = redpacketVo.getNum();
        Short chatmode = redpacketVo.getChatmode();
        AbsTxAtom atom = new AbsTxAtom() {

            @Override
            public boolean noTxRun() {
                //初始化红包主表
                WxWalletSendRedPacket redPacket = new WxWalletSendRedPacket();
                DateTime createtime = new DateTime();
                redPacket.setChatmode(redPacket.getChatmode());
                redPacket.setChatbizid(redpacketVo.getBizid());
                redPacket.setUid(uid);
                redPacket.setMode(mode);
                redPacket.setChatmode(redpacketVo.getChatmode());
                redPacket.setBless(redpacketVo.getRemark());
                redPacket.setCny(Long.parseLong(redpacketVo.getCny()));
                redPacket.setNum(totalNum);
                redPacket.setStatus(PayConst.RedPacketStatus.INIT);
                redPacket.setBacktime(DateUtil.offsetMinute(createtime, timeout));
                redPacket.setCreatetime(createtime);
                redPacket.setUpdatetime(createtime);
                if (Objects.equals(Const.ChatMode.P2P, redpacketVo.getChatmode())) {
                    redPacket.setAcceptuid(redpacketVo.getBizid().intValue());
                }
                boolean init = redPacket.save();
                if (!init) {
                    return failRet("红包初始化异常");
                }
                Integer rid = redPacket.getId();
                Long totalCny = redPacket.getCny();
                if (Objects.equals(totalNum, (short) 1)) {
                    Integer senduid = null;
                    if (Objects.equals(chatmode, Const.ChatMode.P2P)) {
                        senduid = redpacketVo.getBizid().intValue();
                    }
                    boolean childinit = initRedRandom(rid, senduid, totalCny, mode, (short) 1);
                    if (!childinit) {
                        return failRet("子红包初始化异常");
                    }
                } else {
                    if (Objects.equals(PayConst.RedPackMode.NORMAL, mode)) {
                        Long singleCny = Long.parseLong(redpacketVo.getSinglecny());
                        Long totalMount = singleCny*totalNum;
                        if(!totalMount.equals(Long.valueOf(redpacketVo.getCny()))) {
                            return failRet("子红包初始化异常");
                        }
                        for (short i = 1; i <= totalNum; i++) {
                            boolean childinit = initRedRandom(rid, null, singleCny, mode, i);
                            if (!childinit) {
                                return failRet("子红包初始化异常");
                            }
                        }
                    } else {
                        Long minRed = (long) 1;
                        Random random = new Random();
                        for (short i = 1; i < totalNum; i++) {
                            Long safe = (long) Math.ceil(((double) (totalCny - (totalNum - i) * minRed)) / (totalNum - i));
                            int randnum = (int) (safe - minRed);
                            Long cny = minRed;
                            if (randnum > 0) {
                                cny = random.nextInt(randnum) + minRed;
                            }
                            totalCny = totalCny - cny;
                            if(cny<0) {
                                return failRet("子红包初始化异常");
                            }
                            boolean childinit = initRedRandom(rid, null, cny, mode, i);
                            if (!childinit) {
                                return failRet("子红包初始化异常");
                            }
                        }
                        boolean childinit = initRedRandom(rid, null, totalCny, mode, totalNum);
                        if (!childinit) {
                            return failRet("子红包初始化异常");
                        }
                    }
                }
                return okRet(rid);
            }
        };
        if (isAtom) {
            Db.use(Const.Db.TIO_SITE_MAIN).tx(atom);
        } else {
            atom.noTxRun();
        }
        return atom.getRetObj();
    }

    @Override
    public Ret sendRedpacket(Map<String, Object> resp) {
        return RetUtils.okOper();
    }

    @Override
    public Ret grabRedpacket(GrabRedpacketVo grabVo, User user, Boolean isAtom) {
        Integer rid = grabVo.getRid();
        ReentrantReadWriteLock rwLock = LockUtils.getReentrantReadWriteLock(org.tio.sitexxx.service.utils.LockUtils.LockPrefix.CHAT_REDPACKET_GRAB + "." + rid, WxWalletGrabRedItem.class);
        ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
        writeLock.lock();
        try {
            RedissonClient redissonClient = RedisInit.get();
            RList<Object> list = redissonClient.getList(rid+"_redUlist");
            boolean contains = list.contains(rid+user.getId());
            if(contains) {
                return RetUtils.failMsg("您已抢过该红包");
            }
            RBucket<Object> bucket = redissonClient.getBucket("redInfo_"+rid);
            //改为缓存里拿取红包信息
            WxWalletSendRedPacket redPacket =(WxWalletSendRedPacket)bucket.get();

            if (redPacket == null) {
                return RetUtils.failMsg("红包不存在");
            }
            if (Objects.equals(redPacket.getStatus(), PayConst.RedPacketStatus.SUCCESS)) {
                return RetUtils.failMsg("红包已抢完");
            }
            if (Objects.equals(redPacket.getStatus(), PayConst.RedPacketStatus.TIMEOUT)) {
                return RetUtils.failMsg("红包已超时退回");
            }
            if (Objects.equals(redPacket.getStatus(), PayConst.RedPacketStatus.CANCEL)) {
                return RetUtils.failMsg("红包已取消");
            }
            if (!Objects.equals(redPacket.getStatus(), PayConst.RedPacketStatus.PROCESS)) {
                return RetUtils.failMsg("红包未知状态：" + redPacket.getStatus());
            }
            WxWalletRedPacketRandom random = getRandom(redPacket);
            if (random == null) {
                log.error("红包出现已抢完，但是没有结束：red:{}", Json.toJson(redPacket));
                return RetUtils.failMsg("红包已抢完");
            }
            if (Objects.equals(redPacket.getChatmode(), Const.ChatMode.P2P) && !Objects.equals(random.getUid(), user.getId())) {
                log.error("抢红包时出现空：私聊抢的人不等于分配的人：red:{},random:{}", Json.toJson(redPacket), Json.toJson(random));
                return RetUtils.failMsg("你不能抢自己的红包");
            }
            list.add(rid+user.getId());
            random.setStatus(PayConst.RedRandomStatus.RANDOM);
            random.setUid(user.getId());
            Short acceptnum = redPacket.getAcceptnum();
            if(acceptnum == null) {
                acceptnum= 0;
            }
            redPacket.setAcceptnum((short)(acceptnum+1));
            //如果数字一样 说明抢完了 更新
            if(redPacket.getAcceptnum() == redPacket.getNum()) {
                redPacket.setStatus(PayConst.RedPacketStatus.SUCCESS);
                redPacket.setEndtime(new Date());
                redPacket.update();
            }
            bucket.set(redPacket);
            Ret ok = Ret.ok("random", random);
            return ok.set("redpacket", redPacket);
        } catch (Exception e) {
            log.error("", e);
        } finally {
            writeLock.unlock();
        }
        return RetUtils.failMsg("系统错误");
    }

    @Override
    public Ret withhold(BasePayResp resp, WithholdVo withholdVo, Boolean isAtom) {
        String respCheck = resp.getMerMsg();
        if (StrUtil.isNotBlank(respCheck)) {
            WxWalletWithholdItems holditems = WxWalletWithholdItems.mapToBean(resp.getResp());
            holditems.setAmount(Long.parseLong(withholdVo.getAmount()));
            holditems.setAgrno(withholdVo.getAgrno());
            holditems.setNotifyurl(withholdVo.getNotifyUrl());
            holditems.setArrivalamount(holditems.getAmount() - holditems.getBizfee());
            holditems.setStatus(PayConst.WalletChangeStatus.FAIL);
            if (StrUtil.isBlank(holditems.getBizcompletetime())) {
                holditems.setBizcompletetime(DateUtil.format(new DateTime(), "yyyy-MM-dd HH:mm:ss"));
            }
            holditems.setResptime(new DateTime());
            boolean save = holditems.save();
            if (!save) {
                log.error("保存错误提现信息异常:{}", Json.toJson(holditems));
            }
            return RetUtils.failMsg(respCheck);
        }
        WxWalletWithholdItems holditems = WxWalletWithholdItems.mapToBean(resp.getResp());
        holditems.setAmount(Long.parseLong(withholdVo.getAmount()));
        holditems.setAgrno(withholdVo.getAgrno());
        holditems.setNotifyurl(withholdVo.getNotifyUrl());
        holditems.setArrivalamount(holditems.getAmount() - holditems.getBizfee());
        holditems.setMerstatus("-1");
        holditems.setStatus(PayConst.WalletChangeStatus.LOCAL);
        holditems.setResptime(new DateTime());
        boolean save = holditems.save();
        if (!save) {
            return RetUtils.failMsg("保存提现信息异常");
        }
        return RetUtils.okData(holditems);
    }

    @Override
    public Ret withholdQuery(HttpRequest request, BasePayResp resp, WithholdQueryVo withholdVo, Boolean isAtom) {
        Map<String, Object> respMap = resp.getResp();
        Object objectStatus = respMap.get("merstatus");
        String merstatus = "";
        if (objectStatus != null) {
            merstatus = objectStatus.toString();
        }
        if (Objects.equals(merstatus, "1")) {
            ReentrantReadWriteLock rwLock = LockUtils.getReentrantReadWriteLock(org.tio.sitexxx.service.utils.LockUtils.LockPrefix.CHAT_WALLET_COIN + "." + withholdVo.getUid(), WxWalletCoin.class);
            ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
            writeLock.lock();
            try {
                WxWalletWithholdItems last = WxWalletWithholdItems.dao.findFirst("select * from wx_wallet_withhold_items where id = ?", withholdVo.getWid());
                if (!Objects.equals(last.getStatus(), PayConst.WalletChangeStatus.SUCCESS)) {
                    WalletVo walletVo = new WalletVo();
                    walletVo.setUid(last.getUid());
                    BasePayReq req = new BasePayReq(request);
                    req.setParams(walletVo.toMap());
                    BasePayResp walletResp = basePay.getWalletInfo(req, walletVo.getUid());

                    AbsTxAtom absTxAtom = new AbsTxAtom() {

                        @Override
                        public boolean noTxRun() {
                            WxWalletWithholdItems update = new WxWalletWithholdItems();
                            update.setId(withholdVo.getWid());
                            update.setStatus(PayConst.WalletChangeStatus.SUCCESS);
                            update.setMerstatus(MapUtil.getStr(respMap, "merstatus"));
                            update.setOrdererrormsg(resp.getMerMsg());
                            update.setQuerysyn(Const.YesOrNo.YES);
                            update.setQueuetime(new DateTime());
                            update.setBizcompletetime(MapUtil.getStr(respMap, "bizcompletetime"));
                            boolean callback = update.update();
                            if (!callback) {
                                return failRet("充值记录保存失败");
                            }
                            Ret synRet = synWallet(walletResp.getResp(), walletVo, false);
                            if (synRet.isFail()) {
                                return failRet(synRet);
                            }
                            resp.getResp().put("reqid", last.getReqid());
                            Ret coinRet = coinAdd(last.getAmount(), last.getUid(), PayConst.WalletMode.WIHTHOLD, Const.CoinFlag.PAY, "提现", last.getId(),
                                    PayConst.WalletChangeStatus.SUCCESS, resp.getResp(), DateUtil.format(last.getResptime(), "yyyy-MM-dd HH:mm:ss"),
                                    DateUtil.format(update.getQueuetime(), "yyyy-MM-dd HH:mm:ss"), false);
                            if (coinRet.isFail()) {
                                return failRet(coinRet);
                            }
                            return true;
                        }
                    };
                    boolean atom = false;
                    if (isAtom) {
                        atom = Db.use(Const.Db.TIO_SITE_MAIN).tx(absTxAtom);
                    } else {
                        atom = absTxAtom.noTxRun();
                    }
                    if (!atom) {
                        return absTxAtom.getRetObj();
                    }
                }
            } catch (Exception e) {
                log.error("", e);
            } finally {
                writeLock.unlock();
            }
        } else if (Objects.equals(merstatus, "0")) {
        } else if (Objects.equals(merstatus, "3")) {
        } else {
            WxWalletWithholdItems update = new WxWalletWithholdItems();
            update.setId(withholdVo.getWid());
            update.setStatus(PayConst.WalletChangeStatus.FAIL);
            update.setMerstatus(merstatus);
            update.setQuerysyn(Const.YesOrNo.YES);
            update.setQueuetime(new DateTime());
            update.setOrdererrormsg(resp.getMerMsg());
            update.update();
        }
        WxWalletWithholdItems item = getWithholdInfo(withholdVo.getWid());
        return RetUtils.okData(item);
    }

    @Override
    public Ret redpacketJobQuery(BasePayResp resp, RedpacketQueryVo queryVo, Boolean isAtom) {
        Map<String, Object> respMap = resp.getResp();
        Object objectStatus = respMap.get("merstatus");
        String merstatus = "";
        if (objectStatus != null) {
            merstatus = objectStatus.toString();
        }
        if (Objects.equals(merstatus, "1")) {
            Integer offset = ConfService.getInt(Const.ConfMapping.WX_WALLET_SENDREDPACKET_TIMEOUT, 1440);
            ReentrantReadWriteLock rwLock = LockUtils.getReentrantReadWriteLock(org.tio.sitexxx.service.utils.LockUtils.LockPrefix.CHAT_WALLET_COIN + "." + queryVo.getUid(), WxWalletCoin.class);
            ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
            writeLock.lock();
            try {
                WxWalletSendRedPacket last = WxWalletSendRedPacket.dao.findFirst("select * from wx_wallet_send_red_packet where id = ?", queryVo.getRid());
                if (last != null) {
                    if (Objects.equals(last.getStatus(), PayConst.RedPacketStatus.PAYING) || Objects.equals(last.getStatus(), PayConst.RedPacketStatus.PAYING_CONFIRM)) {
                        WalletVo walletVo = new WalletVo();
                        walletVo.setUid(last.getUid());
                        BasePayReq req = new BasePayReq(null);
                        req.setParams(walletVo.toMap());
                        BasePayResp walletResp = basePay.getWalletInfo(req, walletVo.getUid());

                        AbsTxAtom absTxAtom = new AbsTxAtom() {

                            @Override
                            public boolean noTxRun() {
                                DateTime starttime = new DateTime();
                                WxWalletSendRedPacket update = new WxWalletSendRedPacket();
                                update.setBizcompletetime(MapUtil.getStr(respMap, "bizcompletetime"));
                                update.setId(last.getId());
                                //								update.setPaytype(PayConst.RedPayType.BANKCARD);
                                update.setStarttime(starttime);
                                update.setSendmode(PayConst.RedSendMode.JOB);
                                DateTime timeout = DateUtil.offsetMinute(starttime, offset);
                                update.setBacktime(timeout);
                                update.setStatus(PayConst.RedPacketStatus.PROCESS);
                                update.setQuerysyn(Const.YesOrNo.YES);
                                //								boolean callback = update.update();
                                //								if(!callback) {
                                //									log.error("发红包补偿记录保存失败：{}",Json.toJson(last));
                                //									return failRet("发红包补偿记录保存失败");
                                //								}
                                Ret ret = updateRedPacketLock(update, last.getStatus(), true);
                                if (ret.isFail()) {//此处发生数据变更
                                    log.error("数据发生变更:{}", RetUtils.getRetMsg(ret));
                                    return true;
                                }
                                Ret synRet = synWallet(walletResp.getResp(), walletVo, false);
                                if (synRet.isFail()) {
                                    return failRet(synRet);
                                }
                                resp.getResp().put("reqid", last.getReqid());
                                String redTimeStr = DateUtil.format(update.getStarttime(), "yyyy-MM-dd HH:mm:ss");
                                Ret coinRet = coinAdd(last.getCny(), last.getUid(), PayConst.WalletMode.REDPACKET, Const.CoinFlag.PAY, "发红包", last.getId(),
                                        PayConst.WalletChangeStatus.SUCCESS, resp.getResp(), redTimeStr, redTimeStr, false);
                                if (coinRet.isFail()) {
                                    return failRet(coinRet);
                                }
                                return true;
                            }
                        };
                        boolean atom = false;
                        if (isAtom) {
                            atom = Db.use(Const.Db.TIO_SITE_MAIN).tx(absTxAtom);
                        } else {
                            atom = absTxAtom.noTxRun();
                        }
                        if (!atom) {
                            return absTxAtom.getRetObj();
                        }
                    }
                } else {
                    return RetUtils.failMsg("红包不存在");
                }
            } catch (Exception e) {
                log.error("", e);
            } finally {
                writeLock.unlock();
            }
            WxWalletSendRedPacket last = WxWalletSendRedPacket.dao.findFirst("select * from wx_wallet_send_red_packet where  id = ?", queryVo.getRid());
            try {
                if (Objects.equals(last.getSendmode(), PayConst.RedSendMode.JOB)) {
                    WalletQueueApi.sendRedpacketCallback(last);
                }
                return RetUtils.okOper();
            } catch (Exception e) {
                log.error(e.toString(), e);
            }

        } else if (Objects.equals(merstatus, "0")) {
            WxWalletSendRedPacket last = WxWalletSendRedPacket.dao.findFirst("select * from wx_wallet_send_red_packet where id = ?", queryVo.getRid());
            if (last != null) {
                if (Objects.equals(last.getStatus(), PayConst.RedPacketStatus.PAYING)) {
                    WxWalletSendRedPacket update = new WxWalletSendRedPacket();
                    update.setBizcompletetime(MapUtil.getStr(respMap, "bizcompletetime"));
                    update.setId(last.getId());
                    //						update.setPaytype(PayConst.RedPayType.BANKCARD);
                    update.setStatus(PayConst.RedPacketStatus.CANCEL);
                    update.setRemark("快捷支付发起-进行中异常处理为取消");
                    update.setQuerysyn(Const.YesOrNo.YES);
                    //						boolean callback = update.update();
                    //						if(!callback) {
                    //							log.error("快捷支付发起-进行中异常处理为取消-保存异常：{}",Json.toJson(last));
                    //							return RetUtils.failMsg("定时处理异常订单错误");
                    //						}
                    Ret ret = updateRedPacketLock(update, last.getStatus(), true);
                    if (ret.isFail()) {//此处发生数据变更
                        log.error("数据发生变更:{}", RetUtils.getRetMsg(ret));
                        return ret;
                    }

                } else {
                    WxWalletSendRedPacket update = new WxWalletSendRedPacket();
                    update.setBizcompletetime(MapUtil.getStr(respMap, "bizcompletetime"));
                    update.setId(last.getId());
                    //						update.setPaytype(PayConst.RedPayType.BANKCARD);
                    update.setStatus(PayConst.RedPacketStatus.FAIL);
                    update.setRemark("支付确认-进行中异常处理为错误");
                    update.setQuerysyn(Const.YesOrNo.YES);
                    //						boolean callback = update.update();
                    //						if(!callback) {
                    //							log.error("支付确认-进行中异常处理为错误-保存异常：{}",Json.toJson(last));
                    //							return RetUtils.failMsg("定时处理异常订单错误");
                    //						}
                    Ret ret = updateRedPacketLock(update, last.getStatus(), true);
                    if (ret.isFail()) {//此处发生数据变更
                        log.error("数据发生变更:{}", RetUtils.getRetMsg(ret));
                        return ret;
                    }
                }
                return RetUtils.okOper();
            }
        } else if (Objects.equals(merstatus, "3")) {
            WxWalletSendRedPacket last = WxWalletSendRedPacket.dao.findFirst("select * from wx_wallet_send_red_packet where id = ?", queryVo.getRid());
            WxWalletSendRedPacket update = new WxWalletSendRedPacket();
            update.setBizcompletetime(MapUtil.getStr(respMap, "bizcompletetime"));
            update.setId(last.getId());
            update.setStatus(PayConst.RedPacketStatus.CANCEL);
            update.setRemark("订单处理为取消-原始订单状态：" + last.getStatus());
            update.setQuerysyn(Const.YesOrNo.YES);
            //			boolean callback = update.update();
            //			if(!callback) {
            //				log.error("订单处理为取消-保存异常：{}",Json.toJson(last));
            //				return RetUtils.failMsg("定时处理异常订单错误");
            //			}
            Ret ret = updateRedPacketLock(update, last.getStatus(), true);
            if (ret.isFail()) {//此处发生数据变更
                log.error("数据发生变更:{}", RetUtils.getRetMsg(ret));
                return ret;
            }
            return RetUtils.okOper();
        } else {
            WxWalletSendRedPacket last = WxWalletSendRedPacket.dao.findFirst("select * from wx_wallet_send_red_packet where id = ?", queryVo.getRid());
            if (last != null) {
                WxWalletSendRedPacket update = new WxWalletSendRedPacket();
                update.setBizcompletetime(MapUtil.getStr(respMap, "bizcompletetime"));
                update.setId(last.getId());
                update.setStatus(PayConst.RedPacketStatus.FAIL);
                update.setRemark("订单处理为失败-原始订单状态：" + last.getStatus());
                update.setQuerysyn(Const.YesOrNo.YES);
                //					boolean callback = update.update();
                //					if(!callback) {
                //						log.error("订单处理为失败-保存异常：{}",Json.toJson(last));
                //						return RetUtils.failMsg("定时处理异常订单错误");
                //					}
                Ret ret = updateRedPacketLock(update, last.getStatus(), true);
                if (ret.isFail()) {//此处发生数据变更
                    log.error("数据发生变更:{}", RetUtils.getRetMsg(ret));
                    return ret;
                }
                return RetUtils.okOper();
            }
        }
        return RetUtils.failMsg("系统异常");
    }

    @Override
    public Ret redpacketTimeOut(WxWalletSendRedPacket redPacket, Boolean isAtom) {
        ReentrantReadWriteLock grabRwLock = LockUtils.getReentrantReadWriteLock(org.tio.sitexxx.service.utils.LockUtils.LockPrefix.CHAT_REDPACKET_GRAB + "." + redPacket.getId(), WxWalletGrabRedItem.class);
        ReentrantReadWriteLock.WriteLock grabWriteLock = grabRwLock.writeLock();
        grabWriteLock.lock();
        try {
            //超时状态处理
            WxWalletSendRedPacket last = WxWalletSendRedPacket.dao.findFirst("select * from wx_wallet_send_red_packet where id = ?", redPacket.getId());
            if (Objects.equals(last.getStatus(), PayConst.RedPacketStatus.SUCCESS)) {
                return RetUtils.okOper();
            }
            WxWalletSendRedPacket update = new WxWalletSendRedPacket();
            update.setId(redPacket.getId());
            update.setStatus(PayConst.RedPacketStatus.TIMEOUT);
            Ret ret = updateRedPacketLock(update, redPacket.getStatus(), true);
            if (ret.isFail()) {//此处发生数据变更-期待下次处理
                log.error("数据发生变更:{}", RetUtils.getRetMsg(ret));
                return ret;
            }
        } catch (Exception e) {
            log.error("", e);
        } finally {
            grabWriteLock.unlock();
        }
        Record record = Db.use(Const.Db.TIO_SITE_MAIN).findFirst("select count(id) count,IFNULL(sum(cny),0) cny from wx_wallet_red_packet_random WHERE rid = ? and `status` = ?",
                redPacket.getId(), PayConst.RedRandomStatus.INIT);
        long cny = record.getLong("cny");
        int count = record.getInt("count");
        if (cny <= 0 || count <= 0) {
            return RetUtils.failMsg("超时的红包数异常：" + cny);
        }
        Integer uid = redPacket.getUid();
        ReentrantReadWriteLock rwLock = LockUtils.getReentrantReadWriteLock(org.tio.sitexxx.service.utils.LockUtils.LockPrefix.CHAT_WALLET_COIN + "." + uid, WxWalletCoin.class);
        ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
        writeLock.lock();
        try {
            WxWalletBackRedPacketItems backitem = new WxWalletBackRedPacketItems();
            backitem.setUid(uid);
            backitem.setCny(cny);
            backitem.setMode(redPacket.getMode());
            backitem.setNum((short) count);
            backitem.setStatus(PayConst.WalletChangeStatus.SUCCESS);
            backitem.setRid(redPacket.getId());
            backitem.setDealtime(new DateTime());
            //转账
            TransferVo transferVo = new TransferVo();
            transferVo.setCny(cny + "");
            transferVo.setUid(uid);
            BasePayReq transferReq = new BasePayReq(null);
            transferReq.setParams(transferVo.toMap());
            BasePayResp resp = basePay.transfer(transferReq, transferVo.getUid());
            if (!resp.isBizOk()) {
                log.error("出现了异常：{}", resp.getMerMsg());
            }
            backitem.setReqid(MapUtil.getStr(resp.getResp(), "reqid"));
            backitem.setMerid(MapUtil.getStr(resp.getResp(), "merid"));
            backitem.setMerorderid(MapUtil.getStr(resp.getResp(), "merorderid"));
            backitem.setBacktime(new DateTime());
            //查询两个账号的钱
            WalletVo subWalletVo = new WalletVo();
            subWalletVo.setUid(-uid);
            BasePayReq subReq = new BasePayReq(null);
            subReq.setParams(subWalletVo.toMap());
            BasePayResp subWalletResp = basePay.getWalletInfo(subReq, subWalletVo.getUid());
            WalletVo walletVo = new WalletVo();
            walletVo.setUid(uid);
            BasePayReq req = new BasePayReq(null);
            req.setParams(walletVo.toMap());
            BasePayResp walletResp = basePay.getWalletInfo(req, walletVo.getUid());

            AbsTxAtom absTxAtom = new AbsTxAtom() {

                @Override
                public boolean noTxRun() {
                    //红包最后处理
                    WxWalletSendRedPacket timeout = new WxWalletSendRedPacket();
                    timeout.setId(redPacket.getId());
                    timeout.setRemark("超时处理成功");
                    timeout.setEndtime(new DateTime());
                    boolean update = timeout.update();
                    if (!update) {
                        log.error("红包超时保存信息失败：{}", Json.toJson(timeout));
                        return failRet("红包超时信息保存失败");
                    }
                    //两个账户同步数据
                    Ret synRet = synWallet(walletResp.getResp(), walletVo, false);
                    if (synRet.isFail()) {
                        return failRet(synRet);
                    }
                    Ret synSubRet = synWallet(subWalletResp.getResp(), subWalletVo, false);
                    if (synSubRet.isFail()) {
                        return failRet(synSubRet);
                    }
                    //红包退回记录保存
                    backitem.setCompletetime(new DateTime());
                    backitem.replaceSave();
                    String startTimeStr = DateUtil.format(backitem.getBacktime(), "yyyy-MM-dd HH:mm:ss");
                    String endTimeStr = DateUtil.format(backitem.getCompletetime(), "yyyy-MM-dd HH:mm:ss");
                    //明细保存
                    Ret coinRet = coinAdd(cny, uid, PayConst.WalletMode.REDPACKET, Const.CoinFlag.INCOME, "红包退回", backitem.getId(), PayConst.WalletChangeStatus.SUCCESS,
                            resp.getResp(), startTimeStr, endTimeStr, false);
                    if (coinRet.isFail()) {
                        return failRet(coinRet);
                    }
                    return true;
                }
            };
            boolean atom = false;
            if (isAtom) {
                atom = Db.use(Const.Db.TIO_SITE_MAIN).tx(absTxAtom);
            } else {
                atom = absTxAtom.noTxRun();
            }
            if (!atom) {
                return absTxAtom.getRetObj();
            }
        } catch (Exception e) {
            log.error("", e);
        } finally {
            writeLock.unlock();
        }
        return RetUtils.okOper();
    }

    @Override
    public Ret coinAdd(Long cny, Integer uid, Short mode, Short coinflag, String remark, Integer bizid, Short status, Map<String, Object> resultMap, String starttime, String endtime, Boolean isAtom) {
        WxWalletCoinItem coinItem = new WxWalletCoinItem();
        coinItem.setUid(uid);
        coinItem.setMode(mode);
        coinItem.setCoinflag(coinflag);
        coinItem.setStatus(status);
        coinItem.setCny(cny);
        coinItem.setOrderstatus(MapUtil.getStr(resultMap, "merstatus"));
        coinItem.setBizid(bizid);
        coinItem.setReqid(MapUtil.getStr(resultMap, "reqid"));
        coinItem.setMerorderid(MapUtil.getStr(resultMap, "merorderid"));
        coinItem.setBizcompletetime(endtime);
        coinItem.setBizcreattime(starttime);
        coinItem.setRemark(remark);
        boolean save = coinItem.save();
        if (!save) {
            log.error("钱包明细失败：{}", Json.toJson(coinItem));
            return RetUtils.failMsg("保存明细失败");
        }
        return RetUtils.okData(coinItem);
    }

    @Override
    public Ret synWallet(Map<String, Object> resultMap, WalletVo walletVo, Boolean isAtom) {
        WxWalletCoin coin = WxWalletCoin.dao.findFirst("select id,cny,unclearcny from wx_wallet_coin where uid = ?", walletVo.getUid());
        if (coin == null) {
            return RetUtils.failMsg("钱包不存在");
        }
        WxWalletCoin last = WxWalletCoin.dao.findFirst("select id,cny,unclearcny from wx_wallet_coin where id = ?", coin.getId());
        return RetUtils.okData(last);
    }

    /**
     * 初始化子红包
     * @param rid
     * @param cny
     * @param packetType
     * @param index
     * @return
     * @author xufei
     * 2021年3月16日 下午1:56:57
     */
    private static boolean initRedRandom(Integer rid, Integer touid, Long cny, Short packetType, Short index) {
        WxWalletRedPacketRandom random = new WxWalletRedPacketRandom();
        random.setRid(rid);
        if (touid != null) {
            random.setUid(touid);
        }
        if(cny <=0) {
            log.error("子红包初始化异常：{}", Json.toJson(random));
            return false;
        }
        random.setCny(cny);
        random.setMode(packetType);
        random.setRedindex(index);
        if (!random.save()) {
            log.error("子红包初始化异常：{}", Json.toJson(random));
            return false;
        }
        RedissonClient redissonClient = RedisInit.get();
        RList<Object> list = redissonClient.getList("redRandom_"+rid);
        list.add(random);
        return true;
    }

    /**
     * @param redpacket
     * @return
     * @author xufei
     * 2021年3月17日 上午10:48:19
     */
    private WxWalletRedPacketRandom getRandom(WxWalletSendRedPacket redpacket) {
        RedissonClient redissonClient = RedisInit.get();
        RList<Object> list = redissonClient.getList("redRandom_"+redpacket.getId());
        if (list == null || list.size()<=0) {
            log.error("抢红包时出现空：没有红包了", Json.toJson(redpacket));
            return null;
        }
        Collections.shuffle(list);
        WxWalletRedPacketRandom random =(WxWalletRedPacketRandom)list.get(0);
        list.remove(0);
        //WxWalletRedPacketRandom random = WxWalletRedPacketRandom.dao.findFirst("select * from wx_wallet_red_packet_random where rid = ? and status = ? order by redindex",
        //        redpacket.getId(), PayConst.RedRandomStatus.INIT);
        return random;
    }

    @Override
    public Ret updateRedPacketLock(WxWalletSendRedPacket redPacket, Short status, boolean lock) {
        if (lock) {
            ReentrantReadWriteLock rwLock = LockUtils.getReentrantReadWriteLock(org.tio.sitexxx.service.utils.LockUtils.LockPrefix.CHAT_REDPACKET_GET_LOCK + "." + redPacket.getId(), WxWalletSendRedPacket.class);
            ReentrantReadWriteLock.WriteLock writeLock = rwLock.writeLock();
            writeLock.lock();
            try {
                WxWalletSendRedPacket sendRed = WxWalletSendRedPacket.dao.findById(redPacket.getId());
                if (sendRed == null) {
                    return RetUtils.failMsg("操作已超时，请重试");
                }
                if (status != null && !Objects.equals(sendRed.getStatus(), status)) {
                    log.error("修改红包时，发现状态发生变更，red:{},updatestatus:{}", Json.toJson(redPacket), status);
                    return RetUtils.failMsg("红包状态已发生变更");
                }
                boolean update = redPacket.update();
                if (!update) {
                    return RetUtils.failMsg("修改红包数据异常");
                }
                return RetUtils.okOper();
            } catch (Exception e) {
                log.error("", e);
            } finally {
                writeLock.unlock();
            }
        } else {
            boolean update = redPacket.update();
            if (!update) {
                return RetUtils.failMsg("修改红包数据异常");
            }
            return RetUtils.okOper();
        }
        return RetUtils.failMsg("系统异常");
    }

    /**
     * @param id
     * @return
     * @author xufei
     * 2021年4月9日 下午2:42:05
     */
    public static WxWalletWithholdItems getWithholdInfo(Integer id) {
        Kv params = Kv.by("id", id);
        SqlPara sqlPara = Db.use(Const.Db.TIO_SITE_MAIN).getSqlPara("withhold.info", params);
        return WxWalletWithholdItems.dao.findFirst(sqlPara);
    }
}
